@@ -24,17 +24,32 @@ export interface ConversionResult {
2424
2525/**
2626 * Convert a schema from various validation libraries to JSON Schema
27+ *
28+ * This function attempts to convert schemas without requiring external dependencies to be bundled.
29+ * It will only succeed if:
30+ * 1. The schema has built-in JSON Schema conversion (ArkType, Zod 4, TypeBox)
31+ * 2. The required conversion library is available at runtime (zod-to-json-schema, @sodaru/yup-to-json-schema, etc.)
32+ *
33+ * @param schema The schema to convert
34+ * @param options Optional conversion options
35+ * @returns The conversion result or undefined if conversion is not possible
2736 */
2837export function schemaToJsonSchema ( schema : Schema , options ?: ConversionOptions ) : ConversionResult | undefined {
2938 const parser = schema as any ;
3039
31- // Check if schema has a built-in toJsonSchema method (e.g., ArkType)
40+ // Check if schema has a built-in toJsonSchema method (e.g., ArkType, Zod 4 )
3241 if ( typeof parser . toJsonSchema === "function" ) {
33- const jsonSchema = parser . toJsonSchema ( ) ;
34- return {
35- jsonSchema : options ?. additionalProperties ? { ...jsonSchema , ...options . additionalProperties } : jsonSchema ,
36- schemaType : 'arktype'
37- } ;
42+ try {
43+ const jsonSchema = parser . toJsonSchema ( ) ;
44+ // Determine if it's Zod or ArkType based on other methods
45+ const schemaType = ( typeof parser . parseAsync === "function" || typeof parser . parse === "function" ) ? 'zod' : 'arktype' ;
46+ return {
47+ jsonSchema : options ?. additionalProperties ? { ...jsonSchema , ...options . additionalProperties } : jsonSchema ,
48+ schemaType
49+ } ;
50+ } catch ( error ) {
51+ // If toJsonSchema fails, continue to other checks
52+ }
3853 }
3954
4055 // Check if it's a TypeBox schema (has Static and Kind symbols)
@@ -46,106 +61,116 @@ export function schemaToJsonSchema(schema: Schema, options?: ConversionOptions):
4661 } ;
4762 }
4863
49- // Check if it's a Zod schema
64+ // For schemas that need external libraries, we need to check if they're available
65+ // This approach avoids bundling the dependencies while still allowing runtime usage
66+
67+ // Check if it's a Zod schema (without built-in toJsonSchema)
5068 if ( typeof parser . parseAsync === "function" || typeof parser . parse === "function" ) {
51- // Check if it's Zod 4 with built-in toJsonSchema method
52- if ( typeof parser . toJsonSchema === "function" ) {
53- try {
54- const jsonSchema = parser . toJsonSchema ( ) ;
55- return {
56- jsonSchema : options ?. additionalProperties ? { ...jsonSchema , ...options . additionalProperties } : jsonSchema ,
57- schemaType : 'zod'
58- } ;
59- } catch ( error ) {
60- console . warn ( 'Failed to convert Zod 4 schema using built-in toJsonSchema:' , error ) ;
61- }
62- }
63-
64- // Fall back to zod-to-json-schema library (for Zod 3 or if built-in method fails)
6569 try {
66- const { zodToJsonSchema } = require ( 'zod-to-json-schema' ) ;
67- const jsonSchema = options ?. name
68- ? zodToJsonSchema ( parser , options . name )
69- : zodToJsonSchema ( parser ) ;
70-
71- if ( jsonSchema && typeof jsonSchema === 'object' && '$schema' in jsonSchema ) {
72- // Remove the $schema property as it's not needed for our use case
73- const { $schema, ...rest } = jsonSchema as any ;
70+ // Try to access zod-to-json-schema if it's available
71+ // @ts -ignore - This is intentionally dynamic
72+ if ( typeof globalThis . __zodToJsonSchema !== 'undefined' ) {
73+ // @ts -ignore
74+ const { zodToJsonSchema } = globalThis . __zodToJsonSchema ;
75+ const jsonSchema = options ?. name
76+ ? zodToJsonSchema ( parser , options . name )
77+ : zodToJsonSchema ( parser ) ;
78+
79+ if ( jsonSchema && typeof jsonSchema === 'object' && '$schema' in jsonSchema ) {
80+ const { $schema, ...rest } = jsonSchema as any ;
81+ return {
82+ jsonSchema : options ?. additionalProperties ? { ...rest , ...options . additionalProperties } : rest ,
83+ schemaType : 'zod'
84+ } ;
85+ }
86+
7487 return {
75- jsonSchema : options ?. additionalProperties ? { ...rest , ...options . additionalProperties } : rest ,
88+ jsonSchema : options ?. additionalProperties ? { ...jsonSchema , ...options . additionalProperties } : jsonSchema ,
7689 schemaType : 'zod'
7790 } ;
7891 }
79-
80- return {
81- jsonSchema : options ?. additionalProperties ? { ...jsonSchema , ...options . additionalProperties } : jsonSchema ,
82- schemaType : 'zod'
83- } ;
8492 } catch ( error ) {
85- console . warn ( 'Failed to convert Zod schema to JSON Schema using zod-to-json-schema:' , error ) ;
86- return undefined ;
93+ // Library not available
8794 }
8895 }
8996
9097 // Check if it's a Yup schema
9198 if ( typeof parser . validateSync === "function" && typeof parser . describe === "function" ) {
9299 try {
93- const { convertSchema } = require ( '@sodaru/yup-to-json-schema' ) ;
94- const jsonSchema = convertSchema ( parser ) ;
95- return {
96- jsonSchema : options ?. additionalProperties ? { ...jsonSchema , ...options . additionalProperties } : jsonSchema ,
97- schemaType : 'yup'
98- } ;
100+ // @ts -ignore
101+ if ( typeof globalThis . __yupToJsonSchema !== 'undefined' ) {
102+ // @ts -ignore
103+ const { convertSchema } = globalThis . __yupToJsonSchema ;
104+ const jsonSchema = convertSchema ( parser ) ;
105+ return {
106+ jsonSchema : options ?. additionalProperties ? { ...jsonSchema , ...options . additionalProperties } : jsonSchema ,
107+ schemaType : 'yup'
108+ } ;
109+ }
99110 } catch ( error ) {
100- console . warn ( 'Failed to convert Yup schema to JSON Schema:' , error ) ;
101- return undefined ;
111+ // Library not available
102112 }
103113 }
104114
105115 // Check if it's an Effect schema
106116 if ( parser . _tag === "Schema" || parser . _tag === "SchemaClass" || typeof parser . ast === "function" ) {
107117 try {
108- // Try to load Effect's JSONSchema module
109- const effectModule = require ( 'effect' ) ;
110- const schemaModule = require ( '@effect/schema' ) ;
111-
112- if ( effectModule ?. JSONSchema && schemaModule ?. JSONSchema ) {
113- const JSONSchema = schemaModule . JSONSchema || effectModule . JSONSchema ;
118+ // @ts -ignore
119+ if ( typeof globalThis . __effectJsonSchema !== 'undefined' ) {
120+ // @ts -ignore
121+ const { JSONSchema } = globalThis . __effectJsonSchema ;
114122 const jsonSchema = JSONSchema . make ( parser ) ;
115123 return {
116124 jsonSchema : options ?. additionalProperties ? { ...jsonSchema , ...options . additionalProperties } : jsonSchema ,
117125 schemaType : 'effect'
118126 } ;
119127 }
120128 } catch ( error ) {
121- console . warn ( 'Failed to convert Effect schema to JSON Schema:' , error ) ;
122- return undefined ;
129+ // Library not available
123130 }
124131 }
125132
126- // Check if it's a Valibot schema
127- if ( typeof parser === "function" && parser . _def ?. kind !== undefined ) {
128- // Valibot doesn't have built-in JSON Schema conversion yet
129- // We could implement a basic converter for common types
130- return undefined ;
131- }
133+ // Future schema types can be added here...
132134
133- // Check if it's a Superstruct schema
134- if ( typeof parser . create === "function" && parser . TYPE !== undefined ) {
135- // Superstruct doesn't have built-in JSON Schema conversion
136- // We could implement a basic converter for common types
137- return undefined ;
135+ // Unknown schema type
136+ return undefined ;
137+ }
138+
139+ /**
140+ * Initialize the schema conversion libraries
141+ * This should be called by the consuming application if they want to enable
142+ * conversion for schemas that don't have built-in JSON Schema support
143+ */
144+ export async function initializeSchemaConverters ( ) : Promise < void > {
145+ try {
146+ // @ts -ignore
147+ globalThis . __zodToJsonSchema = await import ( 'zod-to-json-schema' ) ;
148+ } catch {
149+ // Zod conversion not available
138150 }
139151
140- // Check if it's a Runtypes schema
141- if ( typeof parser . guard === "function" && parser . _tag !== undefined ) {
142- // Runtypes doesn't have built-in JSON Schema conversion
143- // We could implement a basic converter for common types
144- return undefined ;
152+ try {
153+ // @ts -ignore
154+ globalThis . __yupToJsonSchema = await import ( '@sodaru/yup-to-json-schema' ) ;
155+ } catch {
156+ // Yup conversion not available
145157 }
146158
147- // Unknown schema type
148- return undefined ;
159+ try {
160+ // Try Effect first, then @effect/schema
161+ let module ;
162+ try {
163+ module = await import ( 'effect' ) ;
164+ } catch {
165+ module = await import ( '@effect/schema' ) ;
166+ }
167+ if ( module ?. JSONSchema ) {
168+ // @ts -ignore
169+ globalThis . __effectJsonSchema = { JSONSchema : module . JSONSchema } ;
170+ }
171+ } catch {
172+ // Effect conversion not available
173+ }
149174}
150175
151176/**
@@ -162,4 +187,22 @@ export function canConvertSchema(schema: Schema): boolean {
162187export function detectSchemaType ( schema : Schema ) : ConversionResult [ 'schemaType' ] {
163188 const result = schemaToJsonSchema ( schema ) ;
164189 return result ?. schemaType ?? 'unknown' ;
190+ }
191+
192+ /**
193+ * Check if the conversion libraries are initialized
194+ */
195+ export function areConvertersInitialized ( ) : {
196+ zod : boolean ;
197+ yup : boolean ;
198+ effect : boolean ;
199+ } {
200+ return {
201+ // @ts -ignore
202+ zod : typeof globalThis . __zodToJsonSchema !== 'undefined' ,
203+ // @ts -ignore
204+ yup : typeof globalThis . __yupToJsonSchema !== 'undefined' ,
205+ // @ts -ignore
206+ effect : typeof globalThis . __effectJsonSchema !== 'undefined' ,
207+ } ;
165208}
0 commit comments