Add PromptKit request validation adapter

This commit is contained in:
2026-10-12 22:13:16 +00:00
parent 1c542505b7
commit 7fc5423688
4 changed files with 193 additions and 7 deletions

View File

@@ -43,8 +43,8 @@ type RequirementsRequest struct {
}
// Suite describes application-owned input, shared state, and optional catalog,
// named inspection, and requirements inspection capabilities. Its private
// issues retain adapter construction failures.
// named inspection, requirements inspection, and concrete request validation
// capabilities. Its private issues retain adapter construction failures.
// Factory results and callback effects remain caller-owned: Command does not
// deep-copy arbitrary objects or repair aliases returned by factories.
// A reached operation prepares one input, applies its captured selectors, and
@@ -52,18 +52,18 @@ type RequirementsRequest struct {
// Factories are not invoked during construction, installation, help, or version,
// and need not run after failure.
type Suite[I, S any] struct {
// NewInput is called once when definition, requirements, or catalog input
// NewInput is called once when definition, requirements, validation, or catalog input
// preparation is reached, before applying selectors and calling CheckInput.
// If nil, input is the zero value of I, including nil for pointer types.
NewInput func() I
// NewState is called once when an inspection or catalog callback is reached,
// NewState is called once when an inspection, validation, or catalog callback is reached,
// after input checks pass. If nil, state is the zero value of S, including
// nil for pointer types.
NewState func() S
// Selectors declares the original typed flags accepted by definition,
// requirements, and catalog commands.
// requirements, validation, and catalog commands.
Selectors []commandkit.Flag[I]
// CheckInput validates selected definition, requirements, or catalog input
// CheckInput validates selected definition, requirements, validation, or catalog input
// before state creation.
CheckInput func(ckconfig.Selection, I) error
// InspectPrompt inspects the selected prompt after input and state preparation.
@@ -74,6 +74,10 @@ type Suite[I, S any] struct {
// state preparation. It is optional; a nil callback omits the requirements
// command without disabling other capabilities.
InspectRequirements func(context.Context, RequirementsRequest, I, S) (commandkit.Acquired[*promptkit.RequirementInspection], error)
// ValidateRequest validates a concrete PromptKit request after input and state
// preparation. It is optional; a nil callback omits request validation without
// disabling other capabilities.
ValidateRequest func(context.Context, ValidationRequest, I, S) (commandkit.Acquired[struct{}], error)
// ListPrompts lists prompt identities after input and state preparation.
// It is optional; a nil callback does not install a prompt catalog command.
ListPrompts func(context.Context, ckconfig.Selection, I, S) (commandkit.Acquired[[]promptkit.PromptIdentity], error)
@@ -84,6 +88,62 @@ type Suite[I, S any] struct {
issues []error
}
// WithRequestValidation attaches PromptKit-backed concrete request validation
// to suite. The loader receives the request's original selection; the builder,
// when present, runs first and its returned request is passed to PromptKit
// unchanged. A nil builder forwards the seed request. PromptKit's native
// validation errors and the engine loader's configuration diagnostics are
// returned through the acquisition. A nil loader is retained as a suite wiring
// issue and does not install a validation callback. Caller-owned callback
// references and objects remain caller-owned.
func WithRequestValidation[I, S any](
suite Suite[I, S],
load func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error),
build func(context.Context, ValidationRequest, I, S) (promptkit.RunRequest, error),
) Suite[I, S] {
if load == nil {
issues := append([]error(nil), suite.issues...)
suite.issues = append(issues, errors.New("PromptKit engine loader is nil"))
return suite
}
suite.ValidateRequest = func(ctx context.Context, request ValidationRequest, input I, state S) (commandkit.Acquired[struct{}], error) {
if err := promptKitContextError(ctx); err != nil {
return commandkit.Acquired[struct{}]{}, fmt.Errorf("PromptKit request validation before building: %w", err)
}
concreteRequest := request.Request
if build != nil {
built, err := build(ctx, request, input, state)
if err != nil {
return commandkit.Acquired[struct{}]{}, err
}
concreteRequest = built
}
if err := promptKitContextError(ctx); err != nil {
return commandkit.Acquired[struct{}]{}, fmt.Errorf("PromptKit request validation after building: %w", err)
}
engine, _, diagnostics, err := loadPromptKitEngine(ctx, request.Selection, input, state, "", load)
if err != nil {
return commandkit.Acquired[struct{}]{Diagnostics: diagnostics}, err
}
if err := promptKitContextError(ctx); err != nil {
return commandkit.Acquired[struct{}]{Diagnostics: diagnostics}, fmt.Errorf("PromptKit request validation before native validation: %w", err)
}
if err := engine.ValidateRequest(ctx, concreteRequest); err != nil {
return commandkit.Acquired[struct{}]{Diagnostics: diagnostics}, err
}
if err := promptKitContextError(ctx); err != nil {
return commandkit.Acquired[struct{}]{Diagnostics: diagnostics}, fmt.Errorf("PromptKit request validation after native validation: %w", err)
}
return commandkit.Acquired[struct{}]{Diagnostics: diagnostics}, nil
}
return suite
}
type engineLoader[I, S any] func(context.Context, ckconfig.Selection, I, S) (*promptkit.Engine, error)
// ConfigScope adapts PromptKit's captured configuration inspection to the