zod-compiler 1.23.6 → 1.23.7
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/dist/core/codegen/build-path.d.ts.map +1 -1
- package/dist/core/codegen/build-path.js +57 -6
- package/dist/core/codegen/build-path.js.map +1 -1
- package/dist/core/codegen/context.d.ts +23 -3
- package/dist/core/codegen/context.d.ts.map +1 -1
- package/dist/core/codegen/context.js +23 -1
- package/dist/core/codegen/context.js.map +1 -1
- package/dist/core/codegen/dedupe.js +4 -1
- package/dist/core/codegen/dedupe.js.map +1 -1
- package/dist/core/codegen/emit-issue.d.ts +27 -2
- package/dist/core/codegen/emit-issue.d.ts.map +1 -1
- package/dist/core/codegen/emit-issue.js +8 -3
- package/dist/core/codegen/emit-issue.js.map +1 -1
- package/dist/core/codegen/issue-decls.d.ts +9 -3
- package/dist/core/codegen/issue-decls.d.ts.map +1 -1
- package/dist/core/codegen/issue-decls.js +10 -4
- package/dist/core/codegen/issue-decls.js.map +1 -1
- package/dist/core/codegen/schemas/catch.d.ts.map +1 -1
- package/dist/core/codegen/schemas/catch.js +1 -1
- package/dist/core/codegen/schemas/catch.js.map +1 -1
- package/dist/core/codegen/schemas/discriminated-union.d.ts.map +1 -1
- package/dist/core/codegen/schemas/discriminated-union.js +11 -3
- package/dist/core/codegen/schemas/discriminated-union.js.map +1 -1
- package/dist/core/codegen/schemas/effect.d.ts.map +1 -1
- package/dist/core/codegen/schemas/effect.js +3 -2
- package/dist/core/codegen/schemas/effect.js.map +1 -1
- package/dist/core/codegen/schemas/enum.d.ts.map +1 -1
- package/dist/core/codegen/schemas/enum.js +25 -0
- package/dist/core/codegen/schemas/enum.js.map +1 -1
- package/dist/core/codegen/schemas/literal.d.ts.map +1 -1
- package/dist/core/codegen/schemas/literal.js +37 -3
- package/dist/core/codegen/schemas/literal.js.map +1 -1
- package/dist/core/codegen/schemas/string-bool.d.ts.map +1 -1
- package/dist/core/codegen/schemas/string-bool.js +3 -2
- package/dist/core/codegen/schemas/string-bool.js.map +1 -1
- package/dist/core/codegen/schemas/string.d.ts.map +1 -1
- package/dist/core/codegen/schemas/string.js +13 -7
- package/dist/core/codegen/schemas/string.js.map +1 -1
- package/dist/core/codegen/schemas/template-literal.d.ts.map +1 -1
- package/dist/core/codegen/schemas/template-literal.js +2 -2
- package/dist/core/codegen/schemas/template-literal.js.map +1 -1
- package/dist/core/codegen/schemas/tuple.d.ts +11 -5
- package/dist/core/codegen/schemas/tuple.d.ts.map +1 -1
- package/dist/core/codegen/schemas/tuple.js +12 -6
- package/dist/core/codegen/schemas/tuple.js.map +1 -1
- package/dist/core/codegen/schemas/union.d.ts.map +1 -1
- package/dist/core/codegen/schemas/union.js +17 -17
- package/dist/core/codegen/schemas/union.js.map +1 -1
- package/dist/core/extract/checks.d.ts +18 -2
- package/dist/core/extract/checks.d.ts.map +1 -1
- package/dist/core/extract/checks.js +138 -11
- package/dist/core/extract/checks.js.map +1 -1
- package/dist/core/extract/extractors/array.d.ts.map +1 -1
- package/dist/core/extract/extractors/array.js +2 -2
- package/dist/core/extract/extractors/array.js.map +1 -1
- package/dist/core/extract/extractors/intersection.js +44 -0
- package/dist/core/extract/extractors/intersection.js.map +1 -1
- package/dist/core/extract/extractors/lazy.d.ts.map +1 -1
- package/dist/core/extract/extractors/lazy.js +2 -13
- package/dist/core/extract/extractors/lazy.js.map +1 -1
- package/dist/core/extract/extractors/literal.d.ts +14 -0
- package/dist/core/extract/extractors/literal.d.ts.map +1 -1
- package/dist/core/extract/extractors/literal.js +32 -4
- package/dist/core/extract/extractors/literal.js.map +1 -1
- package/dist/core/extract/extractors/number.d.ts.map +1 -1
- package/dist/core/extract/extractors/number.js +2 -2
- package/dist/core/extract/extractors/number.js.map +1 -1
- package/dist/core/extract/extractors/object.d.ts.map +1 -1
- package/dist/core/extract/extractors/object.js +2 -2
- package/dist/core/extract/extractors/object.js.map +1 -1
- package/dist/core/extract/extractors/record.d.ts.map +1 -1
- package/dist/core/extract/extractors/record.js +2 -0
- package/dist/core/extract/extractors/record.js.map +1 -1
- package/dist/core/extract/extractors/string.d.ts.map +1 -1
- package/dist/core/extract/extractors/string.js +2 -2
- package/dist/core/extract/extractors/string.js.map +1 -1
- package/dist/core/extract/extractors/union.js +2 -0
- package/dist/core/extract/extractors/union.js.map +1 -1
- package/dist/core/extract/recursion.d.ts +26 -0
- package/dist/core/extract/recursion.d.ts.map +1 -0
- package/dist/core/extract/recursion.js +37 -0
- package/dist/core/extract/recursion.js.map +1 -0
- package/dist/core/extract/registry.d.ts.map +1 -1
- package/dist/core/extract/registry.js +2 -0
- package/dist/core/extract/registry.js.map +1 -1
- package/dist/core/extract/types.d.ts +21 -6
- package/dist/core/extract/types.d.ts.map +1 -1
- package/dist/core/iife.d.ts +13 -0
- package/dist/core/iife.d.ts.map +1 -1
- package/dist/core/iife.js +14 -1
- package/dist/core/iife.js.map +1 -1
- package/dist/core/types.d.ts +49 -2
- package/dist/core/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -21,6 +21,30 @@ function refineRefRegistrar(ctx, checks) {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
+
* Build the `customParamsRef` registrar for {@link extractChecks}: it appends a
|
|
25
|
+
* {@link RefEntry} pointing at the check's own `params` object
|
|
26
|
+
* (`<schema>._zod.def.checks[i]._zod.def.params`), which $ZodCustom copies onto
|
|
27
|
+
* the issue by reference.
|
|
28
|
+
*
|
|
29
|
+
* A reference rather than a baked literal because `params` is opaque user data:
|
|
30
|
+
* it may hold functions, symbols or a cycle (none of which survive a literal),
|
|
31
|
+
* and zod hands out the SAME object on every issue, so a per-failure copy would
|
|
32
|
+
* break the identity comparison that is the point of passing params.
|
|
33
|
+
*
|
|
34
|
+
* Returns undefined — meaning "fall back to zod" — when refs are unavailable.
|
|
35
|
+
*/
|
|
36
|
+
function customParamsRefRegistrar(ctx, checks) {
|
|
37
|
+
const refs = ctx.refs;
|
|
38
|
+
if (!refs) return void 0;
|
|
39
|
+
return (index) => {
|
|
40
|
+
refs.push({
|
|
41
|
+
schema: checks[index]?._zod?.def?.params,
|
|
42
|
+
accessPath: `${ctx.path}._zod.def.checks[${index}]._zod.def.params`
|
|
43
|
+
});
|
|
44
|
+
return refs.length - 1;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
24
48
|
* Register the payload-taking callback of check `index` (`superRefine`) as an
|
|
25
49
|
* `__rf[N]` reference, pointing at zod's own wrapper
|
|
26
50
|
* (`<schema>._zod.def.checks[i]._zod.check`).
|
|
@@ -42,9 +66,15 @@ function payloadCheckRef(ctx, checks, index) {
|
|
|
42
66
|
return refs.length - 1;
|
|
43
67
|
}
|
|
44
68
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
69
|
+
* BUILT-IN string formats the codegen can validate without a regex pattern,
|
|
70
|
+
* because it substitutes a known regex of its own (see EMAIL_REGEX_SOURCE /
|
|
71
|
+
* UUID_REGEX_SOURCE in codegen/schemas/string.ts). Everything else without a
|
|
72
|
+
* pattern (e.g. z.jwt(), which validates algorithmically) must fall back to Zod
|
|
73
|
+
* instead of being silently skipped.
|
|
74
|
+
*
|
|
75
|
+
* Built-in ONLY: this is keyed on the format NAME, and a custom format's name is
|
|
76
|
+
* user-chosen, so a match here proves nothing about a `$ZodCustomStringFormat`
|
|
77
|
+
* — see the isCustomStringFormat gate at the use site.
|
|
48
78
|
*/
|
|
49
79
|
const PATTERNLESS_FORMATS = /* @__PURE__ */ new Set([
|
|
50
80
|
"email",
|
|
@@ -52,11 +82,91 @@ const PATTERNLESS_FORMATS = /* @__PURE__ */ new Set([
|
|
|
52
82
|
"url"
|
|
53
83
|
]);
|
|
54
84
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
85
|
+
* String formats whose `def.pattern` is NOT the validator Zod actually runs.
|
|
86
|
+
*
|
|
87
|
+
* Zod attaches a `def.pattern` to every string format because `toJSONSchema()`
|
|
88
|
+
* needs one to emit. A handful of constructors then REPLACE the pattern check
|
|
89
|
+
* outright: `$ZodStringFormat.init` installs the pattern check with `??=`, and
|
|
90
|
+
* the constructor's following `inst._zod.check = …` assignment overwrites it.
|
|
91
|
+
* The pattern survives only as JSON Schema metadata. Compiling it produces a
|
|
92
|
+
* verdict Zod does not share — in BOTH directions — so these delegate instead.
|
|
93
|
+
*
|
|
94
|
+
* - `ipv6`: Zod validates with `new URL("http://[" + value + "]")`. The URL
|
|
95
|
+
* parser accepts IPv4-mapped forms the regex has no alternative for
|
|
96
|
+
* (`"::ffff:1.2.3.4"`, `"1:2:3:4:5:6:1.2.3.4"`) and, since it strips tabs and
|
|
97
|
+
* ends the host at `@` or `/`, even non-addresses (`"@_c.Z=\t/X+9"`).
|
|
98
|
+
* - `cidrv6`: splits on `/`, range-checks the prefix as a number, then runs the
|
|
99
|
+
* same `new URL` probe on the address half — inheriting every `ipv6` gap.
|
|
100
|
+
* - `base64`: `length % 4 === 0 && atob(value)` does not throw. `atob` is
|
|
101
|
+
* HTML's forgiving-base64 decode, which STRIPS ASCII whitespace before
|
|
102
|
+
* decoding, so Zod accepts `"AAAA "`, `"\t\t\t\tAAAA"` and `"+c \n"` —
|
|
103
|
+
* all rejected by `regexes.base64`, whose charset has no room for whitespace.
|
|
104
|
+
* - `base64url`: `regexes.base64url.test(v)` AND an `atob` of the re-padded
|
|
105
|
+
* value. The regex alone is `^[A-Za-z0-9_-]*$`, which accepts every
|
|
106
|
+
* `length % 4 === 1` string (`"a"`, `"-"`, `"A"`, `"9"`) that decoding fails.
|
|
107
|
+
*
|
|
108
|
+
* Every other string format compiles its pattern, which is only safe while that
|
|
109
|
+
* pattern and Zod's installed check keep agreeing. What keeps this list honest
|
|
110
|
+
* — in both directions — is tests/string-format-parity.test.ts, which fuzzes
|
|
111
|
+
* every reachable string format against Zod over one shared corpus.
|
|
112
|
+
*/
|
|
113
|
+
const NON_AUTHORITATIVE_PATTERN_FORMATS = /* @__PURE__ */ new Set([
|
|
114
|
+
"base64",
|
|
115
|
+
"base64url",
|
|
116
|
+
"cidrv6",
|
|
117
|
+
"ipv6"
|
|
118
|
+
]);
|
|
119
|
+
/**
|
|
120
|
+
* True when a `string_format` check is a `$ZodCustomStringFormat` — the family
|
|
121
|
+
* Zod's `_stringFormat` helper builds: `z.stringFormat()`, and the built-ins
|
|
122
|
+
* `z.hex()`, `z.hostname()` and `z.hash()` that route through it.
|
|
123
|
+
*
|
|
124
|
+
* `def.fn` is the structural marker: `_stringFormat` is the ONLY producer of a
|
|
125
|
+
* `string_format` check def that carries one (every other format — guid, uuid,
|
|
126
|
+
* email, ipv4, iso.*, lowercase/uppercase, regex, includes/starts_with/ends_with
|
|
127
|
+
* — is built from a plain `{ check, format, pattern }` def). A name list cannot
|
|
128
|
+
* stand in for it: `z.stringFormat("anything", /re/)` lets the caller pick the
|
|
129
|
+
* format name, so the set of names is open.
|
|
130
|
+
*
|
|
131
|
+
* Two things follow from being in this family, both about Zod's OVERRIDE of
|
|
132
|
+
* `inst._zod.check` (see `$ZodCustomStringFormat` in zod/v4/core/schemas.js):
|
|
133
|
+
* the issue it pushes is bare (see CheckStringFormat.bareIssue), and it calls
|
|
134
|
+
* `def.fn` rather than testing `def.pattern` — which is what makes a `g`/`y`
|
|
135
|
+
* pattern stateful (see {@link isStatefulCustomFormat}).
|
|
58
136
|
*/
|
|
59
|
-
|
|
137
|
+
function isCustomStringFormat(def) {
|
|
138
|
+
return typeof def.fn === "function";
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* True when a `string_format` check validates through `def.fn` over a STATEFUL
|
|
142
|
+
* (`g`/`y`-flagged) pattern — a custom format built by Zod's `_stringFormat`
|
|
143
|
+
* helper, i.e. `z.stringFormat("name", /ab/g)`.
|
|
144
|
+
*
|
|
145
|
+
* Such a format is stateful in Zod, while the compiled output is deliberately
|
|
146
|
+
* stateless — so the two diverge from the first successful match onward.
|
|
147
|
+
* `_stringFormat` stores the regex as `def.pattern` AND closes over it as
|
|
148
|
+
* `def.fn = (val) => fnOrRegex.test(val)`.
|
|
149
|
+
* `$ZodCustomStringFormat` validates by calling that `def.fn`, and NOTHING
|
|
150
|
+
* resets `lastIndex`, so a `g`/`y` regex carries its cursor across parses:
|
|
151
|
+
* `z.stringFormat("f", /ab/g).safeParse("abab")` returns true, true, FALSE,
|
|
152
|
+
* true over four calls. Contrast `$ZodCheckRegex` — what `.regex(/ab/g)` builds
|
|
153
|
+
* — which does an explicit `def.pattern.lastIndex = 0` before testing and is
|
|
154
|
+
* therefore stateless. The codegen's `lastIndexReset` reproduces THAT reset,
|
|
155
|
+
* which is right for `.regex()` and is exactly what makes a custom format
|
|
156
|
+
* diverge. The state lives in the user's own RegExp object, which the compiled
|
|
157
|
+
* validator does not hold, so there is nothing faithful to emit: delegate.
|
|
158
|
+
*
|
|
159
|
+
* This needs a predicate rather than a name set like
|
|
160
|
+
* NON_AUTHORITATIVE_PATTERN_FORMATS above, because a custom format's name is
|
|
161
|
+
* user-chosen and cannot be enumerated. Narrowness matters in both clauses:
|
|
162
|
+
* `def.fn` alone would over-delegate the built-ins Zod also routes through
|
|
163
|
+
* `_stringFormat` (hostname, hex, hash), and the flag test alone would
|
|
164
|
+
* over-delegate `.regex(/…/g)` — both of which stay compiled because their
|
|
165
|
+
* patterns are unflagged and their check is not `def.fn`, respectively.
|
|
166
|
+
*/
|
|
167
|
+
function isStatefulCustomFormat(def) {
|
|
168
|
+
return isCustomStringFormat(def) && def.pattern instanceof RegExp && /[gy]/.test(def.pattern.flags);
|
|
169
|
+
}
|
|
60
170
|
/**
|
|
61
171
|
* Check kinds where Zod itself installs a `when` guard
|
|
62
172
|
* (`!nullish(value) && value.length/size !== undefined`). Compiled output
|
|
@@ -87,7 +197,7 @@ function hasUserWhen(def) {
|
|
|
87
197
|
function hasUncompilableModifiers(def) {
|
|
88
198
|
return hasUserWhen(def) || def.abort === true;
|
|
89
199
|
}
|
|
90
|
-
function extractChecks(checks, refineRef, payloadRefFor) {
|
|
200
|
+
function extractChecks(checks, refineRef, payloadRefFor, customParamsRef) {
|
|
91
201
|
const checkIRs = [];
|
|
92
202
|
let hasFallback = false;
|
|
93
203
|
for (const [index, check] of checks.entries()) {
|
|
@@ -156,6 +266,11 @@ function extractChecks(checks, refineRef, payloadRefFor) {
|
|
|
156
266
|
});
|
|
157
267
|
break;
|
|
158
268
|
case "string_format": {
|
|
269
|
+
const isCustom = isCustomStringFormat(def);
|
|
270
|
+
if (isStatefulCustomFormat(def)) {
|
|
271
|
+
hasFallback = true;
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
159
274
|
if (def.format === "includes" && typeof def.includes === "string") {
|
|
160
275
|
checkIRs.push({
|
|
161
276
|
kind: "includes",
|
|
@@ -181,7 +296,7 @@ function extractChecks(checks, refineRef, payloadRefFor) {
|
|
|
181
296
|
});
|
|
182
297
|
break;
|
|
183
298
|
}
|
|
184
|
-
if (def.format === "url") {
|
|
299
|
+
if (def.format === "url" && !isCustom) {
|
|
185
300
|
checkIRs.push({
|
|
186
301
|
kind: "string_format",
|
|
187
302
|
format: "url",
|
|
@@ -198,7 +313,7 @@ function extractChecks(checks, refineRef, payloadRefFor) {
|
|
|
198
313
|
}
|
|
199
314
|
const pattern = def.pattern instanceof RegExp ? def.pattern.source : def.pattern;
|
|
200
315
|
const flags = def.pattern instanceof RegExp && def.pattern.flags ? def.pattern.flags : "";
|
|
201
|
-
if (!pattern && !PATTERNLESS_FORMATS.has(def.format)) {
|
|
316
|
+
if (!pattern && !(!isCustom && PATTERNLESS_FORMATS.has(def.format))) {
|
|
202
317
|
hasFallback = true;
|
|
203
318
|
break;
|
|
204
319
|
}
|
|
@@ -207,6 +322,7 @@ function extractChecks(checks, refineRef, payloadRefFor) {
|
|
|
207
322
|
format: def.format,
|
|
208
323
|
...pattern ? { pattern } : {},
|
|
209
324
|
...flags ? { patternFlags: flags } : {},
|
|
325
|
+
...isCustom ? { bareIssue: true } : {},
|
|
210
326
|
...message
|
|
211
327
|
});
|
|
212
328
|
break;
|
|
@@ -239,12 +355,22 @@ function extractChecks(checks, refineRef, payloadRefFor) {
|
|
|
239
355
|
break;
|
|
240
356
|
}
|
|
241
357
|
const pathIR = customPath && customPath.length > 0 ? { path: customPath } : {};
|
|
358
|
+
let paramsIR = {};
|
|
359
|
+
if (def.params !== void 0) {
|
|
360
|
+
const paramsRefIndex = customParamsRef?.(index);
|
|
361
|
+
if (paramsRefIndex === void 0) {
|
|
362
|
+
hasFallback = true;
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
paramsIR = { paramsRefIndex };
|
|
366
|
+
}
|
|
242
367
|
const source = tryCompileEffect(def.fn);
|
|
243
368
|
if (source) {
|
|
244
369
|
checkIRs.push({
|
|
245
370
|
kind: "refine_effect",
|
|
246
371
|
source,
|
|
247
372
|
...pathIR,
|
|
373
|
+
...paramsIR,
|
|
248
374
|
...message
|
|
249
375
|
});
|
|
250
376
|
break;
|
|
@@ -254,6 +380,7 @@ function extractChecks(checks, refineRef, payloadRefFor) {
|
|
|
254
380
|
kind: "refine_effect",
|
|
255
381
|
refIndex,
|
|
256
382
|
...pathIR,
|
|
383
|
+
...paramsIR,
|
|
257
384
|
...message
|
|
258
385
|
});
|
|
259
386
|
else hasFallback = true;
|
|
@@ -333,6 +460,6 @@ function resolveCheckMessage(error) {
|
|
|
333
460
|
return { kind: "dynamic" };
|
|
334
461
|
}
|
|
335
462
|
//#endregion
|
|
336
|
-
export { extractChecks, hasUncompilableModifiers, payloadCheckRef, refineRefRegistrar, resolveCheckMessage };
|
|
463
|
+
export { customParamsRefRegistrar, extractChecks, hasUncompilableModifiers, payloadCheckRef, refineRefRegistrar, resolveCheckMessage };
|
|
337
464
|
|
|
338
465
|
//# sourceMappingURL=checks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checks.js","names":[],"sources":["../../../src/core/extract/checks.ts"],"sourcesContent":["import type { CheckOrEffectIR, CheckStringFormat } from \"../types.js\";\nimport { isPayloadCheck, isReferenceablePredicate, tryCompileEffect } from \"./effects.js\";\nimport type { ExtractorContext, ZodCheckDef, ZodCheckSchema } from \"./types.js\";\n\n/**\n * Build the `refineRef` registrar for {@link extractChecks} from an extractor's\n * context: it appends a {@link RefEntry} pointing at the check's own predicate\n * (`<schema>._zod.def.checks[i]._zod.def.fn`) so generated code can call the\n * user's function object directly. Returns undefined — meaning \"fall back as\n * before\" — when the extraction is not collecting refs.\n */\nexport function refineRefRegistrar(\n ctx: ExtractorContext,\n checks: ZodCheckSchema[],\n): ((index: number) => number | undefined) | undefined {\n const refs = ctx.refs;\n if (!refs) return undefined;\n return (index) => {\n const fn = checks[index]?._zod?.def?.fn;\n if (!isReferenceablePredicate(fn)) return undefined;\n refs.push({ schema: fn, accessPath: `${ctx.path}._zod.def.checks[${index}]._zod.def.fn` });\n return refs.length - 1;\n };\n}\n\n/**\n * Register the payload-taking callback of check `index` (`superRefine`) as an\n * `__rf[N]` reference, pointing at zod's own wrapper\n * (`<schema>._zod.def.checks[i]._zod.check`).\n *\n * Only the LAST check on a node qualifies: an issue carrying\n * `fatal`/`continue:false` aborts zod's remaining chain, and compiled output\n * runs every check unconditionally — with nothing following, there is nothing\n * left to abort, so the two agree.\n */\nexport function payloadCheckRef(\n ctx: ExtractorContext,\n checks: ZodCheckSchema[],\n index: number,\n): number | undefined {\n if (index !== checks.length - 1) return undefined;\n const check = checks[index];\n const refs = ctx.refs;\n if (!refs || !check || !isPayloadCheck(check)) return undefined;\n refs.push({\n schema: (check._zod as { check: unknown }).check,\n accessPath: `${ctx.path}._zod.def.checks[${index}]._zod.check`,\n });\n return refs.length - 1;\n}\n\n/**\n * String formats the codegen can validate without a regex pattern.\n * Everything else without a pattern (e.g. z.jwt(), which validates\n * algorithmically) must fall back to Zod instead of being silently skipped.\n */\nconst PATTERNLESS_FORMATS = new Set([\"email\", \"uuid\", \"url\"]);\n\n/**\n * Formats whose def.pattern is NOT the authoritative validator — zod's\n * runtime check is algorithmic and accepts inputs the pattern rejects\n * (e.g. z.cidrv6() splits the prefix and validates the IPv6 part separately).\n */\nconst NON_AUTHORITATIVE_PATTERN_FORMATS = new Set([\"cidrv6\"]);\n\n/**\n * Check kinds where Zod itself installs a `when` guard\n * (`!nullish(value) && value.length/size !== undefined`). Compiled output\n * reproduces that gating structurally — length/size checks only run after the\n * typeof/instanceof guard — so a `when` on these kinds is expected and safe.\n * A `when` anywhere else must be user-supplied → fall back to Zod.\n * (A user-supplied `when` on one of these six kinds is indistinguishable from\n * the internal guard and remains unsupported.)\n */\nconst INTERNAL_WHEN_CHECK_KINDS = new Set([\n \"min_length\",\n \"max_length\",\n \"length_equals\",\n \"min_size\",\n \"max_size\",\n \"size_equals\",\n]);\n\n/** True when a check carries a runtime `when` predicate we cannot reproduce. */\nfunction hasUserWhen(def: ZodCheckDef): boolean {\n return Boolean(def.when) && !INTERNAL_WHEN_CHECK_KINDS.has(def.check);\n}\n\n/**\n * True when a check can't be compiled because of runtime modifiers:\n * a user-supplied `when` predicate, or `abort: true` (which stops later\n * checks and marks the option as aborted in union pruning — compiled\n * output always runs every check).\n */\nexport function hasUncompilableModifiers(def: ZodCheckDef): boolean {\n return hasUserWhen(def) || def.abort === true;\n}\n\nexport function extractChecks(\n checks: ZodCheckSchema[],\n /**\n * Registers the predicate of the check at `index` as an `__rf[N]` reference\n * and returns N, or undefined when references are unavailable (no refs array)\n * or the callback's shape cannot be called directly. Supplied by extractors\n * that know their schema's access path; omitted by callers that do not, which\n * simply keeps the old fall-back-to-zod behavior.\n */\n refineRef?: (index: number) => number | undefined,\n /** Registers a payload-taking callback (superRefine); see payloadCheckRef. */\n payloadRefFor?: (index: number) => number | undefined,\n): {\n checkIRs: CheckOrEffectIR[];\n hasFallback: boolean;\n} {\n const checkIRs: CheckOrEffectIR[] = [];\n let hasFallback = false;\n\n for (const [index, check] of checks.entries()) {\n const def = check._zod?.def;\n if (!def) continue;\n\n // Checks gated by a user-supplied runtime `when` predicate or abort:true\n // can't be reproduced in compiled output — delegate the schema to Zod.\n if (hasUncompilableModifiers(def)) {\n hasFallback = true;\n continue;\n }\n\n // Per-check custom error: bake static messages into the IR; error maps\n // that read the issue (dynamic) can't be compiled — fall back so Zod\n // produces the exact message.\n const resolved = resolveCheckMessage(def.error);\n if (resolved.kind === \"dynamic\") {\n hasFallback = true;\n continue;\n }\n const message = resolved.kind === \"static\" ? { message: resolved.message } : {};\n\n switch (def.check) {\n case \"min_length\":\n checkIRs.push({ kind: \"min_length\", minimum: def.minimum, ...message });\n break;\n case \"max_length\":\n checkIRs.push({ kind: \"max_length\", maximum: def.maximum, ...message });\n break;\n case \"length_equals\":\n checkIRs.push({ kind: \"length_equals\", length: def.length, ...message });\n break;\n case \"greater_than\":\n checkIRs.push({\n kind: \"greater_than\",\n value: def.value,\n inclusive: def.inclusive,\n ...message,\n });\n break;\n case \"less_than\":\n checkIRs.push({\n kind: \"less_than\",\n value: def.value,\n inclusive: def.inclusive,\n ...message,\n });\n break;\n case \"multiple_of\":\n checkIRs.push({ kind: \"multiple_of\", value: def.value, ...message });\n break;\n case \"number_format\":\n checkIRs.push({\n kind: \"number_format\",\n format: def.format as \"safeint\" | \"int32\" | \"uint32\" | \"float32\" | \"float64\",\n ...message,\n });\n break;\n case \"string_format\": {\n if (def.format === \"includes\" && typeof def.includes === \"string\") {\n checkIRs.push({\n kind: \"includes\",\n includes: def.includes,\n ...(typeof def.position === \"number\" ? { position: def.position } : {}),\n ...message,\n });\n break;\n }\n if (def.format === \"starts_with\" && typeof def.prefix === \"string\") {\n checkIRs.push({ kind: \"starts_with\", prefix: def.prefix, ...message });\n break;\n }\n if (def.format === \"ends_with\" && typeof def.suffix === \"string\") {\n checkIRs.push({ kind: \"ends_with\", suffix: def.suffix, ...message });\n break;\n }\n if (def.format === \"url\") {\n checkIRs.push({\n kind: \"string_format\",\n format: \"url\",\n ...regexFields(\"hostname\", \"hostnameFlags\", def.hostname),\n ...regexFields(\"protocol\", \"protocolFlags\", def.protocol),\n ...(def.normalize ? { normalize: true } : {}),\n ...message,\n });\n break;\n }\n if (NON_AUTHORITATIVE_PATTERN_FORMATS.has(def.format)) {\n hasFallback = true;\n break;\n }\n const pattern = def.pattern instanceof RegExp ? def.pattern.source : def.pattern;\n const flags = def.pattern instanceof RegExp && def.pattern.flags ? def.pattern.flags : \"\";\n if (!pattern && !PATTERNLESS_FORMATS.has(def.format)) {\n // Algorithmic format (e.g. jwt) — nothing to compile.\n hasFallback = true;\n break;\n }\n checkIRs.push({\n kind: \"string_format\",\n format: def.format,\n ...(pattern ? { pattern } : {}),\n ...(flags ? { patternFlags: flags } : {}),\n ...message,\n });\n break;\n }\n case \"overwrite\": {\n // $ZodCheckOverwrite (.trim(), .toLowerCase(), ...): value = def.tx(value)\n const source = tryCompileEffect(def.tx);\n if (source) {\n checkIRs.push({ kind: \"overwrite_effect\", source });\n } else {\n hasFallback = true;\n }\n break;\n }\n case \"custom\": {\n // superRefine / raw .check(): the callback collects issues from zod's\n // payload rather than returning a verdict, so it is referenced and\n // called with a synthesized payload (see SuperRefineEffectCheckIR).\n if (isPayloadCheck(check)) {\n const payloadRef = payloadRefFor?.(index);\n if (payloadRef === undefined) {\n hasFallback = true;\n break;\n }\n checkIRs.push({ kind: \"super_refine_effect\", refIndex: payloadRef });\n break;\n }\n // `.refine(fn, { path })` reports the issue against a member of the\n // refined value; anything but plain string/number segments is a shape\n // the generated path expression cannot reproduce.\n const customPath = Array.isArray(def.path) ? def.path : undefined;\n if (customPath?.some((p) => typeof p !== \"string\" && typeof p !== \"number\")) {\n hasFallback = true;\n break;\n }\n const pathIR = customPath && customPath.length > 0 ? { path: customPath } : {};\n const source = tryCompileEffect(def.fn);\n if (source) {\n checkIRs.push({ kind: \"refine_effect\", source, ...pathIR, ...message });\n break;\n }\n // Not inlineable (the predicate captures outer variables). Call the\n // user's own function object through a schema reference instead of\n // delegating: a captured `.refine()` at the root otherwise costs the\n // whole schema its compiled path.\n const refIndex = refineRef?.(index);\n if (refIndex !== undefined) {\n checkIRs.push({ kind: \"refine_effect\", refIndex, ...pathIR, ...message });\n } else {\n hasFallback = true;\n }\n break;\n }\n default:\n // Unknown check kind — never drop silently; delegate to Zod.\n hasFallback = true;\n break;\n }\n }\n\n return { checkIRs, hasFallback };\n}\n\nfunction regexFields(\n sourceKey: \"hostname\" | \"protocol\",\n flagsKey: \"hostnameFlags\" | \"protocolFlags\",\n value: unknown,\n): Partial<CheckStringFormat> {\n if (!(value instanceof RegExp)) return {};\n return { [sourceKey]: value.source, ...(value.flags ? { [flagsKey]: value.flags } : {}) };\n}\n\n// ─── Static error message extraction ────────────────────────────────────────\n\nexport type ResolvedMessage =\n | { kind: \"none\" }\n | { kind: \"static\"; message: string }\n | { kind: \"dynamic\" };\n\n/**\n * Classify a check/schema-level `error` param.\n *\n * Zod normalizes `.min(3, \"msg\")` / `{ error: \"msg\" }` / `{ message: \"msg\" }`\n * into an error-map function that ignores its issue argument and returns the\n * string. We call the function with an access-tracking Proxy: if it never\n * inspects the issue, its return value is a constant we can bake into the\n * generated issue. If it reads any issue property (input-dependent message),\n * it is dynamic and the schema must fall back to Zod for exact messages.\n */\nexport function resolveCheckMessage(error: unknown): ResolvedMessage {\n if (error === null || error === undefined) return { kind: \"none\" };\n if (typeof error === \"string\") return { kind: \"static\", message: error };\n if (typeof error !== \"function\") return { kind: \"dynamic\" };\n\n let accessed = false;\n const track = () => {\n accessed = true;\n };\n const probe = new Proxy(\n {},\n {\n get(_t, prop) {\n track();\n // Avoid breaking string coercion / await probing inside error maps.\n if (prop === Symbol.toPrimitive || prop === \"toString\" || prop === \"valueOf\") {\n return () => \"\";\n }\n return undefined;\n },\n has() {\n track();\n return false;\n },\n ownKeys() {\n track();\n return [];\n },\n getOwnPropertyDescriptor() {\n track();\n return undefined;\n },\n },\n );\n\n let result: unknown;\n try {\n result = (error as (issue: unknown) => unknown)(probe);\n } catch {\n return { kind: \"dynamic\" };\n }\n if (accessed) return { kind: \"dynamic\" };\n if (typeof result === \"string\") return { kind: \"static\", message: result };\n if (\n result !== null &&\n typeof result === \"object\" &&\n typeof (result as { message?: unknown }).message === \"string\"\n ) {\n return { kind: \"static\", message: (result as { message: string }).message };\n }\n // Error map deferred (returned undefined) — no custom message.\n if (result === undefined) return { kind: \"none\" };\n return { kind: \"dynamic\" };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"checks.js","names":[],"sources":["../../../src/core/extract/checks.ts"],"sourcesContent":["import type { CheckOrEffectIR, CheckStringFormat } from \"../types.js\";\nimport { isPayloadCheck, isReferenceablePredicate, tryCompileEffect } from \"./effects.js\";\nimport type { ExtractorContext, ZodCheckDef, ZodCheckSchema } from \"./types.js\";\n\n/**\n * Build the `refineRef` registrar for {@link extractChecks} from an extractor's\n * context: it appends a {@link RefEntry} pointing at the check's own predicate\n * (`<schema>._zod.def.checks[i]._zod.def.fn`) so generated code can call the\n * user's function object directly. Returns undefined — meaning \"fall back as\n * before\" — when the extraction is not collecting refs.\n */\nexport function refineRefRegistrar(\n ctx: ExtractorContext,\n checks: ZodCheckSchema[],\n): ((index: number) => number | undefined) | undefined {\n const refs = ctx.refs;\n if (!refs) return undefined;\n return (index) => {\n const fn = checks[index]?._zod?.def?.fn;\n if (!isReferenceablePredicate(fn)) return undefined;\n refs.push({ schema: fn, accessPath: `${ctx.path}._zod.def.checks[${index}]._zod.def.fn` });\n return refs.length - 1;\n };\n}\n\n/**\n * Build the `customParamsRef` registrar for {@link extractChecks}: it appends a\n * {@link RefEntry} pointing at the check's own `params` object\n * (`<schema>._zod.def.checks[i]._zod.def.params`), which $ZodCustom copies onto\n * the issue by reference.\n *\n * A reference rather than a baked literal because `params` is opaque user data:\n * it may hold functions, symbols or a cycle (none of which survive a literal),\n * and zod hands out the SAME object on every issue, so a per-failure copy would\n * break the identity comparison that is the point of passing params.\n *\n * Returns undefined — meaning \"fall back to zod\" — when refs are unavailable.\n */\nexport function customParamsRefRegistrar(\n ctx: ExtractorContext,\n checks: ZodCheckSchema[],\n): ((index: number) => number | undefined) | undefined {\n const refs = ctx.refs;\n if (!refs) return undefined;\n return (index) => {\n refs.push({\n schema: checks[index]?._zod?.def?.params,\n accessPath: `${ctx.path}._zod.def.checks[${index}]._zod.def.params`,\n });\n return refs.length - 1;\n };\n}\n\n/**\n * Register the payload-taking callback of check `index` (`superRefine`) as an\n * `__rf[N]` reference, pointing at zod's own wrapper\n * (`<schema>._zod.def.checks[i]._zod.check`).\n *\n * Only the LAST check on a node qualifies: an issue carrying\n * `fatal`/`continue:false` aborts zod's remaining chain, and compiled output\n * runs every check unconditionally — with nothing following, there is nothing\n * left to abort, so the two agree.\n */\nexport function payloadCheckRef(\n ctx: ExtractorContext,\n checks: ZodCheckSchema[],\n index: number,\n): number | undefined {\n if (index !== checks.length - 1) return undefined;\n const check = checks[index];\n const refs = ctx.refs;\n if (!refs || !check || !isPayloadCheck(check)) return undefined;\n refs.push({\n schema: (check._zod as { check: unknown }).check,\n accessPath: `${ctx.path}._zod.def.checks[${index}]._zod.check`,\n });\n return refs.length - 1;\n}\n\n/**\n * BUILT-IN string formats the codegen can validate without a regex pattern,\n * because it substitutes a known regex of its own (see EMAIL_REGEX_SOURCE /\n * UUID_REGEX_SOURCE in codegen/schemas/string.ts). Everything else without a\n * pattern (e.g. z.jwt(), which validates algorithmically) must fall back to Zod\n * instead of being silently skipped.\n *\n * Built-in ONLY: this is keyed on the format NAME, and a custom format's name is\n * user-chosen, so a match here proves nothing about a `$ZodCustomStringFormat`\n * — see the isCustomStringFormat gate at the use site.\n */\nconst PATTERNLESS_FORMATS = new Set([\"email\", \"uuid\", \"url\"]);\n\n/**\n * String formats whose `def.pattern` is NOT the validator Zod actually runs.\n *\n * Zod attaches a `def.pattern` to every string format because `toJSONSchema()`\n * needs one to emit. A handful of constructors then REPLACE the pattern check\n * outright: `$ZodStringFormat.init` installs the pattern check with `??=`, and\n * the constructor's following `inst._zod.check = …` assignment overwrites it.\n * The pattern survives only as JSON Schema metadata. Compiling it produces a\n * verdict Zod does not share — in BOTH directions — so these delegate instead.\n *\n * - `ipv6`: Zod validates with `new URL(\"http://[\" + value + \"]\")`. The URL\n * parser accepts IPv4-mapped forms the regex has no alternative for\n * (`\"::ffff:1.2.3.4\"`, `\"1:2:3:4:5:6:1.2.3.4\"`) and, since it strips tabs and\n * ends the host at `@` or `/`, even non-addresses (`\"@_c.Z=\\t/X+9\"`).\n * - `cidrv6`: splits on `/`, range-checks the prefix as a number, then runs the\n * same `new URL` probe on the address half — inheriting every `ipv6` gap.\n * - `base64`: `length % 4 === 0 && atob(value)` does not throw. `atob` is\n * HTML's forgiving-base64 decode, which STRIPS ASCII whitespace before\n * decoding, so Zod accepts `\"AAAA \"`, `\"\\t\\t\\t\\tAAAA\"` and `\"+c \\n\"` —\n * all rejected by `regexes.base64`, whose charset has no room for whitespace.\n * - `base64url`: `regexes.base64url.test(v)` AND an `atob` of the re-padded\n * value. The regex alone is `^[A-Za-z0-9_-]*$`, which accepts every\n * `length % 4 === 1` string (`\"a\"`, `\"-\"`, `\"A\"`, `\"9\"`) that decoding fails.\n *\n * Every other string format compiles its pattern, which is only safe while that\n * pattern and Zod's installed check keep agreeing. What keeps this list honest\n * — in both directions — is tests/string-format-parity.test.ts, which fuzzes\n * every reachable string format against Zod over one shared corpus.\n */\nconst NON_AUTHORITATIVE_PATTERN_FORMATS = new Set([\"base64\", \"base64url\", \"cidrv6\", \"ipv6\"]);\n\n/**\n * True when a `string_format` check is a `$ZodCustomStringFormat` — the family\n * Zod's `_stringFormat` helper builds: `z.stringFormat()`, and the built-ins\n * `z.hex()`, `z.hostname()` and `z.hash()` that route through it.\n *\n * `def.fn` is the structural marker: `_stringFormat` is the ONLY producer of a\n * `string_format` check def that carries one (every other format — guid, uuid,\n * email, ipv4, iso.*, lowercase/uppercase, regex, includes/starts_with/ends_with\n * — is built from a plain `{ check, format, pattern }` def). A name list cannot\n * stand in for it: `z.stringFormat(\"anything\", /re/)` lets the caller pick the\n * format name, so the set of names is open.\n *\n * Two things follow from being in this family, both about Zod's OVERRIDE of\n * `inst._zod.check` (see `$ZodCustomStringFormat` in zod/v4/core/schemas.js):\n * the issue it pushes is bare (see CheckStringFormat.bareIssue), and it calls\n * `def.fn` rather than testing `def.pattern` — which is what makes a `g`/`y`\n * pattern stateful (see {@link isStatefulCustomFormat}).\n */\nfunction isCustomStringFormat(def: ZodCheckDef): boolean {\n return typeof def.fn === \"function\";\n}\n\n/**\n * True when a `string_format` check validates through `def.fn` over a STATEFUL\n * (`g`/`y`-flagged) pattern — a custom format built by Zod's `_stringFormat`\n * helper, i.e. `z.stringFormat(\"name\", /ab/g)`.\n *\n * Such a format is stateful in Zod, while the compiled output is deliberately\n * stateless — so the two diverge from the first successful match onward.\n * `_stringFormat` stores the regex as `def.pattern` AND closes over it as\n * `def.fn = (val) => fnOrRegex.test(val)`.\n * `$ZodCustomStringFormat` validates by calling that `def.fn`, and NOTHING\n * resets `lastIndex`, so a `g`/`y` regex carries its cursor across parses:\n * `z.stringFormat(\"f\", /ab/g).safeParse(\"abab\")` returns true, true, FALSE,\n * true over four calls. Contrast `$ZodCheckRegex` — what `.regex(/ab/g)` builds\n * — which does an explicit `def.pattern.lastIndex = 0` before testing and is\n * therefore stateless. The codegen's `lastIndexReset` reproduces THAT reset,\n * which is right for `.regex()` and is exactly what makes a custom format\n * diverge. The state lives in the user's own RegExp object, which the compiled\n * validator does not hold, so there is nothing faithful to emit: delegate.\n *\n * This needs a predicate rather than a name set like\n * NON_AUTHORITATIVE_PATTERN_FORMATS above, because a custom format's name is\n * user-chosen and cannot be enumerated. Narrowness matters in both clauses:\n * `def.fn` alone would over-delegate the built-ins Zod also routes through\n * `_stringFormat` (hostname, hex, hash), and the flag test alone would\n * over-delegate `.regex(/…/g)` — both of which stay compiled because their\n * patterns are unflagged and their check is not `def.fn`, respectively.\n */\nfunction isStatefulCustomFormat(def: ZodCheckDef): boolean {\n return (\n isCustomStringFormat(def) && def.pattern instanceof RegExp && /[gy]/.test(def.pattern.flags)\n );\n}\n\n/**\n * Check kinds where Zod itself installs a `when` guard\n * (`!nullish(value) && value.length/size !== undefined`). Compiled output\n * reproduces that gating structurally — length/size checks only run after the\n * typeof/instanceof guard — so a `when` on these kinds is expected and safe.\n * A `when` anywhere else must be user-supplied → fall back to Zod.\n * (A user-supplied `when` on one of these six kinds is indistinguishable from\n * the internal guard and remains unsupported.)\n */\nconst INTERNAL_WHEN_CHECK_KINDS = new Set([\n \"min_length\",\n \"max_length\",\n \"length_equals\",\n \"min_size\",\n \"max_size\",\n \"size_equals\",\n]);\n\n/** True when a check carries a runtime `when` predicate we cannot reproduce. */\nfunction hasUserWhen(def: ZodCheckDef): boolean {\n return Boolean(def.when) && !INTERNAL_WHEN_CHECK_KINDS.has(def.check);\n}\n\n/**\n * True when a check can't be compiled because of runtime modifiers:\n * a user-supplied `when` predicate, or `abort: true` (which stops later\n * checks and marks the option as aborted in union pruning — compiled\n * output always runs every check).\n */\nexport function hasUncompilableModifiers(def: ZodCheckDef): boolean {\n return hasUserWhen(def) || def.abort === true;\n}\n\nexport function extractChecks(\n checks: ZodCheckSchema[],\n /**\n * Registers the predicate of the check at `index` as an `__rf[N]` reference\n * and returns N, or undefined when references are unavailable (no refs array)\n * or the callback's shape cannot be called directly. Supplied by extractors\n * that know their schema's access path; omitted by callers that do not, which\n * simply keeps the old fall-back-to-zod behavior.\n */\n refineRef?: (index: number) => number | undefined,\n /** Registers a payload-taking callback (superRefine); see payloadCheckRef. */\n payloadRefFor?: (index: number) => number | undefined,\n /** Registers a `.refine(fn, { params })` payload; see customParamsRefRegistrar. */\n customParamsRef?: (index: number) => number | undefined,\n): {\n checkIRs: CheckOrEffectIR[];\n hasFallback: boolean;\n} {\n const checkIRs: CheckOrEffectIR[] = [];\n let hasFallback = false;\n\n for (const [index, check] of checks.entries()) {\n const def = check._zod?.def;\n if (!def) continue;\n\n // Checks gated by a user-supplied runtime `when` predicate or abort:true\n // can't be reproduced in compiled output — delegate the schema to Zod.\n if (hasUncompilableModifiers(def)) {\n hasFallback = true;\n continue;\n }\n\n // Per-check custom error: bake static messages into the IR; error maps\n // that read the issue (dynamic) can't be compiled — fall back so Zod\n // produces the exact message.\n const resolved = resolveCheckMessage(def.error);\n if (resolved.kind === \"dynamic\") {\n hasFallback = true;\n continue;\n }\n const message = resolved.kind === \"static\" ? { message: resolved.message } : {};\n\n switch (def.check) {\n case \"min_length\":\n checkIRs.push({ kind: \"min_length\", minimum: def.minimum, ...message });\n break;\n case \"max_length\":\n checkIRs.push({ kind: \"max_length\", maximum: def.maximum, ...message });\n break;\n case \"length_equals\":\n checkIRs.push({ kind: \"length_equals\", length: def.length, ...message });\n break;\n case \"greater_than\":\n checkIRs.push({\n kind: \"greater_than\",\n value: def.value,\n inclusive: def.inclusive,\n ...message,\n });\n break;\n case \"less_than\":\n checkIRs.push({\n kind: \"less_than\",\n value: def.value,\n inclusive: def.inclusive,\n ...message,\n });\n break;\n case \"multiple_of\":\n checkIRs.push({ kind: \"multiple_of\", value: def.value, ...message });\n break;\n case \"number_format\":\n checkIRs.push({\n kind: \"number_format\",\n format: def.format as \"safeint\" | \"int32\" | \"uint32\" | \"float32\" | \"float64\",\n ...message,\n });\n break;\n case \"string_format\": {\n // `z.stringFormat(name, …)` lets the CALLER pick the format name, so a\n // custom format can be named \"email\", \"url\" — anything a built-in is\n // named. The name therefore does NOT identify which validator Zod runs;\n // only the structural `def.fn` marker does. Every branch below that\n // treats a name as proof of a built-in's behaviour is gated on this.\n const isCustom = isCustomStringFormat(def);\n\n // A custom format over a g/y regex advances that regex's lastIndex on\n // every parse and Zod never resets it — see isStatefulCustomFormat.\n if (isStatefulCustomFormat(def)) {\n hasFallback = true;\n break;\n }\n if (def.format === \"includes\" && typeof def.includes === \"string\") {\n checkIRs.push({\n kind: \"includes\",\n includes: def.includes,\n ...(typeof def.position === \"number\" ? { position: def.position } : {}),\n ...message,\n });\n break;\n }\n if (def.format === \"starts_with\" && typeof def.prefix === \"string\") {\n checkIRs.push({ kind: \"starts_with\", prefix: def.prefix, ...message });\n break;\n }\n if (def.format === \"ends_with\" && typeof def.suffix === \"string\") {\n checkIRs.push({ kind: \"ends_with\", suffix: def.suffix, ...message });\n break;\n }\n // z.url() validates with the URL parser plus optional hostname/protocol\n // probes, and carries no pattern worth compiling. A custom format merely\n // NAMED \"url\" shares none of that — it validates through `def.fn` — so\n // taking this branch both discarded the user's own `def.pattern` and\n // answered with the URL parser's verdict instead of the user's.\n if (def.format === \"url\" && !isCustom) {\n checkIRs.push({\n kind: \"string_format\",\n format: \"url\",\n ...regexFields(\"hostname\", \"hostnameFlags\", def.hostname),\n ...regexFields(\"protocol\", \"protocolFlags\", def.protocol),\n ...(def.normalize ? { normalize: true } : {}),\n ...message,\n });\n break;\n }\n // Not gated on isCustom: this set only ever adds a fallback, so a custom\n // format that happens to borrow one of these names loses its fast path\n // but never its verdict.\n if (NON_AUTHORITATIVE_PATTERN_FORMATS.has(def.format)) {\n hasFallback = true;\n break;\n }\n const pattern = def.pattern instanceof RegExp ? def.pattern.source : def.pattern;\n const flags = def.pattern instanceof RegExp && def.pattern.flags ? def.pattern.flags : \"\";\n if (!pattern && !(!isCustom && PATTERNLESS_FORMATS.has(def.format))) {\n // Nothing to compile: an algorithmic built-in (e.g. jwt), or a custom\n // format built from a FUNCTION rather than a regex. For the latter the\n // predicate lives only in `def.fn`, and a name-matched built-in regex\n // is emphatically not it — `z.stringFormat(\"email\", (v) => v.length\n // === 3)` accepts \"abc\" and rejects \"a@b.co\", the exact inverse of the\n // email regex. Delegate to Zod.\n hasFallback = true;\n break;\n }\n checkIRs.push({\n kind: \"string_format\",\n format: def.format,\n ...(pattern ? { pattern } : {}),\n ...(flags ? { patternFlags: flags } : {}),\n ...(isCustom ? { bareIssue: true } : {}),\n ...message,\n });\n break;\n }\n case \"overwrite\": {\n // $ZodCheckOverwrite (.trim(), .toLowerCase(), ...): value = def.tx(value)\n const source = tryCompileEffect(def.tx);\n if (source) {\n checkIRs.push({ kind: \"overwrite_effect\", source });\n } else {\n hasFallback = true;\n }\n break;\n }\n case \"custom\": {\n // superRefine / raw .check(): the callback collects issues from zod's\n // payload rather than returning a verdict, so it is referenced and\n // called with a synthesized payload (see SuperRefineEffectCheckIR).\n if (isPayloadCheck(check)) {\n const payloadRef = payloadRefFor?.(index);\n if (payloadRef === undefined) {\n hasFallback = true;\n break;\n }\n checkIRs.push({ kind: \"super_refine_effect\", refIndex: payloadRef });\n break;\n }\n // `.refine(fn, { path })` reports the issue against a member of the\n // refined value; anything but plain string/number segments is a shape\n // the generated path expression cannot reproduce.\n const customPath = Array.isArray(def.path) ? def.path : undefined;\n if (customPath?.some((p) => typeof p !== \"string\" && typeof p !== \"number\")) {\n hasFallback = true;\n break;\n }\n const pathIR = customPath && customPath.length > 0 ? { path: customPath } : {};\n // `.refine(fn, { params })`: zod attaches the object to the issue, and\n // an error map reads it back. Held as a reference (see\n // customParamsRefRegistrar); with no ref array to hold it, the issue\n // cannot be reproduced at all, so the schema goes to zod.\n let paramsIR: { paramsRefIndex?: number } = {};\n if (def.params !== undefined) {\n const paramsRefIndex = customParamsRef?.(index);\n if (paramsRefIndex === undefined) {\n hasFallback = true;\n break;\n }\n paramsIR = { paramsRefIndex };\n }\n const source = tryCompileEffect(def.fn);\n if (source) {\n checkIRs.push({ kind: \"refine_effect\", source, ...pathIR, ...paramsIR, ...message });\n break;\n }\n // Not inlineable (the predicate captures outer variables). Call the\n // user's own function object through a schema reference instead of\n // delegating: a captured `.refine()` at the root otherwise costs the\n // whole schema its compiled path.\n const refIndex = refineRef?.(index);\n if (refIndex !== undefined) {\n checkIRs.push({ kind: \"refine_effect\", refIndex, ...pathIR, ...paramsIR, ...message });\n } else {\n hasFallback = true;\n }\n break;\n }\n default:\n // Unknown check kind — never drop silently; delegate to Zod.\n hasFallback = true;\n break;\n }\n }\n\n return { checkIRs, hasFallback };\n}\n\nfunction regexFields(\n sourceKey: \"hostname\" | \"protocol\",\n flagsKey: \"hostnameFlags\" | \"protocolFlags\",\n value: unknown,\n): Partial<CheckStringFormat> {\n if (!(value instanceof RegExp)) return {};\n return { [sourceKey]: value.source, ...(value.flags ? { [flagsKey]: value.flags } : {}) };\n}\n\n// ─── Static error message extraction ────────────────────────────────────────\n\nexport type ResolvedMessage =\n | { kind: \"none\" }\n | { kind: \"static\"; message: string }\n | { kind: \"dynamic\" };\n\n/**\n * Classify a check/schema-level `error` param.\n *\n * Zod normalizes `.min(3, \"msg\")` / `{ error: \"msg\" }` / `{ message: \"msg\" }`\n * into an error-map function that ignores its issue argument and returns the\n * string. We call the function with an access-tracking Proxy: if it never\n * inspects the issue, its return value is a constant we can bake into the\n * generated issue. If it reads any issue property (input-dependent message),\n * it is dynamic and the schema must fall back to Zod for exact messages.\n */\nexport function resolveCheckMessage(error: unknown): ResolvedMessage {\n if (error === null || error === undefined) return { kind: \"none\" };\n if (typeof error === \"string\") return { kind: \"static\", message: error };\n if (typeof error !== \"function\") return { kind: \"dynamic\" };\n\n let accessed = false;\n const track = () => {\n accessed = true;\n };\n const probe = new Proxy(\n {},\n {\n get(_t, prop) {\n track();\n // Avoid breaking string coercion / await probing inside error maps.\n if (prop === Symbol.toPrimitive || prop === \"toString\" || prop === \"valueOf\") {\n return () => \"\";\n }\n return undefined;\n },\n has() {\n track();\n return false;\n },\n ownKeys() {\n track();\n return [];\n },\n getOwnPropertyDescriptor() {\n track();\n return undefined;\n },\n },\n );\n\n let result: unknown;\n try {\n result = (error as (issue: unknown) => unknown)(probe);\n } catch {\n return { kind: \"dynamic\" };\n }\n if (accessed) return { kind: \"dynamic\" };\n if (typeof result === \"string\") return { kind: \"static\", message: result };\n if (\n result !== null &&\n typeof result === \"object\" &&\n typeof (result as { message?: unknown }).message === \"string\"\n ) {\n return { kind: \"static\", message: (result as { message: string }).message };\n }\n // Error map deferred (returned undefined) — no custom message.\n if (result === undefined) return { kind: \"none\" };\n return { kind: \"dynamic\" };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"array.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/array.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"array.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/array.ts"],"mappings":";;;iBAkBgB,aAAa,KAAK,QAAQ,KAAK,mBAAmB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { extractChecks, payloadCheckRef, refineRefRegistrar } from "../checks.js";
|
|
1
|
+
import { customParamsRefRegistrar, extractChecks, payloadCheckRef, refineRefRegistrar } from "../checks.js";
|
|
2
2
|
//#region src/core/extract/extractors/array.ts
|
|
3
3
|
/** Check kinds the array codegen knows how to emit. Anything else → fallback. */
|
|
4
4
|
const ARRAY_CHECK_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -15,7 +15,7 @@ function extractArray(def, ctx) {
|
|
|
15
15
|
element,
|
|
16
16
|
checks: []
|
|
17
17
|
};
|
|
18
|
-
const { checkIRs, hasFallback } = extractChecks(def.checks, refineRefRegistrar(ctx, def.checks), (i) => payloadCheckRef(ctx, def.checks, i));
|
|
18
|
+
const { checkIRs, hasFallback } = extractChecks(def.checks, refineRefRegistrar(ctx, def.checks), (i) => payloadCheckRef(ctx, def.checks, i), customParamsRefRegistrar(ctx, def.checks));
|
|
19
19
|
if (hasFallback) return ctx.fallback("refine");
|
|
20
20
|
if (checkIRs.some((c) => !ARRAY_CHECK_KINDS.has(c.kind))) return ctx.fallback("unsupported");
|
|
21
21
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"array.js","names":[],"sources":["../../../../src/core/extract/extractors/array.ts"],"sourcesContent":["import type { SchemaIR } from \"../../types.js\";\nimport {
|
|
1
|
+
{"version":3,"file":"array.js","names":[],"sources":["../../../../src/core/extract/extractors/array.ts"],"sourcesContent":["import type { SchemaIR } from \"../../types.js\";\nimport {\n customParamsRefRegistrar,\n extractChecks,\n payloadCheckRef,\n refineRefRegistrar,\n} from \"../checks.js\";\nimport type { ExtractorContext, ZodDef } from \"../types.js\";\n\n/** Check kinds the array codegen knows how to emit. Anything else → fallback. */\nconst ARRAY_CHECK_KINDS = new Set([\n \"min_length\",\n \"max_length\",\n \"length_equals\",\n \"refine_effect\",\n \"super_refine_effect\",\n]);\n\nexport function extractArray(def: ZodDef, ctx: ExtractorContext): SchemaIR {\n const element = ctx.visit(def.element, \"._zod.def.element\");\n if (!def.checks || def.checks.length === 0) {\n return { type: \"array\", element, checks: [] };\n }\n const { checkIRs, hasFallback } = extractChecks(\n def.checks,\n refineRefRegistrar(ctx, def.checks),\n (i) => payloadCheckRef(ctx, def.checks, i),\n customParamsRefRegistrar(ctx, def.checks),\n );\n // Previously hasFallback was ignored here, silently dropping uncompilable\n // refinements (e.g. captured-variable .refine()) from compiled output.\n if (hasFallback) return ctx.fallback(\"refine\");\n if (checkIRs.some((c) => !ARRAY_CHECK_KINDS.has(c.kind))) {\n return ctx.fallback(\"unsupported\");\n }\n return { type: \"array\", element, checks: checkIRs };\n}\n"],"mappings":";;;AAUA,MAAM,oCAAoB,IAAI,IAAI;CAChC;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAgB,aAAa,KAAa,KAAiC;CACzE,MAAM,UAAU,IAAI,MAAM,IAAI,SAAS,mBAAmB;CAC1D,IAAI,CAAC,IAAI,UAAU,IAAI,OAAO,WAAW,GACvC,OAAO;EAAE,MAAM;EAAS;EAAS,QAAQ,CAAC;CAAE;CAE9C,MAAM,EAAE,UAAU,gBAAgB,cAChC,IAAI,QACJ,mBAAmB,KAAK,IAAI,MAAM,IACjC,MAAM,gBAAgB,KAAK,IAAI,QAAQ,CAAC,GACzC,yBAAyB,KAAK,IAAI,MAAM,CAC1C;CAGA,IAAI,aAAa,OAAO,IAAI,SAAS,QAAQ;CAC7C,IAAI,SAAS,MAAM,MAAM,CAAC,kBAAkB,IAAI,EAAE,IAAI,CAAC,GACrD,OAAO,IAAI,SAAS,aAAa;CAEnC,OAAO;EAAE,MAAM;EAAS;EAAS,QAAQ;CAAS;AACpD"}
|
|
@@ -3,6 +3,7 @@ import { hasMutation } from "../../codegen/context.js";
|
|
|
3
3
|
const extractIntersection = (def, ctx) => {
|
|
4
4
|
const left = ctx.visit(def.left, "._zod.def.left");
|
|
5
5
|
const right = ctx.visit(def.right, "._zod.def.right");
|
|
6
|
+
if (canReportUnrecognizedKeys(left) || canReportUnrecognizedKeys(right)) return ctx.fallback("unsupported");
|
|
6
7
|
const merged = mergeDisjointStripObjects(left, right);
|
|
7
8
|
if (merged !== null && ctx.refs) {
|
|
8
9
|
const refIndex = ctx.refs.length;
|
|
@@ -23,6 +24,49 @@ const extractIntersection = (def, ctx) => {
|
|
|
23
24
|
right
|
|
24
25
|
};
|
|
25
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Can this side of an intersection produce an `unrecognized_keys` issue?
|
|
29
|
+
*
|
|
30
|
+
* Zod pairs the two sides' unrecognized keys by NAME ONLY, with no regard for
|
|
31
|
+
* the path the complaint came from: `handleIntersectionResults` walks each
|
|
32
|
+
* side's FLAT issue list, unions the bare `iss.keys` strings into one map, and
|
|
33
|
+
* emits `[...unrecKeys].filter(([, f]) => f.l && f.r)` — "Report only keys
|
|
34
|
+
* unrecognized by BOTH sides" — under the first LEFT issue's path. A strict
|
|
35
|
+
* object nested in a property or an array element therefore participates in
|
|
36
|
+
* the reconciliation exactly like a top-level one, so a strict object ANYWHERE
|
|
37
|
+
* in a side disqualifies the compiled sequential run and the merged single
|
|
38
|
+
* pass alike.
|
|
39
|
+
*
|
|
40
|
+
* Mirrors {@link hasMutation}'s recursion for which node kinds hold children.
|
|
41
|
+
* Only `object.strict` reaches zod's `handleCatchall` unrecognized-keys push;
|
|
42
|
+
* the sole other producer — a record over a finite key set — already falls back
|
|
43
|
+
* at extraction, so a compiled `record` cannot raise it.
|
|
44
|
+
*/
|
|
45
|
+
function canReportUnrecognizedKeys(ir) {
|
|
46
|
+
switch (ir.type) {
|
|
47
|
+
case "object": return ir.strict === true || ir.catchall !== void 0 && canReportUnrecognizedKeys(ir.catchall) || Object.values(ir.properties).some((p) => canReportUnrecognizedKeys(p));
|
|
48
|
+
case "array": return canReportUnrecognizedKeys(ir.element);
|
|
49
|
+
case "tuple": return ir.items.some(canReportUnrecognizedKeys) || ir.rest !== null && canReportUnrecognizedKeys(ir.rest);
|
|
50
|
+
case "record":
|
|
51
|
+
case "set": return canReportUnrecognizedKeys(ir.valueType);
|
|
52
|
+
case "map": return canReportUnrecognizedKeys(ir.keyType) || canReportUnrecognizedKeys(ir.valueType);
|
|
53
|
+
case "catch":
|
|
54
|
+
case "default":
|
|
55
|
+
case "effect":
|
|
56
|
+
case "nullable":
|
|
57
|
+
case "optional":
|
|
58
|
+
case "readonly":
|
|
59
|
+
case "recursionTarget":
|
|
60
|
+
case "zodDelegate": return canReportUnrecognizedKeys(ir.inner);
|
|
61
|
+
case "discriminatedUnion":
|
|
62
|
+
case "union": return ir.options.some(canReportUnrecognizedKeys);
|
|
63
|
+
case "intersection": return canReportUnrecognizedKeys(ir.left) || canReportUnrecognizedKeys(ir.right);
|
|
64
|
+
case "pipe": return canReportUnrecognizedKeys(ir.in) || canReportUnrecognizedKeys(ir.out);
|
|
65
|
+
case "fallback": return true;
|
|
66
|
+
case "recursiveRef": return true;
|
|
67
|
+
default: return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
26
70
|
/** Merge only the object-intersection shape whose equivalence is statically obvious. */
|
|
27
71
|
function mergeDisjointStripObjects(left, right) {
|
|
28
72
|
if (left.type !== "object" || right.type !== "object") return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intersection.js","names":[],"sources":["../../../../src/core/extract/extractors/intersection.ts"],"sourcesContent":["import { hasMutation } from \"../../codegen/context.js\";\nimport type { ObjectIR, SchemaIR } from \"../../types.js\";\nimport type { Extractor } from \"../types.js\";\n\nexport const extractIntersection: Extractor = (def, ctx) => {\n const left = ctx.visit(def.left, \"._zod.def.left\");\n const right = ctx.visit(def.right, \"._zod.def.right\");\n\n // The everyday `.and()` case: two default z.object() shapes with disjoint\n // keys. Zod parses each against the original input, strips each side to its\n // own keys, then merges the two outputs. A single object over the union of\n // those keys has identical acceptance and output semantics. The pristine\n // intersection remains the source of truth for cold-path issues (notably, a\n // non-object input produces one issue per side).\n const merged = mergeDisjointStripObjects(left, right);\n if (merged !== null && ctx.refs) {\n const refIndex = ctx.refs.length;\n ctx.refs.push({ schema: ctx.schema, accessPath: ctx.path });\n return { type: \"zodDelegate\", inner: merged, refIndex };\n }\n\n // Zod validates both sides on the ORIGINAL input and MERGES the results,\n // throwing on unmergable conflicts. The compiled generator runs the sides\n // sequentially on the same value — equivalent only when neither side\n // rewrites it. Mutating sides (coerce, trim, defaults, ...) delegate to Zod.\n if (hasMutation(left) || hasMutation(right)) {\n return ctx.fallback(\"unsupported\");\n }\n return { type: \"intersection\", left, right };\n};\n\n/** Merge only the object-intersection shape whose equivalence is statically obvious. */\nfunction mergeDisjointStripObjects(left: SchemaIR, right: SchemaIR): ObjectIR | null {\n if (left.type !== \"object\" || right.type !== \"object\") return null;\n if (left.stripUnknownKeys !== true || right.stripUnknownKeys !== true) return null;\n if (\n left.strict === true ||\n right.strict === true ||\n left.catchall !== undefined ||\n right.catchall !== undefined ||\n left.checks !== undefined ||\n right.checks !== undefined ||\n left.suppressAbsentKeys !== undefined ||\n right.suppressAbsentKeys !== undefined\n ) {\n return null;\n }\n\n const leftKeys = Object.keys(left.properties);\n if (leftKeys.some((key) => Object.hasOwn(right.properties, key))) return null;\n\n const properties: Record<string, SchemaIR> = Object.create(null) as Record<string, SchemaIR>;\n for (const key of leftKeys) properties[key] = left.properties[key] as SchemaIR;\n const rightKeys = Object.keys(right.properties);\n for (const key of rightKeys) {\n properties[key] = right.properties[key] as SchemaIR;\n }\n // Integer-like keys are enumerated before other strings regardless of\n // insertion order. If that changes left-then-right traversal, a successful\n // parse could observe transform/refine callbacks in a different order.\n const expectedOrder = [...leftKeys, ...rightKeys];\n if (!Object.keys(properties).every((key, index) => key === expectedOrder[index])) return null;\n return { type: \"object\", properties, stripUnknownKeys: true };\n}\n"],"mappings":";;AAIA,MAAa,uBAAkC,KAAK,QAAQ;CAC1D,MAAM,OAAO,IAAI,MAAM,IAAI,MAAM,gBAAgB;CACjD,MAAM,QAAQ,IAAI,MAAM,IAAI,OAAO,iBAAiB;
|
|
1
|
+
{"version":3,"file":"intersection.js","names":[],"sources":["../../../../src/core/extract/extractors/intersection.ts"],"sourcesContent":["import { hasMutation } from \"../../codegen/context.js\";\nimport type { ObjectIR, SchemaIR } from \"../../types.js\";\nimport type { Extractor } from \"../types.js\";\n\nexport const extractIntersection: Extractor = (def, ctx) => {\n const left = ctx.visit(def.left, \"._zod.def.left\");\n const right = ctx.visit(def.right, \"._zod.def.right\");\n\n // Zod RECONCILES the two sides' unrecognized_keys issues instead of taking\n // their union: handleIntersectionResults pulls every unrecognized_keys issue\n // out of both result lists, records which side named each key, and re-emits\n // one issue holding only the keys \"unrecognized by BOTH sides\". Nothing in a\n // sequential two-pass run pairs those key sets, so a strict side's complaint\n // that zod would cancel becomes a compiled rejection — and on the merged\n // path below it becomes a rejection with an EMPTY issue list, because the\n // cold path re-asks zod, which accepts. Delegate whenever either side can\n // raise the issue at all; this has to precede the merge, whose own strict\n // bail only inspects the two top-level objects.\n if (canReportUnrecognizedKeys(left) || canReportUnrecognizedKeys(right)) {\n return ctx.fallback(\"unsupported\");\n }\n\n // The everyday `.and()` case: two default z.object() shapes with disjoint\n // keys. Zod parses each against the original input, strips each side to its\n // own keys, then merges the two outputs. A single object over the union of\n // those keys has identical acceptance and output semantics. The pristine\n // intersection remains the source of truth for cold-path issues (notably, a\n // non-object input produces one issue per side).\n const merged = mergeDisjointStripObjects(left, right);\n if (merged !== null && ctx.refs) {\n const refIndex = ctx.refs.length;\n ctx.refs.push({ schema: ctx.schema, accessPath: ctx.path });\n return { type: \"zodDelegate\", inner: merged, refIndex };\n }\n\n // Zod validates both sides on the ORIGINAL input and MERGES the results,\n // throwing on unmergable conflicts. The compiled generator runs the sides\n // sequentially on the same value — equivalent only when neither side\n // rewrites it. Mutating sides (coerce, trim, defaults, ...) delegate to Zod.\n if (hasMutation(left) || hasMutation(right)) {\n return ctx.fallback(\"unsupported\");\n }\n return { type: \"intersection\", left, right };\n};\n\n/**\n * Can this side of an intersection produce an `unrecognized_keys` issue?\n *\n * Zod pairs the two sides' unrecognized keys by NAME ONLY, with no regard for\n * the path the complaint came from: `handleIntersectionResults` walks each\n * side's FLAT issue list, unions the bare `iss.keys` strings into one map, and\n * emits `[...unrecKeys].filter(([, f]) => f.l && f.r)` — \"Report only keys\n * unrecognized by BOTH sides\" — under the first LEFT issue's path. A strict\n * object nested in a property or an array element therefore participates in\n * the reconciliation exactly like a top-level one, so a strict object ANYWHERE\n * in a side disqualifies the compiled sequential run and the merged single\n * pass alike.\n *\n * Mirrors {@link hasMutation}'s recursion for which node kinds hold children.\n * Only `object.strict` reaches zod's `handleCatchall` unrecognized-keys push;\n * the sole other producer — a record over a finite key set — already falls back\n * at extraction, so a compiled `record` cannot raise it.\n */\nfunction canReportUnrecognizedKeys(ir: SchemaIR): boolean {\n switch (ir.type) {\n case \"object\":\n return (\n ir.strict === true ||\n (ir.catchall !== undefined && canReportUnrecognizedKeys(ir.catchall)) ||\n Object.values(ir.properties).some((p) => canReportUnrecognizedKeys(p))\n );\n case \"array\":\n return canReportUnrecognizedKeys(ir.element);\n case \"tuple\":\n return (\n ir.items.some(canReportUnrecognizedKeys) ||\n (ir.rest !== null && canReportUnrecognizedKeys(ir.rest))\n );\n case \"record\":\n case \"set\":\n return canReportUnrecognizedKeys(ir.valueType);\n case \"map\":\n return canReportUnrecognizedKeys(ir.keyType) || canReportUnrecognizedKeys(ir.valueType);\n case \"catch\":\n case \"default\":\n case \"effect\":\n case \"nullable\":\n case \"optional\":\n case \"readonly\":\n case \"recursionTarget\":\n case \"zodDelegate\":\n return canReportUnrecognizedKeys(ir.inner);\n case \"discriminatedUnion\":\n case \"union\":\n return ir.options.some(canReportUnrecognizedKeys);\n case \"intersection\":\n return canReportUnrecognizedKeys(ir.left) || canReportUnrecognizedKeys(ir.right);\n case \"pipe\":\n return canReportUnrecognizedKeys(ir.in) || canReportUnrecognizedKeys(ir.out);\n // Zod runs the original sub-schema here, which may be (or contain) a strict\n // object that no IR node records.\n case \"fallback\":\n return true;\n // A back-edge whose target can sit OUTSIDE this side's IR — refId 0 points\n // at the compiled root, which may be an enclosing strict object. Unknowable\n // locally, so assume the worst.\n case \"recursiveRef\":\n return true;\n default:\n // Leaves and predicate nodes: primitives, literals, enums, templateLiteral,\n // stringBool, file, and custom (which emits `custom` issues only).\n return false;\n }\n}\n\n/** Merge only the object-intersection shape whose equivalence is statically obvious. */\nfunction mergeDisjointStripObjects(left: SchemaIR, right: SchemaIR): ObjectIR | null {\n if (left.type !== \"object\" || right.type !== \"object\") return null;\n if (left.stripUnknownKeys !== true || right.stripUnknownKeys !== true) return null;\n if (\n left.strict === true ||\n right.strict === true ||\n left.catchall !== undefined ||\n right.catchall !== undefined ||\n left.checks !== undefined ||\n right.checks !== undefined ||\n left.suppressAbsentKeys !== undefined ||\n right.suppressAbsentKeys !== undefined\n ) {\n return null;\n }\n\n const leftKeys = Object.keys(left.properties);\n if (leftKeys.some((key) => Object.hasOwn(right.properties, key))) return null;\n\n const properties: Record<string, SchemaIR> = Object.create(null) as Record<string, SchemaIR>;\n for (const key of leftKeys) properties[key] = left.properties[key] as SchemaIR;\n const rightKeys = Object.keys(right.properties);\n for (const key of rightKeys) {\n properties[key] = right.properties[key] as SchemaIR;\n }\n // Integer-like keys are enumerated before other strings regardless of\n // insertion order. If that changes left-then-right traversal, a successful\n // parse could observe transform/refine callbacks in a different order.\n const expectedOrder = [...leftKeys, ...rightKeys];\n if (!Object.keys(properties).every((key, index) => key === expectedOrder[index])) return null;\n return { type: \"object\", properties, stripUnknownKeys: true };\n}\n"],"mappings":";;AAIA,MAAa,uBAAkC,KAAK,QAAQ;CAC1D,MAAM,OAAO,IAAI,MAAM,IAAI,MAAM,gBAAgB;CACjD,MAAM,QAAQ,IAAI,MAAM,IAAI,OAAO,iBAAiB;CAYpD,IAAI,0BAA0B,IAAI,KAAK,0BAA0B,KAAK,GACpE,OAAO,IAAI,SAAS,aAAa;CASnC,MAAM,SAAS,0BAA0B,MAAM,KAAK;CACpD,IAAI,WAAW,QAAQ,IAAI,MAAM;EAC/B,MAAM,WAAW,IAAI,KAAK;EAC1B,IAAI,KAAK,KAAK;GAAE,QAAQ,IAAI;GAAQ,YAAY,IAAI;EAAK,CAAC;EAC1D,OAAO;GAAE,MAAM;GAAe,OAAO;GAAQ;EAAS;CACxD;CAMA,IAAI,YAAY,IAAI,KAAK,YAAY,KAAK,GACxC,OAAO,IAAI,SAAS,aAAa;CAEnC,OAAO;EAAE,MAAM;EAAgB;EAAM;CAAM;AAC7C;;;;;;;;;;;;;;;;;;;AAoBA,SAAS,0BAA0B,IAAuB;CACxD,QAAQ,GAAG,MAAX;EACE,KAAK,UACH,OACE,GAAG,WAAW,QACb,GAAG,aAAa,KAAA,KAAa,0BAA0B,GAAG,QAAQ,KACnE,OAAO,OAAO,GAAG,UAAU,CAAC,CAAC,MAAM,MAAM,0BAA0B,CAAC,CAAC;EAEzE,KAAK,SACH,OAAO,0BAA0B,GAAG,OAAO;EAC7C,KAAK,SACH,OACE,GAAG,MAAM,KAAK,yBAAyB,KACtC,GAAG,SAAS,QAAQ,0BAA0B,GAAG,IAAI;EAE1D,KAAK;EACL,KAAK,OACH,OAAO,0BAA0B,GAAG,SAAS;EAC/C,KAAK,OACH,OAAO,0BAA0B,GAAG,OAAO,KAAK,0BAA0B,GAAG,SAAS;EACxF,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,eACH,OAAO,0BAA0B,GAAG,KAAK;EAC3C,KAAK;EACL,KAAK,SACH,OAAO,GAAG,QAAQ,KAAK,yBAAyB;EAClD,KAAK,gBACH,OAAO,0BAA0B,GAAG,IAAI,KAAK,0BAA0B,GAAG,KAAK;EACjF,KAAK,QACH,OAAO,0BAA0B,GAAG,EAAE,KAAK,0BAA0B,GAAG,GAAG;EAG7E,KAAK,YACH,OAAO;EAIT,KAAK,gBACH,OAAO;EACT,SAGE,OAAO;CACX;AACF;;AAGA,SAAS,0BAA0B,MAAgB,OAAkC;CACnF,IAAI,KAAK,SAAS,YAAY,MAAM,SAAS,UAAU,OAAO;CAC9D,IAAI,KAAK,qBAAqB,QAAQ,MAAM,qBAAqB,MAAM,OAAO;CAC9E,IACE,KAAK,WAAW,QAChB,MAAM,WAAW,QACjB,KAAK,aAAa,KAAA,KAClB,MAAM,aAAa,KAAA,KACnB,KAAK,WAAW,KAAA,KAChB,MAAM,WAAW,KAAA,KACjB,KAAK,uBAAuB,KAAA,KAC5B,MAAM,uBAAuB,KAAA,GAE7B,OAAO;CAGT,MAAM,WAAW,OAAO,KAAK,KAAK,UAAU;CAC5C,IAAI,SAAS,MAAM,QAAQ,OAAO,OAAO,MAAM,YAAY,GAAG,CAAC,GAAG,OAAO;CAEzE,MAAM,aAAuC,OAAO,OAAO,IAAI;CAC/D,KAAK,MAAM,OAAO,UAAU,WAAW,OAAO,KAAK,WAAW;CAC9D,MAAM,YAAY,OAAO,KAAK,MAAM,UAAU;CAC9C,KAAK,MAAM,OAAO,WAChB,WAAW,OAAO,MAAM,WAAW;CAKrC,MAAM,gBAAgB,CAAC,GAAG,UAAU,GAAG,SAAS;CAChD,IAAI,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,OAAO,KAAK,UAAU,QAAQ,cAAc,MAAM,GAAG,OAAO;CACzF,OAAO;EAAE,MAAM;EAAU;EAAY,kBAAkB;CAAK;AAC9D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lazy.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/lazy.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"lazy.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/lazy.ts"],"mappings":";;;iBAIgB,YAAY,eAAe,KAAK,mBAAmB"}
|
|
@@ -1,20 +1,9 @@
|
|
|
1
|
+
import { makeRecursiveRef } from "../recursion.js";
|
|
1
2
|
//#region src/core/extract/extractors/lazy.ts
|
|
2
3
|
function extractLazy(_def, ctx) {
|
|
3
4
|
const innerSchema = ctx.schema._zod.innerType;
|
|
4
5
|
if (!innerSchema) return ctx.fallback("lazy");
|
|
5
|
-
if (ctx.visiting.has(innerSchema))
|
|
6
|
-
const root = ctx.recursion.root;
|
|
7
|
-
if ((root?._zod?.def?.type === "lazy" ? root._zod.innerType : root) === innerSchema) return { type: "recursiveRef" };
|
|
8
|
-
let refId = ctx.recursion.targets.get(innerSchema);
|
|
9
|
-
if (refId === void 0) {
|
|
10
|
-
refId = ctx.recursion.next++;
|
|
11
|
-
ctx.recursion.targets.set(innerSchema, refId);
|
|
12
|
-
}
|
|
13
|
-
return {
|
|
14
|
-
type: "recursiveRef",
|
|
15
|
-
refId
|
|
16
|
-
};
|
|
17
|
-
}
|
|
6
|
+
if (ctx.visiting.has(innerSchema)) return makeRecursiveRef(innerSchema, ctx.recursion);
|
|
18
7
|
return ctx.visit(innerSchema, "._zod.innerType");
|
|
19
8
|
}
|
|
20
9
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lazy.js","names":[],"sources":["../../../../src/core/extract/extractors/lazy.ts"],"sourcesContent":["import type { SchemaIR } from \"../../types.js\";\nimport type { ExtractorContext, ZodSchema } from \"../types.js\";\n\nexport function extractLazy(_def: unknown, ctx: ExtractorContext): SchemaIR {\n const schema = ctx.schema as ZodSchema;\n const innerSchema = schema._zod.innerType;\n if (!innerSchema) {\n return ctx.fallback(\"lazy\");\n }\n // Cycle detected: the resolved schema is already being
|
|
1
|
+
{"version":3,"file":"lazy.js","names":[],"sources":["../../../../src/core/extract/extractors/lazy.ts"],"sourcesContent":["import type { SchemaIR } from \"../../types.js\";\nimport { makeRecursiveRef } from \"../recursion.js\";\nimport type { ExtractorContext, ZodSchema } from \"../types.js\";\n\nexport function extractLazy(_def: unknown, ctx: ExtractorContext): SchemaIR {\n const schema = ctx.schema as ZodSchema;\n const innerSchema = schema._zod.innerType;\n if (!innerSchema) {\n return ctx.fallback(\"lazy\");\n }\n // Cycle detected one level early: the resolved schema is already being\n // extracted, so the lazy wrapper itself becomes the back-edge and dispatch is\n // never re-entered for the target. Keyed on the RESOLVED schema so this and\n // dispatch's own getter-cycle detector mint identical refIds.\n if (ctx.visiting.has(innerSchema)) {\n return makeRecursiveRef(innerSchema, ctx.recursion);\n }\n return ctx.visit(innerSchema, \"._zod.innerType\");\n}\n"],"mappings":";;AAIA,SAAgB,YAAY,MAAe,KAAiC;CAE1E,MAAM,cADS,IAAI,OACQ,KAAK;CAChC,IAAI,CAAC,aACH,OAAO,IAAI,SAAS,MAAM;CAM5B,IAAI,IAAI,SAAS,IAAI,WAAW,GAC9B,OAAO,iBAAiB,aAAa,IAAI,SAAS;CAEpD,OAAO,IAAI,MAAM,aAAa,iBAAiB;AACjD"}
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { Extractor } from "../types.js";
|
|
2
2
|
//#region src/core/extract/extractors/literal.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* z.literal(). Values with a JS source form compile to an inline `===` chain;
|
|
5
|
+
* anything else — a symbol, an object — has none, so the node retains the
|
|
6
|
+
* ORIGINAL schema in `__rf[]` and codegen tests membership against its own
|
|
7
|
+
* `_zod.def.values` at runtime. `Array.prototype.includes` is SameValueZero,
|
|
8
|
+
* exactly the comparison zod's `new Set(def.values).has(input)` performs, and
|
|
9
|
+
* reporting that same array as the issue's `values` is exactly what zod reports.
|
|
10
|
+
*
|
|
11
|
+
* Zod's public argument type (`util.Literal`) admits none of these, but its
|
|
12
|
+
* runtime accepts them and its own parse ACCEPTS the matching value — so
|
|
13
|
+
* `z.literal(someSymbol)` is a schema that really works, and previously
|
|
14
|
+
* compiled to `x===undefined` (see `hasSourceForm`): it rejected the very symbol
|
|
15
|
+
* it was built from and accepted `undefined`.
|
|
16
|
+
*/
|
|
3
17
|
declare const extractLiteral: Extractor;
|
|
4
18
|
//#endregion
|
|
5
19
|
export { extractLiteral };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"literal.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/literal.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"literal.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/literal.ts"],"mappings":";;;;;;;;;;;;;;;;cAiBa,gBAAgB"}
|
|
@@ -1,8 +1,36 @@
|
|
|
1
|
+
import { hasSourceForm } from "../../codegen/context.js";
|
|
1
2
|
//#region src/core/extract/extractors/literal.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/**
|
|
4
|
+
* z.literal(). Values with a JS source form compile to an inline `===` chain;
|
|
5
|
+
* anything else — a symbol, an object — has none, so the node retains the
|
|
6
|
+
* ORIGINAL schema in `__rf[]` and codegen tests membership against its own
|
|
7
|
+
* `_zod.def.values` at runtime. `Array.prototype.includes` is SameValueZero,
|
|
8
|
+
* exactly the comparison zod's `new Set(def.values).has(input)` performs, and
|
|
9
|
+
* reporting that same array as the issue's `values` is exactly what zod reports.
|
|
10
|
+
*
|
|
11
|
+
* Zod's public argument type (`util.Literal`) admits none of these, but its
|
|
12
|
+
* runtime accepts them and its own parse ACCEPTS the matching value — so
|
|
13
|
+
* `z.literal(someSymbol)` is a schema that really works, and previously
|
|
14
|
+
* compiled to `x===undefined` (see `hasSourceForm`): it rejected the very symbol
|
|
15
|
+
* it was built from and accepted `undefined`.
|
|
16
|
+
*/
|
|
17
|
+
const extractLiteral = (def, ctx) => {
|
|
18
|
+
if (def.values.every(hasSourceForm)) return {
|
|
19
|
+
type: "literal",
|
|
20
|
+
values: def.values
|
|
21
|
+
};
|
|
22
|
+
if (!ctx.refs) return ctx.fallback("unsupported");
|
|
23
|
+
const refIndex = ctx.refs.length;
|
|
24
|
+
ctx.refs.push({
|
|
25
|
+
schema: ctx.schema,
|
|
26
|
+
accessPath: ctx.path
|
|
27
|
+
});
|
|
28
|
+
return {
|
|
29
|
+
type: "literal",
|
|
30
|
+
values: def.values,
|
|
31
|
+
refIndex
|
|
32
|
+
};
|
|
33
|
+
};
|
|
6
34
|
//#endregion
|
|
7
35
|
export { extractLiteral };
|
|
8
36
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"literal.js","names":[],"sources":["../../../../src/core/extract/extractors/literal.ts"],"sourcesContent":["import type { Extractor } from \"../types.js\";\n\nexport const extractLiteral: Extractor = (def) => ({ type: \"literal\", values: def.values });\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"literal.js","names":[],"sources":["../../../../src/core/extract/extractors/literal.ts"],"sourcesContent":["import { hasSourceForm } from \"../../codegen/context.js\";\nimport type { Extractor } from \"../types.js\";\n\n/**\n * z.literal(). Values with a JS source form compile to an inline `===` chain;\n * anything else — a symbol, an object — has none, so the node retains the\n * ORIGINAL schema in `__rf[]` and codegen tests membership against its own\n * `_zod.def.values` at runtime. `Array.prototype.includes` is SameValueZero,\n * exactly the comparison zod's `new Set(def.values).has(input)` performs, and\n * reporting that same array as the issue's `values` is exactly what zod reports.\n *\n * Zod's public argument type (`util.Literal`) admits none of these, but its\n * runtime accepts them and its own parse ACCEPTS the matching value — so\n * `z.literal(someSymbol)` is a schema that really works, and previously\n * compiled to `x===undefined` (see `hasSourceForm`): it rejected the very symbol\n * it was built from and accepted `undefined`.\n */\nexport const extractLiteral: Extractor = (def, ctx) => {\n if (def.values.every(hasSourceForm)) {\n return { type: \"literal\", values: def.values };\n }\n // Same constraint as default/catch: without a ref table there is nowhere to\n // reach the value list from, so the schema has to stay with zod.\n if (!ctx.refs) return ctx.fallback(\"unsupported\");\n const refIndex = ctx.refs.length;\n ctx.refs.push({ schema: ctx.schema, accessPath: ctx.path });\n return { type: \"literal\", values: def.values, refIndex };\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAiBA,MAAa,kBAA6B,KAAK,QAAQ;CACrD,IAAI,IAAI,OAAO,MAAM,aAAa,GAChC,OAAO;EAAE,MAAM;EAAW,QAAQ,IAAI;CAAO;CAI/C,IAAI,CAAC,IAAI,MAAM,OAAO,IAAI,SAAS,aAAa;CAChD,MAAM,WAAW,IAAI,KAAK;CAC1B,IAAI,KAAK,KAAK;EAAE,QAAQ,IAAI;EAAQ,YAAY,IAAI;CAAK,CAAC;CAC1D,OAAO;EAAE,MAAM;EAAW,QAAQ,IAAI;EAAQ;CAAS;AACzD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"number.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/number.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"number.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/number.ts"],"mappings":";;;iBAoBgB,cAAc,KAAK,QAAQ,KAAK,mBAAmB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { extractChecks, payloadCheckRef, refineRefRegistrar, resolveCheckMessage } from "../checks.js";
|
|
1
|
+
import { customParamsRefRegistrar, extractChecks, payloadCheckRef, refineRefRegistrar, resolveCheckMessage } from "../checks.js";
|
|
2
2
|
//#region src/core/extract/extractors/number.ts
|
|
3
3
|
/** Check kinds the number codegen knows how to emit. Anything else → fallback. */
|
|
4
4
|
const NUMBER_CHECK_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -22,7 +22,7 @@ function extractNumber(def, ctx) {
|
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
if (def.checks && def.checks.length > 0) {
|
|
25
|
-
const { checkIRs, hasFallback } = extractChecks(def.checks, refineRefRegistrar(ctx, def.checks), (index) => payloadCheckRef(ctx, def.checks, index));
|
|
25
|
+
const { checkIRs, hasFallback } = extractChecks(def.checks, refineRefRegistrar(ctx, def.checks), (index) => payloadCheckRef(ctx, def.checks, index), customParamsRefRegistrar(ctx, def.checks));
|
|
26
26
|
if (hasFallback) return ctx.fallback("refine");
|
|
27
27
|
allChecks.push(...checkIRs);
|
|
28
28
|
}
|