zod 4.6.2 → 4.6.4
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/README.md +2 -2
- package/package.json +1 -1
- package/src/v4/classic/checks.ts +1 -0
- package/src/v4/classic/schemas.ts +12 -29
- package/src/v4/classic/tests/assignability.test.ts +1 -0
- package/src/v4/classic/tests/cyclic-data.test.ts +2 -11
- package/src/v4/classic/tests/firstparty.test.ts +0 -4
- package/src/v4/classic/tests/instance-footprint.test.ts +22 -0
- package/src/v4/classic/tests/instanceof.test.ts +1 -1
- package/src/v4/classic/tests/number.test.ts +39 -0
- package/src/v4/classic/tests/properties.test.ts +34 -137
- package/src/v4/classic/tests/string.test.ts +70 -1
- package/src/v4/classic/tests/to-json-schema.test.ts +87 -35
- package/src/v4/core/api.ts +21 -29
- package/src/v4/core/checks.ts +61 -1
- package/src/v4/core/compile.ts +35 -24
- package/src/v4/core/core.ts +1 -3
- package/src/v4/core/json-schema-processors.ts +9 -63
- package/src/v4/core/memoizer.ts +2 -5
- package/src/v4/core/regexes.ts +4 -0
- package/src/v4/core/schemas.ts +36 -109
- package/src/v4/core/tests/compile-differential.test.ts +35 -0
- package/src/v4/core/tests/compile.test.ts +177 -0
- package/src/v4/core/tests/url-no-canparse.test.ts +118 -16
- package/src/v4/core/to-json-schema.ts +1 -1
- package/src/v4/core/util.ts +0 -4
- package/src/v4/core/versions.ts +1 -1
- package/src/v4/core/visit.ts +0 -11
- package/src/v4/locales/ar.ts +1 -0
- package/src/v4/locales/az.ts +1 -0
- package/src/v4/locales/be.ts +1 -0
- package/src/v4/locales/bg.ts +1 -0
- package/src/v4/locales/bn.ts +1 -0
- package/src/v4/locales/ca.ts +1 -0
- package/src/v4/locales/ckb.ts +1 -0
- package/src/v4/locales/cs.ts +1 -0
- package/src/v4/locales/da.ts +1 -0
- package/src/v4/locales/de.ts +1 -0
- package/src/v4/locales/el.ts +1 -0
- package/src/v4/locales/en.ts +1 -0
- package/src/v4/locales/eo.ts +1 -0
- package/src/v4/locales/es.ts +1 -0
- package/src/v4/locales/fa.ts +1 -0
- package/src/v4/locales/fi.ts +1 -0
- package/src/v4/locales/fr-CA.ts +1 -0
- package/src/v4/locales/fr.ts +1 -0
- package/src/v4/locales/gu.ts +1 -0
- package/src/v4/locales/he.ts +1 -0
- package/src/v4/locales/hi.ts +1 -0
- package/src/v4/locales/hr.ts +1 -0
- package/src/v4/locales/hu.ts +1 -0
- package/src/v4/locales/hy.ts +1 -0
- package/src/v4/locales/id.ts +1 -0
- package/src/v4/locales/is.ts +1 -0
- package/src/v4/locales/it.ts +1 -0
- package/src/v4/locales/ja.ts +1 -0
- package/src/v4/locales/ka.ts +1 -0
- package/src/v4/locales/km.ts +1 -0
- package/src/v4/locales/kn.ts +1 -0
- package/src/v4/locales/ko.ts +1 -0
- package/src/v4/locales/lt.ts +1 -0
- package/src/v4/locales/mk.ts +1 -0
- package/src/v4/locales/ms.ts +1 -0
- package/src/v4/locales/ne.ts +1 -0
- package/src/v4/locales/nl.ts +1 -0
- package/src/v4/locales/nn.ts +1 -0
- package/src/v4/locales/no.ts +1 -0
- package/src/v4/locales/ota.ts +1 -0
- package/src/v4/locales/pl.ts +1 -0
- package/src/v4/locales/ps.ts +1 -0
- package/src/v4/locales/pt-BR.ts +1 -0
- package/src/v4/locales/pt.ts +1 -0
- package/src/v4/locales/ro.ts +1 -0
- package/src/v4/locales/ru.ts +1 -0
- package/src/v4/locales/sk.ts +1 -0
- package/src/v4/locales/sl.ts +1 -0
- package/src/v4/locales/sv.ts +1 -0
- package/src/v4/locales/ta.ts +1 -0
- package/src/v4/locales/tg.ts +1 -0
- package/src/v4/locales/th.ts +1 -0
- package/src/v4/locales/tk.ts +1 -0
- package/src/v4/locales/tr.ts +1 -0
- package/src/v4/locales/uk.ts +1 -0
- package/src/v4/locales/ur.ts +1 -0
- package/src/v4/locales/uz.ts +1 -0
- package/src/v4/locales/vi.ts +1 -0
- package/src/v4/locales/yo.ts +1 -0
- package/src/v4/locales/zh-CN.ts +1 -0
- package/src/v4/locales/zh-TW.ts +1 -0
- package/src/v4/mini/checks.ts +1 -0
- package/src/v4/mini/schemas.ts +12 -23
- package/src/v4/mini/tests/index.test.ts +56 -0
- package/src/v4/mini/tests/string.test.ts +7 -0
- 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 +11 -15
- package/v4/classic/schemas.d.cts +2 -6
- package/v4/classic/schemas.d.ts +2 -6
- package/v4/classic/schemas.js +9 -13
- package/v4/core/api.cjs +11 -23
- package/v4/core/api.d.cts +8 -4
- package/v4/core/api.d.ts +8 -4
- package/v4/core/api.js +11 -23
- package/v4/core/checks.cjs +32 -1
- package/v4/core/checks.d.cts +16 -0
- package/v4/core/checks.d.ts +16 -0
- package/v4/core/checks.js +31 -0
- package/v4/core/compile.cjs +12 -17
- package/v4/core/compile.js +13 -18
- package/v4/core/core.cjs +1 -1
- package/v4/core/core.js +1 -1
- package/v4/core/json-schema-processors.cjs +8 -44
- package/v4/core/json-schema-processors.d.cts +0 -1
- package/v4/core/json-schema-processors.d.ts +0 -1
- package/v4/core/json-schema-processors.js +8 -43
- package/v4/core/memoizer.cjs +2 -5
- package/v4/core/memoizer.js +2 -5
- package/v4/core/regexes.cjs +4 -2
- package/v4/core/regexes.d.cts +1 -0
- package/v4/core/regexes.d.ts +1 -0
- package/v4/core/regexes.js +2 -0
- package/v4/core/schemas.cjs +33 -73
- package/v4/core/schemas.d.cts +7 -22
- package/v4/core/schemas.d.ts +7 -22
- package/v4/core/schemas.js +30 -72
- package/v4/core/to-json-schema.cjs +0 -1
- package/v4/core/to-json-schema.d.cts +0 -3
- package/v4/core/to-json-schema.d.ts +0 -3
- package/v4/core/to-json-schema.js +1 -1
- package/v4/core/util.cjs +0 -4
- package/v4/core/util.js +0 -4
- package/v4/core/versions.cjs +1 -1
- package/v4/core/versions.js +1 -1
- package/v4/core/visit.cjs +0 -12
- package/v4/core/visit.js +0 -12
- package/v4/locales/ar.cjs +1 -0
- package/v4/locales/ar.js +1 -0
- package/v4/locales/az.cjs +1 -0
- package/v4/locales/az.js +1 -0
- package/v4/locales/be.cjs +1 -0
- package/v4/locales/be.js +1 -0
- package/v4/locales/bg.cjs +1 -0
- package/v4/locales/bg.js +1 -0
- package/v4/locales/bn.cjs +1 -0
- package/v4/locales/bn.js +1 -0
- package/v4/locales/ca.cjs +1 -0
- package/v4/locales/ca.js +1 -0
- package/v4/locales/ckb.cjs +1 -0
- package/v4/locales/ckb.js +1 -0
- package/v4/locales/cs.cjs +1 -0
- package/v4/locales/cs.js +1 -0
- package/v4/locales/da.cjs +1 -0
- package/v4/locales/da.js +1 -0
- package/v4/locales/de.cjs +1 -0
- package/v4/locales/de.js +1 -0
- package/v4/locales/el.cjs +1 -0
- package/v4/locales/el.js +1 -0
- package/v4/locales/en.cjs +1 -0
- package/v4/locales/en.js +1 -0
- package/v4/locales/eo.cjs +1 -0
- package/v4/locales/eo.js +1 -0
- package/v4/locales/es.cjs +1 -0
- package/v4/locales/es.js +1 -0
- package/v4/locales/fa.cjs +1 -0
- package/v4/locales/fa.js +1 -0
- package/v4/locales/fi.cjs +1 -0
- package/v4/locales/fi.js +1 -0
- package/v4/locales/fr-CA.cjs +1 -0
- package/v4/locales/fr-CA.js +1 -0
- package/v4/locales/fr.cjs +1 -0
- package/v4/locales/fr.js +1 -0
- package/v4/locales/gu.cjs +1 -0
- package/v4/locales/gu.js +1 -0
- package/v4/locales/he.cjs +1 -0
- package/v4/locales/he.js +1 -0
- package/v4/locales/hi.cjs +1 -0
- package/v4/locales/hi.js +1 -0
- package/v4/locales/hr.cjs +1 -0
- package/v4/locales/hr.js +1 -0
- package/v4/locales/hu.cjs +1 -0
- package/v4/locales/hu.js +1 -0
- package/v4/locales/hy.cjs +1 -0
- package/v4/locales/hy.js +1 -0
- package/v4/locales/id.cjs +1 -0
- package/v4/locales/id.js +1 -0
- package/v4/locales/is.cjs +1 -0
- package/v4/locales/is.js +1 -0
- package/v4/locales/it.cjs +1 -0
- package/v4/locales/it.js +1 -0
- package/v4/locales/ja.cjs +1 -0
- package/v4/locales/ja.js +1 -0
- package/v4/locales/ka.cjs +1 -0
- package/v4/locales/ka.js +1 -0
- package/v4/locales/km.cjs +1 -0
- package/v4/locales/km.js +1 -0
- package/v4/locales/kn.cjs +1 -0
- package/v4/locales/kn.js +1 -0
- package/v4/locales/ko.cjs +1 -0
- package/v4/locales/ko.js +1 -0
- package/v4/locales/lt.cjs +1 -0
- package/v4/locales/lt.js +1 -0
- package/v4/locales/mk.cjs +1 -0
- package/v4/locales/mk.js +1 -0
- package/v4/locales/ms.cjs +1 -0
- package/v4/locales/ms.js +1 -0
- package/v4/locales/ne.cjs +1 -0
- package/v4/locales/ne.js +1 -0
- package/v4/locales/nl.cjs +1 -0
- package/v4/locales/nl.js +1 -0
- package/v4/locales/nn.cjs +1 -0
- package/v4/locales/nn.js +1 -0
- package/v4/locales/no.cjs +1 -0
- package/v4/locales/no.js +1 -0
- package/v4/locales/ota.cjs +1 -0
- package/v4/locales/ota.js +1 -0
- package/v4/locales/pl.cjs +1 -0
- package/v4/locales/pl.js +1 -0
- package/v4/locales/ps.cjs +1 -0
- package/v4/locales/ps.js +1 -0
- package/v4/locales/pt-BR.cjs +1 -0
- package/v4/locales/pt-BR.js +1 -0
- package/v4/locales/pt.cjs +1 -0
- package/v4/locales/pt.js +1 -0
- package/v4/locales/ro.cjs +1 -0
- package/v4/locales/ro.js +1 -0
- package/v4/locales/ru.cjs +1 -0
- package/v4/locales/ru.js +1 -0
- package/v4/locales/sk.cjs +1 -0
- package/v4/locales/sk.js +1 -0
- package/v4/locales/sl.cjs +1 -0
- package/v4/locales/sl.js +1 -0
- package/v4/locales/sv.cjs +1 -0
- package/v4/locales/sv.js +1 -0
- package/v4/locales/ta.cjs +1 -0
- package/v4/locales/ta.js +1 -0
- package/v4/locales/tg.cjs +1 -0
- package/v4/locales/tg.js +1 -0
- package/v4/locales/th.cjs +1 -0
- package/v4/locales/th.js +1 -0
- package/v4/locales/tk.cjs +1 -0
- package/v4/locales/tk.js +1 -0
- package/v4/locales/tr.cjs +1 -0
- package/v4/locales/tr.js +1 -0
- package/v4/locales/uk.cjs +1 -0
- package/v4/locales/uk.js +1 -0
- package/v4/locales/ur.cjs +1 -0
- package/v4/locales/ur.js +1 -0
- package/v4/locales/uz.cjs +1 -0
- package/v4/locales/uz.js +1 -0
- package/v4/locales/vi.cjs +1 -0
- package/v4/locales/vi.js +1 -0
- package/v4/locales/yo.cjs +1 -0
- package/v4/locales/yo.js +1 -0
- package/v4/locales/zh-CN.cjs +1 -0
- package/v4/locales/zh-CN.js +1 -0
- package/v4/locales/zh-TW.cjs +1 -0
- package/v4/locales/zh-TW.js +1 -0
- package/v4/mini/checks.cjs +2 -1
- package/v4/mini/checks.d.cts +1 -1
- package/v4/mini/checks.d.ts +1 -1
- package/v4/mini/checks.js +1 -1
- package/v4/mini/schemas.cjs +11 -13
- package/v4/mini/schemas.d.cts +1 -4
- package/v4/mini/schemas.d.ts +1 -4
- package/v4/mini/schemas.js +9 -11
package/src/v4/core/schemas.ts
CHANGED
|
@@ -96,8 +96,7 @@ export interface $ZodTypeDef {
|
|
|
96
96
|
| "promise"
|
|
97
97
|
| "lazy"
|
|
98
98
|
| "function"
|
|
99
|
-
| "custom"
|
|
100
|
-
| "properties";
|
|
99
|
+
| "custom";
|
|
101
100
|
error?: errors.$ZodErrorMap<never> | undefined;
|
|
102
101
|
checks?: checks.$ZodCheck<never>[];
|
|
103
102
|
}
|
|
@@ -523,10 +522,30 @@ export interface $ZodURL extends $ZodType {
|
|
|
523
522
|
|
|
524
523
|
/** The `://` guard rejected the input before the URL constructor saw it. */
|
|
525
524
|
export const URL_BAD_FORMAT = 1;
|
|
526
|
-
/** The URL
|
|
525
|
+
/** The URL parser rejected the input. */
|
|
527
526
|
export const URL_UNPARSEABLE = 2;
|
|
528
527
|
|
|
529
|
-
|
|
528
|
+
export function canParseURL(input: string): boolean {
|
|
529
|
+
try {
|
|
530
|
+
if (typeof URL !== "undefined" && typeof URL.canParse === "function") return URL.canParse(input);
|
|
531
|
+
new URL(input);
|
|
532
|
+
return true;
|
|
533
|
+
} catch {
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
export function validateURL(
|
|
539
|
+
trimmed: string,
|
|
540
|
+
def: Pick<$ZodURLDef, "protocol" | "hostname" | "normalize">
|
|
541
|
+
): URL | true | typeof URL_BAD_FORMAT | typeof URL_UNPARSEABLE {
|
|
542
|
+
if (!("normalize" in def) && !("hostname" in def) && !("protocol" in def)) {
|
|
543
|
+
return canParseURL(trimmed) || URL_UNPARSEABLE;
|
|
544
|
+
}
|
|
545
|
+
return parseURLObject(trimmed, def);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/** Parses a URL while preserving the non-normalizing HTTP guard. */
|
|
530
549
|
export function parseURLObject(
|
|
531
550
|
trimmed: string,
|
|
532
551
|
def: Pick<$ZodURLDef, "protocol" | "normalize">
|
|
@@ -537,6 +556,10 @@ export function parseURLObject(
|
|
|
537
556
|
}
|
|
538
557
|
|
|
539
558
|
try {
|
|
559
|
+
if (typeof URL !== "undefined") {
|
|
560
|
+
const URLStatic = URL as typeof URL & { parse?: (input: string) => URL | null };
|
|
561
|
+
if (typeof URLStatic.parse === "function") return URLStatic.parse(trimmed) ?? URL_UNPARSEABLE;
|
|
562
|
+
}
|
|
540
563
|
// @ts-ignore
|
|
541
564
|
return new URL(trimmed);
|
|
542
565
|
} catch {
|
|
@@ -567,7 +590,7 @@ export const $ZodURL: core.$constructor<$ZodURL> = /*@__PURE__*/ core.$construct
|
|
|
567
590
|
try {
|
|
568
591
|
// Trim whitespace from input
|
|
569
592
|
const trimmed = payload.value.trim();
|
|
570
|
-
const url =
|
|
593
|
+
const url = validateURL(trimmed, def);
|
|
571
594
|
|
|
572
595
|
if (url === URL_BAD_FORMAT) {
|
|
573
596
|
payload.issues.push({
|
|
@@ -592,6 +615,11 @@ export const $ZodURL: core.$constructor<$ZodURL> = /*@__PURE__*/ core.$construct
|
|
|
592
615
|
return;
|
|
593
616
|
}
|
|
594
617
|
|
|
618
|
+
if (url === true) {
|
|
619
|
+
payload.value = stripTabAndNewline(trimmed);
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
|
|
595
623
|
if (def.hostname && !urlHostnameOk(url, def.hostname)) {
|
|
596
624
|
payload.issues.push({
|
|
597
625
|
code: "invalid_format",
|
|
@@ -886,13 +914,7 @@ const ipv6Alphabet = /^[0-9a-fA-F:.]+$/;
|
|
|
886
914
|
|
|
887
915
|
export function isValidIPv6(value: string): boolean {
|
|
888
916
|
if (!ipv6Alphabet.test(value)) return false;
|
|
889
|
-
|
|
890
|
-
// @ts-ignore
|
|
891
|
-
new URL(`http://[${value}]`);
|
|
892
|
-
return true;
|
|
893
|
-
} catch {
|
|
894
|
-
return false;
|
|
895
|
-
}
|
|
917
|
+
return canParseURL(`http://[${value}]`);
|
|
896
918
|
}
|
|
897
919
|
|
|
898
920
|
export const $ZodIPv6: core.$constructor<$ZodIPv6> = /*@__PURE__*/ core.$constructor("$ZodIPv6", (inst, def): void => {
|
|
@@ -5096,101 +5118,6 @@ function handleRefineResult(result: unknown, payload: ParsePayload, input: unkno
|
|
|
5096
5118
|
}
|
|
5097
5119
|
}
|
|
5098
5120
|
|
|
5099
|
-
////////////////////////////////////////
|
|
5100
|
-
////////////////////////////////////////
|
|
5101
|
-
////////// //////////
|
|
5102
|
-
////////// $ZodProperties //////////
|
|
5103
|
-
////////// //////////
|
|
5104
|
-
////////////////////////////////////////
|
|
5105
|
-
////////////////////////////////////////
|
|
5106
|
-
export interface $ZodPropertiesDef<Shape extends $ZodShape = $ZodShape> extends $ZodTypeDef, checks.$ZodCheckDef {
|
|
5107
|
-
type: "properties";
|
|
5108
|
-
check: "properties";
|
|
5109
|
-
shape: Shape;
|
|
5110
|
-
}
|
|
5111
|
-
|
|
5112
|
-
// both sides infer the INPUT type: the shape is asserted and its results discarded, so a default, catch or transform inside it would make an output-typed inference a lie. The check side widens a literal to its primitive, so spreading over a `string` property still type-checks (#6520).
|
|
5113
|
-
export interface $ZodPropertiesInternals<Shape extends $ZodShape = $ZodShape>
|
|
5114
|
-
extends $ZodTypeInternals<$InferObjectInput<Shape, {}>, $InferObjectInput<Shape, {}>>,
|
|
5115
|
-
checks.$ZodCheckInternals<{ -readonly [k in keyof Shape]: util.Widen<core.input<Shape[k]>> }> {
|
|
5116
|
-
def: $ZodPropertiesDef<Shape>;
|
|
5117
|
-
isst: errors.$ZodIssueInvalidType;
|
|
5118
|
-
issc: errors.$ZodIssue;
|
|
5119
|
-
}
|
|
5120
|
-
|
|
5121
|
-
export interface $ZodProperties<Shape extends $ZodShape = $ZodShape> extends $ZodType {
|
|
5122
|
-
_zod: $ZodPropertiesInternals<Shape>;
|
|
5123
|
-
// yields the schema itself, so pre-4.6 `.check(...z.properties(shape))` spread call sites keep working
|
|
5124
|
-
[Symbol.iterator](): Iterator<this>;
|
|
5125
|
-
}
|
|
5126
|
-
|
|
5127
|
-
// asserts in place: the child result's value is discarded, matching z.property(), because a nested object or array schema rebuilds its output even when nothing transformed
|
|
5128
|
-
function handlePropertiesResult(result: ParsePayload, payload: ParsePayload, key: string | symbol): void {
|
|
5129
|
-
if (result.issues.length) {
|
|
5130
|
-
payload.issues.push(...util.prefixIssues(key, result.issues));
|
|
5131
|
-
}
|
|
5132
|
-
}
|
|
5133
|
-
|
|
5134
|
-
export const $ZodProperties: core.$constructor<$ZodProperties> = /*@__PURE__*/ core.$constructor(
|
|
5135
|
-
"$ZodProperties",
|
|
5136
|
-
(inst, def) => {
|
|
5137
|
-
// $ZodType.init prepends an instance that already carries the $ZodCheck trait to its own `checks`, which would run the shape a second time and cost the parse context. Initializing the check trait after it keeps the schema role in `parse`, where the context arrives.
|
|
5138
|
-
$ZodType.init(inst, def);
|
|
5139
|
-
checks.$ZodCheck.init(inst, def);
|
|
5140
|
-
|
|
5141
|
-
const memo = core.globalConfig.memoizer;
|
|
5142
|
-
memo?.attach(inst);
|
|
5143
|
-
|
|
5144
|
-
// key and schema snapshotted together: reading one live and the other cached lets a later mutation of the caller's shape object pair a stale key with a missing schema
|
|
5145
|
-
let entries!: [string | symbol, $ZodType][];
|
|
5146
|
-
const runShape = (payload: ParsePayload, ctx: ParseContextInternal): util.MaybeAsync<void> => {
|
|
5147
|
-
entries ??= Reflect.ownKeys(def.shape).map((key) => [key, (def.shape as any)[key] as $ZodType]);
|
|
5148
|
-
const input = payload.value as any;
|
|
5149
|
-
let proms: Promise<any>[] | undefined;
|
|
5150
|
-
for (const [key, schema] of entries) {
|
|
5151
|
-
const result = schema._zod.run({ value: input[key], issues: [] }, ctx);
|
|
5152
|
-
if (result instanceof Promise) {
|
|
5153
|
-
proms ??= [];
|
|
5154
|
-
proms.push(result.then((result) => handlePropertiesResult(result, payload, key)));
|
|
5155
|
-
} else {
|
|
5156
|
-
handlePropertiesResult(result, payload, key);
|
|
5157
|
-
}
|
|
5158
|
-
}
|
|
5159
|
-
if (proms) return Promise.all(proms).then(() => undefined);
|
|
5160
|
-
return undefined;
|
|
5161
|
-
};
|
|
5162
|
-
|
|
5163
|
-
inst._zod.parse = (payload, ctx) => {
|
|
5164
|
-
const input = payload.value;
|
|
5165
|
-
// as a schema this is the type gate, and it infers an object shape, so a primitive is a type error. A function passes: its properties read like any other object's, and z.instanceof allows one.
|
|
5166
|
-
if (input === null || (typeof input !== "object" && typeof input !== "function")) {
|
|
5167
|
-
payload.issues.push({ expected: "object", code: "invalid_type", input, inst });
|
|
5168
|
-
return payload;
|
|
5169
|
-
}
|
|
5170
|
-
// both sides declare the shape's input type, so the assertion runs forward in either direction; encoding the children backward would reject the very type this schema claims to take
|
|
5171
|
-
if (ctx.direction === "backward") ctx = { ...ctx, direction: "forward" };
|
|
5172
|
-
// the input is its own output here, so it registers as its own memo entry: a cycle re-entering this node hits the bucket instead of recursing forever
|
|
5173
|
-
if (memo) memo.alloc(inst, payload, input, ctx);
|
|
5174
|
-
const result = runShape(payload, ctx);
|
|
5175
|
-
return result instanceof Promise ? result.then(() => payload) : payload;
|
|
5176
|
-
};
|
|
5177
|
-
|
|
5178
|
-
// as a check the base schema already typed the value, so this only asserts the properties — which read on a primitive too, matching z.property() on a string's length. It gets no context of its own, so a cycle through a spread schema is not tracked.
|
|
5179
|
-
inst._zod.check = (payload) => {
|
|
5180
|
-
if (payload.value == null) {
|
|
5181
|
-
payload.issues.push({ expected: "object", code: "invalid_type", input: payload.value, inst });
|
|
5182
|
-
return undefined;
|
|
5183
|
-
}
|
|
5184
|
-
return runShape(payload, {});
|
|
5185
|
-
};
|
|
5186
|
-
},
|
|
5187
|
-
{
|
|
5188
|
-
*[Symbol.iterator]() {
|
|
5189
|
-
yield this;
|
|
5190
|
-
},
|
|
5191
|
-
}
|
|
5192
|
-
);
|
|
5193
|
-
|
|
5194
5121
|
export type $ZodTypes =
|
|
5195
5122
|
| $ZodString
|
|
5196
5123
|
| $ZodNumber
|
|
@@ -5223,7 +5150,6 @@ export type $ZodTypes =
|
|
|
5223
5150
|
| $ZodPrefault
|
|
5224
5151
|
| $ZodTemplateLiteral
|
|
5225
5152
|
| $ZodCustom
|
|
5226
|
-
| $ZodProperties
|
|
5227
5153
|
| $ZodTransform
|
|
5228
5154
|
| $ZodNonOptional
|
|
5229
5155
|
| $ZodReadonly
|
|
@@ -5262,4 +5188,5 @@ export type $ZodStringFormatTypes =
|
|
|
5262
5188
|
| $ZodJWT
|
|
5263
5189
|
| $ZodCustomStringFormat<"hex">
|
|
5264
5190
|
| $ZodCustomStringFormat<util.HashFormat>
|
|
5265
|
-
| $ZodCustomStringFormat<"hostname"
|
|
5191
|
+
| $ZodCustomStringFormat<"hostname">
|
|
5192
|
+
| $ZodCustomStringFormat<"currency_code">;
|
|
@@ -677,6 +677,41 @@ test("url options match the runtime across the whole option matrix", () => {
|
|
|
677
677
|
for (const schema of schemas) differential(schema, inputs);
|
|
678
678
|
});
|
|
679
679
|
|
|
680
|
+
test("assert-only URL generation skips unused output without skipping chained transforms", () => {
|
|
681
|
+
const sanitized = "https://example.com";
|
|
682
|
+
const withNewline = "https://exa\nmple.com";
|
|
683
|
+
differential(z.url().length(sanitized.length), [sanitized, withNewline, "https://example.com/long"]);
|
|
684
|
+
differential(z.string().url().length(sanitized.length), [sanitized, withNewline, "https://example.com/long"]);
|
|
685
|
+
|
|
686
|
+
const URLStatic = URL as typeof URL & { parse: (input: string) => URL | null };
|
|
687
|
+
const descriptor = Object.getOwnPropertyDescriptor(URLStatic, "parse")!;
|
|
688
|
+
const marker = new Error("href read");
|
|
689
|
+
Object.defineProperty(URLStatic, "parse", {
|
|
690
|
+
configurable: true,
|
|
691
|
+
value: () =>
|
|
692
|
+
Object.create(URL.prototype, {
|
|
693
|
+
href: {
|
|
694
|
+
get() {
|
|
695
|
+
throw marker;
|
|
696
|
+
},
|
|
697
|
+
},
|
|
698
|
+
}) as URL,
|
|
699
|
+
});
|
|
700
|
+
try {
|
|
701
|
+
const schema = z.url({ normalize: true });
|
|
702
|
+
const parser = compileFn(schema);
|
|
703
|
+
const validator = compileFn(schema, { assertOnly: true });
|
|
704
|
+
expect(validator("https://example.com")).toBe(true);
|
|
705
|
+
expect(z.validate(compile(z.url({ normalize: true })), "https://example.com")).toBe(true);
|
|
706
|
+
expect(z.validate(compile(z.object({ url: z.url({ normalize: true }) })), { url: "https://example.com" })).toBe(
|
|
707
|
+
true
|
|
708
|
+
);
|
|
709
|
+
expect(() => parser("https://example.com")).toThrow(marker);
|
|
710
|
+
} finally {
|
|
711
|
+
Object.defineProperty(URLStatic, "parse", descriptor);
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
|
|
680
715
|
test("empty strict object compiles", () => {
|
|
681
716
|
// With no keys the unknown-key condition is empty, and `if () return INVALID;` is a syntax error the single top-level `new Function` rejects — too late for compileChild to island, so the whole tree lost its fast path.
|
|
682
717
|
differential(z.strictObject({}), [{}, { a: 1 }, Object.create({ inherited: 1 })]);
|
|
@@ -1787,6 +1787,183 @@ test("compiled records walk own enumerable keys without Reflect.ownKeys", () =>
|
|
|
1787
1787
|
expect(compile(plain).parse(revealing())).toStrictEqual({ a: 1, b: 2 });
|
|
1788
1788
|
});
|
|
1789
1789
|
|
|
1790
|
+
test("validate honors custom checks composed with format traits", () => {
|
|
1791
|
+
const postProcessor = z.core.globalConfig.postProcessor;
|
|
1792
|
+
z.core.globalConfig.postProcessor = undefined;
|
|
1793
|
+
try {
|
|
1794
|
+
const CustomEmail = z.core.$constructor<z.core.$ZodEmail>("CustomEmail", (inst, def) => {
|
|
1795
|
+
inst._zod.check = (payload) => {
|
|
1796
|
+
if (payload.value !== "custom") payload.issues.push({ code: "custom", input: payload.value });
|
|
1797
|
+
};
|
|
1798
|
+
z.core.$ZodEmail.init(inst, def);
|
|
1799
|
+
});
|
|
1800
|
+
const schema = new CustomEmail({ type: "string", format: "email", check: "string_format" });
|
|
1801
|
+
for (const [input, valid] of [
|
|
1802
|
+
["custom", true],
|
|
1803
|
+
["test@example.com", false],
|
|
1804
|
+
[42, false],
|
|
1805
|
+
] as const) {
|
|
1806
|
+
expect(z.safeParse(schema, input).success).toBe(valid);
|
|
1807
|
+
expect(z.validate(schema, input)).toBe(valid);
|
|
1808
|
+
}
|
|
1809
|
+
} finally {
|
|
1810
|
+
z.core.globalConfig.postProcessor = postProcessor;
|
|
1811
|
+
}
|
|
1812
|
+
});
|
|
1813
|
+
|
|
1814
|
+
test("validate honors standalone format checks", () => {
|
|
1815
|
+
const cases = [
|
|
1816
|
+
[z.iso.datetime(), "2021-01-01T00:00:00Z"],
|
|
1817
|
+
[z.iso.date(), "2021-01-01"],
|
|
1818
|
+
[z.iso.time(), "00:00:00"],
|
|
1819
|
+
[z.iso.duration(), "P1Y2M3DT4H5M6S"],
|
|
1820
|
+
[z.email(), "test@example.com"],
|
|
1821
|
+
[z.uuid(), "20354d7a-e4fe-47af-8ff6-187bca92f3f9"],
|
|
1822
|
+
[z.ipv4(), "192.168.0.1"],
|
|
1823
|
+
] as const;
|
|
1824
|
+
|
|
1825
|
+
for (const [schema, valid] of cases) {
|
|
1826
|
+
expect(schema._zod.traits.has("$ZodStringFormat")).toBe(true);
|
|
1827
|
+
expect(schema instanceof z.core.$ZodStringFormat).toBe(true);
|
|
1828
|
+
expect(z.validate(schema, valid)).toBe(true);
|
|
1829
|
+
expect(z.validate(schema, "invalid")).toBe(false);
|
|
1830
|
+
expect(z.validate(schema, 1)).toBe(false);
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
const source = z.email();
|
|
1834
|
+
const refined = source.refine((value) => value.startsWith("x"));
|
|
1835
|
+
const asyncRefined = source.refine(async () => true);
|
|
1836
|
+
const coercedDef = Object.assign(Object.create({ coerce: true }), source._zod.def);
|
|
1837
|
+
const coerced = source.clone(coercedDef);
|
|
1838
|
+
const checkedDef = Object.assign(Object.create({ checks: refined._zod.def.checks }), source._zod.def);
|
|
1839
|
+
const inheritedChecked = source.clone(checkedDef);
|
|
1840
|
+
expect(z.validate(refined, "test@example.com")).toBe(false);
|
|
1841
|
+
expect(z.validate(refined, "x@example.com")).toBe(true);
|
|
1842
|
+
expect(z.validate(inheritedChecked, "test@example.com")).toBe(false);
|
|
1843
|
+
expect(() => z.validate(asyncRefined, "test@example.com")).toThrow($ZodAsyncError);
|
|
1844
|
+
expect(z.validate(coerced, { toString: () => "test@example.com" })).toBe(true);
|
|
1845
|
+
});
|
|
1846
|
+
|
|
1847
|
+
test("runtime validate preserves contexts, getters, errors, and regex state", () => {
|
|
1848
|
+
const postProcessor = z.core.globalConfig.postProcessor;
|
|
1849
|
+
z.core.globalConfig.postProcessor = undefined;
|
|
1850
|
+
try {
|
|
1851
|
+
const source = z.email();
|
|
1852
|
+
expect(z.validate(source, "invalid", { skipChecks: true } as any)).toBe(true);
|
|
1853
|
+
|
|
1854
|
+
let whenReads = 0;
|
|
1855
|
+
const whenProto = Object.defineProperty({}, "when", {
|
|
1856
|
+
enumerable: true,
|
|
1857
|
+
get() {
|
|
1858
|
+
whenReads++;
|
|
1859
|
+
return () => false;
|
|
1860
|
+
},
|
|
1861
|
+
});
|
|
1862
|
+
const whenDef = Object.assign(Object.create(whenProto), source._zod.def);
|
|
1863
|
+
const gated = source.clone(whenDef as typeof source._zod.def);
|
|
1864
|
+
expect(whenReads).toBe(0);
|
|
1865
|
+
expect(z.validate(gated, "invalid")).toBe(true);
|
|
1866
|
+
expect(whenReads).toBeGreaterThan(0);
|
|
1867
|
+
|
|
1868
|
+
let errorReads = 0;
|
|
1869
|
+
const errorDef = Object.defineProperties({}, Object.getOwnPropertyDescriptors(source._zod.def));
|
|
1870
|
+
Object.defineProperty(errorDef, "error", {
|
|
1871
|
+
enumerable: true,
|
|
1872
|
+
get() {
|
|
1873
|
+
errorReads++;
|
|
1874
|
+
return () => "custom";
|
|
1875
|
+
},
|
|
1876
|
+
});
|
|
1877
|
+
const customError = source.clone(errorDef as typeof source._zod.def);
|
|
1878
|
+
expect(z.validate(customError, "invalid")).toBe(false);
|
|
1879
|
+
expect(errorReads).toBe(0);
|
|
1880
|
+
const result = customError.safeParse("invalid");
|
|
1881
|
+
expect(errorReads).toBe(0);
|
|
1882
|
+
if (result.success) expect.unreachable();
|
|
1883
|
+
expect(result.error.issues[0]?.message).toBe("custom");
|
|
1884
|
+
expect(errorReads).toBeGreaterThan(0);
|
|
1885
|
+
|
|
1886
|
+
const pattern = /^a$/g;
|
|
1887
|
+
const cloned = source.clone({ ...source._zod.def, pattern });
|
|
1888
|
+
for (let i = 0; i < 3; i++) expect(z.validate(cloned, "a")).toBe(true);
|
|
1889
|
+
expect(z.validate(cloned, "b")).toBe(false);
|
|
1890
|
+
expect(pattern.lastIndex).toBe(0);
|
|
1891
|
+
const replacement = /^b$/g;
|
|
1892
|
+
cloned._zod.def.pattern = replacement;
|
|
1893
|
+
expect(z.validate(cloned, "a")).toBe(false);
|
|
1894
|
+
expect(z.validate(cloned, "b")).toBe(true);
|
|
1895
|
+
} finally {
|
|
1896
|
+
z.core.globalConfig.postProcessor = postProcessor;
|
|
1897
|
+
}
|
|
1898
|
+
});
|
|
1899
|
+
|
|
1900
|
+
test("validate preserves live pattern accessor reads", () => {
|
|
1901
|
+
const postProcessor = z.core.globalConfig.postProcessor;
|
|
1902
|
+
z.core.globalConfig.postProcessor = undefined;
|
|
1903
|
+
try {
|
|
1904
|
+
const schema = z.email();
|
|
1905
|
+
let reads = 0;
|
|
1906
|
+
Object.defineProperty(schema.def, "pattern", {
|
|
1907
|
+
get: () => (++reads % 2 === 1 ? /^a$/ : /^b$/),
|
|
1908
|
+
});
|
|
1909
|
+
expect(schema.safeParse("b").success).toBe(true);
|
|
1910
|
+
expect(reads).toBe(2);
|
|
1911
|
+
reads = 0;
|
|
1912
|
+
expect(z.validate(schema, "b")).toBe(true);
|
|
1913
|
+
expect(reads).toBe(2);
|
|
1914
|
+
} finally {
|
|
1915
|
+
z.core.globalConfig.postProcessor = postProcessor;
|
|
1916
|
+
}
|
|
1917
|
+
});
|
|
1918
|
+
|
|
1919
|
+
test.each(["when", "coerce"] as const)("runtime validate honors live %s options", (option) => {
|
|
1920
|
+
const postProcessor = z.core.globalConfig.postProcessor;
|
|
1921
|
+
z.core.globalConfig.postProcessor = undefined;
|
|
1922
|
+
try {
|
|
1923
|
+
for (const inherited of [false, true]) {
|
|
1924
|
+
const schema = z.email();
|
|
1925
|
+
const input = option === "when" ? "invalid" : { toString: () => "test@example.com" };
|
|
1926
|
+
expect(z.validate(schema, input)).toBe(false);
|
|
1927
|
+
|
|
1928
|
+
const target = inherited ? Object.create(Object.getPrototypeOf(schema.def)) : schema.def;
|
|
1929
|
+
let reads = 0;
|
|
1930
|
+
Object.defineProperty(target, option, {
|
|
1931
|
+
get() {
|
|
1932
|
+
reads++;
|
|
1933
|
+
return option === "when" ? () => false : true;
|
|
1934
|
+
},
|
|
1935
|
+
});
|
|
1936
|
+
if (inherited) Object.setPrototypeOf(schema.def, target);
|
|
1937
|
+
expect(reads).toBe(0);
|
|
1938
|
+
expect(z.validate(schema, input)).toBe(true);
|
|
1939
|
+
expect(reads).toBeGreaterThan(0);
|
|
1940
|
+
expect(schema.safeParse(input).success).toBe(true);
|
|
1941
|
+
}
|
|
1942
|
+
} finally {
|
|
1943
|
+
z.core.globalConfig.postProcessor = postProcessor;
|
|
1944
|
+
}
|
|
1945
|
+
});
|
|
1946
|
+
|
|
1947
|
+
test("validate uses compiled validators and runtime fallbacks", () => {
|
|
1948
|
+
const compiled = compile(z.email());
|
|
1949
|
+
expect((compiled._zod.bag as { validator?: unknown }).validator).toBeTypeOf("function");
|
|
1950
|
+
expect(z.validate(compiled, "test@example.com")).toBe(true);
|
|
1951
|
+
expect(z.validate(compiled, "invalid")).toBe(false);
|
|
1952
|
+
expect(z.validate(compiled, "invalid", { skipChecks: true } as any)).toBe(true);
|
|
1953
|
+
|
|
1954
|
+
const source = z.email();
|
|
1955
|
+
const installed = withParser(source, () => INVALID);
|
|
1956
|
+
const bag = installed._zod.bag as { fallbackRun: typeof source._zod.run };
|
|
1957
|
+
const originalRun = bag.fallbackRun;
|
|
1958
|
+
let fallbackRuns = 0;
|
|
1959
|
+
bag.fallbackRun = (payload, ctx) => {
|
|
1960
|
+
fallbackRuns++;
|
|
1961
|
+
return originalRun(payload, ctx);
|
|
1962
|
+
};
|
|
1963
|
+
expect(z.validate(installed, "invalid")).toBe(false);
|
|
1964
|
+
expect(fallbackRuns).toBeGreaterThan(0);
|
|
1965
|
+
});
|
|
1966
|
+
|
|
1790
1967
|
// withParser: a parser Zod did not generate, installed on the same wrapper compile() uses.
|
|
1791
1968
|
|
|
1792
1969
|
// tags its output so a test fails loudly if the runtime ran instead of the supplied parser
|
|
@@ -1,31 +1,133 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
const URLDescriptor = Object.getOwnPropertyDescriptor(globalThis, "URL")!;
|
|
4
|
+
const NativeURL = globalThis.URL;
|
|
5
|
+
const canParseDescriptor = Object.getOwnPropertyDescriptor(NativeURL, "canParse")!;
|
|
6
|
+
const parseDescriptor = Object.getOwnPropertyDescriptor(NativeURL, "parse")!;
|
|
6
7
|
|
|
8
|
+
function restoreURL() {
|
|
9
|
+
Object.defineProperty(globalThis, "URL", URLDescriptor);
|
|
10
|
+
Object.defineProperty(NativeURL, "canParse", canParseDescriptor);
|
|
11
|
+
Object.defineProperty(NativeURL, "parse", parseDescriptor);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe("URL parser fallbacks", () => {
|
|
7
15
|
beforeEach(() => {
|
|
16
|
+
restoreURL();
|
|
8
17
|
vi.resetModules();
|
|
9
|
-
// @ts-expect-error force the fallback branch (URL.canParse may be undefined on older runtimes)
|
|
10
|
-
delete URL.canParse;
|
|
11
18
|
});
|
|
12
19
|
|
|
13
|
-
afterEach(
|
|
14
|
-
|
|
20
|
+
afterEach(restoreURL);
|
|
21
|
+
|
|
22
|
+
test("uses the boolean and object APIs on modern runtimes", async () => {
|
|
23
|
+
const canParse = vi.spyOn(NativeURL, "canParse");
|
|
24
|
+
const URLStatic = NativeURL as typeof URL & { parse: (input: string) => URL | null };
|
|
25
|
+
const parse = vi.spyOn(URLStatic, "parse");
|
|
26
|
+
const z = await import("../../index.js");
|
|
27
|
+
|
|
28
|
+
expect(z.validate(z.url(), "https://example.com")).toBe(true);
|
|
29
|
+
expect(canParse).toHaveBeenCalledOnce();
|
|
30
|
+
expect(parse).not.toHaveBeenCalled();
|
|
31
|
+
|
|
32
|
+
canParse.mockClear();
|
|
33
|
+
expect(z.validate(z.url({ normalize: true }), "https://example.com")).toBe(true);
|
|
34
|
+
expect(parse).toHaveBeenCalledOnce();
|
|
35
|
+
expect(canParse).not.toHaveBeenCalled();
|
|
36
|
+
|
|
37
|
+
parse.mockClear();
|
|
38
|
+
expect(z.validate(z.ipv6(), "2001:db8::1")).toBe(true);
|
|
39
|
+
expect(canParse).toHaveBeenCalledOnce();
|
|
40
|
+
expect(parse).not.toHaveBeenCalled();
|
|
15
41
|
});
|
|
16
42
|
|
|
17
|
-
test("
|
|
18
|
-
expect(URL.canParse).toBeUndefined();
|
|
43
|
+
test("URL fast-path selection preserves live option getter reads", async () => {
|
|
19
44
|
const z = await import("../../index.js");
|
|
20
|
-
|
|
21
|
-
|
|
45
|
+
const postProcessor = z.core.globalConfig.postProcessor;
|
|
46
|
+
z.core.globalConfig.postProcessor = undefined;
|
|
47
|
+
try {
|
|
48
|
+
for (const inherited of [false, true]) {
|
|
49
|
+
const schema = z.url();
|
|
50
|
+
const target = inherited ? Object.create(Object.getPrototypeOf(schema.def)) : schema.def;
|
|
51
|
+
let reads = 0;
|
|
52
|
+
Object.defineProperty(target, "hostname", {
|
|
53
|
+
get: () => (++reads === 1 ? /^example\.com$/ : undefined),
|
|
54
|
+
});
|
|
55
|
+
if (inherited) Object.setPrototypeOf(schema.def, target);
|
|
56
|
+
expect(z.validate(schema, "https://example.com")).toBe(false);
|
|
57
|
+
expect(reads).toBe(2);
|
|
58
|
+
reads = 0;
|
|
59
|
+
expect(schema.safeParse("https://example.com").success).toBe(false);
|
|
60
|
+
expect(reads).toBe(2);
|
|
61
|
+
}
|
|
62
|
+
} finally {
|
|
63
|
+
z.core.globalConfig.postProcessor = postProcessor;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("the exported object parser still returns a URL", async () => {
|
|
68
|
+
const { parseURLObject } = await import("../schemas.js");
|
|
69
|
+
const result = parseURLObject("https://example.com", {});
|
|
70
|
+
expect(result).toBeInstanceOf(NativeURL);
|
|
71
|
+
if (typeof result === "number") expect.unreachable();
|
|
72
|
+
expect(result.hostname).toBe("example.com");
|
|
73
|
+
expect(parseURLObject("invalid", {})).toBe(2);
|
|
22
74
|
});
|
|
23
75
|
|
|
24
|
-
test("
|
|
25
|
-
|
|
76
|
+
test("falls back when URL.canParse is absent", async () => {
|
|
77
|
+
Reflect.deleteProperty(NativeURL, "canParse");
|
|
26
78
|
const z = await import("../../index.js");
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
79
|
+
const cases = [
|
|
80
|
+
[z.url(), "https://example.com", "not a url"],
|
|
81
|
+
[z.ipv6(), "2001:db8::1", "not-an-ip"],
|
|
82
|
+
[z.cidrv6(), "2001:db8::/32", "not-an-ip/32"],
|
|
83
|
+
] as const;
|
|
84
|
+
|
|
85
|
+
for (const [schema, valid, invalid] of cases) {
|
|
86
|
+
for (const subject of [schema, z.compile(schema)]) {
|
|
87
|
+
expect(z.validate(subject, valid)).toBe(true);
|
|
88
|
+
expect(z.validate(subject, invalid)).toBe(false);
|
|
89
|
+
expect(subject.safeParse(valid).success).toBe(true);
|
|
90
|
+
expect(subject.safeParse(invalid).success).toBe(false);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("falls back when URL.parse is absent", async () => {
|
|
96
|
+
Reflect.deleteProperty(NativeURL, "parse");
|
|
97
|
+
const z = await import("../../index.js");
|
|
98
|
+
const cases = [
|
|
99
|
+
[z.url({ normalize: true }), "https://example.com", "not a url"],
|
|
100
|
+
[z.url({ hostname: /^example\.com$/ }), "https://example.com", "https://other.com"],
|
|
101
|
+
[z.httpUrl(), "https://example.com", "mailto:a@example.com"],
|
|
102
|
+
] as const;
|
|
103
|
+
|
|
104
|
+
for (const [schema, valid, invalid] of cases) {
|
|
105
|
+
for (const subject of [schema, z.compile(schema)]) {
|
|
106
|
+
expect(z.validate(subject, valid)).toBe(true);
|
|
107
|
+
expect(z.validate(subject, invalid)).toBe(false);
|
|
108
|
+
expect(subject.safeParse(valid).success).toBe(true);
|
|
109
|
+
expect(subject.safeParse(invalid).success).toBe(false);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("rejects without a global URL constructor", async () => {
|
|
115
|
+
const z = await import("../../index.js");
|
|
116
|
+
const cases = [
|
|
117
|
+
[z.url(), "https://example.com"],
|
|
118
|
+
[z.url({ normalize: true }), "https://example.com"],
|
|
119
|
+
[z.ipv6(), "2001:db8::1"],
|
|
120
|
+
[z.cidrv6(), "2001:db8::/32"],
|
|
121
|
+
] as const;
|
|
122
|
+
const subjects = cases.flatMap(([schema, input]) => [
|
|
123
|
+
[schema, input] as const,
|
|
124
|
+
[z.compile(schema), input] as const,
|
|
125
|
+
]);
|
|
126
|
+
Object.defineProperty(globalThis, "URL", { configurable: true, value: undefined });
|
|
127
|
+
|
|
128
|
+
for (const [schema, input] of subjects) {
|
|
129
|
+
expect(z.validate(schema, input)).toBe(false);
|
|
130
|
+
expect(schema.safeParse(input).success).toBe(false);
|
|
131
|
+
}
|
|
30
132
|
});
|
|
31
133
|
});
|
package/src/v4/core/util.ts
CHANGED
|
@@ -1125,10 +1125,6 @@ export function members(proto: object, table: object): void {
|
|
|
1125
1125
|
// a method materializes bound on first read, which is what keeps a detached member working: `const opt = schema.optional; opt()`
|
|
1126
1126
|
else defineBound(proto, key, desc.value);
|
|
1127
1127
|
}
|
|
1128
|
-
// for..in sees no symbol keys, so well-known members like Symbol.iterator install here
|
|
1129
|
-
for (const sym of Object.getOwnPropertySymbols(table)) {
|
|
1130
|
-
defineBound(proto, sym, (table as any)[sym]);
|
|
1131
|
-
}
|
|
1132
1128
|
}
|
|
1133
1129
|
|
|
1134
1130
|
/** Shadows a prototype member with an own value, so a getter that builds from the instance runs once. */
|
package/src/v4/core/versions.ts
CHANGED
package/src/v4/core/visit.ts
CHANGED
|
@@ -158,17 +158,6 @@ export function visit(schema: schemas.SomeType, fnOrHandlers: VisitFn | VisitHan
|
|
|
158
158
|
const { _cachedInner, ...rest } = def;
|
|
159
159
|
return clone(s, { ...rest, getter: () => run(original()) });
|
|
160
160
|
}
|
|
161
|
-
case "properties": {
|
|
162
|
-
const oldShape = def.shape as Record<string | symbol, AnyZod>;
|
|
163
|
-
let changed = false;
|
|
164
|
-
const newShape: Record<string | symbol, AnyZod> = {};
|
|
165
|
-
for (const k of Reflect.ownKeys(oldShape)) {
|
|
166
|
-
const mapped = run(oldShape[k]!);
|
|
167
|
-
if (mapped !== oldShape[k]) changed = true;
|
|
168
|
-
newShape[k] = mapped;
|
|
169
|
-
}
|
|
170
|
-
return changed ? clone(s, { ...def, shape: newShape }) : s;
|
|
171
|
-
}
|
|
172
161
|
// A leaf by choice: `parts` are regex fragments, not data positions.
|
|
173
162
|
case "template_literal":
|
|
174
163
|
// Leaves.
|
package/src/v4/locales/ar.ts
CHANGED
package/src/v4/locales/az.ts
CHANGED
package/src/v4/locales/be.ts
CHANGED
package/src/v4/locales/bg.ts
CHANGED