zod-compiler 2.0.4 → 2.0.5

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 (29) hide show
  1. package/dist/core/codegen/context.d.ts +38 -1
  2. package/dist/core/codegen/context.d.ts.map +1 -1
  3. package/dist/core/codegen/context.js +51 -1
  4. package/dist/core/codegen/context.js.map +1 -1
  5. package/dist/core/codegen/schemas/array.d.ts.map +1 -1
  6. package/dist/core/codegen/schemas/array.js +23 -17
  7. package/dist/core/codegen/schemas/array.js.map +1 -1
  8. package/dist/core/codegen/schemas/discriminated-union.d.ts.map +1 -1
  9. package/dist/core/codegen/schemas/discriminated-union.js +2 -2
  10. package/dist/core/codegen/schemas/discriminated-union.js.map +1 -1
  11. package/dist/core/codegen/schemas/effect.d.ts.map +1 -1
  12. package/dist/core/codegen/schemas/effect.js +1 -1
  13. package/dist/core/codegen/schemas/effect.js.map +1 -1
  14. package/dist/core/codegen/schemas/map.d.ts.map +1 -1
  15. package/dist/core/codegen/schemas/map.js +70 -17
  16. package/dist/core/codegen/schemas/map.js.map +1 -1
  17. package/dist/core/codegen/schemas/record.d.ts.map +1 -1
  18. package/dist/core/codegen/schemas/record.js +23 -17
  19. package/dist/core/codegen/schemas/record.js.map +1 -1
  20. package/dist/core/codegen/schemas/set.d.ts.map +1 -1
  21. package/dist/core/codegen/schemas/set.js +51 -13
  22. package/dist/core/codegen/schemas/set.js.map +1 -1
  23. package/dist/core/codegen/schemas/tuple.d.ts.map +1 -1
  24. package/dist/core/codegen/schemas/tuple.js +16 -13
  25. package/dist/core/codegen/schemas/tuple.js.map +1 -1
  26. package/dist/core/codegen/schemas/union.d.ts.map +1 -1
  27. package/dist/core/codegen/schemas/union.js +21 -15
  28. package/dist/core/codegen/schemas/union.js.map +1 -1
  29. package/package.json +1 -1
@@ -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 {\n declareFastTemps,\n escapeString,\n extendPath,\n hasMutation,\n literalToJs,\n} 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 option-applied mutations (defaults, coercions, transforms,\n // overwrite checks, stringbool) back to the output location. Each option is\n // visited with output:objVar — a fresh local — so a mutating option's clone is\n // reassigned into objVar and stranded there; without this write-back the caller\n // returns the ORIGINAL input by reference and the mutation is silently lost.\n // Gated on mutation so a pure-validation union stays a zero-write pass-through\n // (objVar still aliases the input). On the no-match/failure paths objVar equals\n // the input, so the write is a harmless self-assignment.\n if (ir.options.some(hasMutation)) {\n code += `${g.output}=${objVar};`;\n }\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.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 +1 @@
1
- {"version":3,"file":"effect.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/effect.ts"],"mappings":";;;;;;;iBAqBgB,WAAW,IAAI,oBAAoB,oBAAoB,GAAG;;;;;;;;;iBAyC1D,YAAY,OAAO,qBAAqB,cAAc,GAAG;;;;;;;;;;;;iBAkDzD,eAAe,OAAO,qBAAqB,cAAc,GAAG;;;;;iBAqB5D,oBACd,OAAO,0BACP,cACA,GAAG;;;;;;;;;;;;;iBAmBW,iBACd,OAAO,0BACP,cACA,GAAG"}
1
+ {"version":3,"file":"effect.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/effect.ts"],"mappings":";;;;;;;iBAqBgB,WAAW,IAAI,oBAAoB,oBAAoB,GAAG;;;;;;;;;iBAgD1D,YAAY,OAAO,qBAAqB,cAAc,GAAG;;;;;;;;;;;;iBAkDzD,eAAe,OAAO,qBAAqB,cAAc,GAAG;;;;;iBAqB5D,oBACd,OAAO,0BACP,cACA,GAAG;;;;;;;;;;;;;iBAmBW,iBACd,OAAO,0BACP,cACA,GAAG"}
@@ -15,7 +15,7 @@ function slowEffect(ir, g) {
15
15
  if(${valueVar} instanceof Promise)${asy}();
16
16
  ${g.output}=${valueVar};
17
17
  ${g.visit(ir.inner, {
18
- input: valueVar,
18
+ input: g.output,
19
19
  output: g.output,
20
20
  aborted: g.aborted
21
21
  })}
@@ -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 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: valueVar, 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(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 +1 @@
1
- {"version":3,"file":"map.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/map.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;iBAwBgB,QAAQ,IAAI;EAAa;GAAe,GAAG;iBA+D3C,QAAQ,IAAI,OAAO,GAAG"}
1
+ {"version":3,"file":"map.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/map.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;iBAwBgB,QAAQ,IAAI;EAAa;GAAe,GAAG;iBA6H3C,QAAQ,IAAI,OAAO,GAAG"}
@@ -26,28 +26,81 @@ function slowMap(ir, g) {
26
26
  const pkVar = g.temp("map_pk");
27
27
  const keyIssues = g.temp("map_ki");
28
28
  const valIssues = g.temp("map_vi");
29
- const mutates = hasMutation(ir.keyType) || hasMutation(ir.valueType);
30
- const rebuiltVar = mutates ? g.temp("map_n") : "";
31
29
  const msgProp = g.typeMsg === void 0 ? "" : `,message:${JSON.stringify(g.typeMsg)}`;
32
30
  const fz = emitRuntimeHelper(g.ctx, "__zcFz", ZC_FZ_DECL);
33
31
  const pfx = emitRuntimeHelper(g.ctx, "__zcPfx", ZC_PFX_DECL);
34
- const keyCode = g.visit(ir.keyType, {
35
- input: `${entryVar}[0]`,
36
- output: `${entryVar}[0]`,
37
- path: "[]",
38
- issues: keyIssues
39
- });
40
- const valCode = g.visit(ir.valueType, {
41
- input: `${entryVar}[1]`,
42
- output: `${entryVar}[1]`,
43
- path: "[]",
44
- issues: valIssues
45
- });
32
+ const keyOut = g.temp("map_ko");
33
+ const valueOut = g.temp("map_vo");
34
+ let keyCode;
35
+ let valCode;
36
+ let before = "";
37
+ let collect = "";
38
+ let after = "";
39
+ if (hasMutation(ir.keyType) || hasMutation(ir.valueType)) {
40
+ const rebuiltVar = g.temp("map_n");
41
+ keyCode = `var ${keyOut}=${keyVar};` + g.visit(ir.keyType, {
42
+ input: keyOut,
43
+ output: keyOut,
44
+ path: "[]",
45
+ issues: keyIssues
46
+ });
47
+ valCode = `var ${valueOut}=${entryVar}[1];` + g.visit(ir.valueType, {
48
+ input: valueOut,
49
+ output: valueOut,
50
+ path: "[]",
51
+ issues: valIssues
52
+ });
53
+ before = `var ${rebuiltVar}=new Map();`;
54
+ collect = `${rebuiltVar}.set(${keyOut},${valueOut});`;
55
+ after = `${g.output}=${rebuiltVar};`;
56
+ } else {
57
+ const valueVar = g.temp("map_v");
58
+ const keyVisit = g.visit(ir.keyType, {
59
+ input: keyVar,
60
+ output: keyOut,
61
+ path: "[]",
62
+ issues: keyIssues
63
+ });
64
+ const valueVisit = g.visit(ir.valueType, {
65
+ input: valueVar,
66
+ output: valueOut,
67
+ path: "[]",
68
+ issues: valIssues
69
+ });
70
+ const keyWrites = keyVisit.includes(keyOut);
71
+ const valueWrites = valueVisit.includes(valueOut);
72
+ keyCode = `${keyWrites ? `var ${keyOut}=${keyVar};` : ""}${keyVisit}`;
73
+ valCode = `var ${valueVar}=${entryVar}[1];${valueWrites ? `var ${valueOut}=${valueVar};` : ""}${valueVisit}`;
74
+ if (keyWrites || valueWrites) {
75
+ const rebuiltVar = g.temp("map_n");
76
+ const countVar = g.temp("map_c");
77
+ const skipVar = g.temp("map_j");
78
+ const earlierVar = g.temp("map_p");
79
+ const key = keyWrites ? keyOut : keyVar;
80
+ const value = valueWrites ? valueOut : valueVar;
81
+ const replaced = [...keyWrites ? [`${keyOut}!==${keyVar}`] : [], ...valueWrites ? [`${valueOut}!==${valueVar}`] : []].join("||");
82
+ before = `var ${rebuiltVar}=null;var ${countVar}=0;`;
83
+ collect = emit`
84
+ if(${rebuiltVar}!==null){
85
+ ${rebuiltVar}.set(${key},${value});
86
+ }else if(${replaced}){
87
+ ${rebuiltVar}=new Map();
88
+ var ${skipVar}=0;
89
+ for(var ${earlierVar} of ${g.input}){
90
+ if(${skipVar}++===${countVar})break;
91
+ ${rebuiltVar}.set(${earlierVar}[0],${earlierVar}[1]);
92
+ }
93
+ ${rebuiltVar}.set(${key},${value});
94
+ }
95
+ ${countVar}++;`;
96
+ after = `if(${rebuiltVar}!==null){${g.output}=${rebuiltVar};}`;
97
+ }
98
+ }
46
99
  return `${emit`
47
100
  if(!(${g.input} instanceof Map)){
48
101
  ${invalidType(g, "map")}
49
102
  }else{
50
- ${mutates ? `var ${rebuiltVar}=new Map();` : ""}
103
+ ${before}
51
104
  for(var ${entryVar} of ${g.input}){
52
105
  var ${keyVar}=${entryVar}[0];
53
106
  var ${pkVar}=${propertyKeyTest(keyVar)};
@@ -69,9 +122,9 @@ function slowMap(ir, g) {
69
122
  ${g.issues}.push({origin:"map",code:"invalid_element",key:${keyVar},issues:${fz}(${valIssues}),input:${g.input},path:${g.path}${msgProp}});
70
123
  }
71
124
  }
72
- ${mutates ? `${rebuiltVar}.set(${entryVar}[0],${entryVar}[1]);` : ""}
125
+ ${collect}
73
126
  }
74
- ${mutates ? `${g.output}=${rebuiltVar};` : ""}
127
+ ${after}
75
128
  }
76
129
  `}\n`;
77
130
  }
@@ -1 +1 @@
1
- {"version":3,"file":"map.js","names":[],"sources":["../../../../src/core/codegen/schemas/map.ts"],"sourcesContent":["import type { MapIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport { declareFastTemps, emitRuntimeHelper, hasMutation } from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType } from \"../emit-issue.js\";\nimport { propertyKeyTest, ZC_FZ_DECL, ZC_PFX_DECL } from \"../issue-decls.js\";\n\n/**\n * Map entries report through zod's `handleMapResult`, which branches on the\n * RUNTIME type of the key rather than on the key schema:\n *\n * - a property-key type (string | number | symbol) addresses the entry, so key\n * AND value issues are prefixed with the key itself → `[...mapPath, key, …]`.\n * This holds even when the key is the wrong type for the schema — a `number`\n * key under `z.map(z.string(), …)` still reports at `[5]`.\n * - anything else (boolean, bigint, object, …) cannot be a path segment, so the\n * issues are WRAPPED: key issues into one `invalid_key`, value issues into one\n * `invalid_element` carrying the offending `key`, both at the map's own path\n * with the originals nested and finalized.\n *\n * The key is snapshotted before validation because zod passes the ORIGINAL key\n * to both the type test and the prefix, and a mutating key schema (`.trim()`)\n * would otherwise have rewritten it by then.\n */\nexport function slowMap(ir: SchemaIR & { type: \"map\" }, g: SlowGen): string {\n const entryVar = g.temp(\"map_e\");\n const keyVar = g.temp(\"map_k\");\n const pkVar = g.temp(\"map_pk\");\n const keyIssues = g.temp(\"map_ki\");\n const valIssues = g.temp(\"map_vi\");\n // Mutating key/value schemas (coerce, .trim(), url) rewrite the entry tuple,\n // which a Map cannot reflect — rebuild into a fresh Map (mirrors Zod).\n const mutates = hasMutation(ir.keyType) || hasMutation(ir.valueType);\n const rebuiltVar = mutates ? g.temp(\"map_n\") : \"\";\n const msgProp = g.typeMsg === undefined ? \"\" : `,message:${JSON.stringify(g.typeMsg)}`;\n const fz = emitRuntimeHelper(g.ctx, \"__zcFz\", ZC_FZ_DECL);\n const pfx = emitRuntimeHelper(g.ctx, \"__zcPfx\", ZC_PFX_DECL);\n\n // Key and value are validated into scratch arrays at a path RELATIVE to the\n // entry, exactly as zod runs them on a fresh payload — whichever branch the\n // key's type selects then decides where those paths get rooted.\n const keyCode = g.visit(ir.keyType, {\n input: `${entryVar}[0]`,\n output: `${entryVar}[0]`,\n path: \"[]\",\n issues: keyIssues,\n });\n const valCode = g.visit(ir.valueType, {\n input: `${entryVar}[1]`,\n output: `${entryVar}[1]`,\n path: \"[]\",\n issues: valIssues,\n });\n\n return `${emit`\n if(!(${g.input} instanceof Map)){\n ${invalidType(g, \"map\")}\n }else{\n ${mutates ? `var ${rebuiltVar}=new Map();` : \"\"}\n for(var ${entryVar} of ${g.input}){\n var ${keyVar}=${entryVar}[0];\n var ${pkVar}=${propertyKeyTest(keyVar)};\n var ${keyIssues}=[];\n ${keyCode}\n if(${keyIssues}.length>0){\n if(${pkVar}){\n ${pfx}(${g.issues},${keyIssues},${g.path},${keyVar});\n }else{\n ${g.issues}.push({code:\"invalid_key\",origin:\"map\",issues:${fz}(${keyIssues}),input:${g.input},path:${g.path}${msgProp}});\n }\n }\n var ${valIssues}=[];\n ${valCode}\n if(${valIssues}.length>0){\n if(${pkVar}){\n ${pfx}(${g.issues},${valIssues},${g.path},${keyVar});\n }else{\n ${g.issues}.push({origin:\"map\",code:\"invalid_element\",key:${keyVar},issues:${fz}(${valIssues}),input:${g.input},path:${g.path}${msgProp}});\n }\n }\n ${mutates ? `${rebuiltVar}.set(${entryVar}[0],${entryVar}[1]);` : \"\"}\n }\n ${mutates ? `${g.output}=${rebuiltVar};` : \"\"}\n }\n `}\\n`;\n}\n\nexport function fastMap(ir: MapIR, g: FastGen): string | null {\n const x = g.input;\n const parts: string[] = [`${x} instanceof Map`];\n\n // Key/value validation via preamble helper (Map has no .every()).\n // Key + value share one fresh scope — same emitted helper, size-gated\n // independently of the caller.\n const entryVar = g.temp(\"me\");\n const body = g.scoped(`${entryVar}[0]`);\n const keyCheck = body.visit(ir.keyType, { input: `${entryVar}[0]` });\n if (keyCheck === null) return null;\n const valCheck = body.visit(ir.valueType, { input: `${entryVar}[1]` });\n if (valCheck === null) return null;\n\n if (keyCheck !== \"true\" || valCheck !== \"true\") {\n const combined = [keyCheck, valCheck].filter((c) => c !== \"true\").join(\"&&\");\n const helperName = g.temp(\"mh\");\n g.ctx.preamble.push(\n `function ${helperName}(m){${declareFastTemps(body.scope)}for(var ${entryVar} of m){if(!(${combined})){return false;}}return true;}`,\n );\n parts.push(`${helperName}(${x})`);\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,QAAQ,IAAgC,GAAoB;CAC1E,MAAM,WAAW,EAAE,KAAK,OAAO;CAC/B,MAAM,SAAS,EAAE,KAAK,OAAO;CAC7B,MAAM,QAAQ,EAAE,KAAK,QAAQ;CAC7B,MAAM,YAAY,EAAE,KAAK,QAAQ;CACjC,MAAM,YAAY,EAAE,KAAK,QAAQ;CAGjC,MAAM,UAAU,YAAY,GAAG,OAAO,KAAK,YAAY,GAAG,SAAS;CACnE,MAAM,aAAa,UAAU,EAAE,KAAK,OAAO,IAAI;CAC/C,MAAM,UAAU,EAAE,YAAY,KAAA,IAAY,KAAK,YAAY,KAAK,UAAU,EAAE,OAAO;CACnF,MAAM,KAAK,kBAAkB,EAAE,KAAK,UAAU,UAAU;CACxD,MAAM,MAAM,kBAAkB,EAAE,KAAK,WAAW,WAAW;CAK3D,MAAM,UAAU,EAAE,MAAM,GAAG,SAAS;EAClC,OAAO,GAAG,SAAS;EACnB,QAAQ,GAAG,SAAS;EACpB,MAAM;EACN,QAAQ;CACV,CAAC;CACD,MAAM,UAAU,EAAE,MAAM,GAAG,WAAW;EACpC,OAAO,GAAG,SAAS;EACnB,QAAQ,GAAG,SAAS;EACpB,MAAM;EACN,QAAQ;CACV,CAAC;CAED,OAAO,GAAG,IAAI;WACL,EAAE,MAAM;QACX,YAAY,GAAG,KAAK,EAAE;;QAEtB,UAAU,OAAO,WAAW,eAAe,GAAG;gBACtC,SAAS,MAAM,EAAE,MAAM;cACzB,OAAO,GAAG,SAAS;cACnB,MAAM,GAAG,gBAAgB,MAAM,EAAE;cACjC,UAAU;UACd,QAAQ;aACL,UAAU;eACR,MAAM;cACP,IAAI,GAAG,EAAE,OAAO,GAAG,UAAU,GAAG,EAAE,KAAK,GAAG,OAAO;;cAEjD,EAAE,OAAO,gDAAgD,GAAG,GAAG,UAAU,UAAU,EAAE,MAAM,QAAQ,EAAE,OAAO,QAAQ;;;cAGpH,UAAU;UACd,QAAQ;aACL,UAAU;eACR,MAAM;cACP,IAAI,GAAG,EAAE,OAAO,GAAG,UAAU,GAAG,EAAE,KAAK,GAAG,OAAO;;cAEjD,EAAE,OAAO,iDAAiD,OAAO,UAAU,GAAG,GAAG,UAAU,UAAU,EAAE,MAAM,QAAQ,EAAE,OAAO,QAAQ;;;UAG1I,UAAU,GAAG,WAAW,OAAO,SAAS,MAAM,SAAS,SAAS,GAAG;;QAErE,UAAU,GAAG,EAAE,OAAO,GAAG,WAAW,KAAK,GAAG;;IAEhD;AACJ;AAEA,SAAgB,QAAQ,IAAW,GAA2B;CAC5D,MAAM,IAAI,EAAE;CACZ,MAAM,QAAkB,CAAC,GAAG,EAAE,gBAAgB;CAK9C,MAAM,WAAW,EAAE,KAAK,IAAI;CAC5B,MAAM,OAAO,EAAE,OAAO,GAAG,SAAS,IAAI;CACtC,MAAM,WAAW,KAAK,MAAM,GAAG,SAAS,EAAE,OAAO,GAAG,SAAS,KAAK,CAAC;CACnE,IAAI,aAAa,MAAM,OAAO;CAC9B,MAAM,WAAW,KAAK,MAAM,GAAG,WAAW,EAAE,OAAO,GAAG,SAAS,KAAK,CAAC;CACrE,IAAI,aAAa,MAAM,OAAO;CAE9B,IAAI,aAAa,UAAU,aAAa,QAAQ;EAC9C,MAAM,WAAW,CAAC,UAAU,QAAQ,CAAC,CAAC,QAAQ,MAAM,MAAM,MAAM,CAAC,CAAC,KAAK,IAAI;EAC3E,MAAM,aAAa,EAAE,KAAK,IAAI;EAC9B,EAAE,IAAI,SAAS,KACb,YAAY,WAAW,MAAM,iBAAiB,KAAK,KAAK,EAAE,UAAU,SAAS,cAAc,SAAS,gCACtG;EACA,MAAM,KAAK,GAAG,WAAW,GAAG,EAAE,EAAE;CAClC;CAEA,OAAO,MAAM,KAAK,IAAI;AACxB"}
1
+ {"version":3,"file":"map.js","names":[],"sources":["../../../../src/core/codegen/schemas/map.ts"],"sourcesContent":["import type { MapIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport { declareFastTemps, emitRuntimeHelper, hasMutation } from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType } from \"../emit-issue.js\";\nimport { propertyKeyTest, ZC_FZ_DECL, ZC_PFX_DECL } from \"../issue-decls.js\";\n\n/**\n * Map entries report through zod's `handleMapResult`, which branches on the\n * RUNTIME type of the key rather than on the key schema:\n *\n * - a property-key type (string | number | symbol) addresses the entry, so key\n * AND value issues are prefixed with the key itself → `[...mapPath, key, …]`.\n * This holds even when the key is the wrong type for the schema — a `number`\n * key under `z.map(z.string(), …)` still reports at `[5]`.\n * - anything else (boolean, bigint, object, …) cannot be a path segment, so the\n * issues are WRAPPED: key issues into one `invalid_key`, value issues into one\n * `invalid_element` carrying the offending `key`, both at the map's own path\n * with the originals nested and finalized.\n *\n * The key is snapshotted before validation because zod passes the ORIGINAL key\n * to both the type test and the prefix, and a mutating key schema (`.trim()`)\n * would otherwise have rewritten it by then.\n */\nexport function slowMap(ir: SchemaIR & { type: \"map\" }, g: SlowGen): string {\n const entryVar = g.temp(\"map_e\");\n const keyVar = g.temp(\"map_k\");\n const pkVar = g.temp(\"map_pk\");\n const keyIssues = g.temp(\"map_ki\");\n const valIssues = g.temp(\"map_vi\");\n const msgProp = g.typeMsg === undefined ? \"\" : `,message:${JSON.stringify(g.typeMsg)}`;\n const fz = emitRuntimeHelper(g.ctx, \"__zcFz\", ZC_FZ_DECL);\n const pfx = emitRuntimeHelper(g.ctx, \"__zcPfx\", ZC_PFX_DECL);\n\n // Key and value are validated into scratch arrays at a path RELATIVE to the\n // entry, exactly as zod runs them on a fresh payload — whichever branch the\n // key's type selects then decides where those paths get rooted.\n //\n // Both are read out of the entry into locals and never written back into it:\n // the entry array is the iterator's, and a Map subclass can hand out its own.\n // What they produce reaches the output through the rebuilt Map instead.\n const keyOut = g.temp(\"map_ko\");\n const valueOut = g.temp(\"map_vo\");\n let keyCode: string;\n let valCode: string;\n let before = \"\";\n let collect = \"\";\n let after = \"\";\n if (hasMutation(ir.keyType) || hasMutation(ir.valueType)) {\n // A mutating key or value (coerce, .trim(), url) reads back what it wrote,\n // so each keeps one local for both, and every entry is rebuilt into a fresh\n // Map (mirrors Zod).\n const rebuiltVar = g.temp(\"map_n\");\n keyCode =\n `var ${keyOut}=${keyVar};` +\n g.visit(ir.keyType, { input: keyOut, output: keyOut, path: \"[]\", issues: keyIssues });\n valCode =\n `var ${valueOut}=${entryVar}[1];` +\n g.visit(ir.valueType, { input: valueOut, output: valueOut, path: \"[]\", issues: valIssues });\n before = `var ${rebuiltVar}=new Map();`;\n collect = `${rebuiltVar}.set(${keyOut},${valueOut});`;\n after = `${g.output}=${rebuiltVar};`;\n } else {\n // A key or value that rewrites nothing can still hand back a REPLACEMENT —\n // a `__proto__`-scrubbed copy, a recursive value rebuilt by its own\n // validator — which zod's output Map holds in its place. Each is given an\n // output local of its own (see visitMember), and the first replacement\n // starts the rebuilt Map: the entries before it, in order, then every\n // entry's output from there on. A Map nothing replaces still comes back by\n // reference, and a side that never names its output is never compared.\n const valueVar = g.temp(\"map_v\");\n const keyVisit = g.visit(ir.keyType, {\n input: keyVar,\n output: keyOut,\n path: \"[]\",\n issues: keyIssues,\n });\n const valueVisit = g.visit(ir.valueType, {\n input: valueVar,\n output: valueOut,\n path: \"[]\",\n issues: valIssues,\n });\n const keyWrites = keyVisit.includes(keyOut);\n const valueWrites = valueVisit.includes(valueOut);\n keyCode = `${keyWrites ? `var ${keyOut}=${keyVar};` : \"\"}${keyVisit}`;\n valCode = `var ${valueVar}=${entryVar}[1];${valueWrites ? `var ${valueOut}=${valueVar};` : \"\"}${valueVisit}`;\n if (keyWrites || valueWrites) {\n const rebuiltVar = g.temp(\"map_n\");\n const countVar = g.temp(\"map_c\");\n const skipVar = g.temp(\"map_j\");\n const earlierVar = g.temp(\"map_p\");\n const key = keyWrites ? keyOut : keyVar;\n const value = valueWrites ? valueOut : valueVar;\n const replaced = [\n ...(keyWrites ? [`${keyOut}!==${keyVar}`] : []),\n ...(valueWrites ? [`${valueOut}!==${valueVar}`] : []),\n ].join(\"||\");\n before = `var ${rebuiltVar}=null;var ${countVar}=0;`;\n collect = emit`\n if(${rebuiltVar}!==null){\n ${rebuiltVar}.set(${key},${value});\n }else if(${replaced}){\n ${rebuiltVar}=new Map();\n var ${skipVar}=0;\n for(var ${earlierVar} of ${g.input}){\n if(${skipVar}++===${countVar})break;\n ${rebuiltVar}.set(${earlierVar}[0],${earlierVar}[1]);\n }\n ${rebuiltVar}.set(${key},${value});\n }\n ${countVar}++;`;\n after = `if(${rebuiltVar}!==null){${g.output}=${rebuiltVar};}`;\n }\n }\n\n return `${emit`\n if(!(${g.input} instanceof Map)){\n ${invalidType(g, \"map\")}\n }else{\n ${before}\n for(var ${entryVar} of ${g.input}){\n var ${keyVar}=${entryVar}[0];\n var ${pkVar}=${propertyKeyTest(keyVar)};\n var ${keyIssues}=[];\n ${keyCode}\n if(${keyIssues}.length>0){\n if(${pkVar}){\n ${pfx}(${g.issues},${keyIssues},${g.path},${keyVar});\n }else{\n ${g.issues}.push({code:\"invalid_key\",origin:\"map\",issues:${fz}(${keyIssues}),input:${g.input},path:${g.path}${msgProp}});\n }\n }\n var ${valIssues}=[];\n ${valCode}\n if(${valIssues}.length>0){\n if(${pkVar}){\n ${pfx}(${g.issues},${valIssues},${g.path},${keyVar});\n }else{\n ${g.issues}.push({origin:\"map\",code:\"invalid_element\",key:${keyVar},issues:${fz}(${valIssues}),input:${g.input},path:${g.path}${msgProp}});\n }\n }\n ${collect}\n }\n ${after}\n }\n `}\\n`;\n}\n\nexport function fastMap(ir: MapIR, g: FastGen): string | null {\n const x = g.input;\n const parts: string[] = [`${x} instanceof Map`];\n\n // Key/value validation via preamble helper (Map has no .every()).\n // Key + value share one fresh scope — same emitted helper, size-gated\n // independently of the caller.\n const entryVar = g.temp(\"me\");\n const body = g.scoped(`${entryVar}[0]`);\n const keyCheck = body.visit(ir.keyType, { input: `${entryVar}[0]` });\n if (keyCheck === null) return null;\n const valCheck = body.visit(ir.valueType, { input: `${entryVar}[1]` });\n if (valCheck === null) return null;\n\n if (keyCheck !== \"true\" || valCheck !== \"true\") {\n const combined = [keyCheck, valCheck].filter((c) => c !== \"true\").join(\"&&\");\n const helperName = g.temp(\"mh\");\n g.ctx.preamble.push(\n `function ${helperName}(m){${declareFastTemps(body.scope)}for(var ${entryVar} of m){if(!(${combined})){return false;}}return true;}`,\n );\n parts.push(`${helperName}(${x})`);\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"record.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/record.ts"],"mappings":";;;iBA0BgB,WAAW,IAAI;EAAa;GAAkB,GAAG;iBA8DjD,WAAW,IAAI,UAAU,GAAG"}
1
+ {"version":3,"file":"record.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/record.ts"],"mappings":";;;iBAgCgB,WAAW,IAAI;EAAa;GAAkB,GAAG;iBAoFjD,WAAW,IAAI,UAAU,GAAG"}
@@ -1,4 +1,4 @@
1
- import { declareFastTemps, emitRuntimeHelper, extendPath, hasMutation } from "../context.js";
1
+ import { declareFastTemps, emitRuntimeHelper, extendPath, hasMutation, visitMember } from "../context.js";
2
2
  import { emit } from "../emit.js";
3
3
  import { invalidType, unrecognizedKeys } from "../emit-issue.js";
4
4
  import { ZC_FZ_DECL, ZC_HOP_DECL, ZC_PLAIN_DECL, ZC_PROTO_SCRUB_DECL } from "../issue-decls.js";
@@ -25,39 +25,45 @@ function slowRecord(ir, g) {
25
25
  if(!${plainObjectTest(g, g.input)}){
26
26
  ${invalidType(g, "record")}
27
27
  }else{`;
28
- if (hasMutation(ir.valueType)) code += `${g.output}={...${g.input}};`;
29
- code += `${g.output}=${emitRuntimeHelper(g.ctx, "__zcPs", ZC_PROTO_SCRUB_DECL)}(${g.input});`;
28
+ const rec = g.temp("rc");
29
+ code += `var ${rec}=${emitRuntimeHelper(g.ctx, "__zcPs", ZC_PROTO_SCRUB_DECL)}(${g.input});`;
30
+ if (hasMutation(ir.valueType)) code += `if(${rec}===${g.input}){${rec}={...${g.input}};}`;
30
31
  const keyVar = g.temp("rkey");
31
32
  const keyIssuesVar = g.temp("rki");
32
33
  const keyPath = extendPath(g.path, keyVar);
33
- const valExpr = `${g.input}[${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;
37
37
  const onKeyFailure = unrecognizedVar === null ? `${g.issues}.push({code:"invalid_key",origin:"record",issues:${fz}(${keyIssuesVar}),input:${keyVar},path:${keyPath}${g.typeMsg === void 0 ? "" : `,message:${JSON.stringify(g.typeMsg)}`}});` : `(${unrecognizedVar}=${unrecognizedVar}||[]).push(${keyVar});`;
38
- code += emit`
39
- ${unrecognizedVar === null ? "" : `var ${unrecognizedVar}=null;`}
40
- for(var ${keyVar} in ${g.input}){
41
- if(!${hop}.call(${g.input},${keyVar}))continue;
42
- ${PROTO_SKIP(keyVar)}
43
- var ${keyIssuesVar}=[];
44
- ${g.visit(ir.keyType, {
38
+ const keyCode = g.visit(ir.keyType, {
45
39
  input: keyVar,
46
40
  output: keyVar,
47
41
  path: "[]",
48
42
  issues: keyIssuesVar
49
- })}
43
+ });
44
+ const valueCode = visitMember(g, ir.valueType, {
45
+ container: rec,
46
+ original: g.input,
47
+ copy: `{...${g.input}}`,
48
+ key: keyVar,
49
+ path: keyPath,
50
+ issues: g.issues
51
+ });
52
+ code += emit`
53
+ ${unrecognizedVar === null ? "" : `var ${unrecognizedVar}=null;`}
54
+ for(var ${keyVar} in ${rec}){
55
+ if(!${hop}.call(${rec},${keyVar}))continue;
56
+ ${PROTO_SKIP(keyVar)}
57
+ var ${keyIssuesVar}=[];
58
+ ${keyCode}
50
59
  if(${keyIssuesVar}.length>0){
51
60
  ${onKeyFailure}
52
61
  }else{
53
- ${g.visit(ir.valueType, {
54
- input: valExpr,
55
- output: valExpr,
56
- path: keyPath
57
- })}
62
+ ${valueCode}
58
63
  }
59
64
  }
60
65
  ${unrecognizedVar === null ? "" : `if(${unrecognizedVar}!==null){${unrecognizedKeys(g, unrecognizedVar)}}`}
66
+ ${g.output}=${rec};
61
67
  }`;
62
68
  return `${code}\n`;
63
69
  }
@@ -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 { declareFastTemps, emitRuntimeHelper, extendPath, hasMutation } 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 if (hasMutation(ir.valueType)) {\n code += `${g.output}={...${g.input}};`;\n }\n // PROTO_SKIP keeps `__proto__` out of the WALK; this 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.\n code += `${g.output}=${emitRuntimeHelper(g.ctx, \"__zcPs\", ZC_PROTO_SCRUB_DECL)}(${g.input});`;\n\n const keyVar = g.temp(\"rkey\");\n const keyIssuesVar = g.temp(\"rki\");\n const keyPath = extendPath(g.path, keyVar);\n const valExpr = `${g.input}[${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. When the value type mutates (coerce/default/.trim()) the clone\n // above has already replaced g.input, so this iterates the clone exactly as\n // the Object.keys form did — the key set is stable (values change, keys\n // don't). Records whose values mutate run this path eagerly, so they get the\n // same 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 code += emit`\n ${unrecognizedVar === null ? \"\" : `var ${unrecognizedVar}=null;`}\n for(var ${keyVar} in ${g.input}){\n if(!${hop}.call(${g.input},${keyVar}))continue;\n ${PROTO_SKIP(keyVar)}\n var ${keyIssuesVar}=[];\n ${g.visit(ir.keyType, { input: keyVar, output: keyVar, path: \"[]\", issues: keyIssuesVar })}\n if(${keyIssuesVar}.length>0){\n ${onKeyFailure}\n }else{\n ${g.visit(ir.valueType, { input: valExpr, output: valExpr, path: keyPath })}\n }\n }\n ${unrecognizedVar === null ? \"\" : `if(${unrecognizedVar}!==null){${unrecognizedKeys(g, unrecognizedVar)}}`}\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.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 +1 @@
1
- {"version":3,"file":"set.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/set.ts"],"mappings":";;;iBAOgB,QAAQ,IAAI;EAAa;GAAe,GAAG;iBA+D3C,QAAQ,IAAI,OAAO,GAAG"}
1
+ {"version":3,"file":"set.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/set.ts"],"mappings":";;;iBAOgB,QAAQ,IAAI;EAAa;GAAe,GAAG;iBAuG3C,QAAQ,IAAI,OAAO,GAAG"}
@@ -9,20 +9,58 @@ function slowSet(ir, g) {
9
9
  ${invalidType(g, "set")}
10
10
  ${whenGatedSizeChecks(ir.checks ?? [], g, "size")}
11
11
  }else{`;
12
- const mutates = hasMutation(ir.valueType);
13
12
  const iterVar = g.temp("set_v");
14
- const rebuiltVar = mutates ? g.temp("set_n") : "";
15
- if (mutates) code += `var ${rebuiltVar}=new Set();`;
16
- code += emit`
17
- for(var ${iterVar} of ${g.input}){
18
- ${g.visit(ir.valueType, {
19
- input: iterVar,
20
- output: iterVar,
21
- path: g.path
22
- })}
23
- ${mutates ? `${rebuiltVar}.add(${iterVar});` : ""}
24
- }
25
- ${mutates ? `${g.output}=${rebuiltVar};` : ""}`;
13
+ if (hasMutation(ir.valueType)) {
14
+ const rebuiltVar = g.temp("set_n");
15
+ code += emit`
16
+ var ${rebuiltVar}=new Set();
17
+ for(var ${iterVar} of ${g.input}){
18
+ ${g.visit(ir.valueType, {
19
+ input: iterVar,
20
+ output: iterVar,
21
+ path: g.path
22
+ })}
23
+ ${rebuiltVar}.add(${iterVar});
24
+ }
25
+ ${g.output}=${rebuiltVar};`;
26
+ } else {
27
+ const outVar = g.temp("set_o");
28
+ const element = g.visit(ir.valueType, {
29
+ input: iterVar,
30
+ output: outVar,
31
+ path: g.path
32
+ });
33
+ if (!element.includes(outVar)) code += emit`
34
+ for(var ${iterVar} of ${g.input}){
35
+ ${element}
36
+ }`;
37
+ else {
38
+ const rebuiltVar = g.temp("set_n");
39
+ const countVar = g.temp("set_c");
40
+ const skipVar = g.temp("set_j");
41
+ const earlierVar = g.temp("set_p");
42
+ code += emit`
43
+ var ${rebuiltVar}=null;
44
+ var ${countVar}=0;
45
+ for(var ${iterVar} of ${g.input}){
46
+ var ${outVar}=${iterVar};
47
+ ${element}
48
+ if(${rebuiltVar}!==null){
49
+ ${rebuiltVar}.add(${outVar});
50
+ }else if(${outVar}!==${iterVar}){
51
+ ${rebuiltVar}=new Set();
52
+ var ${skipVar}=0;
53
+ for(var ${earlierVar} of ${g.input}){
54
+ if(${skipVar}++===${countVar})break;
55
+ ${rebuiltVar}.add(${earlierVar});
56
+ }
57
+ ${rebuiltVar}.add(${outVar});
58
+ }
59
+ ${countVar}++;
60
+ }
61
+ if(${rebuiltVar}!==null){${g.output}=${rebuiltVar};}`;
62
+ }
63
+ }
26
64
  if (ir.checks) for (const check of ir.checks) switch (check.kind) {
27
65
  case "min_size":
28
66
  code += emit`
@@ -1 +1 @@
1
- {"version":3,"file":"set.js","names":[],"sources":["../../../../src/core/codegen/schemas/set.ts"],"sourcesContent":["import type { SchemaIR, SetIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport { checkPriority, declareFastTemps, hasMutation } from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, tooBig, tooSmall } from \"../emit-issue.js\";\nimport { whenGatedSizeChecks } from \"./sizeable.js\";\n\nexport function slowSet(ir: SchemaIR & { type: \"set\" }, g: SlowGen): string {\n let code = emit`\n if(!(${g.input} instanceof Set)){\n ${invalidType(g, \"set\")}\n ${whenGatedSizeChecks(ir.checks ?? [], g, \"size\")}\n }else{`;\n\n // Validate each element BEFORE the size checks: Zod parses the elements\n // first and runs size checks afterward, so an invalid element surfaces ahead\n // of too_small/too_big when both fail. Mutating element schemas (coerce,\n // .trim(), url) rewrite the loop variable, which a Set cannot reflect —\n // rebuild into a fresh Set so the mutated values land in the output.\n const mutates = hasMutation(ir.valueType);\n const iterVar = g.temp(\"set_v\");\n const rebuiltVar = mutates ? g.temp(\"set_n\") : \"\";\n if (mutates) {\n code += `var ${rebuiltVar}=new Set();`;\n }\n // The element's path is the SET's own path, with no index segment: a Set has\n // no stable positional addressing, so zod's handleSetResult pushes each\n // element's issues into the set's payload UNPREFIXED (unlike an array, whose\n // handleArrayResult prefixes the index). Two bad elements therefore report at\n // the same path — that is zod's output, and an index we invented instead made\n // every set-element issue point somewhere zod never points.\n code += emit`\n for(var ${iterVar} of ${g.input}){\n ${g.visit(ir.valueType, { input: iterVar, output: iterVar, path: g.path })}\n ${mutates ? `${rebuiltVar}.add(${iterVar});` : \"\"}\n }\n ${mutates ? `${g.output}=${rebuiltVar};` : \"\"}`;\n\n // Size checks (run after element validation, mirroring Zod's check order).\n if (ir.checks) {\n for (const check of ir.checks) {\n switch (check.kind) {\n case \"min_size\":\n code += emit`\n if(${g.input}.size<${check.minimum}){\n ${tooSmall(g, check.minimum, \"set\", true, { message: check.message })}\n }`;\n break;\n case \"max_size\":\n code += emit`\n if(${g.input}.size>${check.maximum}){\n ${tooBig(g, check.maximum, \"set\", true, { message: check.message })}\n }`;\n break;\n case \"size_equals\":\n code += emit`\n if(${g.input}.size<${check.size}){\n ${tooSmall(g, check.size, \"set\", true, { exact: true, message: check.message })}\n }else if(${g.input}.size>${check.size}){\n ${tooBig(g, check.size, \"set\", true, { exact: true, message: check.message })}\n }`;\n break;\n }\n }\n }\n\n code += `}`;\n return `${code}\\n`;\n}\n\nexport function fastSet(ir: SetIR, g: FastGen): string | null {\n const x = g.input;\n const parts: string[] = [`${x} instanceof Set`];\n\n // Size checks\n if (ir.checks) {\n for (const check of [...ir.checks].sort(checkPriority)) {\n switch (check.kind) {\n case \"min_size\":\n parts.push(`${x}.size>=${check.minimum}`);\n break;\n case \"max_size\":\n parts.push(`${x}.size<=${check.maximum}`);\n break;\n case \"size_equals\":\n parts.push(`${x}.size===${check.size}`);\n break;\n }\n }\n }\n\n // Element validation via preamble helper (Set has no .every()).\n // Fresh scope: the helper is its own function, size-gated independently.\n const elemVar = g.temp(\"sv\");\n const elemGen = g.scoped(elemVar);\n const elemCheck = elemGen.visit(ir.valueType);\n if (elemCheck === null) return null;\n if (elemCheck !== \"true\") {\n const helperName = g.temp(\"se\");\n g.ctx.preamble.push(\n `function ${helperName}(s){${declareFastTemps(elemGen.scope)}for(var ${elemVar} of s){if(!(${elemCheck})){return false;}}return true;}`,\n );\n parts.push(`${helperName}(${x})`);\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":";;;;;AAOA,SAAgB,QAAQ,IAAgC,GAAoB;CAC1E,IAAI,OAAO,IAAI;WACN,EAAE,MAAM;QACX,YAAY,GAAG,KAAK,EAAE;QACtB,oBAAoB,GAAG,UAAU,CAAC,GAAG,GAAG,MAAM,EAAE;;CAQtD,MAAM,UAAU,YAAY,GAAG,SAAS;CACxC,MAAM,UAAU,EAAE,KAAK,OAAO;CAC9B,MAAM,aAAa,UAAU,EAAE,KAAK,OAAO,IAAI;CAC/C,IAAI,SACF,QAAQ,OAAO,WAAW;CAQ5B,QAAQ,IAAI;cACA,QAAQ,MAAM,EAAE,MAAM;QAC5B,EAAE,MAAM,GAAG,WAAW;EAAE,OAAO;EAAS,QAAQ;EAAS,MAAM,EAAE;CAAK,CAAC,EAAE;QACzE,UAAU,GAAG,WAAW,OAAO,QAAQ,MAAM,GAAG;;MAElD,UAAU,GAAG,EAAE,OAAO,GAAG,WAAW,KAAK;CAG7C,IAAI,GAAG,QACL,KAAK,MAAM,SAAS,GAAG,QACrB,QAAQ,MAAM,MAAd;EACE,KAAK;GACH,QAAQ,IAAI;iBACL,EAAE,MAAM,QAAQ,MAAM,QAAQ;gBAC/B,SAAS,GAAG,MAAM,SAAS,OAAO,MAAM,EAAE,SAAS,MAAM,QAAQ,CAAC,EAAE;;GAE1E;EACF,KAAK;GACH,QAAQ,IAAI;iBACL,EAAE,MAAM,QAAQ,MAAM,QAAQ;gBAC/B,OAAO,GAAG,MAAM,SAAS,OAAO,MAAM,EAAE,SAAS,MAAM,QAAQ,CAAC,EAAE;;GAExE;EACF,KAAK;GACH,QAAQ,IAAI;iBACL,EAAE,MAAM,QAAQ,MAAM,KAAK;gBAC5B,SAAS,GAAG,MAAM,MAAM,OAAO,MAAM;IAAE,OAAO;IAAM,SAAS,MAAM;GAAQ,CAAC,EAAE;uBACvE,EAAE,MAAM,QAAQ,MAAM,KAAK;gBAClC,OAAO,GAAG,MAAM,MAAM,OAAO,MAAM;IAAE,OAAO;IAAM,SAAS,MAAM;GAAQ,CAAC,EAAE;;GAElF;CACJ;CAIJ,QAAQ;CACR,OAAO,GAAG,KAAK;AACjB;AAEA,SAAgB,QAAQ,IAAW,GAA2B;CAC5D,MAAM,IAAI,EAAE;CACZ,MAAM,QAAkB,CAAC,GAAG,EAAE,gBAAgB;CAG9C,IAAI,GAAG,QACL,KAAK,MAAM,SAAS,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC,KAAK,aAAa,GACnD,QAAQ,MAAM,MAAd;EACE,KAAK;GACH,MAAM,KAAK,GAAG,EAAE,SAAS,MAAM,SAAS;GACxC;EACF,KAAK;GACH,MAAM,KAAK,GAAG,EAAE,SAAS,MAAM,SAAS;GACxC;EACF,KAAK;GACH,MAAM,KAAK,GAAG,EAAE,UAAU,MAAM,MAAM;GACtC;CACJ;CAMJ,MAAM,UAAU,EAAE,KAAK,IAAI;CAC3B,MAAM,UAAU,EAAE,OAAO,OAAO;CAChC,MAAM,YAAY,QAAQ,MAAM,GAAG,SAAS;CAC5C,IAAI,cAAc,MAAM,OAAO;CAC/B,IAAI,cAAc,QAAQ;EACxB,MAAM,aAAa,EAAE,KAAK,IAAI;EAC9B,EAAE,IAAI,SAAS,KACb,YAAY,WAAW,MAAM,iBAAiB,QAAQ,KAAK,EAAE,UAAU,QAAQ,cAAc,UAAU,gCACzG;EACA,MAAM,KAAK,GAAG,WAAW,GAAG,EAAE,EAAE;CAClC;CAEA,OAAO,MAAM,KAAK,IAAI;AACxB"}
1
+ {"version":3,"file":"set.js","names":[],"sources":["../../../../src/core/codegen/schemas/set.ts"],"sourcesContent":["import type { SchemaIR, SetIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport { checkPriority, declareFastTemps, hasMutation } from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, tooBig, tooSmall } from \"../emit-issue.js\";\nimport { whenGatedSizeChecks } from \"./sizeable.js\";\n\nexport function slowSet(ir: SchemaIR & { type: \"set\" }, g: SlowGen): string {\n let code = emit`\n if(!(${g.input} instanceof Set)){\n ${invalidType(g, \"set\")}\n ${whenGatedSizeChecks(ir.checks ?? [], g, \"size\")}\n }else{`;\n\n // Validate each element BEFORE the size checks: Zod parses the elements\n // first and runs size checks afterward, so an invalid element surfaces ahead\n // of too_small/too_big when both fail. Mutating element schemas (coerce,\n // .trim(), url) rewrite the loop variable, which a Set cannot reflect —\n // rebuild into a fresh Set so the mutated values land in the output.\n const iterVar = g.temp(\"set_v\");\n // The element's path is the SET's own path, with no index segment: a Set has\n // no stable positional addressing, so zod's handleSetResult pushes each\n // element's issues into the set's payload UNPREFIXED (unlike an array, whose\n // handleArrayResult prefixes the index). Two bad elements therefore report at\n // the same path — that is zod's output, and an index we invented instead made\n // every set-element issue point somewhere zod never points.\n if (hasMutation(ir.valueType)) {\n const rebuiltVar = g.temp(\"set_n\");\n code += emit`\n var ${rebuiltVar}=new Set();\n for(var ${iterVar} of ${g.input}){\n ${g.visit(ir.valueType, { input: iterVar, output: iterVar, path: g.path })}\n ${rebuiltVar}.add(${iterVar});\n }\n ${g.output}=${rebuiltVar};`;\n } else {\n // An element that rewrites nothing can still hand back a REPLACEMENT — a\n // `__proto__`-scrubbed copy, a recursive element rebuilt by its own\n // validator — which zod's output Set holds in that element's place. Visited\n // with an output local of its own (see visitMember), the first replacement\n // starts the rebuilt Set: the elements before it, in order, then every\n // element's output from there on. A Set nothing replaces still comes back\n // by reference, and an element that never names its output costs nothing.\n const outVar = g.temp(\"set_o\");\n const element = g.visit(ir.valueType, { input: iterVar, output: outVar, path: g.path });\n if (!element.includes(outVar)) {\n code += emit`\n for(var ${iterVar} of ${g.input}){\n ${element}\n }`;\n } else {\n const rebuiltVar = g.temp(\"set_n\");\n const countVar = g.temp(\"set_c\");\n const skipVar = g.temp(\"set_j\");\n const earlierVar = g.temp(\"set_p\");\n code += emit`\n var ${rebuiltVar}=null;\n var ${countVar}=0;\n for(var ${iterVar} of ${g.input}){\n var ${outVar}=${iterVar};\n ${element}\n if(${rebuiltVar}!==null){\n ${rebuiltVar}.add(${outVar});\n }else if(${outVar}!==${iterVar}){\n ${rebuiltVar}=new Set();\n var ${skipVar}=0;\n for(var ${earlierVar} of ${g.input}){\n if(${skipVar}++===${countVar})break;\n ${rebuiltVar}.add(${earlierVar});\n }\n ${rebuiltVar}.add(${outVar});\n }\n ${countVar}++;\n }\n if(${rebuiltVar}!==null){${g.output}=${rebuiltVar};}`;\n }\n }\n\n // Size checks (run after element validation, mirroring Zod's check order).\n if (ir.checks) {\n for (const check of ir.checks) {\n switch (check.kind) {\n case \"min_size\":\n code += emit`\n if(${g.input}.size<${check.minimum}){\n ${tooSmall(g, check.minimum, \"set\", true, { message: check.message })}\n }`;\n break;\n case \"max_size\":\n code += emit`\n if(${g.input}.size>${check.maximum}){\n ${tooBig(g, check.maximum, \"set\", true, { message: check.message })}\n }`;\n break;\n case \"size_equals\":\n code += emit`\n if(${g.input}.size<${check.size}){\n ${tooSmall(g, check.size, \"set\", true, { exact: true, message: check.message })}\n }else if(${g.input}.size>${check.size}){\n ${tooBig(g, check.size, \"set\", true, { exact: true, message: check.message })}\n }`;\n break;\n }\n }\n }\n\n code += `}`;\n return `${code}\\n`;\n}\n\nexport function fastSet(ir: SetIR, g: FastGen): string | null {\n const x = g.input;\n const parts: string[] = [`${x} instanceof Set`];\n\n // Size checks\n if (ir.checks) {\n for (const check of [...ir.checks].sort(checkPriority)) {\n switch (check.kind) {\n case \"min_size\":\n parts.push(`${x}.size>=${check.minimum}`);\n break;\n case \"max_size\":\n parts.push(`${x}.size<=${check.maximum}`);\n break;\n case \"size_equals\":\n parts.push(`${x}.size===${check.size}`);\n break;\n }\n }\n }\n\n // Element validation via preamble helper (Set has no .every()).\n // Fresh scope: the helper is its own function, size-gated independently.\n const elemVar = g.temp(\"sv\");\n const elemGen = g.scoped(elemVar);\n const elemCheck = elemGen.visit(ir.valueType);\n if (elemCheck === null) return null;\n if (elemCheck !== \"true\") {\n const helperName = g.temp(\"se\");\n g.ctx.preamble.push(\n `function ${helperName}(s){${declareFastTemps(elemGen.scope)}for(var ${elemVar} of s){if(!(${elemCheck})){return false;}}return true;}`,\n );\n parts.push(`${helperName}(${x})`);\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"tuple.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/tuple.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;iBA4DgB,UAAU,IAAI;EAAa;GAAiB,GAAG;iBAmI/C,UAAU,IAAI,SAAS,GAAG"}
1
+ {"version":3,"file":"tuple.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/tuple.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;iBA6DgB,UAAU,IAAI;EAAa;GAAiB,GAAG;iBA6I/C,UAAU,IAAI,SAAS,GAAG"}
@@ -1,4 +1,4 @@
1
- import { declareFastTemps, extendPath, extendStaticPathIndex, hasMutation, outputAlwaysDefined, tupleRewritesShortInput } from "../context.js";
1
+ import { declareFastTemps, extendPath, extendStaticPathIndex, hasMutation, outputAlwaysDefined, tupleRewritesShortInput, visitMember } from "../context.js";
2
2
  import { emit } from "../emit.js";
3
3
  import { invalidType, tooBig, tooSmall } from "../emit-issue.js";
4
4
  import { orderByRuntimeCost } from "../fast-size.js";
@@ -52,11 +52,12 @@ function slowTuple(ir, g) {
52
52
  for (let i = 0; i < len; i++) {
53
53
  const itemIR = ir.items[i];
54
54
  const elemExpr = `${x}[${i}]`;
55
- const elemPath = extendStaticPathIndex(g.path, i);
56
- const itemCode = g.visit(itemIR, {
57
- input: elemExpr,
58
- output: elemExpr,
59
- path: elemPath,
55
+ const itemCode = visitMember(g, itemIR, {
56
+ container: x,
57
+ original: g.input,
58
+ copy: `${x}.slice()`,
59
+ key: String(i),
60
+ path: extendStaticPathIndex(g.path, i),
60
61
  issues: itemIssues
61
62
  });
62
63
  const absent = `${i}>=${lenVar}`;
@@ -90,15 +91,17 @@ function slowTuple(ir, g) {
90
91
  }
91
92
  if (ir.rest !== null) {
92
93
  const idxVar = g.temp("ti");
93
- const restExpr = `${x}[${idxVar}]`;
94
- const restPath = extendPath(g.path, idxVar);
94
+ const restCode = visitMember(g, ir.rest, {
95
+ container: x,
96
+ original: g.input,
97
+ copy: `${x}.slice()`,
98
+ key: idxVar,
99
+ path: extendPath(g.path, idxVar),
100
+ issues: g.issues
101
+ });
95
102
  itemsCode += emit`
96
103
  for(var ${idxVar}=${len};${idxVar}<${x}.length;${idxVar}++){
97
- ${g.visit(ir.rest, {
98
- input: restExpr,
99
- output: restExpr,
100
- path: restPath
101
- })}
104
+ ${restCode}
102
105
  }`;
103
106
  const flushVar = g.temp("tqj");
104
107
  itemsCode += `for(var ${flushVar}=0;${flushVar}<${itemIssues}.length;${flushVar}++){${g.issues}.push(${itemIssues}[${flushVar}]);}`;