zod-compiler 1.28.0 → 2.0.1
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.
- package/README.md +176 -148
- package/dist/core/codegen/build-path.d.ts +12 -1
- package/dist/core/codegen/build-path.d.ts.map +1 -1
- package/dist/core/codegen/build-path.js +102 -21
- package/dist/core/codegen/build-path.js.map +1 -1
- package/dist/core/codegen/context.d.ts +31 -16
- package/dist/core/codegen/context.d.ts.map +1 -1
- package/dist/core/codegen/context.js +43 -18
- package/dist/core/codegen/context.js.map +1 -1
- package/dist/core/codegen/emit-issue.d.ts +17 -20
- package/dist/core/codegen/emit-issue.d.ts.map +1 -1
- package/dist/core/codegen/emit-issue.js +13 -13
- package/dist/core/codegen/emit-issue.js.map +1 -1
- package/dist/core/codegen/index.d.ts.map +1 -1
- package/dist/core/codegen/index.js +9 -7
- package/dist/core/codegen/index.js.map +1 -1
- package/dist/core/codegen/issue-decls.d.ts +83 -7
- package/dist/core/codegen/issue-decls.d.ts.map +1 -1
- package/dist/core/codegen/issue-decls.js +93 -10
- package/dist/core/codegen/issue-decls.js.map +1 -1
- package/dist/core/codegen/schemas/array.js +1 -1
- package/dist/core/codegen/schemas/array.js.map +1 -1
- package/dist/core/codegen/schemas/bigint.d.ts.map +1 -1
- package/dist/core/codegen/schemas/bigint.js +15 -4
- package/dist/core/codegen/schemas/bigint.js.map +1 -1
- package/dist/core/codegen/schemas/catch.d.ts.map +1 -1
- package/dist/core/codegen/schemas/catch.js +3 -2
- package/dist/core/codegen/schemas/catch.js.map +1 -1
- package/dist/core/codegen/schemas/discriminated-union.d.ts +6 -2
- package/dist/core/codegen/schemas/discriminated-union.d.ts.map +1 -1
- package/dist/core/codegen/schemas/discriminated-union.js +16 -56
- package/dist/core/codegen/schemas/discriminated-union.js.map +1 -1
- package/dist/core/codegen/schemas/effect.d.ts.map +1 -1
- package/dist/core/codegen/schemas/effect.js +4 -2
- package/dist/core/codegen/schemas/effect.js.map +1 -1
- package/dist/core/codegen/schemas/file.js +2 -8
- package/dist/core/codegen/schemas/file.js.map +1 -1
- package/dist/core/codegen/schemas/number.js +4 -3
- package/dist/core/codegen/schemas/number.js.map +1 -1
- package/dist/core/codegen/schemas/object.d.ts +11 -1
- package/dist/core/codegen/schemas/object.d.ts.map +1 -1
- package/dist/core/codegen/schemas/object.js +58 -14
- package/dist/core/codegen/schemas/object.js.map +1 -1
- package/dist/core/codegen/schemas/readonly.d.ts.map +1 -1
- package/dist/core/codegen/schemas/readonly.js.map +1 -1
- package/dist/core/codegen/schemas/record.d.ts.map +1 -1
- package/dist/core/codegen/schemas/record.js +8 -3
- package/dist/core/codegen/schemas/record.js.map +1 -1
- package/dist/core/codegen/schemas/sizeable.d.ts +49 -3
- package/dist/core/codegen/schemas/sizeable.d.ts.map +1 -1
- package/dist/core/codegen/schemas/sizeable.js +101 -8
- package/dist/core/codegen/schemas/sizeable.js.map +1 -1
- package/dist/core/codegen/schemas/string-bool.d.ts +12 -1
- package/dist/core/codegen/schemas/string-bool.d.ts.map +1 -1
- package/dist/core/codegen/schemas/string-bool.js +19 -4
- package/dist/core/codegen/schemas/string-bool.js.map +1 -1
- package/dist/core/codegen/schemas/string.d.ts.map +1 -1
- package/dist/core/codegen/schemas/string.js +60 -26
- package/dist/core/codegen/schemas/string.js.map +1 -1
- package/dist/core/codegen/schemas/tuple.d.ts +14 -11
- package/dist/core/codegen/schemas/tuple.d.ts.map +1 -1
- package/dist/core/codegen/schemas/tuple.js +99 -49
- package/dist/core/codegen/schemas/tuple.js.map +1 -1
- package/dist/core/codegen/well-known-regex.d.ts +26 -1
- package/dist/core/codegen/well-known-regex.d.ts.map +1 -1
- package/dist/core/codegen/well-known-regex.js +31 -4
- package/dist/core/codegen/well-known-regex.js.map +1 -1
- package/dist/core/extract/checks.d.ts.map +1 -1
- package/dist/core/extract/checks.js +1 -0
- package/dist/core/extract/checks.js.map +1 -1
- package/dist/core/extract/extractors/intersection.js +21 -6
- package/dist/core/extract/extractors/intersection.js.map +1 -1
- package/dist/core/extract/extractors/object.d.ts.map +1 -1
- package/dist/core/extract/extractors/object.js +25 -9
- package/dist/core/extract/extractors/object.js.map +1 -1
- package/dist/core/extract/extractors/optional.d.ts.map +1 -1
- package/dist/core/extract/extractors/optional.js +1 -28
- package/dist/core/extract/extractors/optional.js.map +1 -1
- package/dist/core/extract/extractors/record.js +9 -3
- package/dist/core/extract/extractors/record.js.map +1 -1
- package/dist/core/extract/extractors/tuple.d.ts.map +1 -1
- package/dist/core/extract/extractors/tuple.js +16 -20
- package/dist/core/extract/extractors/tuple.js.map +1 -1
- package/dist/core/extract/types.d.ts +16 -8
- package/dist/core/extract/types.d.ts.map +1 -1
- package/dist/core/iife.d.ts +2 -1
- package/dist/core/iife.d.ts.map +1 -1
- package/dist/core/iife.js +2 -1
- package/dist/core/iife.js.map +1 -1
- package/dist/core/types.d.ts +70 -21
- package/dist/core/types.d.ts.map +1 -1
- package/dist/jit.d.ts.map +1 -1
- package/dist/jit.js +47 -12
- package/dist/jit.js.map +1 -1
- package/dist/runtime.d.ts +4 -1
- package/dist/runtime.js +14 -11
- package/package.json +2 -2
|
@@ -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\", { 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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"}
|
|
1
|
+
{"version":3,"file":"discriminated-union.js","names":[],"sources":["../../../../src/core/codegen/schemas/discriminated-union.ts"],"sourcesContent":["import type { DiscriminatedUnionIR, LiteralValue, ObjectIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n escapeString,\n extendPath,\n hasMutation,\n literalToJs,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType } from \"../emit-issue.js\";\n\n/** One `discriminator value → option index` dispatch entry. */\ntype DiscriminatorCase = DiscriminatedUnionIR[\"cases\"][number];\n\nexport function slowDiscriminatedUnion(\n ir: SchemaIR & { type: \"discriminatedUnion\" },\n g: SlowGen,\n): string {\n const discKey = escapeString(ir.discriminator);\n\n let code = emit`\n if(typeof ${g.input}!==\"object\"||${g.input}===null||Array.isArray(${g.input})){\n ${invalidType(g, \"object\", { codeFirst: true })}\n }else{`;\n\n const objVar = g.temp(\"du\");\n code += `var ${objVar}=${g.input};switch(${objVar}[${discKey}]){`;\n\n for (const { value, option: index } of ir.cases) {\n const option = ir.options[index] as SchemaIR;\n code += emit`\n case ${literalToJs(value)}:\n ${g.visit(option, { input: objVar, output: objVar })}\n break;`;\n }\n\n const msgProp = g.typeMsg === undefined ? \"\" : `,message:${JSON.stringify(g.typeMsg)}`;\n // Field for field what $ZodDiscriminatedUnion pushes when no option matches:\n // `{ code, errors: [], note: \"No matching discriminator\", discriminator,\n // options, input, path: [def.discriminator] }`. `options` is\n // `Array.from(disc.value.keys())` — every dispatch value in map insertion\n // order, which is exactly `ir.cases` (option order, then each option's\n // `propValues[discriminator]` in ITS order, so an omittable discriminator\n // lists `undefined` right after its own values). The locale reads the list\n // for \"Invalid discriminator value. Expected 'a' | 'b'\". A fresh array\n // literal per push, as zod allocates one, so consumers never share or\n // mutate a hoisted table.\n const optionsList = ir.cases.map(({ value }) => literalToJs(value)).join(\",\");\n code += emit`\n default:\n ${g.issues}.push({code:\"invalid_union\",errors:[],note:\"No matching discriminator\",discriminator:${discKey},options:[${optionsList}],input:${g.input},path:${extendPath(g.path, discKey)}${msgProp}});\n }`;\n // Propagate option-applied mutations (defaults, coercions, transforms,\n // overwrite checks, stringbool) back to the output location. Each option is\n // visited with output:objVar — a fresh local — so a mutating option's clone is\n // reassigned into objVar and stranded there; without this write-back the caller\n // returns the ORIGINAL input by reference and the mutation is silently lost.\n // Gated on mutation so a pure-validation union stays a zero-write pass-through\n // (objVar still aliases the input). On the no-match/failure paths objVar equals\n // the input, so the write is a harmless self-assignment.\n if (ir.options.some(hasMutation)) {\n code += `${g.output}=${objVar};`;\n }\n code += `}`;\n return `${code}\\n`;\n}\n\n/**\n * Emit an O(1) switch-dispatch fast-check for a discriminated union — real\n * (`z.discriminatedUnion`) or one detected inside a plain `z.union`\n * (see {@link detectUnionDiscriminator}). Both share this so the detected case\n * inherits the size-gating and the per-case guard strip.\n *\n * `discSkipKey` tells each object option to drop its own type-guard and\n * discriminator re-check: the caller's guard (`typeof x===\"object\"&&…` below)\n * already proved object-ness, and the matched switch case has fixed the\n * discriminator value, so re-emitting either is pure redundancy the optimizer\n * only removes when it inlines this helper — which a union large enough to\n * matter won't. Routed through the normal `visit` so size-gated extraction\n * still bounds the switch (the strip survives into any hoisted helper);\n * non-object options ignore the hint and keep their own guard.\n *\n * Returns null if any option is fast-path-ineligible.\n */\nexport function emitFastDiscriminatedSwitch(\n g: FastGen,\n discriminator: string,\n cases: readonly DiscriminatorCase[],\n options: readonly SchemaIR[],\n): string | null {\n const x = g.input;\n const discKey = escapeString(discriminator);\n const helperName = g.temp(\"du\");\n const helperParam = g.temp(\"dx\");\n\n // The switch body is its own function: size-gate the options against the cap\n // in a fresh scope, otherwise many small options accumulate into the caller's\n // scope while this helper itself grows unbounded past the TurboFan budget.\n //\n // The switch is over the literal labels themselves. An earlier revision\n // routed three or more string labels through a `{value: ordinal}` table and\n // switched on the ordinal, on the theory that V8 compiles a string switch as\n // sequential comparisons and a dense integer one as a jump table. Measured on\n // V8 13.x (node 24) that is a loss at every size: the table's `t[v]` is a\n // keyed load whose key varies per parse, which goes megamorphic the moment\n // the input rotates through the options, while the string switch stays\n // nearly flat (8.6 ns at 8 cases, 10.1 at 32, 17.3 at 80, rotating input)\n // against the table's 12.9, 15.8 and 20.8. The build path switches the same\n // way (see `buildDispatch`).\n const body = g.scoped(helperParam);\n const caseStrs: string[] = [];\n for (const { value, option: index } of cases) {\n const check = body.visit(options[index] as SchemaIR, { discSkipKey: discriminator });\n if (check === null) return null;\n caseStrs.push(`case ${literalToJs(value)}:return ${check};`);\n }\n g.ctx.preamble.push(\n `function ${helperName}(${helperParam}){${declareFastTemps(body.scope)}switch(${helperParam}[${discKey}]){${caseStrs.join(\"\")}default:return false;}}`,\n );\n\n return `typeof ${x}===\"object\"&&${x}!==null&&!Array.isArray(${x})&&${helperName}(${x})`;\n}\n\nexport function fastDiscriminatedUnion(ir: DiscriminatedUnionIR, g: FastGen): string | null {\n return emitFastDiscriminatedSwitch(g, ir.discriminator, ir.cases, ir.options);\n}\n\n/**\n * Minimum option count for rewriting a plain `z.union` to switch dispatch. Below\n * this the switch helper's fixed call overhead loses to a fully-inlined\n * `||`-chain that V8 keeps flat: measured crossover is ~4 options (n=3 can hit\n * 0.6x — a regression — while n=5 is 1.27x and n=32 is 2.34x), so 5 captures the\n * stable wins with margin and never regresses a small union. Real\n * `z.discriminatedUnion` is unaffected — it dispatches via switch by construction\n * regardless of size.\n */\nconst MIN_AUTO_DISCRIMINATE_OPTIONS = 5;\n\n/**\n * Values that switch correctly under `===` (excludes `undefined` and `NaN`).\n *\n * Also excludes a SYMBOL — now that `LiteralIR.values` admits one, this is the\n * guard that keeps it out. A symbol discriminant has no source form, so there\n * is no `case` label to emit for it (`literalToJs` refuses it by type), and the\n * ordinal-table variant is string-keyed besides. Detection bails to the\n * `||`-chain, where the option's own literal check does the right thing by\n * reading the value list off the retained schema.\n */\nfunction isSwitchableDiscriminant(v: LiteralValue): v is string | number | boolean | bigint | null {\n return (\n v === null ||\n typeof v === \"string\" ||\n typeof v === \"boolean\" ||\n typeof v === \"bigint\" ||\n (typeof v === \"number\" && !Number.isNaN(v))\n );\n}\n\n/**\n * Detect whether a plain (untagged) `z.union` is *structurally* a discriminated\n * union, so its fast path can use O(1) switch dispatch instead of probing every\n * arm. Returns the discriminator + dispatch table, or null to keep the\n * `||`-chain.\n *\n * Requires (proving the switch accepts exactly what the `||`-chain would): every\n * option is a plain object that pins one shared key to a REQUIRED literal\n * (`prop.type === \"literal\"` — an optional/non-literal key is rejected), and the\n * literal values are pairwise DISJOINT across options. Disjointness is the crux:\n * it guarantees at most one option can accept any given input, so dispatching to\n * that single option is equivalent to trying them all. Any value shared by two\n * options (ambiguous), a non-switchable value (`undefined`/`NaN`), or a\n * non-object option makes detection bail to the safe `||`-chain.\n *\n * The slow path is untouched: it keeps `z.union`'s sequential trial and its\n * `invalid_union` error shape, so failure output stays byte-identical to Zod.\n * The fast path and the build path both dispatch on the result — the build\n * path from two options up (`minOptions`), since its options are hosted calls\n * either way and a switch only ever replaces probes with one call; see\n * `buildUnion`.\n */\nexport function detectUnionDiscriminator(\n options: readonly SchemaIR[],\n minOptions: number = MIN_AUTO_DISCRIMINATE_OPTIONS,\n): { discriminator: string; cases: DiscriminatorCase[] } | null {\n if (options.length < minOptions) return null;\n const objects: ObjectIR[] = [];\n for (const option of options) {\n if (option.type !== \"object\") return null;\n objects.push(option);\n }\n const first = objects[0];\n if (first === undefined) return null; // unreachable (length checked above)\n\n // Only keys present in the first option can be shared by all; try each.\n candidate: for (const key of Object.keys(first.properties)) {\n const seen = new Set<string | number | boolean | bigint | null>();\n const cases: DiscriminatorCase[] = [];\n for (const [i, object] of objects.entries()) {\n const prop = object.properties[key];\n if (prop === undefined || prop.type !== \"literal\") continue candidate;\n for (const value of prop.values) {\n if (!isSwitchableDiscriminant(value)) continue candidate;\n if (seen.has(value)) continue candidate; // shared value → ambiguous dispatch\n seen.add(value);\n cases.push({ value, option: i });\n }\n }\n return { discriminator: key, cases };\n }\n return null;\n}\n"],"mappings":";;;;AAeA,SAAgB,uBACd,IACA,GACQ;CACR,MAAM,UAAU,aAAa,GAAG,aAAa;CAE7C,IAAI,OAAO,IAAI;gBACD,EAAE,MAAM,eAAe,EAAE,MAAM,yBAAyB,EAAE,MAAM;QACxE,YAAY,GAAG,UAAU,EAAE,WAAW,KAAK,CAAC,EAAE;;CAGpD,MAAM,SAAS,EAAE,KAAK,IAAI;CAC1B,QAAQ,OAAO,OAAO,GAAG,EAAE,MAAM,UAAU,OAAO,GAAG,QAAQ;CAE7D,KAAK,MAAM,EAAE,OAAO,QAAQ,WAAW,GAAG,OAAO;EAC/C,MAAM,SAAS,GAAG,QAAQ;EAC1B,QAAQ,IAAI;aACH,YAAY,KAAK,EAAE;UACtB,EAAE,MAAM,QAAQ;GAAE,OAAO;GAAQ,QAAQ;EAAO,CAAC,EAAE;;CAE3D;CAEA,MAAM,UAAU,EAAE,YAAY,KAAA,IAAY,KAAK,YAAY,KAAK,UAAU,EAAE,OAAO;CAWnF,MAAM,cAAc,GAAG,MAAM,KAAK,EAAE,YAAY,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG;CAC5E,QAAQ,IAAI;;QAEN,EAAE,OAAO,uFAAuF,QAAQ,YAAY,YAAY,UAAU,EAAE,MAAM,QAAQ,WAAW,EAAE,MAAM,OAAO,IAAI,QAAQ;;CAUtM,IAAI,GAAG,QAAQ,KAAK,WAAW,GAC7B,QAAQ,GAAG,EAAE,OAAO,GAAG,OAAO;CAEhC,QAAQ;CACR,OAAO,GAAG,KAAK;AACjB;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,4BACd,GACA,eACA,OACA,SACe;CACf,MAAM,IAAI,EAAE;CACZ,MAAM,UAAU,aAAa,aAAa;CAC1C,MAAM,aAAa,EAAE,KAAK,IAAI;CAC9B,MAAM,cAAc,EAAE,KAAK,IAAI;CAgB/B,MAAM,OAAO,EAAE,OAAO,WAAW;CACjC,MAAM,WAAqB,CAAC;CAC5B,KAAK,MAAM,EAAE,OAAO,QAAQ,WAAW,OAAO;EAC5C,MAAM,QAAQ,KAAK,MAAM,QAAQ,QAAoB,EAAE,aAAa,cAAc,CAAC;EACnF,IAAI,UAAU,MAAM,OAAO;EAC3B,SAAS,KAAK,QAAQ,YAAY,KAAK,EAAE,UAAU,MAAM,EAAE;CAC7D;CACA,EAAE,IAAI,SAAS,KACb,YAAY,WAAW,GAAG,YAAY,IAAI,iBAAiB,KAAK,KAAK,EAAE,SAAS,YAAY,GAAG,QAAQ,KAAK,SAAS,KAAK,EAAE,EAAE,wBAChI;CAEA,OAAO,UAAU,EAAE,eAAe,EAAE,0BAA0B,EAAE,KAAK,WAAW,GAAG,EAAE;AACvF;AAEA,SAAgB,uBAAuB,IAA0B,GAA2B;CAC1F,OAAO,4BAA4B,GAAG,GAAG,eAAe,GAAG,OAAO,GAAG,OAAO;AAC9E;;;;;;;;;;AAWA,MAAM,gCAAgC;;;;;;;;;;;AAYtC,SAAS,yBAAyB,GAAiE;CACjG,OACE,MAAM,QACN,OAAO,MAAM,YACb,OAAO,MAAM,aACb,OAAO,MAAM,YACZ,OAAO,MAAM,YAAY,CAAC,OAAO,MAAM,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,yBACd,SACA,aAAqB,+BACyC;CAC9D,IAAI,QAAQ,SAAS,YAAY,OAAO;CACxC,MAAM,UAAsB,CAAC;CAC7B,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,OAAO,SAAS,UAAU,OAAO;EACrC,QAAQ,KAAK,MAAM;CACrB;CACA,MAAM,QAAQ,QAAQ;CACtB,IAAI,UAAU,KAAA,GAAW,OAAO;CAGhC,WAAW,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,UAAU,GAAG;EAC1D,MAAM,uBAAO,IAAI,IAA+C;EAChE,MAAM,QAA6B,CAAC;EACpC,KAAK,MAAM,CAAC,GAAG,WAAW,QAAQ,QAAQ,GAAG;GAC3C,MAAM,OAAO,OAAO,WAAW;GAC/B,IAAI,SAAS,KAAA,KAAa,KAAK,SAAS,WAAW,SAAS;GAC5D,KAAK,MAAM,SAAS,KAAK,QAAQ;IAC/B,IAAI,CAAC,yBAAyB,KAAK,GAAG,SAAS;IAC/C,IAAI,KAAK,IAAI,KAAK,GAAG,SAAS;IAC9B,KAAK,IAAI,KAAK;IACd,MAAM,KAAK;KAAE;KAAO,QAAQ;IAAE,CAAC;GACjC;EACF;EACA,OAAO;GAAE,eAAe;GAAK;EAAM;CACrC;CACA,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effect.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/effect.ts"],"mappings":";;;;;;;iBAqBgB,WAAW,IAAI,oBAAoB,oBAAoB,GAAG;;;;;;;;;iBAmC1D,YAAY,OAAO,qBAAqB,cAAc,GAAG;;;;;
|
|
1
|
+
{"version":3,"file":"effect.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/effect.ts"],"mappings":";;;;;;;iBAqBgB,WAAW,IAAI,oBAAoB,oBAAoB,GAAG;;;;;;;;;iBAmC1D,YAAY,OAAO,qBAAqB,cAAc,GAAG;;;;;iBA+CzD,oBACd,OAAO,0BACP,cACA,GAAG;;;;;;;;;;;;;iBAmBW,iBACd,OAAO,0BACP,cACA,GAAG"}
|
|
@@ -39,7 +39,8 @@ function slowEffect(ir, g) {
|
|
|
39
39
|
* @param g - SlowGen context (provides path, issues)
|
|
40
40
|
*/
|
|
41
41
|
function refineCheck(check, expr, g) {
|
|
42
|
-
const
|
|
42
|
+
const message = check.message ?? g.typeMsg;
|
|
43
|
+
const messageProp = message === void 0 ? "" : `,message:${JSON.stringify(message)}`;
|
|
43
44
|
const path = (check.path ?? []).reduce((acc, segment) => typeof segment === "number" ? extendStaticPathIndex(acc, segment) : extendStaticPath(acc, segment), g.path);
|
|
44
45
|
const paramsProp = check.paramsRefIndex === void 0 ? "" : `,params:${emitConstant(g.ctx, "rpa", `__rf[${check.paramsRefIndex}]`)}`;
|
|
45
46
|
return emit`
|
|
@@ -80,7 +81,8 @@ function superRefineCheck(check, expr, g) {
|
|
|
80
81
|
const helper = emitRuntimeHelper(g.ctx, "__zcSr", ZC_SR_DECL);
|
|
81
82
|
const fn = emitEffectCallable(g.ctx, check);
|
|
82
83
|
const p = g.temp("sp");
|
|
83
|
-
|
|
84
|
+
const msgArg = g.typeMsg === void 0 ? "" : `,${JSON.stringify(g.typeMsg)}`;
|
|
85
|
+
let code = `var ${p}=${helper}(${fn},${expr},${g.path},${g.issues}${msgArg});${g.output}=${p}.value;`;
|
|
84
86
|
if (g.aborted) code += `if(${p}.aborted){${g.aborted}=true;}`;
|
|
85
87
|
return code;
|
|
86
88
|
}
|
|
@@ -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 //
|
|
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 // The refine's own message wins; without one, zod's finalizeIssue falls\n // through to the error map of the schema the check is attached to (the check\n // stamps its owner onto the issue), so the node's schema-level message is the\n // next rung. With neither, __zcFin applies the locale default.\n const message = check.message ?? g.typeMsg;\n const messageProp = message === undefined ? \"\" : `,message:${JSON.stringify(message)}`;\n // `.refine(fn, { path })` reports against a member of the refined value, so\n // the configured segments extend this node's path.\n const path = (check.path ?? []).reduce<string>(\n (acc, segment) =>\n typeof segment === \"number\"\n ? extendStaticPathIndex(acc, segment)\n : extendStaticPath(acc, segment),\n g.path,\n );\n // `.refine(fn, { params })`: $ZodCustom's check does\n // `if (def.params) _iss.params = def.params`, so the key is present only when\n // the schema declared one, and holds the ORIGINAL object — the extractor\n // parked it in `__rf` precisely so the reference survives (see\n // RefineEffectCheckIR.paramsRefIndex). Placed before `message` to keep the\n // key order zod produces.\n const paramsProp =\n check.paramsRefIndex === undefined\n ? \"\"\n : `,params:${emitConstant(g.ctx, \"rpa\", `__rf[${check.paramsRefIndex}]`)}`;\n 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 // The owning schema's message covers an added issue that carries none of its\n // own, as it does for a refine (see refineCheck); the helper applies it.\n const msgArg = g.typeMsg === undefined ? \"\" : `,${JSON.stringify(g.typeMsg)}`;\n let code = `var ${p}=${helper}(${fn},${expr},${g.path},${g.issues}${msgArg});${g.output}=${p}.value;`;\n // Inside a union option, an aborting issue must mark the option aborted so\n // pruning matches zod (see ZC_SR_DECL); elsewhere the flag is unobserved.\n if (g.aborted) code += `if(${p}.aborted){${g.aborted}=true;}`;\n return code;\n}\n"],"mappings":";;;;;;;;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;CAKxF,MAAM,UAAU,MAAM,WAAW,EAAE;CACnC,MAAM,cAAc,YAAY,KAAA,IAAY,KAAK,YAAY,KAAK,UAAU,OAAO;CAGnF,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;CAGrB,MAAM,SAAS,EAAE,YAAY,KAAA,IAAY,KAAK,IAAI,KAAK,UAAU,EAAE,OAAO;CAC1E,IAAI,OAAO,OAAO,EAAE,GAAG,OAAO,GAAG,GAAG,GAAG,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,SAAS,OAAO,IAAI,EAAE,OAAO,GAAG,EAAE;CAG7F,IAAI,EAAE,SAAS,QAAQ,MAAM,EAAE,YAAY,EAAE,QAAQ;CACrD,OAAO;AACT"}
|
|
@@ -27,19 +27,13 @@ function slowFile(ir, g) {
|
|
|
27
27
|
const mimeValuesExpr = JSON.stringify(check.mime);
|
|
28
28
|
if (check.mime.length === 1) code += emit`
|
|
29
29
|
if(${g.input}.type!==${escapeString(check.mime[0])}){
|
|
30
|
-
${invalidValue(g, mimeValuesExpr, {
|
|
31
|
-
message: check.message,
|
|
32
|
-
useTypeMsg: false
|
|
33
|
-
})}
|
|
30
|
+
${invalidValue(g, mimeValuesExpr, { message: check.message })}
|
|
34
31
|
}`;
|
|
35
32
|
else {
|
|
36
33
|
const setVar = g.set("mime", check.mime);
|
|
37
34
|
code += emit`
|
|
38
35
|
if(!${setVar}.has(${g.input}.type)){
|
|
39
|
-
${invalidValue(g, mimeValuesExpr, {
|
|
40
|
-
message: check.message,
|
|
41
|
-
useTypeMsg: false
|
|
42
|
-
})}
|
|
36
|
+
${invalidValue(g, mimeValuesExpr, { message: check.message })}
|
|
43
37
|
}`;
|
|
44
38
|
}
|
|
45
39
|
break;
|
|
@@ -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\";\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
|
|
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 })}\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 })}\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,EAAE,SAAS,MAAM,QAAQ,CAAC,EAAE;;QAE7D;IACL,MAAM,SAAS,EAAE,IAAI,QAAQ,MAAM,IAAI;IACvC,QAAQ,IAAI;oBACJ,OAAO,OAAO,EAAE,MAAM;kBACxB,aAAa,GAAG,gBAAgB,EAAE,SAAS,MAAM,QAAQ,CAAC,EAAE;;GAEpE;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"}
|
|
@@ -13,7 +13,7 @@ function slowNumber(ir, g) {
|
|
|
13
13
|
}else if(Number.isNaN(${g.input})){
|
|
14
14
|
${invalidType(g, "number", { extra: "received:\"NaN\"" })}
|
|
15
15
|
}else if(!Number.isFinite(${g.input})){
|
|
16
|
-
${invalidType(g, "number", { extra:
|
|
16
|
+
${invalidType(g, "number", { extra: `received:String(${g.input})` })}
|
|
17
17
|
}`;
|
|
18
18
|
if (ir.checks.length > 0) {
|
|
19
19
|
code += `else{`;
|
|
@@ -47,7 +47,7 @@ function slowNumber(ir, g) {
|
|
|
47
47
|
}`;
|
|
48
48
|
break;
|
|
49
49
|
case "number_format": {
|
|
50
|
-
const message = check.message;
|
|
50
|
+
const message = check.message ?? g.typeMsg;
|
|
51
51
|
if (check.format === "safeint") {
|
|
52
52
|
const note = `note:"Integers must be within the safe integer range."`;
|
|
53
53
|
const msgProp = message !== void 0 ? `,message:${JSON.stringify(message)}` : "";
|
|
@@ -96,7 +96,8 @@ function slowNumber(ir, g) {
|
|
|
96
96
|
break;
|
|
97
97
|
}
|
|
98
98
|
case "multiple_of": {
|
|
99
|
-
const
|
|
99
|
+
const message = check.message ?? g.typeMsg;
|
|
100
|
+
const msgProp = message !== void 0 ? `,message:${JSON.stringify(message)}` : "";
|
|
100
101
|
const fsr = emitRuntimeHelper(g.ctx, "__zcFsr", ZC_FSR_DECL);
|
|
101
102
|
code += emit`
|
|
102
103
|
if(${fsr}(${g.input},${check.value})!==0){
|
|
@@ -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\"', 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
|
+
{"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:String(${g.input})` })}\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 ?? g.typeMsg;\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 message = check.message ?? g.typeMsg;\n const msgProp = message !== undefined ? `,message:${JSON.stringify(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,10 +1,20 @@
|
|
|
1
1
|
import { ObjectIR, SchemaIR } from "../../types.js";
|
|
2
2
|
import { FastGen, SlowGen } from "../context.js";
|
|
3
3
|
//#region src/core/codegen/schemas/object.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The shape entries whose schemas actually RUN. `$ZodObject` skips a declared
|
|
6
|
+
* `__proto__` key in its shape loop (`if (key === "__proto__") continue`), so
|
|
7
|
+
* that property is never validated and never written to the output — it is
|
|
8
|
+
* only a recognized name for the strict/catchall passes, which read
|
|
9
|
+
* `Object.keys(ir.properties)` directly. Writing it would be worse than
|
|
10
|
+
* pointless: `{__proto__: v}` in an object literal, and `o["__proto__"] = v`
|
|
11
|
+
* on a plain object, both SET THE PROTOTYPE rather than define a key.
|
|
12
|
+
*/
|
|
13
|
+
declare function parsedProperties(ir: ObjectIR): [string, SchemaIR][];
|
|
4
14
|
declare function slowObject(ir: SchemaIR & {
|
|
5
15
|
type: "object";
|
|
6
16
|
}, g: SlowGen): string;
|
|
7
17
|
declare function fastObject(ir: ObjectIR, g: FastGen): string | null;
|
|
8
18
|
//#endregion
|
|
9
|
-
export { fastObject, slowObject };
|
|
19
|
+
export { fastObject, parsedProperties, slowObject };
|
|
10
20
|
//# sourceMappingURL=object.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/object.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"object.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/object.ts"],"mappings":";;;;;;;;;;;;iBA4BgB,iBAAiB,IAAI,oBAAoB;iBAIzC,WAAW,IAAI;EAAa;GAAkB,GAAG;iBA4TjD,WAAW,IAAI,UAAU,GAAG"}
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import { declareFastTemps, emitEffectCallable, emitRuntimeHelper, escapeString, extendPath, extendStaticPath, hasMutation, keyMembershipTest, outputAlwaysDefined } from "../context.js";
|
|
2
2
|
import { emit } from "../emit.js";
|
|
3
3
|
import { invalidType, unrecognizedKeys } from "../emit-issue.js";
|
|
4
|
-
import { ZC_AB_DECL } from "../issue-decls.js";
|
|
4
|
+
import { ZC_AB_DECL, ZC_PROTO_SCRUB_DECL } from "../issue-decls.js";
|
|
5
5
|
import { refineCheck, superRefineCheck, superRefineFastTest } from "./effect.js";
|
|
6
6
|
import { orderByRuntimeCost } from "../fast-size.js";
|
|
7
7
|
//#region src/core/codegen/schemas/object.ts
|
|
8
|
+
/**
|
|
9
|
+
* The shape entries whose schemas actually RUN. `$ZodObject` skips a declared
|
|
10
|
+
* `__proto__` key in its shape loop (`if (key === "__proto__") continue`), so
|
|
11
|
+
* that property is never validated and never written to the output — it is
|
|
12
|
+
* only a recognized name for the strict/catchall passes, which read
|
|
13
|
+
* `Object.keys(ir.properties)` directly. Writing it would be worse than
|
|
14
|
+
* pointless: `{__proto__: v}` in an object literal, and `o["__proto__"] = v`
|
|
15
|
+
* on a plain object, both SET THE PROTOTYPE rather than define a key.
|
|
16
|
+
*/
|
|
17
|
+
function parsedProperties(ir) {
|
|
18
|
+
return Object.entries(ir.properties).filter(([key]) => key !== "__proto__");
|
|
19
|
+
}
|
|
8
20
|
function slowObject(ir, g) {
|
|
9
21
|
let code = emit`
|
|
10
22
|
if(typeof ${g.input}!=="object"||${g.input}===null||Array.isArray(${g.input})){
|
|
@@ -13,13 +25,19 @@ function slowObject(ir, g) {
|
|
|
13
25
|
const strip = ir.stripUnknownKeys === true;
|
|
14
26
|
const needsClone = strip || ir.catchall !== void 0 && hasMutation(ir.catchall) || Object.values(ir.properties).some(hasMutation);
|
|
15
27
|
const objVar = g.temp("o");
|
|
16
|
-
if (!strip)
|
|
28
|
+
if (!strip) {
|
|
29
|
+
code += needsClone ? `var ${objVar}={...${g.input}};` : `var ${objVar}=${g.input};`;
|
|
30
|
+
const scrub = emitRuntimeHelper(g.ctx, "__zcPs", ZC_PROTO_SCRUB_DECL);
|
|
31
|
+
code += `${objVar}=${scrub}(${objVar});`;
|
|
32
|
+
}
|
|
17
33
|
const refineMark = ir.checks && ir.checks.length > 0 ? g.temp("rm") : "";
|
|
18
34
|
if (refineMark) code += `var ${refineMark}=${g.issues}.length;`;
|
|
35
|
+
const skipAbsent = new Set(ir.skipAbsentKeys ?? []);
|
|
19
36
|
const suppressAbsent = new Set(ir.suppressAbsentKeys ?? []);
|
|
37
|
+
const nonoptional = new Set(ir.nonoptionalKeys ?? []);
|
|
20
38
|
/** Strip only: per-property output slot + whether it is always in the result. */
|
|
21
39
|
const slots = [];
|
|
22
|
-
for (const [key, propIR] of
|
|
40
|
+
for (const [key, propIR] of parsedProperties(ir)) {
|
|
23
41
|
const keyStr = escapeString(key);
|
|
24
42
|
const propPath = extendStaticPath(g.path, key);
|
|
25
43
|
const propExpr = strip ? g.temp("sv") : `${objVar}[${keyStr}]`;
|
|
@@ -36,13 +54,35 @@ function slowObject(ir, g) {
|
|
|
36
54
|
output: propExpr,
|
|
37
55
|
path: propPath
|
|
38
56
|
});
|
|
39
|
-
|
|
57
|
+
const dropValue = strip ? `${propExpr}=undefined;` : `delete ${objVar}[${keyStr}];`;
|
|
58
|
+
if (skipAbsent.has(key)) code += emit`if(${keyStr} in ${g.input}){${propCode}}`;
|
|
59
|
+
else if (suppressAbsent.has(key)) {
|
|
60
|
+
const beforeVar = g.temp("ob");
|
|
61
|
+
const presentVar = g.temp("op");
|
|
62
|
+
code += emit`
|
|
63
|
+
var ${presentVar}=${keyStr} in ${g.input};
|
|
64
|
+
var ${beforeVar}=${g.issues}.length;
|
|
65
|
+
${propCode}
|
|
66
|
+
if(!${presentVar}&&${g.issues}.length>${beforeVar}){
|
|
67
|
+
${g.issues}.length=${beforeVar};${dropValue}
|
|
68
|
+
}`;
|
|
69
|
+
} else if (nonoptional.has(key)) {
|
|
40
70
|
const beforeVar = g.temp("ob");
|
|
71
|
+
const presentVar = g.temp("op");
|
|
41
72
|
code += emit`
|
|
73
|
+
var ${presentVar}=${keyStr} in ${g.input};
|
|
42
74
|
var ${beforeVar}=${g.issues}.length;
|
|
43
75
|
${propCode}
|
|
44
|
-
if(
|
|
45
|
-
${
|
|
76
|
+
if(!${presentVar}){
|
|
77
|
+
${dropValue}
|
|
78
|
+
if(${g.issues}.length===${beforeVar}){
|
|
79
|
+
${invalidType(g, "nonoptional", {
|
|
80
|
+
input: "undefined",
|
|
81
|
+
path: propPath,
|
|
82
|
+
codeFirst: true,
|
|
83
|
+
useTypeMsg: false
|
|
84
|
+
})}
|
|
85
|
+
}
|
|
46
86
|
}`;
|
|
47
87
|
} else code += propCode;
|
|
48
88
|
}
|
|
@@ -82,7 +122,7 @@ function slowObject(ir, g) {
|
|
|
82
122
|
const seed = needsClone ? `${slot}=${g.input}[${kVar}];` : "";
|
|
83
123
|
code += emit`
|
|
84
124
|
for(var ${kVar} in ${g.input}){
|
|
85
|
-
if(!(${test})){
|
|
125
|
+
if(!(${test})&&${kVar}!=="__proto__"){
|
|
86
126
|
${seed}
|
|
87
127
|
${g.visit(ir.catchall, {
|
|
88
128
|
input: slot,
|
|
@@ -92,7 +132,7 @@ function slowObject(ir, g) {
|
|
|
92
132
|
}
|
|
93
133
|
}`;
|
|
94
134
|
}
|
|
95
|
-
if (needsClone) code += `${g.output}=${objVar};`;
|
|
135
|
+
if (needsClone || !strip) code += `${g.output}=${objVar};`;
|
|
96
136
|
if (ir.checks && ir.checks.length > 0) {
|
|
97
137
|
let refines = "";
|
|
98
138
|
for (const check of ir.checks) refines += check.kind === "super_refine_effect" ? superRefineCheck(check, objVar, g) : refineCheck(check, objVar, g);
|
|
@@ -113,7 +153,7 @@ function slowObject(ir, g) {
|
|
|
113
153
|
* output is the issue list, whose order is part of zod parity.
|
|
114
154
|
*/
|
|
115
155
|
function orderedProperties(ir, g) {
|
|
116
|
-
return orderByRuntimeCost(
|
|
156
|
+
return orderByRuntimeCost(parsedProperties(ir), ([, propIR]) => propIR, g.ctx);
|
|
117
157
|
}
|
|
118
158
|
/**
|
|
119
159
|
* Property/strict/refine fast-checks for an object, WITHOUT the leading
|
|
@@ -127,12 +167,16 @@ function orderedProperties(ir, g) {
|
|
|
127
167
|
function fastObjectBody(ir, g, skipKey) {
|
|
128
168
|
const x = g.input;
|
|
129
169
|
const parts = [];
|
|
170
|
+
const absentAccepts = /* @__PURE__ */ new Set([...ir.skipAbsentKeys ?? [], ...ir.suppressAbsentKeys ?? []]);
|
|
171
|
+
const nonoptional = new Set(ir.nonoptionalKeys ?? []);
|
|
130
172
|
for (const [key, propIR] of orderedProperties(ir, g)) {
|
|
131
173
|
if (key === skipKey) continue;
|
|
132
|
-
const
|
|
133
|
-
const propCheck = g.visit(propIR, { input:
|
|
174
|
+
const keyStr = escapeString(key);
|
|
175
|
+
const propCheck = g.visit(propIR, { input: `${x}[${keyStr}]` });
|
|
134
176
|
if (propCheck === null) return null;
|
|
135
|
-
parts.push(propCheck);
|
|
177
|
+
if (nonoptional.has(key)) parts.push(propCheck === "true" ? `${keyStr} in ${x}` : `${keyStr} in ${x}&&${propCheck}`);
|
|
178
|
+
else if (absentAccepts.has(key)) parts.push(`(!(${keyStr} in ${x})||${propCheck})`);
|
|
179
|
+
else parts.push(propCheck);
|
|
136
180
|
}
|
|
137
181
|
if (ir.strict) {
|
|
138
182
|
const keys = Object.keys(ir.properties);
|
|
@@ -150,7 +194,7 @@ function fastObjectBody(ir, g, skipKey) {
|
|
|
150
194
|
if (valCheck !== "true") {
|
|
151
195
|
const fnName = g.temp("co");
|
|
152
196
|
const test = keyMembershipTest(g.ctx, Object.keys(ir.properties), kv);
|
|
153
|
-
g.ctx.preamble.push(`function ${fnName}(o){${declareFastTemps(catchallGen.scope)}var ${kv},${vv};for(${kv} in o){if(!(${test})){${vv}=o[${kv}];if(!(${valCheck}))return false;}}return true;}`);
|
|
197
|
+
g.ctx.preamble.push(`function ${fnName}(o){${declareFastTemps(catchallGen.scope)}var ${kv},${vv};for(${kv} in o){if(!(${test})&&${kv}!=="__proto__"){${vv}=o[${kv}];if(!(${valCheck}))return false;}}return true;}`);
|
|
154
198
|
parts.push(`${fnName}(${x})`);
|
|
155
199
|
}
|
|
156
200
|
}
|
|
@@ -173,6 +217,6 @@ function fastObject(ir, g) {
|
|
|
173
217
|
].join("&&");
|
|
174
218
|
}
|
|
175
219
|
//#endregion
|
|
176
|
-
export { fastObject, slowObject };
|
|
220
|
+
export { fastObject, parsedProperties, slowObject };
|
|
177
221
|
|
|
178
222
|
//# sourceMappingURL=object.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object.js","names":[],"sources":["../../../../src/core/codegen/schemas/object.ts"],"sourcesContent":["import type { ObjectIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n emitEffectCallable,\n emitRuntimeHelper,\n escapeString,\n extendPath,\n extendStaticPath,\n hasMutation,\n keyMembershipTest,\n outputAlwaysDefined,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, unrecognizedKeys } from \"../emit-issue.js\";\nimport { ZC_AB_DECL } from \"../issue-decls.js\";\nimport { orderByRuntimeCost } from \"../fast-size.js\";\nimport { refineCheck, superRefineCheck, superRefineFastTest } from \"./effect.js\";\n\nexport function slowObject(ir: SchemaIR & { type: \"object\" }, g: SlowGen): string {\n let code = emit`\n if(typeof ${g.input}!==\"object\"||${g.input}===null||Array.isArray(${g.input})){\n ${invalidType(g, \"object\")}\n }else{`;\n\n // Strip mode (zod's default z.object() output) rebuilds a FRESH object from\n // the declared keys, so it always writes back. Otherwise clone only when a\n // property mutates the value.\n const strip = ir.stripUnknownKeys === true;\n const needsClone =\n strip ||\n (ir.catchall !== undefined && hasMutation(ir.catchall)) ||\n Object.values(ir.properties).some(hasMutation);\n const objVar = g.temp(\"o\");\n if (!strip) {\n // Spread, not Object.assign: V8's CloneObjectIC makes `{...x}` ~25% faster\n // on the whole safeParse call for mutation-bearing schemas.\n code += needsClone ? `var ${objVar}={...${g.input}};` : `var ${objVar}=${g.input};`;\n }\n\n // Object-level refines are gated on zod's ABORT rule, not on \"did anything\n // fail\": zod parses the properties (plus the strict/catchall passes) into the\n // payload and then skips its check chain only when `util.aborted` holds — i.e.\n // when one of those issues is non-continuable. A property that failed its own\n // `min`/format check reports a CONTINUABLE issue, so the outer refine still\n // runs and both messages surface; a property that failed to parse at all\n // (`invalid_type`, an unrecognized key, a bad record key) aborts and\n // suppresses it. Snapshot the issue count before the properties so the scan\n // covers exactly this node's own parse, as zod's fresh sub-payload does.\n const refineMark = ir.checks && ir.checks.length > 0 ? g.temp(\"rm\") : \"\";\n if (refineMark) code += `var ${refineMark}=${g.issues}.length;`;\n\n const suppressAbsent = new Set(ir.suppressAbsentKeys ?? []);\n /** Strip only: per-property output slot + whether it is always in the result. */\n const slots: { always: boolean; keyStr: string; value: string }[] = [];\n\n for (const [key, propIR] of Object.entries(ir.properties)) {\n const keyStr = escapeString(key);\n const propPath = extendStaticPath(g.path, key);\n // Strip validates the value read from the INPUT, held in a local, and\n // assembles the result afterwards. Reading through the half-built copy (the\n // previous shape) both cost a megamorphic access per key — the copy's map\n // changes with every key added — and diverged from zod, which parses\n // `input[key]` and so accepts a value found on the prototype.\n const propExpr = strip ? g.temp(\"sv\") : `${objVar}[${keyStr}]`;\n if (strip) {\n code += `var ${propExpr}=${g.input}[${keyStr}];`;\n slots.push({ always: outputAlwaysDefined(propIR), keyStr, value: propExpr });\n }\n const propCode = g.visit(propIR, { input: propExpr, output: propExpr, path: propPath });\n if (suppressAbsent.has(key)) {\n // Mirrors zod's handlePropertyResult: optional-out fallback props run,\n // but their issues are discarded when the key is absent from the input.\n const beforeVar = g.temp(\"ob\");\n code += emit`\n var ${beforeVar}=${g.issues}.length;\n ${propCode}\n if(!(${keyStr} in ${strip ? g.input : objVar})&&${g.issues}.length>${beforeVar}){\n ${g.issues}.length=${beforeVar};\n }`;\n } else {\n code += propCode;\n }\n }\n\n if (strip) {\n // Assemble the result in shape order (zod's key order), taking as long a\n // LEADING run of always-present keys as possible into one object literal:\n // V8 stamps a literal out of a cached boilerplate map in a single\n // allocation, where adding keys one at a time walks a transition chain and\n // re-checks the map on every store — measured 8.1x (20 keys) / 2.8x (7\n // keys, one optional) on the whole safeParse.\n //\n // Everything after the first conditional key is appended so insertion order\n // still matches zod. The per-key test is zod's own: keep the key when the\n // parsed value is defined, or when it was present on the input at all\n // (`key in input`, prototype included — an own `k: undefined` survives).\n const literal: string[] = [];\n let appends = \"\";\n let leading = true;\n for (const slot of slots) {\n if (leading && slot.always) {\n literal.push(`${slot.keyStr}:${slot.value}`);\n continue;\n }\n leading = false;\n appends += slot.always\n ? `${objVar}[${slot.keyStr}]=${slot.value};`\n : `if(${slot.value}!==undefined||(${slot.keyStr} in ${g.input})){${objVar}[${slot.keyStr}]=${slot.value};}`;\n }\n code += `var ${objVar}={${literal.join(\",\")}};${appends}`;\n }\n\n // Strict unknown-key pass — zod's handleCatchall, byte-exact: for-in over\n // the ORIGINAL input (inherited enumerable keys count, no hasOwnProperty),\n // ALL unknown keys collected into one issue, pushed AFTER property issues\n // and before object-level refines.\n if (ir.strict) {\n const keys = Object.keys(ir.properties);\n const ukVar = g.temp(\"uk\");\n const kVar = g.temp(\"k\");\n const test = keyMembershipTest(g.ctx, keys, kVar);\n code += emit`\n var ${ukVar}=null;\n for(var ${kVar} in ${g.input}){\n if(!(${test})){(${ukVar}=${ukVar}||[]).push(${kVar});}\n }\n if(${ukVar}!==null){\n ${unrecognizedKeys(g, ukVar)}\n }`;\n }\n\n // .catchall(schema): validate every key NOT in the shape, mirroring zod's\n // handleCatchall — same bare for-in over the ORIGINAL input as the strict\n // pass (inherited enumerable keys count, no hasOwnProperty guard), each\n // issue reported at the key. Runs after the properties, as zod does.\n //\n // The key's slot is BOTH the input and the output, exactly as a shape\n // property's is: a value-rewriting catchall (coerce, .trim(), a default)\n // writes through the same expression it later re-reads, so splitting them\n // makes the rewrite invisible to its own checks.\n //\n // When the catchall mutates, objVar is a clone, and each unknown key is\n // copied into it before validation — which also reproduces zod, whose fresh\n // output object gains an OWN key for every for-in key it saw, inherited ones\n // included. A non-mutating catchall writes nothing, so objVar is the input\n // and the object stays pass-through by reference.\n if (ir.catchall) {\n const keys = Object.keys(ir.properties);\n const kVar = g.temp(\"ck\");\n const test = keyMembershipTest(g.ctx, keys, kVar);\n const slot = `${objVar}[${kVar}]`;\n const seed = needsClone ? `${slot}=${g.input}[${kVar}];` : \"\";\n code += emit`\n for(var ${kVar} in ${g.input}){\n if(!(${test})){\n ${seed}\n ${g.visit(ir.catchall, {\n input: slot,\n output: slot,\n path: extendPath(g.path, kVar),\n })}\n }\n }`;\n }\n\n if (needsClone) {\n code += `${g.output}=${objVar};`;\n }\n\n // Object-level refine effects: z.object({...}).refine(fn), suppressed when the\n // parse phase aborted (see refineMark). One gate covers every effect: each of\n // them can only add a continuable `custom` issue, so zod's per-check\n // re-evaluation of `isAborted` can never flip between them.\n if (ir.checks && ir.checks.length > 0) {\n let refines = \"\";\n for (const check of ir.checks) {\n refines +=\n check.kind === \"super_refine_effect\"\n ? superRefineCheck(check, objVar, g)\n : refineCheck(check, objVar, g);\n }\n const aborted = emitRuntimeHelper(g.ctx, \"__zcAb\", ZC_AB_DECL);\n code += `if(!${aborted}(${g.issues},${refineMark})){${refines}}`;\n }\n\n code += `}\\n`;\n return code;\n}\n\n/**\n * Property entries in the order their fast-checks should be `&&`-chained:\n * cheapest first, declaration order preserved among equals (Array#sort is\n * stable). Valid input runs every conjunct whatever the order, so this costs\n * nothing on the hot path; a REJECT stops at the first false conjunct, so\n * pricing a `z.email()` behind the `kind` literal that actually discriminates\n * is what makes union probing and `.is()` misses cheap (see\n * estimateRuntimeCost). The SLOW path keeps declaration order — that one's\n * output is the issue list, whose order is part of zod parity.\n */\nfunction orderedProperties(ir: ObjectIR, g: FastGen): [string, SchemaIR][] {\n return orderByRuntimeCost(Object.entries(ir.properties), ([, propIR]) => propIR, g.ctx);\n}\n\n/**\n * Property/strict/refine fast-checks for an object, WITHOUT the leading\n * `typeof===object && !==null && !Array.isArray` type-guard. Returns the\n * conjunct parts (joinable with `&&`), or null if any child is fast-ineligible.\n *\n * `skipKey`, when given, omits that one property's check. Used by the\n * discriminated-union fast path (via `g.discSkipKey`): the enclosing `switch`\n * has already matched the discriminator's value, so re-checking it is redundant.\n */\nfunction fastObjectBody(ir: ObjectIR, g: FastGen, skipKey?: string): string[] | null {\n const x = g.input;\n const parts: string[] = [];\n\n for (const [key, propIR] of orderedProperties(ir, g)) {\n if (key === skipKey) continue;\n const propExpr = `${x}[${escapeString(key)}]`;\n const propCheck = g.visit(propIR, { input: propExpr });\n if (propCheck === null) return null; // All-or-nothing\n parts.push(propCheck);\n }\n\n // Strict unknown-key pass: hosted boolean helper (a for-in loop cannot live\n // in the && chain). Same for-in iteration as the slow path — fast/slow\n // agreement is load-bearing under the __zcFinD deferral. The membership set is\n // the FULL key list (the discriminator is a recognized key), independent of\n // skipKey, which only suppresses re-validating the discriminator's value.\n if (ir.strict) {\n const keys = Object.keys(ir.properties);\n const fnName = g.temp(\"so\");\n const test = keyMembershipTest(g.ctx, keys, \"k\");\n g.ctx.preamble.push(\n `function ${fnName}(o){for(var k in o){if(!(${test}))return false;}return true;}`,\n );\n parts.push(`${fnName}(${x})`);\n }\n\n // .catchall(schema): same hosted for-in as strict, but each unrecognized key\n // is checked against the catchall rather than rejected outright. The value is\n // hoisted into a local for the same reason fastRecord does it — repeated\n // `o[k]` computed loads are not eliminated across check boundaries.\n if (ir.catchall) {\n const kv = g.temp(\"cak\");\n const vv = g.temp(\"cav\");\n const catchallGen = g.scoped(vv);\n const valCheck = catchallGen.visit(ir.catchall, { input: vv });\n if (valCheck === null) return null;\n if (valCheck !== \"true\") {\n const fnName = g.temp(\"co\");\n const test = keyMembershipTest(g.ctx, Object.keys(ir.properties), kv);\n g.ctx.preamble.push(\n `function ${fnName}(o){${declareFastTemps(catchallGen.scope)}var ${kv},${vv};for(${kv} in o){if(!(${test})){${vv}=o[${kv}];if(!(${valCheck}))return false;}}return true;}`,\n );\n parts.push(`${fnName}(${x})`);\n }\n }\n\n // Object-level refine effects (appended last — run after property checks short-circuit)\n if (ir.checks) {\n for (const check of ir.checks) {\n if (check.kind === \"refine_effect\") {\n parts.push(`${emitEffectCallable(g.ctx, check)}(${x})`);\n } else if (check.kind === \"super_refine_effect\") {\n parts.push(superRefineFastTest(check, x, g));\n }\n }\n }\n\n return parts;\n}\n\nexport function fastObject(ir: ObjectIR, g: FastGen): string | null {\n // A strip object still gets an expression. Stripping reshapes the OUTPUT, not\n // the verdict, so this remains an exact acceptance predicate — which is what\n // `.is()` installs, and what the build path reuses to validate the subtrees it\n // passes through by reference. What it must NOT do is stand in for the parse\n // result: generateValidator withholds the `data:input` shortcut whenever the\n // schema rebuilds (see rebuildsOutput).\n const x = g.input;\n const body = fastObjectBody(ir, g, g.discSkipKey);\n if (body === null) return null;\n // Discriminated-union option: the enclosing switch (and the caller's guard)\n // already established object-ness and the discriminator value, so emit only\n // the remaining checks — no leading type-guard. An option with nothing left\n // to check accepts unconditionally (\"true\").\n if (g.discSkipKey !== undefined) {\n return body.length > 0 ? body.join(\"&&\") : \"true\";\n }\n return [`typeof ${x}===\"object\"`, `${x}!==null`, `!Array.isArray(${x})`, ...body].join(\"&&\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"object.js","names":[],"sources":["../../../../src/core/codegen/schemas/object.ts"],"sourcesContent":["import type { ObjectIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n emitEffectCallable,\n emitRuntimeHelper,\n escapeString,\n extendPath,\n extendStaticPath,\n hasMutation,\n keyMembershipTest,\n outputAlwaysDefined,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, unrecognizedKeys } from \"../emit-issue.js\";\nimport { ZC_AB_DECL, ZC_PROTO_SCRUB_DECL } from \"../issue-decls.js\";\nimport { orderByRuntimeCost } from \"../fast-size.js\";\nimport { refineCheck, superRefineCheck, superRefineFastTest } from \"./effect.js\";\n\n/**\n * The shape entries whose schemas actually RUN. `$ZodObject` skips a declared\n * `__proto__` key in its shape loop (`if (key === \"__proto__\") continue`), so\n * that property is never validated and never written to the output — it is\n * only a recognized name for the strict/catchall passes, which read\n * `Object.keys(ir.properties)` directly. Writing it would be worse than\n * pointless: `{__proto__: v}` in an object literal, and `o[\"__proto__\"] = v`\n * on a plain object, both SET THE PROTOTYPE rather than define a key.\n */\nexport function parsedProperties(ir: ObjectIR): [string, SchemaIR][] {\n return Object.entries(ir.properties).filter(([key]) => key !== \"__proto__\");\n}\n\nexport function slowObject(ir: SchemaIR & { type: \"object\" }, g: SlowGen): string {\n let code = emit`\n if(typeof ${g.input}!==\"object\"||${g.input}===null||Array.isArray(${g.input})){\n ${invalidType(g, \"object\")}\n }else{`;\n\n // Strip mode (zod's default z.object() output) rebuilds a FRESH object from\n // the declared keys, so it always writes back. Otherwise clone only when a\n // property mutates the value.\n const strip = ir.stripUnknownKeys === true;\n const needsClone =\n strip ||\n (ir.catchall !== undefined && hasMutation(ir.catchall)) ||\n Object.values(ir.properties).some(hasMutation);\n const objVar = g.temp(\"o\");\n if (!strip) {\n // Spread, not Object.assign: V8's CloneObjectIC makes `{...x}` ~25% faster\n // on the whole safeParse call for mutation-bearing schemas.\n code += needsClone ? `var ${objVar}={...${g.input}};` : `var ${objVar}=${g.input};`;\n // A loose or catchall output is the INPUT, or a spread of it — and spread\n // copies an own `__proto__` as a plain data property — where zod's fresh\n // `{}` never receives the key at all. Scrub it (see ZC_PROTO_SCRUB_DECL); a\n // strip object rebuilds and needs nothing here.\n const scrub = emitRuntimeHelper(g.ctx, \"__zcPs\", ZC_PROTO_SCRUB_DECL);\n code += `${objVar}=${scrub}(${objVar});`;\n }\n\n // Object-level refines are gated on zod's ABORT rule, not on \"did anything\n // fail\": zod parses the properties (plus the strict/catchall passes) into the\n // payload and then skips its check chain only when `util.aborted` holds — i.e.\n // when one of those issues is non-continuable. A property that failed its own\n // `min`/format check reports a CONTINUABLE issue, so the outer refine still\n // runs and both messages surface — and so does an unrecognized key, which zod\n // pushes with `continue: true`; a property that failed to parse at all\n // (`invalid_type`, a bad record key) aborts and suppresses it. Snapshot the\n // issue count before the properties so the scan covers exactly this node's\n // own parse, as zod's fresh sub-payload does.\n const refineMark = ir.checks && ir.checks.length > 0 ? g.temp(\"rm\") : \"\";\n if (refineMark) code += `var ${refineMark}=${g.issues}.length;`;\n\n const skipAbsent = new Set(ir.skipAbsentKeys ?? []);\n const suppressAbsent = new Set(ir.suppressAbsentKeys ?? []);\n const nonoptional = new Set(ir.nonoptionalKeys ?? []);\n /** Strip only: per-property output slot + whether it is always in the result. */\n const slots: { always: boolean; keyStr: string; value: string }[] = [];\n\n for (const [key, propIR] of parsedProperties(ir)) {\n const keyStr = escapeString(key);\n const propPath = extendStaticPath(g.path, key);\n // Strip validates the value read from the INPUT, held in a local, and\n // assembles the result afterwards. Reading through the half-built copy (the\n // previous shape) both cost a megamorphic access per key — the copy's map\n // changes with every key added — and diverged from zod, which parses\n // `input[key]` and so accepts a value found on the prototype.\n const propExpr = strip ? g.temp(\"sv\") : `${objVar}[${keyStr}]`;\n if (strip) {\n code += `var ${propExpr}=${g.input}[${keyStr}];`;\n slots.push({ always: outputAlwaysDefined(propIR), keyStr, value: propExpr });\n }\n const propCode = g.visit(propIR, { input: propExpr, output: propExpr, path: propPath });\n // The three absent-key rules of zod's handlePropertyResult (see ObjectIR).\n // Presence is tested on the ORIGINAL input, as zod's `key in input` is —\n // a clone would hide an inherited key.\n //\n // The two rules that test presence AFTER running the property snapshot it\n // FIRST. Running the property can create the very key the test asks about:\n // where nothing mutates, `objVar` IS `g.input`, so the write-back of a\n // property that accepted `undefined` (`o[key]=result`) adds an own `key`\n // and the guard then reads it as present. `z.looseObject({a: z.custom()})`\n // on `{}` lost its `nonoptional` issue entirely that way — the fast path\n // still rejected on `\"a\" in x`, so `safeParse` failed with an EMPTY issue\n // array — and left the caller's object carrying an `a` zod never writes.\n const dropValue = strip ? `${propExpr}=undefined;` : `delete ${objVar}[${keyStr}];`;\n if (skipAbsent.has(key)) {\n code += emit`if(${keyStr} in ${g.input}){${propCode}}`;\n } else if (suppressAbsent.has(key)) {\n // A defaulted optional-out property runs, but a failure on an absent key\n // is discarded whole: issues and whatever value it wrote before failing.\n const beforeVar = g.temp(\"ob\");\n const presentVar = g.temp(\"op\");\n code += emit`\n var ${presentVar}=${keyStr} in ${g.input};\n var ${beforeVar}=${g.issues}.length;\n ${propCode}\n if(!${presentVar}&&${g.issues}.length>${beforeVar}){\n ${g.issues}.length=${beforeVar};${dropValue}\n }`;\n } else if (nonoptional.has(key)) {\n // A required key that is absent fails as such when its schema raised\n // nothing for the `undefined` it saw. zod pushes this one without an\n // `inst`, so no schema-level message reaches it — and it returns without\n // assigning, so whatever the property made of `undefined` is dropped.\n const beforeVar = g.temp(\"ob\");\n const presentVar = g.temp(\"op\");\n code += emit`\n var ${presentVar}=${keyStr} in ${g.input};\n var ${beforeVar}=${g.issues}.length;\n ${propCode}\n if(!${presentVar}){\n ${dropValue}\n if(${g.issues}.length===${beforeVar}){\n ${invalidType(g, \"nonoptional\", { input: \"undefined\", path: propPath, codeFirst: true, useTypeMsg: false })}\n }\n }`;\n } else {\n code += propCode;\n }\n }\n\n if (strip) {\n // Assemble the result in shape order (zod's key order), taking as long a\n // LEADING run of always-present keys as possible into one object literal:\n // V8 stamps a literal out of a cached boilerplate map in a single\n // allocation, where adding keys one at a time walks a transition chain and\n // re-checks the map on every store — measured 8.1x (20 keys) / 2.8x (7\n // keys, one optional) on the whole safeParse.\n //\n // Everything after the first conditional key is appended so insertion order\n // still matches zod. The per-key test is zod's own: keep the key when the\n // parsed value is defined, or when it was present on the input at all\n // (`key in input`, prototype included — an own `k: undefined` survives).\n const literal: string[] = [];\n let appends = \"\";\n let leading = true;\n for (const slot of slots) {\n if (leading && slot.always) {\n literal.push(`${slot.keyStr}:${slot.value}`);\n continue;\n }\n leading = false;\n appends += slot.always\n ? `${objVar}[${slot.keyStr}]=${slot.value};`\n : `if(${slot.value}!==undefined||(${slot.keyStr} in ${g.input})){${objVar}[${slot.keyStr}]=${slot.value};}`;\n }\n code += `var ${objVar}={${literal.join(\",\")}};${appends}`;\n }\n\n // Strict unknown-key pass — zod's handleCatchall, byte-exact: for-in over\n // the ORIGINAL input (inherited enumerable keys count, no hasOwnProperty),\n // ALL unknown keys collected into one issue, pushed AFTER property issues\n // and before object-level refines.\n if (ir.strict) {\n const keys = Object.keys(ir.properties);\n const ukVar = g.temp(\"uk\");\n const kVar = g.temp(\"k\");\n const test = keyMembershipTest(g.ctx, keys, kVar);\n code += emit`\n var ${ukVar}=null;\n for(var ${kVar} in ${g.input}){\n if(!(${test})){(${ukVar}=${ukVar}||[]).push(${kVar});}\n }\n if(${ukVar}!==null){\n ${unrecognizedKeys(g, ukVar)}\n }`;\n }\n\n // .catchall(schema): validate every key NOT in the shape, mirroring zod's\n // handleCatchall — same bare for-in over the ORIGINAL input as the strict\n // pass (inherited enumerable keys count, no hasOwnProperty guard), each\n // issue reported at the key. Runs after the properties, as zod does. An\n // undeclared `__proto__` is skipped like zod skips it: never validated, and\n // never assigned, since `o[\"__proto__\"]=v` on the clone would replace its\n // prototype instead of adding a key. (The strict pass above still REPORTS\n // it — there it is an unknown key like any other.)\n //\n // The key's slot is BOTH the input and the output, exactly as a shape\n // property's is: a value-rewriting catchall (coerce, .trim(), a default)\n // writes through the same expression it later re-reads, so splitting them\n // makes the rewrite invisible to its own checks.\n //\n // When the catchall mutates, objVar is a clone, and each unknown key is\n // copied into it before validation — which also reproduces zod, whose fresh\n // output object gains an OWN key for every for-in key it saw, inherited ones\n // included. A non-mutating catchall writes nothing, so objVar is the input\n // and the object stays pass-through by reference.\n if (ir.catchall) {\n const keys = Object.keys(ir.properties);\n const kVar = g.temp(\"ck\");\n const test = keyMembershipTest(g.ctx, keys, kVar);\n const slot = `${objVar}[${kVar}]`;\n const seed = needsClone ? `${slot}=${g.input}[${kVar}];` : \"\";\n code += emit`\n for(var ${kVar} in ${g.input}){\n if(!(${test})&&${kVar}!==\"__proto__\"){\n ${seed}\n ${g.visit(ir.catchall, {\n input: slot,\n output: slot,\n path: extendPath(g.path, kVar),\n })}\n }\n }`;\n }\n\n // `!strip` also writes back: the scrub above may have re-pointed `objVar` at\n // a copy even where nothing else mutates.\n if (needsClone || !strip) {\n code += `${g.output}=${objVar};`;\n }\n\n // Object-level refine effects: z.object({...}).refine(fn), suppressed when the\n // parse phase aborted (see refineMark). One gate covers every effect: each of\n // them can only add a continuable `custom` issue, so zod's per-check\n // re-evaluation of `isAborted` can never flip between them.\n if (ir.checks && ir.checks.length > 0) {\n let refines = \"\";\n for (const check of ir.checks) {\n refines +=\n check.kind === \"super_refine_effect\"\n ? superRefineCheck(check, objVar, g)\n : refineCheck(check, objVar, g);\n }\n const aborted = emitRuntimeHelper(g.ctx, \"__zcAb\", ZC_AB_DECL);\n code += `if(!${aborted}(${g.issues},${refineMark})){${refines}}`;\n }\n\n code += `}\\n`;\n return code;\n}\n\n/**\n * Property entries in the order their fast-checks should be `&&`-chained:\n * cheapest first, declaration order preserved among equals (Array#sort is\n * stable). Valid input runs every conjunct whatever the order, so this costs\n * nothing on the hot path; a REJECT stops at the first false conjunct, so\n * pricing a `z.email()` behind the `kind` literal that actually discriminates\n * is what makes union probing and `.is()` misses cheap (see\n * estimateRuntimeCost). The SLOW path keeps declaration order — that one's\n * output is the issue list, whose order is part of zod parity.\n */\nfunction orderedProperties(ir: ObjectIR, g: FastGen): [string, SchemaIR][] {\n return orderByRuntimeCost(parsedProperties(ir), ([, propIR]) => propIR, g.ctx);\n}\n\n/**\n * Property/strict/refine fast-checks for an object, WITHOUT the leading\n * `typeof===object && !==null && !Array.isArray` type-guard. Returns the\n * conjunct parts (joinable with `&&`), or null if any child is fast-ineligible.\n *\n * `skipKey`, when given, omits that one property's check. Used by the\n * discriminated-union fast path (via `g.discSkipKey`): the enclosing `switch`\n * has already matched the discriminator's value, so re-checking it is redundant.\n */\nfunction fastObjectBody(ir: ObjectIR, g: FastGen, skipKey?: string): string[] | null {\n const x = g.input;\n const parts: string[] = [];\n const absentAccepts = new Set([...(ir.skipAbsentKeys ?? []), ...(ir.suppressAbsentKeys ?? [])]);\n const nonoptional = new Set(ir.nonoptionalKeys ?? []);\n\n for (const [key, propIR] of orderedProperties(ir, g)) {\n if (key === skipKey) continue;\n const keyStr = escapeString(key);\n const propCheck = g.visit(propIR, { input: `${x}[${keyStr}]` });\n if (propCheck === null) return null; // All-or-nothing\n // Presence rules keyed on the schema's `optin`/`optout` (see ObjectIR): a\n // required key must be there whatever its schema makes of `undefined`, and\n // an optional-out key on the \"optional\"/\"defaulted\" rungs is accepted when\n // it is not, whatever the property would say.\n if (nonoptional.has(key)) {\n parts.push(propCheck === \"true\" ? `${keyStr} in ${x}` : `${keyStr} in ${x}&&${propCheck}`);\n } else if (absentAccepts.has(key)) {\n parts.push(`(!(${keyStr} in ${x})||${propCheck})`);\n } else {\n parts.push(propCheck);\n }\n }\n\n // Strict unknown-key pass: hosted boolean helper (a for-in loop cannot live\n // in the && chain). Same for-in iteration as the slow path — fast/slow\n // agreement is load-bearing under the __zcFinD deferral. The membership set is\n // the FULL key list (the discriminator is a recognized key), independent of\n // skipKey, which only suppresses re-validating the discriminator's value.\n if (ir.strict) {\n const keys = Object.keys(ir.properties);\n const fnName = g.temp(\"so\");\n const test = keyMembershipTest(g.ctx, keys, \"k\");\n g.ctx.preamble.push(\n `function ${fnName}(o){for(var k in o){if(!(${test}))return false;}return true;}`,\n );\n parts.push(`${fnName}(${x})`);\n }\n\n // .catchall(schema): same hosted for-in as strict, but each unrecognized key\n // is checked against the catchall rather than rejected outright. The value is\n // hoisted into a local for the same reason fastRecord does it — repeated\n // `o[k]` computed loads are not eliminated across check boundaries.\n if (ir.catchall) {\n const kv = g.temp(\"cak\");\n const vv = g.temp(\"cav\");\n const catchallGen = g.scoped(vv);\n const valCheck = catchallGen.visit(ir.catchall, { input: vv });\n if (valCheck === null) return null;\n if (valCheck !== \"true\") {\n const fnName = g.temp(\"co\");\n const test = keyMembershipTest(g.ctx, Object.keys(ir.properties), kv);\n g.ctx.preamble.push(\n `function ${fnName}(o){${declareFastTemps(catchallGen.scope)}var ${kv},${vv};for(${kv} in o){if(!(${test})&&${kv}!==\"__proto__\"){${vv}=o[${kv}];if(!(${valCheck}))return false;}}return true;}`,\n );\n parts.push(`${fnName}(${x})`);\n }\n }\n\n // Object-level refine effects (appended last — run after property checks short-circuit)\n if (ir.checks) {\n for (const check of ir.checks) {\n if (check.kind === \"refine_effect\") {\n parts.push(`${emitEffectCallable(g.ctx, check)}(${x})`);\n } else if (check.kind === \"super_refine_effect\") {\n parts.push(superRefineFastTest(check, x, g));\n }\n }\n }\n\n return parts;\n}\n\nexport function fastObject(ir: ObjectIR, g: FastGen): string | null {\n // A strip object still gets an expression. Stripping reshapes the OUTPUT, not\n // the verdict, so this remains an exact acceptance predicate — which is what\n // `.is()` installs, and what the build path reuses to validate the subtrees it\n // passes through by reference. What it must NOT do is stand in for the parse\n // result: generateValidator withholds the `data:input` shortcut whenever the\n // schema rebuilds (see rebuildsOutput).\n const x = g.input;\n const body = fastObjectBody(ir, g, g.discSkipKey);\n if (body === null) return null;\n // Discriminated-union option: the enclosing switch (and the caller's guard)\n // already established object-ness and the discriminator value, so emit only\n // the remaining checks — no leading type-guard. An option with nothing left\n // to check accepts unconditionally (\"true\").\n if (g.discSkipKey !== undefined) {\n return body.length > 0 ? body.join(\"&&\") : \"true\";\n }\n return [`typeof ${x}===\"object\"`, `${x}!==null`, `!Array.isArray(${x})`, ...body].join(\"&&\");\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readonly.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/readonly.ts"],"mappings":";;;iBAGgB,aAAa,IAAI;EAAa;GAAoB,GAAG;
|
|
1
|
+
{"version":3,"file":"readonly.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/readonly.ts"],"mappings":";;;iBAGgB,aAAa,IAAI;EAAa;GAAoB,GAAG;iBAkBrD,aAAa,IAAI,YAAY,GAAG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readonly.js","names":[],"sources":["../../../../src/core/codegen/schemas/readonly.ts"],"sourcesContent":["import type { ReadonlyIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\n\nexport function slowReadonly(ir: SchemaIR & { type: \"readonly\" }, g: SlowGen): string {\n // Pass-through wrapper: forward the union abort flag (see slowOptional).\n const inner = g.visit(ir.inner, { aborted: g.aborted });\n if (ir.freeze !== true) return inner;\n // Guarded because the walk only reassigns `output` to its rebuilt object once\n // the type check passed — a failed one leaves the CALLER's input sitting\n // there, and freezing that is the side effect zod avoids.\n //\n // The mark is RELATIVE to the issues already collected, never against zero
|
|
1
|
+
{"version":3,"file":"readonly.js","names":[],"sources":["../../../../src/core/codegen/schemas/readonly.ts"],"sourcesContent":["import type { ReadonlyIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\n\nexport function slowReadonly(ir: SchemaIR & { type: \"readonly\" }, g: SlowGen): string {\n // Pass-through wrapper: forward the union abort flag (see slowOptional).\n const inner = g.visit(ir.inner, { aborted: g.aborted });\n if (ir.freeze !== true) return inner;\n // Guarded because the walk only reassigns `output` to its rebuilt object once\n // the type check passed — a failed one leaves the CALLER's input sitting\n // there, and freezing that is the side effect zod avoids.\n //\n // The mark is RELATIVE to the issues already collected, never against zero:\n // zod runs every node on a fresh payload and freezes whenever THAT node's\n // parse is clean, while the compiled walk shares one issues array across\n // siblings, so an absolute test would skip the freeze as soon as an earlier\n // sibling had failed. Same relative-mark discipline as slowObject's\n // refineMark, slowPipe's lenVar and slowEffect's beforeVar.\n const mark = g.temp(\"rf\");\n return `var ${mark}=${g.issues}.length;\\n${inner}if(${g.issues}.length===${mark}){${g.output}=Object.freeze(${g.output});}\\n`;\n}\n\nexport function fastReadonly(ir: ReadonlyIR, g: FastGen): string | null {\n return g.visit(ir.inner);\n}\n"],"mappings":";AAGA,SAAgB,aAAa,IAAqC,GAAoB;CAEpF,MAAM,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC;CACtD,IAAI,GAAG,WAAW,MAAM,OAAO;CAW/B,MAAM,OAAO,EAAE,KAAK,IAAI;CACxB,OAAO,OAAO,KAAK,GAAG,EAAE,OAAO,YAAY,MAAM,KAAK,EAAE,OAAO,YAAY,KAAK,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO;AACzH;AAEA,SAAgB,aAAa,IAAgB,GAA2B;CACtE,OAAO,EAAE,MAAM,GAAG,KAAK;AACzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/record.ts"],"mappings":";;;iBA0BgB,WAAW,IAAI;EAAa;GAAkB,GAAG;
|
|
1
|
+
{"version":3,"file":"record.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/record.ts"],"mappings":";;;iBA0BgB,WAAW,IAAI;EAAa;GAAkB,GAAG;iBA8DjD,WAAW,IAAI,UAAU,GAAG"}
|