@@ -10,8 +10,9 @@ interface JSONSchemaGeneratorParams {
1010 metadata ?: $ZodRegistry < Record < string , any > > ;
1111 /** The JSON Schema version to target.
1212 * - `"draft-2020-12"` — Default. JSON Schema Draft 2020-12
13- * - `"draft-7"` — JSON Schema Draft 7 */
14- target ?: "draft-7" | "draft-2020-12" ;
13+ * - `"draft-7"` — JSON Schema Draft 7
14+ * - `"draft-4"` — JSON Schema Draft 4 */
15+ target ?: "draft-4" | "draft-7" | "draft-2020-12" ;
1516 /** How to handle unrepresentable types.
1617 * - `"throw"` — Default. Unrepresentable types throw an error
1718 * - `"any"` — Unrepresentable types become `{}` */
@@ -71,7 +72,7 @@ interface Seen {
7172
7273export class JSONSchemaGenerator {
7374 metadataRegistry : $ZodRegistry < Record < string , any > > ;
74- target : "draft-7" | "draft-2020-12" ;
75+ target : "draft-4" | "draft- 7" | "draft-2020-12" ;
7576 unrepresentable : "throw" | "any" ;
7677 override : ( ctx : {
7778 zodSchema : schemas . $ZodTypes ;
@@ -163,7 +164,7 @@ export class JSONSchemaGenerator {
163164 else if ( regexes . length > 1 ) {
164165 result . schema . allOf = [
165166 ...regexes . map ( ( regex ) => ( {
166- ...( this . target === "draft-7" ? ( { type : "string" } as const ) : { } ) ,
167+ ...( this . target === "draft-7" || this . target === "draft-4" ? ( { type : "string" } as const ) : { } ) ,
167168 pattern : regex . source ,
168169 } ) ) ,
169170 ] ;
@@ -178,19 +179,33 @@ export class JSONSchemaGenerator {
178179 if ( typeof format === "string" && format . includes ( "int" ) ) json . type = "integer" ;
179180 else json . type = "number" ;
180181
181- if ( typeof exclusiveMinimum === "number" ) json . exclusiveMinimum = exclusiveMinimum ;
182+ if ( typeof exclusiveMinimum === "number" ) {
183+ if ( this . target === "draft-4" ) {
184+ json . minimum = exclusiveMinimum ;
185+ json . exclusiveMinimum = true ;
186+ } else {
187+ json . exclusiveMinimum = exclusiveMinimum ;
188+ }
189+ }
182190 if ( typeof minimum === "number" ) {
183191 json . minimum = minimum ;
184- if ( typeof exclusiveMinimum === "number" ) {
192+ if ( typeof exclusiveMinimum === "number" && this . target !== "draft-4" ) {
185193 if ( exclusiveMinimum >= minimum ) delete json . minimum ;
186194 else delete json . exclusiveMinimum ;
187195 }
188196 }
189197
190- if ( typeof exclusiveMaximum === "number" ) json . exclusiveMaximum = exclusiveMaximum ;
198+ if ( typeof exclusiveMaximum === "number" ) {
199+ if ( this . target === "draft-4" ) {
200+ json . maximum = exclusiveMaximum ;
201+ json . exclusiveMaximum = true ;
202+ } else {
203+ json . exclusiveMaximum = exclusiveMaximum ;
204+ }
205+ }
191206 if ( typeof maximum === "number" ) {
192207 json . maximum = maximum ;
193- if ( typeof exclusiveMaximum === "number" ) {
208+ if ( typeof exclusiveMaximum === "number" && this . target !== "draft-4" ) {
194209 if ( exclusiveMaximum <= maximum ) delete json . maximum ;
195210 else delete json . exclusiveMaximum ;
196211 }
@@ -379,7 +394,12 @@ export class JSONSchemaGenerator {
379394 case "record" : {
380395 const json : JSONSchema . ObjectSchema = _json as any ;
381396 json . type = "object" ;
382- json . propertyNames = this . process ( def . keyType , { ...params , path : [ ...params . path , "propertyNames" ] } ) ;
397+ if ( this . target !== "draft-4" ) {
398+ json . propertyNames = this . process ( def . keyType , {
399+ ...params ,
400+ path : [ ...params . path , "propertyNames" ] ,
401+ } ) ;
402+ }
383403 json . additionalProperties = this . process ( def . valueType , {
384404 ...params ,
385405 path : [ ...params . path , "additionalProperties" ] ,
@@ -432,7 +452,11 @@ export class JSONSchemaGenerator {
432452 } else if ( vals . length === 1 ) {
433453 const val = vals [ 0 ] ! ;
434454 json . type = val === null ? ( "null" as const ) : ( typeof val as any ) ;
435- json . const = val ;
455+ if ( this . target === "draft-4" ) {
456+ json . enum = [ val ] ;
457+ } else {
458+ json . const = val ;
459+ }
436460 } else {
437461 if ( vals . every ( ( v ) => typeof v === "number" ) ) json . type = "number" ;
438462 if ( vals . every ( ( v ) => typeof v === "string" ) ) json . type = "string" ;
@@ -749,7 +773,7 @@ export class JSONSchemaGenerator {
749773
750774 // merge referenced schema into current
751775 const refSchema = this . seen . get ( ref ) ! . schema ;
752- if ( refSchema . $ref && params . target === "draft-7" ) {
776+ if ( refSchema . $ref && ( params . target === "draft-7" || params . target === "draft-4" ) ) {
753777 schema . allOf = schema . allOf ?? [ ] ;
754778 schema . allOf . push ( refSchema ) ;
755779 } else {
@@ -776,6 +800,8 @@ export class JSONSchemaGenerator {
776800 result . $schema = "https://json-schema.org/draft/2020-12/schema" ;
777801 } else if ( this . target === "draft-7" ) {
778802 result . $schema = "http://json-schema.org/draft-07/schema#" ;
803+ } else if ( this . target === "draft-4" ) {
804+ result . $schema = "http://json-schema.org/draft-04/schema#" ;
779805 } else {
780806 // @ts -ignore
781807 console . warn ( `Invalid target: ${ this . target } ` ) ;
0 commit comments