@geekmidas/schema 0.1.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/{conversion-BJGLAqtd.mjs → conversion-CIBtqJkS.mjs} +20 -2
- package/dist/conversion-CIBtqJkS.mjs.map +1 -0
- package/dist/{conversion-CFlFrSru.cjs → conversion-Cf9R-jGc.cjs} +25 -1
- package/dist/conversion-Cf9R-jGc.cjs.map +1 -0
- package/dist/{conversion-CH_EmflL.d.cts → conversion-JY1Wixil.d.cts} +11 -2
- package/dist/conversion-JY1Wixil.d.cts.map +1 -0
- package/dist/{conversion-DUyZYTWO.d.mts → conversion-e4k9rTxy.d.mts} +11 -2
- package/dist/conversion-e4k9rTxy.d.mts.map +1 -0
- package/dist/conversion.cjs +2 -1
- package/dist/conversion.d.cts +2 -2
- package/dist/conversion.d.mts +2 -2
- package/dist/conversion.mjs +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +1 -1
- package/dist/{openapi-DR4_PG-e.d.cts → openapi-Aeb_1e9T.d.cts} +3 -1
- package/dist/openapi-Aeb_1e9T.d.cts.map +1 -0
- package/dist/openapi-DH5yCqKh.mjs.map +1 -1
- package/dist/openapi-DVvLYx-8.cjs.map +1 -1
- package/dist/{openapi-j01kFjpI.d.mts → openapi-Dxc4FuMP.d.mts} +3 -1
- package/dist/openapi-Dxc4FuMP.d.mts.map +1 -0
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/parser.cjs.map +1 -1
- package/dist/parser.d.cts +3 -1
- package/dist/parser.d.cts.map +1 -0
- package/dist/parser.d.mts +3 -1
- package/dist/parser.d.mts.map +1 -0
- package/dist/parser.mjs.map +1 -1
- package/dist/{types-ByLHeRGs.d.mts → types-D7ZRea_7.d.cts} +3 -1
- package/dist/types-D7ZRea_7.d.cts.map +1 -0
- package/dist/{types-DfcgE7cO.d.cts → types-o4dOkvCd.d.mts} +3 -1
- package/dist/types-o4dOkvCd.d.mts.map +1 -0
- package/dist/types.d.cts +1 -1
- package/dist/types.d.mts +1 -1
- package/package.json +1 -1
- package/src/__benchmarks__/conversion.bench.ts +67 -67
- package/src/__tests__/conversion.spec.ts +440 -257
- package/src/__tests__/openapi.spec.ts +390 -390
- package/src/__tests__/parser.spec.ts +202 -202
- package/src/conversion.ts +179 -154
- package/src/index.ts +8 -9
- package/src/openapi.ts +49 -49
- package/src/parser.ts +11 -11
- package/src/types.ts +16 -16
- package/tsconfig.json +9 -0
- package/tsdown.config.ts +1 -1
- package/dist/conversion-BJGLAqtd.mjs.map +0 -1
- package/dist/conversion-CFlFrSru.cjs.map +0 -1
package/src/conversion.ts
CHANGED
|
@@ -1,195 +1,220 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
2
|
|
|
3
3
|
export enum SchemaVendor {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
zod = 'zod',
|
|
5
|
+
valibot = 'valibot',
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export type VendorConvertor = (schema: any) => Promise<any>;
|
|
9
9
|
export type VendorConvertors = Record<SchemaVendor, VendorConvertor>;
|
|
10
10
|
|
|
11
11
|
function isSchemaVendor(vendor?: string): vendor is SchemaVendor {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
if (!vendor) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
return Object.values(SchemaVendor).includes(vendor as SchemaVendor);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const StandardSchemaJsonSchema: VendorConvertors = {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
20
|
+
zod: async (schema): Promise<any> => {
|
|
21
|
+
try {
|
|
22
|
+
const { z } = await import('zod/v4').catch(() => ({ z: {} }));
|
|
23
|
+
|
|
24
|
+
if ('toJSONSchema' in z && typeof z.toJSONSchema === 'function') {
|
|
25
|
+
return z.toJSONSchema(schema);
|
|
26
|
+
}
|
|
27
|
+
const { zodToJsonSchema } = await import('zod-to-json-schema');
|
|
28
|
+
|
|
29
|
+
const result = zodToJsonSchema(schema, {
|
|
30
|
+
removeAdditionalStrategy: 'strict',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return result;
|
|
34
|
+
} catch {
|
|
35
|
+
// Fallback to basic conversion if zod-to-json-schema is not available
|
|
36
|
+
return { type: 'object' };
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
valibot: async (schema): Promise<any> => {
|
|
40
|
+
const { toJsonSchema } = await import('@valibot/to-json-schema');
|
|
41
|
+
return toJsonSchema(schema as any);
|
|
42
|
+
},
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
function extractAndConvertDefs(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
jsonSchema: any,
|
|
47
|
+
componentCollector?: {
|
|
48
|
+
addSchema(id: string, schema: any): void;
|
|
49
|
+
getReference(id: string): { $ref: string };
|
|
50
|
+
},
|
|
51
51
|
): any {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
52
|
+
if (!jsonSchema || typeof jsonSchema !== 'object') {
|
|
53
|
+
return jsonSchema;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Process the schema recursively to update references
|
|
57
|
+
const processSchema = (schema: any): any => {
|
|
58
|
+
if (!schema || typeof schema !== 'object') {
|
|
59
|
+
return schema;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Handle $ref
|
|
63
|
+
if (schema.$ref && typeof schema.$ref === 'string') {
|
|
64
|
+
// Convert #/$defs/X to #/components/schemas/X
|
|
65
|
+
if (schema.$ref.startsWith('#/$defs/')) {
|
|
66
|
+
const refName = schema.$ref.replace('#/$defs/', '');
|
|
67
|
+
return componentCollector
|
|
68
|
+
? componentCollector.getReference(refName)
|
|
69
|
+
: schema;
|
|
70
|
+
}
|
|
71
|
+
return schema;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Handle arrays
|
|
75
|
+
if (Array.isArray(schema)) {
|
|
76
|
+
return schema.map(processSchema);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Process all properties recursively
|
|
80
|
+
const processed: any = {};
|
|
81
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
82
|
+
if (key === '$defs') {
|
|
83
|
+
// Skip $defs as they've been extracted
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
processed[key] = processSchema(value);
|
|
87
|
+
}
|
|
88
|
+
return processed;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Extract $defs if present
|
|
92
|
+
if (jsonSchema.$defs && componentCollector) {
|
|
93
|
+
for (const [defName, defSchema] of Object.entries(jsonSchema.$defs)) {
|
|
94
|
+
// Process the definition recursively to handle nested $refs
|
|
95
|
+
const processedDefSchema = processSchema(defSchema);
|
|
96
|
+
// Add each definition to the component collector
|
|
97
|
+
componentCollector.addSchema(defName, processedDefSchema);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Process the schema and remove $defs
|
|
102
|
+
const { $defs, ...schemaWithoutDefs } = jsonSchema;
|
|
103
|
+
return processSchema(schemaWithoutDefs);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
export async function convertStandardSchemaToJsonSchema(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
schema?: StandardSchemaV1,
|
|
108
|
+
componentCollector?: {
|
|
109
|
+
addSchema(id: string, schema: any): void;
|
|
110
|
+
getReference(id: string): { $ref: string };
|
|
111
|
+
},
|
|
112
112
|
): Promise<any> {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
if (!schema) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const vendor = schema['~standard']?.vendor;
|
|
118
|
+
if (!isSchemaVendor(vendor)) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Unsupported or missing vendor "${vendor}" for Standard Schema. Supported vendors are: ${Object.keys(StandardSchemaJsonSchema).join(', ')}`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const toJSONSchema = StandardSchemaJsonSchema[vendor];
|
|
125
|
+
const jsonSchema = await toJSONSchema(schema);
|
|
126
|
+
|
|
127
|
+
// Extract and convert $defs to components
|
|
128
|
+
return extractAndConvertDefs(jsonSchema, componentCollector);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
export async function getZodMetadata(
|
|
132
|
-
|
|
132
|
+
schema: StandardSchemaV1,
|
|
133
133
|
): Promise<SchemaMeta | undefined> {
|
|
134
|
-
|
|
134
|
+
const { ZodObject } = await import('zod/v4');
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
if (schema instanceof ZodObject) {
|
|
137
|
+
return schema.meta();
|
|
138
|
+
}
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Return JSON Schema for every schema registered in zod v4's global registry
|
|
145
|
+
* via `.meta({ id })`. Returns an empty object when zod v4 is unavailable or
|
|
146
|
+
* no schemas are registered.
|
|
147
|
+
*
|
|
148
|
+
* `unrepresentable: 'any'` keeps a single unrepresentable schema from
|
|
149
|
+
* collapsing the whole result.
|
|
150
|
+
*/
|
|
151
|
+
export async function getRegisteredZodJsonSchemas(): Promise<
|
|
152
|
+
Record<string, any>
|
|
153
|
+
> {
|
|
154
|
+
try {
|
|
155
|
+
const { z } = await import('zod/v4').catch(() => ({ z: null as any }));
|
|
156
|
+
if (!z?.toJSONSchema || !z.globalRegistry) {
|
|
157
|
+
return {};
|
|
158
|
+
}
|
|
159
|
+
const result = z.toJSONSchema(z.globalRegistry, {
|
|
160
|
+
unrepresentable: 'any',
|
|
161
|
+
});
|
|
162
|
+
return (result?.schemas ?? {}) as Record<string, any>;
|
|
163
|
+
} catch {
|
|
164
|
+
return {};
|
|
165
|
+
}
|
|
141
166
|
}
|
|
142
167
|
|
|
143
168
|
export async function getSchemaMetadata(
|
|
144
|
-
|
|
169
|
+
schema: StandardSchemaV1,
|
|
145
170
|
): Promise<SchemaMeta | undefined> {
|
|
146
|
-
|
|
171
|
+
const vendor = schema['~standard']?.vendor;
|
|
147
172
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
173
|
+
if (vendor === 'zod') {
|
|
174
|
+
return getZodMetadata(schema);
|
|
175
|
+
}
|
|
151
176
|
|
|
152
|
-
|
|
177
|
+
return undefined;
|
|
153
178
|
}
|
|
154
179
|
|
|
155
180
|
interface SchemaMeta {
|
|
156
|
-
|
|
181
|
+
id?: string;
|
|
157
182
|
}
|
|
158
183
|
|
|
159
184
|
export async function convertSchemaWithComponents(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
185
|
+
schema: StandardSchemaV1 | undefined,
|
|
186
|
+
componentCollector?: {
|
|
187
|
+
addSchema(id: string, schema: any): void;
|
|
188
|
+
getReference(id: string): { $ref: string };
|
|
189
|
+
},
|
|
165
190
|
): Promise<any> {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
if (!schema) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Convert to JSON Schema with component collector to handle $defs
|
|
196
|
+
const jsonSchema = await convertStandardSchemaToJsonSchema(
|
|
197
|
+
schema,
|
|
198
|
+
componentCollector,
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
if (!componentCollector) {
|
|
202
|
+
return jsonSchema;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Check if this schema has metadata with an ID
|
|
206
|
+
const metadata = await getSchemaMetadata(schema);
|
|
207
|
+
|
|
208
|
+
// Also check if the JSON Schema itself has an id field (from Zod's meta)
|
|
209
|
+
const schemaId = metadata?.id || jsonSchema?.id;
|
|
210
|
+
|
|
211
|
+
if (schemaId) {
|
|
212
|
+
// Remove the id from the schema before adding to components
|
|
213
|
+
const { id, ...schemaWithoutId } = jsonSchema;
|
|
214
|
+
// Add this schema to components and return a reference
|
|
215
|
+
componentCollector.addSchema(schemaId, schemaWithoutId);
|
|
216
|
+
return componentCollector.getReference(schemaId);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return jsonSchema;
|
|
195
220
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
export type {
|
|
2
|
-
InferStandardSchema,
|
|
3
|
-
ComposableStandardSchema,
|
|
4
|
-
InferComposableStandardSchema,
|
|
5
|
-
} from './types';
|
|
6
|
-
|
|
7
1
|
// Re-export conversion utilities for convenience
|
|
8
2
|
export {
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
convertSchemaWithComponents,
|
|
4
|
+
convertStandardSchemaToJsonSchema,
|
|
11
5
|
} from './conversion';
|
|
6
|
+
export type { ComponentCollector, OpenApiSchemaOptions } from './openapi';
|
|
12
7
|
|
|
13
8
|
// Re-export OpenAPI utilities for convenience
|
|
14
9
|
export { buildOpenApiSchema, createComponentCollector } from './openapi';
|
|
15
|
-
export type {
|
|
10
|
+
export type {
|
|
11
|
+
ComposableStandardSchema,
|
|
12
|
+
InferComposableStandardSchema,
|
|
13
|
+
InferStandardSchema,
|
|
14
|
+
} from './types';
|
package/src/openapi.ts
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import type { OpenAPIV3_1 } from 'openapi-types';
|
|
2
2
|
|
|
3
3
|
export interface OpenApiSchemaOptions {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
title?: string;
|
|
5
|
+
version?: string;
|
|
6
|
+
description?: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export interface ComponentCollector {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
schemas: Record<string, OpenAPIV3_1.SchemaObject>;
|
|
11
|
+
addSchema(id: string, schema: OpenAPIV3_1.SchemaObject): void;
|
|
12
|
+
getReference(id: string): OpenAPIV3_1.ReferenceObject;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function createComponentCollector(): ComponentCollector {
|
|
16
|
-
|
|
16
|
+
const schemas: Record<string, OpenAPIV3_1.SchemaObject> = {};
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
return {
|
|
19
|
+
schemas,
|
|
20
|
+
addSchema(id: string, schema: OpenAPIV3_1.SchemaObject) {
|
|
21
|
+
schemas[id] = schema;
|
|
22
|
+
},
|
|
23
|
+
getReference(id: string): OpenAPIV3_1.ReferenceObject {
|
|
24
|
+
return { $ref: `#/components/schemas/${id}` };
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -33,43 +33,43 @@ export function createComponentCollector(): ComponentCollector {
|
|
|
33
33
|
* The actual implementation is in @geekmidas/constructs to avoid circular dependencies.
|
|
34
34
|
*/
|
|
35
35
|
export async function buildOpenApiSchema(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
endpoints: Array<{
|
|
37
|
+
toOpenApi3Route(collector?: ComponentCollector): Promise<any>;
|
|
38
|
+
}>,
|
|
39
|
+
options: OpenApiSchemaOptions = {},
|
|
40
40
|
): Promise<OpenAPIV3_1.Document> {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
const { title = 'API', version = '1.0.0', description } = options;
|
|
42
|
+
const paths: OpenAPIV3_1.PathsObject = {};
|
|
43
|
+
const componentCollector = createComponentCollector();
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
for (const endpoint of endpoints) {
|
|
46
|
+
const route = await endpoint.toOpenApi3Route(componentCollector);
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
// Merge the route into the paths object
|
|
49
|
+
for (const [path, methods] of Object.entries(route)) {
|
|
50
|
+
if (!paths[path]) {
|
|
51
|
+
paths[path] = {};
|
|
52
|
+
}
|
|
53
|
+
Object.assign(paths[path], methods);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
const doc: OpenAPIV3_1.Document = {
|
|
58
|
+
openapi: '3.0.0',
|
|
59
|
+
info: {
|
|
60
|
+
title,
|
|
61
|
+
version,
|
|
62
|
+
...(description && { description }),
|
|
63
|
+
},
|
|
64
|
+
paths,
|
|
65
|
+
};
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
// Add components if any schemas were collected
|
|
68
|
+
if (Object.keys(componentCollector.schemas).length > 0) {
|
|
69
|
+
doc.components = {
|
|
70
|
+
schemas: componentCollector.schemas,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
return doc;
|
|
75
75
|
}
|
package/src/parser.ts
CHANGED
|
@@ -9,22 +9,22 @@ import type { InferStandardSchema } from './types';
|
|
|
9
9
|
* @returns Validation result with value or issues
|
|
10
10
|
*/
|
|
11
11
|
export function validate<T extends StandardSchemaV1>(schema: T, data: unknown) {
|
|
12
|
-
|
|
12
|
+
return schema['~standard'].validate(data);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export async function parseSchema<T extends StandardSchemaV1>(
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
schema: T,
|
|
17
|
+
data: unknown,
|
|
18
18
|
): Promise<InferStandardSchema<T>> {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
if (!schema) {
|
|
20
|
+
return undefined as InferStandardSchema<T>;
|
|
21
|
+
}
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
const parsed = await validate(schema as unknown as StandardSchemaV1, data);
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
if (parsed.issues) {
|
|
26
|
+
throw parsed.issues;
|
|
27
|
+
}
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
return parsed.value as InferStandardSchema<T>;
|
|
30
30
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
2
|
|
|
3
3
|
export type InferStandardSchema<T> = T extends StandardSchemaV1
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
? StandardSchemaV1.InferOutput<T>
|
|
5
|
+
: never;
|
|
6
6
|
|
|
7
7
|
export type ComposableStandardSchema =
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
| StandardSchemaV1
|
|
9
|
+
| {
|
|
10
|
+
[key: string]: StandardSchemaV1 | undefined;
|
|
11
|
+
};
|
|
12
12
|
|
|
13
13
|
export type InferComposableStandardSchema<T> = T extends StandardSchemaV1
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
? StandardSchemaV1.InferOutput<T>
|
|
15
|
+
: T extends { [key: string]: StandardSchemaV1 | undefined }
|
|
16
|
+
? {
|
|
17
|
+
[K in keyof T as T[K] extends StandardSchemaV1
|
|
18
|
+
? K
|
|
19
|
+
: never]: T[K] extends StandardSchemaV1
|
|
20
|
+
? StandardSchemaV1.InferOutput<T[K]>
|
|
21
|
+
: never;
|
|
22
|
+
}
|
|
23
|
+
: {};
|
package/tsconfig.json
ADDED
package/tsdown.config.ts
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"conversion-BJGLAqtd.mjs","names":["vendor?: string","StandardSchemaJsonSchema: VendorConvertors","jsonSchema: any","componentCollector?: {\n addSchema(id: string, schema: any): void;\n getReference(id: string): { $ref: string };\n }","schema: any","processed: any","schema?: StandardSchemaV1","schema: StandardSchemaV1","schema: StandardSchemaV1 | undefined"],"sources":["../src/conversion.ts"],"sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec';\n\nexport enum SchemaVendor {\n zod = 'zod',\n valibot = 'valibot',\n}\n\nexport type VendorConvertor = (schema: any) => Promise<any>;\nexport type VendorConvertors = Record<SchemaVendor, VendorConvertor>;\n\nfunction isSchemaVendor(vendor?: string): vendor is SchemaVendor {\n if (!vendor) {\n return false;\n }\n\n return Object.values(SchemaVendor).includes(vendor as SchemaVendor);\n}\n\nexport const StandardSchemaJsonSchema: VendorConvertors = {\n zod: async (schema): Promise<any> => {\n try {\n const { z } = await import('zod/v4').catch(() => ({ z: {} }));\n\n if ('toJSONSchema' in z && typeof z.toJSONSchema === 'function') {\n return z.toJSONSchema(schema);\n }\n const { zodToJsonSchema } = await import('zod-to-json-schema');\n\n const result = zodToJsonSchema(schema, {\n removeAdditionalStrategy: 'strict',\n });\n\n return result;\n } catch {\n // Fallback to basic conversion if zod-to-json-schema is not available\n return { type: 'object' };\n }\n },\n valibot: async (schema): Promise<any> => {\n const { toJsonSchema } = await import('@valibot/to-json-schema');\n return toJsonSchema(schema as any);\n },\n};\n\nfunction extractAndConvertDefs(\n jsonSchema: any,\n componentCollector?: {\n addSchema(id: string, schema: any): void;\n getReference(id: string): { $ref: string };\n },\n): any {\n if (!jsonSchema || typeof jsonSchema !== 'object') {\n return jsonSchema;\n }\n\n // Process the schema recursively to update references\n const processSchema = (schema: any): any => {\n if (!schema || typeof schema !== 'object') {\n return schema;\n }\n\n // Handle $ref\n if (schema.$ref && typeof schema.$ref === 'string') {\n // Convert #/$defs/X to #/components/schemas/X\n if (schema.$ref.startsWith('#/$defs/')) {\n const refName = schema.$ref.replace('#/$defs/', '');\n return componentCollector\n ? componentCollector.getReference(refName)\n : schema;\n }\n return schema;\n }\n\n // Handle arrays\n if (Array.isArray(schema)) {\n return schema.map(processSchema);\n }\n\n // Process all properties recursively\n const processed: any = {};\n for (const [key, value] of Object.entries(schema)) {\n if (key === '$defs') {\n // Skip $defs as they've been extracted\n continue;\n }\n processed[key] = processSchema(value);\n }\n return processed;\n };\n\n // Extract $defs if present\n if (jsonSchema.$defs && componentCollector) {\n for (const [defName, defSchema] of Object.entries(jsonSchema.$defs)) {\n // Process the definition recursively to handle nested $refs\n const processedDefSchema = processSchema(defSchema);\n // Add each definition to the component collector\n componentCollector.addSchema(defName, processedDefSchema);\n }\n }\n\n // Process the schema and remove $defs\n const { $defs, ...schemaWithoutDefs } = jsonSchema;\n return processSchema(schemaWithoutDefs);\n}\n\nexport async function convertStandardSchemaToJsonSchema(\n schema?: StandardSchemaV1,\n componentCollector?: {\n addSchema(id: string, schema: any): void;\n getReference(id: string): { $ref: string };\n },\n): Promise<any> {\n if (!schema) {\n return undefined;\n }\n\n const vendor = schema['~standard']?.vendor;\n if (!isSchemaVendor(vendor)) {\n throw new Error(\n `Unsupported or missing vendor \"${vendor}\" for Standard Schema. Supported vendors are: ${Object.keys(StandardSchemaJsonSchema).join(', ')}`,\n );\n }\n\n const toJSONSchema = StandardSchemaJsonSchema[vendor];\n const jsonSchema = await toJSONSchema(schema);\n\n // Extract and convert $defs to components\n return extractAndConvertDefs(jsonSchema, componentCollector);\n}\n\nexport async function getZodMetadata(\n schema: StandardSchemaV1,\n): Promise<SchemaMeta | undefined> {\n const { ZodObject } = await import('zod/v4');\n\n if (schema instanceof ZodObject) {\n return schema.meta();\n }\n\n return undefined;\n}\n\nexport async function getSchemaMetadata(\n schema: StandardSchemaV1,\n): Promise<SchemaMeta | undefined> {\n const vendor = schema['~standard']?.vendor;\n\n if (vendor === 'zod') {\n return getZodMetadata(schema);\n }\n\n return undefined;\n}\n\ninterface SchemaMeta {\n id?: string;\n}\n\nexport async function convertSchemaWithComponents(\n schema: StandardSchemaV1 | undefined,\n componentCollector?: {\n addSchema(id: string, schema: any): void;\n getReference(id: string): { $ref: string };\n },\n): Promise<any> {\n if (!schema) {\n return undefined;\n }\n\n // Convert to JSON Schema with component collector to handle $defs\n const jsonSchema = await convertStandardSchemaToJsonSchema(\n schema,\n componentCollector,\n );\n\n if (!componentCollector) {\n return jsonSchema;\n }\n\n // Check if this schema has metadata with an ID\n const metadata = await getSchemaMetadata(schema);\n\n // Also check if the JSON Schema itself has an id field (from Zod's meta)\n const schemaId = metadata?.id || jsonSchema?.id;\n\n if (schemaId) {\n // Remove the id from the schema before adding to components\n const { id, ...schemaWithoutId } = jsonSchema;\n // Add this schema to components and return a reference\n componentCollector.addSchema(schemaId, schemaWithoutId);\n return componentCollector.getReference(schemaId);\n }\n\n return jsonSchema;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"conversion-CFlFrSru.cjs","names":["vendor?: string","StandardSchemaJsonSchema: VendorConvertors","jsonSchema: any","componentCollector?: {\n addSchema(id: string, schema: any): void;\n getReference(id: string): { $ref: string };\n }","schema: any","processed: any","schema?: StandardSchemaV1","schema: StandardSchemaV1","schema: StandardSchemaV1 | undefined"],"sources":["../src/conversion.ts"],"sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec';\n\nexport enum SchemaVendor {\n zod = 'zod',\n valibot = 'valibot',\n}\n\nexport type VendorConvertor = (schema: any) => Promise<any>;\nexport type VendorConvertors = Record<SchemaVendor, VendorConvertor>;\n\nfunction isSchemaVendor(vendor?: string): vendor is SchemaVendor {\n if (!vendor) {\n return false;\n }\n\n return Object.values(SchemaVendor).includes(vendor as SchemaVendor);\n}\n\nexport const StandardSchemaJsonSchema: VendorConvertors = {\n zod: async (schema): Promise<any> => {\n try {\n const { z } = await import('zod/v4').catch(() => ({ z: {} }));\n\n if ('toJSONSchema' in z && typeof z.toJSONSchema === 'function') {\n return z.toJSONSchema(schema);\n }\n const { zodToJsonSchema } = await import('zod-to-json-schema');\n\n const result = zodToJsonSchema(schema, {\n removeAdditionalStrategy: 'strict',\n });\n\n return result;\n } catch {\n // Fallback to basic conversion if zod-to-json-schema is not available\n return { type: 'object' };\n }\n },\n valibot: async (schema): Promise<any> => {\n const { toJsonSchema } = await import('@valibot/to-json-schema');\n return toJsonSchema(schema as any);\n },\n};\n\nfunction extractAndConvertDefs(\n jsonSchema: any,\n componentCollector?: {\n addSchema(id: string, schema: any): void;\n getReference(id: string): { $ref: string };\n },\n): any {\n if (!jsonSchema || typeof jsonSchema !== 'object') {\n return jsonSchema;\n }\n\n // Process the schema recursively to update references\n const processSchema = (schema: any): any => {\n if (!schema || typeof schema !== 'object') {\n return schema;\n }\n\n // Handle $ref\n if (schema.$ref && typeof schema.$ref === 'string') {\n // Convert #/$defs/X to #/components/schemas/X\n if (schema.$ref.startsWith('#/$defs/')) {\n const refName = schema.$ref.replace('#/$defs/', '');\n return componentCollector\n ? componentCollector.getReference(refName)\n : schema;\n }\n return schema;\n }\n\n // Handle arrays\n if (Array.isArray(schema)) {\n return schema.map(processSchema);\n }\n\n // Process all properties recursively\n const processed: any = {};\n for (const [key, value] of Object.entries(schema)) {\n if (key === '$defs') {\n // Skip $defs as they've been extracted\n continue;\n }\n processed[key] = processSchema(value);\n }\n return processed;\n };\n\n // Extract $defs if present\n if (jsonSchema.$defs && componentCollector) {\n for (const [defName, defSchema] of Object.entries(jsonSchema.$defs)) {\n // Process the definition recursively to handle nested $refs\n const processedDefSchema = processSchema(defSchema);\n // Add each definition to the component collector\n componentCollector.addSchema(defName, processedDefSchema);\n }\n }\n\n // Process the schema and remove $defs\n const { $defs, ...schemaWithoutDefs } = jsonSchema;\n return processSchema(schemaWithoutDefs);\n}\n\nexport async function convertStandardSchemaToJsonSchema(\n schema?: StandardSchemaV1,\n componentCollector?: {\n addSchema(id: string, schema: any): void;\n getReference(id: string): { $ref: string };\n },\n): Promise<any> {\n if (!schema) {\n return undefined;\n }\n\n const vendor = schema['~standard']?.vendor;\n if (!isSchemaVendor(vendor)) {\n throw new Error(\n `Unsupported or missing vendor \"${vendor}\" for Standard Schema. Supported vendors are: ${Object.keys(StandardSchemaJsonSchema).join(', ')}`,\n );\n }\n\n const toJSONSchema = StandardSchemaJsonSchema[vendor];\n const jsonSchema = await toJSONSchema(schema);\n\n // Extract and convert $defs to components\n return extractAndConvertDefs(jsonSchema, componentCollector);\n}\n\nexport async function getZodMetadata(\n schema: StandardSchemaV1,\n): Promise<SchemaMeta | undefined> {\n const { ZodObject } = await import('zod/v4');\n\n if (schema instanceof ZodObject) {\n return schema.meta();\n }\n\n return undefined;\n}\n\nexport async function getSchemaMetadata(\n schema: StandardSchemaV1,\n): Promise<SchemaMeta | undefined> {\n const vendor = schema['~standard']?.vendor;\n\n if (vendor === 'zod') {\n return getZodMetadata(schema);\n }\n\n return undefined;\n}\n\ninterface SchemaMeta {\n id?: string;\n}\n\nexport async function convertSchemaWithComponents(\n schema: StandardSchemaV1 | undefined,\n componentCollector?: {\n addSchema(id: string, schema: any): void;\n getReference(id: string): { $ref: string };\n },\n): Promise<any> {\n if (!schema) {\n return undefined;\n }\n\n // Convert to JSON Schema with component collector to handle $defs\n const jsonSchema = await convertStandardSchemaToJsonSchema(\n schema,\n componentCollector,\n );\n\n if (!componentCollector) {\n return jsonSchema;\n }\n\n // Check if this schema has metadata with an ID\n const metadata = await getSchemaMetadata(schema);\n\n // Also check if the JSON Schema itself has an id field (from Zod's meta)\n const schemaId = metadata?.id || jsonSchema?.id;\n\n if (schemaId) {\n // Remove the id from the schema before adding to components\n const { id, ...schemaWithoutId } = jsonSchema;\n // Add this schema to components and return a reference\n componentCollector.addSchema(schemaId, schemaWithoutId);\n return componentCollector.getReference(schemaId);\n }\n\n return jsonSchema;\n}\n"],"mappings":"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"}
|