@@ -10,8 +10,9 @@ interface JSONSchemaGeneratorParams {
10
10
metadata ?: $ZodRegistry < Record < string , any > > ;
11
11
/** The JSON Schema version to target.
12
12
* - `"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" ;
15
16
/** How to handle unrepresentable types.
16
17
* - `"throw"` — Default. Unrepresentable types throw an error
17
18
* - `"any"` — Unrepresentable types become `{}` */
@@ -71,7 +72,7 @@ interface Seen {
71
72
72
73
export class JSONSchemaGenerator {
73
74
metadataRegistry : $ZodRegistry < Record < string , any > > ;
74
- target : "draft-7" | "draft-2020-12" ;
75
+ target : "draft-4" | "draft- 7" | "draft-2020-12" ;
75
76
unrepresentable : "throw" | "any" ;
76
77
override : ( ctx : {
77
78
zodSchema : schemas . $ZodTypes ;
@@ -163,7 +164,7 @@ export class JSONSchemaGenerator {
163
164
else if ( regexes . length > 1 ) {
164
165
result . schema . allOf = [
165
166
...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 ) : { } ) ,
167
168
pattern : regex . source ,
168
169
} ) ) ,
169
170
] ;
@@ -178,19 +179,33 @@ export class JSONSchemaGenerator {
178
179
if ( typeof format === "string" && format . includes ( "int" ) ) json . type = "integer" ;
179
180
else json . type = "number" ;
180
181
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
+ }
182
190
if ( typeof minimum === "number" ) {
183
191
json . minimum = minimum ;
184
- if ( typeof exclusiveMinimum === "number" ) {
192
+ if ( typeof exclusiveMinimum === "number" && this . target !== "draft-4" ) {
185
193
if ( exclusiveMinimum >= minimum ) delete json . minimum ;
186
194
else delete json . exclusiveMinimum ;
187
195
}
188
196
}
189
197
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
+ }
191
206
if ( typeof maximum === "number" ) {
192
207
json . maximum = maximum ;
193
- if ( typeof exclusiveMaximum === "number" ) {
208
+ if ( typeof exclusiveMaximum === "number" && this . target !== "draft-4" ) {
194
209
if ( exclusiveMaximum <= maximum ) delete json . maximum ;
195
210
else delete json . exclusiveMaximum ;
196
211
}
@@ -379,7 +394,12 @@ export class JSONSchemaGenerator {
379
394
case "record" : {
380
395
const json : JSONSchema . ObjectSchema = _json as any ;
381
396
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
+ }
383
403
json . additionalProperties = this . process ( def . valueType , {
384
404
...params ,
385
405
path : [ ...params . path , "additionalProperties" ] ,
@@ -432,7 +452,11 @@ export class JSONSchemaGenerator {
432
452
} else if ( vals . length === 1 ) {
433
453
const val = vals [ 0 ] ! ;
434
454
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
+ }
436
460
} else {
437
461
if ( vals . every ( ( v ) => typeof v === "number" ) ) json . type = "number" ;
438
462
if ( vals . every ( ( v ) => typeof v === "string" ) ) json . type = "string" ;
@@ -749,7 +773,7 @@ export class JSONSchemaGenerator {
749
773
750
774
// merge referenced schema into current
751
775
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" ) ) {
753
777
schema . allOf = schema . allOf ?? [ ] ;
754
778
schema . allOf . push ( refSchema ) ;
755
779
} else {
@@ -776,6 +800,8 @@ export class JSONSchemaGenerator {
776
800
result . $schema = "https://json-schema.org/draft/2020-12/schema" ;
777
801
} else if ( this . target === "draft-7" ) {
778
802
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#" ;
779
805
} else {
780
806
// @ts -ignore
781
807
console . warn ( `Invalid target: ${ this . target } ` ) ;
0 commit comments