@@ -37,10 +37,7 @@ export type JSONSchema = {
3737 */
3838export function convertJsonSchemaToZod ( schema : JSONSchema ) : z . ZodTypeAny {
3939 // Create a helper function to add metadata like description
40- function addMetadata (
41- zodSchema : z . ZodTypeAny ,
42- jsonSchema : JSONSchema ,
43- ) : z . ZodTypeAny {
40+ function addMetadata ( zodSchema : z . ZodTypeAny , jsonSchema : JSONSchema ) : z . ZodTypeAny {
4441 if ( jsonSchema . description ) {
4542 zodSchema = zodSchema . describe ( jsonSchema . description ) ;
4643 }
@@ -72,11 +69,11 @@ export function convertJsonSchemaToZod(schema: JSONSchema): z.ZodTypeAny {
7269 if ( schema . enum . length === 0 ) {
7370 return addMetadata ( z . string ( ) , schema ) ;
7471 }
75-
72+
7673 // Since we know this is a string type, we can safely cast enum values
7774 return addMetadata ( z . enum ( schema . enum as [ string , ...string [ ] ] ) , schema ) ;
7875 }
79-
76+
8077 let stringSchema = z . string ( ) ;
8178
8279 // Apply string-specific constraints
@@ -101,22 +98,22 @@ export function convertJsonSchemaToZod(schema: JSONSchema): z.ZodTypeAny {
10198 if ( schema . enum . length === 0 ) {
10299 return addMetadata ( z . number ( ) , schema ) ;
103100 }
104-
101+
105102 // For numbers we need a union of literals since z.enum only works with strings
106- const options = schema . enum . map ( val => z . literal ( val as number ) ) ;
107-
103+ const options = schema . enum . map ( ( val ) => z . literal ( val as number ) ) ;
104+
108105 // Handle single option enum specially
109106 if ( options . length === 1 ) {
110107 return addMetadata ( options [ 0 ] , schema ) ;
111108 }
112-
109+
113110 // For multiple options, create a union
114111 if ( options . length >= 2 ) {
115112 const unionSchema = z . union ( [ options [ 0 ] , options [ 1 ] , ...options . slice ( 2 ) ] ) ;
116113 return addMetadata ( unionSchema , schema ) ;
117114 }
118115 }
119-
116+
120117 let numberSchema = schema . type === "integer" ? z . number ( ) . int ( ) : z . number ( ) ;
121118
122119 // Apply number-specific constraints
@@ -145,14 +142,14 @@ export function convertJsonSchemaToZod(schema: JSONSchema): z.ZodTypeAny {
145142 if ( schema . enum . length === 0 ) {
146143 return addMetadata ( z . boolean ( ) , schema ) ;
147144 }
148-
149- const options = schema . enum . map ( val => z . literal ( val as boolean ) ) ;
150-
145+
146+ const options = schema . enum . map ( ( val ) => z . literal ( val as boolean ) ) ;
147+
151148 // Handle single option enum specially
152149 if ( options . length === 1 ) {
153150 return addMetadata ( options [ 0 ] , schema ) ;
154151 }
155-
152+
156153 // For multiple options, create a union
157154 if ( options . length >= 2 ) {
158155 const unionSchema = z . union ( [ options [ 0 ] , options [ 1 ] , ...options . slice ( 2 ) ] ) ;
@@ -167,9 +164,7 @@ export function convertJsonSchemaToZod(schema: JSONSchema): z.ZodTypeAny {
167164 const shape : Record < string , z . ZodTypeAny > = { } ;
168165
169166 // Process each property
170- for ( const [ key , propSchema ] of Object . entries (
171- schema . properties ,
172- ) ) {
167+ for ( const [ key , propSchema ] of Object . entries ( schema . properties ) ) {
173168 shape [ key ] = convertJsonSchemaToZod ( propSchema ) ;
174169 }
175170
@@ -192,7 +187,7 @@ export function convertJsonSchemaToZod(schema: JSONSchema): z.ZodTypeAny {
192187
193188 // Create the schema with or without passthrough based on additionalProperties
194189 let zodSchema : z . ZodTypeAny ;
195-
190+
196191 // By default, JSON Schema allows additional properties, so use passthrough
197192 // unless additionalProperties is explicitly set to false
198193 if ( schema . additionalProperties !== false ) {
@@ -227,11 +222,11 @@ export function convertJsonSchemaToZod(schema: JSONSchema): z.ZodTypeAny {
227222 const seen = new Set ( ) ;
228223 return items . every ( ( item ) => {
229224 // For primitive values, we can use a Set directly
230- if ( typeof item === ' string' || typeof item === ' number' || typeof item === ' boolean' ) {
225+ if ( typeof item === " string" || typeof item === " number" || typeof item === " boolean" ) {
231226 if ( seen . has ( item ) ) return false ;
232227 seen . add ( item ) ;
233228 return true ;
234- }
229+ }
235230 // For objects, we'd need more complex comparison
236231 // For simplicity, we stringfy objects for comparison
237232 const serialized = JSON . stringify ( item ) ;
@@ -240,7 +235,7 @@ export function convertJsonSchemaToZod(schema: JSONSchema): z.ZodTypeAny {
240235 return true ;
241236 } ) ;
242237 } ,
243- { message : "Array items must be unique" }
238+ { message : "Array items must be unique" } ,
244239 ) ;
245240 }
246241
@@ -256,22 +251,22 @@ export function convertJsonSchemaToZod(schema: JSONSchema): z.ZodTypeAny {
256251 if ( schema . enum . length === 0 ) {
257252 return addMetadata ( z . never ( ) , schema ) ;
258253 }
259-
254+
260255 // Check if all enum values are strings
261- const allStrings = schema . enum . every ( val => typeof val === ' string' ) ;
262-
256+ const allStrings = schema . enum . every ( ( val ) => typeof val === " string" ) ;
257+
263258 if ( allStrings ) {
264259 // If all values are strings, use z.enum which is more efficient
265260 return addMetadata ( z . enum ( schema . enum as [ string , ...string [ ] ] ) , schema ) ;
266261 } else {
267262 // For mixed types or non-strings, use a union of literals
268- const options = schema . enum . map ( val => z . literal ( val ) ) ;
269-
263+ const options = schema . enum . map ( ( val ) => z . literal ( val ) ) ;
264+
270265 // Handle single option enum specially
271266 if ( options . length === 1 ) {
272267 return addMetadata ( options [ 0 ] , schema ) ;
273268 }
274-
269+
275270 // For multiple options, create a union
276271 if ( options . length >= 2 ) {
277272 const unionSchema = z . union ( [ options [ 0 ] , options [ 1 ] , ...options . slice ( 2 ) ] ) ;
@@ -283,17 +278,13 @@ export function convertJsonSchemaToZod(schema: JSONSchema): z.ZodTypeAny {
283278 // Handle combinations
284279 if ( schema . anyOf && schema . anyOf . length >= 2 ) {
285280 const schemas = schema . anyOf . map ( convertJsonSchemaToZod ) ;
286- return addMetadata (
287- z . union ( [ schemas [ 0 ] , schemas [ 1 ] , ...schemas . slice ( 2 ) ] ) ,
288- schema ,
289- ) ;
281+ return addMetadata ( z . union ( [ schemas [ 0 ] , schemas [ 1 ] , ...schemas . slice ( 2 ) ] ) , schema ) ;
290282 }
291283
292284 if ( schema . allOf ) {
293285 return addMetadata (
294286 schema . allOf . reduce (
295- ( acc : z . ZodTypeAny , s : JSONSchema ) =>
296- z . intersection ( acc , convertJsonSchemaToZod ( s ) ) ,
287+ ( acc : z . ZodTypeAny , s : JSONSchema ) => z . intersection ( acc , convertJsonSchemaToZod ( s ) ) ,
297288 z . object ( { } ) ,
298289 ) ,
299290 schema ,
@@ -302,10 +293,7 @@ export function convertJsonSchemaToZod(schema: JSONSchema): z.ZodTypeAny {
302293
303294 if ( schema . oneOf && schema . oneOf . length >= 2 ) {
304295 const schemas = schema . oneOf . map ( convertJsonSchemaToZod ) ;
305- return addMetadata (
306- z . union ( [ schemas [ 0 ] , schemas [ 1 ] , ...schemas . slice ( 2 ) ] ) ,
307- schema ,
308- ) ;
296+ return addMetadata ( z . union ( [ schemas [ 0 ] , schemas [ 1 ] , ...schemas . slice ( 2 ) ] ) , schema ) ;
309297 }
310298
311299 // Default fallback
@@ -326,6 +314,8 @@ export function jsonSchemaObjectToZodRawShape(schema: JSONSchema): z.ZodRawShape
326314
327315 // Process each property
328316 for ( const [ key , propSchema ] of Object . entries ( schema . properties ?? { } ) ) {
317+ if ( propSchema === undefined ) continue ;
318+
329319 let zodSchema = convertJsonSchemaToZod ( propSchema ) ;
330320
331321 // If there's a required array and the field is not in it, make it optional
@@ -343,4 +333,4 @@ export function jsonSchemaObjectToZodRawShape(schema: JSONSchema): z.ZodRawShape
343333 }
344334
345335 return raw ;
346- }
336+ }
0 commit comments