zod-compiler 1.23.7 → 1.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +116 -12
  2. package/dist/core/codegen/build-path.d.ts +31 -1
  3. package/dist/core/codegen/build-path.d.ts.map +1 -1
  4. package/dist/core/codegen/build-path.js +44 -4
  5. package/dist/core/codegen/build-path.js.map +1 -1
  6. package/dist/core/codegen/context.d.ts +17 -1
  7. package/dist/core/codegen/context.d.ts.map +1 -1
  8. package/dist/core/codegen/context.js +18 -2
  9. package/dist/core/codegen/context.js.map +1 -1
  10. package/dist/core/codegen/emit-issue.d.ts +46 -6
  11. package/dist/core/codegen/emit-issue.d.ts.map +1 -1
  12. package/dist/core/codegen/emit-issue.js +48 -18
  13. package/dist/core/codegen/emit-issue.js.map +1 -1
  14. package/dist/core/codegen/index.js +3 -3
  15. package/dist/core/codegen/index.js.map +1 -1
  16. package/dist/core/codegen/issue-decls.d.ts +62 -2
  17. package/dist/core/codegen/issue-decls.d.ts.map +1 -1
  18. package/dist/core/codegen/issue-decls.js +64 -11
  19. package/dist/core/codegen/issue-decls.js.map +1 -1
  20. package/dist/core/codegen/schemas/array.d.ts.map +1 -1
  21. package/dist/core/codegen/schemas/array.js +2 -0
  22. package/dist/core/codegen/schemas/array.js.map +1 -1
  23. package/dist/core/codegen/schemas/bigint.js +1 -1
  24. package/dist/core/codegen/schemas/bigint.js.map +1 -1
  25. package/dist/core/codegen/schemas/discriminated-union.js +2 -2
  26. package/dist/core/codegen/schemas/discriminated-union.js.map +1 -1
  27. package/dist/core/codegen/schemas/effect.js +1 -1
  28. package/dist/core/codegen/schemas/effect.js.map +1 -1
  29. package/dist/core/codegen/schemas/file.d.ts.map +1 -1
  30. package/dist/core/codegen/schemas/file.js +2 -0
  31. package/dist/core/codegen/schemas/file.js.map +1 -1
  32. package/dist/core/codegen/schemas/map.js +2 -2
  33. package/dist/core/codegen/schemas/map.js.map +1 -1
  34. package/dist/core/codegen/schemas/number.js +6 -3
  35. package/dist/core/codegen/schemas/number.js.map +1 -1
  36. package/dist/core/codegen/schemas/record.d.ts.map +1 -1
  37. package/dist/core/codegen/schemas/record.js +23 -9
  38. package/dist/core/codegen/schemas/record.js.map +1 -1
  39. package/dist/core/codegen/schemas/set.d.ts.map +1 -1
  40. package/dist/core/codegen/schemas/set.js +2 -0
  41. package/dist/core/codegen/schemas/set.js.map +1 -1
  42. package/dist/core/codegen/schemas/sizeable.d.ts +37 -0
  43. package/dist/core/codegen/schemas/sizeable.d.ts.map +1 -0
  44. package/dist/core/codegen/schemas/sizeable.js +115 -0
  45. package/dist/core/codegen/schemas/sizeable.js.map +1 -0
  46. package/dist/core/codegen/schemas/string.d.ts.map +1 -1
  47. package/dist/core/codegen/schemas/string.js +10 -4
  48. package/dist/core/codegen/schemas/string.js.map +1 -1
  49. package/dist/core/codegen/schemas/tuple.d.ts.map +1 -1
  50. package/dist/core/codegen/schemas/tuple.js +25 -20
  51. package/dist/core/codegen/schemas/tuple.js.map +1 -1
  52. package/dist/core/codegen/schemas/union.d.ts.map +1 -1
  53. package/dist/core/codegen/schemas/union.js +5 -2
  54. package/dist/core/codegen/schemas/union.js.map +1 -1
  55. package/dist/core/extract/extractors/object.d.ts.map +1 -1
  56. package/dist/core/extract/extractors/object.js +21 -1
  57. package/dist/core/extract/extractors/object.js.map +1 -1
  58. package/dist/core/extract/extractors/optional.d.ts.map +1 -1
  59. package/dist/core/extract/extractors/optional.js +31 -1
  60. package/dist/core/extract/extractors/optional.js.map +1 -1
  61. package/dist/core/extract/extractors/tuple.d.ts.map +1 -1
  62. package/dist/core/extract/extractors/tuple.js +23 -1
  63. package/dist/core/extract/extractors/tuple.js.map +1 -1
  64. package/dist/core/extract/types.d.ts +9 -0
  65. package/dist/core/extract/types.d.ts.map +1 -1
  66. package/dist/core/iife.d.ts +17 -0
  67. package/dist/core/iife.d.ts.map +1 -1
  68. package/dist/core/iife.js +19 -2
  69. package/dist/core/iife.js.map +1 -1
  70. package/dist/core/types.d.ts +27 -0
  71. package/dist/core/types.d.ts.map +1 -1
  72. package/dist/runtime.d.ts +53 -0
  73. package/dist/runtime.js +55 -0
  74. package/dist/turbopack.d.ts +56 -0
  75. package/dist/turbopack.d.ts.map +1 -0
  76. package/dist/turbopack.js +200 -0
  77. package/dist/turbopack.js.map +1 -0
  78. package/dist/unplugin/dep-graph.d.ts +45 -2
  79. package/dist/unplugin/dep-graph.d.ts.map +1 -1
  80. package/dist/unplugin/dep-graph.js +42 -2
  81. package/dist/unplugin/dep-graph.js.map +1 -1
  82. package/dist/unplugin/edits.d.ts +13 -1
  83. package/dist/unplugin/edits.d.ts.map +1 -1
  84. package/dist/unplugin/edits.js +35 -1
  85. package/dist/unplugin/edits.js.map +1 -1
  86. package/dist/unplugin/hoist.d.ts.map +1 -1
  87. package/dist/unplugin/hoist.js +2 -26
  88. package/dist/unplugin/hoist.js.map +1 -1
  89. package/dist/unplugin/transform.d.ts.map +1 -1
  90. package/dist/unplugin/transform.js +38 -19
  91. package/dist/unplugin/transform.js.map +1 -1
  92. package/dist/unplugin/virtual.d.ts +28 -1
  93. package/dist/unplugin/virtual.d.ts.map +1 -1
  94. package/dist/unplugin/virtual.js +27 -1
  95. package/dist/unplugin/virtual.js.map +1 -1
  96. package/package.json +12 -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 emitConstant,\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\")}\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 // input, path: [def.discriminator] }`. There is deliberately no `options`\n // key — zod never enumerates the valid discriminator values here, so adding\n // them invents a field consumers would find on no real zod issue.\n code += emit`\n default:\n ${g.issues}.push({code:\"invalid_union\",errors:[],note:\"No matching discriminator\",discriminator:${discKey}${msgProp},input:${g.input},path:${extendPath(g.path, discKey)}});\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 const body = g.scoped(helperParam);\n const table = stringDispatchTable(cases);\n\n if (table === null) {\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 } else {\n // Dispatch through a string→ordinal table, then switch on the ordinal. V8\n // compiles a switch over STRING labels as sequential `===` comparisons, so\n // the plain form costs ~0.5 ns per preceding case: measured 4.1 ns (2\n // variants), 11.4 (8), 52.1 (80). A dense integer switch becomes a jump\n // table, leaving the lookup flat — 5.9 ns (8) and 15.1 (80), i.e. 1.9x to\n // 3.5x, for a table that adds ~1.3% to a union's generated bytes.\n //\n // `typeof t===\"string\"` is load-bearing: property access would coerce a\n // non-string discriminator (an object's toString, a number) into a key that\n // could hit a case whose own discriminator check the switch has stripped.\n // Strict equality never matched those, so the guard keeps the verdict.\n const caseStrs: string[] = [];\n for (const [optionIndex, ordinal] of table.ordinals) {\n const check = body.visit(options[optionIndex] as SchemaIR, { discSkipKey: discriminator });\n if (check === null) return null;\n caseStrs.push(`case ${ordinal}:return ${check};`);\n }\n const tableVar = emitConstant(g.ctx, \"dt\", table.initializer);\n const t = g.temp(\"dv\");\n g.ctx.preamble.push(\n `function ${helperName}(${helperParam}){${declareFastTemps(body.scope)}var ${t}=${helperParam}[${discKey}];` +\n `switch(typeof ${t}===\"string\"?${tableVar}[${t}]:0){${caseStrs.join(\"\")}default:return false;}}`,\n );\n }\n\n return `typeof ${x}===\"object\"&&${x}!==null&&!Array.isArray(${x})&&${helperName}(${x})`;\n}\n\n/**\n * Minimum case count for ordinal dispatch. Measured crossover is 3: at 2 cases\n * the string switch is one comparison and beats the extra table lookup\n * (4.1 ns vs 4.5), at 3 the table already wins (6.1 vs 4.6).\n */\nconst MIN_TABLE_DISPATCH = 3;\n\n/**\n * Build the `{value: ordinal}` dispatch table for a set of cases, or null when\n * the plain string switch should be kept. Requires every discriminator value to\n * be a string — mixed types would collide once coerced to property keys (`5`\n * and `\"5\"`) — and excludes `__proto__`, which an object literal cannot hold as\n * an own key. Values that select the SAME option share one ordinal, so a\n * multi-value literal emits its check once instead of per value.\n */\nfunction stringDispatchTable(\n cases: readonly DiscriminatorCase[],\n): { initializer: string; ordinals: Map<number, number> } | null {\n if (cases.length < MIN_TABLE_DISPATCH) return null;\n if (!cases.every((c) => typeof c.value === \"string\" && c.value !== \"__proto__\")) return null;\n\n const ordinals = new Map<number, number>();\n const entries: string[] = [];\n for (const { value, option } of cases) {\n let ordinal = ordinals.get(option);\n if (ordinal === undefined) {\n ordinal = ordinals.size + 1;\n ordinals.set(option, ordinal);\n }\n entries.push(`${escapeString(value as string)}:${ordinal}`);\n }\n return { initializer: `{${entries.join(\",\")}}`, ordinals };\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 * Fast-path only: the slow path keeps `z.union`'s sequential trial and its\n * `invalid_union` error shape, so failure output stays byte-identical to Zod.\n */\nexport function detectUnionDiscriminator(\n options: readonly SchemaIR[],\n): { discriminator: string; cases: DiscriminatorCase[] } | null {\n if (options.length < MIN_AUTO_DISCRIMINATE_OPTIONS) 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 {\n declareFastTemps,\n emitConstant,\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 // input, path: [def.discriminator] }`. There is deliberately no `options`\n // key — zod never enumerates the valid discriminator values here, so adding\n // them invents a field consumers would find on no real zod issue.\n code += emit`\n default:\n ${g.issues}.push({code:\"invalid_union\",errors:[],note:\"No matching discriminator\",discriminator:${discKey},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 const body = g.scoped(helperParam);\n const table = stringDispatchTable(cases);\n\n if (table === null) {\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 } else {\n // Dispatch through a string→ordinal table, then switch on the ordinal. V8\n // compiles a switch over STRING labels as sequential `===` comparisons, so\n // the plain form costs ~0.5 ns per preceding case: measured 4.1 ns (2\n // variants), 11.4 (8), 52.1 (80). A dense integer switch becomes a jump\n // table, leaving the lookup flat — 5.9 ns (8) and 15.1 (80), i.e. 1.9x to\n // 3.5x, for a table that adds ~1.3% to a union's generated bytes.\n //\n // `typeof t===\"string\"` is load-bearing: property access would coerce a\n // non-string discriminator (an object's toString, a number) into a key that\n // could hit a case whose own discriminator check the switch has stripped.\n // Strict equality never matched those, so the guard keeps the verdict.\n const caseStrs: string[] = [];\n for (const [optionIndex, ordinal] of table.ordinals) {\n const check = body.visit(options[optionIndex] as SchemaIR, { discSkipKey: discriminator });\n if (check === null) return null;\n caseStrs.push(`case ${ordinal}:return ${check};`);\n }\n const tableVar = emitConstant(g.ctx, \"dt\", table.initializer);\n const t = g.temp(\"dv\");\n g.ctx.preamble.push(\n `function ${helperName}(${helperParam}){${declareFastTemps(body.scope)}var ${t}=${helperParam}[${discKey}];` +\n `switch(typeof ${t}===\"string\"?${tableVar}[${t}]:0){${caseStrs.join(\"\")}default:return false;}}`,\n );\n }\n\n return `typeof ${x}===\"object\"&&${x}!==null&&!Array.isArray(${x})&&${helperName}(${x})`;\n}\n\n/**\n * Minimum case count for ordinal dispatch. Measured crossover is 3: at 2 cases\n * the string switch is one comparison and beats the extra table lookup\n * (4.1 ns vs 4.5), at 3 the table already wins (6.1 vs 4.6).\n */\nconst MIN_TABLE_DISPATCH = 3;\n\n/**\n * Build the `{value: ordinal}` dispatch table for a set of cases, or null when\n * the plain string switch should be kept. Requires every discriminator value to\n * be a string — mixed types would collide once coerced to property keys (`5`\n * and `\"5\"`) — and excludes `__proto__`, which an object literal cannot hold as\n * an own key. Values that select the SAME option share one ordinal, so a\n * multi-value literal emits its check once instead of per value.\n */\nfunction stringDispatchTable(\n cases: readonly DiscriminatorCase[],\n): { initializer: string; ordinals: Map<number, number> } | null {\n if (cases.length < MIN_TABLE_DISPATCH) return null;\n if (!cases.every((c) => typeof c.value === \"string\" && c.value !== \"__proto__\")) return null;\n\n const ordinals = new Map<number, number>();\n const entries: string[] = [];\n for (const { value, option } of cases) {\n let ordinal = ordinals.get(option);\n if (ordinal === undefined) {\n ordinal = ordinals.size + 1;\n ordinals.set(option, ordinal);\n }\n entries.push(`${escapeString(value as string)}:${ordinal}`);\n }\n return { initializer: `{${entries.join(\",\")}}`, ordinals };\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 * Fast-path only: the slow path keeps `z.union`'s sequential trial and its\n * `invalid_union` error shape, so failure output stays byte-identical to Zod.\n */\nexport function detectUnionDiscriminator(\n options: readonly SchemaIR[],\n): { discriminator: string; cases: DiscriminatorCase[] } | null {\n if (options.length < MIN_AUTO_DISCRIMINATE_OPTIONS) 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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"}
@@ -44,7 +44,7 @@ function refineCheck(check, expr, g) {
44
44
  const paramsProp = check.paramsRefIndex === void 0 ? "" : `,params:${emitConstant(g.ctx, "rpa", `__rf[${check.paramsRefIndex}]`)}`;
45
45
  return emit`
46
46
  if(!${emitEffectCallable(g.ctx, check)}(${expr})){
47
- ${g.issues}.push({code:"custom",path:${path}${paramsProp}${messageProp},input:${expr}});
47
+ ${g.issues}.push({code:"custom",path:${path}${paramsProp},input:${expr}${messageProp}});
48
48
  }`;
49
49
  }
50
50
  /**
@@ -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_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 if (ir.effectKind === \"preprocess\") {\n const valueVar = g.temp(\"pv\");\n return `${emit`\n var ${valueVar}=${emitEffectCallable(g.ctx, ir)}(${g.input});\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 }${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 // Custom issues are created by the refine check instance, so only the\n // refine's own message applies (never the schema-level error). With no\n // message baked in, __zcFin applies the locale default (\"Invalid input\").\n const messageProp =\n check.message === undefined ? \"\" : `,message:${JSON.stringify(check.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 return emit`\n if(!${emitEffectCallable(g.ctx, check)}(${expr})){\n ${g.issues}.push({code:\"custom\",path:${path}${paramsProp}${messageProp},input:${expr}});\n }`;\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 let code = `var ${p}=${helper}(${fn},${expr},${g.path},${g.issues});${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":";;;;;;;;AAqBA,SAAgB,WAAW,IAA4C,GAAoB;CACzF,IAAI,GAAG,eAAe,cAAc;EAClC,MAAM,WAAW,EAAE,KAAK,IAAI;EAC5B,OAAO,GAAG,IAAI;YACN,SAAS,GAAG,mBAAmB,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM;QACzD,EAAE,OAAO,GAAG,SAAS;QACrB,EAAE,MAAM,GAAG,OAAO;GAAE,OAAO;GAAU,QAAQ,EAAE;GAAQ,SAAS,EAAE;EAAQ,CAAC,EAAE;MAC/E;CACJ;CAEA,MAAM,YAAY,EAAE,KAAK,IAAI;CAC7B,MAAM,YAAY,EAAE,MAAM,GAAG,KAAK;CAKlC,MAAM,cAAc,EAAE,UAAU,QAAQ,EAAE,QAAQ,WAAW;CAE7D,OAAO,GAAG,IAAI;UACN,UAAU,GAAG,EAAE,OAAO;MAC1B,UAAU;SACP,EAAE,OAAO,YAAY,UAAU;QAChC,EAAE,OAAO,GAAG,mBAAmB,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,OAAO;OACvD,YAAY;IACf;AACJ;;;;;;;;;AAUA,SAAgB,YAAY,OAA4B,MAAc,GAAoB;CAIxF,MAAM,cACJ,MAAM,YAAY,KAAA,IAAY,KAAK,YAAY,KAAK,UAAU,MAAM,OAAO;CAG7E,MAAM,QAAQ,MAAM,QAAQ,CAAC,EAAA,CAAG,QAC7B,KAAK,YACJ,OAAO,YAAY,WACf,sBAAsB,KAAK,OAAO,IAClC,iBAAiB,KAAK,OAAO,GACnC,EAAE,IACJ;CAOA,MAAM,aACJ,MAAM,mBAAmB,KAAA,IACrB,KACA,WAAW,aAAa,EAAE,KAAK,OAAO,QAAQ,MAAM,eAAe,EAAE;CAC3E,OAAO,IAAI;UACH,mBAAmB,EAAE,KAAK,KAAK,EAAE,GAAG,KAAK;QAC3C,EAAE,OAAO,4BAA4B,OAAO,aAAa,YAAY,SAAS,KAAK;;AAE3F;;;;;;AAOA,SAAS,sBAAsB,KAA2B;CACxD,IAAI,IAAI,SAAS,UAAU,CAAC,IAAI,SAAS,SAAA,+GAAuB,GAC9D,IAAI,SAAS,KAAK,cAAc;AAEpC;;;;;AAMA,SAAgB,oBACd,OACA,MACA,GACQ;CACR,sBAAsB,EAAE,GAAG;CAE3B,OAAO,GADI,kBAAkB,EAAE,KAAK,YAAY,aACrC,EAAE,GAAG,mBAAmB,EAAE,KAAK,KAAK,EAAE,GAAG,KAAK;AAC3D;;;;;;;;;;;;;AAcA,SAAgB,iBACd,OACA,MACA,GACQ;CACR,sBAAsB,EAAE,GAAG;CAC3B,MAAM,SAAS,kBAAkB,EAAE,KAAK,UAAU,UAAU;CAC5D,MAAM,KAAK,mBAAmB,EAAE,KAAK,KAAK;CAC1C,MAAM,IAAI,EAAE,KAAK,IAAI;CACrB,IAAI,OAAO,OAAO,EAAE,GAAG,OAAO,GAAG,GAAG,GAAG,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,OAAO,IAAI,EAAE,OAAO,GAAG,EAAE;CAGpF,IAAI,EAAE,SAAS,QAAQ,MAAM,EAAE,YAAY,EAAE,QAAQ;CACrD,OAAO;AACT"}
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_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 if (ir.effectKind === \"preprocess\") {\n const valueVar = g.temp(\"pv\");\n return `${emit`\n var ${valueVar}=${emitEffectCallable(g.ctx, ir)}(${g.input});\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 }${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 // Custom issues are created by the refine check instance, so only the\n // refine's own message applies (never the schema-level error). With no\n // message baked in, __zcFin applies the locale default (\"Invalid input\").\n const messageProp =\n check.message === undefined ? \"\" : `,message:${JSON.stringify(check.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 return emit`\n if(!${emitEffectCallable(g.ctx, check)}(${expr})){\n ${g.issues}.push({code:\"custom\",path:${path}${paramsProp},input:${expr}${messageProp}});\n }`;\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 let code = `var ${p}=${helper}(${fn},${expr},${g.path},${g.issues});${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":";;;;;;;;AAqBA,SAAgB,WAAW,IAA4C,GAAoB;CACzF,IAAI,GAAG,eAAe,cAAc;EAClC,MAAM,WAAW,EAAE,KAAK,IAAI;EAC5B,OAAO,GAAG,IAAI;YACN,SAAS,GAAG,mBAAmB,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM;QACzD,EAAE,OAAO,GAAG,SAAS;QACrB,EAAE,MAAM,GAAG,OAAO;GAAE,OAAO;GAAU,QAAQ,EAAE;GAAQ,SAAS,EAAE;EAAQ,CAAC,EAAE;MAC/E;CACJ;CAEA,MAAM,YAAY,EAAE,KAAK,IAAI;CAC7B,MAAM,YAAY,EAAE,MAAM,GAAG,KAAK;CAKlC,MAAM,cAAc,EAAE,UAAU,QAAQ,EAAE,QAAQ,WAAW;CAE7D,OAAO,GAAG,IAAI;UACN,UAAU,GAAG,EAAE,OAAO;MAC1B,UAAU;SACP,EAAE,OAAO,YAAY,UAAU;QAChC,EAAE,OAAO,GAAG,mBAAmB,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,OAAO;OACvD,YAAY;IACf;AACJ;;;;;;;;;AAUA,SAAgB,YAAY,OAA4B,MAAc,GAAoB;CAIxF,MAAM,cACJ,MAAM,YAAY,KAAA,IAAY,KAAK,YAAY,KAAK,UAAU,MAAM,OAAO;CAG7E,MAAM,QAAQ,MAAM,QAAQ,CAAC,EAAA,CAAG,QAC7B,KAAK,YACJ,OAAO,YAAY,WACf,sBAAsB,KAAK,OAAO,IAClC,iBAAiB,KAAK,OAAO,GACnC,EAAE,IACJ;CAOA,MAAM,aACJ,MAAM,mBAAmB,KAAA,IACrB,KACA,WAAW,aAAa,EAAE,KAAK,OAAO,QAAQ,MAAM,eAAe,EAAE;CAC3E,OAAO,IAAI;UACH,mBAAmB,EAAE,KAAK,KAAK,EAAE,GAAG,KAAK;QAC3C,EAAE,OAAO,4BAA4B,OAAO,WAAW,SAAS,OAAO,YAAY;;AAE3F;;;;;;AAOA,SAAS,sBAAsB,KAA2B;CACxD,IAAI,IAAI,SAAS,UAAU,CAAC,IAAI,SAAS,SAAA,+GAAuB,GAC9D,IAAI,SAAS,KAAK,cAAc;AAEpC;;;;;AAMA,SAAgB,oBACd,OACA,MACA,GACQ;CACR,sBAAsB,EAAE,GAAG;CAE3B,OAAO,GADI,kBAAkB,EAAE,KAAK,YAAY,aACrC,EAAE,GAAG,mBAAmB,EAAE,KAAK,KAAK,EAAE,GAAG,KAAK;AAC3D;;;;;;;;;;;;;AAcA,SAAgB,iBACd,OACA,MACA,GACQ;CACR,sBAAsB,EAAE,GAAG;CAC3B,MAAM,SAAS,kBAAkB,EAAE,KAAK,UAAU,UAAU;CAC5D,MAAM,KAAK,mBAAmB,EAAE,KAAK,KAAK;CAC1C,MAAM,IAAI,EAAE,KAAK,IAAI;CACrB,IAAI,OAAO,OAAO,EAAE,GAAG,OAAO,GAAG,GAAG,GAAG,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,OAAO,IAAI,EAAE,OAAO,GAAG,EAAE;CAGpF,IAAI,EAAE,SAAS,QAAQ,MAAM,EAAE,YAAY,EAAE,QAAQ;CACrD,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"file.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/file.ts"],"mappings":";;;iBAMgB,SAAS,IAAI,QAAQ,GAAG;iBA8CxB,SAAS,IAAI,QAAQ,GAAG"}
1
+ {"version":3,"file":"file.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/file.ts"],"mappings":";;;iBAOgB,SAAS,IAAI,QAAQ,GAAG;iBA+CxB,SAAS,IAAI,QAAQ,GAAG"}
@@ -1,11 +1,13 @@
1
1
  import { emitSet, escapeString } from "../context.js";
2
2
  import { emit } from "../emit.js";
3
3
  import { invalidType, invalidValue, tooBig, tooSmall } from "../emit-issue.js";
4
+ import { whenGatedSizeChecks } from "./sizeable.js";
4
5
  //#region src/core/codegen/schemas/file.ts
5
6
  function slowFile(ir, g) {
6
7
  let code = emit`
7
8
  if(!(${g.input} instanceof File)){
8
9
  ${invalidType(g, "file")}
10
+ ${whenGatedSizeChecks(ir.checks ?? [], g, "size")}
9
11
  }else{`;
10
12
  if (ir.checks) for (const check of ir.checks) switch (check.kind) {
11
13
  case "min_size":
@@ -1 +1 @@
1
- {"version":3,"file":"file.js","names":[],"sources":["../../../../src/core/codegen/schemas/file.ts"],"sourcesContent":["import type { FileIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport { emitSet, escapeString } from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, invalidValue, tooBig, tooSmall } from \"../emit-issue.js\";\n\nexport function slowFile(ir: FileIR, g: SlowGen): string {\n let code = emit`\n if(!(${g.input} instanceof File)){\n ${invalidType(g, \"file\")}\n }else{`;\n\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, \"file\", 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, \"file\", true, { message: check.message })}\n }`;\n break;\n case \"mime_type\": {\n if (check.mime.length === 0) break;\n const mimeValuesExpr = JSON.stringify(check.mime);\n if (check.mime.length === 1) {\n code += emit`\n if(${g.input}.type!==${escapeString(check.mime[0] as string)}){\n ${invalidValue(g, mimeValuesExpr, { message: check.message, useTypeMsg: false })}\n }`;\n } else {\n const setVar = g.set(\"mime\", check.mime);\n code += emit`\n if(!${setVar}.has(${g.input}.type)){\n ${invalidValue(g, mimeValuesExpr, { message: check.message, useTypeMsg: false })}\n }`;\n }\n break;\n }\n }\n }\n }\n\n code += emit`}`;\n return `${code}\\n`;\n}\n\nexport function fastFile(ir: FileIR, g: FastGen): string | null {\n const x = g.input;\n const parts: string[] = [`${x} instanceof File`];\n\n if (ir.checks) {\n for (const check of ir.checks) {\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 \"mime_type\":\n if (check.mime.length === 0) break;\n if (check.mime.length === 1) {\n parts.push(`${x}.type===${escapeString(check.mime[0] as string)}`);\n } else {\n parts.push(`${emitSet(g.ctx, \"mime\", check.mime)}.has(${x}.type)`);\n }\n break;\n }\n }\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":";;;;AAMA,SAAgB,SAAS,IAAY,GAAoB;CACvD,IAAI,OAAO,IAAI;WACN,EAAE,MAAM;QACX,YAAY,GAAG,MAAM,EAAE;;CAG7B,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,QAAQ,MAAM,EAAE,SAAS,MAAM,QAAQ,CAAC,EAAE;;GAE3E;EACF,KAAK;GACH,QAAQ,IAAI;iBACL,EAAE,MAAM,QAAQ,MAAM,QAAQ;gBAC/B,OAAO,GAAG,MAAM,SAAS,QAAQ,MAAM,EAAE,SAAS,MAAM,QAAQ,CAAC,EAAE;;GAEzE;EACF,KAAK,aAAa;GAChB,IAAI,MAAM,KAAK,WAAW,GAAG;GAC7B,MAAM,iBAAiB,KAAK,UAAU,MAAM,IAAI;GAChD,IAAI,MAAM,KAAK,WAAW,GACxB,QAAQ,IAAI;mBACL,EAAE,MAAM,UAAU,aAAa,MAAM,KAAK,EAAY,EAAE;kBACzD,aAAa,GAAG,gBAAgB;IAAE,SAAS,MAAM;IAAS,YAAY;GAAM,CAAC,EAAE;;QAEhF;IACL,MAAM,SAAS,EAAE,IAAI,QAAQ,MAAM,IAAI;IACvC,QAAQ,IAAI;oBACJ,OAAO,OAAO,EAAE,MAAM;kBACxB,aAAa,GAAG,gBAAgB;KAAE,SAAS,MAAM;KAAS,YAAY;IAAM,CAAC,EAAE;;GAEvF;GACA;EACF;CACF;CAIJ,QAAQ,IAAI;CACZ,OAAO,GAAG,KAAK;AACjB;AAEA,SAAgB,SAAS,IAAY,GAA2B;CAC9D,MAAM,IAAI,EAAE;CACZ,MAAM,QAAkB,CAAC,GAAG,EAAE,iBAAiB;CAE/C,IAAI,GAAG,QACL,KAAK,MAAM,SAAS,GAAG,QACrB,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,IAAI,MAAM,KAAK,WAAW,GAAG;GAC7B,IAAI,MAAM,KAAK,WAAW,GACxB,MAAM,KAAK,GAAG,EAAE,UAAU,aAAa,MAAM,KAAK,EAAY,GAAG;QAEjE,MAAM,KAAK,GAAG,QAAQ,EAAE,KAAK,QAAQ,MAAM,IAAI,EAAE,OAAO,EAAE,OAAO;GAEnE;CACJ;CAIJ,OAAO,MAAM,KAAK,IAAI;AACxB"}
1
+ {"version":3,"file":"file.js","names":[],"sources":["../../../../src/core/codegen/schemas/file.ts"],"sourcesContent":["import type { FileIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport { emitSet, escapeString } from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, invalidValue, tooBig, tooSmall } from \"../emit-issue.js\";\nimport { whenGatedSizeChecks } from \"./sizeable.js\";\n\nexport function slowFile(ir: FileIR, g: SlowGen): string {\n let code = emit`\n if(!(${g.input} instanceof File)){\n ${invalidType(g, \"file\")}\n ${whenGatedSizeChecks(ir.checks ?? [], g, \"size\")}\n }else{`;\n\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, \"file\", 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, \"file\", true, { message: check.message })}\n }`;\n break;\n case \"mime_type\": {\n if (check.mime.length === 0) break;\n const mimeValuesExpr = JSON.stringify(check.mime);\n if (check.mime.length === 1) {\n code += emit`\n if(${g.input}.type!==${escapeString(check.mime[0] as string)}){\n ${invalidValue(g, mimeValuesExpr, { message: check.message, useTypeMsg: false })}\n }`;\n } else {\n const setVar = g.set(\"mime\", check.mime);\n code += emit`\n if(!${setVar}.has(${g.input}.type)){\n ${invalidValue(g, mimeValuesExpr, { message: check.message, useTypeMsg: false })}\n }`;\n }\n break;\n }\n }\n }\n }\n\n code += emit`}`;\n return `${code}\\n`;\n}\n\nexport function fastFile(ir: FileIR, g: FastGen): string | null {\n const x = g.input;\n const parts: string[] = [`${x} instanceof File`];\n\n if (ir.checks) {\n for (const check of ir.checks) {\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 \"mime_type\":\n if (check.mime.length === 0) break;\n if (check.mime.length === 1) {\n parts.push(`${x}.type===${escapeString(check.mime[0] as string)}`);\n } else {\n parts.push(`${emitSet(g.ctx, \"mime\", check.mime)}.has(${x}.type)`);\n }\n break;\n }\n }\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":";;;;;AAOA,SAAgB,SAAS,IAAY,GAAoB;CACvD,IAAI,OAAO,IAAI;WACN,EAAE,MAAM;QACX,YAAY,GAAG,MAAM,EAAE;QACvB,oBAAoB,GAAG,UAAU,CAAC,GAAG,GAAG,MAAM,EAAE;;CAGtD,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,QAAQ,MAAM,EAAE,SAAS,MAAM,QAAQ,CAAC,EAAE;;GAE3E;EACF,KAAK;GACH,QAAQ,IAAI;iBACL,EAAE,MAAM,QAAQ,MAAM,QAAQ;gBAC/B,OAAO,GAAG,MAAM,SAAS,QAAQ,MAAM,EAAE,SAAS,MAAM,QAAQ,CAAC,EAAE;;GAEzE;EACF,KAAK,aAAa;GAChB,IAAI,MAAM,KAAK,WAAW,GAAG;GAC7B,MAAM,iBAAiB,KAAK,UAAU,MAAM,IAAI;GAChD,IAAI,MAAM,KAAK,WAAW,GACxB,QAAQ,IAAI;mBACL,EAAE,MAAM,UAAU,aAAa,MAAM,KAAK,EAAY,EAAE;kBACzD,aAAa,GAAG,gBAAgB;IAAE,SAAS,MAAM;IAAS,YAAY;GAAM,CAAC,EAAE;;QAEhF;IACL,MAAM,SAAS,EAAE,IAAI,QAAQ,MAAM,IAAI;IACvC,QAAQ,IAAI;oBACJ,OAAO,OAAO,EAAE,MAAM;kBACxB,aAAa,GAAG,gBAAgB;KAAE,SAAS,MAAM;KAAS,YAAY;IAAM,CAAC,EAAE;;GAEvF;GACA;EACF;CACF;CAIJ,QAAQ,IAAI;CACZ,OAAO,GAAG,KAAK;AACjB;AAEA,SAAgB,SAAS,IAAY,GAA2B;CAC9D,MAAM,IAAI,EAAE;CACZ,MAAM,QAAkB,CAAC,GAAG,EAAE,iBAAiB;CAE/C,IAAI,GAAG,QACL,KAAK,MAAM,SAAS,GAAG,QACrB,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,IAAI,MAAM,KAAK,WAAW,GAAG;GAC7B,IAAI,MAAM,KAAK,WAAW,GACxB,MAAM,KAAK,GAAG,EAAE,UAAU,aAAa,MAAM,KAAK,EAAY,GAAG;QAEjE,MAAM,KAAK,GAAG,QAAQ,EAAE,KAAK,QAAQ,MAAM,IAAI,EAAE,OAAO,EAAE,OAAO;GAEnE;CACJ;CAIJ,OAAO,MAAM,KAAK,IAAI;AACxB"}
@@ -57,7 +57,7 @@ function slowMap(ir, g) {
57
57
  if(${pkVar}){
58
58
  ${pfx}(${g.issues},${keyIssues},${g.path},${keyVar});
59
59
  }else{
60
- ${g.issues}.push({code:"invalid_key",origin:"map"${msgProp},input:${g.input},path:${g.path},issues:${fz}(${keyIssues})});
60
+ ${g.issues}.push({code:"invalid_key",origin:"map",issues:${fz}(${keyIssues}),input:${g.input},path:${g.path}${msgProp}});
61
61
  }
62
62
  }
63
63
  var ${valIssues}=[];
@@ -66,7 +66,7 @@ function slowMap(ir, g) {
66
66
  if(${pkVar}){
67
67
  ${pfx}(${g.issues},${valIssues},${g.path},${keyVar});
68
68
  }else{
69
- ${g.issues}.push({code:"invalid_element",origin:"map",key:${keyVar}${msgProp},input:${g.input},path:${g.path},issues:${fz}(${valIssues})});
69
+ ${g.issues}.push({origin:"map",code:"invalid_element",key:${keyVar},issues:${fz}(${valIssues}),input:${g.input},path:${g.path}${msgProp}});
70
70
  }
71
71
  }
72
72
  ${mutates ? `${rebuiltVar}.set(${entryVar}[0],${entryVar}[1]);` : ""}
@@ -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\"${msgProp},input:${g.input},path:${g.path},issues:${fz}(${keyIssues})});\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({code:\"invalid_element\",origin:\"map\",key:${keyVar}${msgProp},input:${g.input},path:${g.path},issues:${fz}(${valIssues})});\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,wCAAwC,QAAQ,SAAS,EAAE,MAAM,QAAQ,EAAE,KAAK,UAAU,GAAG,GAAG,UAAU;;;cAGnH,UAAU;UACd,QAAQ;aACL,UAAU;eACR,MAAM;cACP,IAAI,GAAG,EAAE,OAAO,GAAG,UAAU,GAAG,EAAE,KAAK,GAAG,OAAO;;cAEjD,EAAE,OAAO,iDAAiD,SAAS,QAAQ,SAAS,EAAE,MAAM,QAAQ,EAAE,KAAK,UAAU,GAAG,GAAG,UAAU;;;UAGzI,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 // 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"}
@@ -55,17 +55,19 @@ function slowNumber(ir, g) {
55
55
  if(!Number.isInteger(${g.input})){
56
56
  ${invalidType(g, "int", {
57
57
  extra: "format:\"safeint\"",
58
+ extraBeforeCode: true,
58
59
  message
59
60
  })}
60
61
  }else if(${g.input}<-9007199254740991){
61
- ${g.issues}.push({code:"too_small",minimum:-9007199254740991,origin:"int",${note},inclusive:true${msgProp},input:${g.input},path:${g.path}});
62
+ ${g.issues}.push({code:"too_small",minimum:-9007199254740991,${note},origin:"int",inclusive:true,input:${g.input},path:${g.path}${msgProp}});
62
63
  }else if(${g.input}>9007199254740991){
63
- ${g.issues}.push({code:"too_big",maximum:9007199254740991,origin:"int",${note},inclusive:true${msgProp},input:${g.input},path:${g.path}});
64
+ ${g.issues}.push({code:"too_big",maximum:9007199254740991,${note},origin:"int",inclusive:true,input:${g.input},path:${g.path}${msgProp}});
64
65
  }`;
65
66
  } else if (check.format === "int32") code += emit`
66
67
  if(!Number.isInteger(${g.input})){
67
68
  ${invalidType(g, "int", {
68
69
  extra: "format:\"int32\"",
70
+ extraBeforeCode: true,
69
71
  message
70
72
  })}
71
73
  }else if(${g.input}<-2147483648){
@@ -77,6 +79,7 @@ function slowNumber(ir, g) {
77
79
  if(!Number.isInteger(${g.input})){
78
80
  ${invalidType(g, "int", {
79
81
  extra: "format:\"uint32\"",
82
+ extraBeforeCode: true,
80
83
  message
81
84
  })}
82
85
  }else if(${g.input}<0){
@@ -97,7 +100,7 @@ function slowNumber(ir, g) {
97
100
  const fsr = emitRuntimeHelper(g.ctx, "__zcFsr", ZC_FSR_DECL);
98
101
  code += emit`
99
102
  if(${fsr}(${g.input},${check.value})!==0){
100
- ${g.issues}.push({code:"not_multiple_of",divisor:${check.value},origin:"number"${msgProp},input:${g.input},path:${g.path}});
103
+ ${g.issues}.push({origin:"number",code:"not_multiple_of",divisor:${check.value},input:${g.input},path:${g.path}${msgProp}});
101
104
  }`;
102
105
  break;
103
106
  }
@@ -1 +1 @@
1
- {"version":3,"file":"number.js","names":[],"sources":["../../../../src/core/codegen/schemas/number.ts"],"sourcesContent":["import type { CheckIR, NumberIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport { checkPriority, emitEffectCallable, emitRuntimeHelper } from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, tooBig, tooSmall } from \"../emit-issue.js\";\nimport { ZC_FSR_DECL } from \"../issue-decls.js\";\nimport { refineCheck, superRefineCheck, superRefineFastTest } from \"./effect.js\";\n\nexport function slowNumber(ir: NumberIR, g: SlowGen): string {\n let code = \"\";\n if (ir.coerce) {\n code += emit`try{${g.output}=Number(${g.input});}catch(_){}`;\n }\n code += emit`\n if(typeof ${g.input}!==\"number\"){\n ${invalidType(g, \"number\")}\n }else if(Number.isNaN(${g.input})){\n ${invalidType(g, \"number\", { extra: 'received:\"NaN\"' })}\n }else if(!Number.isFinite(${g.input})){\n ${invalidType(g, \"number\", { extra: 'received:\"Infinity\"' })}\n }`;\n\n if (ir.checks.length > 0) {\n code += `else{`;\n // An integer FORMAT check (`.int()`, `.int32()`, `.uint32()`) is the one\n // check here that can report `invalid_type` — non-continuable — and zod's\n // `runChecks` then skips every check after it (`else if (isAborted)\n // continue`). So `z.number().int().min(5)` on 1.5 reports the int failure\n // ALONE, where an ungated chain also volunteers too_small. Its aborting\n // branch is exactly `!Number.isInteger(value)` (the range branches report\n // continuable too_small/too_big, which do NOT stop later checks), so one\n // guard opened after the first such check reproduces zod without any\n // runtime abort bookkeeping. Non-integer formats (float32/float64) never\n // report invalid_type and so never open it.\n const opensIntGuard = (check: (typeof ir.checks)[number]): boolean =>\n check.kind === \"number_format\" &&\n (check.format === \"safeint\" || check.format === \"int32\" || check.format === \"uint32\");\n let seenIntFormat = false;\n let intGuardOpen = false;\n // Insertion order mirrors zod's issue order for multi-failure inputs.\n // The slow path collects ALL issues (no short-circuit), so cost ordering\n // buys nothing here — only the fast path's && chain benefits from it.\n for (const check of ir.checks) {\n // Opened lazily, on the first check that actually needs gating: an integer\n // format check in LAST position has nothing after it, so the guard would\n // be dead code.\n if (seenIntFormat && !intGuardOpen) {\n code += `if(Number.isInteger(${g.input})){`;\n intGuardOpen = true;\n }\n switch (check.kind) {\n case \"greater_than\":\n if (check.inclusive) {\n code += emit`\n if(${g.input}<${check.value}){\n ${tooSmall(g, check.value, \"number\", true, { message: check.message })}\n }`;\n } else {\n code += emit`\n if(${g.input}<=${check.value}){\n ${tooSmall(g, check.value, \"number\", false, { message: check.message })}\n }`;\n }\n break;\n case \"less_than\":\n if (check.inclusive) {\n code += emit`\n if(${g.input}>${check.value}){\n ${tooBig(g, check.value, \"number\", true, { message: check.message })}\n }`;\n } else {\n code += emit`\n if(${g.input}>=${check.value}){\n ${tooBig(g, check.value, \"number\", false, { message: check.message })}\n }`;\n }\n break;\n case \"number_format\": {\n const message = check.message;\n if (check.format === \"safeint\") {\n // Mirrors $ZodCheckNumberFormat: non-integers → invalid_type;\n // integers outside the safe range → too_small/too_big with\n // origin \"int\" and zod's explanatory note.\n const note = `note:\"Integers must be within the safe integer range.\"`;\n const msgProp = message !== undefined ? `,message:${JSON.stringify(message)}` : \"\";\n code += emit`\n if(!Number.isInteger(${g.input})){\n ${invalidType(g, \"int\", { extra: 'format:\"safeint\"', message })}\n }else if(${g.input}<-9007199254740991){\n ${g.issues}.push({code:\"too_small\",minimum:-9007199254740991,origin:\"int\",${note},inclusive:true${msgProp},input:${g.input},path:${g.path}});\n }else if(${g.input}>9007199254740991){\n ${g.issues}.push({code:\"too_big\",maximum:9007199254740991,origin:\"int\",${note},inclusive:true${msgProp},input:${g.input},path:${g.path}});\n }`;\n } else if (check.format === \"int32\") {\n code += emit`\n if(!Number.isInteger(${g.input})){\n ${invalidType(g, \"int\", { extra: 'format:\"int32\"', message })}\n }else if(${g.input}<-2147483648){\n ${tooSmall(g, -2147483648, \"number\", true, { message })}\n }else if(${g.input}>2147483647){\n ${tooBig(g, 2147483647, \"number\", true, { message })}\n }`;\n } else if (check.format === \"uint32\") {\n code += emit`\n if(!Number.isInteger(${g.input})){\n ${invalidType(g, \"int\", { extra: 'format:\"uint32\"', message })}\n }else if(${g.input}<0){\n ${tooSmall(g, 0, \"number\", true, { message })}\n }else if(${g.input}>4294967295){\n ${tooBig(g, 4294967295, \"number\", true, { message })}\n }`;\n } else if (check.format === \"float32\") {\n code += emit`\n if(${g.input}<-3.4028234663852886e+38){\n ${tooSmall(g, \"-3.4028234663852886e+38\", \"number\", true, { message })}\n }else if(${g.input}>3.4028234663852886e+38){\n ${tooBig(g, \"3.4028234663852886e+38\", \"number\", true, { message })}\n }`;\n }\n // float64 range is [-Number.MAX_VALUE, Number.MAX_VALUE], already covered by the isFinite check above\n break;\n }\n case \"multiple_of\": {\n const msgProp =\n check.message !== undefined ? `,message:${JSON.stringify(check.message)}` : \"\";\n // zod uses a float-safe remainder: raw % mis-rejects 0.3 % 0.1\n const fsr = emitRuntimeHelper(g.ctx, \"__zcFsr\", ZC_FSR_DECL);\n code += emit`\n if(${fsr}(${g.input},${check.value})!==0){\n ${g.issues}.push({code:\"not_multiple_of\",divisor:${check.value},origin:\"number\"${msgProp},input:${g.input},path:${g.path}});\n }`;\n break;\n }\n case \"refine_effect\":\n code += refineCheck(check, g.input, g);\n break;\n case \"super_refine_effect\":\n code += superRefineCheck(check, g.input, g);\n break;\n }\n if (opensIntGuard(check)) seenIntFormat = true;\n }\n if (intGuardOpen) code += `}`;\n code += `}`;\n }\n\n return `${code}\\n`;\n}\n\nexport function fastNumber(ir: NumberIR, g: FastGen): string | null {\n if (ir.coerce) return null;\n\n const x = g.input;\n const checks = ir.checks.filter((c): c is CheckIR => c.kind !== \"refine_effect\");\n\n // Number.isFinite(x) alone implies typeof number && !NaN && finite — zod's\n // entire number type gate in one non-coercing intrinsic. Likewise\n // Number.isSafeInteger covers the safeint format gate. Only the bitwise int\n // formats ((x|0)===x, (x>>>0)===x) still need the typeof guard: applying\n // ToNumber to an arbitrary input would invoke valueOf side effects zod\n // never triggers. Math.fround(Infinity)===Infinity, so float32 keeps the\n // isFinite gate.\n const hasSafeInt = checks.some((c) => c.kind === \"number_format\" && c.format === \"safeint\");\n const hasBitwiseInt = checks.some(\n (c) => c.kind === \"number_format\" && (c.format === \"int32\" || c.format === \"uint32\"),\n );\n const parts: string[] = [];\n if (hasBitwiseInt) {\n parts.push(`typeof ${x}===\"number\"`);\n } else if (!hasSafeInt) {\n parts.push(`Number.isFinite(${x})`);\n }\n\n for (const check of checks.sort(checkPriority)) {\n switch (check.kind) {\n case \"number_format\":\n switch (check.format) {\n case \"safeint\":\n parts.push(`Number.isSafeInteger(${x})`);\n break;\n case \"int32\":\n parts.push(`(${x}|0)===${x}`);\n break;\n case \"uint32\":\n parts.push(`${x}>=0`, `${x}<=4294967295`, `(${x}>>>0)===${x}`);\n break;\n case \"float32\":\n // zod's float32 check is a pure RANGE check (3.14 is accepted\n // even though Math.fround(3.14) !== 3.14) — fround here would\n // make the fast path stricter than the slow path / zod.\n parts.push(`${x}>=-3.4028234663852886e+38`, `${x}<=3.4028234663852886e+38`);\n break;\n case \"float64\":\n // All finite numbers are valid float64\n break;\n }\n break;\n case \"greater_than\":\n parts.push(check.inclusive ? `${x}>=${check.value}` : `${x}>${check.value}`);\n break;\n case \"less_than\":\n parts.push(check.inclusive ? `${x}<=${check.value}` : `${x}<${check.value}`);\n break;\n case \"multiple_of\": {\n const fsr = emitRuntimeHelper(g.ctx, \"__zcFsr\", ZC_FSR_DECL);\n parts.push(`${fsr}(${x},${check.value})===0`);\n break;\n }\n case \"min_length\":\n case \"max_length\":\n case \"length_equals\":\n case \"string_format\":\n case \"includes\":\n case \"starts_with\":\n case \"ends_with\":\n // String-only checks on a number schema — shouldn't happen, skip\n break;\n }\n }\n\n // Refine effect checks (appended last — run after cheap checks short-circuit)\n for (const check of ir.checks) {\n if (check.kind === \"refine_effect\") {\n parts.push(`${emitEffectCallable(g.ctx, check)}(${x})`);\n } else if (check.kind === \"super_refine_effect\") {\n parts.push(superRefineFastTest(check, x, g));\n }\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"number.js","names":[],"sources":["../../../../src/core/codegen/schemas/number.ts"],"sourcesContent":["import type { CheckIR, NumberIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport { checkPriority, emitEffectCallable, emitRuntimeHelper } from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, tooBig, tooSmall } from \"../emit-issue.js\";\nimport { ZC_FSR_DECL } from \"../issue-decls.js\";\nimport { refineCheck, superRefineCheck, superRefineFastTest } from \"./effect.js\";\n\nexport function slowNumber(ir: NumberIR, g: SlowGen): string {\n let code = \"\";\n if (ir.coerce) {\n code += emit`try{${g.output}=Number(${g.input});}catch(_){}`;\n }\n code += emit`\n if(typeof ${g.input}!==\"number\"){\n ${invalidType(g, \"number\")}\n }else if(Number.isNaN(${g.input})){\n ${invalidType(g, \"number\", { extra: 'received:\"NaN\"' })}\n }else if(!Number.isFinite(${g.input})){\n ${invalidType(g, \"number\", { extra: 'received:\"Infinity\"' })}\n }`;\n\n if (ir.checks.length > 0) {\n code += `else{`;\n // An integer FORMAT check (`.int()`, `.int32()`, `.uint32()`) is the one\n // check here that can report `invalid_type` — non-continuable — and zod's\n // `runChecks` then skips every check after it (`else if (isAborted)\n // continue`). So `z.number().int().min(5)` on 1.5 reports the int failure\n // ALONE, where an ungated chain also volunteers too_small. Its aborting\n // branch is exactly `!Number.isInteger(value)` (the range branches report\n // continuable too_small/too_big, which do NOT stop later checks), so one\n // guard opened after the first such check reproduces zod without any\n // runtime abort bookkeeping. Non-integer formats (float32/float64) never\n // report invalid_type and so never open it.\n const opensIntGuard = (check: (typeof ir.checks)[number]): boolean =>\n check.kind === \"number_format\" &&\n (check.format === \"safeint\" || check.format === \"int32\" || check.format === \"uint32\");\n let seenIntFormat = false;\n let intGuardOpen = false;\n // Insertion order mirrors zod's issue order for multi-failure inputs.\n // The slow path collects ALL issues (no short-circuit), so cost ordering\n // buys nothing here — only the fast path's && chain benefits from it.\n for (const check of ir.checks) {\n // Opened lazily, on the first check that actually needs gating: an integer\n // format check in LAST position has nothing after it, so the guard would\n // be dead code.\n if (seenIntFormat && !intGuardOpen) {\n code += `if(Number.isInteger(${g.input})){`;\n intGuardOpen = true;\n }\n switch (check.kind) {\n case \"greater_than\":\n if (check.inclusive) {\n code += emit`\n if(${g.input}<${check.value}){\n ${tooSmall(g, check.value, \"number\", true, { message: check.message })}\n }`;\n } else {\n code += emit`\n if(${g.input}<=${check.value}){\n ${tooSmall(g, check.value, \"number\", false, { message: check.message })}\n }`;\n }\n break;\n case \"less_than\":\n if (check.inclusive) {\n code += emit`\n if(${g.input}>${check.value}){\n ${tooBig(g, check.value, \"number\", true, { message: check.message })}\n }`;\n } else {\n code += emit`\n if(${g.input}>=${check.value}){\n ${tooBig(g, check.value, \"number\", false, { message: check.message })}\n }`;\n }\n break;\n case \"number_format\": {\n const message = check.message;\n if (check.format === \"safeint\") {\n // Mirrors $ZodCheckNumberFormat: non-integers → invalid_type;\n // integers outside the safe range → too_small/too_big with\n // origin \"int\" and zod's explanatory note.\n const note = `note:\"Integers must be within the safe integer range.\"`;\n const msgProp = message !== undefined ? `,message:${JSON.stringify(message)}` : \"\";\n code += emit`\n if(!Number.isInteger(${g.input})){\n ${invalidType(g, \"int\", { extra: 'format:\"safeint\"', extraBeforeCode: true, message })}\n }else if(${g.input}<-9007199254740991){\n ${g.issues}.push({code:\"too_small\",minimum:-9007199254740991,${note},origin:\"int\",inclusive:true,input:${g.input},path:${g.path}${msgProp}});\n }else if(${g.input}>9007199254740991){\n ${g.issues}.push({code:\"too_big\",maximum:9007199254740991,${note},origin:\"int\",inclusive:true,input:${g.input},path:${g.path}${msgProp}});\n }`;\n } else if (check.format === \"int32\") {\n code += emit`\n if(!Number.isInteger(${g.input})){\n ${invalidType(g, \"int\", { extra: 'format:\"int32\"', extraBeforeCode: true, message })}\n }else if(${g.input}<-2147483648){\n ${tooSmall(g, -2147483648, \"number\", true, { message })}\n }else if(${g.input}>2147483647){\n ${tooBig(g, 2147483647, \"number\", true, { message })}\n }`;\n } else if (check.format === \"uint32\") {\n code += emit`\n if(!Number.isInteger(${g.input})){\n ${invalidType(g, \"int\", { extra: 'format:\"uint32\"', extraBeforeCode: true, message })}\n }else if(${g.input}<0){\n ${tooSmall(g, 0, \"number\", true, { message })}\n }else if(${g.input}>4294967295){\n ${tooBig(g, 4294967295, \"number\", true, { message })}\n }`;\n } else if (check.format === \"float32\") {\n code += emit`\n if(${g.input}<-3.4028234663852886e+38){\n ${tooSmall(g, \"-3.4028234663852886e+38\", \"number\", true, { message })}\n }else if(${g.input}>3.4028234663852886e+38){\n ${tooBig(g, \"3.4028234663852886e+38\", \"number\", true, { message })}\n }`;\n }\n // float64 range is [-Number.MAX_VALUE, Number.MAX_VALUE], already covered by the isFinite check above\n break;\n }\n case \"multiple_of\": {\n const msgProp =\n check.message !== undefined ? `,message:${JSON.stringify(check.message)}` : \"\";\n // zod uses a float-safe remainder: raw % mis-rejects 0.3 % 0.1\n const fsr = emitRuntimeHelper(g.ctx, \"__zcFsr\", ZC_FSR_DECL);\n code += emit`\n if(${fsr}(${g.input},${check.value})!==0){\n ${g.issues}.push({origin:\"number\",code:\"not_multiple_of\",divisor:${check.value},input:${g.input},path:${g.path}${msgProp}});\n }`;\n break;\n }\n case \"refine_effect\":\n code += refineCheck(check, g.input, g);\n break;\n case \"super_refine_effect\":\n code += superRefineCheck(check, g.input, g);\n break;\n }\n if (opensIntGuard(check)) seenIntFormat = true;\n }\n if (intGuardOpen) code += `}`;\n code += `}`;\n }\n\n return `${code}\\n`;\n}\n\nexport function fastNumber(ir: NumberIR, g: FastGen): string | null {\n if (ir.coerce) return null;\n\n const x = g.input;\n const checks = ir.checks.filter((c): c is CheckIR => c.kind !== \"refine_effect\");\n\n // Number.isFinite(x) alone implies typeof number && !NaN && finite — zod's\n // entire number type gate in one non-coercing intrinsic. Likewise\n // Number.isSafeInteger covers the safeint format gate. Only the bitwise int\n // formats ((x|0)===x, (x>>>0)===x) still need the typeof guard: applying\n // ToNumber to an arbitrary input would invoke valueOf side effects zod\n // never triggers. Math.fround(Infinity)===Infinity, so float32 keeps the\n // isFinite gate.\n const hasSafeInt = checks.some((c) => c.kind === \"number_format\" && c.format === \"safeint\");\n const hasBitwiseInt = checks.some(\n (c) => c.kind === \"number_format\" && (c.format === \"int32\" || c.format === \"uint32\"),\n );\n const parts: string[] = [];\n if (hasBitwiseInt) {\n parts.push(`typeof ${x}===\"number\"`);\n } else if (!hasSafeInt) {\n parts.push(`Number.isFinite(${x})`);\n }\n\n for (const check of checks.sort(checkPriority)) {\n switch (check.kind) {\n case \"number_format\":\n switch (check.format) {\n case \"safeint\":\n parts.push(`Number.isSafeInteger(${x})`);\n break;\n case \"int32\":\n parts.push(`(${x}|0)===${x}`);\n break;\n case \"uint32\":\n parts.push(`${x}>=0`, `${x}<=4294967295`, `(${x}>>>0)===${x}`);\n break;\n case \"float32\":\n // zod's float32 check is a pure RANGE check (3.14 is accepted\n // even though Math.fround(3.14) !== 3.14) — fround here would\n // make the fast path stricter than the slow path / zod.\n parts.push(`${x}>=-3.4028234663852886e+38`, `${x}<=3.4028234663852886e+38`);\n break;\n case \"float64\":\n // All finite numbers are valid float64\n break;\n }\n break;\n case \"greater_than\":\n parts.push(check.inclusive ? `${x}>=${check.value}` : `${x}>${check.value}`);\n break;\n case \"less_than\":\n parts.push(check.inclusive ? `${x}<=${check.value}` : `${x}<${check.value}`);\n break;\n case \"multiple_of\": {\n const fsr = emitRuntimeHelper(g.ctx, \"__zcFsr\", ZC_FSR_DECL);\n parts.push(`${fsr}(${x},${check.value})===0`);\n break;\n }\n case \"min_length\":\n case \"max_length\":\n case \"length_equals\":\n case \"string_format\":\n case \"includes\":\n case \"starts_with\":\n case \"ends_with\":\n // String-only checks on a number schema — shouldn't happen, skip\n break;\n }\n }\n\n // Refine effect checks (appended last — run after cheap checks short-circuit)\n for (const check of ir.checks) {\n if (check.kind === \"refine_effect\") {\n parts.push(`${emitEffectCallable(g.ctx, check)}(${x})`);\n } else if (check.kind === \"super_refine_effect\") {\n parts.push(superRefineFastTest(check, x, g));\n }\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":";;;iBAOgB,WAAW,IAAI;EAAa;GAAkB,GAAG;iBA6CjD,WAAW,IAAI,UAAU,GAAG"}
1
+ {"version":3,"file":"record.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/record.ts"],"mappings":";;;iBA0BgB,WAAW,IAAI;EAAa;GAAkB,GAAG;iBA8CjD,WAAW,IAAI,UAAU,GAAG"}
@@ -1,11 +1,28 @@
1
1
  import { declareFastTemps, emitRuntimeHelper, extendPath, hasMutation } from "../context.js";
2
2
  import { emit } from "../emit.js";
3
3
  import { invalidType } from "../emit-issue.js";
4
- import { ZC_FZ_DECL, ZC_HOP_DECL } from "../issue-decls.js";
4
+ import { ZC_FZ_DECL, ZC_HOP_DECL, ZC_PLAIN_DECL } from "../issue-decls.js";
5
5
  //#region src/core/codegen/schemas/record.ts
6
+ /**
7
+ * `$ZodRecord` gates on `util.isPlainObject`, NOT the looser `util.isObject`
8
+ * that `$ZodObject` uses — see {@link ZC_PLAIN_DECL} for why the two differ and
9
+ * what accepting the wrong one lets through.
10
+ */
11
+ function plainObjectTest(g, input) {
12
+ return `${emitRuntimeHelper(g.ctx, "__zcPlain", ZC_PLAIN_DECL)}(${input})`;
13
+ }
14
+ /**
15
+ * Zod's record walk opens with `if (key === "__proto__") continue`, so an own
16
+ * `__proto__` data property (which `JSON.parse('{"__proto__":…}')` creates) is
17
+ * neither key-validated nor value-validated nor copied. Without this the
18
+ * compiled walk reported issues zod does not: `z.record(z.string(), z.number())`
19
+ * over such an object raised a SECOND `invalid_type`, and a constrained key
20
+ * schema a second `invalid_key`, for a key zod never looks at.
21
+ */
22
+ const PROTO_SKIP = (keyVar) => `if(${keyVar}==="__proto__")continue;`;
6
23
  function slowRecord(ir, g) {
7
24
  let code = emit`
8
- if(typeof ${g.input}!=="object"||${g.input}===null||Array.isArray(${g.input})){
25
+ if(!${plainObjectTest(g, g.input)}){
9
26
  ${invalidType(g, "record")}
10
27
  }else{`;
11
28
  if (hasMutation(ir.valueType)) code += `${g.output}={...${g.input}};`;
@@ -18,6 +35,7 @@ function slowRecord(ir, g) {
18
35
  code += emit`
19
36
  for(var ${keyVar} in ${g.input}){
20
37
  if(!${hop}.call(${g.input},${keyVar}))continue;
38
+ ${PROTO_SKIP(keyVar)}
21
39
  var ${keyIssuesVar}=[];
22
40
  ${g.visit(ir.keyType, {
23
41
  input: keyVar,
@@ -26,7 +44,7 @@ function slowRecord(ir, g) {
26
44
  issues: keyIssuesVar
27
45
  })}
28
46
  if(${keyIssuesVar}.length>0){
29
- ${g.issues}.push({code:"invalid_key",origin:"record"${g.typeMsg === void 0 ? "" : `,message:${JSON.stringify(g.typeMsg)}`},input:${keyVar},path:${keyPath},issues:${fz}(${keyIssuesVar})});
47
+ ${g.issues}.push({code:"invalid_key",origin:"record",issues:${fz}(${keyIssuesVar}),input:${keyVar},path:${keyPath}${g.typeMsg === void 0 ? "" : `,message:${JSON.stringify(g.typeMsg)}`}});
30
48
  }else{
31
49
  ${g.visit(ir.valueType, {
32
50
  input: valExpr,
@@ -40,11 +58,7 @@ function slowRecord(ir, g) {
40
58
  }
41
59
  function fastRecord(ir, g) {
42
60
  const x = g.input;
43
- const parts = [
44
- `typeof ${x}==="object"`,
45
- `${x}!==null`,
46
- `!Array.isArray(${x})`
47
- ];
61
+ const parts = [plainObjectTest(g, x)];
48
62
  const plainStringKey = ir.keyType.type === "string" && ir.keyType.checks.length === 0 && ir.keyType.coerce !== true;
49
63
  const kv = g.temp("rk");
50
64
  const vv = g.temp("rv");
@@ -59,7 +73,7 @@ function fastRecord(ir, g) {
59
73
  const helperName = g.temp("rf");
60
74
  const valAssign = valCheck !== "true" ? `${vv}=o[${kv}];` : "";
61
75
  const hop = emitRuntimeHelper(g.ctx, "__zcHop", ZC_HOP_DECL);
62
- g.ctx.preamble.push(`function ${helperName}(o){${declareFastTemps(body.scope)}var ${kv},${vv};for(${kv} in o){if(${hop}.call(o,${kv})){${valAssign}if(!(${conditions.join("&&")})){return false;}}}return true;}`);
76
+ g.ctx.preamble.push(`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;}`);
63
77
  parts.push(`${helperName}(${x})`);
64
78
  }
65
79
  return parts.join("&&");
@@ -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 } from \"../emit-issue.js\";\nimport { ZC_FZ_DECL, ZC_HOP_DECL } from \"../issue-decls.js\";\n\nexport function slowRecord(ir: SchemaIR & { type: \"record\" }, g: SlowGen): string {\n let code = emit`\n if(typeof ${g.input}!==\"object\"||${g.input}===null||Array.isArray(${g.input})){\n ${invalidType(g, \"record\")}\n }else{`;\n\n if (hasMutation(ir.valueType)) {\n code += `${g.output}={...${g.input}};`;\n }\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 code += emit`\n for(var ${keyVar} in ${g.input}){\n if(!${hop}.call(${g.input},${keyVar}))continue;\n var ${keyIssuesVar}=[];\n ${g.visit(ir.keyType, { input: keyVar, output: keyVar, path: \"[]\", issues: keyIssuesVar })}\n if(${keyIssuesVar}.length>0){\n ${g.issues}.push({code:\"invalid_key\",origin:\"record\"${g.typeMsg === undefined ? \"\" : `,message:${JSON.stringify(g.typeMsg)}`},input:${keyVar},path:${keyPath},issues:${fz}(${keyIssuesVar})});\n }else{\n ${g.visit(ir.valueType, { input: valExpr, output: valExpr, path: keyPath })}\n }\n }\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[] = [`typeof ${x}===\"object\"`, `${x}!==null`, `!Array.isArray(${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){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":";;;;;AAOA,SAAgB,WAAW,IAAmC,GAAoB;CAChF,IAAI,OAAO,IAAI;gBACD,EAAE,MAAM,eAAe,EAAE,MAAM,yBAAyB,EAAE,MAAM;QACxE,YAAY,GAAG,QAAQ,EAAE;;CAG/B,IAAI,YAAY,GAAG,SAAS,GAC1B,QAAQ,GAAG,EAAE,OAAO,OAAO,EAAE,MAAM;CAGrC,MAAM,SAAS,EAAE,KAAK,MAAM;CAC5B,MAAM,eAAe,EAAE,KAAK,KAAK;CACjC,MAAM,UAAU,WAAW,EAAE,MAAM,MAAM;CACzC,MAAM,UAAU,GAAG,EAAE,MAAM,GAAG,OAAO;CAQrC,MAAM,MAAM,kBAAkB,EAAE,KAAK,WAAW,WAAW;CAO3D,MAAM,KAAK,kBAAkB,EAAE,KAAK,UAAU,UAAU;CAExD,QAAQ,IAAI;cACA,OAAO,MAAM,EAAE,MAAM;YACvB,IAAI,QAAQ,EAAE,MAAM,GAAG,OAAO;YAC9B,aAAa;QACjB,EAAE,MAAM,GAAG,SAAS;EAAE,OAAO;EAAQ,QAAQ;EAAQ,MAAM;EAAM,QAAQ;CAAa,CAAC,EAAE;WACtF,aAAa;UACd,EAAE,OAAO,2CAA2C,EAAE,YAAY,KAAA,IAAY,KAAK,YAAY,KAAK,UAAU,EAAE,OAAO,IAAI,SAAS,OAAO,QAAQ,QAAQ,UAAU,GAAG,GAAG,aAAa;;UAExL,EAAE,MAAM,GAAG,WAAW;EAAE,OAAO;EAAS,QAAQ;EAAS,MAAM;CAAQ,CAAC,EAAE;;;;CAIlF,OAAO,GAAG,KAAK;AACjB;AAEA,SAAgB,WAAW,IAAc,GAA2B;CAClE,MAAM,IAAI,EAAE;CACZ,MAAM,QAAkB;EAAC,UAAU,EAAE;EAAc,GAAG,EAAE;EAAU,kBAAkB,EAAE;CAAE;CAIxF,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,YAAY,IAAI,UAAU,GAAG,KAAK,UAAU,OAAO,WAAW,KAAK,IAAI,EAAE,iCAC9J;EACA,MAAM,KAAK,GAAG,WAAW,GAAG,EAAE,EAAE;CAClC;CAEA,OAAO,MAAM,KAAK,IAAI;AACxB"}
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 } from \"../emit-issue.js\";\nimport { ZC_FZ_DECL, ZC_HOP_DECL, ZC_PLAIN_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\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 code += emit`\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 ${g.issues}.push({code:\"invalid_key\",origin:\"record\",issues:${fz}(${keyIssuesVar}),input:${keyVar},path:${keyPath}${g.typeMsg === undefined ? \"\" : `,message:${JSON.stringify(g.typeMsg)}`}});\n }else{\n ${g.visit(ir.valueType, { input: valExpr, output: valExpr, path: keyPath })}\n }\n }\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":";;;;;;;;;;AAYA,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;;CAG/B,IAAI,YAAY,GAAG,SAAS,GAC1B,QAAQ,GAAG,EAAE,OAAO,OAAO,EAAE,MAAM;CAGrC,MAAM,SAAS,EAAE,KAAK,MAAM;CAC5B,MAAM,eAAe,EAAE,KAAK,KAAK;CACjC,MAAM,UAAU,WAAW,EAAE,MAAM,MAAM;CACzC,MAAM,UAAU,GAAG,EAAE,MAAM,GAAG,OAAO;CAQrC,MAAM,MAAM,kBAAkB,EAAE,KAAK,WAAW,WAAW;CAO3D,MAAM,KAAK,kBAAkB,EAAE,KAAK,UAAU,UAAU;CAExD,QAAQ,IAAI;cACA,OAAO,MAAM,EAAE,MAAM;YACvB,IAAI,QAAQ,EAAE,MAAM,GAAG,OAAO;QAClC,WAAW,MAAM,EAAE;YACf,aAAa;QACjB,EAAE,MAAM,GAAG,SAAS;EAAE,OAAO;EAAQ,QAAQ;EAAQ,MAAM;EAAM,QAAQ;CAAa,CAAC,EAAE;WACtF,aAAa;UACd,EAAE,OAAO,mDAAmD,GAAG,GAAG,aAAa,UAAU,OAAO,QAAQ,UAAU,EAAE,YAAY,KAAA,IAAY,KAAK,YAAY,KAAK,UAAU,EAAE,OAAO,IAAI;;UAEzL,EAAE,MAAM,GAAG,WAAW;EAAE,OAAO;EAAS,QAAQ;EAAS,MAAM;CAAQ,CAAC,EAAE;;;;CAIlF,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 +1 @@
1
- {"version":3,"file":"set.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/set.ts"],"mappings":";;;iBAMgB,QAAQ,IAAI;EAAa;GAAe,GAAG;iBA8D3C,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;iBA+D3C,QAAQ,IAAI,OAAO,GAAG"}
@@ -1,11 +1,13 @@
1
1
  import { checkPriority, declareFastTemps, hasMutation } from "../context.js";
2
2
  import { emit } from "../emit.js";
3
3
  import { invalidType, tooBig, tooSmall } from "../emit-issue.js";
4
+ import { whenGatedSizeChecks } from "./sizeable.js";
4
5
  //#region src/core/codegen/schemas/set.ts
5
6
  function slowSet(ir, g) {
6
7
  let code = emit`
7
8
  if(!(${g.input} instanceof Set)){
8
9
  ${invalidType(g, "set")}
10
+ ${whenGatedSizeChecks(ir.checks ?? [], g, "size")}
9
11
  }else{`;
10
12
  const mutates = hasMutation(ir.valueType);
11
13
  const iterVar = g.temp("set_v");
@@ -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\";\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 }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":";;;;AAMA,SAAgB,QAAQ,IAAgC,GAAoB;CAC1E,IAAI,OAAO,IAAI;WACN,EAAE,MAAM;QACX,YAAY,GAAG,KAAK,EAAE;;CAQ5B,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 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"}
@@ -0,0 +1,37 @@
1
+ import { CheckOrEffectIR, FileCheckIR, SetCheckIR } from "../../types.js";
2
+ import { SlowGen } from "../context.js";
3
+ //#region src/core/codegen/schemas/sizeable.d.ts
4
+ /**
5
+ * Length/size checks re-emitted for the branch where the node's TYPE CHECK
6
+ * ALREADY FAILED.
7
+ *
8
+ * zod skips a schema's checks once its parse aborted — except the ones carrying
9
+ * a `when` predicate, which `runChecks` consults instead of the abort flag:
10
+ *
11
+ * ```js
12
+ * if (ch._zod.def.when) { if (!ch._zod.def.when(payload)) continue; }
13
+ * else if (isAborted) continue;
14
+ * ```
15
+ *
16
+ * and `$ZodCheckMinLength` & co. install exactly such a predicate —
17
+ * `!nullish(value) && value.length !== undefined` (`.size` for the size family)
18
+ * — so they run on ANY input carrying that property, of any type. Hence
19
+ * `z.string().min(2).safeParse([])` reports TWO issues in zod: the
20
+ * `invalid_type`, and a `too_small` whose `origin` is `"array"`, because the
21
+ * empty array satisfied the `when`. Compiled output kept every check inside the
22
+ * matched-type branch and reported only the first. Same for
23
+ * `z.array(…).min(3)` over a short string, `z.set(…).min(2)` over a `Map`, and
24
+ * `z.file().min(2)` over a `Set`.
25
+ *
26
+ * Emitted as a SEPARATE copy in the failure branch rather than hoisted out of
27
+ * both, so the success path — where the origin is statically known and the guard
28
+ * is trivially true — stays byte-for-byte what it was. This is cold code: it
29
+ * runs only for input the node has already rejected.
30
+ *
31
+ * `origin` is computed at runtime here (see ZC_LENGTH_ORIGIN_DECL), because the
32
+ * value that reached this branch is by definition not the schema's own type.
33
+ */
34
+ declare function whenGatedSizeChecks(checks: readonly (CheckOrEffectIR | SetCheckIR | FileCheckIR)[], g: SlowGen, family: "length" | "size"): string;
35
+ //#endregion
36
+ export { whenGatedSizeChecks };
37
+ //# sourceMappingURL=sizeable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sizeable.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/sizeable.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqCgB,oBACd,kBAAkB,kBAAkB,aAAa,gBACjD,GAAG,SACH"}