zod 4.1.12 → 4.1.13
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/checks.ts +1 -0
- package/src/v4/classic/schemas.ts +20 -0
- package/src/v4/classic/tests/continuability.test.ts +22 -0
- package/src/v4/classic/tests/describe-meta-checks.test.ts +27 -0
- package/src/v4/classic/tests/index.test.ts +55 -1
- package/src/v4/classic/tests/promise.test.ts +1 -1
- package/src/v4/classic/tests/readonly.test.ts +1 -1
- package/src/v4/classic/tests/record.test.ts +141 -9
- package/src/v4/classic/tests/registries.test.ts +5 -1
- package/src/v4/classic/tests/string.test.ts +72 -0
- package/src/v4/classic/tests/template-literal.test.ts +8 -0
- package/src/v4/classic/tests/to-json-schema.test.ts +97 -0
- package/src/v4/classic/tests/tuple.test.ts +18 -0
- package/src/v4/classic/tests/url.test.ts +13 -0
- package/src/v4/core/api.ts +45 -0
- package/src/v4/core/core.ts +22 -9
- package/src/v4/core/regexes.ts +6 -1
- package/src/v4/core/registries.ts +12 -1
- package/src/v4/core/schemas.ts +50 -33
- package/src/v4/core/tests/extend.test.ts +42 -1
- package/src/v4/core/tests/locales/he.test.ts +379 -0
- package/src/v4/core/tests/locales/nl.test.ts +46 -0
- package/src/v4/core/tests/record-constructor.test.ts +67 -0
- package/src/v4/core/tests/recursive-tuples.test.ts +45 -0
- package/src/v4/core/to-json-schema.ts +55 -91
- package/src/v4/core/util.ts +11 -0
- package/src/v4/core/versions.ts +1 -1
- package/src/v4/locales/en.ts +1 -0
- package/src/v4/locales/he.ts +202 -71
- package/src/v4/locales/nl.ts +10 -10
- package/src/v4/mini/iso.ts +4 -4
- package/src/v4/mini/schemas.ts +17 -0
- package/src/v4/mini/tests/functions.test.ts +0 -38
- package/src/v4/mini/tests/index.test.ts +24 -1
- package/src/v4/mini/tests/string.test.ts +32 -0
- package/v3/ZodError.d.cts +1 -1
- package/v3/ZodError.d.ts +1 -1
- package/v4/classic/checks.cjs +2 -1
- package/v4/classic/checks.d.cts +1 -1
- package/v4/classic/checks.d.ts +1 -1
- package/v4/classic/checks.js +1 -1
- package/v4/classic/schemas.cjs +15 -2
- package/v4/classic/schemas.d.cts +8 -0
- package/v4/classic/schemas.d.ts +8 -0
- package/v4/classic/schemas.js +12 -0
- package/v4/core/api.cjs +40 -0
- package/v4/core/api.d.cts +7 -0
- package/v4/core/api.d.ts +7 -0
- package/v4/core/api.js +36 -0
- package/v4/core/core.cjs +20 -11
- package/v4/core/core.js +20 -11
- package/v4/core/regexes.cjs +31 -2
- package/v4/core/regexes.d.cts +1 -0
- package/v4/core/regexes.d.ts +1 -0
- package/v4/core/regexes.js +5 -0
- package/v4/core/registries.cjs +3 -1
- package/v4/core/registries.js +3 -1
- package/v4/core/schemas.cjs +32 -33
- package/v4/core/schemas.d.cts +12 -2
- package/v4/core/schemas.d.ts +12 -2
- package/v4/core/schemas.js +30 -31
- package/v4/core/to-json-schema.cjs +55 -92
- package/v4/core/to-json-schema.js +55 -92
- package/v4/core/util.cjs +11 -0
- package/v4/core/util.d.cts +1 -0
- package/v4/core/util.d.ts +1 -0
- package/v4/core/util.js +10 -0
- package/v4/core/versions.cjs +1 -1
- package/v4/core/versions.js +1 -1
- package/v4/locales/en.cjs +1 -0
- package/v4/locales/en.js +1 -0
- package/v4/locales/he.cjs +177 -66
- package/v4/locales/he.js +177 -66
- package/v4/locales/nl.cjs +8 -8
- package/v4/locales/nl.js +8 -8
- package/v4/mini/iso.cjs +4 -4
- package/v4/mini/iso.js +4 -4
- package/v4/mini/schemas.cjs +13 -2
- package/v4/mini/schemas.d.cts +6 -0
- package/v4/mini/schemas.d.ts +6 -0
- package/v4/mini/schemas.js +10 -0
|
@@ -132,6 +132,30 @@ describe("toJSONSchema", () => {
|
|
|
132
132
|
"type": "string",
|
|
133
133
|
}
|
|
134
134
|
`);
|
|
135
|
+
expect(z.toJSONSchema(z.mac())).toMatchInlineSnapshot(`
|
|
136
|
+
{
|
|
137
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
138
|
+
"format": "mac",
|
|
139
|
+
"pattern": "^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$",
|
|
140
|
+
"type": "string",
|
|
141
|
+
}
|
|
142
|
+
`);
|
|
143
|
+
expect(z.toJSONSchema(z.mac({ delimiter: ":" }))).toMatchInlineSnapshot(`
|
|
144
|
+
{
|
|
145
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
146
|
+
"format": "mac",
|
|
147
|
+
"pattern": "^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$",
|
|
148
|
+
"type": "string",
|
|
149
|
+
}
|
|
150
|
+
`);
|
|
151
|
+
expect(z.toJSONSchema(z.mac({ delimiter: "-" }))).toMatchInlineSnapshot(`
|
|
152
|
+
{
|
|
153
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
154
|
+
"format": "mac",
|
|
155
|
+
"pattern": "^(?:[0-9A-F]{2}-){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}-){5}[0-9a-f]{2}$",
|
|
156
|
+
"type": "string",
|
|
157
|
+
}
|
|
158
|
+
`);
|
|
135
159
|
expect(z.toJSONSchema(z.uuid())).toMatchInlineSnapshot(`
|
|
136
160
|
{
|
|
137
161
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -357,6 +381,31 @@ describe("toJSONSchema", () => {
|
|
|
357
381
|
}
|
|
358
382
|
`);
|
|
359
383
|
|
|
384
|
+
expect(z.toJSONSchema(z.mac())).toMatchInlineSnapshot(`
|
|
385
|
+
{
|
|
386
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
387
|
+
"format": "mac",
|
|
388
|
+
"pattern": "^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$",
|
|
389
|
+
"type": "string",
|
|
390
|
+
}
|
|
391
|
+
`);
|
|
392
|
+
expect(z.toJSONSchema(z.mac({ delimiter: ":" }))).toMatchInlineSnapshot(`
|
|
393
|
+
{
|
|
394
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
395
|
+
"format": "mac",
|
|
396
|
+
"pattern": "^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$",
|
|
397
|
+
"type": "string",
|
|
398
|
+
}
|
|
399
|
+
`);
|
|
400
|
+
expect(z.toJSONSchema(z.mac({ delimiter: "-" }))).toMatchInlineSnapshot(`
|
|
401
|
+
{
|
|
402
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
403
|
+
"format": "mac",
|
|
404
|
+
"pattern": "^(?:[0-9A-F]{2}-){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}-){5}[0-9a-f]{2}$",
|
|
405
|
+
"type": "string",
|
|
406
|
+
}
|
|
407
|
+
`);
|
|
408
|
+
|
|
360
409
|
expect(z.toJSONSchema(z.base64())).toMatchInlineSnapshot(`
|
|
361
410
|
{
|
|
362
411
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -657,6 +706,54 @@ describe("toJSONSchema", () => {
|
|
|
657
706
|
`);
|
|
658
707
|
});
|
|
659
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
|
+
|
|
660
757
|
test("intersections", () => {
|
|
661
758
|
const schema = z.intersection(z.object({ name: z.string() }), z.object({ age: z.number() }));
|
|
662
759
|
|
|
@@ -145,6 +145,24 @@ test("tuple with optional elements followed by required", () => {
|
|
|
145
145
|
}
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
+
test("tuple with all optional elements", () => {
|
|
149
|
+
const allOptionalTuple = z.tuple([z.string().optional(), z.number().optional(), z.boolean().optional()]);
|
|
150
|
+
expectTypeOf<typeof allOptionalTuple._output>().toEqualTypeOf<[string?, number?, boolean?]>();
|
|
151
|
+
|
|
152
|
+
// Empty array should be valid (all items optional)
|
|
153
|
+
expect(allOptionalTuple.parse([])).toEqual([]);
|
|
154
|
+
|
|
155
|
+
// Partial arrays should be valid
|
|
156
|
+
expect(allOptionalTuple.parse(["hello"])).toEqual(["hello"]);
|
|
157
|
+
expect(allOptionalTuple.parse(["hello", 42])).toEqual(["hello", 42]);
|
|
158
|
+
|
|
159
|
+
// Full array should be valid
|
|
160
|
+
expect(allOptionalTuple.parse(["hello", 42, true])).toEqual(["hello", 42, true]);
|
|
161
|
+
|
|
162
|
+
// Array that's too long should fail
|
|
163
|
+
expect(() => allOptionalTuple.parse(["hello", 42, true, "extra"])).toThrow();
|
|
164
|
+
});
|
|
165
|
+
|
|
148
166
|
test("tuple with rest schema", () => {
|
|
149
167
|
const myTuple = z.tuple([z.string(), z.number()]).rest(z.boolean());
|
|
150
168
|
expect(myTuple.parse(["asdf", 1234, true, false, true])).toEqual(["asdf", 1234, true, false, true]);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { expect, expectTypeOf, test } from "vitest";
|
|
2
|
+
import * as z from "zod/v4";
|
|
3
|
+
|
|
4
|
+
test("type inference", () => {
|
|
5
|
+
const schema = z.string().array();
|
|
6
|
+
expectTypeOf<z.infer<typeof schema>>().toEqualTypeOf<string[]>();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test("url regex", () => {
|
|
10
|
+
expect((z.url({ hostname: /^example\.com$/ }).safeParse("http://example.org/").error?.issues[0] as any).pattern).toBe(
|
|
11
|
+
"^example\\.com$"
|
|
12
|
+
);
|
|
13
|
+
});
|
package/src/v4/core/api.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as checks from "./checks.js";
|
|
2
2
|
import type * as core from "./core.js";
|
|
3
3
|
import type * as errors from "./errors.js";
|
|
4
|
+
import * as registries from "./registries.js";
|
|
4
5
|
import * as schemas from "./schemas.js";
|
|
5
6
|
import * as util from "./util.js";
|
|
6
7
|
|
|
@@ -345,6 +346,22 @@ export function _ipv6<T extends schemas.$ZodIPv6>(
|
|
|
345
346
|
});
|
|
346
347
|
}
|
|
347
348
|
|
|
349
|
+
// MAC
|
|
350
|
+
export type $ZodMACParams = StringFormatParams<schemas.$ZodMAC, "pattern" | "when">;
|
|
351
|
+
export type $ZodCheckMACParams = CheckStringFormatParams<schemas.$ZodMAC, "pattern" | "when">;
|
|
352
|
+
export function _mac<T extends schemas.$ZodMAC>(
|
|
353
|
+
Class: util.SchemaClass<T>,
|
|
354
|
+
params?: string | $ZodMACParams | $ZodCheckMACParams
|
|
355
|
+
): T {
|
|
356
|
+
return new Class({
|
|
357
|
+
type: "string",
|
|
358
|
+
format: "mac",
|
|
359
|
+
check: "string_format",
|
|
360
|
+
abort: false,
|
|
361
|
+
...util.normalizeParams(params),
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
348
365
|
// CIDRv4
|
|
349
366
|
export type $ZodCIDRv4Params = StringFormatParams<schemas.$ZodCIDRv4, "pattern" | "when">;
|
|
350
367
|
export type $ZodCheckCIDRv4Params = CheckStringFormatParams<schemas.$ZodCIDRv4, "pattern" | "when">;
|
|
@@ -1036,6 +1053,10 @@ export function _toLowerCase(): checks.$ZodCheckOverwrite<string> {
|
|
|
1036
1053
|
export function _toUpperCase(): checks.$ZodCheckOverwrite<string> {
|
|
1037
1054
|
return _overwrite((input) => input.toUpperCase());
|
|
1038
1055
|
}
|
|
1056
|
+
// slugify
|
|
1057
|
+
export function _slugify(): checks.$ZodCheckOverwrite<string> {
|
|
1058
|
+
return _overwrite((input) => util.slugify(input));
|
|
1059
|
+
}
|
|
1039
1060
|
|
|
1040
1061
|
/////// collections ///////
|
|
1041
1062
|
|
|
@@ -1518,6 +1539,30 @@ export function _check<O = unknown>(fn: schemas.CheckFn<O>, params?: string | $Z
|
|
|
1518
1539
|
return ch;
|
|
1519
1540
|
}
|
|
1520
1541
|
|
|
1542
|
+
export function describe<T>(description: string): checks.$ZodCheck<T> {
|
|
1543
|
+
const ch = new checks.$ZodCheck({ check: "describe" });
|
|
1544
|
+
ch._zod.onattach = [
|
|
1545
|
+
(inst) => {
|
|
1546
|
+
const existing = registries.globalRegistry.get(inst) ?? {};
|
|
1547
|
+
registries.globalRegistry.add(inst, { ...existing, description });
|
|
1548
|
+
},
|
|
1549
|
+
];
|
|
1550
|
+
ch._zod.check = () => {}; // no-op check
|
|
1551
|
+
return ch;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
export function meta<T>(metadata: registries.GlobalMeta): checks.$ZodCheck<T> {
|
|
1555
|
+
const ch = new checks.$ZodCheck({ check: "meta" });
|
|
1556
|
+
ch._zod.onattach = [
|
|
1557
|
+
(inst) => {
|
|
1558
|
+
const existing = registries.globalRegistry.get(inst) ?? {};
|
|
1559
|
+
registries.globalRegistry.add(inst, { ...existing, ...metadata });
|
|
1560
|
+
},
|
|
1561
|
+
];
|
|
1562
|
+
ch._zod.check = () => {}; // no-op check
|
|
1563
|
+
return ch;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1521
1566
|
// export type $ZodCustomParams = CheckTypeParams<schemas.$ZodCustom, "fn">
|
|
1522
1567
|
|
|
1523
1568
|
///////// STRINGBOOL /////////
|
package/src/v4/core/core.ts
CHANGED
|
@@ -20,21 +20,34 @@ export /*@__NO_SIDE_EFFECTS__*/ function $constructor<T extends ZodTrait, D = T[
|
|
|
20
20
|
params?: { Parent?: typeof Class }
|
|
21
21
|
): $constructor<T, D> {
|
|
22
22
|
function init(inst: T, def: D) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
if (!inst._zod) {
|
|
24
|
+
Object.defineProperty(inst, "_zod", {
|
|
25
|
+
value: {
|
|
26
|
+
def,
|
|
27
|
+
constr: _,
|
|
28
|
+
traits: new Set(),
|
|
29
|
+
},
|
|
30
|
+
enumerable: false,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
27
33
|
|
|
28
|
-
inst._zod.traits
|
|
34
|
+
if (inst._zod.traits.has(name)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
29
37
|
|
|
30
38
|
inst._zod.traits.add(name);
|
|
39
|
+
|
|
31
40
|
initializer(inst, def);
|
|
41
|
+
|
|
32
42
|
// support prototype modifications
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
const proto = _.prototype;
|
|
44
|
+
const keys = Object.keys(proto);
|
|
45
|
+
for (let i = 0; i < keys.length; i++) {
|
|
46
|
+
const k = keys[i]!;
|
|
47
|
+
if (!(k in inst)) {
|
|
48
|
+
(inst as any)[k] = proto[k].bind(inst);
|
|
49
|
+
}
|
|
35
50
|
}
|
|
36
|
-
inst._zod.constr = _;
|
|
37
|
-
inst._zod.def = def;
|
|
38
51
|
}
|
|
39
52
|
|
|
40
53
|
// doesn't work if Parent has a constructor with arguments
|
package/src/v4/core/regexes.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as util from "./util.js";
|
|
2
|
+
|
|
1
3
|
export const cuid: RegExp = /^[cC][^\s-]{8,}$/;
|
|
2
4
|
export const cuid2: RegExp = /^[0-9a-z]+$/;
|
|
3
5
|
export const ulid: RegExp = /^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/;
|
|
@@ -59,7 +61,10 @@ export const ipv4: RegExp =
|
|
|
59
61
|
/^(?:(?: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])$/;
|
|
60
62
|
export const ipv6: RegExp =
|
|
61
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}|:))$/;
|
|
62
|
-
|
|
64
|
+
export const mac = (delimiter?: string): RegExp => {
|
|
65
|
+
const escapedDelim = util.escapeRegex(delimiter ?? ":");
|
|
66
|
+
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
67
|
+
};
|
|
63
68
|
export const cidrv4: RegExp =
|
|
64
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])$/;
|
|
65
70
|
export const cidrv6: RegExp =
|
|
@@ -94,4 +94,15 @@ export function registry<T extends MetadataType = MetadataType, S extends $ZodTy
|
|
|
94
94
|
return new $ZodRegistry<T, S>();
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
interface GlobalThisWithRegistry {
|
|
98
|
+
/**
|
|
99
|
+
* The globalRegistry instance shared across both CommonJS and ESM builds.
|
|
100
|
+
* By attaching the registry to `globalThis`, this property ensures a single, deduplicated instance
|
|
101
|
+
* is used regardless of whether the package is loaded via `require` (CJS) or `import` (ESM).
|
|
102
|
+
* This prevents dual package hazards and keeps registry state consistent.
|
|
103
|
+
*/
|
|
104
|
+
__zod_globalRegistry?: $ZodRegistry<GlobalMeta>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
(globalThis as GlobalThisWithRegistry).__zod_globalRegistry ??= registry<GlobalMeta>();
|
|
108
|
+
export const globalRegistry: $ZodRegistry<GlobalMeta> = (globalThis as GlobalThisWithRegistry).__zod_globalRegistry!;
|
package/src/v4/core/schemas.ts
CHANGED
|
@@ -489,7 +489,7 @@ export const $ZodURL: core.$constructor<$ZodURL> = /*@__PURE__*/ core.$construct
|
|
|
489
489
|
code: "invalid_format",
|
|
490
490
|
format: "url",
|
|
491
491
|
note: "Invalid hostname",
|
|
492
|
-
pattern:
|
|
492
|
+
pattern: def.hostname.source,
|
|
493
493
|
input: payload.value,
|
|
494
494
|
inst,
|
|
495
495
|
continue: !def.abort,
|
|
@@ -741,10 +741,8 @@ export interface $ZodIPv4 extends $ZodType {
|
|
|
741
741
|
export const $ZodIPv4: core.$constructor<$ZodIPv4> = /*@__PURE__*/ core.$constructor("$ZodIPv4", (inst, def): void => {
|
|
742
742
|
def.pattern ??= regexes.ipv4;
|
|
743
743
|
$ZodStringFormat.init(inst, def);
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
bag.format = `ipv4`;
|
|
747
|
-
});
|
|
744
|
+
|
|
745
|
+
inst._zod.bag.format = `ipv4`;
|
|
748
746
|
});
|
|
749
747
|
|
|
750
748
|
////////////////////////////// ZodIPv6 //////////////////////////////
|
|
@@ -765,10 +763,7 @@ export const $ZodIPv6: core.$constructor<$ZodIPv6> = /*@__PURE__*/ core.$constru
|
|
|
765
763
|
def.pattern ??= regexes.ipv6;
|
|
766
764
|
$ZodStringFormat.init(inst, def);
|
|
767
765
|
|
|
768
|
-
inst._zod.
|
|
769
|
-
const bag = inst._zod.bag as $ZodStringInternals<unknown>["bag"];
|
|
770
|
-
bag.format = `ipv6`;
|
|
771
|
-
});
|
|
766
|
+
inst._zod.bag.format = `ipv6`;
|
|
772
767
|
|
|
773
768
|
inst._zod.check = (payload) => {
|
|
774
769
|
try {
|
|
@@ -787,6 +782,26 @@ export const $ZodIPv6: core.$constructor<$ZodIPv6> = /*@__PURE__*/ core.$constru
|
|
|
787
782
|
};
|
|
788
783
|
});
|
|
789
784
|
|
|
785
|
+
////////////////////////////// ZodMAC //////////////////////////////
|
|
786
|
+
export interface $ZodMACDef extends $ZodStringFormatDef<"mac"> {
|
|
787
|
+
delimiter?: string;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
export interface $ZodMACInternals extends $ZodStringFormatInternals<"mac"> {
|
|
791
|
+
def: $ZodMACDef;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
export interface $ZodMAC extends $ZodType {
|
|
795
|
+
_zod: $ZodMACInternals;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
export const $ZodMAC: core.$constructor<$ZodMAC> = /*@__PURE__*/ core.$constructor("$ZodMAC", (inst, def): void => {
|
|
799
|
+
def.pattern ??= regexes.mac(def.delimiter);
|
|
800
|
+
$ZodStringFormat.init(inst, def);
|
|
801
|
+
|
|
802
|
+
inst._zod.bag.format = `mac`;
|
|
803
|
+
});
|
|
804
|
+
|
|
790
805
|
////////////////////////////// ZodCIDRv4 //////////////////////////////
|
|
791
806
|
|
|
792
807
|
export interface $ZodCIDRv4Def extends $ZodStringFormatDef<"cidrv4"> {
|
|
@@ -879,9 +894,7 @@ export const $ZodBase64: core.$constructor<$ZodBase64> = /*@__PURE__*/ core.$con
|
|
|
879
894
|
def.pattern ??= regexes.base64;
|
|
880
895
|
$ZodStringFormat.init(inst, def);
|
|
881
896
|
|
|
882
|
-
inst._zod.
|
|
883
|
-
inst._zod.bag.contentEncoding = "base64";
|
|
884
|
-
});
|
|
897
|
+
inst._zod.bag.contentEncoding = "base64";
|
|
885
898
|
|
|
886
899
|
inst._zod.check = (payload) => {
|
|
887
900
|
if (isValidBase64(payload.value)) return;
|
|
@@ -918,9 +931,7 @@ export const $ZodBase64URL: core.$constructor<$ZodBase64URL> = /*@__PURE__*/ cor
|
|
|
918
931
|
def.pattern ??= regexes.base64url;
|
|
919
932
|
$ZodStringFormat.init(inst, def);
|
|
920
933
|
|
|
921
|
-
inst._zod.
|
|
922
|
-
inst._zod.bag.contentEncoding = "base64url";
|
|
923
|
-
});
|
|
934
|
+
inst._zod.bag.contentEncoding = "base64url";
|
|
924
935
|
|
|
925
936
|
inst._zod.check = (payload) => {
|
|
926
937
|
if (isValidBase64URL(payload.value)) return;
|
|
@@ -1065,8 +1076,8 @@ export interface $ZodNumber<Input = unknown> extends $ZodType {
|
|
|
1065
1076
|
|
|
1066
1077
|
export const $ZodNumber: core.$constructor<$ZodNumber> = /*@__PURE__*/ core.$constructor("$ZodNumber", (inst, def) => {
|
|
1067
1078
|
$ZodType.init(inst, def);
|
|
1068
|
-
inst._zod.pattern = inst._zod.bag.pattern ?? regexes.number;
|
|
1069
1079
|
|
|
1080
|
+
inst._zod.pattern = inst._zod.bag.pattern ?? regexes.number;
|
|
1070
1081
|
inst._zod.parse = (payload, _ctx) => {
|
|
1071
1082
|
if (def.coerce)
|
|
1072
1083
|
try {
|
|
@@ -1113,10 +1124,10 @@ export interface $ZodNumberFormat extends $ZodType {
|
|
|
1113
1124
|
}
|
|
1114
1125
|
|
|
1115
1126
|
export const $ZodNumberFormat: core.$constructor<$ZodNumberFormat> = /*@__PURE__*/ core.$constructor(
|
|
1116
|
-
"$
|
|
1127
|
+
"$ZodNumberFormat",
|
|
1117
1128
|
(inst, def) => {
|
|
1118
1129
|
checks.$ZodCheckNumberFormat.init(inst, def);
|
|
1119
|
-
$ZodNumber.init(inst, def); // no format
|
|
1130
|
+
$ZodNumber.init(inst, def); // no format checks
|
|
1120
1131
|
}
|
|
1121
1132
|
);
|
|
1122
1133
|
|
|
@@ -1237,7 +1248,7 @@ export interface $ZodBigIntFormat extends $ZodType {
|
|
|
1237
1248
|
}
|
|
1238
1249
|
|
|
1239
1250
|
export const $ZodBigIntFormat: core.$constructor<$ZodBigIntFormat> = /*@__PURE__*/ core.$constructor(
|
|
1240
|
-
"$
|
|
1251
|
+
"$ZodBigIntFormat",
|
|
1241
1252
|
(inst, def) => {
|
|
1242
1253
|
checks.$ZodCheckBigIntFormat.init(inst, def);
|
|
1243
1254
|
$ZodBigInt.init(inst, def); // no format checks
|
|
@@ -1792,7 +1803,7 @@ function handleCatchall(
|
|
|
1792
1803
|
const keySet = def.keySet;
|
|
1793
1804
|
const _catchall = def.catchall!._zod;
|
|
1794
1805
|
const t = _catchall.def.type;
|
|
1795
|
-
for (const key
|
|
1806
|
+
for (const key in input) {
|
|
1796
1807
|
if (keySet.has(key)) continue;
|
|
1797
1808
|
if (t === "never") {
|
|
1798
1809
|
unrecognized.push(key);
|
|
@@ -2420,7 +2431,6 @@ export interface $ZodTuple<
|
|
|
2420
2431
|
export const $ZodTuple: core.$constructor<$ZodTuple> = /*@__PURE__*/ core.$constructor("$ZodTuple", (inst, def) => {
|
|
2421
2432
|
$ZodType.init(inst, def);
|
|
2422
2433
|
const items = def.items;
|
|
2423
|
-
const optStart = items.length - [...items].reverse().findIndex((item) => item._zod.optin !== "optional");
|
|
2424
2434
|
|
|
2425
2435
|
inst._zod.parse = (payload, ctx) => {
|
|
2426
2436
|
const input = payload.value;
|
|
@@ -2437,6 +2447,9 @@ export const $ZodTuple: core.$constructor<$ZodTuple> = /*@__PURE__*/ core.$const
|
|
|
2437
2447
|
payload.value = [];
|
|
2438
2448
|
const proms: Promise<any>[] = [];
|
|
2439
2449
|
|
|
2450
|
+
const reversedIndex = [...items].reverse().findIndex((item) => item._zod.optin !== "optional");
|
|
2451
|
+
const optStart = reversedIndex === -1 ? 0 : items.length - reversedIndex;
|
|
2452
|
+
|
|
2440
2453
|
if (!def.rest) {
|
|
2441
2454
|
const tooBig = input.length > items.length;
|
|
2442
2455
|
const tooSmall = input.length < optStart - 1;
|
|
@@ -2590,11 +2603,13 @@ export const $ZodRecord: core.$constructor<$ZodRecord> = /*@__PURE__*/ core.$con
|
|
|
2590
2603
|
|
|
2591
2604
|
const proms: Promise<any>[] = [];
|
|
2592
2605
|
|
|
2593
|
-
|
|
2594
|
-
|
|
2606
|
+
const values = def.keyType._zod.values;
|
|
2607
|
+
if (values) {
|
|
2595
2608
|
payload.value = {};
|
|
2609
|
+
const recordKeys = new Set<string | symbol>();
|
|
2596
2610
|
for (const key of values) {
|
|
2597
2611
|
if (typeof key === "string" || typeof key === "number" || typeof key === "symbol") {
|
|
2612
|
+
recordKeys.add(typeof key === "number" ? key.toString() : key);
|
|
2598
2613
|
const result = def.valueType._zod.run({ value: input[key], issues: [] }, ctx);
|
|
2599
2614
|
|
|
2600
2615
|
if (result instanceof Promise) {
|
|
@@ -2617,7 +2632,7 @@ export const $ZodRecord: core.$constructor<$ZodRecord> = /*@__PURE__*/ core.$con
|
|
|
2617
2632
|
|
|
2618
2633
|
let unrecognized!: string[];
|
|
2619
2634
|
for (const key in input) {
|
|
2620
|
-
if (!
|
|
2635
|
+
if (!recordKeys.has(key)) {
|
|
2621
2636
|
unrecognized = unrecognized ?? [];
|
|
2622
2637
|
unrecognized.push(key);
|
|
2623
2638
|
}
|
|
@@ -2942,7 +2957,8 @@ export const $ZodLiteral: core.$constructor<$ZodLiteral> = /*@__PURE__*/ core.$c
|
|
|
2942
2957
|
throw new Error("Cannot create literal schema with no valid values");
|
|
2943
2958
|
}
|
|
2944
2959
|
|
|
2945
|
-
|
|
2960
|
+
const values = new Set<util.Literal>(def.values);
|
|
2961
|
+
inst._zod.values = values;
|
|
2946
2962
|
inst._zod.pattern = new RegExp(
|
|
2947
2963
|
`^(${def.values
|
|
2948
2964
|
|
|
@@ -2952,7 +2968,7 @@ export const $ZodLiteral: core.$constructor<$ZodLiteral> = /*@__PURE__*/ core.$c
|
|
|
2952
2968
|
|
|
2953
2969
|
inst._zod.parse = (payload, _ctx) => {
|
|
2954
2970
|
const input = payload.value;
|
|
2955
|
-
if (
|
|
2971
|
+
if (values.has(input)) {
|
|
2956
2972
|
return payload;
|
|
2957
2973
|
}
|
|
2958
2974
|
payload.issues.push({
|
|
@@ -3779,8 +3795,8 @@ export const $ZodReadonly: core.$constructor<$ZodReadonly> = /*@__PURE__*/ core.
|
|
|
3779
3795
|
$ZodType.init(inst, def);
|
|
3780
3796
|
util.defineLazy(inst._zod, "propValues", () => def.innerType._zod.propValues);
|
|
3781
3797
|
util.defineLazy(inst._zod, "values", () => def.innerType._zod.values);
|
|
3782
|
-
util.defineLazy(inst._zod, "optin", () => def.innerType
|
|
3783
|
-
util.defineLazy(inst._zod, "optout", () => def.innerType
|
|
3798
|
+
util.defineLazy(inst._zod, "optin", () => def.innerType?._zod?.optin);
|
|
3799
|
+
util.defineLazy(inst._zod, "optout", () => def.innerType?._zod?.optout);
|
|
3784
3800
|
|
|
3785
3801
|
inst._zod.parse = (payload, ctx) => {
|
|
3786
3802
|
if (ctx.direction === "backward") {
|
|
@@ -4112,7 +4128,7 @@ export interface $ZodPromiseDef<T extends SomeType = $ZodType> extends $ZodTypeD
|
|
|
4112
4128
|
}
|
|
4113
4129
|
|
|
4114
4130
|
export interface $ZodPromiseInternals<T extends SomeType = $ZodType>
|
|
4115
|
-
extends $ZodTypeInternals<core.output<T
|
|
4131
|
+
extends $ZodTypeInternals<Promise<core.output<T>>, util.MaybeAsync<core.input<T>>> {
|
|
4116
4132
|
def: $ZodPromiseDef<T>;
|
|
4117
4133
|
isst: never;
|
|
4118
4134
|
}
|
|
@@ -4172,10 +4188,10 @@ export const $ZodLazy: core.$constructor<$ZodLazy> = /*@__PURE__*/ core.$constru
|
|
|
4172
4188
|
// return () => _innerType;
|
|
4173
4189
|
// });
|
|
4174
4190
|
util.defineLazy(inst._zod, "innerType", () => def.getter() as $ZodType);
|
|
4175
|
-
util.defineLazy(inst._zod, "pattern", () => inst._zod.innerType
|
|
4176
|
-
util.defineLazy(inst._zod, "propValues", () => inst._zod.innerType
|
|
4177
|
-
util.defineLazy(inst._zod, "optin", () => inst._zod.innerType
|
|
4178
|
-
util.defineLazy(inst._zod, "optout", () => inst._zod.innerType
|
|
4191
|
+
util.defineLazy(inst._zod, "pattern", () => inst._zod.innerType?._zod?.pattern);
|
|
4192
|
+
util.defineLazy(inst._zod, "propValues", () => inst._zod.innerType?._zod?.propValues);
|
|
4193
|
+
util.defineLazy(inst._zod, "optin", () => inst._zod.innerType?._zod?.optin ?? undefined);
|
|
4194
|
+
util.defineLazy(inst._zod, "optout", () => inst._zod.innerType?._zod?.optout ?? undefined);
|
|
4179
4195
|
inst._zod.parse = (payload, ctx) => {
|
|
4180
4196
|
const inner = inst._zod.innerType;
|
|
4181
4197
|
return inner._zod.run(payload, ctx);
|
|
@@ -4306,6 +4322,7 @@ export type $ZodStringFormatTypes =
|
|
|
4306
4322
|
| $ZodISODuration
|
|
4307
4323
|
| $ZodIPv4
|
|
4308
4324
|
| $ZodIPv6
|
|
4325
|
+
| $ZodMAC
|
|
4309
4326
|
| $ZodCIDRv4
|
|
4310
4327
|
| $ZodCIDRv6
|
|
4311
4328
|
| $ZodBase64
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { test } from "vitest";
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
2
|
import * as z from "zod/v4";
|
|
3
3
|
|
|
4
4
|
test("extend chaining preserves and overrides properties", () => {
|
|
@@ -16,3 +16,44 @@ test("extend chaining preserves and overrides properties", () => {
|
|
|
16
16
|
|
|
17
17
|
schema3.parse({ email: "test@example.com" });
|
|
18
18
|
});
|
|
19
|
+
|
|
20
|
+
test("extend with constructor field in shape", () => {
|
|
21
|
+
const baseSchema = z.object({
|
|
22
|
+
name: z.string(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const extendedSchema = baseSchema.extend({
|
|
26
|
+
constructor: z.string(),
|
|
27
|
+
age: z.number(),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const result = extendedSchema.parse({
|
|
31
|
+
name: "John",
|
|
32
|
+
constructor: "Person",
|
|
33
|
+
age: 30,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
expect(result).toEqual({
|
|
37
|
+
name: "John",
|
|
38
|
+
constructor: "Person",
|
|
39
|
+
age: 30,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const testCases = [
|
|
43
|
+
{ name: "Test", constructor: 123, age: 25 },
|
|
44
|
+
{ name: "Test", constructor: null, age: 25 },
|
|
45
|
+
{ name: "Test", constructor: true, age: 25 },
|
|
46
|
+
{ name: "Test", constructor: {}, age: 25 },
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
for (const testCase of testCases) {
|
|
50
|
+
const anyConstructorSchema = baseSchema.extend({
|
|
51
|
+
constructor: z.any(),
|
|
52
|
+
age: z.number(),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
expect(() => anyConstructorSchema.parse(testCase)).not.toThrow();
|
|
56
|
+
const parsed = anyConstructorSchema.parse(testCase);
|
|
57
|
+
expect(parsed).toEqual(testCase);
|
|
58
|
+
}
|
|
59
|
+
});
|