|
6 | 6 |
|
7 | 7 | /* eslint-disable @typescript-eslint/no-namespace */ |
8 | 8 |
|
9 | | -import type { JsonSchemaType, jsonSchemaValidator } from '../validators/types.js'; |
10 | | - |
11 | | -// Standard Schema interfaces (from https://standardschema.dev) |
| 9 | +// Standard Schema interfaces — vendored from https://standardschema.dev (spec v1, Jan 2025) |
12 | 10 |
|
13 | 11 | export interface StandardTypedV1<Input = unknown, Output = Input> { |
14 | 12 | readonly '~standard': StandardTypedV1.Props<Input, Output>; |
@@ -92,11 +90,28 @@ export namespace StandardJSONSchemaV1 { |
92 | 90 | export type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>; |
93 | 91 | } |
94 | 92 |
|
95 | | -/** Combined interface for schemas with both validation and JSON Schema conversion (e.g., Zod v4). */ |
| 93 | +/** |
| 94 | + * Combined interface for schemas with both validation and JSON Schema conversion — |
| 95 | + * the intersection of {@linkcode StandardSchemaV1} and {@linkcode StandardJSONSchemaV1}. |
| 96 | + * |
| 97 | + * This is the type accepted by `registerTool` / `registerPrompt`. The SDK needs |
| 98 | + * `~standard.jsonSchema` to advertise the tool's argument shape in `tools/list`, and |
| 99 | + * `~standard.validate` to check incoming arguments when a `tools/call` arrives. |
| 100 | + * |
| 101 | + * Zod v4, ArkType, and Valibot (via `@valibot/to-json-schema`'s `toStandardJsonSchema`) |
| 102 | + * all implement both interfaces. |
| 103 | + * |
| 104 | + * @see https://standardschema.dev/ for the Standard Schema specification |
| 105 | + */ |
96 | 106 | export interface StandardSchemaWithJSON<Input = unknown, Output = Input> { |
97 | 107 | readonly '~standard': StandardSchemaV1.Props<Input, Output> & StandardJSONSchemaV1.Props<Input, Output>; |
98 | 108 | } |
99 | 109 |
|
| 110 | +export namespace StandardSchemaWithJSON { |
| 111 | + export type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>; |
| 112 | + export type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>; |
| 113 | +} |
| 114 | + |
100 | 115 | // Type guards |
101 | 116 |
|
102 | 117 | export function isStandardJSONSchema(schema: unknown): schema is StandardJSONSchemaV1 { |
@@ -131,35 +146,21 @@ export function standardSchemaToJsonSchema(schema: StandardJSONSchemaV1, io: 'in |
131 | 146 |
|
132 | 147 | export type StandardSchemaValidationResult<T> = { success: true; data: T } | { success: false; error: string }; |
133 | 148 |
|
134 | | -export async function validateStandardSchema<T extends StandardJSONSchemaV1>( |
135 | | - schema: T, |
136 | | - data: unknown, |
137 | | - jsonSchemaValidatorInstance?: jsonSchemaValidator |
138 | | -): Promise<StandardSchemaValidationResult<StandardJSONSchemaV1.InferOutput<T>>> { |
139 | | - // Use native validation if available |
140 | | - if (isStandardSchema(schema)) { |
141 | | - const result = await schema['~standard'].validate(data); |
142 | | - if (result.issues && result.issues.length > 0) { |
143 | | - const errorMessage = result.issues.map((i: StandardSchemaV1.Issue) => i.message).join(', '); |
144 | | - return { success: false, error: errorMessage }; |
145 | | - } |
146 | | - return { success: true, data: (result as StandardSchemaV1.SuccessResult<unknown>).value as StandardJSONSchemaV1.InferOutput<T> }; |
147 | | - } |
148 | | - |
149 | | - // Fall back to JSON Schema validation |
150 | | - if (jsonSchemaValidatorInstance) { |
151 | | - const jsonSchema = standardSchemaToJsonSchema(schema, 'input'); |
152 | | - const validator = jsonSchemaValidatorInstance.getValidator<StandardJSONSchemaV1.InferOutput<T>>(jsonSchema as JsonSchemaType); |
153 | | - const validationResult = validator(data); |
| 149 | +function formatIssue(issue: StandardSchemaV1.Issue): string { |
| 150 | + if (!issue.path?.length) return issue.message; |
| 151 | + const path = issue.path.map(p => String(typeof p === 'object' ? p.key : p)).join('.'); |
| 152 | + return `${path}: ${issue.message}`; |
| 153 | +} |
154 | 154 |
|
155 | | - if (validationResult.valid) { |
156 | | - return { success: true, data: validationResult.data }; |
157 | | - } |
158 | | - return { success: false, error: validationResult.errorMessage ?? 'Validation failed' }; |
| 155 | +export async function validateStandardSchema<T extends StandardSchemaWithJSON>( |
| 156 | + schema: T, |
| 157 | + data: unknown |
| 158 | +): Promise<StandardSchemaValidationResult<StandardSchemaWithJSON.InferOutput<T>>> { |
| 159 | + const result = await schema['~standard'].validate(data); |
| 160 | + if (result.issues && result.issues.length > 0) { |
| 161 | + return { success: false, error: result.issues.map(i => formatIssue(i)).join(', ') }; |
159 | 162 | } |
160 | | - |
161 | | - // No validation - trust the data |
162 | | - return { success: true, data: data as StandardJSONSchemaV1.InferOutput<T> }; |
| 163 | + return { success: true, data: (result as StandardSchemaV1.SuccessResult<unknown>).value as StandardSchemaWithJSON.InferOutput<T> }; |
163 | 164 | } |
164 | 165 |
|
165 | 166 | // Prompt argument extraction |
|
0 commit comments