zod 4.2.0-canary.20251118T055019 → 4.2.0-canary.20251118T055751
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 +1 -1
- package/src/v4/classic/tests/continuability.test.ts +1 -1
- package/src/v4/classic/tests/template-literal.test.ts +1 -1
- package/src/v4/classic/tests/to-json-schema.test.ts +54 -6
- package/src/v4/core/regexes.ts +1 -3
- package/src/v4/core/to-json-schema.ts +9 -2
- package/v4/core/regexes.cjs +1 -1
- package/v4/core/regexes.js +1 -1
- package/v4/core/to-json-schema.cjs +10 -2
- package/v4/core/to-json-schema.js +10 -2
package/package.json
CHANGED
|
@@ -208,7 +208,7 @@ test("continuability", () => {
|
|
|
208
208
|
"message": "Invalid MAC address",
|
|
209
209
|
"origin": "string",
|
|
210
210
|
"path": [],
|
|
211
|
-
"pattern": "/^(
|
|
211
|
+
"pattern": "/^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$/",
|
|
212
212
|
},
|
|
213
213
|
{
|
|
214
214
|
"code": "custom",
|
|
@@ -574,7 +574,7 @@ test("regexes", () => {
|
|
|
574
574
|
`"^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$"`
|
|
575
575
|
);
|
|
576
576
|
expect(mac._zod.pattern.source).toMatchInlineSnapshot(
|
|
577
|
-
`"^(
|
|
577
|
+
`"^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$"`
|
|
578
578
|
);
|
|
579
579
|
expect(ulid._zod.pattern.source).toMatchInlineSnapshot(`"^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$"`);
|
|
580
580
|
expect(uuid._zod.pattern.source).toMatchInlineSnapshot(
|
|
@@ -136,7 +136,7 @@ describe("toJSONSchema", () => {
|
|
|
136
136
|
{
|
|
137
137
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
138
138
|
"format": "mac",
|
|
139
|
-
"pattern": "^(
|
|
139
|
+
"pattern": "^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$",
|
|
140
140
|
"type": "string",
|
|
141
141
|
}
|
|
142
142
|
`);
|
|
@@ -144,7 +144,7 @@ describe("toJSONSchema", () => {
|
|
|
144
144
|
{
|
|
145
145
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
146
146
|
"format": "mac",
|
|
147
|
-
"pattern": "^(
|
|
147
|
+
"pattern": "^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$",
|
|
148
148
|
"type": "string",
|
|
149
149
|
}
|
|
150
150
|
`);
|
|
@@ -152,7 +152,7 @@ describe("toJSONSchema", () => {
|
|
|
152
152
|
{
|
|
153
153
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
154
154
|
"format": "mac",
|
|
155
|
-
"pattern": "^(
|
|
155
|
+
"pattern": "^(?:[0-9A-F]{2}-){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}-){5}[0-9a-f]{2}$",
|
|
156
156
|
"type": "string",
|
|
157
157
|
}
|
|
158
158
|
`);
|
|
@@ -385,7 +385,7 @@ describe("toJSONSchema", () => {
|
|
|
385
385
|
{
|
|
386
386
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
387
387
|
"format": "mac",
|
|
388
|
-
"pattern": "^(
|
|
388
|
+
"pattern": "^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$",
|
|
389
389
|
"type": "string",
|
|
390
390
|
}
|
|
391
391
|
`);
|
|
@@ -393,7 +393,7 @@ describe("toJSONSchema", () => {
|
|
|
393
393
|
{
|
|
394
394
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
395
395
|
"format": "mac",
|
|
396
|
-
"pattern": "^(
|
|
396
|
+
"pattern": "^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$",
|
|
397
397
|
"type": "string",
|
|
398
398
|
}
|
|
399
399
|
`);
|
|
@@ -401,7 +401,7 @@ describe("toJSONSchema", () => {
|
|
|
401
401
|
{
|
|
402
402
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
403
403
|
"format": "mac",
|
|
404
|
-
"pattern": "^(
|
|
404
|
+
"pattern": "^(?:[0-9A-F]{2}-){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}-){5}[0-9a-f]{2}$",
|
|
405
405
|
"type": "string",
|
|
406
406
|
}
|
|
407
407
|
`);
|
|
@@ -706,6 +706,54 @@ describe("toJSONSchema", () => {
|
|
|
706
706
|
`);
|
|
707
707
|
});
|
|
708
708
|
|
|
709
|
+
test("discriminated unions", () => {
|
|
710
|
+
const schema = z.discriminatedUnion("type", [
|
|
711
|
+
z.object({ type: z.literal("success"), data: z.string() }),
|
|
712
|
+
z.object({ type: z.literal("error"), message: z.string() }),
|
|
713
|
+
]);
|
|
714
|
+
expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
|
|
715
|
+
{
|
|
716
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
717
|
+
"oneOf": [
|
|
718
|
+
{
|
|
719
|
+
"additionalProperties": false,
|
|
720
|
+
"properties": {
|
|
721
|
+
"data": {
|
|
722
|
+
"type": "string",
|
|
723
|
+
},
|
|
724
|
+
"type": {
|
|
725
|
+
"const": "success",
|
|
726
|
+
"type": "string",
|
|
727
|
+
},
|
|
728
|
+
},
|
|
729
|
+
"required": [
|
|
730
|
+
"type",
|
|
731
|
+
"data",
|
|
732
|
+
],
|
|
733
|
+
"type": "object",
|
|
734
|
+
},
|
|
735
|
+
{
|
|
736
|
+
"additionalProperties": false,
|
|
737
|
+
"properties": {
|
|
738
|
+
"message": {
|
|
739
|
+
"type": "string",
|
|
740
|
+
},
|
|
741
|
+
"type": {
|
|
742
|
+
"const": "error",
|
|
743
|
+
"type": "string",
|
|
744
|
+
},
|
|
745
|
+
},
|
|
746
|
+
"required": [
|
|
747
|
+
"type",
|
|
748
|
+
"message",
|
|
749
|
+
],
|
|
750
|
+
"type": "object",
|
|
751
|
+
},
|
|
752
|
+
],
|
|
753
|
+
}
|
|
754
|
+
`);
|
|
755
|
+
});
|
|
756
|
+
|
|
709
757
|
test("intersections", () => {
|
|
710
758
|
const schema = z.intersection(z.object({ name: z.string() }), z.object({ age: z.number() }));
|
|
711
759
|
|
package/src/v4/core/regexes.ts
CHANGED
|
@@ -63,9 +63,7 @@ export const ipv6: RegExp =
|
|
|
63
63
|
/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
|
|
64
64
|
export const mac = (delimiter?: string): RegExp => {
|
|
65
65
|
const escapedDelim = util.escapeRegex(delimiter ?? ":");
|
|
66
|
-
return new RegExp(
|
|
67
|
-
`^(([0-9A-F]{2}(${escapedDelim})[0-9A-F]{2}(\\3[0-9A-F]{2}){4})|([0-9a-f]{2}(${escapedDelim})[0-9a-f]{2}(\\6[0-9a-f]{2}){4}))$`
|
|
68
|
-
);
|
|
66
|
+
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
69
67
|
};
|
|
70
68
|
export const cidrv4: RegExp =
|
|
71
69
|
/^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
|
|
@@ -330,13 +330,20 @@ export class JSONSchemaGenerator {
|
|
|
330
330
|
}
|
|
331
331
|
case "union": {
|
|
332
332
|
const json: JSONSchema.BaseSchema = _json as any;
|
|
333
|
+
// Discriminated unions use oneOf (exactly one match) instead of anyOf (one or more matches)
|
|
334
|
+
// because the discriminator field ensures mutual exclusivity between options in JSON Schema
|
|
335
|
+
const isDiscriminated = (def as any).discriminator !== undefined;
|
|
333
336
|
const options = def.options.map((x, i) =>
|
|
334
337
|
this.process(x, {
|
|
335
338
|
...params,
|
|
336
|
-
path: [...params.path, "anyOf", i],
|
|
339
|
+
path: [...params.path, isDiscriminated ? "oneOf" : "anyOf", i],
|
|
337
340
|
})
|
|
338
341
|
);
|
|
339
|
-
|
|
342
|
+
if (isDiscriminated) {
|
|
343
|
+
json.oneOf = options;
|
|
344
|
+
} else {
|
|
345
|
+
json.anyOf = options;
|
|
346
|
+
}
|
|
340
347
|
break;
|
|
341
348
|
}
|
|
342
349
|
case "intersection": {
|
package/v4/core/regexes.cjs
CHANGED
|
@@ -72,7 +72,7 @@ exports.ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?
|
|
|
72
72
|
exports.ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
|
|
73
73
|
const mac = (delimiter) => {
|
|
74
74
|
const escapedDelim = util.escapeRegex(delimiter ?? ":");
|
|
75
|
-
return new RegExp(`^(
|
|
75
|
+
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
76
76
|
};
|
|
77
77
|
exports.mac = mac;
|
|
78
78
|
exports.cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
|
package/v4/core/regexes.js
CHANGED
|
@@ -41,7 +41,7 @@ export const ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.)
|
|
|
41
41
|
export const ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
|
|
42
42
|
export const mac = (delimiter) => {
|
|
43
43
|
const escapedDelim = util.escapeRegex(delimiter ?? ":");
|
|
44
|
-
return new RegExp(`^(
|
|
44
|
+
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
45
45
|
};
|
|
46
46
|
export const cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
|
|
47
47
|
export const cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;
|
|
@@ -252,11 +252,19 @@ class JSONSchemaGenerator {
|
|
|
252
252
|
}
|
|
253
253
|
case "union": {
|
|
254
254
|
const json = _json;
|
|
255
|
+
// Discriminated unions use oneOf (exactly one match) instead of anyOf (one or more matches)
|
|
256
|
+
// because the discriminator field ensures mutual exclusivity between options in JSON Schema
|
|
257
|
+
const isDiscriminated = def.discriminator !== undefined;
|
|
255
258
|
const options = def.options.map((x, i) => this.process(x, {
|
|
256
259
|
...params,
|
|
257
|
-
path: [...params.path, "anyOf", i],
|
|
260
|
+
path: [...params.path, isDiscriminated ? "oneOf" : "anyOf", i],
|
|
258
261
|
}));
|
|
259
|
-
|
|
262
|
+
if (isDiscriminated) {
|
|
263
|
+
json.oneOf = options;
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
json.anyOf = options;
|
|
267
|
+
}
|
|
260
268
|
break;
|
|
261
269
|
}
|
|
262
270
|
case "intersection": {
|
|
@@ -248,11 +248,19 @@ export class JSONSchemaGenerator {
|
|
|
248
248
|
}
|
|
249
249
|
case "union": {
|
|
250
250
|
const json = _json;
|
|
251
|
+
// Discriminated unions use oneOf (exactly one match) instead of anyOf (one or more matches)
|
|
252
|
+
// because the discriminator field ensures mutual exclusivity between options in JSON Schema
|
|
253
|
+
const isDiscriminated = def.discriminator !== undefined;
|
|
251
254
|
const options = def.options.map((x, i) => this.process(x, {
|
|
252
255
|
...params,
|
|
253
|
-
path: [...params.path, "anyOf", i],
|
|
256
|
+
path: [...params.path, isDiscriminated ? "oneOf" : "anyOf", i],
|
|
254
257
|
}));
|
|
255
|
-
|
|
258
|
+
if (isDiscriminated) {
|
|
259
|
+
json.oneOf = options;
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
json.anyOf = options;
|
|
263
|
+
}
|
|
256
264
|
break;
|
|
257
265
|
}
|
|
258
266
|
case "intersection": {
|