zod-compiler 1.28.0 → 2.0.0

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 (92) hide show
  1. package/README.md +59 -53
  2. package/dist/core/codegen/build-path.d.ts +12 -1
  3. package/dist/core/codegen/build-path.d.ts.map +1 -1
  4. package/dist/core/codegen/build-path.js +47 -9
  5. package/dist/core/codegen/build-path.js.map +1 -1
  6. package/dist/core/codegen/context.d.ts +31 -16
  7. package/dist/core/codegen/context.d.ts.map +1 -1
  8. package/dist/core/codegen/context.js +43 -18
  9. package/dist/core/codegen/context.js.map +1 -1
  10. package/dist/core/codegen/emit-issue.d.ts +17 -20
  11. package/dist/core/codegen/emit-issue.d.ts.map +1 -1
  12. package/dist/core/codegen/emit-issue.js +13 -13
  13. package/dist/core/codegen/emit-issue.js.map +1 -1
  14. package/dist/core/codegen/index.d.ts.map +1 -1
  15. package/dist/core/codegen/index.js +9 -7
  16. package/dist/core/codegen/index.js.map +1 -1
  17. package/dist/core/codegen/issue-decls.d.ts +41 -7
  18. package/dist/core/codegen/issue-decls.d.ts.map +1 -1
  19. package/dist/core/codegen/issue-decls.js +49 -9
  20. package/dist/core/codegen/issue-decls.js.map +1 -1
  21. package/dist/core/codegen/schemas/array.js +1 -1
  22. package/dist/core/codegen/schemas/array.js.map +1 -1
  23. package/dist/core/codegen/schemas/bigint.d.ts.map +1 -1
  24. package/dist/core/codegen/schemas/bigint.js +15 -4
  25. package/dist/core/codegen/schemas/bigint.js.map +1 -1
  26. package/dist/core/codegen/schemas/catch.d.ts.map +1 -1
  27. package/dist/core/codegen/schemas/catch.js +3 -2
  28. package/dist/core/codegen/schemas/catch.js.map +1 -1
  29. package/dist/core/codegen/schemas/discriminated-union.d.ts.map +1 -1
  30. package/dist/core/codegen/schemas/discriminated-union.js +2 -1
  31. package/dist/core/codegen/schemas/discriminated-union.js.map +1 -1
  32. package/dist/core/codegen/schemas/effect.d.ts.map +1 -1
  33. package/dist/core/codegen/schemas/effect.js +4 -2
  34. package/dist/core/codegen/schemas/effect.js.map +1 -1
  35. package/dist/core/codegen/schemas/file.js +2 -8
  36. package/dist/core/codegen/schemas/file.js.map +1 -1
  37. package/dist/core/codegen/schemas/number.js +4 -3
  38. package/dist/core/codegen/schemas/number.js.map +1 -1
  39. package/dist/core/codegen/schemas/object.d.ts +11 -1
  40. package/dist/core/codegen/schemas/object.d.ts.map +1 -1
  41. package/dist/core/codegen/schemas/object.js +58 -14
  42. package/dist/core/codegen/schemas/object.js.map +1 -1
  43. package/dist/core/codegen/schemas/readonly.d.ts.map +1 -1
  44. package/dist/core/codegen/schemas/readonly.js.map +1 -1
  45. package/dist/core/codegen/schemas/record.d.ts.map +1 -1
  46. package/dist/core/codegen/schemas/record.js +8 -3
  47. package/dist/core/codegen/schemas/record.js.map +1 -1
  48. package/dist/core/codegen/schemas/sizeable.d.ts +49 -3
  49. package/dist/core/codegen/schemas/sizeable.d.ts.map +1 -1
  50. package/dist/core/codegen/schemas/sizeable.js +101 -8
  51. package/dist/core/codegen/schemas/sizeable.js.map +1 -1
  52. package/dist/core/codegen/schemas/string.d.ts.map +1 -1
  53. package/dist/core/codegen/schemas/string.js +45 -16
  54. package/dist/core/codegen/schemas/string.js.map +1 -1
  55. package/dist/core/codegen/schemas/tuple.d.ts +14 -11
  56. package/dist/core/codegen/schemas/tuple.d.ts.map +1 -1
  57. package/dist/core/codegen/schemas/tuple.js +99 -49
  58. package/dist/core/codegen/schemas/tuple.js.map +1 -1
  59. package/dist/core/codegen/well-known-regex.d.ts +7 -0
  60. package/dist/core/codegen/well-known-regex.d.ts.map +1 -1
  61. package/dist/core/codegen/well-known-regex.js +10 -3
  62. package/dist/core/codegen/well-known-regex.js.map +1 -1
  63. package/dist/core/extract/checks.d.ts.map +1 -1
  64. package/dist/core/extract/checks.js +1 -0
  65. package/dist/core/extract/checks.js.map +1 -1
  66. package/dist/core/extract/extractors/intersection.js +21 -6
  67. package/dist/core/extract/extractors/intersection.js.map +1 -1
  68. package/dist/core/extract/extractors/object.d.ts.map +1 -1
  69. package/dist/core/extract/extractors/object.js +25 -9
  70. package/dist/core/extract/extractors/object.js.map +1 -1
  71. package/dist/core/extract/extractors/optional.d.ts.map +1 -1
  72. package/dist/core/extract/extractors/optional.js +1 -28
  73. package/dist/core/extract/extractors/optional.js.map +1 -1
  74. package/dist/core/extract/extractors/record.js +9 -3
  75. package/dist/core/extract/extractors/record.js.map +1 -1
  76. package/dist/core/extract/extractors/tuple.d.ts.map +1 -1
  77. package/dist/core/extract/extractors/tuple.js +16 -20
  78. package/dist/core/extract/extractors/tuple.js.map +1 -1
  79. package/dist/core/extract/types.d.ts +16 -8
  80. package/dist/core/extract/types.d.ts.map +1 -1
  81. package/dist/core/iife.d.ts +2 -1
  82. package/dist/core/iife.d.ts.map +1 -1
  83. package/dist/core/iife.js +2 -1
  84. package/dist/core/iife.js.map +1 -1
  85. package/dist/core/types.d.ts +70 -21
  86. package/dist/core/types.d.ts.map +1 -1
  87. package/dist/jit.d.ts.map +1 -1
  88. package/dist/jit.js +47 -12
  89. package/dist/jit.js.map +1 -1
  90. package/dist/runtime.d.ts +3 -1
  91. package/dist/runtime.js +12 -10
  92. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","names":[],"sources":["../../../src/core/codegen/context.ts"],"sourcesContent":["import type {\n BigIntCheckIR,\n CheckIR,\n DateCheckIR,\n LiteralValue,\n SchemaIR,\n SetCheckIR,\n} from \"../types.js\";\nimport type { SharedSchemaPlan } from \"./dedupe.js\";\nimport {\n fastTestSource,\n lookupWellKnownRegex,\n wellKnownRegexSourceName,\n} from \"./well-known-regex.js\";\n\n/** Codegen output mode. \"inline\" emits self-contained code (CLI .compiled.ts). \"lean\" emits references to imports from \"virtual:zod-compiler/runtime\" (unplugin). */\nexport type CodegenMode = \"inline\" | \"lean\";\n\n/**\n * Kind of a poolable constant. Decides the shared identifier's prefix, so a\n * module-scope pool stays readable (`__zcSet_0`, …) rather than opaque.\n */\nexport type ConstantKind = \"Bf\" | \"Rx\" | \"Set\";\n\n/**\n * A poolable constant declaration emitted while generating one validator.\n *\n * Reported to the file pipeline, which pools initializers used by two or more\n * validators into a single module-scope declaration. Only value constants\n * qualify: they are allocated once at module init and read identically from\n * anywhere, so hoisting one out of a validator changes neither its identity nor\n * the hot path.\n *\n * Generated FAST-PATH functions are deliberately not poolable, even when two\n * validators emit byte-identical ones: sharing a function merges its call sites\n * onto a single feedback vector, which is exactly the polymorphism the fast\n * path is inlined to avoid. Cold slow walks are a different bargain and are\n * shared — by their own mechanism, `__zcSw_N` (see dedupe.ts).\n */\nexport interface GeneratedConstant {\n readonly kind: ConstantKind;\n readonly name: string;\n readonly initializer: string;\n}\n\nexport interface CodeGenResult {\n code: string;\n functionDef: string;\n /** Number of fallback schemas referenced by __rf[N] in the generated code. 0 = no fallbacks. */\n refCount: number;\n /**\n * Helper names referenced by this schema in lean mode (e.g. \"__zcTS\", \"__zcReEmail\").\n * Used by the unplugin transform to construct the `import { ... } from \"virtual:zod-compiler/runtime\"` line.\n * Always empty in inline mode.\n */\n usedHelpers: Set<string>;\n /**\n * Name of the hosted fast-check boolean function in the preamble (null when\n * the schema has no Fast Path). generateIIFE passes it to __zcMkv so parse()\n * and parseAsync() can return valid input without allocating an\n * intermediate SafeParseResult.\n */\n fastFnName: string | null;\n /**\n * True when `fastFnName` is a TOTAL predicate: `fc(input) === true` iff the\n * schema accepts `input` (mutation-free schemas, where a fast-check failure\n * can never become a slow-path success). generateIIFE installs it as the\n * zero-allocation `.is()` guard. False for partial fast paths\n * (default/catch — `fc` only shortcuts present-and-valid input, so a `false`\n * result does NOT imply rejection) and for schemas with no fast path, such as\n * coercion; `.is()` then derives from `safeParse(input).success`.\n */\n fastTotal: boolean;\n /**\n * Hosted predicate installed as `.is()`, when it differs from `fastFnName`.\n * A schema that rebuilds its output has no by-reference shortcut (so\n * `fastFnName` is null) yet still has an exact acceptance predicate, because\n * stripping reshapes the payload and never the verdict.\n */\n isFnName?: string | null;\n /**\n * Compact mode only: this validator reads {@link RETAINED_SCHEMA_VAR}\n * directly, so `generateIIFE` must bind it even when the schema has no\n * fallback refs of its own. Absent for every non-compact (fully compiled)\n * validator.\n */\n usesRetainedSchema?: boolean;\n}\n\n/** Hosted-validator names for one recursion target (see CodeGenContext.recTargets). */\nexport interface RecTargetGen {\n /** True for the root target (refId 0) — reuses the schema's own functions. */\n isRoot: boolean;\n /**\n * safeParse-shaped slow validator name: `safeParse_<name>` for the root,\n * `__rsp_N` for a non-root target. `slowRecursiveRef` calls this.\n */\n slowName: string;\n /**\n * Boolean fast-check name (`__fcr_N`). Allocated lazily for the root (mirrors\n * recFastName), eagerly for non-root targets. Absent until the fast path\n * reaches a ref to this target.\n */\n fastName?: string;\n /** Inner IR hosted as the standalone validator body (non-root targets only). */\n inner?: SchemaIR;\n}\n\n/** Shared mutable state for code generation. Fast and slow paths share the same instance. */\nexport interface CodeGenContext {\n preamble: string[];\n counter: number;\n fnName: string;\n /** Deduplicates regex patterns: same pattern string → same preamble variable name. */\n regexCache: Map<string, string>;\n /** Codegen output mode. */\n mode: CodegenMode;\n /** Names of helpers from \"virtual:zod-compiler/runtime\" referenced in this schema (lean mode only). */\n usedHelpers: Set<string>;\n /**\n * Name of the fast-path boolean helper for the ROOT recursion target\n * (refId 0), allocated on first fastRecursiveRef visit. generateValidator\n * wraps the root fast expression as `function <name>(input){return <expr>;}`\n * so recursive refs can call it. undefined = root has no recursion on the\n * fast path.\n */\n recFastName?: string;\n /**\n * Hosted-validator name table for recursion targets, keyed by refId. Entry 0\n * is the root (the schema's own `safeParse_<name>` / `recFastName`); entries\n * ≥ 1 are non-root targets hosted as standalone `__rsp_N` (slow) / `__fcr_N`\n * (fast) helpers. `recursiveRef`/`recursionTarget` generators look up the\n * call target here. Undefined when not generating a full validator (e.g. unit\n * tests calling a single generator) — treated as root-only.\n */\n recTargets?: Map<number, RecTargetGen>;\n /** Dedup cache for hosted zero-capture effect functions: source text → preamble var. */\n effectFnCache?: Map<string, string>;\n /** Dedup cache for constant preamble declarations: initializer text → preamble var. */\n valueCache?: Map<string, string>;\n /** Reports poolable constants so the file pipeline can share exact duplicates across validators. */\n onConstant?: ((constant: GeneratedConstant) => void) | undefined;\n /** Exact initializer → file-level name, populated by the pipeline's second codegen pass. */\n sharedConstantNames?: ReadonlyMap<string, string> | undefined;\n /** Name of the build path's FAIL sentinel, declared once per validator. */\n buildFailName?: string;\n /** Hosted build-function name per recursion target refId, so back-edges resolve. */\n buildRecNames?: Map<number, string>;\n /**\n * Set by the build path when it emitted a `.default()` substitution. Such a\n * schema ACCEPTS an input its fast expression rejects — `fastDefault` demands a\n * present value, since the fast path's contract is `data === input` and a\n * substituted default is not the input — so the expression is no longer an\n * exact acceptance predicate and must not be installed as `.is()`. Stripping,\n * by contrast, reshapes only the payload, which is why a build-path schema\n * otherwise still hands its predicate over.\n */\n buildSubstitutesValue?: boolean;\n /** Memo for estimateFastCost (size-gated fast-check extraction). Lazily created. */\n fastSizeCache?: WeakMap<SchemaIR, number>;\n /** Memo for estimateRuntimeCost (cheapest-first check ordering). Lazily created. */\n fastRuntimeCostCache?: WeakMap<SchemaIR, number>;\n /**\n * File-level shared slow-walk plan. Set only when generating a mutation-free\n * schema (so shared walks stay on the deferred cold path); the slow-path\n * visit() consults it to replace a repeated sub-IR with a `__zcSw_N` call.\n */\n sharedSchemas?: SharedSchemaPlan;\n}\n\n// ─── Slow Path context ────────────────────────────────────────────────────────\n\n/** Context object for slow-path (error-collecting) generator functions. */\nexport interface SlowGen {\n readonly input: string;\n readonly output: string;\n readonly path: string;\n readonly issues: string;\n readonly ctx: CodeGenContext;\n /**\n * Schema-level static error message of the node being generated\n * (z.string({ error: \"...\" })). Default message for issues this node emits\n * when the individual check has no message of its own. Set by generateSlow()\n * from ir.typeMessage; never inherited by child nodes.\n */\n readonly typeMsg?: string | undefined;\n\n /**\n * Name of a boolean variable that this node sets to `true` when it aborts in\n * the zod sense (`payload.aborted`) — currently only a pipe/codec whose `in`\n * step fails (zod's `handlePipeResult` sets `left.aborted = true`). A `union`\n * allocates one per option and reads it during pruning so a pipe option whose\n * `in` failed counts as aborted even when its only issue is a non-aborting\n * `custom`/check-level code. Undefined when the node is not inside an\n * abort-tracking option, in which case the abort is a no-op.\n *\n * Unlike input/output/path/issues, this is NOT inherited by `visit()`: it is\n * cleared at every boundary unless a node explicitly forwards it (the\n * pass-through wrappers optional/nullable/readonly do), mirroring how zod\n * propagates `payload.aborted` through transparent wrappers but not across\n * container boundaries.\n */\n readonly aborted?: string | undefined;\n\n /**\n * Recursively generate validation for a child IR node.\n * input/output/path/issues are inherited from parent unless overridden;\n * `aborted` is the exception — it is only set when explicitly passed (see the\n * `aborted` field doc), so it never leaks into container children.\n * Union generators use `{ issues }` to redirect child errors to temporary arrays.\n * Container generators use `{ input, output, path }` for element traversal.\n */\n visit(\n ir: SchemaIR,\n overrides?: {\n input?: string;\n output?: string;\n path?: string;\n issues?: string;\n // `| undefined` (unlike the others): pass-through wrappers forward\n // `g.aborted` verbatim, which is undefined outside an abort-tracking option.\n aborted?: string | undefined;\n },\n ): string;\n\n /** Generate a unique temp variable name: `__${prefix}_${counter++}` */\n temp(prefix: string): string;\n\n /** Add a regex to preamble and return the variable name. */\n regex(prefix: string, pattern: string, flags?: string): string;\n\n /** Add a Set to preamble and return the variable name. */\n set(prefix: string, values: readonly unknown[]): string;\n}\n\n/** Slow-path generator function signature — registered in slowRegistry. */\nexport type SlowGenerator<T extends SchemaIR = SchemaIR> = (ir: T, g: SlowGen) => string;\n\n// ─── Fast Path context ────────────────────────────────────────────────────────\n\n/**\n * Per-emitted-function size accumulator for size-gated fast-check extraction.\n * Shared by every node inlined into the same function; a fresh instance starts\n * each hosted helper (and the root). See fast-size.ts / generateFast.\n */\nexport interface FastScope {\n used: number;\n /**\n * `var` temps that the function this scope is assembling must declare, in\n * allocation order. Populated by {@link FastGen.local}; every site that\n * materializes a function body from a fresh scope emits\n * {@link declareFastTemps} at the top of that body.\n *\n * Function-scoped (never module-scoped) is load-bearing: a recursive\n * validator re-enters itself while an outer frame still holds a live temp,\n * and each invocation needs its own binding.\n */\n temps: string[];\n}\n\n/** `var a,b;` declaration for a scope's temps, or \"\" when it allocated none. */\nexport function declareFastTemps(scope: FastScope): string {\n return scope.temps.length > 0 ? `var ${scope.temps.join(\",\")};` : \"\";\n}\n\n/**\n * Fast check for a wrapper that compares its input against a sentinel and\n * otherwise delegates to an inner schema — `optional` (`===undefined`),\n * `nullable` (`===null`) and `default` (`!==undefined`).\n *\n * Written naively these read the input TWICE: once for the sentinel test and\n * again inside the inner check (which may itself read it several more times —\n * `typeof x===\"string\"&&x.length>=3&&x.length<=20`). V8's load elimination\n * removes the repeats only while the access is monomorphic; on the polymorphic\n * and megamorphic call sites real payloads produce (an array of objects with\n * differing key order, anything out of `JSON.parse`) every repeat is a fresh\n * megamorphic lookup. Binding the value to a local once is worth 1.1-1.7x on\n * the whole object check when the optional key is present, and is neutral when\n * it is absent.\n *\n * Only hoisted when the input is a property access; a bare local (an array\n * element variable, a record value) is already a single load, so it keeps the\n * shorter form and byte-identical output.\n */\nexport function fastSentinelWrapper(\n g: FastGen,\n innerIR: SchemaIR,\n sentinel: string,\n joiner: \"&&\" | \"||\",\n): string | null {\n if (!isPropertyAccess(g.input)) {\n const inner = g.visit(innerIR);\n return inner === null ? null : `(${g.input}${sentinel}${joiner}(${inner}))`;\n }\n const value = g.local(\"w\");\n const inner = g.visit(innerIR, { input: value });\n if (inner === null) return null;\n return `((${value}=${g.input})${sentinel}${joiner}(${inner}))`;\n}\n\n/** True for an expression that performs a property load (`x[\"a\"]`, `x.a`, `x[0][1]`). */\nfunction isPropertyAccess(expr: string): boolean {\n return expr.includes(\"[\") || expr.includes(\".\");\n}\n\n/** Context object for fast-path (boolean expression) generator functions. */\nexport interface FastGen {\n readonly input: string;\n readonly ctx: CodeGenContext;\n\n /**\n * Whether the CURRENT node may be hoisted into its own boolean helper when it\n * (with the already-emitted siblings) would overflow the function size cap.\n * False for the root and for a helper's own top node — those are already their\n * own function — but their children are extractable. See generateFast.\n */\n readonly extractable: boolean;\n\n /** Accumulated size (≈ chars) of the function currently being assembled. */\n readonly scope: FastScope;\n\n /**\n * Set on the gen for a discriminated-union option only: the discriminator\n * key. Signals `fastObject` to omit its type-guard and skip re-checking that\n * property (the switch already matched its value). Never propagated to child\n * nodes — nested objects keep their own guard.\n */\n readonly discSkipKey?: string | undefined;\n\n /**\n * Recursively generate fast-check expression for a child IR node.\n * Returns null if any child is ineligible for fast path.\n */\n visit(ir: SchemaIR, overrides?: { input?: string; discSkipKey?: string }): string | null;\n\n /**\n * A FastGen for emitting a SEPARATE function body (a hand-built preamble\n * helper such as a discriminated-union switch or an array-element loop). It\n * carries a FRESH size accumulator, so the helper's own content is size-gated\n * against the cap independently of the caller — without this, a helper's body\n * accrues to the caller's scope while the helper itself grows unbounded.\n */\n scoped(input: string): FastGen;\n\n /** Generate a unique temp variable name. */\n temp(prefix: string): string;\n\n /**\n * Allocate a unique name AND record it on this scope so the enclosing\n * emitted function declares it as a `var` (see {@link FastScope.temps}).\n * Use for a value bound inside an expression — `(t=x[\"k\"])===undefined` —\n * where `temp()` alone would leave the name undeclared.\n */\n local(prefix: string): string;\n\n /** Add a regex to preamble and return the variable name. */\n regex(prefix: string, pattern: string, flags?: string): string;\n}\n\n/** Fast-path generator function signature — registered in fastRegistry. */\nexport type FastGenerator<T extends SchemaIR = SchemaIR> = (ir: T, g: FastGen) => string | null;\n\n// ─── Shared emit helpers (used by both slow-path and fast-path factories) ────\n\n/** Allocate a fresh `__${prefix}_${n}` identifier and bump the shared counter. */\nexport function emitTemp(ctx: CodeGenContext, prefix: string): string {\n return `__${prefix}_${ctx.counter++}`;\n}\n\n/**\n * Host a zero-capture effect function (refine predicate, transform,\n * overwrite) in the preamble and return its variable name. The inline\n * `(${source})(x)` form evaluates the function expression — allocating a\n * function object — on EVERY parse at every effect site, including inside\n * the \"zero-allocation\" fast chain. V8's escape analysis erases that in\n * optimized frames, but interpreter/baseline/deopt frames pay it, and the\n * full source text re-parses as bytecode at each site. Zero-capture sources\n * reference only their own parameters and safe globals by construction, so\n * a single preamble binding is semantically identical. Deduped per schema\n * by source text.\n */\nexport function emitEffectFn(ctx: CodeGenContext, source: string): string {\n ctx.effectFnCache ??= new Map();\n const cached = ctx.effectFnCache.get(source);\n if (cached !== undefined) return cached;\n const name = `__ef_${ctx.counter++}`;\n ctx.preamble.push(`var ${name}=(${source});`);\n ctx.effectFnCache.set(source, name);\n return name;\n}\n\n/**\n * Callable expression for a user callback — a refine predicate or a transform.\n *\n * A zero-capture callback is hosted from its source text; one that CAPTURES\n * outer variables is called by reference through `__rf[N]` — the user's own\n * function object, reached from the schema — instead of costing the schema its\n * compiled path. The reference is aliased into a preamble binding rather than\n * re-read per call, for the same reason call-invoked helpers are (a per-call\n * array element load is not a foldable callee).\n */\nexport function emitEffectCallable(\n ctx: CodeGenContext,\n effect: { refIndex?: number | undefined; source?: string | undefined },\n): string {\n if (effect.refIndex !== undefined) return emitConstant(ctx, \"rfn\", `__rf[${effect.refIndex}]`);\n if (effect.source === undefined) {\n throw new Error(\"effect has neither inlineable source nor a reference index\");\n }\n return emitEffectFn(ctx, effect.source);\n}\n\n/**\n * Pristine fallback delegate: declare `var __rfp_N=__rf[N].safeParse.bind(__rf[N]);`\n * in the preamble and return the variable name. Generated code must NEVER read\n * `__rf[N].safeParse` at parse time: `__zcMkv` installs the compiled safeParse as\n * an OWN property on the original schema object, and whenever `__rf[N]` is that\n * same object the read resolves to the compiled delegate itself — infinite\n * recursion (RangeError on every parse). The fallback entry and the __zcMkv\n * target ARE the same object in compile mode (schemaExpr is the compile()\n * argument identifier) and the CLI emitter ((__src_X as any).schema); in\n * autoDiscover mode they are two textually identical constructions that any\n * downstream CSE/dedup transform (babel-plugin-zod-hoist in a field incident)\n * collapses back into one. Capturing at IIFE evaluation — before the trailing\n * `return __zcMkv(...)` mutates anything — pins zod's own implementation; the\n * worst case under cross-validator merges is delegating to an equivalent\n * compiled validator (whose own delegates were captured even earlier), never\n * a cycle.\n */\nexport function emitRfDelegate(ctx: CodeGenContext, refIndex: number): string {\n const name = `__rfp_${refIndex}`;\n const decl = `var ${name}=__rf[${refIndex}].safeParse.bind(__rf[${refIndex}]);`;\n if (!ctx.preamble.includes(decl)) {\n ctx.preamble.push(decl);\n }\n return name;\n}\n\n/**\n * Identifier `generateIIFE` binds the retained Zod schema to, once per export.\n *\n * Compact delegation reaches the schema through this binding rather than\n * through `__rf[]`. Routing it through the array meant every compact validator\n * — the common case being one with no fallback refs at all — declared a\n * one-element `var __rf=[__zs];` whose only reads were `__rf[0]`, i.e. an array\n * allocation per compiled schema at module init to alias a binding that was\n * already in scope. Naming the schema directly also keeps the reference a\n * foldable constant instead of an element load (the same reason\n * {@link emitEffectCallable} aliases its `__rf[N]` into a preamble binding).\n */\nexport const RETAINED_SCHEMA_VAR = \"__zs\";\n\n/**\n * Capture the retained schema's pristine `safeParse` without allocating a bound\n * function. Declared in the preamble, which `generateIIFE` places after the\n * `__zs` binding and before the trailing `__zcMkv` call — so the capture is\n * zod's own implementation, never the compiled delegate that call installs\n * (see {@link emitRfDelegate} for the recursion this avoids).\n */\nexport function emitRetainedMethod(ctx: CodeGenContext): string {\n const name = \"__rfm_z\";\n const decl = `var ${name}=${RETAINED_SCHEMA_VAR}.safeParse;`;\n if (!ctx.preamble.includes(decl)) {\n ctx.preamble.push(decl);\n }\n return name;\n}\n\n/**\n * Is a RegExp built with these flags safe to share between validators?\n *\n * `g` and `y` make the object STATEFUL: `.test()` advances `lastIndex` and the\n * next call resumes from there. Generated code is already correct about this —\n * every flagged site emits a `lastIndex=0` reset first (see `lastIndexReset` in\n * schemas/string.ts, the only generator that passes flags through) — so pooling\n * them would in fact work today.\n *\n * They are held back anyway, because the pool changes what a future lapse\n * costs. A missing reset on a validator-local regex misbehaves inside one\n * export, deterministically. On a pooled one it misbehaves across exports, and\n * only in the order the module happens to evaluate them. Flagged patterns are\n * rare enough that the sharing is not worth buying that failure mode. Every\n * other flag (`i`, `m`, `s`, `u`, `v`, `d`) is pure configuration and pools like\n * any other constant.\n */\nfunction isPoolableRegex(flags: string | undefined): boolean {\n return flags === undefined || !/[gy]/.test(flags);\n}\n\n/**\n * Resolve a regex pattern to a runtime variable name.\n *\n * Three layers, widest first. Lean mode short-circuits well-known patterns to\n * virtual-module names, deduping them across the whole bundle. What is left is\n * pooled at FILE level when two or more validators build the identical RegExp —\n * the same bargain Sets get, and a larger one in practice: a repeated enum is\n * one `new Set([...])`, while a repeated `z.iso.datetime()` or shared\n * `.regex()` is a several-hundred-byte pattern plus a RegExp construction per\n * validator at module init. Anything still unique is cached and declared in the\n * per-IIFE preamble exactly once per pattern.\n */\nexport function emitRegex(\n ctx: CodeGenContext,\n prefix: string,\n pattern: string,\n flags?: string,\n): string {\n if (ctx.mode === \"lean\" && !flags) {\n const wellKnown = lookupWellKnownRegex(pattern);\n if (wellKnown !== null) {\n ctx.usedHelpers.add(wellKnown);\n return wellKnown;\n }\n }\n const cacheKey = flags ? `${flags}\\u0000${pattern}` : pattern;\n const cached = ctx.regexCache.get(cacheKey);\n if (cached) return cached;\n const flagsArg = flags ? `,${escapeString(flags)}` : \"\";\n // Flag-less patterns may carry a faster behavior-equivalent rewrite (a\n // well-known table entry, repeat unrolling, or both); the regex OBJECT uses\n // it while issue sites keep reporting the original pattern (see slowString).\n const testSource = flags ? null : fastTestSource(pattern);\n const initializer = `new RegExp(${escapeString(testSource ?? pattern)}${flagsArg})`;\n const localPrefix = `re_${prefix}`;\n const name = isPoolableRegex(flags)\n ? emitPooledConstant(ctx, \"Rx\", localPrefix, initializer)\n : emitConstant(ctx, localPrefix, initializer);\n ctx.regexCache.set(cacheKey, name);\n return name;\n}\n\n/**\n * Resolve the ORIGINAL `/source/flags` pattern string of a regex for issue\n * reporting. Only needed when emitRegex swapped in a faster equivalent test\n * pattern (the runtime regex's toString() would leak the rewrite). Lean mode\n * references the shared `<name>Src` virtual export so the original pattern\n * stays a single bundle-wide string; inline mode declares it once per IIFE.\n */\nexport function emitRegexSourceString(ctx: CodeGenContext, pattern: string): string {\n if (ctx.mode === \"lean\") {\n const srcName = wellKnownRegexSourceName(pattern);\n if (srcName !== null) {\n ctx.usedHelpers.add(srcName);\n return srcName;\n }\n }\n const cacheKey = `src\\u0000${pattern}`;\n const cached = ctx.regexCache.get(cacheKey);\n if (cached) return cached;\n const name = `__res_${ctx.counter++}`;\n ctx.preamble.push(`var ${name}=${escapeString(`/${pattern}/`)};`);\n ctx.regexCache.set(cacheKey, name);\n return name;\n}\n\n/**\n * Declare a constant value in the preamble and return its variable name,\n * reusing an earlier declaration of the SAME initializer.\n *\n * Value tables are reached from both halves of a validator — an enum's `Set`\n * from its fast check and again from its slow walk, a strict shape's key table\n * likewise — and repeat across sibling properties that share a value list. One\n * declaration per USE emitted the payload two or four times: measured 17% of a\n * 20-value enum schema's generated bytes, 16% for an object with two identical\n * enums. Keyed by initializer text, so only identical payloads collapse.\n */\nexport function emitConstant(ctx: CodeGenContext, prefix: string, initializer: string): string {\n ctx.valueCache ??= new Map();\n const cached = ctx.valueCache.get(initializer);\n if (cached !== undefined) return cached;\n const name = `__${prefix}_${ctx.counter++}`;\n ctx.preamble.push(`var ${name}=${initializer};`);\n ctx.valueCache.set(initializer, name);\n return name;\n}\n\n/**\n * Declare a poolable constant in the preamble and return its variable name —\n * unless the file pipeline has already decided to hoist this exact initializer\n * to module scope, in which case the shared name is returned and nothing is\n * declared locally.\n *\n * The pipeline learns which initializers repeat by running codegen once and\n * collecting what was reported here, so every poolable constant must route\n * through this function rather than calling {@link emitConstant} directly.\n */\nexport function emitPooledConstant(\n ctx: CodeGenContext,\n kind: ConstantKind,\n localPrefix: string,\n initializer: string,\n): string {\n const sharedName = ctx.sharedConstantNames?.get(initializer);\n if (sharedName !== undefined) return sharedName;\n const name = emitConstant(ctx, localPrefix, initializer);\n ctx.onConstant?.({ kind, name, initializer });\n return name;\n}\n\n/** Declare a `new Set([...])` in the preamble and return its variable name. */\nexport function emitSet(ctx: CodeGenContext, prefix: string, values: readonly unknown[]): string {\n const initializer = `new Set(${JSON.stringify([...values])})`;\n return emitPooledConstant(ctx, \"Set\", `set_${prefix}`, initializer);\n}\n\n/**\n * Shape-key count at or below which the unknown-key pass compares with an\n * inline `===` chain rather than a hashed lookup.\n *\n * This is deliberately NOT {@link ENUM_INLINE_THRESHOLD}: the two look alike but\n * are different workloads. An enum compares schema literals against arbitrary\n * INPUT strings, which may be long, share prefixes, and are not necessarily\n * internalized — so a hashed set earns its keep quickly. A shape-key test\n * compares them against keys arriving from `for-in`, i.e. the object's own\n * internalized key strings, so every arm of the chain is a pointer compare that\n * V8 predicts perfectly, while `table[k]` / `set.has(k)` pays a string hash and\n * probe per key.\n *\n * Measured over a strict object's for-in pass (JSON-parsed input, 8 rotated\n * shapes), `===` chain vs the previous `{k:1}` table: 3.7x at 6 keys, 3.8x at\n * 10, 3.1x at 20, 3.1x at 48 — the chain still leads at 64 (283 ns vs 690) and\n * only loses past ~96, where `Set.has` (not the table, which never wins at any\n * size) takes over. 64 sits below that crossover and above any realistic shape.\n */\nexport const KEY_MEMBERSHIP_INLINE_THRESHOLD = 64;\n\n/**\n * Boolean membership test for one key variable against a fixed key list.\n * Empty list recognizes nothing.\n *\n * `Set.has` is the large-shape fallback rather than a `{key:1}` object table:\n * the table is also `__proto__`-hostile (an own `__proto__` key cannot be set\n * by an object literal, so that key would silently read as unknown), which the\n * Set has no trouble with.\n */\nexport function keyMembershipTest(\n ctx: CodeGenContext,\n keys: readonly string[],\n keyVar: string,\n): string {\n if (keys.length === 0) return \"false\";\n if (keys.length <= KEY_MEMBERSHIP_INLINE_THRESHOLD) {\n return keys.map((k) => `${keyVar}===${escapeString(k)}`).join(\"||\");\n }\n return `${emitSet(ctx, \"ks\", keys)}.has(${keyVar})`;\n}\n\n/**\n * Enum values at or below this count use inline === checks instead of Set.has().\n * Measured on V8: for ≤5 values, an === chain beats Set.has by up to ~3x with\n * realistic (distinct-prefix, JSON-parsed) values — V8 internalizes strings on\n * successful comparison, making subsequent arms pointer-equality — and is no\n * worse than Set.has even with adversarial shared-prefix values.\n */\nexport const ENUM_INLINE_THRESHOLD = 5;\n\nconst CHECK_PRIORITY: Record<string, number> = {\n // Cheapest: length/size comparisons (O(1))\n min_length: 10,\n max_length: 11,\n length_equals: 12,\n min_size: 13,\n max_size: 14,\n // Number format checks (comparison + bitwise)\n number_format: 15,\n // Range comparisons\n greater_than: 20,\n less_than: 21,\n bigint_greater_than: 20,\n bigint_less_than: 21,\n date_greater_than: 22,\n date_less_than: 23,\n // Modulo\n multiple_of: 30,\n bigint_multiple_of: 30,\n // String prefix/suffix (O(prefix/suffix length))\n starts_with: 40,\n ends_with: 41,\n // String search (O(n·m) worst case)\n includes: 42,\n // Regex (most expensive)\n string_format: 50,\n};\n\nexport function escapeString(s: string | number): string {\n return JSON.stringify(s);\n}\n\n/** The {@link LiteralValue}s that {@link literalToJs} can spell. */\nexport type SourceFormLiteral = string | number | boolean | null | bigint | undefined;\n\n/**\n * Can {@link literalToJs} render this value as JS source that strict-equals it?\n *\n * Total by construction — it NAMES the value kinds that have a source form\n * rather than excluding the ones that don't, so every reference value falls out\n * on the false side. That matters because `literalToJs` used to end in a bare\n * `JSON.stringify`, which does not fail loudly on the values it cannot spell:\n * for a symbol it RETURNS `undefined` (the value, not a string), so\n * `z.literal(sym)` compiled to the comparison `x===undefined` — rejecting the\n * symbol it was built from and accepting `undefined`. An object is mis-rendered\n * the other way: `{}` stringifies to `\"{}\"`, and `x==={}` is never true, so the\n * very object the schema was built from was rejected. Both take the runtime\n * membership path instead (see the literal generator).\n */\nexport function hasSourceForm(v: LiteralValue): v is SourceFormLiteral {\n if (v === null) return true;\n const t = typeof v;\n return t === \"string\" || t === \"number\" || t === \"boolean\" || t === \"bigint\" || t === \"undefined\";\n}\n\n/**\n * JS source for a primitive literal value (literal schemas, discriminator\n * case labels). JSON.stringify covers string/number/boolean/null; bigint\n * needs the `n` suffix (JSON.stringify throws and String(5n) renders a\n * number literal that never strict-equals a bigint); undefined isn't JSON.\n *\n * The parameter type is deliberately NARROWER than {@link LiteralValue}: every\n * caller must first prove its value is spellable with {@link hasSourceForm}.\n */\nexport function literalToJs(v: SourceFormLiteral): string {\n if (typeof v === \"bigint\") return `${v}n`;\n if (v === undefined) return \"undefined\";\n // JSON.stringify maps NaN/±Infinity to \"null\"; emit them as JS expressions so a\n // non-finite numeric literal round-trips (z.literal(Infinity) must compare\n // against Infinity, not null). String(NaN)=\"NaN\", String(Infinity)=\"Infinity\",\n // String(-Infinity)=\"-Infinity\" — all valid JS that evaluate to the value.\n if (typeof v === \"number\" && !Number.isFinite(v)) return String(v);\n return JSON.stringify(v);\n}\n\n/**\n * Helpers that generated code invokes through `Function.prototype.call`, and\n * which therefore must be aliased into a module-local binding in lean mode.\n *\n * V8 folds a local `const` callee into a constant and inlines straight through\n * `x.call(...)`; an IMPORTED binding is a cell it will not fold, so the same\n * expression stays a generic property load plus a generic call — measured 4.5x\n * (5 keys) to 6.5x (20 keys) slower on the record fast path, 35.9 ns vs 7.2 ns\n * for a 5-key record. Aliasing the import into the IIFE recovers all of it\n * (7.4 ns). A DIRECT call to an imported function (`__zcFsr(v,s)`) is not\n * penalized — measured identical — and neither is an imported RegExp receiver,\n * so only the `.call` sites are listed here.\n */\nconst CALL_INVOKED_HELPERS: ReadonlySet<string> = new Set([\"__zcHop\"]);\n\n/**\n * Reference a shared runtime helper (e.g. __zcFsr) from generated code.\n * Lean mode: registers it for the `virtual:zod-compiler/runtime` import.\n * Inline mode: declares it once in the per-IIFE preamble.\n */\nexport function emitRuntimeHelper(ctx: CodeGenContext, name: string, decl: string): string {\n if (ctx.mode === \"lean\") {\n ctx.usedHelpers.add(name);\n if (CALL_INVOKED_HELPERS.has(name)) return emitConstant(ctx, \"lh\", name);\n } else if (!ctx.preamble.includes(decl)) {\n ctx.preamble.push(decl);\n }\n return name;\n}\n\n/**\n * Extend a path expression with one or more scalar segment expressions\n * (escaped string literals, numeric literals, or loop-variable names).\n *\n * Path expressions are only ever composed by these helpers starting from the\n * `[]` root, so any path that looks like an array literal IS one — the new\n * segment is spliced in to keep issue paths a single array allocation\n * (`[\"data\",\"items\",__i_7]`) instead of an allocation per nesting level\n * (`[\"data\"].concat(\"items\").concat(__i_7)`). Opaque expressions fall back\n * to .concat().\n */\nexport function extendPath(parentPath: string, segExpr: string): string {\n if (parentPath === \"[]\") return `[${segExpr}]`;\n if (parentPath.startsWith(\"[\") && parentPath.endsWith(\"]\")) {\n return `${parentPath.slice(0, -1)},${segExpr}]`;\n }\n return `${parentPath}.concat(${segExpr})`;\n}\n\n/** Extend a path expression with a static string key. */\nexport function extendStaticPath(parentPath: string, key: string): string {\n return extendPath(parentPath, escapeString(key));\n}\n\n/** Extend a path expression with a numeric index. */\nexport function extendStaticPathIndex(parentPath: string, index: number): string {\n return extendPath(parentPath, String(index));\n}\n\n/**\n * A superRefine callback receives zod's payload, whose `value` is public,\n * typed, writable API ($RefinementCtx extends ParsePayload) — so any node\n * carrying one MAY rewrite its value and must be treated as mutating. Which\n * callbacks actually do is undecidable here; the emitted fast check settles it\n * at runtime by refusing when the value changed (see ZC_SR_OK_DECL), so a\n * non-mutating callback still exits through the fast path.\n */\nfunction hasSuperRefine(checks: readonly { kind: string }[] | undefined): boolean {\n return checks !== undefined && checks.some((c) => c.kind === \"super_refine_effect\");\n}\n\n/**\n * Check if a SchemaIR tree produces output that is not the input itself —\n * either value-mutating operations (coerce, default, catch, overwrite) that\n * write back to the input expression, or a strip object that rebuilds a fresh\n * object from its known keys. Used by container generators to decide whether to\n * clone (so the rebuilt/mutated value never writes through to the caller's\n * input), by generateValidator to keep such schemas off the by-reference fast\n * path, and by the shared-walk dedup + intersection extractor to exclude them.\n */\n/**\n * Can this tuple's output be LONGER than its input?\n *\n * `handleTupleResult` assigns `final.value[i] = result.value` for every item it\n * runs, and $ZodTuple runs every item below `optStart` even when the input is\n * shorter — so a required slot past the end is written with the `undefined` its\n * schema returned, extending the array. `z.tuple([z.any(), z.any()])` therefore\n * answers `[\"x\"]` with `[\"x\", undefined]`, length 2. A required item that\n * REJECTS undefined can't produce that: the parse fails and the value is never\n * read. So the extension is possible exactly when some required item accepts\n * `undefined` — which also makes the tuple a mutating node, since its output is\n * then not its input.\n */\nexport function tuplePadsShortInput(ir: SchemaIR & { type: \"tuple\" }): boolean {\n return ir.items.some((item, index) => index < ir.optStart && !rejectsUndefined(item));\n}\n\nexport function hasMutation(ir: SchemaIR): boolean {\n switch (ir.type) {\n case \"string\":\n // url checks trim (and optionally normalize) the value; overwrite\n // effects (.trim(), .toLowerCase()) rewrite it.\n return (\n ir.coerce === true ||\n hasSuperRefine(ir.checks) ||\n ir.checks.some(\n (c) =>\n c.kind === \"overwrite_effect\" || (c.kind === \"string_format\" && c.format === \"url\"),\n )\n );\n case \"number\":\n return ir.coerce === true || hasSuperRefine(ir.checks);\n case \"boolean\":\n case \"bigint\":\n case \"date\":\n return ir.coerce === true;\n case \"default\":\n case \"catch\":\n case \"effect\":\n case \"fallback\":\n case \"stringBool\":\n return true;\n case \"object\":\n // A strip object produces a FRESH output (only the declared keys), so it\n // mutates: parents must clone before it writes back, it never takes the\n // by-reference fast path, and intersections of strip objects delegate to\n // zod (see extractIntersection's hasMutation guard) — matching zod's\n // parse-both-sides-then-merge semantics instead of over-stripping.\n return (\n ir.stripUnknownKeys === true ||\n hasSuperRefine(ir.checks) ||\n (ir.catchall !== undefined && hasMutation(ir.catchall)) ||\n Object.values(ir.properties).some((p) => hasMutation(p))\n );\n case \"array\":\n return hasSuperRefine(ir.checks) || hasMutation(ir.element);\n case \"tuple\":\n return (\n ir.items.some(hasMutation) ||\n (ir.rest !== null && hasMutation(ir.rest)) ||\n tuplePadsShortInput(ir)\n );\n case \"record\":\n return hasMutation(ir.valueType);\n // A freezing readonly produces a value that is not its input, exactly as a\n // strip object does — so it must never take a by-reference shortcut.\n case \"readonly\":\n return ir.freeze === true || hasMutation(ir.inner);\n case \"optional\":\n case \"nullable\":\n case \"recursionTarget\":\n case \"zodDelegate\":\n return hasMutation(ir.inner);\n case \"union\":\n case \"discriminatedUnion\":\n return ir.options.some(hasMutation);\n case \"intersection\":\n return hasMutation(ir.left) || hasMutation(ir.right);\n case \"pipe\":\n return hasMutation(ir.in) || hasMutation(ir.out);\n case \"set\":\n return hasMutation(ir.valueType);\n case \"map\":\n return hasMutation(ir.keyType) || hasMutation(ir.valueType);\n case \"file\":\n return false;\n default:\n return false;\n }\n}\n\n/**\n * Is a defaulted property's key guaranteed to appear in the stripped output?\n *\n * Distinct from {@link rejectsUndefined}, which asks whether `undefined` is\n * REJECTED — a `.default()` accepts it and yet still produces a defined value, so\n * only this question earns the key a slot in the output object literal. The two\n * answers coincide everywhere else.\n *\n * Sound for both branches of a default: the substituted value is defined\n * (`alwaysDefined`, checked against the schema at extraction time), and the inner\n * branch runs only when `input[key] !== undefined`, which implies `key in input`\n * — so zod's presence test keeps the key whatever the inner produced.\n */\nexport function outputAlwaysDefined(ir: SchemaIR): boolean {\n return ir.type === \"default\" ? ir.alwaysDefined === true : rejectsUndefined(ir);\n}\n\n/**\n * Does this schema reject `undefined` outright?\n *\n * Read as \"can this slot be ABSENT from the input\" by the tuple build, whose\n * output length depends on it — zod marks a defaulted or optional item\n * `optin: \"optional\"` and accepts a shorter array. Conservative: anything that\n * might accept, produce, or default to `undefined` answers false.\n */\nexport function rejectsUndefined(ir: SchemaIR): boolean {\n switch (ir.type) {\n // Coercion turns undefined into a value (`String(undefined)`), so a\n // coercing primitive is NOT a rejector.\n case \"string\":\n case \"number\":\n case \"boolean\":\n case \"bigint\":\n case \"date\":\n return ir.coerce !== true;\n case \"symbol\":\n case \"null\":\n case \"nan\":\n case \"never\":\n case \"enum\":\n case \"object\":\n case \"array\":\n case \"tuple\":\n case \"record\":\n case \"set\":\n case \"map\":\n case \"file\":\n case \"templateLiteral\":\n case \"discriminatedUnion\":\n case \"stringBool\":\n return true;\n case \"literal\":\n return !ir.values.includes(undefined);\n case \"union\":\n return ir.options.every(rejectsUndefined);\n case \"intersection\":\n return rejectsUndefined(ir.left) || rejectsUndefined(ir.right);\n case \"nullable\":\n case \"readonly\":\n case \"recursionTarget\":\n case \"zodDelegate\":\n return rejectsUndefined(ir.inner);\n default:\n // optional / any / unknown / undefined / void / default / catch /\n // fallback / effect / pipe / recursiveRef — each can yield undefined,\n // or is opaque enough that we must not assume otherwise.\n return false;\n }\n}\n\n/**\n * Sort comparator for CheckIR: cheapest/most-discriminating checks first.\n * Used by fast-path generators after filtering out refine_effect entries.\n */\nexport function checkPriority(\n a: CheckIR | BigIntCheckIR | DateCheckIR | SetCheckIR,\n b: CheckIR | BigIntCheckIR | DateCheckIR | SetCheckIR,\n): number {\n return (CHECK_PRIORITY[a.kind] ?? 99) - (CHECK_PRIORITY[b.kind] ?? 99);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"context.js","names":[],"sources":["../../../src/core/codegen/context.ts"],"sourcesContent":["import type {\n BigIntCheckIR,\n CheckIR,\n DateCheckIR,\n LiteralValue,\n SchemaIR,\n SetCheckIR,\n} from \"../types.js\";\nimport type { SharedSchemaPlan } from \"./dedupe.js\";\nimport {\n fastTestSource,\n lookupWellKnownRegex,\n wellKnownRegexSourceName,\n} from \"./well-known-regex.js\";\n\n/** Codegen output mode. \"inline\" emits self-contained code (CLI .compiled.ts). \"lean\" emits references to imports from \"virtual:zod-compiler/runtime\" (unplugin). */\nexport type CodegenMode = \"inline\" | \"lean\";\n\n/**\n * Kind of a poolable constant. Decides the shared identifier's prefix, so a\n * module-scope pool stays readable (`__zcSet_0`, …) rather than opaque.\n */\nexport type ConstantKind = \"Bf\" | \"Rx\" | \"Set\";\n\n/**\n * A poolable constant declaration emitted while generating one validator.\n *\n * Reported to the file pipeline, which pools initializers used by two or more\n * validators into a single module-scope declaration. Only value constants\n * qualify: they are allocated once at module init and read identically from\n * anywhere, so hoisting one out of a validator changes neither its identity nor\n * the hot path.\n *\n * Generated FAST-PATH functions are deliberately not poolable, even when two\n * validators emit byte-identical ones: sharing a function merges its call sites\n * onto a single feedback vector, which is exactly the polymorphism the fast\n * path is inlined to avoid. Cold slow walks are a different bargain and are\n * shared — by their own mechanism, `__zcSw_N` (see dedupe.ts).\n */\nexport interface GeneratedConstant {\n readonly kind: ConstantKind;\n readonly name: string;\n readonly initializer: string;\n}\n\nexport interface CodeGenResult {\n code: string;\n functionDef: string;\n /** Number of fallback schemas referenced by __rf[N] in the generated code. 0 = no fallbacks. */\n refCount: number;\n /**\n * Helper names referenced by this schema in lean mode (e.g. \"__zcTS\", \"__zcReEmail\").\n * Used by the unplugin transform to construct the `import { ... } from \"virtual:zod-compiler/runtime\"` line.\n * Always empty in inline mode.\n */\n usedHelpers: Set<string>;\n /**\n * Name of the hosted fast-check boolean function in the preamble (null when\n * the schema has no Fast Path). generateIIFE passes it to __zcMkv so parse()\n * and parseAsync() can return valid input without allocating an\n * intermediate SafeParseResult.\n */\n fastFnName: string | null;\n /**\n * True when `fastFnName` is a TOTAL predicate: `fc(input) === true` iff the\n * schema accepts `input` (mutation-free schemas, where a fast-check failure\n * can never become a slow-path success). generateIIFE installs it as the\n * zero-allocation `.is()` guard. False for partial fast paths\n * (default/catch — `fc` only shortcuts present-and-valid input, so a `false`\n * result does NOT imply rejection) and for schemas with no fast path, such as\n * coercion; `.is()` then derives from `safeParse(input).success`.\n */\n fastTotal: boolean;\n /**\n * Hosted predicate installed as `.is()`, when it differs from `fastFnName`.\n * A schema that rebuilds its output has no by-reference shortcut (so\n * `fastFnName` is null) yet still has an exact acceptance predicate, because\n * stripping reshapes the payload and never the verdict.\n */\n isFnName?: string | null;\n /**\n * Compact mode only: this validator reads {@link RETAINED_SCHEMA_VAR}\n * directly, so `generateIIFE` must bind it even when the schema has no\n * fallback refs of its own. Absent for every non-compact (fully compiled)\n * validator.\n */\n usesRetainedSchema?: boolean;\n}\n\n/** Hosted-validator names for one recursion target (see CodeGenContext.recTargets). */\nexport interface RecTargetGen {\n /** True for the root target (refId 0) — reuses the schema's own functions. */\n isRoot: boolean;\n /**\n * safeParse-shaped slow validator name: `safeParse_<name>` for the root,\n * `__rsp_N` for a non-root target. `slowRecursiveRef` calls this.\n */\n slowName: string;\n /**\n * Boolean fast-check name (`__fcr_N`). Allocated lazily for the root (mirrors\n * recFastName), eagerly for non-root targets. Absent until the fast path\n * reaches a ref to this target.\n */\n fastName?: string;\n /** Inner IR hosted as the standalone validator body (non-root targets only). */\n inner?: SchemaIR;\n}\n\n/** Shared mutable state for code generation. Fast and slow paths share the same instance. */\nexport interface CodeGenContext {\n preamble: string[];\n counter: number;\n fnName: string;\n /** Deduplicates regex patterns: same pattern string → same preamble variable name. */\n regexCache: Map<string, string>;\n /** Codegen output mode. */\n mode: CodegenMode;\n /** Names of helpers from \"virtual:zod-compiler/runtime\" referenced in this schema (lean mode only). */\n usedHelpers: Set<string>;\n /**\n * Name of the fast-path boolean helper for the ROOT recursion target\n * (refId 0), allocated on first fastRecursiveRef visit. generateValidator\n * wraps the root fast expression as `function <name>(input){return <expr>;}`\n * so recursive refs can call it. undefined = root has no recursion on the\n * fast path.\n */\n recFastName?: string;\n /**\n * Hosted-validator name table for recursion targets, keyed by refId. Entry 0\n * is the root (the schema's own `safeParse_<name>` / `recFastName`); entries\n * ≥ 1 are non-root targets hosted as standalone `__rsp_N` (slow) / `__fcr_N`\n * (fast) helpers. `recursiveRef`/`recursionTarget` generators look up the\n * call target here. Undefined when not generating a full validator (e.g. unit\n * tests calling a single generator) — treated as root-only.\n */\n recTargets?: Map<number, RecTargetGen>;\n /** Dedup cache for hosted zero-capture effect functions: source text → preamble var. */\n effectFnCache?: Map<string, string>;\n /** Dedup cache for constant preamble declarations: initializer text → preamble var. */\n valueCache?: Map<string, string>;\n /** Reports poolable constants so the file pipeline can share exact duplicates across validators. */\n onConstant?: ((constant: GeneratedConstant) => void) | undefined;\n /** Exact initializer → file-level name, populated by the pipeline's second codegen pass. */\n sharedConstantNames?: ReadonlyMap<string, string> | undefined;\n /** Name of the build path's FAIL sentinel, declared once per validator. */\n buildFailName?: string;\n /** Hosted build-function name per recursion target refId, so back-edges resolve. */\n buildRecNames?: Map<number, string>;\n /**\n * Set by the build path when it emitted a `.default()` substitution. Such a\n * schema ACCEPTS an input its fast expression rejects — `fastDefault` demands a\n * present value, since the fast path's contract is `data === input` and a\n * substituted default is not the input — so the expression is no longer an\n * exact acceptance predicate and must not be installed as `.is()`. Stripping,\n * by contrast, reshapes only the payload, which is why a build-path schema\n * otherwise still hands its predicate over.\n */\n buildSubstitutesValue?: boolean;\n /** Memo for estimateFastCost (size-gated fast-check extraction). Lazily created. */\n fastSizeCache?: WeakMap<SchemaIR, number>;\n /** Memo for estimateRuntimeCost (cheapest-first check ordering). Lazily created. */\n fastRuntimeCostCache?: WeakMap<SchemaIR, number>;\n /**\n * File-level shared slow-walk plan. Set only when generating a mutation-free\n * schema (so shared walks stay on the deferred cold path); the slow-path\n * visit() consults it to replace a repeated sub-IR with a `__zcSw_N` call.\n */\n sharedSchemas?: SharedSchemaPlan;\n}\n\n// ─── Slow Path context ────────────────────────────────────────────────────────\n\n/** Context object for slow-path (error-collecting) generator functions. */\nexport interface SlowGen {\n readonly input: string;\n readonly output: string;\n readonly path: string;\n readonly issues: string;\n readonly ctx: CodeGenContext;\n /**\n * Schema-level static error message of the node being generated\n * (z.string({ error: \"...\" })). Default message for issues this node emits\n * when the individual check has no message of its own. Set by generateSlow()\n * from ir.typeMessage; never inherited by child nodes.\n */\n readonly typeMsg?: string | undefined;\n\n /**\n * Name of a boolean variable that this node sets to `true` when it aborts in\n * the zod sense (`payload.aborted`) — currently only a pipe/codec whose `in`\n * step fails (zod's `handlePipeResult` sets `left.aborted = true`). A `union`\n * allocates one per option and reads it during pruning so a pipe option whose\n * `in` failed counts as aborted even when its only issue is a non-aborting\n * `custom`/check-level code. Undefined when the node is not inside an\n * abort-tracking option, in which case the abort is a no-op.\n *\n * Unlike input/output/path/issues, this is NOT inherited by `visit()`: it is\n * cleared at every boundary unless a node explicitly forwards it (the\n * pass-through wrappers optional/nullable/readonly do), mirroring how zod\n * propagates `payload.aborted` through transparent wrappers but not across\n * container boundaries.\n */\n readonly aborted?: string | undefined;\n\n /**\n * Recursively generate validation for a child IR node.\n * input/output/path/issues are inherited from parent unless overridden;\n * `aborted` is the exception — it is only set when explicitly passed (see the\n * `aborted` field doc), so it never leaks into container children.\n * Union generators use `{ issues }` to redirect child errors to temporary arrays.\n * Container generators use `{ input, output, path }` for element traversal.\n */\n visit(\n ir: SchemaIR,\n overrides?: {\n input?: string;\n output?: string;\n path?: string;\n issues?: string;\n // `| undefined` (unlike the others): pass-through wrappers forward\n // `g.aborted` verbatim, which is undefined outside an abort-tracking option.\n aborted?: string | undefined;\n },\n ): string;\n\n /** Generate a unique temp variable name: `__${prefix}_${counter++}` */\n temp(prefix: string): string;\n\n /** Add a regex to preamble and return the variable name. */\n regex(prefix: string, pattern: string, flags?: string): string;\n\n /** Add a Set to preamble and return the variable name. */\n set(prefix: string, values: readonly unknown[]): string;\n}\n\n/** Slow-path generator function signature — registered in slowRegistry. */\nexport type SlowGenerator<T extends SchemaIR = SchemaIR> = (ir: T, g: SlowGen) => string;\n\n// ─── Fast Path context ────────────────────────────────────────────────────────\n\n/**\n * Per-emitted-function size accumulator for size-gated fast-check extraction.\n * Shared by every node inlined into the same function; a fresh instance starts\n * each hosted helper (and the root). See fast-size.ts / generateFast.\n */\nexport interface FastScope {\n used: number;\n /**\n * `var` temps that the function this scope is assembling must declare, in\n * allocation order. Populated by {@link FastGen.local}; every site that\n * materializes a function body from a fresh scope emits\n * {@link declareFastTemps} at the top of that body.\n *\n * Function-scoped (never module-scoped) is load-bearing: a recursive\n * validator re-enters itself while an outer frame still holds a live temp,\n * and each invocation needs its own binding.\n */\n temps: string[];\n}\n\n/** `var a,b;` declaration for a scope's temps, or \"\" when it allocated none. */\nexport function declareFastTemps(scope: FastScope): string {\n return scope.temps.length > 0 ? `var ${scope.temps.join(\",\")};` : \"\";\n}\n\n/**\n * Fast check for a wrapper that compares its input against a sentinel and\n * otherwise delegates to an inner schema — `optional` (`===undefined`),\n * `nullable` (`===null`) and `default` (`!==undefined`).\n *\n * Written naively these read the input TWICE: once for the sentinel test and\n * again inside the inner check (which may itself read it several more times —\n * `typeof x===\"string\"&&x.length>=3&&x.length<=20`). V8's load elimination\n * removes the repeats only while the access is monomorphic; on the polymorphic\n * and megamorphic call sites real payloads produce (an array of objects with\n * differing key order, anything out of `JSON.parse`) every repeat is a fresh\n * megamorphic lookup. Binding the value to a local once is worth 1.1-1.7x on\n * the whole object check when the optional key is present, and is neutral when\n * it is absent.\n *\n * Only hoisted when the input is a property access; a bare local (an array\n * element variable, a record value) is already a single load, so it keeps the\n * shorter form and byte-identical output.\n */\nexport function fastSentinelWrapper(\n g: FastGen,\n innerIR: SchemaIR,\n sentinel: string,\n joiner: \"&&\" | \"||\",\n): string | null {\n if (!isPropertyAccess(g.input)) {\n const inner = g.visit(innerIR);\n return inner === null ? null : `(${g.input}${sentinel}${joiner}(${inner}))`;\n }\n const value = g.local(\"w\");\n const inner = g.visit(innerIR, { input: value });\n if (inner === null) return null;\n return `((${value}=${g.input})${sentinel}${joiner}(${inner}))`;\n}\n\n/** True for an expression that performs a property load (`x[\"a\"]`, `x.a`, `x[0][1]`). */\nfunction isPropertyAccess(expr: string): boolean {\n return expr.includes(\"[\") || expr.includes(\".\");\n}\n\n/** Context object for fast-path (boolean expression) generator functions. */\nexport interface FastGen {\n readonly input: string;\n readonly ctx: CodeGenContext;\n\n /**\n * Whether the CURRENT node may be hoisted into its own boolean helper when it\n * (with the already-emitted siblings) would overflow the function size cap.\n * False for the root and for a helper's own top node — those are already their\n * own function — but their children are extractable. See generateFast.\n */\n readonly extractable: boolean;\n\n /** Accumulated size (≈ chars) of the function currently being assembled. */\n readonly scope: FastScope;\n\n /**\n * Set on the gen for a discriminated-union option only: the discriminator\n * key. Signals `fastObject` to omit its type-guard and skip re-checking that\n * property (the switch already matched its value). Never propagated to child\n * nodes — nested objects keep their own guard.\n */\n readonly discSkipKey?: string | undefined;\n\n /**\n * Recursively generate fast-check expression for a child IR node.\n * Returns null if any child is ineligible for fast path.\n */\n visit(ir: SchemaIR, overrides?: { input?: string; discSkipKey?: string }): string | null;\n\n /**\n * A FastGen for emitting a SEPARATE function body (a hand-built preamble\n * helper such as a discriminated-union switch or an array-element loop). It\n * carries a FRESH size accumulator, so the helper's own content is size-gated\n * against the cap independently of the caller — without this, a helper's body\n * accrues to the caller's scope while the helper itself grows unbounded.\n */\n scoped(input: string): FastGen;\n\n /** Generate a unique temp variable name. */\n temp(prefix: string): string;\n\n /**\n * Allocate a unique name AND record it on this scope so the enclosing\n * emitted function declares it as a `var` (see {@link FastScope.temps}).\n * Use for a value bound inside an expression — `(t=x[\"k\"])===undefined` —\n * where `temp()` alone would leave the name undeclared.\n */\n local(prefix: string): string;\n\n /** Add a regex to preamble and return the variable name. */\n regex(prefix: string, pattern: string, flags?: string): string;\n}\n\n/** Fast-path generator function signature — registered in fastRegistry. */\nexport type FastGenerator<T extends SchemaIR = SchemaIR> = (ir: T, g: FastGen) => string | null;\n\n// ─── Shared emit helpers (used by both slow-path and fast-path factories) ────\n\n/** Allocate a fresh `__${prefix}_${n}` identifier and bump the shared counter. */\nexport function emitTemp(ctx: CodeGenContext, prefix: string): string {\n return `__${prefix}_${ctx.counter++}`;\n}\n\n/**\n * Host a zero-capture effect function (refine predicate, transform,\n * overwrite) in the preamble and return its variable name. The inline\n * `(${source})(x)` form evaluates the function expression — allocating a\n * function object — on EVERY parse at every effect site, including inside\n * the \"zero-allocation\" fast chain. V8's escape analysis erases that in\n * optimized frames, but interpreter/baseline/deopt frames pay it, and the\n * full source text re-parses as bytecode at each site. Zero-capture sources\n * reference only their own parameters and safe globals by construction, so\n * a single preamble binding is semantically identical. Deduped per schema\n * by source text.\n */\nexport function emitEffectFn(ctx: CodeGenContext, source: string): string {\n ctx.effectFnCache ??= new Map();\n const cached = ctx.effectFnCache.get(source);\n if (cached !== undefined) return cached;\n const name = `__ef_${ctx.counter++}`;\n ctx.preamble.push(`var ${name}=(${source});`);\n ctx.effectFnCache.set(source, name);\n return name;\n}\n\n/**\n * Callable expression for a user callback — a refine predicate or a transform.\n *\n * A zero-capture callback is hosted from its source text; one that CAPTURES\n * outer variables is called by reference through `__rf[N]` — the user's own\n * function object, reached from the schema — instead of costing the schema its\n * compiled path. The reference is aliased into a preamble binding rather than\n * re-read per call, for the same reason call-invoked helpers are (a per-call\n * array element load is not a foldable callee).\n */\nexport function emitEffectCallable(\n ctx: CodeGenContext,\n effect: { refIndex?: number | undefined; source?: string | undefined },\n): string {\n if (effect.refIndex !== undefined) return emitConstant(ctx, \"rfn\", `__rf[${effect.refIndex}]`);\n if (effect.source === undefined) {\n throw new Error(\"effect has neither inlineable source nor a reference index\");\n }\n return emitEffectFn(ctx, effect.source);\n}\n\n/**\n * Pristine fallback delegate: declare `var __rfp_N=__rf[N].safeParse.bind(__rf[N]);`\n * in the preamble and return the variable name. Generated code must NEVER read\n * `__rf[N].safeParse` at parse time: `__zcMkv` installs the compiled safeParse as\n * an OWN property on the original schema object, and whenever `__rf[N]` is that\n * same object the read resolves to the compiled delegate itself — infinite\n * recursion (RangeError on every parse). The fallback entry and the __zcMkv\n * target ARE the same object in compile mode (schemaExpr is the compile()\n * argument identifier) and the CLI emitter ((__src_X as any).schema); in\n * autoDiscover mode they are two textually identical constructions that any\n * downstream CSE/dedup transform (babel-plugin-zod-hoist in a field incident)\n * collapses back into one. Capturing at IIFE evaluation — before the trailing\n * `return __zcMkv(...)` mutates anything — pins zod's own implementation; the\n * worst case under cross-validator merges is delegating to an equivalent\n * compiled validator (whose own delegates were captured even earlier), never\n * a cycle.\n */\nexport function emitRfDelegate(ctx: CodeGenContext, refIndex: number): string {\n const name = `__rfp_${refIndex}`;\n const decl = `var ${name}=__rf[${refIndex}].safeParse.bind(__rf[${refIndex}]);`;\n if (!ctx.preamble.includes(decl)) {\n ctx.preamble.push(decl);\n }\n return name;\n}\n\n/**\n * Identifier `generateIIFE` binds the retained Zod schema to, once per export.\n *\n * Compact delegation reaches the schema through this binding rather than\n * through `__rf[]`. Routing it through the array meant every compact validator\n * — the common case being one with no fallback refs at all — declared a\n * one-element `var __rf=[__zs];` whose only reads were `__rf[0]`, i.e. an array\n * allocation per compiled schema at module init to alias a binding that was\n * already in scope. Naming the schema directly also keeps the reference a\n * foldable constant instead of an element load (the same reason\n * {@link emitEffectCallable} aliases its `__rf[N]` into a preamble binding).\n */\nexport const RETAINED_SCHEMA_VAR = \"__zs\";\n\n/**\n * Capture the retained schema's pristine `safeParse` without allocating a bound\n * function. Declared in the preamble, which `generateIIFE` places after the\n * `__zs` binding and before the trailing `__zcMkv` call — so the capture is\n * zod's own implementation, never the compiled delegate that call installs\n * (see {@link emitRfDelegate} for the recursion this avoids).\n */\nexport function emitRetainedMethod(ctx: CodeGenContext): string {\n const name = \"__rfm_z\";\n const decl = `var ${name}=${RETAINED_SCHEMA_VAR}.safeParse;`;\n if (!ctx.preamble.includes(decl)) {\n ctx.preamble.push(decl);\n }\n return name;\n}\n\n/**\n * Is a RegExp built with these flags safe to share between validators?\n *\n * `g` and `y` make the object STATEFUL: `.test()` advances `lastIndex` and the\n * next call resumes from there. Generated code is already correct about this —\n * every flagged site emits a `lastIndex=0` reset first (see `lastIndexReset` in\n * schemas/string.ts, the only generator that passes flags through) — so pooling\n * them would in fact work today.\n *\n * They are held back anyway, because the pool changes what a future lapse\n * costs. A missing reset on a validator-local regex misbehaves inside one\n * export, deterministically. On a pooled one it misbehaves across exports, and\n * only in the order the module happens to evaluate them. Flagged patterns are\n * rare enough that the sharing is not worth buying that failure mode. Every\n * other flag (`i`, `m`, `s`, `u`, `v`, `d`) is pure configuration and pools like\n * any other constant.\n */\nfunction isPoolableRegex(flags: string | undefined): boolean {\n return flags === undefined || !/[gy]/.test(flags);\n}\n\n/**\n * Resolve a regex pattern to a runtime variable name.\n *\n * Three layers, widest first. Lean mode short-circuits well-known patterns to\n * virtual-module names, deduping them across the whole bundle. What is left is\n * pooled at FILE level when two or more validators build the identical RegExp —\n * the same bargain Sets get, and a larger one in practice: a repeated enum is\n * one `new Set([...])`, while a repeated `z.iso.datetime()` or shared\n * `.regex()` is a several-hundred-byte pattern plus a RegExp construction per\n * validator at module init. Anything still unique is cached and declared in the\n * per-IIFE preamble exactly once per pattern.\n */\nexport function emitRegex(\n ctx: CodeGenContext,\n prefix: string,\n pattern: string,\n flags?: string,\n): string {\n if (ctx.mode === \"lean\" && !flags) {\n const wellKnown = lookupWellKnownRegex(pattern);\n if (wellKnown !== null) {\n ctx.usedHelpers.add(wellKnown);\n return wellKnown;\n }\n }\n const cacheKey = flags ? `${flags}\\u0000${pattern}` : pattern;\n const cached = ctx.regexCache.get(cacheKey);\n if (cached) return cached;\n const flagsArg = flags ? `,${escapeString(flags)}` : \"\";\n // Flag-less patterns may carry a faster behavior-equivalent rewrite (a\n // well-known table entry, repeat unrolling, or both); the regex OBJECT uses\n // it while issue sites keep reporting the original pattern (see slowString).\n const testSource = flags ? null : fastTestSource(pattern);\n const initializer = `new RegExp(${escapeString(testSource ?? pattern)}${flagsArg})`;\n const localPrefix = `re_${prefix}`;\n const name = isPoolableRegex(flags)\n ? emitPooledConstant(ctx, \"Rx\", localPrefix, initializer)\n : emitConstant(ctx, localPrefix, initializer);\n ctx.regexCache.set(cacheKey, name);\n return name;\n}\n\n/**\n * Resolve the ORIGINAL `/source/flags` pattern string of a regex for issue\n * reporting. Only needed when emitRegex swapped in a faster equivalent test\n * pattern (the runtime regex's toString() would leak the rewrite). Lean mode\n * references the shared `<name>Src` virtual export so the original pattern\n * stays a single bundle-wide string; inline mode declares it once per IIFE.\n */\nexport function emitRegexSourceString(ctx: CodeGenContext, pattern: string): string {\n if (ctx.mode === \"lean\") {\n const srcName = wellKnownRegexSourceName(pattern);\n if (srcName !== null) {\n ctx.usedHelpers.add(srcName);\n return srcName;\n }\n }\n const cacheKey = `src\\u0000${pattern}`;\n const cached = ctx.regexCache.get(cacheKey);\n if (cached) return cached;\n const name = `__res_${ctx.counter++}`;\n ctx.preamble.push(`var ${name}=${escapeString(`/${pattern}/`)};`);\n ctx.regexCache.set(cacheKey, name);\n return name;\n}\n\n/**\n * Declare a constant value in the preamble and return its variable name,\n * reusing an earlier declaration of the SAME initializer.\n *\n * Value tables are reached from both halves of a validator — an enum's `Set`\n * from its fast check and again from its slow walk, a strict shape's key table\n * likewise — and repeat across sibling properties that share a value list. One\n * declaration per USE emitted the payload two or four times: measured 17% of a\n * 20-value enum schema's generated bytes, 16% for an object with two identical\n * enums. Keyed by initializer text, so only identical payloads collapse.\n */\nexport function emitConstant(ctx: CodeGenContext, prefix: string, initializer: string): string {\n ctx.valueCache ??= new Map();\n const cached = ctx.valueCache.get(initializer);\n if (cached !== undefined) return cached;\n const name = `__${prefix}_${ctx.counter++}`;\n ctx.preamble.push(`var ${name}=${initializer};`);\n ctx.valueCache.set(initializer, name);\n return name;\n}\n\n/**\n * Declare a poolable constant in the preamble and return its variable name —\n * unless the file pipeline has already decided to hoist this exact initializer\n * to module scope, in which case the shared name is returned and nothing is\n * declared locally.\n *\n * The pipeline learns which initializers repeat by running codegen once and\n * collecting what was reported here, so every poolable constant must route\n * through this function rather than calling {@link emitConstant} directly.\n */\nexport function emitPooledConstant(\n ctx: CodeGenContext,\n kind: ConstantKind,\n localPrefix: string,\n initializer: string,\n): string {\n const sharedName = ctx.sharedConstantNames?.get(initializer);\n if (sharedName !== undefined) return sharedName;\n const name = emitConstant(ctx, localPrefix, initializer);\n ctx.onConstant?.({ kind, name, initializer });\n return name;\n}\n\n/** Declare a `new Set([...])` in the preamble and return its variable name. */\nexport function emitSet(ctx: CodeGenContext, prefix: string, values: readonly unknown[]): string {\n const initializer = `new Set(${JSON.stringify([...values])})`;\n return emitPooledConstant(ctx, \"Set\", `set_${prefix}`, initializer);\n}\n\n/**\n * Shape-key count at or below which the unknown-key pass compares with an\n * inline `===` chain rather than a hashed lookup.\n *\n * This is deliberately NOT {@link ENUM_INLINE_THRESHOLD}: the two look alike but\n * are different workloads. An enum compares schema literals against arbitrary\n * INPUT strings, which may be long, share prefixes, and are not necessarily\n * internalized — so a hashed set earns its keep quickly. A shape-key test\n * compares them against keys arriving from `for-in`, i.e. the object's own\n * internalized key strings, so every arm of the chain is a pointer compare that\n * V8 predicts perfectly, while `table[k]` / `set.has(k)` pays a string hash and\n * probe per key.\n *\n * Measured over a strict object's for-in pass (JSON-parsed input, 8 rotated\n * shapes), `===` chain vs the previous `{k:1}` table: 3.7x at 6 keys, 3.8x at\n * 10, 3.1x at 20, 3.1x at 48 — the chain still leads at 64 (283 ns vs 690) and\n * only loses past ~96, where `Set.has` (not the table, which never wins at any\n * size) takes over. 64 sits below that crossover and above any realistic shape.\n */\nexport const KEY_MEMBERSHIP_INLINE_THRESHOLD = 64;\n\n/**\n * Boolean membership test for one key variable against a fixed key list.\n * Empty list recognizes nothing.\n *\n * `Set.has` is the large-shape fallback rather than a `{key:1}` object table:\n * the table is also `__proto__`-hostile (an own `__proto__` key cannot be set\n * by an object literal, so that key would silently read as unknown), which the\n * Set has no trouble with.\n */\nexport function keyMembershipTest(\n ctx: CodeGenContext,\n keys: readonly string[],\n keyVar: string,\n): string {\n if (keys.length === 0) return \"false\";\n if (keys.length <= KEY_MEMBERSHIP_INLINE_THRESHOLD) {\n return keys.map((k) => `${keyVar}===${escapeString(k)}`).join(\"||\");\n }\n return `${emitSet(ctx, \"ks\", keys)}.has(${keyVar})`;\n}\n\n/**\n * Enum values at or below this count use inline === checks instead of Set.has().\n * Measured on V8: for ≤5 values, an === chain beats Set.has by up to ~3x with\n * realistic (distinct-prefix, JSON-parsed) values — V8 internalizes strings on\n * successful comparison, making subsequent arms pointer-equality — and is no\n * worse than Set.has even with adversarial shared-prefix values.\n */\nexport const ENUM_INLINE_THRESHOLD = 5;\n\nconst CHECK_PRIORITY: Record<string, number> = {\n // Cheapest: length/size comparisons (O(1))\n min_length: 10,\n max_length: 11,\n length_equals: 12,\n min_size: 13,\n max_size: 14,\n // Number format checks (comparison + bitwise)\n number_format: 15,\n // Range comparisons\n greater_than: 20,\n less_than: 21,\n bigint_greater_than: 20,\n bigint_less_than: 21,\n date_greater_than: 22,\n date_less_than: 23,\n // Modulo\n multiple_of: 30,\n bigint_multiple_of: 30,\n // String prefix/suffix (O(prefix/suffix length))\n starts_with: 40,\n ends_with: 41,\n // String search (O(n·m) worst case)\n includes: 42,\n // Regex (most expensive)\n string_format: 50,\n};\n\nexport function escapeString(s: string | number): string {\n return JSON.stringify(s);\n}\n\n/** The {@link LiteralValue}s that {@link literalToJs} can spell. */\nexport type SourceFormLiteral = string | number | boolean | null | bigint | undefined;\n\n/**\n * Can {@link literalToJs} render this value as JS source that strict-equals it?\n *\n * Total by construction — it NAMES the value kinds that have a source form\n * rather than excluding the ones that don't, so every reference value falls out\n * on the false side. That matters because `literalToJs` used to end in a bare\n * `JSON.stringify`, which does not fail loudly on the values it cannot spell:\n * for a symbol it RETURNS `undefined` (the value, not a string), so\n * `z.literal(sym)` compiled to the comparison `x===undefined` — rejecting the\n * symbol it was built from and accepting `undefined`. An object is mis-rendered\n * the other way: `{}` stringifies to `\"{}\"`, and `x==={}` is never true, so the\n * very object the schema was built from was rejected. Both take the runtime\n * membership path instead (see the literal generator).\n */\nexport function hasSourceForm(v: LiteralValue): v is SourceFormLiteral {\n if (v === null) return true;\n const t = typeof v;\n return t === \"string\" || t === \"number\" || t === \"boolean\" || t === \"bigint\" || t === \"undefined\";\n}\n\n/**\n * JS source for a primitive literal value (literal schemas, discriminator\n * case labels). JSON.stringify covers string/number/boolean/null; bigint\n * needs the `n` suffix (JSON.stringify throws and String(5n) renders a\n * number literal that never strict-equals a bigint); undefined isn't JSON.\n *\n * The parameter type is deliberately NARROWER than {@link LiteralValue}: every\n * caller must first prove its value is spellable with {@link hasSourceForm}.\n */\nexport function literalToJs(v: SourceFormLiteral): string {\n if (typeof v === \"bigint\") return `${v}n`;\n if (v === undefined) return \"undefined\";\n // JSON.stringify maps NaN/±Infinity to \"null\"; emit them as JS expressions so a\n // non-finite numeric literal round-trips (z.literal(Infinity) must compare\n // against Infinity, not null). String(NaN)=\"NaN\", String(Infinity)=\"Infinity\",\n // String(-Infinity)=\"-Infinity\" — all valid JS that evaluate to the value.\n if (typeof v === \"number\" && !Number.isFinite(v)) return String(v);\n return JSON.stringify(v);\n}\n\n/**\n * Helpers that generated code invokes through `Function.prototype.call`, and\n * which therefore must be aliased into a module-local binding in lean mode.\n *\n * V8 folds a local `const` callee into a constant and inlines straight through\n * `x.call(...)`; an IMPORTED binding is a cell it will not fold, so the same\n * expression stays a generic property load plus a generic call — measured 4.5x\n * (5 keys) to 6.5x (20 keys) slower on the record fast path, 35.9 ns vs 7.2 ns\n * for a 5-key record. Aliasing the import into the IIFE recovers all of it\n * (7.4 ns). A DIRECT call to an imported function (`__zcFsr(v,s)`) is not\n * penalized — measured identical — and neither is an imported RegExp receiver,\n * so only the `.call` sites are listed here.\n */\nconst CALL_INVOKED_HELPERS: ReadonlySet<string> = new Set([\"__zcHop\"]);\n\n/**\n * Reference a shared runtime helper (e.g. __zcFsr) from generated code.\n * Lean mode: registers it for the `virtual:zod-compiler/runtime` import.\n * Inline mode: declares it once in the per-IIFE preamble.\n */\nexport function emitRuntimeHelper(ctx: CodeGenContext, name: string, decl: string): string {\n if (ctx.mode === \"lean\") {\n ctx.usedHelpers.add(name);\n if (CALL_INVOKED_HELPERS.has(name)) return emitConstant(ctx, \"lh\", name);\n } else if (!ctx.preamble.includes(decl)) {\n ctx.preamble.push(decl);\n }\n return name;\n}\n\n/**\n * Extend a path expression with one or more scalar segment expressions\n * (escaped string literals, numeric literals, or loop-variable names).\n *\n * Path expressions are only ever composed by these helpers starting from the\n * `[]` root, so any path that looks like an array literal IS one — the new\n * segment is spliced in to keep issue paths a single array allocation\n * (`[\"data\",\"items\",__i_7]`) instead of an allocation per nesting level\n * (`[\"data\"].concat(\"items\").concat(__i_7)`). Opaque expressions fall back\n * to .concat().\n */\nexport function extendPath(parentPath: string, segExpr: string): string {\n if (parentPath === \"[]\") return `[${segExpr}]`;\n if (parentPath.startsWith(\"[\") && parentPath.endsWith(\"]\")) {\n return `${parentPath.slice(0, -1)},${segExpr}]`;\n }\n return `${parentPath}.concat(${segExpr})`;\n}\n\n/** Extend a path expression with a static string key. */\nexport function extendStaticPath(parentPath: string, key: string): string {\n return extendPath(parentPath, escapeString(key));\n}\n\n/** Extend a path expression with a numeric index. */\nexport function extendStaticPathIndex(parentPath: string, index: number): string {\n return extendPath(parentPath, String(index));\n}\n\n/**\n * A superRefine callback receives zod's payload, whose `value` is public,\n * typed, writable API ($RefinementCtx extends ParsePayload) — so any node\n * carrying one MAY rewrite its value and must be treated as mutating. Which\n * callbacks actually do is undecidable here; the emitted fast check settles it\n * at runtime by refusing when the value changed (see ZC_SR_OK_DECL), so a\n * non-mutating callback still exits through the fast path.\n */\nfunction hasSuperRefine(checks: readonly { kind: string }[] | undefined): boolean {\n return checks !== undefined && checks.some((c) => c.kind === \"super_refine_effect\");\n}\n\n/**\n * Check if a SchemaIR tree produces output that is not the input itself —\n * either value-mutating operations (coerce, default, catch, overwrite) that\n * write back to the input expression, or a strip object that rebuilds a fresh\n * object from its known keys. Used by container generators to decide whether to\n * clone (so the rebuilt/mutated value never writes through to the caller's\n * input), by generateValidator to keep such schemas off the by-reference fast\n * path, and by the shared-walk dedup + intersection extractor to exclude them.\n */\n/**\n * Can this tuple's output differ from a SHORT input it accepts?\n *\n * $ZodTuple runs every item even when the input is shorter, and\n * `handleTupleResults` writes each result back (`final.value[i] = r.value`)\n * unless the slot is at or past `optoutStart` and on the \"optional\" rung of\n * `optin` — that one ends the output where the input ended. Every other absent\n * slot lands in the output: below `optoutStart` as whatever the item made of\n * `undefined` (an own `undefined` for `z.string().optional()`, \"c\" for\n * `.catch(\"c\")`), at or past it as a substituted value (a \"defaulted\" item).\n * `z.tuple([z.string(), z.number().default(1)])` therefore answers `[\"x\"]` with\n * `[\"x\", 1]`, length 2, and `z.tuple([z.any()]).rest(z.number())` answers `[]`\n * with `[undefined]`. So the tuple is a mutating node — its output is not its\n * input — exactly when some slot that CAN be absent is not a truncating one.\n * Without a rest element only slots from `optStart` on can be absent (a shorter\n * input is `too_small`); with one, any slot can.\n */\nexport function tupleRewritesShortInput(ir: SchemaIR & { type: \"tuple\" }): boolean {\n const len = ir.items.length;\n const optoutStart = ir.optoutStart ?? len;\n const optionalIn = ir.optionalIn ?? [];\n for (let i = ir.rest === null ? ir.optStart : 0; i < len; i++) {\n if (i < optoutStart || !optionalIn.includes(i)) return true;\n }\n return false;\n}\n\n/**\n * Does this node hand back a container that could still carry an own\n * `__proto__` zod would have stripped?\n *\n * True for the shapes whose output IS their input: a LOOSE or CATCHALL object\n * (a stripping one rebuilds from the declared keys, and `parsedProperties`\n * already drops the key) and any record. See {@link ZC_PROTO_SCRUB_DECL} for\n * what zod does and why the difference matters.\n */\nexport function needsProtoScrub(ir: SchemaIR): boolean {\n // Every record: the walk skips `__proto__` but the container is handed back.\n if (ir.type === \"record\") return true;\n if (ir.type !== \"object\") return false;\n // A STRIPPING object rebuilds from the declared keys and `parsedProperties`\n // drops `__proto__`, so its output can never carry one.\n if (ir.stripUnknownKeys === true) return false;\n // A STRICT object reports an undeclared `__proto__` as an unrecognized key\n // and fails, so the only way one reaches its output is by being DECLARED —\n // which zod's shape loop strips while still counting the name as recognized.\n if (ir.strict === true) return Object.hasOwn(ir.properties, \"__proto__\");\n // Loose or catchall: an undeclared `__proto__` rides through untouched.\n return true;\n}\n\nexport function hasMutation(ir: SchemaIR): boolean {\n switch (ir.type) {\n case \"string\":\n // url checks trim (and optionally normalize) the value; overwrite\n // effects (.trim(), .toLowerCase()) rewrite it.\n return (\n ir.coerce === true ||\n hasSuperRefine(ir.checks) ||\n ir.checks.some(\n (c) =>\n c.kind === \"overwrite_effect\" || (c.kind === \"string_format\" && c.format === \"url\"),\n )\n );\n case \"number\":\n return ir.coerce === true || hasSuperRefine(ir.checks);\n case \"boolean\":\n case \"bigint\":\n case \"date\":\n return ir.coerce === true;\n case \"default\":\n case \"catch\":\n case \"effect\":\n case \"fallback\":\n case \"stringBool\":\n return true;\n case \"object\":\n // A strip object produces a FRESH output (only the declared keys), so it\n // mutates: parents must clone before it writes back, it never takes the\n // by-reference fast path, and intersections of strip objects delegate to\n // zod (see extractIntersection's hasMutation guard) — matching zod's\n // parse-both-sides-then-merge semantics instead of over-stripping.\n return (\n ir.stripUnknownKeys === true ||\n hasSuperRefine(ir.checks) ||\n (ir.catchall !== undefined && hasMutation(ir.catchall)) ||\n Object.values(ir.properties).some((p) => hasMutation(p))\n );\n case \"array\":\n return hasSuperRefine(ir.checks) || hasMutation(ir.element);\n case \"tuple\":\n return (\n ir.items.some(hasMutation) ||\n (ir.rest !== null && hasMutation(ir.rest)) ||\n tupleRewritesShortInput(ir)\n );\n case \"record\":\n return hasMutation(ir.valueType);\n // A freezing readonly produces a value that is not its input, exactly as a\n // strip object does — so it must never take a by-reference shortcut.\n case \"readonly\":\n return ir.freeze === true || hasMutation(ir.inner);\n case \"optional\":\n case \"nullable\":\n case \"recursionTarget\":\n case \"zodDelegate\":\n return hasMutation(ir.inner);\n case \"union\":\n case \"discriminatedUnion\":\n return ir.options.some(hasMutation);\n case \"intersection\":\n return hasMutation(ir.left) || hasMutation(ir.right);\n case \"pipe\":\n return hasMutation(ir.in) || hasMutation(ir.out);\n case \"set\":\n return hasMutation(ir.valueType);\n case \"map\":\n return hasMutation(ir.keyType) || hasMutation(ir.valueType);\n case \"file\":\n return false;\n default:\n return false;\n }\n}\n\n/**\n * Is a defaulted property's key guaranteed to appear in the stripped output?\n *\n * Distinct from {@link rejectsUndefined}, which asks whether `undefined` is\n * REJECTED — a `.default()` accepts it and yet still produces a defined value, so\n * only this question earns the key a slot in the output object literal. The two\n * answers coincide everywhere else.\n *\n * Sound for both branches of a default: the substituted value is defined\n * (`alwaysDefined`, checked against the schema at extraction time), and the inner\n * branch runs only when `input[key] !== undefined`, which implies `key in input`\n * — so zod's presence test keeps the key whatever the inner produced.\n */\nexport function outputAlwaysDefined(ir: SchemaIR): boolean {\n return ir.type === \"default\" ? ir.alwaysDefined === true : rejectsUndefined(ir);\n}\n\n/**\n * Does this schema reject `undefined` outright?\n *\n * Read as \"does an absent key already fail on its own\" by the object extractor,\n * which otherwise has to report the absence itself (zod's `nonoptional`\n * issue), and as \"is a fixed-length rebuild exact\" by the tuple build.\n * Conservative: anything that might accept, produce, or default to `undefined`\n * answers false.\n */\nexport function rejectsUndefined(ir: SchemaIR): boolean {\n switch (ir.type) {\n // Coercion turns undefined into a value (`String(undefined)`), so a\n // coercing primitive is NOT a rejector.\n case \"string\":\n case \"number\":\n case \"boolean\":\n case \"bigint\":\n case \"date\":\n return ir.coerce !== true;\n case \"symbol\":\n case \"null\":\n case \"nan\":\n case \"never\":\n case \"enum\":\n case \"object\":\n case \"array\":\n case \"tuple\":\n case \"record\":\n case \"set\":\n case \"map\":\n case \"file\":\n case \"templateLiteral\":\n case \"discriminatedUnion\":\n case \"stringBool\":\n return true;\n case \"literal\":\n return !ir.values.includes(undefined);\n case \"union\":\n return ir.options.every(rejectsUndefined);\n case \"intersection\":\n return rejectsUndefined(ir.left) || rejectsUndefined(ir.right);\n case \"nullable\":\n case \"readonly\":\n case \"recursionTarget\":\n case \"zodDelegate\":\n return rejectsUndefined(ir.inner);\n default:\n // optional / any / unknown / undefined / void / default / catch /\n // fallback / effect / pipe / recursiveRef — each can yield undefined,\n // or is opaque enough that we must not assume otherwise.\n return false;\n }\n}\n\n/**\n * Sort comparator for CheckIR: cheapest/most-discriminating checks first.\n * Used by fast-path generators after filtering out refine_effect entries.\n */\nexport function checkPriority(\n a: CheckIR | BigIntCheckIR | DateCheckIR | SetCheckIR,\n b: CheckIR | BigIntCheckIR | DateCheckIR | SetCheckIR,\n): number {\n return (CHECK_PRIORITY[a.kind] ?? 99) - (CHECK_PRIORITY[b.kind] ?? 99);\n}\n"],"mappings":"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"}
@@ -16,23 +16,17 @@ interface IssueGen {
16
16
  type Origin = string | {
17
17
  expr: string;
18
18
  };
19
- declare function tooSmall(g: IssueGen, minimum: string | number, origin: Origin,
20
- /**
21
- * `"omit"` leaves the key OFF the issue entirely — not the same as `false`.
22
- * $ZodTuple's under-length branch pushes a bare
23
- * `{ code: "too_small", minimum: items.length }` (its over-length sibling in
24
- * the very same ternary spells out `inclusive: true`), so a tuple issue
25
- * carrying `inclusive: false` has a field zod never wrote. Every check-created
26
- * size issue (array/string/set/number/bigint/date min) does carry it, `false`
27
- * included — that is what `.gt()`/`.lt()` report.
28
- */
29
- inclusive: boolean | "omit", options?: {
19
+ declare function tooSmall(g: IssueGen, minimum: string | number, origin: Origin, inclusive: boolean, options?: {
30
20
  exact?: boolean;
31
21
  input?: string;
32
22
  path?: string;
33
23
  message?: string | undefined;
34
- /** Set for node-level issues (tuple length) where schema error applies. */
35
- useTypeMsg?: boolean;
24
+ /**
25
+ * `"tuple"` selects $ZodTuple's under-length key order — `code, minimum,
26
+ * inclusive, origin` — where a check-created size issue leads with `origin`.
27
+ * Same shape as its over-length sibling in {@link tooBig}.
28
+ */
29
+ layout?: "tuple";
36
30
  /** Emit the union-abort marker; see {@link abortsProp}. */
37
31
  aborts?: boolean;
38
32
  }): string;
@@ -41,8 +35,6 @@ declare function tooBig(g: IssueGen, maximum: string | number, origin: Origin, i
41
35
  input?: string;
42
36
  path?: string;
43
37
  message?: string | undefined;
44
- /** Set for node-level issues (tuple length) where schema error applies. */
45
- useTypeMsg?: boolean;
46
38
  /**
47
39
  * `"tuple"` selects $ZodTuple's over-length key order — `code, maximum,
48
40
  * inclusive, origin` — where a check-created size issue leads with `origin`.
@@ -67,11 +59,18 @@ declare function invalidType(g: IssueGen, expected: string, options?: {
67
59
  */
68
60
  extraBeforeCode?: boolean;
69
61
  /**
70
- * Lead with `code` instead of `expected`. Only `$ZodDiscriminatedUnion`'s
71
- * non-object guard writes the issue that way; every other schema in zod puts
72
- * `expected` first.
62
+ * Lead with `code` instead of `expected`. `$ZodDiscriminatedUnion`'s
63
+ * non-object guard and `$ZodObject`'s absent-required-key issue
64
+ * (`expected: "nonoptional"`) write the issue that way; every other schema
65
+ * in zod puts `expected` first.
73
66
  */
74
67
  codeFirst?: boolean;
68
+ /**
69
+ * Set to false for the one `invalid_type` zod pushes WITHOUT an `inst`:
70
+ * the object's `nonoptional` issue for an absent required key, which no
71
+ * schema-level `error` reaches.
72
+ */
73
+ useTypeMsg?: boolean;
75
74
  }): string;
76
75
  declare function invalidFormat(g: IssueGen, format: string | {
77
76
  expr: string;
@@ -112,8 +111,6 @@ declare function invalidValue(g: IssueGen, valuesExpr: string, options?: {
112
111
  */
113
112
  extra?: string | undefined;
114
113
  message?: string | undefined;
115
- /** Set to false for check-level issues (file mime) where schema error does not apply. */
116
- useTypeMsg?: boolean;
117
114
  }): string;
118
115
  //#endregion
119
116
  export { invalidFormat, invalidType, invalidValue, tooBig, tooSmall, unrecognizedKeys };
@@ -1 +1 @@
1
- {"version":3,"file":"emit-issue.d.ts","names":[],"sources":["../../../src/core/codegen/emit-issue.ts"],"mappings":";;;UA6CU;WACC;WACA;WACA;WACA,KAAK;WACL;;;;;;;KAqDN;EAAoB;;iBAMT,SACd,GAAG,UACH,0BACA,QAAQ,QAUR;;;;;;;;;;AAAA,6BACA;EACE;EACA;EACA;EACA;;EAEA;;EAEA;;iBAgDY,OACd,GAAG,UACH,0BACA,QAAQ,QACR,oBACA;EACE;EACA;EACA;EACA;;EAEA;;;;;;;;EAQA;;EAEA;;iBA8CY,YACd,GAAG,UACH,kBACA;EACE;EACA;EACA;EACA;;;;;;;EAOA;;;;;;EAMA;;iBAqBY,cACd,GAAG,UACH;EAAmB;GACnB;EACE;EACA;;;;;;;EAOA;;;;;;;EAOA;EACA;;iBAyBY,iBACd,GAAG,UACH,kBACA;EAAY;EAAgB;EAAe;;iBAgB7B,aACd,GAAG,UACH,oBACA;EACE;EACA;;;;;;;;;EASA;EACA;;EAEA"}
1
+ {"version":3,"file":"emit-issue.d.ts","names":[],"sources":["../../../src/core/codegen/emit-issue.ts"],"mappings":";;;UA4CU;WACC;WACA;WACA;WACA,KAAK;WACL;;;;;;;KAiDN;EAAoB;;iBAMT,SACd,GAAG,UACH,0BACA,QAAQ,QACR,oBACA;EACE;EACA;EACA;EACA;;;;;;EAMA;;EAEA;;iBA8CY,OACd,GAAG,UACH,0BACA,QAAQ,QACR,oBACA;EACE;EACA;EACA;EACA;;;;;;;;EAQA;;EAEA;;iBA8CY,YACd,GAAG,UACH,kBACA;EACE;EACA;EACA;EACA;;;;;;;EAOA;;;;;;;EAOA;;;;;;EAMA;;iBAqBY,cACd,GAAG,UACH;EAAmB;GACnB;EACE;EACA;;;;;;;EAOA;;;;;;;EAOA;EACA;;iBAwBY,iBACd,GAAG,UACH,kBACA;EAAY;EAAgB;EAAe;;iBAe7B,aACd,GAAG,UACH,oBACA;EACE;EACA;;;;;;;;;EASA;EACA"}
@@ -3,9 +3,9 @@ import { escapeString } from "./context.js";
3
3
  function pushIssue(g, body) {
4
4
  return `${g.issues}.push(${body});`;
5
5
  }
6
- /** Resolve the effective static message for an issue site. */
7
- function resolveMessage(g, explicit, useTypeMsg) {
8
- return explicit ?? (useTypeMsg ? g.typeMsg : void 0);
6
+ /** Resolve the effective static message for an issue site: the check's own message, else the owning schema's. */
7
+ function resolveMessage(g, explicit) {
8
+ return explicit ?? g.typeMsg;
9
9
  }
10
10
  /** `,message:"..."` fragment for inline object literals ("" when no message). */
11
11
  /**
@@ -43,28 +43,28 @@ function tooSmall(g, minimum, origin, inclusive, options) {
43
43
  const input = options?.input ?? g.input;
44
44
  const path = options?.path ?? g.path;
45
45
  const exact = options?.exact === true;
46
- const m = resolveMessage(g, options?.message, options?.useTypeMsg === true);
46
+ const m = resolveMessage(g, options?.message);
47
47
  if (g.ctx.mode === "lean") {
48
48
  if (exact) {
49
49
  g.ctx.usedHelpers.add("__zcTSx");
50
50
  return pushIssue(g, `__zcTSx(${minimum},${originExpr(origin)},${input},${path}${messageArg(m)})`);
51
51
  }
52
- if (inclusive === "omit") {
53
- g.ctx.usedHelpers.add("__zcTSn");
54
- return pushIssue(g, `__zcTSn(${minimum},${originExpr(origin)},${input},${path}${messageArg(m)})`);
52
+ if (options?.layout === "tuple") {
53
+ g.ctx.usedHelpers.add("__zcTSt");
54
+ return pushIssue(g, `__zcTSt(${minimum},${originExpr(origin)},${input},${path}${messageArg(m)})`);
55
55
  }
56
56
  g.ctx.usedHelpers.add("__zcTS");
57
57
  return pushIssue(g, `__zcTS(${minimum},${originExpr(origin)},${inclusive},${input},${path}${messageArg(m)})`);
58
58
  }
59
59
  if (exact) return pushIssue(g, `{origin:${originExpr(origin)},code:"too_small",minimum:${minimum},inclusive:true,exact:true,input:${input},path:${path}${messageProp(m)}}`);
60
- if (inclusive === "omit") return pushIssue(g, `{code:"too_small",minimum:${minimum},origin:${originExpr(origin)},input:${input},path:${path}${messageProp(m)}${abortsProp(options?.aborts)}}`);
60
+ if (options?.layout === "tuple") return pushIssue(g, `{code:"too_small",minimum:${minimum},inclusive:${inclusive},origin:${originExpr(origin)},input:${input},path:${path}${messageProp(m)}${abortsProp(options?.aborts)}}`);
61
61
  return pushIssue(g, `{origin:${originExpr(origin)},code:"too_small",minimum:${minimum},inclusive:${inclusive},input:${input},path:${path}${messageProp(m)}}`);
62
62
  }
63
63
  function tooBig(g, maximum, origin, inclusive, options) {
64
64
  const input = options?.input ?? g.input;
65
65
  const path = options?.path ?? g.path;
66
66
  const exact = options?.exact === true;
67
- const m = resolveMessage(g, options?.message, options?.useTypeMsg === true);
67
+ const m = resolveMessage(g, options?.message);
68
68
  if (g.ctx.mode === "lean") {
69
69
  if (exact) {
70
70
  g.ctx.usedHelpers.add("__zcTBx");
@@ -84,7 +84,7 @@ function tooBig(g, maximum, origin, inclusive, options) {
84
84
  function invalidType(g, expected, options) {
85
85
  const input = options?.input ?? g.input;
86
86
  const path = options?.path ?? g.path;
87
- const m = resolveMessage(g, options?.message, true);
87
+ const m = options?.useTypeMsg === false ? options?.message : resolveMessage(g, options?.message);
88
88
  if (g.ctx.mode === "lean" && !options?.extra) {
89
89
  const helper = options?.codeFirst ? "__zcITc" : "__zcIT";
90
90
  g.ctx.usedHelpers.add(helper);
@@ -97,7 +97,7 @@ function invalidFormat(g, format, options) {
97
97
  const input = options?.input ?? g.input;
98
98
  const path = options?.path ?? g.path;
99
99
  const formatExpr = typeof format === "string" ? escapeString(format) : format.expr;
100
- const m = options?.message;
100
+ const m = resolveMessage(g, options?.message);
101
101
  if (g.ctx.mode === "lean") {
102
102
  g.ctx.usedHelpers.add("__zcIF");
103
103
  return pushIssue(g, `__zcIF(${options?.origin === void 0 ? "undefined" : escapeString(options.origin)},${formatExpr},${input},${path}${options?.extra ? `,{${options.extra}}` : m !== void 0 ? ",undefined" : ""}${messageArg(m)})`);
@@ -107,7 +107,7 @@ function invalidFormat(g, format, options) {
107
107
  function unrecognizedKeys(g, keysExpr, options) {
108
108
  const input = options?.input ?? g.input;
109
109
  const path = options?.path ?? g.path;
110
- const m = resolveMessage(g, options?.message, true);
110
+ const m = resolveMessage(g, options?.message);
111
111
  if (g.ctx.mode === "lean") {
112
112
  g.ctx.usedHelpers.add("__zcUK");
113
113
  return pushIssue(g, `__zcUK(${keysExpr},${input},${path}${messageArg(m)})`);
@@ -117,7 +117,7 @@ function unrecognizedKeys(g, keysExpr, options) {
117
117
  function invalidValue(g, valuesExpr, options) {
118
118
  const input = options?.input ?? g.input;
119
119
  const path = options?.path ?? g.path;
120
- const m = resolveMessage(g, options?.message, options?.useTypeMsg !== false);
120
+ const m = resolveMessage(g, options?.message);
121
121
  if (g.ctx.mode === "lean") {
122
122
  g.ctx.usedHelpers.add("__zcIV");
123
123
  return pushIssue(g, `__zcIV(${valuesExpr},${input},${path}${options?.extra ? `,{${options.extra}}` : m !== void 0 ? ",undefined" : ""}${messageArg(m)})`);
@@ -1 +1 @@
1
- {"version":3,"file":"emit-issue.js","names":[],"sources":["../../../src/core/codegen/emit-issue.ts"],"sourcesContent":["/**\n * Issue object emit helpers.\n *\n * Each helper returns a string of the form `_e.push(...)` (or equivalent on a\n * caller-supplied issues variable). Two emit modes:\n *\n * - inline (CLI .compiled.ts): emits the literal `{code:\"too_small\",...}` object.\n * - lean (unplugin): emits a call like `__zcTS(min,origin,inclusive,input,path)`\n * and registers the helper in `ctx.usedHelpers` so the transform layer can\n * construct the corresponding `import` from `\"virtual:zod-compiler/runtime\"`.\n *\n * Centralizing all issue emission here means schema codegen files don't carry\n * the inline/lean branching; they just call `tooSmall(g, ...)` etc.\n *\n * Message resolution mirrors Zod, which consults the error map of the\n * INSTANCE that created the issue:\n * - check-created issues (min/max/format/refine): per-check message only\n * (options.message, extracted from `.min(3, \"msg\")`)\n * - schema-created issues (invalid_type, enum/literal invalid_value, tuple\n * length, invalid_union, invalid_key): schema-level message (g.typeMsg,\n * from `z.string({ error: \"msg\" })`)\n * Helpers default per the most common call-site kind; size-issue emitters\n * accept `useTypeMsg: true` for node-level uses (tuple), and invalidValue\n * accepts `useTypeMsg: false` for check-level uses (file mime).\n * When no message lands, the __zcFin finalizer applies the locale default.\n *\n * KEY ORDER MATTERS, and is reproduced from zod issue by issue. `ZodError`\n * builds its `message` as `JSON.stringify(issues, …, 2)`, so insertion order is\n * printed text in a property consumers log, snapshot and serialize — an issue\n * carrying the right fields in the wrong order still renders a different error.\n * zod's own orders are irregular (`too_small` leads with `origin` from a check\n * but with `code` from a tuple's length branch; `invalid_format` carries\n * `origin` for a pattern check and omits it for `z.url()`), so each emitter\n * below mirrors one `payload.issues.push({…})` rather than a house style.\n *\n * `message` is written LAST everywhere, after `path`: zod never puts it in the\n * literal — `finalizeIssue` assigns `full.message` afterwards — and that holds\n * for a baked custom message too, since the custom error map is consulted from\n * inside that same assignment.\n */\n\nimport type { CodeGenContext } from \"./context.js\";\nimport { escapeString } from \"./context.js\";\n\n/** Common slim slice of SlowGen + FastGen needed for issue emission. */\ninterface IssueGen {\n readonly issues: string;\n readonly input: string;\n readonly path: string;\n readonly ctx: CodeGenContext;\n readonly typeMsg?: string | undefined;\n}\n\nfunction pushIssue(g: IssueGen, body: string): string {\n return `${g.issues}.push(${body});`;\n}\n\n/** Resolve the effective static message for an issue site. */\nfunction resolveMessage(\n g: IssueGen,\n explicit: string | undefined,\n useTypeMsg: boolean,\n): string | undefined {\n return explicit ?? (useTypeMsg ? g.typeMsg : undefined);\n}\n\n/** `,message:\"...\"` fragment for inline object literals (\"\" when no message). */\n/**\n * `,continue:false` — the marker that makes an issue ABORT its union option.\n *\n * zod prunes union options with `util.aborted`, which asks whether any issue has\n * `continue !== true`. Compiled issues carry no `continue` at all, so the union\n * generator classifies by CODE instead — sound for every issue a schema node\n * raises (`invalid_type`, `unrecognized_keys`, …) but blind to the one\n * exception: `$ZodTuple`'s length branch pushes a `too_small`/`too_big` from the\n * NODE, with no `continue`, where the identically-coded issue from an\n * `.min()`/`.max()` CHECK carries `continue: true` and does not abort. Without\n * this, `z.union([z.tuple([a, b]), z.number()])` handed `[]` surfaced the\n * tuple's `too_small` directly instead of wrapping both options in an\n * `invalid_union`.\n *\n * Stripped during finalization exactly as zod strips its own (`delete\n * full.continue`), so it never reaches a reported issue — see FAIL_CLASS_DECL\n * and ZC_FZ_DECL.\n */\nfunction abortsProp(aborts: boolean | undefined): string {\n return aborts === true ? \",continue:false\" : \"\";\n}\n\nfunction messageProp(m: string | undefined): string {\n return m === undefined ? \"\" : `,message:${escapeString(m)}`;\n}\n\n/** `,\"...\"` trailing argument fragment for lean factory calls (\"\" when no message). */\nfunction messageArg(m: string | undefined): string {\n return m === undefined ? \"\" : `,${escapeString(m)}`;\n}\n\n/**\n * `origin` as source: a bare string is a literal (`\"string\"`), `{ expr }` is an\n * expression evaluated at runtime — what a `when`-gated length/size check needs,\n * since zod derives its origin from the INPUT (`getLengthableOrigin`).\n */\ntype Origin = string | { expr: string };\n\nfunction originExpr(origin: Origin): string {\n return typeof origin === \"string\" ? escapeString(origin) : origin.expr;\n}\n\nexport function tooSmall(\n g: IssueGen,\n minimum: string | number,\n origin: Origin,\n /**\n * `\"omit\"` leaves the key OFF the issue entirely — not the same as `false`.\n * $ZodTuple's under-length branch pushes a bare\n * `{ code: \"too_small\", minimum: items.length }` (its over-length sibling in\n * the very same ternary spells out `inclusive: true`), so a tuple issue\n * carrying `inclusive: false` has a field zod never wrote. Every check-created\n * size issue (array/string/set/number/bigint/date min) does carry it, `false`\n * included — that is what `.gt()`/`.lt()` report.\n */\n inclusive: boolean | \"omit\",\n options?: {\n exact?: boolean;\n input?: string;\n path?: string;\n message?: string | undefined;\n /** Set for node-level issues (tuple length) where schema error applies. */\n useTypeMsg?: boolean;\n /** Emit the union-abort marker; see {@link abortsProp}. */\n aborts?: boolean;\n },\n): string {\n const input = options?.input ?? g.input;\n const path = options?.path ?? g.path;\n const exact = options?.exact === true;\n const m = resolveMessage(g, options?.message, options?.useTypeMsg === true);\n if (g.ctx.mode === \"lean\") {\n if (exact) {\n g.ctx.usedHelpers.add(\"__zcTSx\");\n return pushIssue(\n g,\n `__zcTSx(${minimum},${originExpr(origin)},${input},${path}${messageArg(m)})`,\n );\n }\n if (inclusive === \"omit\") {\n g.ctx.usedHelpers.add(\"__zcTSn\");\n return pushIssue(\n g,\n `__zcTSn(${minimum},${originExpr(origin)},${input},${path}${messageArg(m)})`,\n );\n }\n g.ctx.usedHelpers.add(\"__zcTS\");\n return pushIssue(\n g,\n `__zcTS(${minimum},${originExpr(origin)},${inclusive},${input},${path}${messageArg(m)})`,\n );\n }\n if (exact) {\n return pushIssue(\n g,\n `{origin:${originExpr(origin)},code:\"too_small\",minimum:${minimum},inclusive:true,exact:true,input:${input},path:${path}${messageProp(m)}}`,\n );\n }\n // The tuple's under-length branch is the only caller that omits `inclusive`,\n // and it also puts `origin` last — see __zcTSn.\n if (inclusive === \"omit\") {\n return pushIssue(\n g,\n `{code:\"too_small\",minimum:${minimum},origin:${originExpr(origin)},input:${input},path:${path}${messageProp(m)}${abortsProp(options?.aborts)}}`,\n );\n }\n return pushIssue(\n g,\n `{origin:${originExpr(origin)},code:\"too_small\",minimum:${minimum},inclusive:${inclusive},input:${input},path:${path}${messageProp(m)}}`,\n );\n}\n\nexport function tooBig(\n g: IssueGen,\n maximum: string | number,\n origin: Origin,\n inclusive: boolean,\n options?: {\n exact?: boolean;\n input?: string;\n path?: string;\n message?: string | undefined;\n /** Set for node-level issues (tuple length) where schema error applies. */\n useTypeMsg?: boolean;\n /**\n * `\"tuple\"` selects $ZodTuple's over-length key order — `code, maximum,\n * inclusive, origin` — where a check-created size issue leads with `origin`.\n * zod builds the tuple issue by spreading `{code, maximum, inclusive}` and\n * appending `origin` after `input`/`inst`, and that ordering is visible in\n * `ZodError.message`.\n */\n layout?: \"tuple\";\n /** Emit the union-abort marker; see {@link abortsProp}. */\n aborts?: boolean;\n },\n): string {\n const input = options?.input ?? g.input;\n const path = options?.path ?? g.path;\n const exact = options?.exact === true;\n const m = resolveMessage(g, options?.message, options?.useTypeMsg === true);\n if (g.ctx.mode === \"lean\") {\n if (exact) {\n g.ctx.usedHelpers.add(\"__zcTBx\");\n return pushIssue(\n g,\n `__zcTBx(${maximum},${originExpr(origin)},${input},${path}${messageArg(m)})`,\n );\n }\n if (options?.layout === \"tuple\") {\n g.ctx.usedHelpers.add(\"__zcTBt\");\n return pushIssue(\n g,\n `__zcTBt(${maximum},${originExpr(origin)},${input},${path}${messageArg(m)})`,\n );\n }\n g.ctx.usedHelpers.add(\"__zcTB\");\n return pushIssue(\n g,\n `__zcTB(${maximum},${originExpr(origin)},${inclusive},${input},${path}${messageArg(m)})`,\n );\n }\n if (exact) {\n return pushIssue(\n g,\n `{origin:${originExpr(origin)},code:\"too_big\",maximum:${maximum},inclusive:true,exact:true,input:${input},path:${path}${messageProp(m)}}`,\n );\n }\n if (options?.layout === \"tuple\") {\n return pushIssue(\n g,\n `{code:\"too_big\",maximum:${maximum},inclusive:${inclusive},origin:${originExpr(origin)},input:${input},path:${path}${messageProp(m)}${abortsProp(options?.aborts)}}`,\n );\n }\n return pushIssue(\n g,\n `{origin:${originExpr(origin)},code:\"too_big\",maximum:${maximum},inclusive:${inclusive},input:${input},path:${path}${messageProp(m)}}`,\n );\n}\n\nexport function invalidType(\n g: IssueGen,\n expected: string,\n options?: {\n input?: string;\n path?: string;\n extra?: string;\n message?: string | undefined;\n /**\n * Put `extra` BEFORE `code` rather than after it. $ZodCheckNumberFormat's\n * non-integer issue is `{expected, format, code}` while the `received` a\n * number/date schema adds trails `code` — two orders for one issue code, both\n * printed verbatim in `ZodError.message`.\n */\n extraBeforeCode?: boolean;\n /**\n * Lead with `code` instead of `expected`. Only `$ZodDiscriminatedUnion`'s\n * non-object guard writes the issue that way; every other schema in zod puts\n * `expected` first.\n */\n codeFirst?: boolean;\n },\n): string {\n const input = options?.input ?? g.input;\n const path = options?.path ?? g.path;\n // invalid_type is always created by the schema node — schema error applies.\n const m = resolveMessage(g, options?.message, true);\n if (g.ctx.mode === \"lean\" && !options?.extra) {\n const helper = options?.codeFirst ? \"__zcITc\" : \"__zcIT\";\n g.ctx.usedHelpers.add(helper);\n return pushIssue(g, `${helper}(${escapeString(expected)},${input},${path}${messageArg(m)})`);\n }\n const extra = options?.extra ?? \"\";\n const head = options?.codeFirst\n ? `code:\"invalid_type\",expected:${escapeString(expected)}${extra ? `,${extra}` : \"\"}`\n : options?.extraBeforeCode\n ? `expected:${escapeString(expected)}${extra ? `,${extra}` : \"\"},code:\"invalid_type\"`\n : `expected:${escapeString(expected)},code:\"invalid_type\"${extra ? `,${extra}` : \"\"}`;\n return pushIssue(g, `{${head},input:${input},path:${path}${messageProp(m)}}`);\n}\n\nexport function invalidFormat(\n g: IssueGen,\n format: string | { expr: string },\n options?: {\n input?: string;\n path?: string;\n /**\n * Leading `origin`, present only where zod's push carries one:\n * `$ZodCheckStringFormat`'s default pattern check and the regex / includes /\n * starts_with / ends_with checks lead with `origin: \"string\"`, while\n * `z.url()`'s own issues and every `$ZodCustomStringFormat` omit it.\n */\n origin?: string | undefined;\n /**\n * Extra issue properties as a source fragment (`'pattern:\"…\"'`), emitted\n * right after `format` where zod writes them. Explicitly `| undefined` so a\n * caller can compute it conditionally: zod's `invalid_format` carries\n * different fields per format, and the bare-issue formats carry none.\n */\n extra?: string | undefined;\n message?: string | undefined;\n },\n): string {\n const input = options?.input ?? g.input;\n const path = options?.path ?? g.path;\n const formatExpr = typeof format === \"string\" ? escapeString(format) : format.expr;\n // Format issues are created by check instances — schema error never applies.\n const m = options?.message;\n if (g.ctx.mode === \"lean\") {\n g.ctx.usedHelpers.add(\"__zcIF\");\n const originArg = options?.origin === undefined ? \"undefined\" : escapeString(options.origin);\n const extraArg = options?.extra ? `,{${options.extra}}` : m !== undefined ? \",undefined\" : \"\";\n return pushIssue(\n g,\n `__zcIF(${originArg},${formatExpr},${input},${path}${extraArg}${messageArg(m)})`,\n );\n }\n const originProp = options?.origin === undefined ? \"\" : `origin:${escapeString(options.origin)},`;\n const extra = options?.extra ? `,${options.extra}` : \"\";\n return pushIssue(\n g,\n `{${originProp}code:\"invalid_format\",format:${formatExpr}${extra},input:${input},path:${path}${messageProp(m)}}`,\n );\n}\n\nexport function unrecognizedKeys(\n g: IssueGen,\n keysExpr: string,\n options?: { input?: string; path?: string; message?: string | undefined },\n): string {\n const input = options?.input ?? g.input;\n const path = options?.path ?? g.path;\n // unrecognized_keys is created by the object schema node — schema error applies.\n const m = resolveMessage(g, options?.message, true);\n if (g.ctx.mode === \"lean\") {\n g.ctx.usedHelpers.add(\"__zcUK\");\n return pushIssue(g, `__zcUK(${keysExpr},${input},${path}${messageArg(m)})`);\n }\n return pushIssue(\n g,\n `{code:\"unrecognized_keys\",keys:${keysExpr},input:${input},path:${path}${messageProp(m)}}`,\n );\n}\n\nexport function invalidValue(\n g: IssueGen,\n valuesExpr: string,\n options?: {\n input?: string;\n path?: string;\n /**\n * Extra issue properties as a source fragment (`'expected:\"stringbool\"'`),\n * modelled on {@link invalidFormat}'s option of the same name. Explicitly\n * `| undefined` so a caller can compute it conditionally: the `invalid_value`\n * producers do NOT agree on fields — enum and literal push only `values`,\n * while z.stringbool()'s codec transform also pushes\n * `expected: \"stringbool\"`, which zod's locale reads to phrase the message.\n */\n extra?: string | undefined;\n message?: string | undefined;\n /** Set to false for check-level issues (file mime) where schema error does not apply. */\n useTypeMsg?: boolean;\n },\n): string {\n const input = options?.input ?? g.input;\n const path = options?.path ?? g.path;\n // Default: enum/literal invalid_value is created by the schema node.\n const m = resolveMessage(g, options?.message, options?.useTypeMsg !== false);\n if (g.ctx.mode === \"lean\") {\n g.ctx.usedHelpers.add(\"__zcIV\");\n const extraArg = options?.extra ? `,{${options.extra}}` : m !== undefined ? \",undefined\" : \"\";\n return pushIssue(g, `__zcIV(${valuesExpr},${input},${path}${extraArg}${messageArg(m)})`);\n }\n // `expected` precedes `values` in z.stringbool()'s push, so extras go between\n // `code` and `values` rather than after them.\n const extra = options?.extra ? `,${options.extra}` : \"\";\n return pushIssue(\n g,\n `{code:\"invalid_value\"${extra},values:${valuesExpr},input:${input},path:${path}${messageProp(m)}}`,\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"emit-issue.js","names":[],"sources":["../../../src/core/codegen/emit-issue.ts"],"sourcesContent":["/**\n * Issue object emit helpers.\n *\n * Each helper returns a string of the form `_e.push(...)` (or equivalent on a\n * caller-supplied issues variable). Two emit modes:\n *\n * - inline (CLI .compiled.ts): emits the literal `{code:\"too_small\",...}` object.\n * - lean (unplugin): emits a call like `__zcTS(min,origin,inclusive,input,path)`\n * and registers the helper in `ctx.usedHelpers` so the transform layer can\n * construct the corresponding `import` from `\"virtual:zod-compiler/runtime\"`.\n *\n * Centralizing all issue emission here means schema codegen files don't carry\n * the inline/lean branching; they just call `tooSmall(g, ...)` etc.\n *\n * Message resolution mirrors zod's `finalizeIssue`, which consults the error\n * map of the INSTANCE that created the issue first and then the map of the\n * SCHEMA that owns it: a check stamps its owner onto every issue it raises\n * (`attachSchema`), so `z.string({ error: \"msg\" }).min(3)` reports \"msg\" for the\n * `too_small` as well as for the `invalid_type`. Every emitter therefore takes\n * the per-check message (options.message, extracted from `.min(3, \"msg\")`) and\n * falls back to the node's schema-level message (g.typeMsg, from\n * `z.string({ error: \"msg\" })`), which is scoped to the node being generated and\n * never inherited by a child node's issues. When no message lands, the __zcFin\n * finalizer applies the locale default.\n *\n * KEY ORDER MATTERS, and is reproduced from zod issue by issue. `ZodError`\n * builds its `message` as `JSON.stringify(issues, …, 2)`, so insertion order is\n * printed text in a property consumers log, snapshot and serialize — an issue\n * carrying the right fields in the wrong order still renders a different error.\n * zod's own orders are irregular (`too_small` leads with `origin` from a check\n * but with `code` from a tuple's length branch; `invalid_format` carries\n * `origin` for a pattern check and omits it for `z.url()`), so each emitter\n * below mirrors one `payload.issues.push({…})` rather than a house style.\n *\n * `message` is written LAST everywhere, after `path`: zod never puts it in the\n * literal — `finalizeIssue` assigns `full.message` afterwards — and that holds\n * for a baked custom message too, since the custom error map is consulted from\n * inside that same assignment.\n */\n\nimport type { CodeGenContext } from \"./context.js\";\nimport { escapeString } from \"./context.js\";\n\n/** Common slim slice of SlowGen + FastGen needed for issue emission. */\ninterface IssueGen {\n readonly issues: string;\n readonly input: string;\n readonly path: string;\n readonly ctx: CodeGenContext;\n readonly typeMsg?: string | undefined;\n}\n\nfunction pushIssue(g: IssueGen, body: string): string {\n return `${g.issues}.push(${body});`;\n}\n\n/** Resolve the effective static message for an issue site: the check's own message, else the owning schema's. */\nfunction resolveMessage(g: IssueGen, explicit: string | undefined): string | undefined {\n return explicit ?? g.typeMsg;\n}\n\n/** `,message:\"...\"` fragment for inline object literals (\"\" when no message). */\n/**\n * `,continue:false` — the marker that makes an issue ABORT its union option.\n *\n * zod prunes union options with `util.aborted`, which asks whether any issue has\n * `continue !== true`. Compiled issues carry no `continue` at all, so the union\n * generator classifies by CODE instead — sound for every issue a schema node\n * raises (`invalid_type`, `unrecognized_keys`, …) but blind to the one\n * exception: `$ZodTuple`'s length branch pushes a `too_small`/`too_big` from the\n * NODE, with no `continue`, where the identically-coded issue from an\n * `.min()`/`.max()` CHECK carries `continue: true` and does not abort. Without\n * this, `z.union([z.tuple([a, b]), z.number()])` handed `[]` surfaced the\n * tuple's `too_small` directly instead of wrapping both options in an\n * `invalid_union`.\n *\n * Stripped during finalization exactly as zod strips its own (`delete\n * full.continue`), so it never reaches a reported issue — see FAIL_CLASS_DECL\n * and ZC_FZ_DECL.\n */\nfunction abortsProp(aborts: boolean | undefined): string {\n return aborts === true ? \",continue:false\" : \"\";\n}\n\nfunction messageProp(m: string | undefined): string {\n return m === undefined ? \"\" : `,message:${escapeString(m)}`;\n}\n\n/** `,\"...\"` trailing argument fragment for lean factory calls (\"\" when no message). */\nfunction messageArg(m: string | undefined): string {\n return m === undefined ? \"\" : `,${escapeString(m)}`;\n}\n\n/**\n * `origin` as source: a bare string is a literal (`\"string\"`), `{ expr }` is an\n * expression evaluated at runtime — what a `when`-gated length/size check needs,\n * since zod derives its origin from the INPUT (`getLengthableOrigin`).\n */\ntype Origin = string | { expr: string };\n\nfunction originExpr(origin: Origin): string {\n return typeof origin === \"string\" ? escapeString(origin) : origin.expr;\n}\n\nexport function tooSmall(\n g: IssueGen,\n minimum: string | number,\n origin: Origin,\n inclusive: boolean,\n options?: {\n exact?: boolean;\n input?: string;\n path?: string;\n message?: string | undefined;\n /**\n * `\"tuple\"` selects $ZodTuple's under-length key order — `code, minimum,\n * inclusive, origin` — where a check-created size issue leads with `origin`.\n * Same shape as its over-length sibling in {@link tooBig}.\n */\n layout?: \"tuple\";\n /** Emit the union-abort marker; see {@link abortsProp}. */\n aborts?: boolean;\n },\n): string {\n const input = options?.input ?? g.input;\n const path = options?.path ?? g.path;\n const exact = options?.exact === true;\n const m = resolveMessage(g, options?.message);\n if (g.ctx.mode === \"lean\") {\n if (exact) {\n g.ctx.usedHelpers.add(\"__zcTSx\");\n return pushIssue(\n g,\n `__zcTSx(${minimum},${originExpr(origin)},${input},${path}${messageArg(m)})`,\n );\n }\n if (options?.layout === \"tuple\") {\n g.ctx.usedHelpers.add(\"__zcTSt\");\n return pushIssue(\n g,\n `__zcTSt(${minimum},${originExpr(origin)},${input},${path}${messageArg(m)})`,\n );\n }\n g.ctx.usedHelpers.add(\"__zcTS\");\n return pushIssue(\n g,\n `__zcTS(${minimum},${originExpr(origin)},${inclusive},${input},${path}${messageArg(m)})`,\n );\n }\n if (exact) {\n return pushIssue(\n g,\n `{origin:${originExpr(origin)},code:\"too_small\",minimum:${minimum},inclusive:true,exact:true,input:${input},path:${path}${messageProp(m)}}`,\n );\n }\n if (options?.layout === \"tuple\") {\n return pushIssue(\n g,\n `{code:\"too_small\",minimum:${minimum},inclusive:${inclusive},origin:${originExpr(origin)},input:${input},path:${path}${messageProp(m)}${abortsProp(options?.aborts)}}`,\n );\n }\n return pushIssue(\n g,\n `{origin:${originExpr(origin)},code:\"too_small\",minimum:${minimum},inclusive:${inclusive},input:${input},path:${path}${messageProp(m)}}`,\n );\n}\n\nexport function tooBig(\n g: IssueGen,\n maximum: string | number,\n origin: Origin,\n inclusive: boolean,\n options?: {\n exact?: boolean;\n input?: string;\n path?: string;\n message?: string | undefined;\n /**\n * `\"tuple\"` selects $ZodTuple's over-length key order — `code, maximum,\n * inclusive, origin` — where a check-created size issue leads with `origin`.\n * zod builds the tuple issue by spreading `{code, maximum, inclusive}` and\n * appending `origin` after `input`/`inst`, and that ordering is visible in\n * `ZodError.message`.\n */\n layout?: \"tuple\";\n /** Emit the union-abort marker; see {@link abortsProp}. */\n aborts?: boolean;\n },\n): string {\n const input = options?.input ?? g.input;\n const path = options?.path ?? g.path;\n const exact = options?.exact === true;\n const m = resolveMessage(g, options?.message);\n if (g.ctx.mode === \"lean\") {\n if (exact) {\n g.ctx.usedHelpers.add(\"__zcTBx\");\n return pushIssue(\n g,\n `__zcTBx(${maximum},${originExpr(origin)},${input},${path}${messageArg(m)})`,\n );\n }\n if (options?.layout === \"tuple\") {\n g.ctx.usedHelpers.add(\"__zcTBt\");\n return pushIssue(\n g,\n `__zcTBt(${maximum},${originExpr(origin)},${input},${path}${messageArg(m)})`,\n );\n }\n g.ctx.usedHelpers.add(\"__zcTB\");\n return pushIssue(\n g,\n `__zcTB(${maximum},${originExpr(origin)},${inclusive},${input},${path}${messageArg(m)})`,\n );\n }\n if (exact) {\n return pushIssue(\n g,\n `{origin:${originExpr(origin)},code:\"too_big\",maximum:${maximum},inclusive:true,exact:true,input:${input},path:${path}${messageProp(m)}}`,\n );\n }\n if (options?.layout === \"tuple\") {\n return pushIssue(\n g,\n `{code:\"too_big\",maximum:${maximum},inclusive:${inclusive},origin:${originExpr(origin)},input:${input},path:${path}${messageProp(m)}${abortsProp(options?.aborts)}}`,\n );\n }\n return pushIssue(\n g,\n `{origin:${originExpr(origin)},code:\"too_big\",maximum:${maximum},inclusive:${inclusive},input:${input},path:${path}${messageProp(m)}}`,\n );\n}\n\nexport function invalidType(\n g: IssueGen,\n expected: string,\n options?: {\n input?: string;\n path?: string;\n extra?: string;\n message?: string | undefined;\n /**\n * Put `extra` BEFORE `code` rather than after it. $ZodCheckNumberFormat's\n * non-integer issue is `{expected, format, code}` while the `received` a\n * number/date schema adds trails `code` — two orders for one issue code, both\n * printed verbatim in `ZodError.message`.\n */\n extraBeforeCode?: boolean;\n /**\n * Lead with `code` instead of `expected`. `$ZodDiscriminatedUnion`'s\n * non-object guard and `$ZodObject`'s absent-required-key issue\n * (`expected: \"nonoptional\"`) write the issue that way; every other schema\n * in zod puts `expected` first.\n */\n codeFirst?: boolean;\n /**\n * Set to false for the one `invalid_type` zod pushes WITHOUT an `inst`:\n * the object's `nonoptional` issue for an absent required key, which no\n * schema-level `error` reaches.\n */\n useTypeMsg?: boolean;\n },\n): string {\n const input = options?.input ?? g.input;\n const path = options?.path ?? g.path;\n // The schema's own message applies unless the issue is raised inst-less, as the nonoptional issue is.\n const m = options?.useTypeMsg === false ? options?.message : resolveMessage(g, options?.message);\n if (g.ctx.mode === \"lean\" && !options?.extra) {\n const helper = options?.codeFirst ? \"__zcITc\" : \"__zcIT\";\n g.ctx.usedHelpers.add(helper);\n return pushIssue(g, `${helper}(${escapeString(expected)},${input},${path}${messageArg(m)})`);\n }\n const extra = options?.extra ?? \"\";\n const head = options?.codeFirst\n ? `code:\"invalid_type\",expected:${escapeString(expected)}${extra ? `,${extra}` : \"\"}`\n : options?.extraBeforeCode\n ? `expected:${escapeString(expected)}${extra ? `,${extra}` : \"\"},code:\"invalid_type\"`\n : `expected:${escapeString(expected)},code:\"invalid_type\"${extra ? `,${extra}` : \"\"}`;\n return pushIssue(g, `{${head},input:${input},path:${path}${messageProp(m)}}`);\n}\n\nexport function invalidFormat(\n g: IssueGen,\n format: string | { expr: string },\n options?: {\n input?: string;\n path?: string;\n /**\n * Leading `origin`, present only where zod's push carries one:\n * `$ZodCheckStringFormat`'s default pattern check and the regex / includes /\n * starts_with / ends_with checks lead with `origin: \"string\"`, while\n * `z.url()`'s own issues and every `$ZodCustomStringFormat` omit it.\n */\n origin?: string | undefined;\n /**\n * Extra issue properties as a source fragment (`'pattern:\"…\"'`), emitted\n * right after `format` where zod writes them. Explicitly `| undefined` so a\n * caller can compute it conditionally: zod's `invalid_format` carries\n * different fields per format, and the bare-issue formats carry none.\n */\n extra?: string | undefined;\n message?: string | undefined;\n },\n): string {\n const input = options?.input ?? g.input;\n const path = options?.path ?? g.path;\n const formatExpr = typeof format === \"string\" ? escapeString(format) : format.expr;\n const m = resolveMessage(g, options?.message);\n if (g.ctx.mode === \"lean\") {\n g.ctx.usedHelpers.add(\"__zcIF\");\n const originArg = options?.origin === undefined ? \"undefined\" : escapeString(options.origin);\n const extraArg = options?.extra ? `,{${options.extra}}` : m !== undefined ? \",undefined\" : \"\";\n return pushIssue(\n g,\n `__zcIF(${originArg},${formatExpr},${input},${path}${extraArg}${messageArg(m)})`,\n );\n }\n const originProp = options?.origin === undefined ? \"\" : `origin:${escapeString(options.origin)},`;\n const extra = options?.extra ? `,${options.extra}` : \"\";\n return pushIssue(\n g,\n `{${originProp}code:\"invalid_format\",format:${formatExpr}${extra},input:${input},path:${path}${messageProp(m)}}`,\n );\n}\n\nexport function unrecognizedKeys(\n g: IssueGen,\n keysExpr: string,\n options?: { input?: string; path?: string; message?: string | undefined },\n): string {\n const input = options?.input ?? g.input;\n const path = options?.path ?? g.path;\n const m = resolveMessage(g, options?.message);\n if (g.ctx.mode === \"lean\") {\n g.ctx.usedHelpers.add(\"__zcUK\");\n return pushIssue(g, `__zcUK(${keysExpr},${input},${path}${messageArg(m)})`);\n }\n return pushIssue(\n g,\n `{code:\"unrecognized_keys\",keys:${keysExpr},input:${input},path:${path}${messageProp(m)}}`,\n );\n}\n\nexport function invalidValue(\n g: IssueGen,\n valuesExpr: string,\n options?: {\n input?: string;\n path?: string;\n /**\n * Extra issue properties as a source fragment (`'expected:\"stringbool\"'`),\n * modelled on {@link invalidFormat}'s option of the same name. Explicitly\n * `| undefined` so a caller can compute it conditionally: the `invalid_value`\n * producers do NOT agree on fields — enum and literal push only `values`,\n * while z.stringbool()'s codec transform also pushes\n * `expected: \"stringbool\"`, which zod's locale reads to phrase the message.\n */\n extra?: string | undefined;\n message?: string | undefined;\n },\n): string {\n const input = options?.input ?? g.input;\n const path = options?.path ?? g.path;\n const m = resolveMessage(g, options?.message);\n if (g.ctx.mode === \"lean\") {\n g.ctx.usedHelpers.add(\"__zcIV\");\n const extraArg = options?.extra ? `,{${options.extra}}` : m !== undefined ? \",undefined\" : \"\";\n return pushIssue(g, `__zcIV(${valuesExpr},${input},${path}${extraArg}${messageArg(m)})`);\n }\n // `expected` precedes `values` in z.stringbool()'s push, so extras go between\n // `code` and `values` rather than after them.\n const extra = options?.extra ? `,${options.extra}` : \"\";\n return pushIssue(\n g,\n `{code:\"invalid_value\"${extra},values:${valuesExpr},input:${input},path:${path}${messageProp(m)}}`,\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/core/codegen/index.ts"],"mappings":";;;;UAsBiB;EACf;;EAEA,OAAO;;;;;EAKP,gBAAgB;;;;;;;;;EAShB;;EAEA,eAAe,UAAU;;EAEzB,sBAAsB;;;;;;;;;;;iBAYR,kBACd,IAAI,UACJ,cACA,UAAU,2BACT"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/core/codegen/index.ts"],"mappings":";;;;UA8BiB;EACf;;EAEA,OAAO;;;;;EAKP,gBAAgB;;;;;;;;;EAShB;;EAEA,eAAe,UAAU;;EAEzB,sBAAsB;;;;;;;;;;;iBAYR,kBACd,IAAI,UACJ,cACA,UAAU,2BACT"}
@@ -1,7 +1,8 @@
1
- import { RETAINED_SCHEMA_VAR, declareFastTemps, emitRetainedMethod, emitRfDelegate, hasMutation } from "./context.js";
1
+ import { RETAINED_SCHEMA_VAR, declareFastTemps, emitRetainedMethod, emitRfDelegate, emitRuntimeHelper, hasMutation, needsProtoScrub } from "./context.js";
2
+ import { ZC_PROTO_SCRUB_DECL } from "./issue-decls.js";
2
3
  import { createSlowGen, generateSlow } from "./slow-path.js";
3
4
  import { createFastGen, generateFast } from "./fast-path.js";
4
- import { fastResultIsInput, generateBuild, rebuildsOutput } from "./build-path.js";
5
+ import { fastResultIsInput, generateBuild, nestedNeedsProtoScrub, rebuildsOutput } from "./build-path.js";
5
6
  //#region src/core/codegen/index.ts
6
7
  /**
7
8
  * Generate optimized validation code from SchemaIR.
@@ -101,7 +102,7 @@ function generateValidator(ir, name, options) {
101
102
  }
102
103
  const buildIsFnName = ctx.buildSubstitutesValue === true ? null : fastFnName;
103
104
  const baseRefCount = options?.refCount ?? 0;
104
- if (options?.compact === true && fastExpr !== null && fastExpr !== "true" && !hasMutation(ir) && !hasNonRootTargets) {
105
+ if (options?.compact === true && fastExpr !== null && fastExpr !== "true" && !hasMutation(ir) && !needsProtoScrub(ir) && !nestedNeedsProtoScrub(ir) && !hasNonRootTargets) {
105
106
  const delegate = emitRetainedMethod(ctx);
106
107
  ctx.usedHelpers.add("__zcFinZ");
107
108
  return {
@@ -190,20 +191,21 @@ function generateValidator(ir, name, options) {
190
191
  fastTotal: false
191
192
  };
192
193
  }
193
- if (fastExpr !== null && !hasMutation(ir)) {
194
+ if (fastExpr !== null && !hasMutation(ir) && !nestedNeedsProtoScrub(ir)) {
195
+ const fastData = needsProtoScrub(ir) ? `${emitRuntimeHelper(ctx, "__zcPs", ZC_PROTO_SCRUB_DECL)}(input)` : "input";
194
196
  ctx.usedHelpers.add("__zcFinD");
195
- if (slowCode.includes(fnName)) functionDefParts.push(`if(${fastExpr}){return{success:true,data:input};}`, `return __zcFinD(function(input){`, `var _e=[];`, `var _d=input;`, slowCode, `return _e;`, `},input);`, `}`);
197
+ if (slowCode.includes(fnName)) functionDefParts.push(`if(${fastExpr}){return{success:true,data:${fastData}};}`, `return __zcFinD(function(input){`, `var _e=[];`, `var _d=input;`, slowCode, `return _e;`, `},input);`, `}`);
196
198
  else {
197
199
  const walkName = `__sw_${ctx.counter++}`;
198
200
  ctx.preamble.push(`function ${walkName}(input){var _e=[];\nvar _d=input;\n${slowCode}\nreturn _e;}`);
199
- functionDefParts.push(`if(${fastExpr}){return{success:true,data:input};}`, `return __zcFinD(${walkName},input);`, `}`);
201
+ functionDefParts.push(`if(${fastExpr}){return{success:true,data:${fastData}};}`, `return __zcFinD(${walkName},input);`, `}`);
200
202
  }
201
203
  return {
202
204
  code: buildCode(),
203
205
  functionDef: functionDefParts.join("\n"),
204
206
  refCount: options?.refCount ?? 0,
205
207
  usedHelpers: ctx.usedHelpers,
206
- fastFnName,
208
+ fastFnName: fastResultIsInput(ir) ? fastFnName : null,
207
209
  fastTotal: true
208
210
  };
209
211
  }