zod 4.0.12 → 4.0.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod",
3
- "version": "4.0.12",
3
+ "version": "4.0.14",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Colin McDonnell <zod@colinhacks.com>",
@@ -121,3 +121,16 @@ test("pipe optionality inside objects", () => {
121
121
  e: string;
122
122
  }>();
123
123
  });
124
+
125
+ test("optional prop with pipe", () => {
126
+ const schema = z.object({
127
+ id: z
128
+ .union([z.number(), z.string().nullish()])
129
+ .transform((val) => (val === null || val === undefined ? val : Number(val)))
130
+ .pipe(z.number())
131
+ .optional(),
132
+ });
133
+
134
+ schema.parse({});
135
+ schema.parse({}, { jitless: true });
136
+ });
@@ -366,6 +366,21 @@ test("object utilities with recursive types", () => {
366
366
  ]);
367
367
  });
368
368
 
369
+ test("tuple with recursive types", () => {
370
+ const TaskListNodeSchema = z.strictObject({
371
+ type: z.literal("taskList"),
372
+ get content() {
373
+ return z.array(z.tuple([TaskListNodeSchema, z.union([TaskListNodeSchema])])).min(1);
374
+ },
375
+ });
376
+ type TaskListNodeSchema = z.infer<typeof TaskListNodeSchema>;
377
+ type _TaskListNodeSchema = {
378
+ type: "taskList";
379
+ content: [_TaskListNodeSchema, _TaskListNodeSchema][];
380
+ };
381
+ expectTypeOf<TaskListNodeSchema>().toEqualTypeOf<_TaskListNodeSchema>();
382
+ });
383
+
369
384
  test("recursion compatibility", () => {
370
385
  // array
371
386
  const A = z.object({
@@ -539,6 +539,19 @@ describe("toJSONSchema", () => {
539
539
  `);
540
540
  });
541
541
 
542
+ test("number constraints draft-4", () => {
543
+ expect(z.toJSONSchema(z.number().gt(5).lt(10), { target: "draft-4" })).toMatchInlineSnapshot(`
544
+ {
545
+ "$schema": "http://json-schema.org/draft-04/schema#",
546
+ "exclusiveMaximum": true,
547
+ "exclusiveMinimum": true,
548
+ "maximum": 10,
549
+ "minimum": 5,
550
+ "type": "number",
551
+ }
552
+ `);
553
+ });
554
+
542
555
  test("arrays", () => {
543
556
  expect(z.toJSONSchema(z.array(z.string()))).toMatchInlineSnapshot(`
544
557
  {
@@ -745,6 +758,19 @@ describe("toJSONSchema", () => {
745
758
  `);
746
759
  });
747
760
 
761
+ test("literal draft-4", () => {
762
+ const a = z.literal("hello");
763
+ expect(z.toJSONSchema(a, { target: "draft-4" })).toMatchInlineSnapshot(`
764
+ {
765
+ "$schema": "http://json-schema.org/draft-04/schema#",
766
+ "enum": [
767
+ "hello",
768
+ ],
769
+ "type": "string",
770
+ }
771
+ `);
772
+ });
773
+
748
774
  // pipe
749
775
  test("pipe", () => {
750
776
  const schema = z
@@ -45,7 +45,10 @@ export type Schema =
45
45
  export type _JSONSchema = boolean | JSONSchema;
46
46
  export type JSONSchema = {
47
47
  [k: string]: unknown;
48
- $schema?: "https://json-schema.org/draft/2020-12/schema" | "http://json-schema.org/draft-07/schema#";
48
+ $schema?:
49
+ | "https://json-schema.org/draft/2020-12/schema"
50
+ | "http://json-schema.org/draft-07/schema#"
51
+ | "http://json-schema.org/draft-04/schema#";
49
52
  $id?: string;
50
53
  $anchor?: string;
51
54
  $ref?: string;
@@ -75,9 +78,9 @@ export type JSONSchema = {
75
78
  not?: _JSONSchema;
76
79
  multipleOf?: number;
77
80
  maximum?: number;
78
- exclusiveMaximum?: number;
81
+ exclusiveMaximum?: number | boolean;
79
82
  minimum?: number;
80
- exclusiveMinimum?: number;
83
+ exclusiveMinimum?: number | boolean;
81
84
  maxLength?: number;
82
85
  minLength?: number;
83
86
  pattern?: string;
@@ -1701,6 +1701,8 @@ export interface $ZodObjectInternals<
1701
1701
  propValues: util.PropValues;
1702
1702
  output: $InferObjectOutput<Shape, Config["out"]>;
1703
1703
  input: $InferObjectInput<Shape, Config["in"]>;
1704
+ optin?: "optional" | undefined;
1705
+ optout?: "optional" | undefined;
1704
1706
  }
1705
1707
  export type $ZodLooseShape = Record<string, any>;
1706
1708
 
@@ -2282,9 +2284,12 @@ type TupleOutputTypeWithOptionals<T extends util.TupleItems> = T extends readonl
2282
2284
  export interface $ZodTupleInternals<
2283
2285
  T extends util.TupleItems = readonly $ZodType[],
2284
2286
  Rest extends SomeType | null = $ZodType | null,
2285
- > extends $ZodTypeInternals<$InferTupleOutputType<T, Rest>, $InferTupleInputType<T, Rest>> {
2287
+ > extends _$ZodTypeInternals {
2286
2288
  def: $ZodTupleDef<T, Rest>;
2287
2289
  isst: errors.$ZodIssueInvalidType | errors.$ZodIssueTooBig<unknown[]> | errors.$ZodIssueTooSmall<unknown[]>;
2290
+ // $ZodTypeInternals<$InferTupleOutputType<T, Rest>, $InferTupleInputType<T, Rest>>
2291
+ output: $InferTupleOutputType<T, Rest>;
2292
+ input: $InferTupleInputType<T, Rest>;
2288
2293
  }
2289
2294
 
2290
2295
  export interface $ZodTuple<
@@ -3007,6 +3012,13 @@ export interface $ZodOptional<T extends SomeType = $ZodType> extends $ZodType {
3007
3012
  _zod: $ZodOptionalInternals<T>;
3008
3013
  }
3009
3014
 
3015
+ function handleOptionalResult(result: ParsePayload, input: unknown) {
3016
+ if (result.issues.length && input === undefined) {
3017
+ return { issues: [], value: undefined };
3018
+ }
3019
+ return result;
3020
+ }
3021
+
3010
3022
  export const $ZodOptional: core.$constructor<$ZodOptional> = /*@__PURE__*/ core.$constructor(
3011
3023
  "$ZodOptional",
3012
3024
  (inst, def) => {
@@ -3024,7 +3036,9 @@ export const $ZodOptional: core.$constructor<$ZodOptional> = /*@__PURE__*/ core.
3024
3036
 
3025
3037
  inst._zod.parse = (payload, ctx) => {
3026
3038
  if (def.innerType._zod.optin === "optional") {
3027
- return def.innerType._zod.run(payload, ctx);
3039
+ const result = def.innerType._zod.run(payload, ctx);
3040
+ if (result instanceof Promise) return result.then((r) => handleOptionalResult(r, payload.value));
3041
+ return handleOptionalResult(result, payload.value);
3028
3042
  }
3029
3043
  if (payload.value === undefined) {
3030
3044
  return payload;
@@ -3737,11 +3751,18 @@ export interface $ZodLazy<T extends SomeType = $ZodType> extends $ZodType {
3737
3751
  export const $ZodLazy: core.$constructor<$ZodLazy> = /*@__PURE__*/ core.$constructor("$ZodLazy", (inst, def) => {
3738
3752
  $ZodType.init(inst, def);
3739
3753
 
3754
+ // let _innerType!: any;
3755
+ // util.defineLazy(def, "getter", () => {
3756
+ // if (!_innerType) {
3757
+ // _innerType = def.getter();
3758
+ // }
3759
+ // return () => _innerType;
3760
+ // });
3740
3761
  util.defineLazy(inst._zod, "innerType", () => def.getter() as $ZodType);
3741
3762
  util.defineLazy(inst._zod, "pattern", () => inst._zod.innerType._zod.pattern);
3742
3763
  util.defineLazy(inst._zod, "propValues", () => inst._zod.innerType._zod.propValues);
3743
- util.defineLazy(inst._zod, "optin", () => inst._zod.innerType._zod.optin);
3744
- util.defineLazy(inst._zod, "optout", () => inst._zod.innerType._zod.optout);
3764
+ util.defineLazy(inst._zod, "optin", () => inst._zod.innerType._zod.optin ?? undefined);
3765
+ util.defineLazy(inst._zod, "optout", () => inst._zod.innerType._zod.optout ?? undefined);
3745
3766
  inst._zod.parse = (payload, ctx) => {
3746
3767
  const inner = inst._zod.innerType;
3747
3768
  return inner._zod.run(payload, ctx);
@@ -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}`);
@@ -257,16 +257,21 @@ export function floatSafeRemainder(val: number, step: number): number {
257
257
  return (valInt % stepInt) / 10 ** decCount;
258
258
  }
259
259
 
260
+ const EVALUATING = Symbol("evaluating");
261
+
260
262
  export function defineLazy<T, K extends keyof T>(object: T, key: K, getter: () => T[K]): void {
261
- const set = false;
263
+ let value: T[K] | typeof EVALUATING | undefined = undefined;
262
264
  Object.defineProperty(object, key, {
263
265
  get() {
264
- if (!set) {
265
- const value = getter();
266
- object[key] = value;
267
- return value;
266
+ if (value === EVALUATING) {
267
+ // Circular reference detected, return undefined to break the cycle
268
+ return undefined as T[K];
268
269
  }
269
- throw new Error("cached value already set");
270
+ if (value === undefined) {
271
+ value = EVALUATING;
272
+ value = getter();
273
+ }
274
+ return value;
270
275
  },
271
276
  set(v) {
272
277
  Object.defineProperty(object, key, {
@@ -1,5 +1,5 @@
1
1
  export const version = {
2
2
  major: 4,
3
3
  minor: 0,
4
- patch: 12 as number,
4
+ patch: 14 as number,
5
5
  } as const;
@@ -2,7 +2,7 @@ export type Schema = ObjectSchema | ArraySchema | StringSchema | NumberSchema |
2
2
  export type _JSONSchema = boolean | JSONSchema;
3
3
  export type JSONSchema = {
4
4
  [k: string]: unknown;
5
- $schema?: "https://json-schema.org/draft/2020-12/schema" | "http://json-schema.org/draft-07/schema#";
5
+ $schema?: "https://json-schema.org/draft/2020-12/schema" | "http://json-schema.org/draft-07/schema#" | "http://json-schema.org/draft-04/schema#";
6
6
  $id?: string;
7
7
  $anchor?: string;
8
8
  $ref?: string;
@@ -32,9 +32,9 @@ export type JSONSchema = {
32
32
  not?: _JSONSchema;
33
33
  multipleOf?: number;
34
34
  maximum?: number;
35
- exclusiveMaximum?: number;
35
+ exclusiveMaximum?: number | boolean;
36
36
  minimum?: number;
37
- exclusiveMinimum?: number;
37
+ exclusiveMinimum?: number | boolean;
38
38
  maxLength?: number;
39
39
  minLength?: number;
40
40
  pattern?: string;
@@ -2,7 +2,7 @@ export type Schema = ObjectSchema | ArraySchema | StringSchema | NumberSchema |
2
2
  export type _JSONSchema = boolean | JSONSchema;
3
3
  export type JSONSchema = {
4
4
  [k: string]: unknown;
5
- $schema?: "https://json-schema.org/draft/2020-12/schema" | "http://json-schema.org/draft-07/schema#";
5
+ $schema?: "https://json-schema.org/draft/2020-12/schema" | "http://json-schema.org/draft-07/schema#" | "http://json-schema.org/draft-04/schema#";
6
6
  $id?: string;
7
7
  $anchor?: string;
8
8
  $ref?: string;
@@ -32,9 +32,9 @@ export type JSONSchema = {
32
32
  not?: _JSONSchema;
33
33
  multipleOf?: number;
34
34
  maximum?: number;
35
- exclusiveMaximum?: number;
35
+ exclusiveMaximum?: number | boolean;
36
36
  minimum?: number;
37
- exclusiveMinimum?: number;
37
+ exclusiveMinimum?: number | boolean;
38
38
  maxLength?: number;
39
39
  minLength?: number;
40
40
  pattern?: string;
@@ -1415,6 +1415,12 @@ exports.$ZodTransform = core.$constructor("$ZodTransform", (inst, def) => {
1415
1415
  return payload;
1416
1416
  };
1417
1417
  });
1418
+ function handleOptionalResult(result, input) {
1419
+ if (result.issues.length && input === undefined) {
1420
+ return { issues: [], value: undefined };
1421
+ }
1422
+ return result;
1423
+ }
1418
1424
  exports.$ZodOptional = core.$constructor("$ZodOptional", (inst, def) => {
1419
1425
  exports.$ZodType.init(inst, def);
1420
1426
  inst._zod.optin = "optional";
@@ -1428,7 +1434,10 @@ exports.$ZodOptional = core.$constructor("$ZodOptional", (inst, def) => {
1428
1434
  });
1429
1435
  inst._zod.parse = (payload, ctx) => {
1430
1436
  if (def.innerType._zod.optin === "optional") {
1431
- return def.innerType._zod.run(payload, ctx);
1437
+ const result = def.innerType._zod.run(payload, ctx);
1438
+ if (result instanceof Promise)
1439
+ return result.then((r) => handleOptionalResult(r, payload.value));
1440
+ return handleOptionalResult(result, payload.value);
1432
1441
  }
1433
1442
  if (payload.value === undefined) {
1434
1443
  return payload;
@@ -1675,11 +1684,18 @@ exports.$ZodPromise = core.$constructor("$ZodPromise", (inst, def) => {
1675
1684
  });
1676
1685
  exports.$ZodLazy = core.$constructor("$ZodLazy", (inst, def) => {
1677
1686
  exports.$ZodType.init(inst, def);
1687
+ // let _innerType!: any;
1688
+ // util.defineLazy(def, "getter", () => {
1689
+ // if (!_innerType) {
1690
+ // _innerType = def.getter();
1691
+ // }
1692
+ // return () => _innerType;
1693
+ // });
1678
1694
  util.defineLazy(inst._zod, "innerType", () => def.getter());
1679
1695
  util.defineLazy(inst._zod, "pattern", () => inst._zod.innerType._zod.pattern);
1680
1696
  util.defineLazy(inst._zod, "propValues", () => inst._zod.innerType._zod.propValues);
1681
- util.defineLazy(inst._zod, "optin", () => inst._zod.innerType._zod.optin);
1682
- util.defineLazy(inst._zod, "optout", () => inst._zod.innerType._zod.optout);
1697
+ util.defineLazy(inst._zod, "optin", () => inst._zod.innerType._zod.optin ?? undefined);
1698
+ util.defineLazy(inst._zod, "optout", () => inst._zod.innerType._zod.optout ?? undefined);
1683
1699
  inst._zod.parse = (payload, ctx) => {
1684
1700
  const inner = inst._zod.innerType;
1685
1701
  return inner._zod.run(payload, ctx);
@@ -587,6 +587,8 @@ out Shape extends Readonly<$ZodShape> = Readonly<$ZodShape>, out Config extends
587
587
  propValues: util.PropValues;
588
588
  output: $InferObjectOutput<Shape, Config["out"]>;
589
589
  input: $InferObjectInput<Shape, Config["in"]>;
590
+ optin?: "optional" | undefined;
591
+ optout?: "optional" | undefined;
590
592
  }
591
593
  export type $ZodLooseShape = Record<string, any>;
592
594
  export interface $ZodObject<
@@ -673,9 +675,11 @@ type TupleOutputTypeWithOptionals<T extends util.TupleItems> = T extends readonl
673
675
  ...infer Prefix extends SomeType[],
674
676
  infer Tail extends SomeType
675
677
  ] ? Tail["_zod"]["optout"] extends "optional" ? [...TupleOutputTypeWithOptionals<Prefix>, core.output<Tail>?] : TupleOutputTypeNoOptionals<T> : [];
676
- export interface $ZodTupleInternals<T extends util.TupleItems = readonly $ZodType[], Rest extends SomeType | null = $ZodType | null> extends $ZodTypeInternals<$InferTupleOutputType<T, Rest>, $InferTupleInputType<T, Rest>> {
678
+ export interface $ZodTupleInternals<T extends util.TupleItems = readonly $ZodType[], Rest extends SomeType | null = $ZodType | null> extends _$ZodTypeInternals {
677
679
  def: $ZodTupleDef<T, Rest>;
678
680
  isst: errors.$ZodIssueInvalidType | errors.$ZodIssueTooBig<unknown[]> | errors.$ZodIssueTooSmall<unknown[]>;
681
+ output: $InferTupleOutputType<T, Rest>;
682
+ input: $InferTupleInputType<T, Rest>;
679
683
  }
680
684
  export interface $ZodTuple<T extends util.TupleItems = readonly $ZodType[], Rest extends SomeType | null = $ZodType | null> extends $ZodType {
681
685
  _zod: $ZodTupleInternals<T, Rest>;
@@ -587,6 +587,8 @@ out Shape extends Readonly<$ZodShape> = Readonly<$ZodShape>, out Config extends
587
587
  propValues: util.PropValues;
588
588
  output: $InferObjectOutput<Shape, Config["out"]>;
589
589
  input: $InferObjectInput<Shape, Config["in"]>;
590
+ optin?: "optional" | undefined;
591
+ optout?: "optional" | undefined;
590
592
  }
591
593
  export type $ZodLooseShape = Record<string, any>;
592
594
  export interface $ZodObject<
@@ -673,9 +675,11 @@ type TupleOutputTypeWithOptionals<T extends util.TupleItems> = T extends readonl
673
675
  ...infer Prefix extends SomeType[],
674
676
  infer Tail extends SomeType
675
677
  ] ? Tail["_zod"]["optout"] extends "optional" ? [...TupleOutputTypeWithOptionals<Prefix>, core.output<Tail>?] : TupleOutputTypeNoOptionals<T> : [];
676
- export interface $ZodTupleInternals<T extends util.TupleItems = readonly $ZodType[], Rest extends SomeType | null = $ZodType | null> extends $ZodTypeInternals<$InferTupleOutputType<T, Rest>, $InferTupleInputType<T, Rest>> {
678
+ export interface $ZodTupleInternals<T extends util.TupleItems = readonly $ZodType[], Rest extends SomeType | null = $ZodType | null> extends _$ZodTypeInternals {
677
679
  def: $ZodTupleDef<T, Rest>;
678
680
  isst: errors.$ZodIssueInvalidType | errors.$ZodIssueTooBig<unknown[]> | errors.$ZodIssueTooSmall<unknown[]>;
681
+ output: $InferTupleOutputType<T, Rest>;
682
+ input: $InferTupleInputType<T, Rest>;
679
683
  }
680
684
  export interface $ZodTuple<T extends util.TupleItems = readonly $ZodType[], Rest extends SomeType | null = $ZodType | null> extends $ZodType {
681
685
  _zod: $ZodTupleInternals<T, Rest>;
@@ -1384,6 +1384,12 @@ export const $ZodTransform = /*@__PURE__*/ core.$constructor("$ZodTransform", (i
1384
1384
  return payload;
1385
1385
  };
1386
1386
  });
1387
+ function handleOptionalResult(result, input) {
1388
+ if (result.issues.length && input === undefined) {
1389
+ return { issues: [], value: undefined };
1390
+ }
1391
+ return result;
1392
+ }
1387
1393
  export const $ZodOptional = /*@__PURE__*/ core.$constructor("$ZodOptional", (inst, def) => {
1388
1394
  $ZodType.init(inst, def);
1389
1395
  inst._zod.optin = "optional";
@@ -1397,7 +1403,10 @@ export const $ZodOptional = /*@__PURE__*/ core.$constructor("$ZodOptional", (ins
1397
1403
  });
1398
1404
  inst._zod.parse = (payload, ctx) => {
1399
1405
  if (def.innerType._zod.optin === "optional") {
1400
- return def.innerType._zod.run(payload, ctx);
1406
+ const result = def.innerType._zod.run(payload, ctx);
1407
+ if (result instanceof Promise)
1408
+ return result.then((r) => handleOptionalResult(r, payload.value));
1409
+ return handleOptionalResult(result, payload.value);
1401
1410
  }
1402
1411
  if (payload.value === undefined) {
1403
1412
  return payload;
@@ -1644,11 +1653,18 @@ export const $ZodPromise = /*@__PURE__*/ core.$constructor("$ZodPromise", (inst,
1644
1653
  });
1645
1654
  export const $ZodLazy = /*@__PURE__*/ core.$constructor("$ZodLazy", (inst, def) => {
1646
1655
  $ZodType.init(inst, def);
1656
+ // let _innerType!: any;
1657
+ // util.defineLazy(def, "getter", () => {
1658
+ // if (!_innerType) {
1659
+ // _innerType = def.getter();
1660
+ // }
1661
+ // return () => _innerType;
1662
+ // });
1647
1663
  util.defineLazy(inst._zod, "innerType", () => def.getter());
1648
1664
  util.defineLazy(inst._zod, "pattern", () => inst._zod.innerType._zod.pattern);
1649
1665
  util.defineLazy(inst._zod, "propValues", () => inst._zod.innerType._zod.propValues);
1650
- util.defineLazy(inst._zod, "optin", () => inst._zod.innerType._zod.optin);
1651
- util.defineLazy(inst._zod, "optout", () => inst._zod.innerType._zod.optout);
1666
+ util.defineLazy(inst._zod, "optin", () => inst._zod.innerType._zod.optin ?? undefined);
1667
+ util.defineLazy(inst._zod, "optout", () => inst._zod.innerType._zod.optout ?? undefined);
1652
1668
  inst._zod.parse = (payload, ctx) => {
1653
1669
  const inner = inst._zod.innerType;
1654
1670
  return inner._zod.run(payload, ctx);
@@ -83,7 +83,7 @@ class JSONSchemaGenerator {
83
83
  else if (regexes.length > 1) {
84
84
  result.schema.allOf = [
85
85
  ...regexes.map((regex) => ({
86
- ...(this.target === "draft-7" ? { type: "string" } : {}),
86
+ ...(this.target === "draft-7" || this.target === "draft-4" ? { type: "string" } : {}),
87
87
  pattern: regex.source,
88
88
  })),
89
89
  ];
@@ -98,22 +98,36 @@ class JSONSchemaGenerator {
98
98
  json.type = "integer";
99
99
  else
100
100
  json.type = "number";
101
- if (typeof exclusiveMinimum === "number")
102
- json.exclusiveMinimum = exclusiveMinimum;
101
+ if (typeof exclusiveMinimum === "number") {
102
+ if (this.target === "draft-4") {
103
+ json.minimum = exclusiveMinimum;
104
+ json.exclusiveMinimum = true;
105
+ }
106
+ else {
107
+ json.exclusiveMinimum = exclusiveMinimum;
108
+ }
109
+ }
103
110
  if (typeof minimum === "number") {
104
111
  json.minimum = minimum;
105
- if (typeof exclusiveMinimum === "number") {
112
+ if (typeof exclusiveMinimum === "number" && this.target !== "draft-4") {
106
113
  if (exclusiveMinimum >= minimum)
107
114
  delete json.minimum;
108
115
  else
109
116
  delete json.exclusiveMinimum;
110
117
  }
111
118
  }
112
- if (typeof exclusiveMaximum === "number")
113
- json.exclusiveMaximum = exclusiveMaximum;
119
+ if (typeof exclusiveMaximum === "number") {
120
+ if (this.target === "draft-4") {
121
+ json.maximum = exclusiveMaximum;
122
+ json.exclusiveMaximum = true;
123
+ }
124
+ else {
125
+ json.exclusiveMaximum = exclusiveMaximum;
126
+ }
127
+ }
114
128
  if (typeof maximum === "number") {
115
129
  json.maximum = maximum;
116
- if (typeof exclusiveMaximum === "number") {
130
+ if (typeof exclusiveMaximum === "number" && this.target !== "draft-4") {
117
131
  if (exclusiveMaximum <= maximum)
118
132
  delete json.maximum;
119
133
  else
@@ -294,7 +308,12 @@ class JSONSchemaGenerator {
294
308
  case "record": {
295
309
  const json = _json;
296
310
  json.type = "object";
297
- json.propertyNames = this.process(def.keyType, { ...params, path: [...params.path, "propertyNames"] });
311
+ if (this.target !== "draft-4") {
312
+ json.propertyNames = this.process(def.keyType, {
313
+ ...params,
314
+ path: [...params.path, "propertyNames"],
315
+ });
316
+ }
298
317
  json.additionalProperties = this.process(def.valueType, {
299
318
  ...params,
300
319
  path: [...params.path, "additionalProperties"],
@@ -354,7 +373,12 @@ class JSONSchemaGenerator {
354
373
  else if (vals.length === 1) {
355
374
  const val = vals[0];
356
375
  json.type = val === null ? "null" : typeof val;
357
- json.const = val;
376
+ if (this.target === "draft-4") {
377
+ json.enum = [val];
378
+ }
379
+ else {
380
+ json.const = val;
381
+ }
358
382
  }
359
383
  else {
360
384
  if (vals.every((v) => typeof v === "number"))
@@ -650,7 +674,7 @@ class JSONSchemaGenerator {
650
674
  flattenRef(ref, params);
651
675
  // merge referenced schema into current
652
676
  const refSchema = this.seen.get(ref).schema;
653
- if (refSchema.$ref && params.target === "draft-7") {
677
+ if (refSchema.$ref && (params.target === "draft-7" || params.target === "draft-4")) {
654
678
  schema.allOf = schema.allOf ?? [];
655
679
  schema.allOf.push(refSchema);
656
680
  }
@@ -677,6 +701,9 @@ class JSONSchemaGenerator {
677
701
  else if (this.target === "draft-7") {
678
702
  result.$schema = "http://json-schema.org/draft-07/schema#";
679
703
  }
704
+ else if (this.target === "draft-4") {
705
+ result.$schema = "http://json-schema.org/draft-04/schema#";
706
+ }
680
707
  else {
681
708
  // @ts-ignore
682
709
  console.warn(`Invalid target: ${this.target}`);
@@ -7,8 +7,9 @@ interface JSONSchemaGeneratorParams {
7
7
  metadata?: $ZodRegistry<Record<string, any>>;
8
8
  /** The JSON Schema version to target.
9
9
  * - `"draft-2020-12"` — Default. JSON Schema Draft 2020-12
10
- * - `"draft-7"` — JSON Schema Draft 7 */
11
- target?: "draft-7" | "draft-2020-12";
10
+ * - `"draft-7"` — JSON Schema Draft 7
11
+ * - `"draft-4"` JSON Schema Draft 4 */
12
+ target?: "draft-4" | "draft-7" | "draft-2020-12";
12
13
  /** How to handle unrepresentable types.
13
14
  * - `"throw"` — Default. Unrepresentable types throw an error
14
15
  * - `"any"` — Unrepresentable types become `{}` */
@@ -60,7 +61,7 @@ interface Seen {
60
61
  }
61
62
  export declare class JSONSchemaGenerator {
62
63
  metadataRegistry: $ZodRegistry<Record<string, any>>;
63
- target: "draft-7" | "draft-2020-12";
64
+ target: "draft-4" | "draft-7" | "draft-2020-12";
64
65
  unrepresentable: "throw" | "any";
65
66
  override: (ctx: {
66
67
  zodSchema: schemas.$ZodTypes;
@@ -7,8 +7,9 @@ interface JSONSchemaGeneratorParams {
7
7
  metadata?: $ZodRegistry<Record<string, any>>;
8
8
  /** The JSON Schema version to target.
9
9
  * - `"draft-2020-12"` — Default. JSON Schema Draft 2020-12
10
- * - `"draft-7"` — JSON Schema Draft 7 */
11
- target?: "draft-7" | "draft-2020-12";
10
+ * - `"draft-7"` — JSON Schema Draft 7
11
+ * - `"draft-4"` JSON Schema Draft 4 */
12
+ target?: "draft-4" | "draft-7" | "draft-2020-12";
12
13
  /** How to handle unrepresentable types.
13
14
  * - `"throw"` — Default. Unrepresentable types throw an error
14
15
  * - `"any"` — Unrepresentable types become `{}` */
@@ -60,7 +61,7 @@ interface Seen {
60
61
  }
61
62
  export declare class JSONSchemaGenerator {
62
63
  metadataRegistry: $ZodRegistry<Record<string, any>>;
63
- target: "draft-7" | "draft-2020-12";
64
+ target: "draft-4" | "draft-7" | "draft-2020-12";
64
65
  unrepresentable: "throw" | "any";
65
66
  override: (ctx: {
66
67
  zodSchema: schemas.$ZodTypes;
@@ -79,7 +79,7 @@ export class JSONSchemaGenerator {
79
79
  else if (regexes.length > 1) {
80
80
  result.schema.allOf = [
81
81
  ...regexes.map((regex) => ({
82
- ...(this.target === "draft-7" ? { type: "string" } : {}),
82
+ ...(this.target === "draft-7" || this.target === "draft-4" ? { type: "string" } : {}),
83
83
  pattern: regex.source,
84
84
  })),
85
85
  ];
@@ -94,22 +94,36 @@ export class JSONSchemaGenerator {
94
94
  json.type = "integer";
95
95
  else
96
96
  json.type = "number";
97
- if (typeof exclusiveMinimum === "number")
98
- json.exclusiveMinimum = exclusiveMinimum;
97
+ if (typeof exclusiveMinimum === "number") {
98
+ if (this.target === "draft-4") {
99
+ json.minimum = exclusiveMinimum;
100
+ json.exclusiveMinimum = true;
101
+ }
102
+ else {
103
+ json.exclusiveMinimum = exclusiveMinimum;
104
+ }
105
+ }
99
106
  if (typeof minimum === "number") {
100
107
  json.minimum = minimum;
101
- if (typeof exclusiveMinimum === "number") {
108
+ if (typeof exclusiveMinimum === "number" && this.target !== "draft-4") {
102
109
  if (exclusiveMinimum >= minimum)
103
110
  delete json.minimum;
104
111
  else
105
112
  delete json.exclusiveMinimum;
106
113
  }
107
114
  }
108
- if (typeof exclusiveMaximum === "number")
109
- json.exclusiveMaximum = exclusiveMaximum;
115
+ if (typeof exclusiveMaximum === "number") {
116
+ if (this.target === "draft-4") {
117
+ json.maximum = exclusiveMaximum;
118
+ json.exclusiveMaximum = true;
119
+ }
120
+ else {
121
+ json.exclusiveMaximum = exclusiveMaximum;
122
+ }
123
+ }
110
124
  if (typeof maximum === "number") {
111
125
  json.maximum = maximum;
112
- if (typeof exclusiveMaximum === "number") {
126
+ if (typeof exclusiveMaximum === "number" && this.target !== "draft-4") {
113
127
  if (exclusiveMaximum <= maximum)
114
128
  delete json.maximum;
115
129
  else
@@ -290,7 +304,12 @@ export class JSONSchemaGenerator {
290
304
  case "record": {
291
305
  const json = _json;
292
306
  json.type = "object";
293
- json.propertyNames = this.process(def.keyType, { ...params, path: [...params.path, "propertyNames"] });
307
+ if (this.target !== "draft-4") {
308
+ json.propertyNames = this.process(def.keyType, {
309
+ ...params,
310
+ path: [...params.path, "propertyNames"],
311
+ });
312
+ }
294
313
  json.additionalProperties = this.process(def.valueType, {
295
314
  ...params,
296
315
  path: [...params.path, "additionalProperties"],
@@ -350,7 +369,12 @@ export class JSONSchemaGenerator {
350
369
  else if (vals.length === 1) {
351
370
  const val = vals[0];
352
371
  json.type = val === null ? "null" : typeof val;
353
- json.const = val;
372
+ if (this.target === "draft-4") {
373
+ json.enum = [val];
374
+ }
375
+ else {
376
+ json.const = val;
377
+ }
354
378
  }
355
379
  else {
356
380
  if (vals.every((v) => typeof v === "number"))
@@ -646,7 +670,7 @@ export class JSONSchemaGenerator {
646
670
  flattenRef(ref, params);
647
671
  // merge referenced schema into current
648
672
  const refSchema = this.seen.get(ref).schema;
649
- if (refSchema.$ref && params.target === "draft-7") {
673
+ if (refSchema.$ref && (params.target === "draft-7" || params.target === "draft-4")) {
650
674
  schema.allOf = schema.allOf ?? [];
651
675
  schema.allOf.push(refSchema);
652
676
  }
@@ -673,6 +697,9 @@ export class JSONSchemaGenerator {
673
697
  else if (this.target === "draft-7") {
674
698
  result.$schema = "http://json-schema.org/draft-07/schema#";
675
699
  }
700
+ else if (this.target === "draft-4") {
701
+ result.$schema = "http://json-schema.org/draft-04/schema#";
702
+ }
676
703
  else {
677
704
  // @ts-ignore
678
705
  console.warn(`Invalid target: ${this.target}`);
package/v4/core/util.cjs CHANGED
@@ -107,16 +107,20 @@ function floatSafeRemainder(val, step) {
107
107
  const stepInt = Number.parseInt(step.toFixed(decCount).replace(".", ""));
108
108
  return (valInt % stepInt) / 10 ** decCount;
109
109
  }
110
+ const EVALUATING = Symbol("evaluating");
110
111
  function defineLazy(object, key, getter) {
111
- const set = false;
112
+ let value = undefined;
112
113
  Object.defineProperty(object, key, {
113
114
  get() {
114
- if (!set) {
115
- const value = getter();
116
- object[key] = value;
117
- return value;
115
+ if (value === EVALUATING) {
116
+ // Circular reference detected, return undefined to break the cycle
117
+ return undefined;
118
118
  }
119
- throw new Error("cached value already set");
119
+ if (value === undefined) {
120
+ value = EVALUATING;
121
+ value = getter();
122
+ }
123
+ return value;
120
124
  },
121
125
  set(v) {
122
126
  Object.defineProperty(object, key, {
package/v4/core/util.js CHANGED
@@ -61,16 +61,20 @@ export function floatSafeRemainder(val, step) {
61
61
  const stepInt = Number.parseInt(step.toFixed(decCount).replace(".", ""));
62
62
  return (valInt % stepInt) / 10 ** decCount;
63
63
  }
64
+ const EVALUATING = Symbol("evaluating");
64
65
  export function defineLazy(object, key, getter) {
65
- const set = false;
66
+ let value = undefined;
66
67
  Object.defineProperty(object, key, {
67
68
  get() {
68
- if (!set) {
69
- const value = getter();
70
- object[key] = value;
71
- return value;
69
+ if (value === EVALUATING) {
70
+ // Circular reference detected, return undefined to break the cycle
71
+ return undefined;
72
72
  }
73
- throw new Error("cached value already set");
73
+ if (value === undefined) {
74
+ value = EVALUATING;
75
+ value = getter();
76
+ }
77
+ return value;
74
78
  },
75
79
  set(v) {
76
80
  Object.defineProperty(object, key, {
@@ -4,5 +4,5 @@ exports.version = void 0;
4
4
  exports.version = {
5
5
  major: 4,
6
6
  minor: 0,
7
- patch: 12,
7
+ patch: 14,
8
8
  };
@@ -1,5 +1,5 @@
1
1
  export const version = {
2
2
  major: 4,
3
3
  minor: 0,
4
- patch: 12,
4
+ patch: 14,
5
5
  };