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.
Files changed (94) hide show
  1. package/dist/core/codegen/build-path.d.ts.map +1 -1
  2. package/dist/core/codegen/build-path.js +57 -6
  3. package/dist/core/codegen/build-path.js.map +1 -1
  4. package/dist/core/codegen/context.d.ts +23 -3
  5. package/dist/core/codegen/context.d.ts.map +1 -1
  6. package/dist/core/codegen/context.js +23 -1
  7. package/dist/core/codegen/context.js.map +1 -1
  8. package/dist/core/codegen/dedupe.js +4 -1
  9. package/dist/core/codegen/dedupe.js.map +1 -1
  10. package/dist/core/codegen/emit-issue.d.ts +27 -2
  11. package/dist/core/codegen/emit-issue.d.ts.map +1 -1
  12. package/dist/core/codegen/emit-issue.js +8 -3
  13. package/dist/core/codegen/emit-issue.js.map +1 -1
  14. package/dist/core/codegen/issue-decls.d.ts +9 -3
  15. package/dist/core/codegen/issue-decls.d.ts.map +1 -1
  16. package/dist/core/codegen/issue-decls.js +10 -4
  17. package/dist/core/codegen/issue-decls.js.map +1 -1
  18. package/dist/core/codegen/schemas/catch.d.ts.map +1 -1
  19. package/dist/core/codegen/schemas/catch.js +1 -1
  20. package/dist/core/codegen/schemas/catch.js.map +1 -1
  21. package/dist/core/codegen/schemas/discriminated-union.d.ts.map +1 -1
  22. package/dist/core/codegen/schemas/discriminated-union.js +11 -3
  23. package/dist/core/codegen/schemas/discriminated-union.js.map +1 -1
  24. package/dist/core/codegen/schemas/effect.d.ts.map +1 -1
  25. package/dist/core/codegen/schemas/effect.js +3 -2
  26. package/dist/core/codegen/schemas/effect.js.map +1 -1
  27. package/dist/core/codegen/schemas/enum.d.ts.map +1 -1
  28. package/dist/core/codegen/schemas/enum.js +25 -0
  29. package/dist/core/codegen/schemas/enum.js.map +1 -1
  30. package/dist/core/codegen/schemas/literal.d.ts.map +1 -1
  31. package/dist/core/codegen/schemas/literal.js +37 -3
  32. package/dist/core/codegen/schemas/literal.js.map +1 -1
  33. package/dist/core/codegen/schemas/string-bool.d.ts.map +1 -1
  34. package/dist/core/codegen/schemas/string-bool.js +3 -2
  35. package/dist/core/codegen/schemas/string-bool.js.map +1 -1
  36. package/dist/core/codegen/schemas/string.d.ts.map +1 -1
  37. package/dist/core/codegen/schemas/string.js +13 -7
  38. package/dist/core/codegen/schemas/string.js.map +1 -1
  39. package/dist/core/codegen/schemas/template-literal.d.ts.map +1 -1
  40. package/dist/core/codegen/schemas/template-literal.js +2 -2
  41. package/dist/core/codegen/schemas/template-literal.js.map +1 -1
  42. package/dist/core/codegen/schemas/tuple.d.ts +11 -5
  43. package/dist/core/codegen/schemas/tuple.d.ts.map +1 -1
  44. package/dist/core/codegen/schemas/tuple.js +12 -6
  45. package/dist/core/codegen/schemas/tuple.js.map +1 -1
  46. package/dist/core/codegen/schemas/union.d.ts.map +1 -1
  47. package/dist/core/codegen/schemas/union.js +17 -17
  48. package/dist/core/codegen/schemas/union.js.map +1 -1
  49. package/dist/core/extract/checks.d.ts +18 -2
  50. package/dist/core/extract/checks.d.ts.map +1 -1
  51. package/dist/core/extract/checks.js +138 -11
  52. package/dist/core/extract/checks.js.map +1 -1
  53. package/dist/core/extract/extractors/array.d.ts.map +1 -1
  54. package/dist/core/extract/extractors/array.js +2 -2
  55. package/dist/core/extract/extractors/array.js.map +1 -1
  56. package/dist/core/extract/extractors/intersection.js +44 -0
  57. package/dist/core/extract/extractors/intersection.js.map +1 -1
  58. package/dist/core/extract/extractors/lazy.d.ts.map +1 -1
  59. package/dist/core/extract/extractors/lazy.js +2 -13
  60. package/dist/core/extract/extractors/lazy.js.map +1 -1
  61. package/dist/core/extract/extractors/literal.d.ts +14 -0
  62. package/dist/core/extract/extractors/literal.d.ts.map +1 -1
  63. package/dist/core/extract/extractors/literal.js +32 -4
  64. package/dist/core/extract/extractors/literal.js.map +1 -1
  65. package/dist/core/extract/extractors/number.d.ts.map +1 -1
  66. package/dist/core/extract/extractors/number.js +2 -2
  67. package/dist/core/extract/extractors/number.js.map +1 -1
  68. package/dist/core/extract/extractors/object.d.ts.map +1 -1
  69. package/dist/core/extract/extractors/object.js +2 -2
  70. package/dist/core/extract/extractors/object.js.map +1 -1
  71. package/dist/core/extract/extractors/record.d.ts.map +1 -1
  72. package/dist/core/extract/extractors/record.js +2 -0
  73. package/dist/core/extract/extractors/record.js.map +1 -1
  74. package/dist/core/extract/extractors/string.d.ts.map +1 -1
  75. package/dist/core/extract/extractors/string.js +2 -2
  76. package/dist/core/extract/extractors/string.js.map +1 -1
  77. package/dist/core/extract/extractors/union.js +2 -0
  78. package/dist/core/extract/extractors/union.js.map +1 -1
  79. package/dist/core/extract/recursion.d.ts +26 -0
  80. package/dist/core/extract/recursion.d.ts.map +1 -0
  81. package/dist/core/extract/recursion.js +37 -0
  82. package/dist/core/extract/recursion.js.map +1 -0
  83. package/dist/core/extract/registry.d.ts.map +1 -1
  84. package/dist/core/extract/registry.js +2 -0
  85. package/dist/core/extract/registry.js.map +1 -1
  86. package/dist/core/extract/types.d.ts +21 -6
  87. package/dist/core/extract/types.d.ts.map +1 -1
  88. package/dist/core/iife.d.ts +13 -0
  89. package/dist/core/iife.d.ts.map +1 -1
  90. package/dist/core/iife.js +14 -1
  91. package/dist/core/iife.js.map +1 -1
  92. package/dist/core/types.d.ts +49 -2
  93. package/dist/core/types.d.ts.map +1 -1
  94. 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
- * String formats the codegen can validate without a regex pattern.
46
- * Everything else without a pattern (e.g. z.jwt(), which validates
47
- * algorithmically) must fall back to Zod instead of being silently skipped.
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
- * Formats whose def.pattern is NOT the authoritative validator — zod's
56
- * runtime check is algorithmic and accepts inputs the pattern rejects
57
- * (e.g. z.cidrv6() splits the prefix and validates the IPv6 part separately).
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
- const NON_AUTHORITATIVE_PATTERN_FORMATS = /* @__PURE__ */ new Set(["cidrv6"]);
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":";;;;;;;;;AAWA,SAAgB,mBACd,KACA,QACqD;CACrD,MAAM,OAAO,IAAI;CACjB,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,QAAQ,UAAU;EAChB,MAAM,KAAK,OAAO,MAAM,EAAE,MAAM,KAAK;EACrC,IAAI,CAAC,yBAAyB,EAAE,GAAG,OAAO,KAAA;EAC1C,KAAK,KAAK;GAAE,QAAQ;GAAI,YAAY,GAAG,IAAI,KAAK,mBAAmB,MAAM;EAAe,CAAC;EACzF,OAAO,KAAK,SAAS;CACvB;AACF;;;;;;;;;;;;;;AAeA,SAAgB,yBACd,KACA,QACqD;CACrD,MAAM,OAAO,IAAI;CACjB,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,QAAQ,UAAU;EAChB,KAAK,KAAK;GACR,QAAQ,OAAO,MAAM,EAAE,MAAM,KAAK;GAClC,YAAY,GAAG,IAAI,KAAK,mBAAmB,MAAM;EACnD,CAAC;EACD,OAAO,KAAK,SAAS;CACvB;AACF;;;;;;;;;;;AAYA,SAAgB,gBACd,KACA,QACA,OACoB;CACpB,IAAI,UAAU,OAAO,SAAS,GAAG,OAAO,KAAA;CACxC,MAAM,QAAQ,OAAO;CACrB,MAAM,OAAO,IAAI;CACjB,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,eAAe,KAAK,GAAG,OAAO,KAAA;CACtD,KAAK,KAAK;EACR,QAAS,MAAM,KAA4B;EAC3C,YAAY,GAAG,IAAI,KAAK,mBAAmB,MAAM;CACnD,CAAC;CACD,OAAO,KAAK,SAAS;AACvB;;;;;;;;;;;;AAaA,MAAM,sCAAsB,IAAI,IAAI;CAAC;CAAS;CAAQ;AAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+B5D,MAAM,oDAAoC,IAAI,IAAI;CAAC;CAAU;CAAa;CAAU;AAAM,CAAC;;;;;;;;;;;;;;;;;;;AAoB3F,SAAS,qBAAqB,KAA2B;CACvD,OAAO,OAAO,IAAI,OAAO;AAC3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAS,uBAAuB,KAA2B;CACzD,OACE,qBAAqB,GAAG,KAAK,IAAI,mBAAmB,UAAU,OAAO,KAAK,IAAI,QAAQ,KAAK;AAE/F;;;;;;;;;;AAWA,MAAM,4CAA4B,IAAI,IAAI;CACxC;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;AAGD,SAAS,YAAY,KAA2B;CAC9C,OAAO,QAAQ,IAAI,IAAI,KAAK,CAAC,0BAA0B,IAAI,IAAI,KAAK;AACtE;;;;;;;AAQA,SAAgB,yBAAyB,KAA2B;CAClE,OAAO,YAAY,GAAG,KAAK,IAAI,UAAU;AAC3C;AAEA,SAAgB,cACd,QAQA,WAEA,eAEA,iBAIA;CACA,MAAM,WAA8B,CAAC;CACrC,IAAI,cAAc;CAElB,KAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,GAAG;EAC7C,MAAM,MAAM,MAAM,MAAM;EACxB,IAAI,CAAC,KAAK;EAIV,IAAI,yBAAyB,GAAG,GAAG;GACjC,cAAc;GACd;EACF;EAKA,MAAM,WAAW,oBAAoB,IAAI,KAAK;EAC9C,IAAI,SAAS,SAAS,WAAW;GAC/B,cAAc;GACd;EACF;EACA,MAAM,UAAU,SAAS,SAAS,WAAW,EAAE,SAAS,SAAS,QAAQ,IAAI,CAAC;EAE9E,QAAQ,IAAI,OAAZ;GACE,KAAK;IACH,SAAS,KAAK;KAAE,MAAM;KAAc,SAAS,IAAI;KAAS,GAAG;IAAQ,CAAC;IACtE;GACF,KAAK;IACH,SAAS,KAAK;KAAE,MAAM;KAAc,SAAS,IAAI;KAAS,GAAG;IAAQ,CAAC;IACtE;GACF,KAAK;IACH,SAAS,KAAK;KAAE,MAAM;KAAiB,QAAQ,IAAI;KAAQ,GAAG;IAAQ,CAAC;IACvE;GACF,KAAK;IACH,SAAS,KAAK;KACZ,MAAM;KACN,OAAO,IAAI;KACX,WAAW,IAAI;KACf,GAAG;IACL,CAAC;IACD;GACF,KAAK;IACH,SAAS,KAAK;KACZ,MAAM;KACN,OAAO,IAAI;KACX,WAAW,IAAI;KACf,GAAG;IACL,CAAC;IACD;GACF,KAAK;IACH,SAAS,KAAK;KAAE,MAAM;KAAe,OAAO,IAAI;KAAO,GAAG;IAAQ,CAAC;IACnE;GACF,KAAK;IACH,SAAS,KAAK;KACZ,MAAM;KACN,QAAQ,IAAI;KACZ,GAAG;IACL,CAAC;IACD;GACF,KAAK,iBAAiB;IAMpB,MAAM,WAAW,qBAAqB,GAAG;IAIzC,IAAI,uBAAuB,GAAG,GAAG;KAC/B,cAAc;KACd;IACF;IACA,IAAI,IAAI,WAAW,cAAc,OAAO,IAAI,aAAa,UAAU;KACjE,SAAS,KAAK;MACZ,MAAM;MACN,UAAU,IAAI;MACd,GAAI,OAAO,IAAI,aAAa,WAAW,EAAE,UAAU,IAAI,SAAS,IAAI,CAAC;MACrE,GAAG;KACL,CAAC;KACD;IACF;IACA,IAAI,IAAI,WAAW,iBAAiB,OAAO,IAAI,WAAW,UAAU;KAClE,SAAS,KAAK;MAAE,MAAM;MAAe,QAAQ,IAAI;MAAQ,GAAG;KAAQ,CAAC;KACrE;IACF;IACA,IAAI,IAAI,WAAW,eAAe,OAAO,IAAI,WAAW,UAAU;KAChE,SAAS,KAAK;MAAE,MAAM;MAAa,QAAQ,IAAI;MAAQ,GAAG;KAAQ,CAAC;KACnE;IACF;IAMA,IAAI,IAAI,WAAW,SAAS,CAAC,UAAU;KACrC,SAAS,KAAK;MACZ,MAAM;MACN,QAAQ;MACR,GAAG,YAAY,YAAY,iBAAiB,IAAI,QAAQ;MACxD,GAAG,YAAY,YAAY,iBAAiB,IAAI,QAAQ;MACxD,GAAI,IAAI,YAAY,EAAE,WAAW,KAAK,IAAI,CAAC;MAC3C,GAAG;KACL,CAAC;KACD;IACF;IAIA,IAAI,kCAAkC,IAAI,IAAI,MAAM,GAAG;KACrD,cAAc;KACd;IACF;IACA,MAAM,UAAU,IAAI,mBAAmB,SAAS,IAAI,QAAQ,SAAS,IAAI;IACzE,MAAM,QAAQ,IAAI,mBAAmB,UAAU,IAAI,QAAQ,QAAQ,IAAI,QAAQ,QAAQ;IACvF,IAAI,CAAC,WAAW,EAAE,CAAC,YAAY,oBAAoB,IAAI,IAAI,MAAM,IAAI;KAOnE,cAAc;KACd;IACF;IACA,SAAS,KAAK;KACZ,MAAM;KACN,QAAQ,IAAI;KACZ,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;KAC7B,GAAI,QAAQ,EAAE,cAAc,MAAM,IAAI,CAAC;KACvC,GAAI,WAAW,EAAE,WAAW,KAAK,IAAI,CAAC;KACtC,GAAG;IACL,CAAC;IACD;GACF;GACA,KAAK,aAAa;IAEhB,MAAM,SAAS,iBAAiB,IAAI,EAAE;IACtC,IAAI,QACF,SAAS,KAAK;KAAE,MAAM;KAAoB;IAAO,CAAC;SAElD,cAAc;IAEhB;GACF;GACA,KAAK,UAAU;IAIb,IAAI,eAAe,KAAK,GAAG;KACzB,MAAM,aAAa,gBAAgB,KAAK;KACxC,IAAI,eAAe,KAAA,GAAW;MAC5B,cAAc;MACd;KACF;KACA,SAAS,KAAK;MAAE,MAAM;MAAuB,UAAU;KAAW,CAAC;KACnE;IACF;IAIA,MAAM,aAAa,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,OAAO,KAAA;IACxD,IAAI,YAAY,MAAM,MAAM,OAAO,MAAM,YAAY,OAAO,MAAM,QAAQ,GAAG;KAC3E,cAAc;KACd;IACF;IACA,MAAM,SAAS,cAAc,WAAW,SAAS,IAAI,EAAE,MAAM,WAAW,IAAI,CAAC;IAK7E,IAAI,WAAwC,CAAC;IAC7C,IAAI,IAAI,WAAW,KAAA,GAAW;KAC5B,MAAM,iBAAiB,kBAAkB,KAAK;KAC9C,IAAI,mBAAmB,KAAA,GAAW;MAChC,cAAc;MACd;KACF;KACA,WAAW,EAAE,eAAe;IAC9B;IACA,MAAM,SAAS,iBAAiB,IAAI,EAAE;IACtC,IAAI,QAAQ;KACV,SAAS,KAAK;MAAE,MAAM;MAAiB;MAAQ,GAAG;MAAQ,GAAG;MAAU,GAAG;KAAQ,CAAC;KACnF;IACF;IAKA,MAAM,WAAW,YAAY,KAAK;IAClC,IAAI,aAAa,KAAA,GACf,SAAS,KAAK;KAAE,MAAM;KAAiB;KAAU,GAAG;KAAQ,GAAG;KAAU,GAAG;IAAQ,CAAC;SAErF,cAAc;IAEhB;GACF;GACA;IAEE,cAAc;IACd;EACJ;CACF;CAEA,OAAO;EAAE;EAAU;CAAY;AACjC;AAEA,SAAS,YACP,WACA,UACA,OAC4B;CAC5B,IAAI,EAAE,iBAAiB,SAAS,OAAO,CAAC;CACxC,OAAO;GAAG,YAAY,MAAM;EAAQ,GAAI,MAAM,QAAQ,GAAG,WAAW,MAAM,MAAM,IAAI,CAAC;CAAG;AAC1F;;;;;;;;;;;AAmBA,SAAgB,oBAAoB,OAAiC;CACnE,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW,OAAO,EAAE,MAAM,OAAO;CACjE,IAAI,OAAO,UAAU,UAAU,OAAO;EAAE,MAAM;EAAU,SAAS;CAAM;CACvE,IAAI,OAAO,UAAU,YAAY,OAAO,EAAE,MAAM,UAAU;CAE1D,IAAI,WAAW;CACf,MAAM,cAAc;EAClB,WAAW;CACb;CACA,MAAM,QAAQ,IAAI,MAChB,CAAC,GACD;EACE,IAAI,IAAI,MAAM;GACZ,MAAM;GAEN,IAAI,SAAS,OAAO,eAAe,SAAS,cAAc,SAAS,WACjE,aAAa;EAGjB;EACA,MAAM;GACJ,MAAM;GACN,OAAO;EACT;EACA,UAAU;GACR,MAAM;GACN,OAAO,CAAC;EACV;EACA,2BAA2B;GACzB,MAAM;EAER;CACF,CACF;CAEA,IAAI;CACJ,IAAI;EACF,SAAU,MAAsC,KAAK;CACvD,QAAQ;EACN,OAAO,EAAE,MAAM,UAAU;CAC3B;CACA,IAAI,UAAU,OAAO,EAAE,MAAM,UAAU;CACvC,IAAI,OAAO,WAAW,UAAU,OAAO;EAAE,MAAM;EAAU,SAAS;CAAO;CACzE,IACE,WAAW,QACX,OAAO,WAAW,YAClB,OAAQ,OAAiC,YAAY,UAErD,OAAO;EAAE,MAAM;EAAU,SAAU,OAA+B;CAAQ;CAG5E,IAAI,WAAW,KAAA,GAAW,OAAO,EAAE,MAAM,OAAO;CAChD,OAAO,EAAE,MAAM,UAAU;AAC3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"array.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/array.ts"],"mappings":";;;iBAagB,aAAa,KAAK,QAAQ,KAAK,mBAAmB"}
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 { extractChecks, payloadCheckRef, refineRefRegistrar } 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 );\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":";;;AAKA,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,CAC3C;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"}
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;CAQpD,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;;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
+ {"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":";;;iBAGgB,YAAY,eAAe,KAAK,mBAAmB"}
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 extracted.\n if (ctx.visiting.has(innerSchema)) {\n // A `recursiveRef` re-invokes the validator hosting its target's shape.\n // The ROOT schema's resolution is hosted by the schema's own\n // `safeParse_<name>` / fast-check, so a cycle back to it emits the implicit\n // refId 0 (the directly self-recursive case — byte-identical to before).\n const root = ctx.recursion.root as ZodSchema | undefined;\n const rootResolved = root?._zod?.def?.type === \"lazy\" ? root._zod.innerType : root;\n if (rootResolved === innerSchema) {\n return { type: \"recursiveRef\" };\n }\n // A cycle back to a NON-root sub-schema (recursive schema nested in a\n // wrapper, multiple distinct recursive sub-schemas, or mutual recursion):\n // assign the target a stable refId (≥ 1) keyed by schema identity. dispatch\n // wraps that schema's IR in a `recursionTarget` node so codegen hosts it as\n // a standalone validator the ref can call.\n let refId = ctx.recursion.targets.get(innerSchema);\n if (refId === undefined) {\n refId = ctx.recursion.next++;\n ctx.recursion.targets.set(innerSchema, refId);\n }\n return { type: \"recursiveRef\", refId };\n }\n return ctx.visit(innerSchema, \"._zod.innerType\");\n}\n"],"mappings":";AAGA,SAAgB,YAAY,MAAe,KAAiC;CAE1E,MAAM,cADS,IAAI,OACQ,KAAK;CAChC,IAAI,CAAC,aACH,OAAO,IAAI,SAAS,MAAM;CAG5B,IAAI,IAAI,SAAS,IAAI,WAAW,GAAG;EAKjC,MAAM,OAAO,IAAI,UAAU;EAE3B,KADqB,MAAM,MAAM,KAAK,SAAS,SAAS,KAAK,KAAK,YAAY,UACzD,aACnB,OAAO,EAAE,MAAM,eAAe;EAOhC,IAAI,QAAQ,IAAI,UAAU,QAAQ,IAAI,WAAW;EACjD,IAAI,UAAU,KAAA,GAAW;GACvB,QAAQ,IAAI,UAAU;GACtB,IAAI,UAAU,QAAQ,IAAI,aAAa,KAAK;EAC9C;EACA,OAAO;GAAE,MAAM;GAAgB;EAAM;CACvC;CACA,OAAO,IAAI,MAAM,aAAa,iBAAiB;AACjD"}
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":";;cAEa,gBAAgB"}
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
- const extractLiteral = (def) => ({
3
- type: "literal",
4
- values: def.values
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":";AAEA,MAAa,kBAA6B,SAAS;CAAE,MAAM;CAAW,QAAQ,IAAI;AAAO"}
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":";;;iBAmBgB,cAAc,KAAK,QAAQ,KAAK,mBAAmB"}
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
  }