zod-compiler 2.0.5 → 2.0.6

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 (34) hide show
  1. package/README.md +97 -118
  2. package/dist/core/codegen/build-path.js +1 -1
  3. package/dist/core/codegen/context.d.ts +6 -5
  4. package/dist/core/codegen/context.d.ts.map +1 -1
  5. package/dist/core/codegen/context.js +10 -8
  6. package/dist/core/codegen/context.js.map +1 -1
  7. package/dist/core/codegen/index.js +1 -1
  8. package/dist/core/codegen/issue-decls.d.ts +18 -1
  9. package/dist/core/codegen/issue-decls.d.ts.map +1 -1
  10. package/dist/core/codegen/issue-decls.js +19 -1
  11. package/dist/core/codegen/issue-decls.js.map +1 -1
  12. package/dist/core/codegen/schemas/array.js +2 -2
  13. package/dist/core/codegen/schemas/array.js.map +1 -1
  14. package/dist/core/codegen/schemas/custom.js +1 -1
  15. package/dist/core/codegen/schemas/discriminated-union.js +1 -1
  16. package/dist/core/codegen/schemas/discriminated-union.js.map +1 -1
  17. package/dist/core/codegen/schemas/effect.js +2 -2
  18. package/dist/core/codegen/schemas/effect.js.map +1 -1
  19. package/dist/core/codegen/schemas/fallback.js +1 -1
  20. package/dist/core/codegen/schemas/map.js +1 -1
  21. package/dist/core/codegen/schemas/number.js +1 -1
  22. package/dist/core/codegen/schemas/object.js +3 -3
  23. package/dist/core/codegen/schemas/object.js.map +1 -1
  24. package/dist/core/codegen/schemas/record.js +2 -2
  25. package/dist/core/codegen/schemas/record.js.map +1 -1
  26. package/dist/core/codegen/schemas/sizeable.js +1 -1
  27. package/dist/core/codegen/schemas/string.js +1 -1
  28. package/dist/core/codegen/schemas/tuple.js +2 -2
  29. package/dist/core/codegen/schemas/tuple.js.map +1 -1
  30. package/dist/core/codegen/schemas/union.js +1 -1
  31. package/dist/runtime.d.ts +1 -0
  32. package/dist/runtime.js +1 -0
  33. package/dist/unplugin/virtual.js +1 -1
  34. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
- import { emitEffectCallable, emitRuntimeHelper } from "../context.js";
2
1
  import { ZC_CUSTOM_OK_DECL } from "../issue-decls.js";
2
+ import { emitEffectCallable, emitRuntimeHelper } from "../context.js";
3
3
  import { slowZodDelegate } from "./fallback.js";
4
4
  //#region src/core/codegen/schemas/custom.ts
5
5
  /** Total hot-path predicate for z.custom() and z.instanceof(). */
@@ -24,7 +24,7 @@ function slowDiscriminatedUnion(ir, g) {
24
24
  const optionsList = ir.cases.map(({ value }) => literalToJs(value)).join(",");
25
25
  code += emit`
26
26
  default:
27
- ${g.issues}.push({code:"invalid_union",errors:[],note:"No matching discriminator",discriminator:${discKey},options:[${optionsList}],input:${g.input},path:${extendPath(g.path, discKey)}${msgProp}});
27
+ ${g.issues}.push({code:"invalid_union",errors:[],note:"No matching discriminator",discriminator:${discKey},options:[${optionsList}],input:${g.input},path:${extendPath(g.ctx, g.path, discKey)}${msgProp}});
28
28
  }`;
29
29
  code += `${g.output}=${objVar};`;
30
30
  code += `}`;
@@ -1 +1 @@
1
- {"version":3,"file":"discriminated-union.js","names":[],"sources":["../../../../src/core/codegen/schemas/discriminated-union.ts"],"sourcesContent":["import type { DiscriminatedUnionIR, LiteralValue, ObjectIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport { declareFastTemps, escapeString, extendPath, literalToJs } from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType } from \"../emit-issue.js\";\n\n/** One `discriminator value → option index` dispatch entry. */\ntype DiscriminatorCase = DiscriminatedUnionIR[\"cases\"][number];\n\nexport function slowDiscriminatedUnion(\n ir: SchemaIR & { type: \"discriminatedUnion\" },\n g: SlowGen,\n): string {\n const discKey = escapeString(ir.discriminator);\n\n let code = emit`\n if(typeof ${g.input}!==\"object\"||${g.input}===null||Array.isArray(${g.input})){\n ${invalidType(g, \"object\", { codeFirst: true })}\n }else{`;\n\n const objVar = g.temp(\"du\");\n code += `var ${objVar}=${g.input};switch(${objVar}[${discKey}]){`;\n\n for (const { value, option: index } of ir.cases) {\n const option = ir.options[index] as SchemaIR;\n code += emit`\n case ${literalToJs(value)}:\n ${g.visit(option, { input: objVar, output: objVar })}\n break;`;\n }\n\n const msgProp = g.typeMsg === undefined ? \"\" : `,message:${JSON.stringify(g.typeMsg)}`;\n // Field for field what $ZodDiscriminatedUnion pushes when no option matches:\n // `{ code, errors: [], note: \"No matching discriminator\", discriminator,\n // options, input, path: [def.discriminator] }`. `options` is\n // `Array.from(disc.value.keys())` — every dispatch value in map insertion\n // order, which is exactly `ir.cases` (option order, then each option's\n // `propValues[discriminator]` in ITS order, so an omittable discriminator\n // lists `undefined` right after its own values). The locale reads the list\n // for \"Invalid discriminator value. Expected 'a' | 'b'\". A fresh array\n // literal per push, as zod allocates one, so consumers never share or\n // mutate a hoisted table.\n const optionsList = ir.cases.map(({ value }) => literalToJs(value)).join(\",\");\n code += emit`\n default:\n ${g.issues}.push({code:\"invalid_union\",errors:[],note:\"No matching discriminator\",discriminator:${discKey},options:[${optionsList}],input:${g.input},path:${extendPath(g.path, discKey)}${msgProp}});\n }`;\n // Propagate what the matched option produced back to the output location.\n // Each option is visited with output:objVar — a fresh local — so its result is\n // reassigned into objVar and stranded there; without this write-back the\n // caller gets the ORIGINAL input by reference and the result is silently lost.\n // That is as true of an option that rewrites nothing as of a mutating one\n // (defaults, coercions, transforms, overwrite checks, stringbool): it can\n // still hand back a replacement — a `__proto__`-scrubbed copy, or a copy a\n // nested replacement landed on — so the write is not gated on mutation. Where\n // nothing was replaced, and on the no-match path, objVar is still the input.\n code += `${g.output}=${objVar};`;\n code += `}`;\n return `${code}\\n`;\n}\n\n/**\n * Emit an O(1) switch-dispatch fast-check for a discriminated union — real\n * (`z.discriminatedUnion`) or one detected inside a plain `z.union`\n * (see {@link detectUnionDiscriminator}). Both share this so the detected case\n * inherits the size-gating and the per-case guard strip.\n *\n * `discSkipKey` tells each object option to drop its own type-guard and\n * discriminator re-check: the caller's guard (`typeof x===\"object\"&&…` below)\n * already proved object-ness, and the matched switch case has fixed the\n * discriminator value, so re-emitting either is pure redundancy the optimizer\n * only removes when it inlines this helper — which a union large enough to\n * matter won't. Routed through the normal `visit` so size-gated extraction\n * still bounds the switch (the strip survives into any hoisted helper);\n * non-object options ignore the hint and keep their own guard.\n *\n * Returns null if any option is fast-path-ineligible.\n */\nexport function emitFastDiscriminatedSwitch(\n g: FastGen,\n discriminator: string,\n cases: readonly DiscriminatorCase[],\n options: readonly SchemaIR[],\n): string | null {\n const x = g.input;\n const discKey = escapeString(discriminator);\n const helperName = g.temp(\"du\");\n const helperParam = g.temp(\"dx\");\n\n // The switch body is its own function: size-gate the options against the cap\n // in a fresh scope, otherwise many small options accumulate into the caller's\n // scope while this helper itself grows unbounded past the TurboFan budget.\n //\n // The switch is over the literal labels themselves. An earlier revision\n // routed three or more string labels through a `{value: ordinal}` table and\n // switched on the ordinal, on the theory that V8 compiles a string switch as\n // sequential comparisons and a dense integer one as a jump table. Measured on\n // V8 13.x (node 24) that is a loss at every size: the table's `t[v]` is a\n // keyed load whose key varies per parse, which goes megamorphic the moment\n // the input rotates through the options, while the string switch stays\n // nearly flat (8.6 ns at 8 cases, 10.1 at 32, 17.3 at 80, rotating input)\n // against the table's 12.9, 15.8 and 20.8. The build path switches the same\n // way (see `buildDispatch`).\n const body = g.scoped(helperParam);\n const caseStrs: string[] = [];\n for (const { value, option: index } of cases) {\n const check = body.visit(options[index] as SchemaIR, { discSkipKey: discriminator });\n if (check === null) return null;\n caseStrs.push(`case ${literalToJs(value)}:return ${check};`);\n }\n g.ctx.preamble.push(\n `function ${helperName}(${helperParam}){${declareFastTemps(body.scope)}switch(${helperParam}[${discKey}]){${caseStrs.join(\"\")}default:return false;}}`,\n );\n\n return `typeof ${x}===\"object\"&&${x}!==null&&!Array.isArray(${x})&&${helperName}(${x})`;\n}\n\nexport function fastDiscriminatedUnion(ir: DiscriminatedUnionIR, g: FastGen): string | null {\n return emitFastDiscriminatedSwitch(g, ir.discriminator, ir.cases, ir.options);\n}\n\n/**\n * Minimum option count for rewriting a plain `z.union` to switch dispatch. Below\n * this the switch helper's fixed call overhead loses to a fully-inlined\n * `||`-chain that V8 keeps flat: measured crossover is ~4 options (n=3 can hit\n * 0.6x — a regression — while n=5 is 1.27x and n=32 is 2.34x), so 5 captures the\n * stable wins with margin and never regresses a small union. Real\n * `z.discriminatedUnion` is unaffected — it dispatches via switch by construction\n * regardless of size.\n */\nconst MIN_AUTO_DISCRIMINATE_OPTIONS = 5;\n\n/**\n * Values that switch correctly under `===` (excludes `undefined` and `NaN`).\n *\n * Also excludes a SYMBOL — now that `LiteralIR.values` admits one, this is the\n * guard that keeps it out. A symbol discriminant has no source form, so there\n * is no `case` label to emit for it (`literalToJs` refuses it by type), and the\n * ordinal-table variant is string-keyed besides. Detection bails to the\n * `||`-chain, where the option's own literal check does the right thing by\n * reading the value list off the retained schema.\n */\nfunction isSwitchableDiscriminant(v: LiteralValue): v is string | number | boolean | bigint | null {\n return (\n v === null ||\n typeof v === \"string\" ||\n typeof v === \"boolean\" ||\n typeof v === \"bigint\" ||\n (typeof v === \"number\" && !Number.isNaN(v))\n );\n}\n\n/**\n * Detect whether a plain (untagged) `z.union` is *structurally* a discriminated\n * union, so its fast path can use O(1) switch dispatch instead of probing every\n * arm. Returns the discriminator + dispatch table, or null to keep the\n * `||`-chain.\n *\n * Requires (proving the switch accepts exactly what the `||`-chain would): every\n * option is a plain object that pins one shared key to a REQUIRED literal\n * (`prop.type === \"literal\"` — an optional/non-literal key is rejected), and the\n * literal values are pairwise DISJOINT across options. Disjointness is the crux:\n * it guarantees at most one option can accept any given input, so dispatching to\n * that single option is equivalent to trying them all. Any value shared by two\n * options (ambiguous), a non-switchable value (`undefined`/`NaN`), or a\n * non-object option makes detection bail to the safe `||`-chain.\n *\n * The slow path is untouched: it keeps `z.union`'s sequential trial and its\n * `invalid_union` error shape, so failure output stays byte-identical to Zod.\n * The fast path and the build path both dispatch on the result — the build\n * path from two options up (`minOptions`), since its options are hosted calls\n * either way and a switch only ever replaces probes with one call; see\n * `buildUnion`.\n */\nexport function detectUnionDiscriminator(\n options: readonly SchemaIR[],\n minOptions: number = MIN_AUTO_DISCRIMINATE_OPTIONS,\n): { discriminator: string; cases: DiscriminatorCase[] } | null {\n if (options.length < minOptions) return null;\n const objects: ObjectIR[] = [];\n for (const option of options) {\n if (option.type !== \"object\") return null;\n objects.push(option);\n }\n const first = objects[0];\n if (first === undefined) return null; // unreachable (length checked above)\n\n // Only keys present in the first option can be shared by all; try each.\n candidate: for (const key of Object.keys(first.properties)) {\n const seen = new Set<string | number | boolean | bigint | null>();\n const cases: DiscriminatorCase[] = [];\n for (const [i, object] of objects.entries()) {\n const prop = object.properties[key];\n if (prop === undefined || prop.type !== \"literal\") continue candidate;\n for (const value of prop.values) {\n if (!isSwitchableDiscriminant(value)) continue candidate;\n if (seen.has(value)) continue candidate; // shared value → ambiguous dispatch\n seen.add(value);\n cases.push({ value, option: i });\n }\n }\n return { discriminator: key, cases };\n }\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"discriminated-union.js","names":[],"sources":["../../../../src/core/codegen/schemas/discriminated-union.ts"],"sourcesContent":["import type { DiscriminatedUnionIR, LiteralValue, ObjectIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport { declareFastTemps, escapeString, extendPath, literalToJs } from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType } from \"../emit-issue.js\";\n\n/** One `discriminator value → option index` dispatch entry. */\ntype DiscriminatorCase = DiscriminatedUnionIR[\"cases\"][number];\n\nexport function slowDiscriminatedUnion(\n ir: SchemaIR & { type: \"discriminatedUnion\" },\n g: SlowGen,\n): string {\n const discKey = escapeString(ir.discriminator);\n\n let code = emit`\n if(typeof ${g.input}!==\"object\"||${g.input}===null||Array.isArray(${g.input})){\n ${invalidType(g, \"object\", { codeFirst: true })}\n }else{`;\n\n const objVar = g.temp(\"du\");\n code += `var ${objVar}=${g.input};switch(${objVar}[${discKey}]){`;\n\n for (const { value, option: index } of ir.cases) {\n const option = ir.options[index] as SchemaIR;\n code += emit`\n case ${literalToJs(value)}:\n ${g.visit(option, { input: objVar, output: objVar })}\n break;`;\n }\n\n const msgProp = g.typeMsg === undefined ? \"\" : `,message:${JSON.stringify(g.typeMsg)}`;\n // Field for field what $ZodDiscriminatedUnion pushes when no option matches:\n // `{ code, errors: [], note: \"No matching discriminator\", discriminator,\n // options, input, path: [def.discriminator] }`. `options` is\n // `Array.from(disc.value.keys())` — every dispatch value in map insertion\n // order, which is exactly `ir.cases` (option order, then each option's\n // `propValues[discriminator]` in ITS order, so an omittable discriminator\n // lists `undefined` right after its own values). The locale reads the list\n // for \"Invalid discriminator value. Expected 'a' | 'b'\". A fresh array\n // literal per push, as zod allocates one, so consumers never share or\n // mutate a hoisted table.\n const optionsList = ir.cases.map(({ value }) => literalToJs(value)).join(\",\");\n code += emit`\n default:\n ${g.issues}.push({code:\"invalid_union\",errors:[],note:\"No matching discriminator\",discriminator:${discKey},options:[${optionsList}],input:${g.input},path:${extendPath(g.ctx, g.path, discKey)}${msgProp}});\n }`;\n // Propagate what the matched option produced back to the output location.\n // Each option is visited with output:objVar — a fresh local — so its result is\n // reassigned into objVar and stranded there; without this write-back the\n // caller gets the ORIGINAL input by reference and the result is silently lost.\n // That is as true of an option that rewrites nothing as of a mutating one\n // (defaults, coercions, transforms, overwrite checks, stringbool): it can\n // still hand back a replacement — a `__proto__`-scrubbed copy, or a copy a\n // nested replacement landed on — so the write is not gated on mutation. Where\n // nothing was replaced, and on the no-match path, objVar is still the input.\n code += `${g.output}=${objVar};`;\n code += `}`;\n return `${code}\\n`;\n}\n\n/**\n * Emit an O(1) switch-dispatch fast-check for a discriminated union — real\n * (`z.discriminatedUnion`) or one detected inside a plain `z.union`\n * (see {@link detectUnionDiscriminator}). Both share this so the detected case\n * inherits the size-gating and the per-case guard strip.\n *\n * `discSkipKey` tells each object option to drop its own type-guard and\n * discriminator re-check: the caller's guard (`typeof x===\"object\"&&…` below)\n * already proved object-ness, and the matched switch case has fixed the\n * discriminator value, so re-emitting either is pure redundancy the optimizer\n * only removes when it inlines this helper — which a union large enough to\n * matter won't. Routed through the normal `visit` so size-gated extraction\n * still bounds the switch (the strip survives into any hoisted helper);\n * non-object options ignore the hint and keep their own guard.\n *\n * Returns null if any option is fast-path-ineligible.\n */\nexport function emitFastDiscriminatedSwitch(\n g: FastGen,\n discriminator: string,\n cases: readonly DiscriminatorCase[],\n options: readonly SchemaIR[],\n): string | null {\n const x = g.input;\n const discKey = escapeString(discriminator);\n const helperName = g.temp(\"du\");\n const helperParam = g.temp(\"dx\");\n\n // The switch body is its own function: size-gate the options against the cap\n // in a fresh scope, otherwise many small options accumulate into the caller's\n // scope while this helper itself grows unbounded past the TurboFan budget.\n //\n // The switch is over the literal labels themselves. An earlier revision\n // routed three or more string labels through a `{value: ordinal}` table and\n // switched on the ordinal, on the theory that V8 compiles a string switch as\n // sequential comparisons and a dense integer one as a jump table. Measured on\n // V8 13.x (node 24) that is a loss at every size: the table's `t[v]` is a\n // keyed load whose key varies per parse, which goes megamorphic the moment\n // the input rotates through the options, while the string switch stays\n // nearly flat (8.6 ns at 8 cases, 10.1 at 32, 17.3 at 80, rotating input)\n // against the table's 12.9, 15.8 and 20.8. The build path switches the same\n // way (see `buildDispatch`).\n const body = g.scoped(helperParam);\n const caseStrs: string[] = [];\n for (const { value, option: index } of cases) {\n const check = body.visit(options[index] as SchemaIR, { discSkipKey: discriminator });\n if (check === null) return null;\n caseStrs.push(`case ${literalToJs(value)}:return ${check};`);\n }\n g.ctx.preamble.push(\n `function ${helperName}(${helperParam}){${declareFastTemps(body.scope)}switch(${helperParam}[${discKey}]){${caseStrs.join(\"\")}default:return false;}}`,\n );\n\n return `typeof ${x}===\"object\"&&${x}!==null&&!Array.isArray(${x})&&${helperName}(${x})`;\n}\n\nexport function fastDiscriminatedUnion(ir: DiscriminatedUnionIR, g: FastGen): string | null {\n return emitFastDiscriminatedSwitch(g, ir.discriminator, ir.cases, ir.options);\n}\n\n/**\n * Minimum option count for rewriting a plain `z.union` to switch dispatch. Below\n * this the switch helper's fixed call overhead loses to a fully-inlined\n * `||`-chain that V8 keeps flat: measured crossover is ~4 options (n=3 can hit\n * 0.6x — a regression — while n=5 is 1.27x and n=32 is 2.34x), so 5 captures the\n * stable wins with margin and never regresses a small union. Real\n * `z.discriminatedUnion` is unaffected — it dispatches via switch by construction\n * regardless of size.\n */\nconst MIN_AUTO_DISCRIMINATE_OPTIONS = 5;\n\n/**\n * Values that switch correctly under `===` (excludes `undefined` and `NaN`).\n *\n * Also excludes a SYMBOL — now that `LiteralIR.values` admits one, this is the\n * guard that keeps it out. A symbol discriminant has no source form, so there\n * is no `case` label to emit for it (`literalToJs` refuses it by type), and the\n * ordinal-table variant is string-keyed besides. Detection bails to the\n * `||`-chain, where the option's own literal check does the right thing by\n * reading the value list off the retained schema.\n */\nfunction isSwitchableDiscriminant(v: LiteralValue): v is string | number | boolean | bigint | null {\n return (\n v === null ||\n typeof v === \"string\" ||\n typeof v === \"boolean\" ||\n typeof v === \"bigint\" ||\n (typeof v === \"number\" && !Number.isNaN(v))\n );\n}\n\n/**\n * Detect whether a plain (untagged) `z.union` is *structurally* a discriminated\n * union, so its fast path can use O(1) switch dispatch instead of probing every\n * arm. Returns the discriminator + dispatch table, or null to keep the\n * `||`-chain.\n *\n * Requires (proving the switch accepts exactly what the `||`-chain would): every\n * option is a plain object that pins one shared key to a REQUIRED literal\n * (`prop.type === \"literal\"` — an optional/non-literal key is rejected), and the\n * literal values are pairwise DISJOINT across options. Disjointness is the crux:\n * it guarantees at most one option can accept any given input, so dispatching to\n * that single option is equivalent to trying them all. Any value shared by two\n * options (ambiguous), a non-switchable value (`undefined`/`NaN`), or a\n * non-object option makes detection bail to the safe `||`-chain.\n *\n * The slow path is untouched: it keeps `z.union`'s sequential trial and its\n * `invalid_union` error shape, so failure output stays byte-identical to Zod.\n * The fast path and the build path both dispatch on the result — the build\n * path from two options up (`minOptions`), since its options are hosted calls\n * either way and a switch only ever replaces probes with one call; see\n * `buildUnion`.\n */\nexport function detectUnionDiscriminator(\n options: readonly SchemaIR[],\n minOptions: number = MIN_AUTO_DISCRIMINATE_OPTIONS,\n): { discriminator: string; cases: DiscriminatorCase[] } | null {\n if (options.length < minOptions) return null;\n const objects: ObjectIR[] = [];\n for (const option of options) {\n if (option.type !== \"object\") return null;\n objects.push(option);\n }\n const first = objects[0];\n if (first === undefined) return null; // unreachable (length checked above)\n\n // Only keys present in the first option can be shared by all; try each.\n candidate: for (const key of Object.keys(first.properties)) {\n const seen = new Set<string | number | boolean | bigint | null>();\n const cases: DiscriminatorCase[] = [];\n for (const [i, object] of objects.entries()) {\n const prop = object.properties[key];\n if (prop === undefined || prop.type !== \"literal\") continue candidate;\n for (const value of prop.values) {\n if (!isSwitchableDiscriminant(value)) continue candidate;\n if (seen.has(value)) continue candidate; // shared value → ambiguous dispatch\n seen.add(value);\n cases.push({ value, option: i });\n }\n }\n return { discriminator: key, cases };\n }\n return null;\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
+ import { ZC_ASYNC_DECL, ZC_SR_DECL, ZC_SR_OK_DECL, ZC_SR_RUN_DECL } from "../issue-decls.js";
1
2
  import { emitConstant, emitEffectCallable, emitRuntimeHelper, extendStaticPath, extendStaticPathIndex } from "../context.js";
2
3
  import { emit } from "../emit.js";
3
- import { ZC_ASYNC_DECL, ZC_SR_DECL, ZC_SR_OK_DECL, ZC_SR_RUN_DECL } from "../issue-decls.js";
4
4
  //#region src/core/codegen/schemas/effect.ts
5
5
  /**
6
6
  * Generate code for a TransformEffectIR node.
@@ -44,7 +44,7 @@ function slowEffect(ir, g) {
44
44
  function refineCheck(check, expr, g) {
45
45
  const message = check.message ?? g.typeMsg;
46
46
  const messageProp = message === void 0 ? "" : `,message:${JSON.stringify(message)}`;
47
- const path = (check.path ?? []).reduce((acc, segment) => typeof segment === "number" ? extendStaticPathIndex(acc, segment) : extendStaticPath(acc, segment), g.path);
47
+ const path = (check.path ?? []).reduce((acc, segment) => typeof segment === "number" ? extendStaticPathIndex(g.ctx, acc, segment) : extendStaticPath(g.ctx, acc, segment), g.path);
48
48
  const paramsProp = check.paramsRefIndex === void 0 ? "" : `,params:${emitConstant(g.ctx, "rpa", `__rf[${check.paramsRefIndex}]`)}`;
49
49
  const asy = emitRuntimeHelper(g.ctx, "__zcAsy", ZC_ASYNC_DECL);
50
50
  const result = g.temp("rr");
@@ -1 +1 @@
1
- {"version":3,"file":"effect.js","names":[],"sources":["../../../../src/core/codegen/schemas/effect.ts"],"sourcesContent":["import type {\n PreprocessEffectIR,\n RefineEffectCheckIR,\n SuperRefineEffectCheckIR,\n TransformEffectIR,\n} from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n emitConstant,\n emitEffectCallable,\n emitRuntimeHelper,\n extendStaticPath,\n extendStaticPathIndex,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { ZC_ASYNC_DECL, ZC_SR_DECL, ZC_SR_OK_DECL, ZC_SR_RUN_DECL } from \"../issue-decls.js\";\n\n/**\n * Generate code for a TransformEffectIR node.\n * Validates the inner schema, then applies the transform function and writes back the result.\n */\nexport function slowEffect(ir: TransformEffectIR | PreprocessEffectIR, g: SlowGen): string {\n // A callback that returns a Promise is zod's synchronous-parse error, not a\n // value (see ZC_ASYNC_DECL); zod tests `_out instanceof Promise` right after\n // the call, so the guard sits at the same point.\n const asy = emitRuntimeHelper(g.ctx, \"__zcAsy\", ZC_ASYNC_DECL);\n if (ir.effectKind === \"preprocess\") {\n const valueVar = g.temp(\"pv\");\n // The callback's result is written to the output first, and the inner\n // schema then reads and writes it through that ONE expression, as a root\n // does. A rewriting inner reads back what it wrote — `.trim().min(1)`\n // measures the trimmed string, a coercion checks the converted value — so\n // handing it the result as its input and the output as its output made those\n // checks see the value from before the rewrite: nested in a walk that runs\n // eagerly, `z.preprocess(fn, z.string().trim().min(1))` accepted `\" \"`.\n return `${emit`\n var ${valueVar}=${emitEffectCallable(g.ctx, ir)}(${g.input});\n if(${valueVar} instanceof Promise)${asy}();\n ${g.output}=${valueVar};\n ${g.visit(ir.inner, { input: g.output, output: g.output, aborted: g.aborted })}\n `}\\n`;\n }\n\n const beforeVar = g.temp(\"ib\");\n const innerCode = g.visit(ir.inner);\n // A transform is `inner.transform(fn)` = a pipe(inner, transform): zod's\n // handlePipeResult aborts when `inner` produces any issue. Inside a union the\n // option must therefore count as aborted even if `inner`'s only issue is a\n // non-aborting `custom`/check-level code (mirrors slowPipe's abort branch).\n const abortBranch = g.aborted ? `else{${g.aborted}=true;}` : \"\";\n\n return `${emit`\n var ${beforeVar}=${g.issues}.length;\n ${innerCode}\n if(${g.issues}.length===${beforeVar}){\n ${g.output}=${emitEffectCallable(g.ctx, ir)}(${g.output});\n if(${g.output} instanceof Promise)${asy}();\n }${abortBranch}\n `}\\n`;\n}\n\n/**\n * Generate code for a RefineEffectCheckIR (inline refine function call).\n * Called from string/number/object check loops when a refine_effect is encountered.\n *\n * @param check - The refine effect check IR\n * @param expr - The expression to validate (may differ from g.input, e.g. objVar in object generators)\n * @param g - SlowGen context (provides path, issues)\n */\nexport function refineCheck(check: RefineEffectCheckIR, expr: string, g: SlowGen): string {\n // The refine's own message wins; without one, zod's finalizeIssue falls\n // through to the error map of the schema the check is attached to (the check\n // stamps its owner onto the issue), so the node's schema-level message is the\n // next rung. With neither, __zcFin applies the locale default.\n const message = check.message ?? g.typeMsg;\n const messageProp = message === undefined ? \"\" : `,message:${JSON.stringify(message)}`;\n // `.refine(fn, { path })` reports against a member of the refined value, so\n // the configured segments extend this node's path.\n const path = (check.path ?? []).reduce<string>(\n (acc, segment) =>\n typeof segment === \"number\"\n ? extendStaticPathIndex(acc, segment)\n : extendStaticPath(acc, segment),\n g.path,\n );\n // `.refine(fn, { params })`: $ZodCustom's check does\n // `if (def.params) _iss.params = def.params`, so the key is present only when\n // the schema declared one, and holds the ORIGINAL object — the extractor\n // parked it in `__rf` precisely so the reference survives (see\n // RefineEffectCheckIR.paramsRefIndex). Placed before `message` to keep the\n // key order zod produces.\n const paramsProp =\n check.paramsRefIndex === undefined\n ? \"\"\n : `,params:${emitConstant(g.ctx, \"rpa\", `__rf[${check.paramsRefIndex}]`)}`;\n // The predicate's return value is tested for a Promise before its truthiness\n // (see ZC_ASYNC_DECL): a Promise is truthy, so without the guard a plain\n // function returning one would ACCEPT every input where zod throws.\n const asy = emitRuntimeHelper(g.ctx, \"__zcAsy\", ZC_ASYNC_DECL);\n const result = g.temp(\"rr\");\n return emit`\n var ${result}=${emitEffectCallable(g.ctx, check)}(${expr});\n if(${result} instanceof Promise)${asy}();\n if(!${result}){\n ${g.issues}.push({code:\"custom\",path:${path}${paramsProp},input:${expr}${messageProp}});\n }`;\n}\n\n/**\n * Fast-path test for a RefineEffectCheckIR: the predicate's verdict, bound to a\n * local so a Promise can be told apart from a truthy value. A Promise throws\n * zod's `$ZodAsyncError` right here, EAGERLY: zod's `runChecks` throws the\n * moment a check returns a Promise under a synchronous parse — before any later\n * check, and even inside a union whose next option would have matched — so\n * deferring the throw to the slow walk (which a fast-eligible schema only runs\n * when `.error` is read) would turn zod's throw into a failure result. The\n * throw itself lives in a hosted helper, so the hot expression only pays the\n * `instanceof` test.\n */\nexport function fastRefineTest(check: RefineEffectCheckIR, expr: string, g: FastGen): string {\n const asy = emitRuntimeHelper(g.ctx, \"__zcAsy\", ZC_ASYNC_DECL);\n const verdict = g.local(\"fr\");\n return `((${verdict}=${emitEffectCallable(g.ctx, check)}(${expr})) instanceof Promise?${asy}():${verdict})`;\n}\n\n/**\n * Declare the shared invoker both superRefine helpers call. It is module-local\n * rather than an imported helper (lean mode declares it in the runtime module\n * beside them), so inline mode must place it in the preamble itself.\n */\nfunction emitSuperRefineRunner(ctx: SlowGen[\"ctx\"]): void {\n if (ctx.mode !== \"lean\" && !ctx.preamble.includes(ZC_SR_RUN_DECL)) {\n ctx.preamble.push(ZC_SR_RUN_DECL);\n }\n}\n\n/**\n * Fast-path test for a SuperRefineEffectCheckIR: a boolean term reporting that\n * the callback added no issue and left the value alone (see ZC_SR_OK_DECL).\n */\nexport function superRefineFastTest(\n check: SuperRefineEffectCheckIR,\n expr: string,\n g: FastGen,\n): string {\n emitSuperRefineRunner(g.ctx);\n const ok = emitRuntimeHelper(g.ctx, \"__zcSrOk\", ZC_SR_OK_DECL);\n return `${ok}(${emitEffectCallable(g.ctx, check)},${expr})`;\n}\n\n/**\n * Generate code for a SuperRefineEffectCheckIR: call the payload-taking\n * callback with a synthesized `{ value, issues }` and merge what it collected.\n * zod's own wrapper installs `addIssue` and normalizes the result, so the\n * issues are zod's — the helper only reprojects them onto this node's path and\n * strips the internal bookkeeping zod deletes before they become visible.\n *\n * The payload's `value` is writable public API, so it is written back to the\n * output slot the way an overwrite effect is. This walk is always the eager one\n * (the node reports as mutating, see hasMutation), so the write-back lands on\n * every parse that reaches it — not only on failures.\n */\nexport function superRefineCheck(\n check: SuperRefineEffectCheckIR,\n expr: string,\n g: SlowGen,\n): string {\n emitSuperRefineRunner(g.ctx);\n const helper = emitRuntimeHelper(g.ctx, \"__zcSr\", ZC_SR_DECL);\n const fn = emitEffectCallable(g.ctx, check);\n const p = g.temp(\"sp\");\n // The owning schema's message covers an added issue that carries none of its\n // own, as it does for a refine (see refineCheck); the helper applies it.\n const msgArg = g.typeMsg === undefined ? \"\" : `,${JSON.stringify(g.typeMsg)}`;\n let code = `var ${p}=${helper}(${fn},${expr},${g.path},${g.issues}${msgArg});${g.output}=${p}.value;`;\n // Inside a union option, an aborting issue must mark the option aborted so\n // pruning matches zod (see ZC_SR_DECL); elsewhere the flag is unobserved.\n if (g.aborted) code += `if(${p}.aborted){${g.aborted}=true;}`;\n return code;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"effect.js","names":[],"sources":["../../../../src/core/codegen/schemas/effect.ts"],"sourcesContent":["import type {\n PreprocessEffectIR,\n RefineEffectCheckIR,\n SuperRefineEffectCheckIR,\n TransformEffectIR,\n} from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n emitConstant,\n emitEffectCallable,\n emitRuntimeHelper,\n extendStaticPath,\n extendStaticPathIndex,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { ZC_ASYNC_DECL, ZC_SR_DECL, ZC_SR_OK_DECL, ZC_SR_RUN_DECL } from \"../issue-decls.js\";\n\n/**\n * Generate code for a TransformEffectIR node.\n * Validates the inner schema, then applies the transform function and writes back the result.\n */\nexport function slowEffect(ir: TransformEffectIR | PreprocessEffectIR, g: SlowGen): string {\n // A callback that returns a Promise is zod's synchronous-parse error, not a\n // value (see ZC_ASYNC_DECL); zod tests `_out instanceof Promise` right after\n // the call, so the guard sits at the same point.\n const asy = emitRuntimeHelper(g.ctx, \"__zcAsy\", ZC_ASYNC_DECL);\n if (ir.effectKind === \"preprocess\") {\n const valueVar = g.temp(\"pv\");\n // The callback's result is written to the output first, and the inner\n // schema then reads and writes it through that ONE expression, as a root\n // does. A rewriting inner reads back what it wrote — `.trim().min(1)`\n // measures the trimmed string, a coercion checks the converted value — so\n // handing it the result as its input and the output as its output made those\n // checks see the value from before the rewrite: nested in a walk that runs\n // eagerly, `z.preprocess(fn, z.string().trim().min(1))` accepted `\" \"`.\n return `${emit`\n var ${valueVar}=${emitEffectCallable(g.ctx, ir)}(${g.input});\n if(${valueVar} instanceof Promise)${asy}();\n ${g.output}=${valueVar};\n ${g.visit(ir.inner, { input: g.output, output: g.output, aborted: g.aborted })}\n `}\\n`;\n }\n\n const beforeVar = g.temp(\"ib\");\n const innerCode = g.visit(ir.inner);\n // A transform is `inner.transform(fn)` = a pipe(inner, transform): zod's\n // handlePipeResult aborts when `inner` produces any issue. Inside a union the\n // option must therefore count as aborted even if `inner`'s only issue is a\n // non-aborting `custom`/check-level code (mirrors slowPipe's abort branch).\n const abortBranch = g.aborted ? `else{${g.aborted}=true;}` : \"\";\n\n return `${emit`\n var ${beforeVar}=${g.issues}.length;\n ${innerCode}\n if(${g.issues}.length===${beforeVar}){\n ${g.output}=${emitEffectCallable(g.ctx, ir)}(${g.output});\n if(${g.output} instanceof Promise)${asy}();\n }${abortBranch}\n `}\\n`;\n}\n\n/**\n * Generate code for a RefineEffectCheckIR (inline refine function call).\n * Called from string/number/object check loops when a refine_effect is encountered.\n *\n * @param check - The refine effect check IR\n * @param expr - The expression to validate (may differ from g.input, e.g. objVar in object generators)\n * @param g - SlowGen context (provides path, issues)\n */\nexport function refineCheck(check: RefineEffectCheckIR, expr: string, g: SlowGen): string {\n // The refine's own message wins; without one, zod's finalizeIssue falls\n // through to the error map of the schema the check is attached to (the check\n // stamps its owner onto the issue), so the node's schema-level message is the\n // next rung. With neither, __zcFin applies the locale default.\n const message = check.message ?? g.typeMsg;\n const messageProp = message === undefined ? \"\" : `,message:${JSON.stringify(message)}`;\n // `.refine(fn, { path })` reports against a member of the refined value, so\n // the configured segments extend this node's path.\n const path = (check.path ?? []).reduce<string>(\n (acc, segment) =>\n typeof segment === \"number\"\n ? extendStaticPathIndex(g.ctx, acc, segment)\n : extendStaticPath(g.ctx, acc, segment),\n g.path,\n );\n // `.refine(fn, { params })`: $ZodCustom's check does\n // `if (def.params) _iss.params = def.params`, so the key is present only when\n // the schema declared one, and holds the ORIGINAL object — the extractor\n // parked it in `__rf` precisely so the reference survives (see\n // RefineEffectCheckIR.paramsRefIndex). Placed before `message` to keep the\n // key order zod produces.\n const paramsProp =\n check.paramsRefIndex === undefined\n ? \"\"\n : `,params:${emitConstant(g.ctx, \"rpa\", `__rf[${check.paramsRefIndex}]`)}`;\n // The predicate's return value is tested for a Promise before its truthiness\n // (see ZC_ASYNC_DECL): a Promise is truthy, so without the guard a plain\n // function returning one would ACCEPT every input where zod throws.\n const asy = emitRuntimeHelper(g.ctx, \"__zcAsy\", ZC_ASYNC_DECL);\n const result = g.temp(\"rr\");\n return emit`\n var ${result}=${emitEffectCallable(g.ctx, check)}(${expr});\n if(${result} instanceof Promise)${asy}();\n if(!${result}){\n ${g.issues}.push({code:\"custom\",path:${path}${paramsProp},input:${expr}${messageProp}});\n }`;\n}\n\n/**\n * Fast-path test for a RefineEffectCheckIR: the predicate's verdict, bound to a\n * local so a Promise can be told apart from a truthy value. A Promise throws\n * zod's `$ZodAsyncError` right here, EAGERLY: zod's `runChecks` throws the\n * moment a check returns a Promise under a synchronous parse — before any later\n * check, and even inside a union whose next option would have matched — so\n * deferring the throw to the slow walk (which a fast-eligible schema only runs\n * when `.error` is read) would turn zod's throw into a failure result. The\n * throw itself lives in a hosted helper, so the hot expression only pays the\n * `instanceof` test.\n */\nexport function fastRefineTest(check: RefineEffectCheckIR, expr: string, g: FastGen): string {\n const asy = emitRuntimeHelper(g.ctx, \"__zcAsy\", ZC_ASYNC_DECL);\n const verdict = g.local(\"fr\");\n return `((${verdict}=${emitEffectCallable(g.ctx, check)}(${expr})) instanceof Promise?${asy}():${verdict})`;\n}\n\n/**\n * Declare the shared invoker both superRefine helpers call. It is module-local\n * rather than an imported helper (lean mode declares it in the runtime module\n * beside them), so inline mode must place it in the preamble itself.\n */\nfunction emitSuperRefineRunner(ctx: SlowGen[\"ctx\"]): void {\n if (ctx.mode !== \"lean\" && !ctx.preamble.includes(ZC_SR_RUN_DECL)) {\n ctx.preamble.push(ZC_SR_RUN_DECL);\n }\n}\n\n/**\n * Fast-path test for a SuperRefineEffectCheckIR: a boolean term reporting that\n * the callback added no issue and left the value alone (see ZC_SR_OK_DECL).\n */\nexport function superRefineFastTest(\n check: SuperRefineEffectCheckIR,\n expr: string,\n g: FastGen,\n): string {\n emitSuperRefineRunner(g.ctx);\n const ok = emitRuntimeHelper(g.ctx, \"__zcSrOk\", ZC_SR_OK_DECL);\n return `${ok}(${emitEffectCallable(g.ctx, check)},${expr})`;\n}\n\n/**\n * Generate code for a SuperRefineEffectCheckIR: call the payload-taking\n * callback with a synthesized `{ value, issues }` and merge what it collected.\n * zod's own wrapper installs `addIssue` and normalizes the result, so the\n * issues are zod's — the helper only reprojects them onto this node's path and\n * strips the internal bookkeeping zod deletes before they become visible.\n *\n * The payload's `value` is writable public API, so it is written back to the\n * output slot the way an overwrite effect is. This walk is always the eager one\n * (the node reports as mutating, see hasMutation), so the write-back lands on\n * every parse that reaches it — not only on failures.\n */\nexport function superRefineCheck(\n check: SuperRefineEffectCheckIR,\n expr: string,\n g: SlowGen,\n): string {\n emitSuperRefineRunner(g.ctx);\n const helper = emitRuntimeHelper(g.ctx, \"__zcSr\", ZC_SR_DECL);\n const fn = emitEffectCallable(g.ctx, check);\n const p = g.temp(\"sp\");\n // The owning schema's message covers an added issue that carries none of its\n // own, as it does for a refine (see refineCheck); the helper applies it.\n const msgArg = g.typeMsg === undefined ? \"\" : `,${JSON.stringify(g.typeMsg)}`;\n let code = `var ${p}=${helper}(${fn},${expr},${g.path},${g.issues}${msgArg});${g.output}=${p}.value;`;\n // Inside a union option, an aborting issue must mark the option aborted so\n // pruning matches zod (see ZC_SR_DECL); elsewhere the flag is unobserved.\n if (g.aborted) code += `if(${p}.aborted){${g.aborted}=true;}`;\n return code;\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
+ import { ZC_DELEGATE_ISSUES_DECL, ZC_RUN_DELEGATE_DECL } from "../issue-decls.js";
1
2
  import { emitRfZod, emitRuntimeHelper } from "../context.js";
2
3
  import { emit } from "../emit.js";
3
- import { ZC_DELEGATE_ISSUES_DECL, ZC_RUN_DELEGATE_DECL } from "../issue-decls.js";
4
4
  //#region src/core/codegen/schemas/fallback.ts
5
5
  function slowFallback(ir, g) {
6
6
  if (ir.refIndex !== void 0) return slowZodDelegate(ir.refIndex, g);
@@ -1,7 +1,7 @@
1
+ import { ZC_FZ_DECL, ZC_PFX_DECL, propertyKeyTest } from "../issue-decls.js";
1
2
  import { declareFastTemps, emitRuntimeHelper, hasMutation } from "../context.js";
2
3
  import { emit } from "../emit.js";
3
4
  import { invalidType } from "../emit-issue.js";
4
- import { ZC_FZ_DECL, ZC_PFX_DECL, propertyKeyTest } from "../issue-decls.js";
5
5
  //#region src/core/codegen/schemas/map.ts
6
6
  /**
7
7
  * Map entries report through zod's `handleMapResult`, which branches on the
@@ -1,7 +1,7 @@
1
+ import { ZC_FSR_DECL } from "../issue-decls.js";
1
2
  import { checkPriority, emitRuntimeHelper } from "../context.js";
2
3
  import { emit } from "../emit.js";
3
4
  import { invalidType, tooBig, tooSmall } from "../emit-issue.js";
4
- import { ZC_FSR_DECL } from "../issue-decls.js";
5
5
  import { fastRefineTest, refineCheck, superRefineCheck, superRefineFastTest } from "./effect.js";
6
6
  //#region src/core/codegen/schemas/number.ts
7
7
  /**
@@ -1,7 +1,7 @@
1
+ import { ZC_AB_DECL, ZC_PROTO_SCRUB_DECL } from "../issue-decls.js";
1
2
  import { declareFastTemps, emitRuntimeHelper, escapeString, extendPath, extendStaticPath, hasMutation, keyMembershipTest, outputAlwaysDefined } from "../context.js";
2
3
  import { emit } from "../emit.js";
3
4
  import { invalidType, unrecognizedKeys } from "../emit-issue.js";
4
- import { ZC_AB_DECL, ZC_PROTO_SCRUB_DECL } from "../issue-decls.js";
5
5
  import { fastRefineTest, refineCheck, superRefineCheck, superRefineFastTest } from "./effect.js";
6
6
  import { orderByRuntimeCost } from "../fast-size.js";
7
7
  //#region src/core/codegen/schemas/object.ts
@@ -53,7 +53,7 @@ function slowObject(ir, g) {
53
53
  const slots = [];
54
54
  for (const [key, propIR] of parsedProperties(ir)) {
55
55
  const keyStr = escapeString(key);
56
- const propPath = extendStaticPath(g.path, key);
56
+ const propPath = extendStaticPath(g.ctx, g.path, key);
57
57
  const propExpr = g.temp("sv");
58
58
  code += `var ${propExpr}=${g.input}[${keyStr}];`;
59
59
  let outExpr = propExpr;
@@ -144,7 +144,7 @@ function slowObject(ir, g) {
144
144
  const keys = Object.keys(ir.properties);
145
145
  const kVar = g.temp("ck");
146
146
  const test = keyMembershipTest(g.ctx, keys, kVar);
147
- const catchallPath = extendPath(g.path, kVar);
147
+ const catchallPath = extendPath(g.ctx, g.path, kVar);
148
148
  let body;
149
149
  if (rebuild) {
150
150
  const slot = `${objVar}[${kVar}]`;
@@ -1 +1 @@
1
- {"version":3,"file":"object.js","names":[],"sources":["../../../../src/core/codegen/schemas/object.ts"],"sourcesContent":["import type { ObjectIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n emitRuntimeHelper,\n escapeString,\n extendPath,\n extendStaticPath,\n hasMutation,\n keyMembershipTest,\n outputAlwaysDefined,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, unrecognizedKeys } from \"../emit-issue.js\";\nimport { ZC_AB_DECL, ZC_PROTO_SCRUB_DECL } from \"../issue-decls.js\";\nimport { orderByRuntimeCost } from \"../fast-size.js\";\nimport { fastRefineTest, refineCheck, superRefineCheck, superRefineFastTest } from \"./effect.js\";\n\n/**\n * The shape entries whose schemas actually RUN. `$ZodObject` skips a declared\n * `__proto__` key in its shape loop (`if (key === \"__proto__\") continue`), so\n * that property is never validated and never written to the output — it is\n * only a recognized name for the strict/catchall passes, which read\n * `Object.keys(ir.properties)` directly. Writing it would be worse than\n * pointless: `{__proto__: v}` in an object literal, and `o[\"__proto__\"] = v`\n * on a plain object, both SET THE PROTOTYPE rather than define a key.\n */\nexport function parsedProperties(ir: ObjectIR): [string, SchemaIR][] {\n return Object.entries(ir.properties).filter(([key]) => key !== \"__proto__\");\n}\n\nexport function slowObject(ir: SchemaIR & { type: \"object\" }, g: SlowGen): string {\n let code = emit`\n if(typeof ${g.input}!==\"object\"||${g.input}===null||Array.isArray(${g.input})){\n ${invalidType(g, \"object\")}\n }else{`;\n\n // zod parses every object into a FRESH `{}`: the shape keys land first, in\n // shape order (handlePropertyResult), and a loose or catchall object then\n // appends the unknown keys in the input's for-in order (handleCatchall).\n //\n // Two modes reproduce that. REBUILD assembles exactly that object: always\n // for strip mode (zod's default z.object() output is only the declared keys),\n // and for a loose, strict or catchall object whenever a property or the\n // catchall rewrites its value — a rewritten value has to land somewhere, and\n // a spread clone of the input (what this once did) put it at the wrong\n // position: the input's key order, with a substituted key appended last.\n // PASS-THROUGH hands the input back by reference when nothing rewrites\n // anything; identity, key order and inherited keys then ride along with it,\n // which is the documented container-identity divergence (see\n // known-divergences.test.ts).\n const strip = ir.stripUnknownKeys === true;\n const rebuild =\n strip ||\n (ir.catchall !== undefined && hasMutation(ir.catchall)) ||\n Object.values(ir.properties).some(hasMutation);\n const objVar = g.temp(\"o\");\n if (!rebuild) {\n code += `var ${objVar}=${g.input};`;\n // The pass-through output is the input, and an own `__proto__` must not\n // ride along on it where zod's fresh `{}` never receives the key. Scrub it\n // (see ZC_PROTO_SCRUB_DECL); a rebuilt object never copies the key in the\n // first place.\n const scrub = emitRuntimeHelper(g.ctx, \"__zcPs\", ZC_PROTO_SCRUB_DECL);\n code += `${objVar}=${scrub}(${objVar});`;\n }\n /**\n * Pass-through only: land a nested node's REPLACEMENT value on the output.\n * A pass-through property never rewrites its value (that is what\n * pass-through means), but a nested object or record can still hand back a\n * proto-scrubbed COPY of its input, and that copy has to reach the output\n * object — not the caller's object. Writing it straight into `objVar` did\n * exactly that when `objVar` was the input: the caller's own object was\n * edited in place (its nested value swapped for the copy), and a frozen\n * input threw \"Cannot assign to read only property\" out of `safeParse`. So\n * the output becomes a copy of its own the first time a replacement lands,\n * and only then. Spread is safe here: `objVar` is still the input only when\n * the scrub above found no own `__proto__` on it.\n */\n const handoff = (outVar: string, inVar: string, keyExpr: string): string =>\n `if(${outVar}!==${inVar}){if(${objVar}===${g.input}){${objVar}={...${g.input}};}${objVar}[${keyExpr}]=${outVar};}`;\n\n // Object-level refines are gated on zod's ABORT rule, not on \"did anything\n // fail\": zod parses the properties (plus the strict/catchall passes) into the\n // payload and then skips its check chain only when `util.aborted` holds — i.e.\n // when one of those issues is non-continuable. A property that failed its own\n // `min`/format check reports a CONTINUABLE issue, so the outer refine still\n // runs and both messages surface — and so does an unrecognized key, which zod\n // pushes with `continue: true`; a property that failed to parse at all\n // (`invalid_type`, a bad record key) aborts and suppresses it. Snapshot the\n // issue count before the properties so the scan covers exactly this node's\n // own parse, as zod's fresh sub-payload does.\n const refineMark = ir.checks && ir.checks.length > 0 ? g.temp(\"rm\") : \"\";\n if (refineMark) code += `var ${refineMark}=${g.issues}.length;`;\n\n const skipAbsent = new Set(ir.skipAbsentKeys ?? []);\n const suppressAbsent = new Set(ir.suppressAbsentKeys ?? []);\n const nonoptional = new Set(ir.nonoptionalKeys ?? []);\n /** Rebuild only: per-property output slot + whether it is always in the result. */\n const slots: { always: boolean; keyStr: string; value: string }[] = [];\n\n for (const [key, propIR] of parsedProperties(ir)) {\n const keyStr = escapeString(key);\n const propPath = extendStaticPath(g.path, key);\n // Every mode validates the value read from the ORIGINAL input, held in a\n // local. zod parses `input[key]` — a prototype-inclusive read — so a value\n // found on the prototype is accepted and, on a rebuild, copied out as an\n // own key. Reading through a spread clone, as the loose/strict/catchall\n // branch once did, turned such a value into `undefined` (spread copies own\n // keys only): the fast and build paths, which read `input[key]`, rejected\n // the input, this walk then found nothing wrong with it, and a strict\n // object came back as a failure with an EMPTY issue list. The local also\n // spares every check the megamorphic re-load of `o[key]` — the clone's map\n // changed with every key added.\n const propExpr = g.temp(\"sv\");\n code += `var ${propExpr}=${g.input}[${keyStr}];`;\n // A rebuild assembles the output from the local afterwards (in shape order,\n // below). A pass-through property is handed a slot of its own to write a\n // replacement into, which `handoff` then lands on the output.\n let outExpr = propExpr;\n if (rebuild) {\n slots.push({ always: outputAlwaysDefined(propIR), keyStr, value: propExpr });\n } else {\n outExpr = g.temp(\"ov\");\n code += `var ${outExpr}=${propExpr};`;\n }\n const propCode = g.visit(propIR, { input: propExpr, output: outExpr, path: propPath });\n // The three absent-key rules of zod's handlePropertyResult (see ObjectIR).\n // Presence is tested on the ORIGINAL input, as zod's `key in input` is —\n // a clone would hide an inherited key.\n //\n // The two rules that test presence AFTER running the property snapshot it\n // FIRST, exactly as zod reads `isPresent` before it looks at the result. A\n // property writes nowhere but its own local, so nothing it makes of\n // `undefined` can create the very key it is asked about (writing straight\n // into the output object once did that: `z.looseObject({a: z.custom()})` on\n // `{}` lost its `nonoptional` issue and left the caller's object carrying\n // an `a` zod never writes); dropping its value is resetting that local.\n const dropValue = rebuild ? `${propExpr}=undefined;` : `${outExpr}=${propExpr};`;\n if (skipAbsent.has(key)) {\n code += emit`if(${keyStr} in ${g.input}){${propCode}}`;\n } else if (suppressAbsent.has(key)) {\n // A defaulted optional-out property runs, but a failure on an absent key\n // is discarded whole: issues and whatever value it wrote before failing.\n const beforeVar = g.temp(\"ob\");\n const presentVar = g.temp(\"op\");\n code += emit`\n var ${presentVar}=${keyStr} in ${g.input};\n var ${beforeVar}=${g.issues}.length;\n ${propCode}\n if(!${presentVar}&&${g.issues}.length>${beforeVar}){\n ${g.issues}.length=${beforeVar};${dropValue}\n }`;\n } else if (nonoptional.has(key)) {\n // A required key that is absent fails as such when its schema raised\n // nothing for the `undefined` it saw. zod pushes this one without an\n // `inst`, so no schema-level message reaches it — and it returns without\n // assigning, so whatever the property made of `undefined` is dropped.\n const beforeVar = g.temp(\"ob\");\n const presentVar = g.temp(\"op\");\n code += emit`\n var ${presentVar}=${keyStr} in ${g.input};\n var ${beforeVar}=${g.issues}.length;\n ${propCode}\n if(!${presentVar}){\n ${dropValue}\n if(${g.issues}.length===${beforeVar}){\n ${invalidType(g, \"nonoptional\", { input: \"undefined\", path: propPath, codeFirst: true, useTypeMsg: false })}\n }\n }`;\n } else {\n code += propCode;\n }\n if (!rebuild) code += handoff(outExpr, propExpr, keyStr);\n }\n\n if (rebuild) {\n // Assemble the result in shape order (zod's key order), taking as long a\n // LEADING run of always-present keys as possible into one object literal:\n // V8 stamps a literal out of a cached boilerplate map in a single\n // allocation, where adding keys one at a time walks a transition chain and\n // re-checks the map on every store — measured 8.1x (20 keys) / 2.8x (7\n // keys, one optional) on the whole safeParse.\n //\n // Everything after the first conditional key is appended so insertion order\n // still matches zod. The per-key test is zod's own: keep the key when the\n // parsed value is defined, or when it was present on the input at all\n // (`key in input`, prototype included — an own `k: undefined` survives).\n const literal: string[] = [];\n let appends = \"\";\n let leading = true;\n for (const slot of slots) {\n if (leading && slot.always) {\n literal.push(`${slot.keyStr}:${slot.value}`);\n continue;\n }\n leading = false;\n appends += slot.always\n ? `${objVar}[${slot.keyStr}]=${slot.value};`\n : `if(${slot.value}!==undefined||(${slot.keyStr} in ${g.input})){${objVar}[${slot.keyStr}]=${slot.value};}`;\n }\n code += `var ${objVar}={${literal.join(\",\")}};${appends}`;\n\n // A rebuilt loose object then takes every unknown key, in the input's\n // for-in order — zod's handleCatchall running its `unknown` catchall, which\n // hands each value back as it is and assigns it as an own key, inherited\n // ones included. (A catchall with a schema does the same in its own pass\n // below; a strict object reports the keys instead.)\n if (!strip && !ir.strict && ir.catchall === undefined) {\n const kVar = g.temp(\"lk\");\n const test = keyMembershipTest(g.ctx, Object.keys(ir.properties), kVar);\n code += emit`\n for(var ${kVar} in ${g.input}){\n if(!(${test})&&${kVar}!==\"__proto__\"){${objVar}[${kVar}]=${g.input}[${kVar}];}\n }`;\n }\n }\n\n // Strict unknown-key pass — zod's handleCatchall, byte-exact: for-in over\n // the ORIGINAL input (inherited enumerable keys count, no hasOwnProperty),\n // ALL unknown keys collected into one issue, pushed AFTER property issues\n // and before object-level refines.\n if (ir.strict) {\n const keys = Object.keys(ir.properties);\n const ukVar = g.temp(\"uk\");\n const kVar = g.temp(\"k\");\n const test = keyMembershipTest(g.ctx, keys, kVar);\n code += emit`\n var ${ukVar}=null;\n for(var ${kVar} in ${g.input}){\n if(!(${test})){(${ukVar}=${ukVar}||[]).push(${kVar});}\n }\n if(${ukVar}!==null){\n ${unrecognizedKeys(g, ukVar)}\n }`;\n }\n\n // .catchall(schema): validate every key NOT in the shape, mirroring zod's\n // handleCatchall — same bare for-in over the ORIGINAL input as the strict\n // pass (inherited enumerable keys count, no hasOwnProperty guard), each\n // issue reported at the key. Runs after the properties, as zod does. An\n // undeclared `__proto__` is skipped like zod skips it: never validated, and\n // never assigned, since `o[\"__proto__\"]=v` on a plain object would replace\n // its prototype instead of adding a key. (The strict pass above still\n // REPORTS it — there it is an unknown key like any other.)\n //\n // On a rebuild each unknown key is copied onto the fresh output before it is\n // validated — which reproduces zod, whose fresh output object gains an OWN\n // key for every for-in key it saw, inherited ones included, whether or not\n // the catchall accepted its value. The key's slot is then BOTH the input and\n // the output, exactly as a shape property's local is: a value-rewriting\n // catchall (coerce, .trim(), a default) writes through the same expression\n // it later re-reads, so splitting them makes the rewrite invisible to its own\n // checks. A pass-through catchall writes nothing, so the object stays by\n // reference; it gets the same replacement slot a pass-through property does,\n // for the same reason.\n if (ir.catchall) {\n const keys = Object.keys(ir.properties);\n const kVar = g.temp(\"ck\");\n const test = keyMembershipTest(g.ctx, keys, kVar);\n const catchallPath = extendPath(g.path, kVar);\n let body: string;\n if (rebuild) {\n const slot = `${objVar}[${kVar}]`;\n body = `${slot}=${g.input}[${kVar}];${g.visit(ir.catchall, { input: slot, output: slot, path: catchallPath })}`;\n } else {\n const inVar = g.temp(\"cv\");\n const outVar = g.temp(\"co\");\n body =\n `var ${inVar}=${g.input}[${kVar}];var ${outVar}=${inVar};` +\n g.visit(ir.catchall, { input: inVar, output: outVar, path: catchallPath }) +\n handoff(outVar, inVar, kVar);\n }\n code += emit`\n for(var ${kVar} in ${g.input}){\n if(!(${test})&&${kVar}!==\"__proto__\"){${body}}\n }`;\n }\n\n // Always written back: a rebuild is a fresh object, and a pass-through may\n // have become a copy (the scrub above, or a handoff) even where nothing\n // rewrote a value.\n code += `${g.output}=${objVar};`;\n\n // Object-level refine effects: z.object({...}).refine(fn), suppressed when the\n // parse phase aborted (see refineMark). One gate covers every effect: each of\n // them can only add a continuable `custom` issue, so zod's per-check\n // re-evaluation of `isAborted` can never flip between them.\n if (ir.checks && ir.checks.length > 0) {\n let refines = \"\";\n for (const check of ir.checks) {\n refines +=\n check.kind === \"super_refine_effect\"\n ? superRefineCheck(check, objVar, g)\n : refineCheck(check, objVar, g);\n }\n const aborted = emitRuntimeHelper(g.ctx, \"__zcAb\", ZC_AB_DECL);\n code += `if(!${aborted}(${g.issues},${refineMark})){${refines}}`;\n }\n\n code += `}\\n`;\n return code;\n}\n\n/**\n * Property entries in the order their fast-checks should be `&&`-chained:\n * cheapest first, declaration order preserved among equals (Array#sort is\n * stable). Valid input runs every conjunct whatever the order, so this costs\n * nothing on the hot path; a REJECT stops at the first false conjunct, so\n * pricing a `z.email()` behind the `kind` literal that actually discriminates\n * is what makes union probing and `.is()` misses cheap (see\n * estimateRuntimeCost). The SLOW path keeps declaration order — that one's\n * output is the issue list, whose order is part of zod parity.\n */\nfunction orderedProperties(ir: ObjectIR, g: FastGen): [string, SchemaIR][] {\n return orderByRuntimeCost(parsedProperties(ir), ([, propIR]) => propIR, g.ctx);\n}\n\n/**\n * Property/strict/refine fast-checks for an object, WITHOUT the leading\n * `typeof===object && !==null && !Array.isArray` type-guard. Returns the\n * conjunct parts (joinable with `&&`), or null if any child is fast-ineligible.\n *\n * `skipKey`, when given, omits that one property's check. Used by the\n * discriminated-union fast path (via `g.discSkipKey`): the enclosing `switch`\n * has already matched the discriminator's value, so re-checking it is redundant.\n */\nfunction fastObjectBody(ir: ObjectIR, g: FastGen, skipKey?: string): string[] | null {\n const x = g.input;\n const parts: string[] = [];\n const absentAccepts = new Set([...(ir.skipAbsentKeys ?? []), ...(ir.suppressAbsentKeys ?? [])]);\n const nonoptional = new Set(ir.nonoptionalKeys ?? []);\n\n for (const [key, propIR] of orderedProperties(ir, g)) {\n if (key === skipKey) continue;\n const keyStr = escapeString(key);\n const propCheck = g.visit(propIR, { input: `${x}[${keyStr}]` });\n if (propCheck === null) return null; // All-or-nothing\n // Presence rules keyed on the schema's `optin`/`optout` (see ObjectIR): a\n // required key must be there whatever its schema makes of `undefined`, and\n // an optional-out key on the \"optional\"/\"defaulted\" rungs is accepted when\n // it is not, whatever the property would say.\n if (nonoptional.has(key)) {\n parts.push(propCheck === \"true\" ? `${keyStr} in ${x}` : `${keyStr} in ${x}&&${propCheck}`);\n } else if (absentAccepts.has(key)) {\n parts.push(`(!(${keyStr} in ${x})||${propCheck})`);\n } else {\n parts.push(propCheck);\n }\n }\n\n // Strict unknown-key pass: hosted boolean helper (a for-in loop cannot live\n // in the && chain). Same for-in iteration as the slow path — fast/slow\n // agreement is load-bearing under the __zcFinD deferral. The membership set is\n // the FULL key list (the discriminator is a recognized key), independent of\n // skipKey, which only suppresses re-validating the discriminator's value.\n if (ir.strict) {\n const keys = Object.keys(ir.properties);\n const fnName = g.temp(\"so\");\n const test = keyMembershipTest(g.ctx, keys, \"k\");\n g.ctx.preamble.push(\n `function ${fnName}(o){for(var k in o){if(!(${test}))return false;}return true;}`,\n );\n parts.push(`${fnName}(${x})`);\n }\n\n // .catchall(schema): same hosted for-in as strict, but each unrecognized key\n // is checked against the catchall rather than rejected outright. The value is\n // hoisted into a local for the same reason fastRecord does it — repeated\n // `o[k]` computed loads are not eliminated across check boundaries.\n if (ir.catchall) {\n const kv = g.temp(\"cak\");\n const vv = g.temp(\"cav\");\n const catchallGen = g.scoped(vv);\n const valCheck = catchallGen.visit(ir.catchall, { input: vv });\n if (valCheck === null) return null;\n if (valCheck !== \"true\") {\n const fnName = g.temp(\"co\");\n const test = keyMembershipTest(g.ctx, Object.keys(ir.properties), kv);\n g.ctx.preamble.push(\n `function ${fnName}(o){${declareFastTemps(catchallGen.scope)}var ${kv},${vv};for(${kv} in o){if(!(${test})&&${kv}!==\"__proto__\"){${vv}=o[${kv}];if(!(${valCheck}))return false;}}return true;}`,\n );\n parts.push(`${fnName}(${x})`);\n }\n }\n\n // Object-level refine effects (appended last — run after property checks short-circuit)\n if (ir.checks) {\n for (const check of ir.checks) {\n if (check.kind === \"refine_effect\") {\n parts.push(fastRefineTest(check, x, g));\n } else if (check.kind === \"super_refine_effect\") {\n parts.push(superRefineFastTest(check, x, g));\n }\n }\n }\n\n return parts;\n}\n\nexport function fastObject(ir: ObjectIR, g: FastGen): string | null {\n // A strip object still gets an expression. Stripping reshapes the OUTPUT, not\n // the verdict, so this remains an exact acceptance predicate — which is what\n // `.is()` installs, and what the build path reuses to validate the subtrees it\n // passes through by reference. What it must NOT do is stand in for the parse\n // result: generateValidator withholds the `data:input` shortcut whenever the\n // schema rebuilds (see rebuildsOutput).\n const x = g.input;\n const body = fastObjectBody(ir, g, g.discSkipKey);\n if (body === null) return null;\n // Discriminated-union option: the enclosing switch (and the caller's guard)\n // already established object-ness and the discriminator value, so emit only\n // the remaining checks — no leading type-guard. An option with nothing left\n // to check accepts unconditionally (\"true\").\n if (g.discSkipKey !== undefined) {\n return body.length > 0 ? body.join(\"&&\") : \"true\";\n }\n return [`typeof ${x}===\"object\"`, `${x}!==null`, `!Array.isArray(${x})`, ...body].join(\"&&\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"object.js","names":[],"sources":["../../../../src/core/codegen/schemas/object.ts"],"sourcesContent":["import type { ObjectIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n emitRuntimeHelper,\n escapeString,\n extendPath,\n extendStaticPath,\n hasMutation,\n keyMembershipTest,\n outputAlwaysDefined,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, unrecognizedKeys } from \"../emit-issue.js\";\nimport { ZC_AB_DECL, ZC_PROTO_SCRUB_DECL } from \"../issue-decls.js\";\nimport { orderByRuntimeCost } from \"../fast-size.js\";\nimport { fastRefineTest, refineCheck, superRefineCheck, superRefineFastTest } from \"./effect.js\";\n\n/**\n * The shape entries whose schemas actually RUN. `$ZodObject` skips a declared\n * `__proto__` key in its shape loop (`if (key === \"__proto__\") continue`), so\n * that property is never validated and never written to the output — it is\n * only a recognized name for the strict/catchall passes, which read\n * `Object.keys(ir.properties)` directly. Writing it would be worse than\n * pointless: `{__proto__: v}` in an object literal, and `o[\"__proto__\"] = v`\n * on a plain object, both SET THE PROTOTYPE rather than define a key.\n */\nexport function parsedProperties(ir: ObjectIR): [string, SchemaIR][] {\n return Object.entries(ir.properties).filter(([key]) => key !== \"__proto__\");\n}\n\nexport function slowObject(ir: SchemaIR & { type: \"object\" }, g: SlowGen): string {\n let code = emit`\n if(typeof ${g.input}!==\"object\"||${g.input}===null||Array.isArray(${g.input})){\n ${invalidType(g, \"object\")}\n }else{`;\n\n // zod parses every object into a FRESH `{}`: the shape keys land first, in\n // shape order (handlePropertyResult), and a loose or catchall object then\n // appends the unknown keys in the input's for-in order (handleCatchall).\n //\n // Two modes reproduce that. REBUILD assembles exactly that object: always\n // for strip mode (zod's default z.object() output is only the declared keys),\n // and for a loose, strict or catchall object whenever a property or the\n // catchall rewrites its value — a rewritten value has to land somewhere, and\n // a spread clone of the input (what this once did) put it at the wrong\n // position: the input's key order, with a substituted key appended last.\n // PASS-THROUGH hands the input back by reference when nothing rewrites\n // anything; identity, key order and inherited keys then ride along with it,\n // which is the documented container-identity divergence (see\n // known-divergences.test.ts).\n const strip = ir.stripUnknownKeys === true;\n const rebuild =\n strip ||\n (ir.catchall !== undefined && hasMutation(ir.catchall)) ||\n Object.values(ir.properties).some(hasMutation);\n const objVar = g.temp(\"o\");\n if (!rebuild) {\n code += `var ${objVar}=${g.input};`;\n // The pass-through output is the input, and an own `__proto__` must not\n // ride along on it where zod's fresh `{}` never receives the key. Scrub it\n // (see ZC_PROTO_SCRUB_DECL); a rebuilt object never copies the key in the\n // first place.\n const scrub = emitRuntimeHelper(g.ctx, \"__zcPs\", ZC_PROTO_SCRUB_DECL);\n code += `${objVar}=${scrub}(${objVar});`;\n }\n /**\n * Pass-through only: land a nested node's REPLACEMENT value on the output.\n * A pass-through property never rewrites its value (that is what\n * pass-through means), but a nested object or record can still hand back a\n * proto-scrubbed COPY of its input, and that copy has to reach the output\n * object — not the caller's object. Writing it straight into `objVar` did\n * exactly that when `objVar` was the input: the caller's own object was\n * edited in place (its nested value swapped for the copy), and a frozen\n * input threw \"Cannot assign to read only property\" out of `safeParse`. So\n * the output becomes a copy of its own the first time a replacement lands,\n * and only then. Spread is safe here: `objVar` is still the input only when\n * the scrub above found no own `__proto__` on it.\n */\n const handoff = (outVar: string, inVar: string, keyExpr: string): string =>\n `if(${outVar}!==${inVar}){if(${objVar}===${g.input}){${objVar}={...${g.input}};}${objVar}[${keyExpr}]=${outVar};}`;\n\n // Object-level refines are gated on zod's ABORT rule, not on \"did anything\n // fail\": zod parses the properties (plus the strict/catchall passes) into the\n // payload and then skips its check chain only when `util.aborted` holds — i.e.\n // when one of those issues is non-continuable. A property that failed its own\n // `min`/format check reports a CONTINUABLE issue, so the outer refine still\n // runs and both messages surface — and so does an unrecognized key, which zod\n // pushes with `continue: true`; a property that failed to parse at all\n // (`invalid_type`, a bad record key) aborts and suppresses it. Snapshot the\n // issue count before the properties so the scan covers exactly this node's\n // own parse, as zod's fresh sub-payload does.\n const refineMark = ir.checks && ir.checks.length > 0 ? g.temp(\"rm\") : \"\";\n if (refineMark) code += `var ${refineMark}=${g.issues}.length;`;\n\n const skipAbsent = new Set(ir.skipAbsentKeys ?? []);\n const suppressAbsent = new Set(ir.suppressAbsentKeys ?? []);\n const nonoptional = new Set(ir.nonoptionalKeys ?? []);\n /** Rebuild only: per-property output slot + whether it is always in the result. */\n const slots: { always: boolean; keyStr: string; value: string }[] = [];\n\n for (const [key, propIR] of parsedProperties(ir)) {\n const keyStr = escapeString(key);\n const propPath = extendStaticPath(g.ctx, g.path, key);\n // Every mode validates the value read from the ORIGINAL input, held in a\n // local. zod parses `input[key]` — a prototype-inclusive read — so a value\n // found on the prototype is accepted and, on a rebuild, copied out as an\n // own key. Reading through a spread clone, as the loose/strict/catchall\n // branch once did, turned such a value into `undefined` (spread copies own\n // keys only): the fast and build paths, which read `input[key]`, rejected\n // the input, this walk then found nothing wrong with it, and a strict\n // object came back as a failure with an EMPTY issue list. The local also\n // spares every check the megamorphic re-load of `o[key]` — the clone's map\n // changed with every key added.\n const propExpr = g.temp(\"sv\");\n code += `var ${propExpr}=${g.input}[${keyStr}];`;\n // A rebuild assembles the output from the local afterwards (in shape order,\n // below). A pass-through property is handed a slot of its own to write a\n // replacement into, which `handoff` then lands on the output.\n let outExpr = propExpr;\n if (rebuild) {\n slots.push({ always: outputAlwaysDefined(propIR), keyStr, value: propExpr });\n } else {\n outExpr = g.temp(\"ov\");\n code += `var ${outExpr}=${propExpr};`;\n }\n const propCode = g.visit(propIR, { input: propExpr, output: outExpr, path: propPath });\n // The three absent-key rules of zod's handlePropertyResult (see ObjectIR).\n // Presence is tested on the ORIGINAL input, as zod's `key in input` is —\n // a clone would hide an inherited key.\n //\n // The two rules that test presence AFTER running the property snapshot it\n // FIRST, exactly as zod reads `isPresent` before it looks at the result. A\n // property writes nowhere but its own local, so nothing it makes of\n // `undefined` can create the very key it is asked about (writing straight\n // into the output object once did that: `z.looseObject({a: z.custom()})` on\n // `{}` lost its `nonoptional` issue and left the caller's object carrying\n // an `a` zod never writes); dropping its value is resetting that local.\n const dropValue = rebuild ? `${propExpr}=undefined;` : `${outExpr}=${propExpr};`;\n if (skipAbsent.has(key)) {\n code += emit`if(${keyStr} in ${g.input}){${propCode}}`;\n } else if (suppressAbsent.has(key)) {\n // A defaulted optional-out property runs, but a failure on an absent key\n // is discarded whole: issues and whatever value it wrote before failing.\n const beforeVar = g.temp(\"ob\");\n const presentVar = g.temp(\"op\");\n code += emit`\n var ${presentVar}=${keyStr} in ${g.input};\n var ${beforeVar}=${g.issues}.length;\n ${propCode}\n if(!${presentVar}&&${g.issues}.length>${beforeVar}){\n ${g.issues}.length=${beforeVar};${dropValue}\n }`;\n } else if (nonoptional.has(key)) {\n // A required key that is absent fails as such when its schema raised\n // nothing for the `undefined` it saw. zod pushes this one without an\n // `inst`, so no schema-level message reaches it — and it returns without\n // assigning, so whatever the property made of `undefined` is dropped.\n const beforeVar = g.temp(\"ob\");\n const presentVar = g.temp(\"op\");\n code += emit`\n var ${presentVar}=${keyStr} in ${g.input};\n var ${beforeVar}=${g.issues}.length;\n ${propCode}\n if(!${presentVar}){\n ${dropValue}\n if(${g.issues}.length===${beforeVar}){\n ${invalidType(g, \"nonoptional\", { input: \"undefined\", path: propPath, codeFirst: true, useTypeMsg: false })}\n }\n }`;\n } else {\n code += propCode;\n }\n if (!rebuild) code += handoff(outExpr, propExpr, keyStr);\n }\n\n if (rebuild) {\n // Assemble the result in shape order (zod's key order), taking as long a\n // LEADING run of always-present keys as possible into one object literal:\n // V8 stamps a literal out of a cached boilerplate map in a single\n // allocation, where adding keys one at a time walks a transition chain and\n // re-checks the map on every store — measured 8.1x (20 keys) / 2.8x (7\n // keys, one optional) on the whole safeParse.\n //\n // Everything after the first conditional key is appended so insertion order\n // still matches zod. The per-key test is zod's own: keep the key when the\n // parsed value is defined, or when it was present on the input at all\n // (`key in input`, prototype included — an own `k: undefined` survives).\n const literal: string[] = [];\n let appends = \"\";\n let leading = true;\n for (const slot of slots) {\n if (leading && slot.always) {\n literal.push(`${slot.keyStr}:${slot.value}`);\n continue;\n }\n leading = false;\n appends += slot.always\n ? `${objVar}[${slot.keyStr}]=${slot.value};`\n : `if(${slot.value}!==undefined||(${slot.keyStr} in ${g.input})){${objVar}[${slot.keyStr}]=${slot.value};}`;\n }\n code += `var ${objVar}={${literal.join(\",\")}};${appends}`;\n\n // A rebuilt loose object then takes every unknown key, in the input's\n // for-in order — zod's handleCatchall running its `unknown` catchall, which\n // hands each value back as it is and assigns it as an own key, inherited\n // ones included. (A catchall with a schema does the same in its own pass\n // below; a strict object reports the keys instead.)\n if (!strip && !ir.strict && ir.catchall === undefined) {\n const kVar = g.temp(\"lk\");\n const test = keyMembershipTest(g.ctx, Object.keys(ir.properties), kVar);\n code += emit`\n for(var ${kVar} in ${g.input}){\n if(!(${test})&&${kVar}!==\"__proto__\"){${objVar}[${kVar}]=${g.input}[${kVar}];}\n }`;\n }\n }\n\n // Strict unknown-key pass — zod's handleCatchall, byte-exact: for-in over\n // the ORIGINAL input (inherited enumerable keys count, no hasOwnProperty),\n // ALL unknown keys collected into one issue, pushed AFTER property issues\n // and before object-level refines.\n if (ir.strict) {\n const keys = Object.keys(ir.properties);\n const ukVar = g.temp(\"uk\");\n const kVar = g.temp(\"k\");\n const test = keyMembershipTest(g.ctx, keys, kVar);\n code += emit`\n var ${ukVar}=null;\n for(var ${kVar} in ${g.input}){\n if(!(${test})){(${ukVar}=${ukVar}||[]).push(${kVar});}\n }\n if(${ukVar}!==null){\n ${unrecognizedKeys(g, ukVar)}\n }`;\n }\n\n // .catchall(schema): validate every key NOT in the shape, mirroring zod's\n // handleCatchall — same bare for-in over the ORIGINAL input as the strict\n // pass (inherited enumerable keys count, no hasOwnProperty guard), each\n // issue reported at the key. Runs after the properties, as zod does. An\n // undeclared `__proto__` is skipped like zod skips it: never validated, and\n // never assigned, since `o[\"__proto__\"]=v` on a plain object would replace\n // its prototype instead of adding a key. (The strict pass above still\n // REPORTS it — there it is an unknown key like any other.)\n //\n // On a rebuild each unknown key is copied onto the fresh output before it is\n // validated — which reproduces zod, whose fresh output object gains an OWN\n // key for every for-in key it saw, inherited ones included, whether or not\n // the catchall accepted its value. The key's slot is then BOTH the input and\n // the output, exactly as a shape property's local is: a value-rewriting\n // catchall (coerce, .trim(), a default) writes through the same expression\n // it later re-reads, so splitting them makes the rewrite invisible to its own\n // checks. A pass-through catchall writes nothing, so the object stays by\n // reference; it gets the same replacement slot a pass-through property does,\n // for the same reason.\n if (ir.catchall) {\n const keys = Object.keys(ir.properties);\n const kVar = g.temp(\"ck\");\n const test = keyMembershipTest(g.ctx, keys, kVar);\n const catchallPath = extendPath(g.ctx, g.path, kVar);\n let body: string;\n if (rebuild) {\n const slot = `${objVar}[${kVar}]`;\n body = `${slot}=${g.input}[${kVar}];${g.visit(ir.catchall, { input: slot, output: slot, path: catchallPath })}`;\n } else {\n const inVar = g.temp(\"cv\");\n const outVar = g.temp(\"co\");\n body =\n `var ${inVar}=${g.input}[${kVar}];var ${outVar}=${inVar};` +\n g.visit(ir.catchall, { input: inVar, output: outVar, path: catchallPath }) +\n handoff(outVar, inVar, kVar);\n }\n code += emit`\n for(var ${kVar} in ${g.input}){\n if(!(${test})&&${kVar}!==\"__proto__\"){${body}}\n }`;\n }\n\n // Always written back: a rebuild is a fresh object, and a pass-through may\n // have become a copy (the scrub above, or a handoff) even where nothing\n // rewrote a value.\n code += `${g.output}=${objVar};`;\n\n // Object-level refine effects: z.object({...}).refine(fn), suppressed when the\n // parse phase aborted (see refineMark). One gate covers every effect: each of\n // them can only add a continuable `custom` issue, so zod's per-check\n // re-evaluation of `isAborted` can never flip between them.\n if (ir.checks && ir.checks.length > 0) {\n let refines = \"\";\n for (const check of ir.checks) {\n refines +=\n check.kind === \"super_refine_effect\"\n ? superRefineCheck(check, objVar, g)\n : refineCheck(check, objVar, g);\n }\n const aborted = emitRuntimeHelper(g.ctx, \"__zcAb\", ZC_AB_DECL);\n code += `if(!${aborted}(${g.issues},${refineMark})){${refines}}`;\n }\n\n code += `}\\n`;\n return code;\n}\n\n/**\n * Property entries in the order their fast-checks should be `&&`-chained:\n * cheapest first, declaration order preserved among equals (Array#sort is\n * stable). Valid input runs every conjunct whatever the order, so this costs\n * nothing on the hot path; a REJECT stops at the first false conjunct, so\n * pricing a `z.email()` behind the `kind` literal that actually discriminates\n * is what makes union probing and `.is()` misses cheap (see\n * estimateRuntimeCost). The SLOW path keeps declaration order — that one's\n * output is the issue list, whose order is part of zod parity.\n */\nfunction orderedProperties(ir: ObjectIR, g: FastGen): [string, SchemaIR][] {\n return orderByRuntimeCost(parsedProperties(ir), ([, propIR]) => propIR, g.ctx);\n}\n\n/**\n * Property/strict/refine fast-checks for an object, WITHOUT the leading\n * `typeof===object && !==null && !Array.isArray` type-guard. Returns the\n * conjunct parts (joinable with `&&`), or null if any child is fast-ineligible.\n *\n * `skipKey`, when given, omits that one property's check. Used by the\n * discriminated-union fast path (via `g.discSkipKey`): the enclosing `switch`\n * has already matched the discriminator's value, so re-checking it is redundant.\n */\nfunction fastObjectBody(ir: ObjectIR, g: FastGen, skipKey?: string): string[] | null {\n const x = g.input;\n const parts: string[] = [];\n const absentAccepts = new Set([...(ir.skipAbsentKeys ?? []), ...(ir.suppressAbsentKeys ?? [])]);\n const nonoptional = new Set(ir.nonoptionalKeys ?? []);\n\n for (const [key, propIR] of orderedProperties(ir, g)) {\n if (key === skipKey) continue;\n const keyStr = escapeString(key);\n const propCheck = g.visit(propIR, { input: `${x}[${keyStr}]` });\n if (propCheck === null) return null; // All-or-nothing\n // Presence rules keyed on the schema's `optin`/`optout` (see ObjectIR): a\n // required key must be there whatever its schema makes of `undefined`, and\n // an optional-out key on the \"optional\"/\"defaulted\" rungs is accepted when\n // it is not, whatever the property would say.\n if (nonoptional.has(key)) {\n parts.push(propCheck === \"true\" ? `${keyStr} in ${x}` : `${keyStr} in ${x}&&${propCheck}`);\n } else if (absentAccepts.has(key)) {\n parts.push(`(!(${keyStr} in ${x})||${propCheck})`);\n } else {\n parts.push(propCheck);\n }\n }\n\n // Strict unknown-key pass: hosted boolean helper (a for-in loop cannot live\n // in the && chain). Same for-in iteration as the slow path — fast/slow\n // agreement is load-bearing under the __zcFinD deferral. The membership set is\n // the FULL key list (the discriminator is a recognized key), independent of\n // skipKey, which only suppresses re-validating the discriminator's value.\n if (ir.strict) {\n const keys = Object.keys(ir.properties);\n const fnName = g.temp(\"so\");\n const test = keyMembershipTest(g.ctx, keys, \"k\");\n g.ctx.preamble.push(\n `function ${fnName}(o){for(var k in o){if(!(${test}))return false;}return true;}`,\n );\n parts.push(`${fnName}(${x})`);\n }\n\n // .catchall(schema): same hosted for-in as strict, but each unrecognized key\n // is checked against the catchall rather than rejected outright. The value is\n // hoisted into a local for the same reason fastRecord does it — repeated\n // `o[k]` computed loads are not eliminated across check boundaries.\n if (ir.catchall) {\n const kv = g.temp(\"cak\");\n const vv = g.temp(\"cav\");\n const catchallGen = g.scoped(vv);\n const valCheck = catchallGen.visit(ir.catchall, { input: vv });\n if (valCheck === null) return null;\n if (valCheck !== \"true\") {\n const fnName = g.temp(\"co\");\n const test = keyMembershipTest(g.ctx, Object.keys(ir.properties), kv);\n g.ctx.preamble.push(\n `function ${fnName}(o){${declareFastTemps(catchallGen.scope)}var ${kv},${vv};for(${kv} in o){if(!(${test})&&${kv}!==\"__proto__\"){${vv}=o[${kv}];if(!(${valCheck}))return false;}}return true;}`,\n );\n parts.push(`${fnName}(${x})`);\n }\n }\n\n // Object-level refine effects (appended last — run after property checks short-circuit)\n if (ir.checks) {\n for (const check of ir.checks) {\n if (check.kind === \"refine_effect\") {\n parts.push(fastRefineTest(check, x, g));\n } else if (check.kind === \"super_refine_effect\") {\n parts.push(superRefineFastTest(check, x, g));\n }\n }\n }\n\n return parts;\n}\n\nexport function fastObject(ir: ObjectIR, g: FastGen): string | null {\n // A strip object still gets an expression. Stripping reshapes the OUTPUT, not\n // the verdict, so this remains an exact acceptance predicate — which is what\n // `.is()` installs, and what the build path reuses to validate the subtrees it\n // passes through by reference. What it must NOT do is stand in for the parse\n // result: generateValidator withholds the `data:input` shortcut whenever the\n // schema rebuilds (see rebuildsOutput).\n const x = g.input;\n const body = fastObjectBody(ir, g, g.discSkipKey);\n if (body === null) return null;\n // Discriminated-union option: the enclosing switch (and the caller's guard)\n // already established object-ness and the discriminator value, so emit only\n // the remaining checks — no leading type-guard. An option with nothing left\n // to check accepts unconditionally (\"true\").\n if (g.discSkipKey !== undefined) {\n return body.length > 0 ? body.join(\"&&\") : \"true\";\n }\n return [`typeof ${x}===\"object\"`, `${x}!==null`, `!Array.isArray(${x})`, ...body].join(\"&&\");\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
+ import { ZC_FZ_DECL, ZC_HOP_DECL, ZC_PLAIN_DECL, ZC_PROTO_SCRUB_DECL } from "../issue-decls.js";
1
2
  import { declareFastTemps, emitRuntimeHelper, extendPath, hasMutation, visitMember } from "../context.js";
2
3
  import { emit } from "../emit.js";
3
4
  import { invalidType, unrecognizedKeys } from "../emit-issue.js";
4
- import { ZC_FZ_DECL, ZC_HOP_DECL, ZC_PLAIN_DECL, ZC_PROTO_SCRUB_DECL } from "../issue-decls.js";
5
5
  //#region src/core/codegen/schemas/record.ts
6
6
  /**
7
7
  * `$ZodRecord` gates on `util.isPlainObject`, NOT the looser `util.isObject`
@@ -30,7 +30,7 @@ function slowRecord(ir, g) {
30
30
  if (hasMutation(ir.valueType)) code += `if(${rec}===${g.input}){${rec}={...${g.input}};}`;
31
31
  const keyVar = g.temp("rkey");
32
32
  const keyIssuesVar = g.temp("rki");
33
- const keyPath = extendPath(g.path, keyVar);
33
+ const keyPath = extendPath(g.ctx, g.path, keyVar);
34
34
  const hop = emitRuntimeHelper(g.ctx, "__zcHop", ZC_HOP_DECL);
35
35
  const fz = emitRuntimeHelper(g.ctx, "__zcFz", ZC_FZ_DECL);
36
36
  const unrecognizedVar = ir.enumerableKeys ? g.temp("ruk") : null;
@@ -1 +1 @@
1
- {"version":3,"file":"record.js","names":[],"sources":["../../../../src/core/codegen/schemas/record.ts"],"sourcesContent":["import type { RecordIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n emitRuntimeHelper,\n extendPath,\n hasMutation,\n visitMember,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, unrecognizedKeys } from \"../emit-issue.js\";\nimport { ZC_FZ_DECL, ZC_HOP_DECL, ZC_PLAIN_DECL, ZC_PROTO_SCRUB_DECL } from \"../issue-decls.js\";\n\n/**\n * `$ZodRecord` gates on `util.isPlainObject`, NOT the looser `util.isObject`\n * that `$ZodObject` uses — see {@link ZC_PLAIN_DECL} for why the two differ and\n * what accepting the wrong one lets through.\n */\nfunction plainObjectTest(g: SlowGen | FastGen, input: string): string {\n return `${emitRuntimeHelper(g.ctx, \"__zcPlain\", ZC_PLAIN_DECL)}(${input})`;\n}\n\n/**\n * Zod's record walk opens with `if (key === \"__proto__\") continue`, so an own\n * `__proto__` data property (which `JSON.parse('{\"__proto__\":…}')` creates) is\n * neither key-validated nor value-validated nor copied. Without this the\n * compiled walk reported issues zod does not: `z.record(z.string(), z.number())`\n * over such an object raised a SECOND `invalid_type`, and a constrained key\n * schema a second `invalid_key`, for a key zod never looks at.\n */\nconst PROTO_SKIP = (keyVar: string): string => `if(${keyVar}===\"__proto__\")continue;`;\n\nexport function slowRecord(ir: SchemaIR & { type: \"record\" }, g: SlowGen): string {\n let code = emit`\n if(!${plainObjectTest(g, g.input)}){\n ${invalidType(g, \"record\")}\n }else{`;\n\n // One binding for every read and write, handed back as the output after the\n // walk: a parent can hand this node `g.input` and `g.output` as two different\n // bindings (see slowTuple).\n //\n // PROTO_SKIP keeps `__proto__` out of the WALK; the scrub keeps it out of the\n // OUTPUT, which zod also does — it copies into a fresh `{}` and skips the key\n // (see ZC_PROTO_SCRUB_DECL). Runs before the walk so the loop iterates the\n // same container the caller gets back. A mutating value schema rewrites values\n // as a matter of course, so it takes a copy up front unless the scrub already\n // made one; anything else copies only once a value hands back a replacement\n // (see visitMember).\n const rec = g.temp(\"rc\");\n code += `var ${rec}=${emitRuntimeHelper(g.ctx, \"__zcPs\", ZC_PROTO_SCRUB_DECL)}(${g.input});`;\n if (hasMutation(ir.valueType)) {\n code += `if(${rec}===${g.input}){${rec}={...${g.input}};}`;\n }\n\n const keyVar = g.temp(\"rkey\");\n const keyIssuesVar = g.temp(\"rki\");\n const keyPath = extendPath(g.path, keyVar);\n // for-in + hasOwnProperty guard instead of Object.keys(): identical\n // own-enumerable string-key set and iteration order, no keys-array\n // allocation. The loop iterates the binding as the walk found it — the clone,\n // when one was taken above — and a copy taken mid-walk for a replacement has\n // the same keys, so the key set is stable (values change, keys don't).\n // Records whose values mutate run this path eagerly, so they get the same\n // speedup the fast path does.\n const hop = emitRuntimeHelper(g.ctx, \"__zcHop\", ZC_HOP_DECL);\n\n // The key is validated at a RELATIVE path and its issues are finalized before\n // being nested, mirroring zod: `issues: keyResult.issues.map(finalizeIssue)`\n // over a payload zod ran fresh. Nested issues never reach the top-level\n // finalization loop (it walks only the outer array), so an unfinalized nest\n // leaked absolute paths and the raw `input` into the reported error.\n const fz = emitRuntimeHelper(g.ctx, \"__zcFz\", ZC_FZ_DECL);\n\n // A key the enumerable key schema rejects is unrecognized, not invalid: zod\n // collects those and pushes ONE `unrecognized_keys` after the walk, so it\n // trails every value issue (see RecordIR.enumerableKeys).\n const unrecognizedVar = ir.enumerableKeys ? g.temp(\"ruk\") : null;\n const onKeyFailure =\n unrecognizedVar === null\n ? `${g.issues}.push({code:\"invalid_key\",origin:\"record\",issues:${fz}(${keyIssuesVar}),input:${keyVar},path:${keyPath}${g.typeMsg === undefined ? \"\" : `,message:${JSON.stringify(g.typeMsg)}`}});`\n : `(${unrecognizedVar}=${unrecognizedVar}||[]).push(${keyVar});`;\n\n const keyCode = g.visit(ir.keyType, {\n input: keyVar,\n output: keyVar,\n path: \"[]\",\n issues: keyIssuesVar,\n });\n const valueCode = visitMember(g, ir.valueType, {\n container: rec,\n original: g.input,\n copy: `{...${g.input}}`,\n key: keyVar,\n path: keyPath,\n issues: g.issues,\n });\n code += emit`\n ${unrecognizedVar === null ? \"\" : `var ${unrecognizedVar}=null;`}\n for(var ${keyVar} in ${rec}){\n if(!${hop}.call(${rec},${keyVar}))continue;\n ${PROTO_SKIP(keyVar)}\n var ${keyIssuesVar}=[];\n ${keyCode}\n if(${keyIssuesVar}.length>0){\n ${onKeyFailure}\n }else{\n ${valueCode}\n }\n }\n ${unrecognizedVar === null ? \"\" : `if(${unrecognizedVar}!==null){${unrecognizedKeys(g, unrecognizedVar)}}`}\n ${g.output}=${rec};\n }`;\n return `${code}\\n`;\n}\n\nexport function fastRecord(ir: RecordIR, g: FastGen): string | null {\n const x = g.input;\n const parts: string[] = [plainObjectTest(g, x)];\n\n // Object.keys only yields strings — a plain unconstrained string key schema\n // is always satisfied, so skip generating its check entirely.\n const plainStringKey =\n ir.keyType.type === \"string\" && ir.keyType.checks.length === 0 && ir.keyType.coerce !== true;\n\n const kv = g.temp(\"rk\");\n const vv = g.temp(\"rv\");\n // Key + value checks share one fresh scope — they live in the same emitted\n // helper function, size-gated independently of the caller.\n const body = g.scoped(kv);\n const keyCheck = plainStringKey ? \"true\" : body.visit(ir.keyType, { input: kv });\n // Hoist o[k] into a loop variable: computed-key lookups don't get V8's load\n // elimination across check boundaries, so each repeated o[k] would re-walk\n // the (often dictionary-mode) object.\n const valCheck = body.visit(ir.valueType, { input: vv });\n if (keyCheck === null || valCheck === null) return null;\n\n const conditions: string[] = [];\n if (keyCheck !== \"true\") conditions.push(keyCheck);\n if (valCheck !== \"true\") conditions.push(valCheck);\n\n if (conditions.length > 0) {\n const helperName = g.temp(\"rf\");\n const valAssign = valCheck !== \"true\" ? `${vv}=o[${kv}];` : \"\";\n // for-in (no Object.keys array allocation) + hasOwnProperty guard. The\n // guard restricts iteration to own enumerable string keys — the exact set\n // Object.keys/the slow path produce — so inherited enumerable props can't\n // make the fast check disagree with the deferred slow walk. Measured 2.9x\n // (5 keys) to 5.8x (20 keys) faster than the Object.keys form; the hoisted\n // __zcHop.call inlines, making the guard ~free vs an unguarded for-in.\n const hop = emitRuntimeHelper(g.ctx, \"__zcHop\", ZC_HOP_DECL);\n g.ctx.preamble.push(\n `function ${helperName}(o){${declareFastTemps(body.scope)}var ${kv},${vv};for(${kv} in o){${PROTO_SKIP(kv)}if(${hop}.call(o,${kv})){${valAssign}if(!(${conditions.join(\"&&\")})){return false;}}}return true;}`,\n );\n parts.push(`${helperName}(${x})`);\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"record.js","names":[],"sources":["../../../../src/core/codegen/schemas/record.ts"],"sourcesContent":["import type { RecordIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n emitRuntimeHelper,\n extendPath,\n hasMutation,\n visitMember,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, unrecognizedKeys } from \"../emit-issue.js\";\nimport { ZC_FZ_DECL, ZC_HOP_DECL, ZC_PLAIN_DECL, ZC_PROTO_SCRUB_DECL } from \"../issue-decls.js\";\n\n/**\n * `$ZodRecord` gates on `util.isPlainObject`, NOT the looser `util.isObject`\n * that `$ZodObject` uses — see {@link ZC_PLAIN_DECL} for why the two differ and\n * what accepting the wrong one lets through.\n */\nfunction plainObjectTest(g: SlowGen | FastGen, input: string): string {\n return `${emitRuntimeHelper(g.ctx, \"__zcPlain\", ZC_PLAIN_DECL)}(${input})`;\n}\n\n/**\n * Zod's record walk opens with `if (key === \"__proto__\") continue`, so an own\n * `__proto__` data property (which `JSON.parse('{\"__proto__\":…}')` creates) is\n * neither key-validated nor value-validated nor copied. Without this the\n * compiled walk reported issues zod does not: `z.record(z.string(), z.number())`\n * over such an object raised a SECOND `invalid_type`, and a constrained key\n * schema a second `invalid_key`, for a key zod never looks at.\n */\nconst PROTO_SKIP = (keyVar: string): string => `if(${keyVar}===\"__proto__\")continue;`;\n\nexport function slowRecord(ir: SchemaIR & { type: \"record\" }, g: SlowGen): string {\n let code = emit`\n if(!${plainObjectTest(g, g.input)}){\n ${invalidType(g, \"record\")}\n }else{`;\n\n // One binding for every read and write, handed back as the output after the\n // walk: a parent can hand this node `g.input` and `g.output` as two different\n // bindings (see slowTuple).\n //\n // PROTO_SKIP keeps `__proto__` out of the WALK; the scrub keeps it out of the\n // OUTPUT, which zod also does — it copies into a fresh `{}` and skips the key\n // (see ZC_PROTO_SCRUB_DECL). Runs before the walk so the loop iterates the\n // same container the caller gets back. A mutating value schema rewrites values\n // as a matter of course, so it takes a copy up front unless the scrub already\n // made one; anything else copies only once a value hands back a replacement\n // (see visitMember).\n const rec = g.temp(\"rc\");\n code += `var ${rec}=${emitRuntimeHelper(g.ctx, \"__zcPs\", ZC_PROTO_SCRUB_DECL)}(${g.input});`;\n if (hasMutation(ir.valueType)) {\n code += `if(${rec}===${g.input}){${rec}={...${g.input}};}`;\n }\n\n const keyVar = g.temp(\"rkey\");\n const keyIssuesVar = g.temp(\"rki\");\n const keyPath = extendPath(g.ctx, g.path, keyVar);\n // for-in + hasOwnProperty guard instead of Object.keys(): identical\n // own-enumerable string-key set and iteration order, no keys-array\n // allocation. The loop iterates the binding as the walk found it — the clone,\n // when one was taken above — and a copy taken mid-walk for a replacement has\n // the same keys, so the key set is stable (values change, keys don't).\n // Records whose values mutate run this path eagerly, so they get the same\n // speedup the fast path does.\n const hop = emitRuntimeHelper(g.ctx, \"__zcHop\", ZC_HOP_DECL);\n\n // The key is validated at a RELATIVE path and its issues are finalized before\n // being nested, mirroring zod: `issues: keyResult.issues.map(finalizeIssue)`\n // over a payload zod ran fresh. Nested issues never reach the top-level\n // finalization loop (it walks only the outer array), so an unfinalized nest\n // leaked absolute paths and the raw `input` into the reported error.\n const fz = emitRuntimeHelper(g.ctx, \"__zcFz\", ZC_FZ_DECL);\n\n // A key the enumerable key schema rejects is unrecognized, not invalid: zod\n // collects those and pushes ONE `unrecognized_keys` after the walk, so it\n // trails every value issue (see RecordIR.enumerableKeys).\n const unrecognizedVar = ir.enumerableKeys ? g.temp(\"ruk\") : null;\n const onKeyFailure =\n unrecognizedVar === null\n ? `${g.issues}.push({code:\"invalid_key\",origin:\"record\",issues:${fz}(${keyIssuesVar}),input:${keyVar},path:${keyPath}${g.typeMsg === undefined ? \"\" : `,message:${JSON.stringify(g.typeMsg)}`}});`\n : `(${unrecognizedVar}=${unrecognizedVar}||[]).push(${keyVar});`;\n\n const keyCode = g.visit(ir.keyType, {\n input: keyVar,\n output: keyVar,\n path: \"[]\",\n issues: keyIssuesVar,\n });\n const valueCode = visitMember(g, ir.valueType, {\n container: rec,\n original: g.input,\n copy: `{...${g.input}}`,\n key: keyVar,\n path: keyPath,\n issues: g.issues,\n });\n code += emit`\n ${unrecognizedVar === null ? \"\" : `var ${unrecognizedVar}=null;`}\n for(var ${keyVar} in ${rec}){\n if(!${hop}.call(${rec},${keyVar}))continue;\n ${PROTO_SKIP(keyVar)}\n var ${keyIssuesVar}=[];\n ${keyCode}\n if(${keyIssuesVar}.length>0){\n ${onKeyFailure}\n }else{\n ${valueCode}\n }\n }\n ${unrecognizedVar === null ? \"\" : `if(${unrecognizedVar}!==null){${unrecognizedKeys(g, unrecognizedVar)}}`}\n ${g.output}=${rec};\n }`;\n return `${code}\\n`;\n}\n\nexport function fastRecord(ir: RecordIR, g: FastGen): string | null {\n const x = g.input;\n const parts: string[] = [plainObjectTest(g, x)];\n\n // Object.keys only yields strings — a plain unconstrained string key schema\n // is always satisfied, so skip generating its check entirely.\n const plainStringKey =\n ir.keyType.type === \"string\" && ir.keyType.checks.length === 0 && ir.keyType.coerce !== true;\n\n const kv = g.temp(\"rk\");\n const vv = g.temp(\"rv\");\n // Key + value checks share one fresh scope — they live in the same emitted\n // helper function, size-gated independently of the caller.\n const body = g.scoped(kv);\n const keyCheck = plainStringKey ? \"true\" : body.visit(ir.keyType, { input: kv });\n // Hoist o[k] into a loop variable: computed-key lookups don't get V8's load\n // elimination across check boundaries, so each repeated o[k] would re-walk\n // the (often dictionary-mode) object.\n const valCheck = body.visit(ir.valueType, { input: vv });\n if (keyCheck === null || valCheck === null) return null;\n\n const conditions: string[] = [];\n if (keyCheck !== \"true\") conditions.push(keyCheck);\n if (valCheck !== \"true\") conditions.push(valCheck);\n\n if (conditions.length > 0) {\n const helperName = g.temp(\"rf\");\n const valAssign = valCheck !== \"true\" ? `${vv}=o[${kv}];` : \"\";\n // for-in (no Object.keys array allocation) + hasOwnProperty guard. The\n // guard restricts iteration to own enumerable string keys — the exact set\n // Object.keys/the slow path produce — so inherited enumerable props can't\n // make the fast check disagree with the deferred slow walk. Measured 2.9x\n // (5 keys) to 5.8x (20 keys) faster than the Object.keys form; the hoisted\n // __zcHop.call inlines, making the guard ~free vs an unguarded for-in.\n const hop = emitRuntimeHelper(g.ctx, \"__zcHop\", ZC_HOP_DECL);\n g.ctx.preamble.push(\n `function ${helperName}(o){${declareFastTemps(body.scope)}var ${kv},${vv};for(${kv} in o){${PROTO_SKIP(kv)}if(${hop}.call(o,${kv})){${valAssign}if(!(${conditions.join(\"&&\")})){return false;}}}return true;}`,\n );\n parts.push(`${helperName}(${x})`);\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":";;;;;;;;;;AAkBA,SAAS,gBAAgB,GAAsB,OAAuB;CACpE,OAAO,GAAG,kBAAkB,EAAE,KAAK,aAAa,aAAa,EAAE,GAAG,MAAM;AAC1E;;;;;;;;;AAUA,MAAM,cAAc,WAA2B,MAAM,OAAO;AAE5D,SAAgB,WAAW,IAAmC,GAAoB;CAChF,IAAI,OAAO,IAAI;UACP,gBAAgB,GAAG,EAAE,KAAK,EAAE;QAC9B,YAAY,GAAG,QAAQ,EAAE;;CAc/B,MAAM,MAAM,EAAE,KAAK,IAAI;CACvB,QAAQ,OAAO,IAAI,GAAG,kBAAkB,EAAE,KAAK,UAAU,mBAAmB,EAAE,GAAG,EAAE,MAAM;CACzF,IAAI,YAAY,GAAG,SAAS,GAC1B,QAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,OAAO,EAAE,MAAM;CAGxD,MAAM,SAAS,EAAE,KAAK,MAAM;CAC5B,MAAM,eAAe,EAAE,KAAK,KAAK;CACjC,MAAM,UAAU,WAAW,EAAE,KAAK,EAAE,MAAM,MAAM;CAQhD,MAAM,MAAM,kBAAkB,EAAE,KAAK,WAAW,WAAW;CAO3D,MAAM,KAAK,kBAAkB,EAAE,KAAK,UAAU,UAAU;CAKxD,MAAM,kBAAkB,GAAG,iBAAiB,EAAE,KAAK,KAAK,IAAI;CAC5D,MAAM,eACJ,oBAAoB,OAChB,GAAG,EAAE,OAAO,mDAAmD,GAAG,GAAG,aAAa,UAAU,OAAO,QAAQ,UAAU,EAAE,YAAY,KAAA,IAAY,KAAK,YAAY,KAAK,UAAU,EAAE,OAAO,IAAI,OAC5L,IAAI,gBAAgB,GAAG,gBAAgB,aAAa,OAAO;CAEjE,MAAM,UAAU,EAAE,MAAM,GAAG,SAAS;EAClC,OAAO;EACP,QAAQ;EACR,MAAM;EACN,QAAQ;CACV,CAAC;CACD,MAAM,YAAY,YAAY,GAAG,GAAG,WAAW;EAC7C,WAAW;EACX,UAAU,EAAE;EACZ,MAAM,OAAO,EAAE,MAAM;EACrB,KAAK;EACL,MAAM;EACN,QAAQ,EAAE;CACZ,CAAC;CACD,QAAQ,IAAI;MACR,oBAAoB,OAAO,KAAK,OAAO,gBAAgB,QAAQ;cACvD,OAAO,MAAM,IAAI;YACnB,IAAI,QAAQ,IAAI,GAAG,OAAO;QAC9B,WAAW,MAAM,EAAE;YACf,aAAa;QACjB,QAAQ;WACL,aAAa;UACd,aAAa;;UAEb,UAAU;;;MAGd,oBAAoB,OAAO,KAAK,MAAM,gBAAgB,WAAW,iBAAiB,GAAG,eAAe,EAAE,GAAG;MACzG,EAAE,OAAO,GAAG,IAAI;;CAEpB,OAAO,GAAG,KAAK;AACjB;AAEA,SAAgB,WAAW,IAAc,GAA2B;CAClE,MAAM,IAAI,EAAE;CACZ,MAAM,QAAkB,CAAC,gBAAgB,GAAG,CAAC,CAAC;CAI9C,MAAM,iBACJ,GAAG,QAAQ,SAAS,YAAY,GAAG,QAAQ,OAAO,WAAW,KAAK,GAAG,QAAQ,WAAW;CAE1F,MAAM,KAAK,EAAE,KAAK,IAAI;CACtB,MAAM,KAAK,EAAE,KAAK,IAAI;CAGtB,MAAM,OAAO,EAAE,OAAO,EAAE;CACxB,MAAM,WAAW,iBAAiB,SAAS,KAAK,MAAM,GAAG,SAAS,EAAE,OAAO,GAAG,CAAC;CAI/E,MAAM,WAAW,KAAK,MAAM,GAAG,WAAW,EAAE,OAAO,GAAG,CAAC;CACvD,IAAI,aAAa,QAAQ,aAAa,MAAM,OAAO;CAEnD,MAAM,aAAuB,CAAC;CAC9B,IAAI,aAAa,QAAQ,WAAW,KAAK,QAAQ;CACjD,IAAI,aAAa,QAAQ,WAAW,KAAK,QAAQ;CAEjD,IAAI,WAAW,SAAS,GAAG;EACzB,MAAM,aAAa,EAAE,KAAK,IAAI;EAC9B,MAAM,YAAY,aAAa,SAAS,GAAG,GAAG,KAAK,GAAG,MAAM;EAO5D,MAAM,MAAM,kBAAkB,EAAE,KAAK,WAAW,WAAW;EAC3D,EAAE,IAAI,SAAS,KACb,YAAY,WAAW,MAAM,iBAAiB,KAAK,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,GAAG,SAAS,WAAW,EAAE,EAAE,KAAK,IAAI,UAAU,GAAG,KAAK,UAAU,OAAO,WAAW,KAAK,IAAI,EAAE,iCAC/K;EACA,MAAM,KAAK,GAAG,WAAW,GAAG,EAAE,EAAE;CAClC;CAEA,OAAO,MAAM,KAAK,IAAI;AACxB"}
@@ -1,7 +1,7 @@
1
+ import { ZC_CPL_DECL, ZC_LENGTH_ORIGIN_DECL, ZC_SIZE_ORIGIN_DECL } from "../issue-decls.js";
1
2
  import { emitRuntimeHelper } from "../context.js";
2
3
  import { emit } from "../emit.js";
3
4
  import { tooBig, tooSmall } from "../emit-issue.js";
4
- import { ZC_CPL_DECL, ZC_LENGTH_ORIGIN_DECL, ZC_SIZE_ORIGIN_DECL } from "../issue-decls.js";
5
5
  //#region src/core/codegen/schemas/sizeable.ts
6
6
  /**
7
7
  * The UTF-16 unit count from which a string is CERTAINLY at least `min` code
@@ -1,8 +1,8 @@
1
+ import { ZC_EMAIL_DECL } from "../issue-decls.js";
1
2
  import { EMAIL_REGEX_SOURCE, fastTestSource, isDefaultEmailPattern } from "../well-known-regex.js";
2
3
  import { checkPriority, emitEffectFn, emitRegex, emitRegexSourceString, emitRuntimeHelper, escapeString } from "../context.js";
3
4
  import { emit } from "../emit.js";
4
5
  import { invalidFormat, invalidType, tooBig, tooSmall } from "../emit-issue.js";
5
- import { ZC_EMAIL_DECL } from "../issue-decls.js";
6
6
  import { fastRefineTest, refineCheck, superRefineCheck, superRefineFastTest } from "./effect.js";
7
7
  import { stringLengthTests, whenGatedSizeChecks } from "./sizeable.js";
8
8
  //#region src/core/codegen/schemas/string.ts
@@ -57,7 +57,7 @@ function slowTuple(ir, g) {
57
57
  original: g.input,
58
58
  copy: `${x}.slice()`,
59
59
  key: String(i),
60
- path: extendStaticPathIndex(g.path, i),
60
+ path: extendStaticPathIndex(g.ctx, g.path, i),
61
61
  issues: itemIssues
62
62
  });
63
63
  const absent = `${i}>=${lenVar}`;
@@ -96,7 +96,7 @@ function slowTuple(ir, g) {
96
96
  original: g.input,
97
97
  copy: `${x}.slice()`,
98
98
  key: idxVar,
99
- path: extendPath(g.path, idxVar),
99
+ path: extendPath(g.ctx, g.path, idxVar),
100
100
  issues: g.issues
101
101
  });
102
102
  itemsCode += emit`
@@ -1 +1 @@
1
- {"version":3,"file":"tuple.js","names":[],"sources":["../../../../src/core/codegen/schemas/tuple.ts"],"sourcesContent":["import type { SchemaIR, TupleIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n extendPath,\n extendStaticPathIndex,\n hasMutation,\n outputAlwaysDefined,\n tupleRewritesShortInput,\n visitMember,\n} from \"../context.js\";\nimport { orderByRuntimeCost } from \"../fast-size.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, tooBig, tooSmall } from \"../emit-issue.js\";\n\n/**\n * What an ABSENT slot (index at or past the input's length) does to the output,\n * per zod's `handleTupleResults`:\n *\n * - `present`: can never be absent — below `optStart` without a rest element,\n * where a shorter input is already `too_small`.\n * - `skip`: at or past `optoutStart` and on the \"optional\" rung of `optin`.\n * The output ends here; the item is not consulted and nothing after it runs.\n * - `tail`: at or past `optoutStart` on any other rung. The item runs on\n * `undefined`; a failure ends the output here with its issues dropped, a\n * success is written back and may be trimmed later if it is `undefined`.\n * - `pad`: below `optoutStart`, so a later slot has to keep its index. The\n * item runs on `undefined`, its issues count, and its result — an own\n * `undefined` at the least — is written back.\n */\ntype SlotKind = \"present\" | \"skip\" | \"tail\" | \"pad\";\n\nfunction slotKinds(ir: TupleIR): SlotKind[] {\n const len = ir.items.length;\n const optoutStart = ir.optoutStart ?? len;\n const optionalIn = new Set(ir.optionalIn ?? []);\n const absentFrom = ir.rest === null ? ir.optStart : 0;\n return ir.items.map((_, i) => {\n if (i < absentFrom) return \"present\";\n if (i < optoutStart) return \"pad\";\n return optionalIn.has(i) ? \"skip\" : \"tail\";\n });\n}\n\n/**\n * Mirrors $ZodTuple. Without rest, an input shorter than `optStart` is a single\n * `too_small` (`minimum: optStart`, `inclusive: true`) that skips item\n * validation, and a longer one a single `too_big` that does NOT — zod pushes it\n * and runs the items anyway, so their issues follow it. Both are created by\n * the tuple node (schema error applies) and both share the `code, minimum |\n * maximum, inclusive, origin` key order that check-created size issues do not.\n *\n * Every item then runs, absent slots included, and the output is shaped by\n * {@link slotKinds}. The generated code keeps zod's invariant that the output\n * grows one slot at a time from the input's length: a slot that is about to be\n * handled as absent is exactly the output's current length, and once a `skip`\n * or a failed `tail` ends the output, every later absent slot sees a shorter\n * array and falls through. Ending the output at slot `i` needs no truncation\n * beyond what the failed item may have written, because nothing past `i` was\n * ever assigned.\n */\nexport function slowTuple(ir: SchemaIR & { type: \"tuple\" }, g: SlowGen): string {\n const len = ir.items.length;\n const kinds = slotKinds(ir);\n const optoutStart = ir.optoutStart ?? len;\n\n // Every read and write goes through ONE binding, seeded from the input and\n // re-pointed by each copy, with the result written to `g.output` at the end.\n // Reading `g.input` and writing `g.output` directly only works while the two\n // are the same identifier — true at every `createSlowGen` root, but not where\n // a parent hands over a separate local for each (an object's pass-through\n // property, a container member that rewrites nothing: see visitMember). There\n // a copy lands in `g.output` while the writes go on hitting the original, so\n // the output keeps the pristine array and the caller's array collects the\n // changes instead.\n const x = g.temp(\"ta\");\n\n // With a rest element the fixed items' issues are BUFFERED and flushed after\n // the rest loop, because that is the order zod reports them in: `$ZodTuple`\n // collects the fixed items into `itemResults` WITHOUT touching the payload,\n // runs the rest loop (which pushes through `handleTupleResult`), and only\n // then calls `handleTupleResults`, which pushes what it buffered. So\n // `z.tuple([z.string()]).rest(z.number())` on `[1,\"b\"]` reports the rest\n // element's issue first. `ZodError.message` is `JSON.stringify(issues)`, so\n // the order is user-visible, not just an array detail.\n const itemIssues = ir.rest === null ? g.issues : g.temp(\"tqi\");\n\n let code = emit`\n if(!Array.isArray(${g.input})){\n ${invalidType(g, \"tuple\")}\n }else{\n var ${x}=${g.input};\n ${ir.rest === null ? \"\" : `var ${itemIssues}=[];`}`;\n\n let itemsCode = \"\";\n const mutates = ir.items.some(hasMutation) || (ir.rest !== null && hasMutation(ir.rest));\n if (mutates) {\n itemsCode += `${x}=${x}.slice();`;\n }\n\n // The input's length, read once: absent slots are written back and would\n // otherwise shift the presence test for the slots after them.\n const lenVar = kinds.some((k) => k !== \"present\") ? g.temp(\"tl\") : \"\";\n if (lenVar) itemsCode += `var ${lenVar}=${x}.length;`;\n // A short input gains slots, so it is copied before the first write — the\n // caller's array must not grow. Guarded on the length so a well-formed input\n // never allocates; a mutating item has already paid for the copy.\n if (!mutates && tupleRewritesShortInput(ir)) {\n itemsCode += `if(${lenVar}<${len}){${x}=${x}.slice();}`;\n }\n\n for (let i = 0; i < len; i++) {\n const itemIR = ir.items[i] as SchemaIR;\n const elemExpr = `${x}[${i}]`;\n // Read into a local and handed back through visitMember, which lands a\n // replacement on a copy while `x` is still the caller's array.\n const itemCode = visitMember(g, itemIR, {\n container: x,\n original: g.input,\n copy: `${x}.slice()`,\n key: String(i),\n path: extendStaticPathIndex(g.path, i),\n issues: itemIssues,\n });\n // One copy of the item code per slot: an absent slot is materialized as an\n // own `undefined` first (`x[i] = undefined`, which is also what extends the\n // copied array), then validated exactly as a present one would be.\n const absent = `${i}>=${lenVar}`;\n switch (kinds[i]) {\n case \"present\":\n itemsCode += itemCode;\n break;\n case \"skip\":\n itemsCode += emit`if(${i}<${lenVar}){${itemCode}}`;\n break;\n case \"tail\": {\n const beforeVar = g.temp(\"tb\");\n itemsCode += emit`\n if(${i}<${lenVar}||${x}.length===${i}){\n var ${beforeVar}=${itemIssues}.length;\n if(${absent}){${elemExpr}=undefined;}\n ${itemCode}\n if(${absent}&&${itemIssues}.length>${beforeVar}){${itemIssues}.length=${beforeVar};${x}.length=${i};}\n }`;\n break;\n }\n case \"pad\":\n itemsCode += emit`if(${absent}){${elemExpr}=undefined;}${itemCode}`;\n break;\n }\n }\n\n // zod's trailing trim: absent slots that produced `undefined` are dropped\n // from the end while the item is optional-out. Only a `tail` slot can leave\n // one behind (a `pad` slot sits below `optoutStart`, a `skip` slot writes\n // nothing), so the loop is emitted only when some tail item might.\n if (kinds.some((k, i) => k === \"tail\" && !outputAlwaysDefined(ir.items[i] as SchemaIR))) {\n const tVar = g.temp(\"tt\");\n const floor = optoutStart > 0 ? `${tVar}>=${optoutStart}&&` : \"\";\n itemsCode += `for(var ${tVar}=${x}.length-1;${floor}${tVar}>=${lenVar}&&${x}[${tVar}]===undefined;${tVar}--){${x}.length=${tVar};}`;\n }\n\n if (ir.rest !== null) {\n const idxVar = g.temp(\"ti\");\n const restCode = visitMember(g, ir.rest, {\n container: x,\n original: g.input,\n copy: `${x}.slice()`,\n key: idxVar,\n path: extendPath(g.path, idxVar),\n issues: g.issues,\n });\n itemsCode += emit`\n for(var ${idxVar}=${len};${idxVar}<${x}.length;${idxVar}++){\n ${restCode}\n }`;\n const flushVar = g.temp(\"tqj\");\n itemsCode += `for(var ${flushVar}=0;${flushVar}<${itemIssues}.length;${flushVar}++){${g.issues}.push(${itemIssues}[${flushVar}]);}`;\n itemsCode += `${g.output}=${x};`;\n code += itemsCode;\n } else {\n const body = emit`\n if(${x}.length>${len}){\n ${tooBig(g, len, \"array\", true, { layout: \"tuple\", aborts: true })}\n }\n ${itemsCode}\n ${g.output}=${x};`;\n code +=\n ir.optStart > 0\n ? emit`\n if(${x}.length<${ir.optStart}){\n ${tooSmall(g, ir.optStart, \"array\", true, { layout: \"tuple\", aborts: true })}\n }else{\n ${body}\n }`\n : body;\n }\n\n code += `}\\n`;\n return code;\n}\n\nexport function fastTuple(ir: TupleIR, g: FastGen): string | null {\n const x = g.input;\n const parts: string[] = [`Array.isArray(${x})`];\n const kinds = slotKinds(ir);\n\n if (ir.rest === null) {\n if (ir.optStart === ir.items.length) {\n parts.push(`${x}.length===${ir.items.length}`);\n } else {\n if (ir.optStart > 0) parts.push(`${x}.length>=${ir.optStart}`);\n parts.push(`${x}.length<=${ir.items.length}`);\n }\n }\n\n // Per-index checks, cheapest-first: positions are independent, so the emitted\n // order only decides which one a reject stops on (see estimateRuntimeCost).\n // The Array.isArray + length conjuncts above stay in front — the element\n // reads are only meaningful once they hold.\n const indexed = ir.items.flatMap((itemIR, index) => (itemIR ? [{ index, itemIR }] : []));\n for (const { index, itemIR } of orderByRuntimeCost(indexed, (e) => e.itemIR, g.ctx)) {\n const itemCheck = g.visit(itemIR, { input: `${x}[${index}]` });\n if (itemCheck === null) return null;\n const kind = kinds[index];\n // The fast check answers true only where the output IS the input (see\n // fastResultIsInput), so a slot that would be written back when absent —\n // `pad` and `tail` alike — must be present here; the slow walk builds the\n // longer output. A `skip` slot is the reverse: absent, it is accepted\n // whatever its item says, since zod ends the output there without\n // consulting it.\n if (kind === \"pad\" || kind === \"tail\") {\n parts.push(\n itemCheck === \"true\" ? `${x}.length>${index}` : `${x}.length>${index}&&${itemCheck}`,\n );\n } else if (itemCheck === \"true\") {\n continue;\n } else {\n parts.push(kind === \"skip\" ? `(${x}.length<=${index}||${itemCheck})` : itemCheck);\n }\n }\n\n // Rest element validation via preamble helper (avoids .slice().every()\n // allocation). Fresh scope: the helper is its own function, size-gated\n // independently. (Fixed items above stay inline in the caller's && chain.)\n if (ir.rest !== null) {\n const rv = g.temp(\"tr\");\n const restGen = g.scoped(rv);\n const restCheck = restGen.visit(ir.rest);\n if (restCheck === null) return null;\n if (restCheck !== \"true\") {\n const helperName = g.temp(\"te\");\n g.ctx.preamble.push(\n `function ${helperName}(a,s){${declareFastTemps(restGen.scope)}for(var ${rv},i=s;i<a.length;i++){${rv}=a[i];if(!(${restCheck})){return false;}}return true;}`,\n );\n parts.push(`${helperName}(${x},${ir.items.length})`);\n }\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tuple.js","names":[],"sources":["../../../../src/core/codegen/schemas/tuple.ts"],"sourcesContent":["import type { SchemaIR, TupleIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n extendPath,\n extendStaticPathIndex,\n hasMutation,\n outputAlwaysDefined,\n tupleRewritesShortInput,\n visitMember,\n} from \"../context.js\";\nimport { orderByRuntimeCost } from \"../fast-size.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, tooBig, tooSmall } from \"../emit-issue.js\";\n\n/**\n * What an ABSENT slot (index at or past the input's length) does to the output,\n * per zod's `handleTupleResults`:\n *\n * - `present`: can never be absent — below `optStart` without a rest element,\n * where a shorter input is already `too_small`.\n * - `skip`: at or past `optoutStart` and on the \"optional\" rung of `optin`.\n * The output ends here; the item is not consulted and nothing after it runs.\n * - `tail`: at or past `optoutStart` on any other rung. The item runs on\n * `undefined`; a failure ends the output here with its issues dropped, a\n * success is written back and may be trimmed later if it is `undefined`.\n * - `pad`: below `optoutStart`, so a later slot has to keep its index. The\n * item runs on `undefined`, its issues count, and its result — an own\n * `undefined` at the least — is written back.\n */\ntype SlotKind = \"present\" | \"skip\" | \"tail\" | \"pad\";\n\nfunction slotKinds(ir: TupleIR): SlotKind[] {\n const len = ir.items.length;\n const optoutStart = ir.optoutStart ?? len;\n const optionalIn = new Set(ir.optionalIn ?? []);\n const absentFrom = ir.rest === null ? ir.optStart : 0;\n return ir.items.map((_, i) => {\n if (i < absentFrom) return \"present\";\n if (i < optoutStart) return \"pad\";\n return optionalIn.has(i) ? \"skip\" : \"tail\";\n });\n}\n\n/**\n * Mirrors $ZodTuple. Without rest, an input shorter than `optStart` is a single\n * `too_small` (`minimum: optStart`, `inclusive: true`) that skips item\n * validation, and a longer one a single `too_big` that does NOT — zod pushes it\n * and runs the items anyway, so their issues follow it. Both are created by\n * the tuple node (schema error applies) and both share the `code, minimum |\n * maximum, inclusive, origin` key order that check-created size issues do not.\n *\n * Every item then runs, absent slots included, and the output is shaped by\n * {@link slotKinds}. The generated code keeps zod's invariant that the output\n * grows one slot at a time from the input's length: a slot that is about to be\n * handled as absent is exactly the output's current length, and once a `skip`\n * or a failed `tail` ends the output, every later absent slot sees a shorter\n * array and falls through. Ending the output at slot `i` needs no truncation\n * beyond what the failed item may have written, because nothing past `i` was\n * ever assigned.\n */\nexport function slowTuple(ir: SchemaIR & { type: \"tuple\" }, g: SlowGen): string {\n const len = ir.items.length;\n const kinds = slotKinds(ir);\n const optoutStart = ir.optoutStart ?? len;\n\n // Every read and write goes through ONE binding, seeded from the input and\n // re-pointed by each copy, with the result written to `g.output` at the end.\n // Reading `g.input` and writing `g.output` directly only works while the two\n // are the same identifier — true at every `createSlowGen` root, but not where\n // a parent hands over a separate local for each (an object's pass-through\n // property, a container member that rewrites nothing: see visitMember). There\n // a copy lands in `g.output` while the writes go on hitting the original, so\n // the output keeps the pristine array and the caller's array collects the\n // changes instead.\n const x = g.temp(\"ta\");\n\n // With a rest element the fixed items' issues are BUFFERED and flushed after\n // the rest loop, because that is the order zod reports them in: `$ZodTuple`\n // collects the fixed items into `itemResults` WITHOUT touching the payload,\n // runs the rest loop (which pushes through `handleTupleResult`), and only\n // then calls `handleTupleResults`, which pushes what it buffered. So\n // `z.tuple([z.string()]).rest(z.number())` on `[1,\"b\"]` reports the rest\n // element's issue first. `ZodError.message` is `JSON.stringify(issues)`, so\n // the order is user-visible, not just an array detail.\n const itemIssues = ir.rest === null ? g.issues : g.temp(\"tqi\");\n\n let code = emit`\n if(!Array.isArray(${g.input})){\n ${invalidType(g, \"tuple\")}\n }else{\n var ${x}=${g.input};\n ${ir.rest === null ? \"\" : `var ${itemIssues}=[];`}`;\n\n let itemsCode = \"\";\n const mutates = ir.items.some(hasMutation) || (ir.rest !== null && hasMutation(ir.rest));\n if (mutates) {\n itemsCode += `${x}=${x}.slice();`;\n }\n\n // The input's length, read once: absent slots are written back and would\n // otherwise shift the presence test for the slots after them.\n const lenVar = kinds.some((k) => k !== \"present\") ? g.temp(\"tl\") : \"\";\n if (lenVar) itemsCode += `var ${lenVar}=${x}.length;`;\n // A short input gains slots, so it is copied before the first write — the\n // caller's array must not grow. Guarded on the length so a well-formed input\n // never allocates; a mutating item has already paid for the copy.\n if (!mutates && tupleRewritesShortInput(ir)) {\n itemsCode += `if(${lenVar}<${len}){${x}=${x}.slice();}`;\n }\n\n for (let i = 0; i < len; i++) {\n const itemIR = ir.items[i] as SchemaIR;\n const elemExpr = `${x}[${i}]`;\n // Read into a local and handed back through visitMember, which lands a\n // replacement on a copy while `x` is still the caller's array.\n const itemCode = visitMember(g, itemIR, {\n container: x,\n original: g.input,\n copy: `${x}.slice()`,\n key: String(i),\n path: extendStaticPathIndex(g.ctx, g.path, i),\n issues: itemIssues,\n });\n // One copy of the item code per slot: an absent slot is materialized as an\n // own `undefined` first (`x[i] = undefined`, which is also what extends the\n // copied array), then validated exactly as a present one would be.\n const absent = `${i}>=${lenVar}`;\n switch (kinds[i]) {\n case \"present\":\n itemsCode += itemCode;\n break;\n case \"skip\":\n itemsCode += emit`if(${i}<${lenVar}){${itemCode}}`;\n break;\n case \"tail\": {\n const beforeVar = g.temp(\"tb\");\n itemsCode += emit`\n if(${i}<${lenVar}||${x}.length===${i}){\n var ${beforeVar}=${itemIssues}.length;\n if(${absent}){${elemExpr}=undefined;}\n ${itemCode}\n if(${absent}&&${itemIssues}.length>${beforeVar}){${itemIssues}.length=${beforeVar};${x}.length=${i};}\n }`;\n break;\n }\n case \"pad\":\n itemsCode += emit`if(${absent}){${elemExpr}=undefined;}${itemCode}`;\n break;\n }\n }\n\n // zod's trailing trim: absent slots that produced `undefined` are dropped\n // from the end while the item is optional-out. Only a `tail` slot can leave\n // one behind (a `pad` slot sits below `optoutStart`, a `skip` slot writes\n // nothing), so the loop is emitted only when some tail item might.\n if (kinds.some((k, i) => k === \"tail\" && !outputAlwaysDefined(ir.items[i] as SchemaIR))) {\n const tVar = g.temp(\"tt\");\n const floor = optoutStart > 0 ? `${tVar}>=${optoutStart}&&` : \"\";\n itemsCode += `for(var ${tVar}=${x}.length-1;${floor}${tVar}>=${lenVar}&&${x}[${tVar}]===undefined;${tVar}--){${x}.length=${tVar};}`;\n }\n\n if (ir.rest !== null) {\n const idxVar = g.temp(\"ti\");\n const restCode = visitMember(g, ir.rest, {\n container: x,\n original: g.input,\n copy: `${x}.slice()`,\n key: idxVar,\n path: extendPath(g.ctx, g.path, idxVar),\n issues: g.issues,\n });\n itemsCode += emit`\n for(var ${idxVar}=${len};${idxVar}<${x}.length;${idxVar}++){\n ${restCode}\n }`;\n const flushVar = g.temp(\"tqj\");\n itemsCode += `for(var ${flushVar}=0;${flushVar}<${itemIssues}.length;${flushVar}++){${g.issues}.push(${itemIssues}[${flushVar}]);}`;\n itemsCode += `${g.output}=${x};`;\n code += itemsCode;\n } else {\n const body = emit`\n if(${x}.length>${len}){\n ${tooBig(g, len, \"array\", true, { layout: \"tuple\", aborts: true })}\n }\n ${itemsCode}\n ${g.output}=${x};`;\n code +=\n ir.optStart > 0\n ? emit`\n if(${x}.length<${ir.optStart}){\n ${tooSmall(g, ir.optStart, \"array\", true, { layout: \"tuple\", aborts: true })}\n }else{\n ${body}\n }`\n : body;\n }\n\n code += `}\\n`;\n return code;\n}\n\nexport function fastTuple(ir: TupleIR, g: FastGen): string | null {\n const x = g.input;\n const parts: string[] = [`Array.isArray(${x})`];\n const kinds = slotKinds(ir);\n\n if (ir.rest === null) {\n if (ir.optStart === ir.items.length) {\n parts.push(`${x}.length===${ir.items.length}`);\n } else {\n if (ir.optStart > 0) parts.push(`${x}.length>=${ir.optStart}`);\n parts.push(`${x}.length<=${ir.items.length}`);\n }\n }\n\n // Per-index checks, cheapest-first: positions are independent, so the emitted\n // order only decides which one a reject stops on (see estimateRuntimeCost).\n // The Array.isArray + length conjuncts above stay in front — the element\n // reads are only meaningful once they hold.\n const indexed = ir.items.flatMap((itemIR, index) => (itemIR ? [{ index, itemIR }] : []));\n for (const { index, itemIR } of orderByRuntimeCost(indexed, (e) => e.itemIR, g.ctx)) {\n const itemCheck = g.visit(itemIR, { input: `${x}[${index}]` });\n if (itemCheck === null) return null;\n const kind = kinds[index];\n // The fast check answers true only where the output IS the input (see\n // fastResultIsInput), so a slot that would be written back when absent —\n // `pad` and `tail` alike — must be present here; the slow walk builds the\n // longer output. A `skip` slot is the reverse: absent, it is accepted\n // whatever its item says, since zod ends the output there without\n // consulting it.\n if (kind === \"pad\" || kind === \"tail\") {\n parts.push(\n itemCheck === \"true\" ? `${x}.length>${index}` : `${x}.length>${index}&&${itemCheck}`,\n );\n } else if (itemCheck === \"true\") {\n continue;\n } else {\n parts.push(kind === \"skip\" ? `(${x}.length<=${index}||${itemCheck})` : itemCheck);\n }\n }\n\n // Rest element validation via preamble helper (avoids .slice().every()\n // allocation). Fresh scope: the helper is its own function, size-gated\n // independently. (Fixed items above stay inline in the caller's && chain.)\n if (ir.rest !== null) {\n const rv = g.temp(\"tr\");\n const restGen = g.scoped(rv);\n const restCheck = restGen.visit(ir.rest);\n if (restCheck === null) return null;\n if (restCheck !== \"true\") {\n const helperName = g.temp(\"te\");\n g.ctx.preamble.push(\n `function ${helperName}(a,s){${declareFastTemps(restGen.scope)}for(var ${rv},i=s;i<a.length;i++){${rv}=a[i];if(!(${restCheck})){return false;}}return true;}`,\n );\n parts.push(`${helperName}(${x},${ir.items.length})`);\n }\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
+ import { ZC_FZ_DECL, abortingCodeTest } from "../issue-decls.js";
1
2
  import { emitRuntimeHelper, hasMutation } from "../context.js";
2
3
  import { emit } from "../emit.js";
3
- import { ZC_FZ_DECL, abortingCodeTest } from "../issue-decls.js";
4
4
  import { detectUnionDiscriminator, emitFastDiscriminatedSwitch } from "./discriminated-union.js";
5
5
  import { orderByRuntimeCost } from "../fast-size.js";
6
6
  //#region src/core/codegen/schemas/union.ts
package/dist/runtime.d.ts CHANGED
@@ -27,6 +27,7 @@ export declare const __zcEmail: any;
27
27
  export declare const __zcPs: any;
28
28
  export declare const __zcPlain: any;
29
29
  export declare const __zcPfx: any;
30
+ export declare const __zcPa: any;
30
31
  export declare const __zcCu: any;
31
32
  export declare const __zcSr: any;
32
33
  export declare const __zcSrOk: any;
package/dist/runtime.js CHANGED
@@ -29,6 +29,7 @@ export function __zcEmail(s){var n=s.length,i=0,c,p=46;for(;;){if(i===n)return f
29
29
  export function __zcPs(o){if(!Object.prototype.hasOwnProperty.call(o,"__proto__"))return o;var c={...o};delete c["__proto__"];return c;}
30
30
  export function __zcPlain(o){if(typeof o!=="object"||o===null||Array.isArray(o))return false;var c=o.constructor;if(c===Object||c===undefined||typeof c!=="function")return true;var p=c.prototype;if(typeof p!=="object"||p===null||Array.isArray(p))return false;return Object.prototype.hasOwnProperty.call(p,"isPrototypeOf");}
31
31
  export function __zcPfx(d,s,b,k){for(var i=0;i<s.length;i++){var x=s[i];x.path=b.concat(k,x.path);d.push(x);}}
32
+ export function __zcPa(p,k){var n=p.length,r=new Array(n+1);for(var i=0;i<n;i++)r[i]=p[i];r[n]=k;return r;}
32
33
  export function __zcCu(f,v){var r=f(v);if(r&&typeof r.then==="function"){throw new __zcCore.$ZodAsyncError();}return !!r;}
33
34
  export function __zcSr(f,v,p,e,m){var q={value:v,issues:[]};__zcSrRun(f,q);for(var i=0;i<q.issues.length;i++){var s=q.issues[i],t={};for(var k in s){if(k!=="inst"&&k!=="continue")t[k]=s[k];}if(s.continue!==true){q.aborted=true;t.continue=false;}t.path=s.path&&s.path.length?p.concat(s.path):p;if(t.message===undefined&&m!==undefined)t.message=m;e.push(t);}return q;}
34
35
  export function __zcSrOk(f,v){var p={value:v,issues:[]};__zcSrRun(f,p);return p.issues.length===0&&p.value===v;}
@@ -1,6 +1,6 @@
1
+ import { ISSUE_DECLS, RUNTIME_HELPER_DECLS, ZC_SR_RUN_DECL } from "../core/codegen/issue-decls.js";
1
2
  import { WELL_KNOWN_REGEXES, fastTestSource } from "../core/codegen/well-known-regex.js";
2
3
  import { FAILZ_CLASS_DECL, FAIL_CLASS_DECL, FINZ_DECL, FIN_DECL, FIN_DEFERRED_DECL, MK_VALIDATOR_DECL, ZOD_CONFIG_IMPORT, ZOD_MSG_DECLARATION } from "../core/iife.js";
3
- import { ISSUE_DECLS, RUNTIME_HELPER_DECLS, ZC_SR_RUN_DECL } from "../core/codegen/issue-decls.js";
4
4
  //#region src/unplugin/virtual.ts
5
5
  /**
6
6
  * Virtual module "virtual:zod-compiler/runtime".
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod-compiler",
3
- "version": "2.0.5",
3
+ "version": "2.0.6",
4
4
  "description": "Compile Zod schemas into zero-overhead validation functions",
5
5
  "keywords": [
6
6
  "aot",