zod-compiler 1.23.2 → 1.23.4
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 +10 -5
- package/dist/core/codegen/build-path.d.ts.map +1 -1
- package/dist/core/codegen/build-path.js +36 -9
- package/dist/core/codegen/build-path.js.map +1 -1
- package/dist/core/codegen/dedupe.js +1 -0
- package/dist/core/codegen/dedupe.js.map +1 -1
- package/dist/core/codegen/fast-path.d.ts.map +1 -1
- package/dist/core/codegen/fast-path.js +2 -0
- package/dist/core/codegen/fast-path.js.map +1 -1
- package/dist/core/codegen/fast-size.d.ts.map +1 -1
- package/dist/core/codegen/fast-size.js +2 -0
- package/dist/core/codegen/fast-size.js.map +1 -1
- package/dist/core/codegen/issue-decls.d.ts +7 -1
- package/dist/core/codegen/issue-decls.d.ts.map +1 -1
- package/dist/core/codegen/issue-decls.js +8 -1
- package/dist/core/codegen/issue-decls.js.map +1 -1
- package/dist/core/codegen/schemas/custom.d.ts +10 -0
- package/dist/core/codegen/schemas/custom.d.ts.map +1 -0
- package/dist/core/codegen/schemas/custom.js +17 -0
- package/dist/core/codegen/schemas/custom.js.map +1 -0
- package/dist/core/codegen/schemas/fallback.d.ts +3 -1
- package/dist/core/codegen/schemas/fallback.d.ts.map +1 -1
- package/dist/core/codegen/schemas/fallback.js +23 -20
- package/dist/core/codegen/schemas/fallback.js.map +1 -1
- package/dist/core/codegen/schemas/string-bool.d.ts +5 -2
- package/dist/core/codegen/schemas/string-bool.d.ts.map +1 -1
- package/dist/core/codegen/schemas/string-bool.js +18 -9
- package/dist/core/codegen/schemas/string-bool.js.map +1 -1
- package/dist/core/codegen/slow-path.d.ts.map +1 -1
- package/dist/core/codegen/slow-path.js +3 -1
- package/dist/core/codegen/slow-path.js.map +1 -1
- package/dist/core/extract/extractors/custom.d.ts +12 -0
- package/dist/core/extract/extractors/custom.d.ts.map +1 -0
- package/dist/core/extract/extractors/custom.js +35 -0
- package/dist/core/extract/extractors/custom.js.map +1 -0
- package/dist/core/extract/registry.d.ts +2 -0
- package/dist/core/extract/registry.d.ts.map +1 -1
- package/dist/core/extract/registry.js +2 -0
- package/dist/core/extract/registry.js.map +1 -1
- package/dist/core/extract/types.d.ts +7 -1
- package/dist/core/extract/types.d.ts.map +1 -1
- package/dist/core/iife.d.ts +3 -4
- package/dist/core/iife.d.ts.map +1 -1
- package/dist/core/iife.js +3 -4
- package/dist/core/iife.js.map +1 -1
- package/dist/core/types.d.ts +17 -2
- package/dist/core/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -356,7 +356,8 @@ npx zod-compiler check src/schemas.ts --json --fail-under 80
|
|
|
356
356
|
Every Zod type except the fallbacks below — all primitives, `object` / `strictObject` / `looseObject`,
|
|
357
357
|
`array`, `tuple`, `record`, `set`, `map`, `union`, `discriminatedUnion`, `intersection`, `pipe`,
|
|
358
358
|
the `optional` / `nullable` / `readonly` / `default` / `catch` / `coerce` wrappers, `templateLiteral`,
|
|
359
|
-
recursive `lazy` (self, mutual and nested),
|
|
359
|
+
recursive `lazy` (self, mutual and nested), `custom` / `instanceof`, and
|
|
360
|
+
`transform` / `refine` / `superRefine`.
|
|
360
361
|
|
|
361
362
|
All standard checks are supported: `min`, `max`, `length`, `email`, `uuid`, `regex`, `int`, `positive`,
|
|
362
363
|
`multipleOf`, `includes`, `startsWith`, and the rest.
|
|
@@ -368,8 +369,7 @@ A schema delegates to Zod when it reaches JavaScript the generated code cannot r
|
|
|
368
369
|
| Construct | Why |
|
|
369
370
|
| ------------------------------------------- | -------------------------------------------------------------------------- |
|
|
370
371
|
| `.check(fn)`, `superRefine` + later checks | The callback holds Zod's payload unmediated, or `fatal` aborts Zod's chain |
|
|
371
|
-
| `ctx`-taking or `async`
|
|
372
|
-
| `z.custom()`, `z.instanceof()` | No extractor yet |
|
|
372
|
+
| `ctx`-taking or `async` callbacks | Needs Zod's parse context / the async pipeline |
|
|
373
373
|
| `z.url()`, `z.jwt()` | Algorithmic formats (`new URL()`, signature parsing) |
|
|
374
374
|
| Object intersections | Zod parses both sides and merges; the compiler cannot reproduce the merge |
|
|
375
375
|
| Dynamic error maps, unresolvable `z.lazy()` | Not knowable at build time |
|
|
@@ -436,6 +436,10 @@ Schema-level `error` and `z.config()` maps are unaffected; for a per-call map us
|
|
|
436
436
|
| object with superRefine (cross-field) | 1.6M | 2.3M | **11.6M** | — | — | 5.0x |
|
|
437
437
|
| coerced query object (valid) | 1.9M | 2.3M | **5.2M** | — | — | 2.2x |
|
|
438
438
|
| coerced query object (invalid) | 1.1M | 162K | **10.1M** | — | — | **62x** |
|
|
439
|
+
| stringbool config object (valid) | — | 1.9M | **6.0M** | — | — | 3.1x |
|
|
440
|
+
| stringbool config object (invalid) | — | 129K | **13.1M** | — | — | **101x** |
|
|
441
|
+
| custom/instanceof request (valid) | 894K | 3.0M | **8.6M** | — | — | 2.9x |
|
|
442
|
+
| custom/instanceof request (invalid) | 755K | 158K | **8.5M** | — | — | **54x** |
|
|
439
443
|
|
|
440
444
|
_ops/s, higher is better. `vitest bench` on an Apple M4 Max (zod 4.3.6, zod v3 3.23.8, typia 12, ajv 8),
|
|
441
445
|
best of three runs. The harness costs ~55 ns per iteration, so the fastest rows sit at that floor and gaps
|
|
@@ -458,8 +462,9 @@ idioms (array size checks, `.refine()`, `.default()`, `.trim()`, `.transform()`)
|
|
|
458
462
|
|
|
459
463
|
Regexes are pre-compiled with bounded repeats unrolled, checks run cheapest-first, discriminated unions
|
|
460
464
|
dispatch through a jump table (plain tagged unions are auto-discriminated into it), and oversized check
|
|
461
|
-
functions are split to stay within V8's optimizer budget. Stripping objects, native coercions,
|
|
462
|
-
string rewrites and synchronous transforms validate and build their output in
|
|
465
|
+
functions are split to stay within V8's optimizer budget. Stripping objects, native coercions,
|
|
466
|
+
`stringbool`, defaults, string rewrites and synchronous transforms validate and build their output in
|
|
467
|
+
one pass.
|
|
463
468
|
|
|
464
469
|
## Development
|
|
465
470
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-path.d.ts","names":[],"sources":["../../../src/core/codegen/build-path.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"build-path.d.ts","names":[],"sources":["../../../src/core/codegen/build-path.ts"],"mappings":";;;;iBAmJgB,eAAe,IAAI;;;;;;iBAkGnB,cAAc,IAAI,UAAU,KAAK"}
|
|
@@ -4,13 +4,14 @@ import { defaultValueExpr, needsPostInnerDefault } from "./schemas/default.js";
|
|
|
4
4
|
import { estimateFastCost, predictedInlineSize } from "./fast-size.js";
|
|
5
5
|
import { innerAppliesDefaultOnUndefined } from "./schemas/optional.js";
|
|
6
6
|
import { fastStringCheck } from "./schemas/string.js";
|
|
7
|
+
import { emitStringBoolMap, stringBoolUsesInline } from "./schemas/string-bool.js";
|
|
7
8
|
import { createFastGen, generateFast } from "./fast-path.js";
|
|
8
9
|
//#region src/core/codegen/build-path.ts
|
|
9
10
|
/**
|
|
10
11
|
* Which nodes of `root` produce a value that is not their input — a stripping
|
|
11
|
-
* object, coercion, default, overwrite or transform, including
|
|
12
|
-
* contain one. Everything else can be validated in place and
|
|
13
|
-
* which is what keeps this generator small.
|
|
12
|
+
* object, coercion, codec, default, overwrite or transform, including
|
|
13
|
+
* containers that contain one. Everything else can be validated in place and
|
|
14
|
+
* passed through, which is what keeps this generator small.
|
|
14
15
|
*
|
|
15
16
|
* Computed as a fixpoint rather than a plain walk because of recursion: a
|
|
16
17
|
* `recursiveRef` is a back-edge with no children, so a local walk reads false
|
|
@@ -37,7 +38,7 @@ function rebuildSet(root) {
|
|
|
37
38
|
for (const node of nodes) {
|
|
38
39
|
if (rebuilds.has(node)) continue;
|
|
39
40
|
const target = node.type === "recursiveRef" ? targets.get(node.refId ?? 0) : void 0;
|
|
40
|
-
if (node.type === "object" && node.stripUnknownKeys === true || node.type === "default" || node.type === "string" && (node.coerce === true || node.checks.some((c) => c.kind === "overwrite_effect")) || (node.type === "number" || node.type === "boolean" || node.type === "bigint" || node.type === "date") && node.coerce === true || node.type === "effect" || target !== void 0 && rebuilds.has(target) || children(node).some((child) => rebuilds.has(child))) {
|
|
41
|
+
if (node.type === "object" && node.stripUnknownKeys === true || node.type === "default" || node.type === "stringBool" || node.type === "string" && (node.coerce === true || node.checks.some((c) => c.kind === "overwrite_effect")) || (node.type === "number" || node.type === "boolean" || node.type === "bigint" || node.type === "date") && node.coerce === true || node.type === "effect" || target !== void 0 && rebuilds.has(target) || children(node).some((child) => rebuilds.has(child))) {
|
|
41
42
|
rebuilds.add(node);
|
|
42
43
|
changed = true;
|
|
43
44
|
}
|
|
@@ -52,9 +53,9 @@ function rebuildsOutput(ir) {
|
|
|
52
53
|
/**
|
|
53
54
|
* True when the subtree mutates for any reason the build pass cannot reproduce —
|
|
54
55
|
* `.catch()`, `z.url()`, `superRefine`. Those rewrite values in ways this pass
|
|
55
|
-
* (which validates, coerces,
|
|
56
|
-
* string rewrites and copies) does not model, so the
|
|
57
|
-
* walk.
|
|
56
|
+
* (which validates, coerces, decodes string booleans, substitutes declared
|
|
57
|
+
* defaults, applies ordered string rewrites and copies) does not model, so the
|
|
58
|
+
* schema keeps the eager walk.
|
|
58
59
|
*/
|
|
59
60
|
function mutatesBeyondStrip(ir) {
|
|
60
61
|
return mutatesHere(ir) || children(ir).some(mutatesBeyondStrip);
|
|
@@ -72,8 +73,7 @@ function mutatesHere(ir) {
|
|
|
72
73
|
case "bigint":
|
|
73
74
|
case "date": return false;
|
|
74
75
|
case "catch":
|
|
75
|
-
case "fallback":
|
|
76
|
-
case "stringBool": return true;
|
|
76
|
+
case "fallback": return true;
|
|
77
77
|
case "object":
|
|
78
78
|
case "array": return superRefines(ir.checks);
|
|
79
79
|
default: return false;
|
|
@@ -199,6 +199,7 @@ function buildInline(ir, input, g) {
|
|
|
199
199
|
case "nullable": return buildSentinel(ir.inner, input, g, "===null", "null");
|
|
200
200
|
case "default": return buildDefault(ir, input, g);
|
|
201
201
|
case "string": return buildString(ir, input, g);
|
|
202
|
+
case "stringBool": return buildStringBool(ir, input, g);
|
|
202
203
|
case "number":
|
|
203
204
|
case "boolean":
|
|
204
205
|
case "bigint":
|
|
@@ -470,6 +471,32 @@ function buildString(ir, input, g) {
|
|
|
470
471
|
};
|
|
471
472
|
}
|
|
472
473
|
/**
|
|
474
|
+
* `z.stringbool()`: normalize once, select the declared truthy/falsy side, and
|
|
475
|
+
* return the boolean directly. The ordinary Fast Path cannot host this codec
|
|
476
|
+
* because its contract returns the original input by reference; the build path
|
|
477
|
+
* is designed for exactly this kind of small output rewrite.
|
|
478
|
+
*/
|
|
479
|
+
function buildStringBool(ir, input, g) {
|
|
480
|
+
let code = `if(typeof ${input}!=="string")return ${g.fail};`;
|
|
481
|
+
let normalized = input;
|
|
482
|
+
if (!ir.caseSensitive) {
|
|
483
|
+
normalized = local(g, "bn");
|
|
484
|
+
code += `${normalized}=${input}.toLowerCase();`;
|
|
485
|
+
}
|
|
486
|
+
const out = local(g, "bb");
|
|
487
|
+
if (stringBoolUsesInline(ir)) {
|
|
488
|
+
const membership = (values) => values.map((value) => `${normalized}===${escapeString(value)}`).join("||");
|
|
489
|
+
code += `if(${membership(ir.truthy)}){${out}=true;}else if(${membership(ir.falsy)}){${out}=false;}else{return ${g.fail};}`;
|
|
490
|
+
} else {
|
|
491
|
+
const lookup = emitStringBoolMap(ir, g.ctx);
|
|
492
|
+
code += `${out}=${lookup}.get(${normalized});if(${out}===undefined)return ${g.fail};`;
|
|
493
|
+
}
|
|
494
|
+
return {
|
|
495
|
+
code,
|
|
496
|
+
value: out
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
473
500
|
* Coerce once into a local, then reuse the ordinary Fast Path as the acceptance
|
|
474
501
|
* predicate over the converted value. The build path only needs a verdict on
|
|
475
502
|
* its hot pass; if it fails, the existing deferred slow walk reruns the original
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-path.js","names":[],"sources":["../../../src/core/codegen/build-path.ts"],"sourcesContent":["/**\n * Build Path: one uninstrumented pass that VALIDATES and BUILDS rewritten\n * output together, abandoning the whole parse at the first failing check.\n *\n * `z.object()` strips unknown keys, so a successful parse cannot return the\n * input by reference — it must produce a fresh object. That rules out the Fast\n * Path (whose contract is `data === input`), and before this the only remaining\n * option was the eager slow walk: a fully instrumented traversal that collects\n * issues on every parse, valid or not.\n *\n * Two passes are wasteful in either direction. Validating first and building\n * afterwards reads every property twice (measured 29.7 ns vs 21.6 for the slow\n * walk on a 6-field object). Building with issue collection pays the\n * instrumentation even when nothing fails. Doing both in ONE pass, with a\n * sentinel instead of an issues array, beats both — and a failure costs only\n * the checks up to the first bad one, because the issue-producing walk is\n * deferred into `.error` exactly as `__zcFinD` does for mutation-free schemas:\n *\n * object clean object invalid array(8) invalid\n * slow walk 21.6 ns 30.4 ns 197.5 ns\n * build path 18.1 ns 7.7 ns 9.8 ns\n *\n * A subtree that rebuilds nothing is validated with its existing Fast Path\n * expression and passed through by reference, so only nodes that genuinely\n * produce a new value need code here; anything else returns null and keeps the\n * eager walk.\n *\n * Coverage is what decides whether this pass is reached at all, because it is\n * all-or-nothing per schema: ONE unmodelled node anywhere in the tree costs the\n * whole schema its single-pass parse. Modelled, beyond the stripping containers\n * this started with: array size checks and `.refine()`, object-level `.refine()`,\n * `.default()` substitution, ordered string rewrites (`.trim()`,\n * `.toLowerCase()`), sync `.transform()`, and the five native coercions\n * (`string`, `number`, `boolean`, `bigint`, `date`). Still declined, via\n * {@link mutatesBeyondStrip} — `.catch()` (its callback wants the inner schema's\n * issue list, which this pass never builds), `z.url()`, and `superRefine`.\n */\n\nimport type { ObjectIR, RefineEffectCheckIR, SchemaIR } from \"../types.js\";\nimport type { CodeGenContext, FastScope } from \"./context.js\";\nimport {\n declareFastTemps,\n emitEffectCallable,\n emitEffectFn,\n emitRuntimeHelper,\n emitTemp,\n escapeString,\n keyMembershipTest,\n outputAlwaysDefined,\n rejectsUndefined,\n} from \"./context.js\";\nimport { createFastGen, generateFast } from \"./fast-path.js\";\nimport { EXTRACT_CAP, estimateFastCost, MIN_EXTRACT, predictedInlineSize } from \"./fast-size.js\";\nimport { ZC_HOP_DECL } from \"./issue-decls.js\";\nimport { defaultValueExpr, needsPostInnerDefault } from \"./schemas/default.js\";\nimport { innerAppliesDefaultOnUndefined } from \"./schemas/optional.js\";\nimport { fastStringCheck } from \"./schemas/string.js\";\n\n/** Statements that leave the built value in `value`, or `return <FAIL>` on failure. */\ninterface Built {\n code: string;\n value: string;\n}\n\ninterface BuildGen {\n ctx: CodeGenContext;\n /** Identifier of the per-validator FAIL sentinel. */\n fail: string;\n /** `var` temps and running emitted size of the function being assembled. */\n scope: FastScope;\n /**\n * May THIS node be hosted in its own function? False for the node a hosted\n * build was created for — it already IS that function, so re-hosting it would\n * recurse forever. Children are always extractable, letting an oversized\n * helper split further.\n */\n extractable: boolean;\n /** Nodes of the root schema that rebuild (see rebuildSet). */\n rebuilds: ReadonlySet<SchemaIR>;\n}\n\n/**\n * Which nodes of `root` produce a value that is not their input — a stripping\n * object, coercion, default, overwrite or transform, including containers that\n * contain one. Everything else can be validated in place and passed through,\n * which is what keeps this generator small.\n *\n * Computed as a fixpoint rather than a plain walk because of recursion: a\n * `recursiveRef` is a back-edge with no children, so a local walk reads false\n * for it and would pass the whole recursive subtree through by reference —\n * leaving every nested value unstripped while the outermost one was rebuilt.\n * Resolving the ref against its target closes the cycle, and iterating to a\n * fixpoint settles the mutual dependency between the two.\n */\nfunction rebuildSet(root: SchemaIR): ReadonlySet<SchemaIR> {\n const targets = new Map<number, SchemaIR>([[0, root]]);\n const nodes: SchemaIR[] = [];\n const seen = new Set<SchemaIR>();\n const collect = (node: SchemaIR): void => {\n if (seen.has(node)) return;\n seen.add(node);\n nodes.push(node);\n if (node.type === \"recursionTarget\") targets.set(node.refId, node.inner);\n for (const child of children(node)) collect(child);\n };\n collect(root);\n\n const rebuilds = new Set<SchemaIR>();\n for (let changed = true; changed;) {\n changed = false;\n for (const node of nodes) {\n if (rebuilds.has(node)) continue;\n const target = node.type === \"recursiveRef\" ? targets.get(node.refId ?? 0) : undefined;\n const rebuild =\n (node.type === \"object\" && node.stripUnknownKeys === true) ||\n // `.default()` substitutes its own value for `undefined`, so its output\n // is not its input even when the inner schema passes through — it must\n // never be handed to `passthrough`, whose fast check would reject the\n // absent value outright.\n node.type === \"default\" ||\n // An overwrite effect (`.trim()`, `.toLowerCase()`) rewrites the string,\n // so the node's output is a new value: it has to be BUILT rather than\n // validated in place (see buildString).\n (node.type === \"string\" &&\n (node.coerce === true || node.checks.some((c) => c.kind === \"overwrite_effect\"))) ||\n ((node.type === \"number\" ||\n node.type === \"boolean\" ||\n node.type === \"bigint\" ||\n node.type === \"date\") &&\n node.coerce === true) ||\n // `.transform(fn)` replaces the value with the callback's result.\n node.type === \"effect\" ||\n (target !== undefined && rebuilds.has(target)) ||\n children(node).some((child) => rebuilds.has(child));\n if (rebuild) {\n rebuilds.add(node);\n changed = true;\n }\n }\n }\n return rebuilds;\n}\n\n/** Does `ir`, taken as a whole schema, produce a value that is not its input? */\nexport function rebuildsOutput(ir: SchemaIR): boolean {\n return rebuildSet(ir).has(ir);\n}\n\n/**\n * True when the subtree mutates for any reason the build pass cannot reproduce —\n * `.catch()`, `z.url()`, `superRefine`. Those rewrite values in ways this pass\n * (which validates, coerces, substitutes declared defaults, applies ordered\n * string rewrites and copies) does not model, so the schema keeps the eager\n * walk.\n */\nfunction mutatesBeyondStrip(ir: SchemaIR): boolean {\n return mutatesHere(ir) || children(ir).some(mutatesBeyondStrip);\n}\n\n/**\n * Does this node rewrite values on its own account (ignoring its children, and\n * ignoring the reshaping a strip object does)? Mirrors the node-local half of\n * `hasMutation`; the recursion above supplies the other half.\n */\nfunction mutatesHere(ir: SchemaIR): boolean {\n switch (ir.type) {\n case \"string\":\n // Coercion and overwrite effects are absent: `buildString` applies them\n // in order. A `z.url()` check still is not — it trims, normalizes and\n // needs its own normalization/error semantics.\n return (\n superRefines(ir.checks) ||\n ir.checks.some((c) => c.kind === \"string_format\" && c.format === \"url\")\n );\n case \"number\":\n return superRefines(ir.checks);\n case \"boolean\":\n case \"bigint\":\n case \"date\":\n return false;\n // `default` and `effect` are absent: substituting a constant for `undefined`\n // and applying a sync transform are both modelled (see buildDefault /\n // buildEffect), and their inners are reached through `children`.\n //\n // `catch` is NOT: its catchValue callback receives a ctx carrying the inner\n // schema's collected issues, and this pass produces a sentinel instead of an\n // issue list — there is nothing to hand it.\n case \"catch\":\n case \"fallback\":\n case \"stringBool\":\n return true;\n case \"object\":\n case \"array\":\n return superRefines(ir.checks);\n default:\n return false;\n }\n}\n\nfunction superRefines(checks: readonly { kind: string }[] | undefined): boolean {\n return checks !== undefined && checks.some((c) => c.kind === \"super_refine_effect\");\n}\n\nfunction children(ir: SchemaIR): readonly SchemaIR[] {\n switch (ir.type) {\n case \"object\":\n return ir.catchall\n ? [...Object.values(ir.properties), ir.catchall]\n : Object.values(ir.properties);\n case \"array\":\n return [ir.element];\n case \"tuple\":\n return ir.rest === null ? ir.items : [...ir.items, ir.rest];\n case \"record\":\n case \"map\":\n return [ir.keyType, ir.valueType];\n case \"set\":\n return [ir.valueType];\n case \"union\":\n case \"discriminatedUnion\":\n return ir.options;\n case \"intersection\":\n return [ir.left, ir.right];\n case \"optional\":\n case \"nullable\":\n case \"readonly\":\n case \"default\":\n case \"catch\":\n case \"effect\":\n case \"recursionTarget\":\n return [ir.inner];\n case \"pipe\":\n return [ir.in, ir.out];\n default:\n return [];\n }\n}\n\n/**\n * Host the whole schema as `function NAME(input){…}` returning the built value\n * or the FAIL sentinel. Returns the function name, or null when the schema is\n * not expressible as a single build pass.\n */\nexport function generateBuild(ir: SchemaIR, ctx: CodeGenContext): string | null {\n const rebuilds = rebuildSet(ir);\n if (!rebuilds.has(ir) || mutatesBeyondStrip(ir)) return null;\n const fail = emitFailSentinel(ctx);\n const scope: FastScope = { temps: [], used: 0 };\n const built = build(ir, \"input\", { ctx, extractable: false, fail, rebuilds, scope });\n if (built === null) return null;\n const name = emitTemp(ctx, \"vb\");\n ctx.preamble.push(\n `function ${name}(input){${declareFastTemps(scope)}${built.code}return ${built.value};}`,\n );\n return name;\n}\n\n/** One `{}` per validator, compared by identity — no parse output can equal it. */\nfunction emitFailSentinel(ctx: CodeGenContext): string {\n if (ctx.buildFailName === undefined) {\n ctx.buildFailName = `__bf_${ctx.counter++}`;\n ctx.preamble.push(`var ${ctx.buildFailName}={};`);\n }\n return ctx.buildFailName;\n}\n\n/**\n * Statements producing the built value of `ir` read from `input`, or null.\n *\n * Size-gated exactly like the fast path: once inlining `ir` would push the\n * function being assembled past EXTRACT_CAP, the sub-build is hosted as its own\n * `__vb_N(p)` returning value-or-FAIL and replaced by a call. Without this a\n * deeply nested schema emits one enormous build function — measured at 113 KB\n * and 354 KB on the deep fixtures — far past the bytecode size where V8 stops\n * running TurboFan on it, which would forfeit the speed this path exists for.\n */\nfunction build(ir: SchemaIR, input: string, g: BuildGen): Built | null {\n // Resolved before the passthrough shortcut below. A back-edge carries no\n // children, so `rebuildsOutput` reads false for it — and passing it through by\n // reference would leave every nested recursive value unstripped while the\n // outermost one was rebuilt.\n if (ir.type === \"recursiveRef\") return buildRecursiveCall(ir.refId ?? 0, input, g);\n if (ir.type === \"recursionTarget\") return buildRecursionTarget(ir, input, g);\n if (!g.rebuilds.has(ir)) return passthrough(ir, input, g);\n\n const cache = (g.ctx.fastSizeCache ??= new WeakMap<SchemaIR, number>());\n if (\n g.extractable &&\n g.scope.used + predictedInlineSize(ir, input.length, cache) > EXTRACT_CAP &&\n (g.scope.used > EXTRACT_CAP || estimateFastCost(ir, cache) >= MIN_EXTRACT)\n ) {\n const hosted = hostBuild(ir, g);\n if (hosted !== null) {\n const slot = local(g, \"bh\");\n const code = `${slot}=${hosted}(${input});if(${slot}===${g.fail})return ${g.fail};`;\n g.scope.used += code.length;\n return { code, value: slot };\n }\n }\n\n // This node's extraction decision is made; its descendants get to make their\n // own, so an oversized hosted helper keeps splitting.\n const before = g.scope.used;\n const out = buildInline(ir, input, { ...g, extractable: true });\n if (out !== null) g.scope.used = before + out.code.length;\n return out;\n}\n\n/** Host `ir`'s build in its own function over a fresh parameter; returns its name. */\nfunction hostBuild(ir: SchemaIR, g: BuildGen): string | null {\n const param = emitTemp(g.ctx, \"bp\");\n const scope: FastScope = { temps: [], used: 0 };\n const inner = build(ir, param, { ...g, extractable: false, scope });\n if (inner === null) return null;\n const name = emitTemp(g.ctx, \"vb\");\n g.ctx.preamble.push(\n `function ${name}(${param}){${declareFastTemps(scope)}${inner.code}return ${inner.value};}`,\n );\n return name;\n}\n\nfunction buildInline(ir: SchemaIR, input: string, g: BuildGen): Built | null {\n switch (ir.type) {\n case \"object\":\n return buildObject(ir, input, g);\n case \"array\":\n return buildArray(ir, input, g);\n case \"tuple\":\n return buildTuple(ir, input, g);\n case \"record\":\n return buildRecord(ir, input, g);\n case \"optional\":\n // A default further down the chain consumes undefined into a value, so\n // the `undefined → undefined` shortcut must not fire — same rule (and\n // same helper) the slow and fast paths already apply.\n return innerAppliesDefaultOnUndefined(ir.inner)\n ? build(ir.inner, input, g)\n : buildSentinel(ir.inner, input, g, \"===undefined\", \"undefined\");\n case \"nullable\":\n // `null` short-circuits unconditionally in zod, whatever the inner is;\n // undefined flows through, so an inner default still fires.\n return buildSentinel(ir.inner, input, g, \"===null\", \"null\");\n case \"default\":\n return buildDefault(ir, input, g);\n case \"string\":\n return buildString(ir, input, g);\n case \"number\":\n case \"boolean\":\n case \"bigint\":\n case \"date\":\n return buildCoercedPrimitive(ir, input, g);\n case \"effect\":\n return buildEffect(ir, input, g);\n case \"readonly\":\n return build(ir.inner, input, g);\n case \"union\":\n case \"discriminatedUnion\":\n return buildUnion(ir, input, g);\n default:\n // A rebuilding intersection, map or set: expressible in principle, but\n // each needs its own output-shaping rules, so they keep the eager walk\n // until there is a measured reason to add them.\n return null;\n }\n}\n\n/**\n * Host the target's build once under a name registered BEFORE its body is\n * generated, so the back-edges inside that body resolve to it.\n */\nfunction buildRecursionTarget(\n ir: SchemaIR & { type: \"recursionTarget\" },\n input: string,\n g: BuildGen,\n): Built | null {\n const table = (g.ctx.buildRecNames ??= new Map<number, string>());\n if (!table.has(ir.refId)) {\n const name = emitTemp(g.ctx, \"vbr\");\n table.set(ir.refId, name);\n const param = emitTemp(g.ctx, \"bp\");\n const scope: FastScope = { temps: [], used: 0 };\n const inner = build(ir.inner, param, { ...g, extractable: false, scope });\n if (inner === null) {\n table.delete(ir.refId);\n return null;\n }\n g.ctx.preamble.push(\n `function ${name}(${param}){${declareFastTemps(scope)}${inner.code}return ${inner.value};}`,\n );\n }\n return buildRecursiveCall(ir.refId, input, g);\n}\n\n/** Call the hosted build for a recursion target, propagating its FAIL. */\nfunction buildRecursiveCall(refId: number, input: string, g: BuildGen): Built | null {\n const name = g.ctx.buildRecNames?.get(refId);\n if (name === undefined) return null;\n const slot = local(g, \"bh\");\n return {\n code: `${slot}=${name}(${input});if(${slot}===${g.fail})return ${g.fail};`,\n value: slot,\n };\n}\n\n/**\n * Try each option in declaration order and take the first that builds — which\n * is what zod's union does with the first option that parses.\n *\n * Every option is HOSTED rather than inlined, and that is load-bearing: a\n * failing build signals with `return FAIL`, which inside the enclosing function\n * would abandon the whole parse instead of moving on to the next option. Behind\n * a call, the same signal is just a value to test.\n *\n * A discriminated union gets the same treatment rather than a switch: the\n * options still have to be probed by call, and its dispatch advantage is\n * already spent by the enclosing container's own work.\n */\nfunction buildUnion(\n ir: SchemaIR & { type: \"discriminatedUnion\" | \"union\" },\n input: string,\n g: BuildGen,\n): Built | null {\n const hosted: string[] = [];\n for (const option of ir.options) {\n const fn = g.rebuilds.has(option) ? hostBuild(option, g) : hostPassthrough(option, g);\n if (fn === null) return null;\n hosted.push(fn);\n }\n if (hosted.length === 0) return null;\n\n const out = local(g, \"bu\");\n let code = `${out}=${hosted[0] as string}(${input});`;\n for (const fn of hosted.slice(1)) {\n code += `if(${out}===${g.fail}){${out}=${fn}(${input});}`;\n }\n code += `if(${out}===${g.fail})return ${g.fail};`;\n return { code, value: out };\n}\n\n/**\n * Host a non-rebuilding option as `value-or-FAIL`, so a union can probe it with\n * the same protocol as a rebuilding one.\n */\nfunction hostPassthrough(ir: SchemaIR, g: BuildGen): string | null {\n const param = emitTemp(g.ctx, \"bp\");\n const scope: FastScope = { temps: [], used: 0 };\n const expr = generateFast(ir, createFastGen(param, g.ctx, true, scope));\n if (expr === null) return null;\n const name = emitTemp(g.ctx, \"vp\");\n g.ctx.preamble.push(\n `function ${name}(${param}){${declareFastTemps(scope)}return ${expr === \"true\" ? param : `(${expr})?${param}:${g.fail}`};}`,\n );\n return name;\n}\n\n/** Validate in place with the Fast Path and hand the input straight back. */\nfunction passthrough(ir: SchemaIR, input: string, g: BuildGen): Built | null {\n const scoped = createFastGen(input, g.ctx, true, g.scope);\n const expr = generateFast(ir, scoped);\n if (expr === null) return null;\n return { code: expr === \"true\" ? \"\" : `if(!(${expr}))return ${g.fail};`, value: input };\n}\n\n/**\n * Rebuild from the declared keys. Sound for a stripping object (that IS the\n * output) and for a strict one (unknown keys are rejected, so the declared keys\n * are the whole key set). A loose object or one with a `.catchall()` keeps keys\n * this pass does not enumerate, so those bail.\n */\nfunction buildObject(ir: ObjectIR, input: string, g: BuildGen): Built | null {\n if (ir.catchall !== undefined) return null;\n if (ir.stripUnknownKeys !== true && ir.strict !== true) return null;\n if (ir.suppressAbsentKeys !== undefined && ir.suppressAbsentKeys.length > 0) return null;\n // Object-level `.refine()` runs on the assembled output (below). superRefine\n // rewrites the payload, which this pass does not model — mutatesBeyondStrip\n // already rejects it, so this is a belt-and-braces narrowing of the type.\n const refines = ir.checks ?? [];\n if (refines.some((check) => check.kind !== \"refine_effect\")) return null;\n\n let code = `if(typeof ${input}!==\"object\"||${input}===null||Array.isArray(${input}))return ${g.fail};`;\n\n if (ir.strict === true) {\n const keyVar = local(g, \"bk\");\n code += `for(${keyVar} in ${input}){if(!(${keyMembershipTest(g.ctx, Object.keys(ir.properties), keyVar)}))return ${g.fail};}`;\n }\n\n const slots: { always: boolean; keyStr: string; value: string }[] = [];\n for (const [key, propIR] of Object.entries(ir.properties)) {\n const keyStr = escapeString(key);\n const slot = local(g, \"bv\");\n code += `${slot}=${input}[${keyStr}];`;\n const propBuilt = build(propIR, slot, g);\n if (propBuilt === null) return null;\n code += propBuilt.code;\n slots.push({ always: outputAlwaysDefined(propIR), keyStr, value: propBuilt.value });\n }\n\n // Same assembly the eager strip walk uses: the longest LEADING run of\n // always-present keys goes into one object literal (V8 stamps it from a\n // cached boilerplate map in a single allocation), and everything after the\n // first conditional key is appended so insertion order still matches zod.\n // The per-key test is zod's own — keep the key when the parsed value is\n // defined, or when it was present on the input at all.\n const out = local(g, \"bo\");\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 ? `${out}[${slot.keyStr}]=${slot.value};`\n : `if(${slot.value}!==undefined||(${slot.keyStr} in ${input})){${out}[${slot.keyStr}]=${slot.value};}`;\n }\n code += `${out}={${literal.join(\",\")}};${appends}`;\n // Zod parses the properties into the payload first and skips the check chain\n // when that produced issues, so a bad property suppresses the refine — which\n // this pass gets for free, having already returned FAIL at that property.\n for (const check of refines) {\n code += `if(!${emitEffectCallable(g.ctx, check as RefineEffectCheckIR)}(${out}))return ${g.fail};`;\n }\n return { code, value: out };\n}\n\nfunction buildArray(ir: SchemaIR & { type: \"array\" }, input: string, g: BuildGen): Built | null {\n // Length checks are pure predicates over `input.length`, so they hoist ahead\n // of the element loop: a size mismatch bails before a single element is\n // validated. Zod reports the per-element issue first when both fail, but the\n // build pass produces no issues — only the sentinel — and the deferred walk\n // that does produce them keeps zod's order.\n let sizes = \"\";\n const refines: RefineEffectCheckIR[] = [];\n for (const check of ir.checks) {\n switch (check.kind) {\n case \"min_length\":\n sizes += `if(${input}.length<${check.minimum})return ${g.fail};`;\n break;\n case \"max_length\":\n sizes += `if(${input}.length>${check.maximum})return ${g.fail};`;\n break;\n case \"length_equals\":\n sizes += `if(${input}.length!==${check.length})return ${g.fail};`;\n break;\n case \"refine_effect\":\n refines.push(check);\n break;\n default:\n // super_refine (rewrites the value) or a check kind not modelled here.\n return null;\n }\n }\n\n const out = local(g, \"ba\");\n const index = local(g, \"bi\");\n const elem = local(g, \"be\");\n const inner = build(ir.element, elem, g);\n if (inner === null) return null;\n let code =\n `if(!Array.isArray(${input}))return ${g.fail};` +\n sizes +\n `${out}=new Array(${input}.length);` +\n `for(${index}=0;${index}<${input}.length;${index}++){` +\n `${elem}=${input}[${index}];${inner.code}${out}[${index}]=${inner.value};}`;\n // `.refine()` sees the parsed payload, which for a rebuilding element is the\n // freshly assembled array — the same value zod hands its checks.\n for (const check of refines) {\n code += `if(!${emitEffectCallable(g.ctx, check)}(${out}))return ${g.fail};`;\n }\n return { code, value: out };\n}\n\nfunction buildTuple(ir: SchemaIR & { type: \"tuple\" }, input: string, g: BuildGen): Built | null {\n // Trailing-optional and rest handling shape the output length; keep those on\n // the eager walk rather than restating the rules here.\n if (ir.rest !== null) return null;\n if (ir.items.some((item) => !rejectsUndefined(item))) return null;\n\n let code = `if(!Array.isArray(${input})||${input}.length!==${ir.items.length})return ${g.fail};`;\n const values: string[] = [];\n for (const [index, itemIR] of ir.items.entries()) {\n const slot = local(g, \"bt\");\n code += `${slot}=${input}[${index}];`;\n const inner = build(itemIR as SchemaIR, slot, g);\n if (inner === null) return null;\n code += inner.code;\n values.push(inner.value);\n }\n const out = local(g, \"bl\");\n code += `${out}=[${values.join(\",\")}];`;\n return { code, value: out };\n}\n\nfunction buildRecord(ir: SchemaIR & { type: \"record\" }, input: string, g: BuildGen): Built | null {\n const plainStringKey =\n ir.keyType.type === \"string\" && ir.keyType.checks.length === 0 && ir.keyType.coerce !== true;\n if (!plainStringKey) return null;\n\n const out = local(g, \"br\");\n const keyVar = local(g, \"brk\");\n const valVar = local(g, \"brv\");\n const inner = build(ir.valueType, valVar, g);\n if (inner === null) return null;\n const hop = emitRuntimeHelper(g.ctx, \"__zcHop\", ZC_HOP_DECL);\n const code =\n `if(typeof ${input}!==\"object\"||${input}===null||Array.isArray(${input}))return ${g.fail};` +\n `${out}={};` +\n `for(${keyVar} in ${input}){if(${hop}.call(${input},${keyVar})){` +\n `${valVar}=${input}[${keyVar}];${inner.code}${out}[${keyVar}]=${inner.value};}}`;\n return { code, value: out };\n}\n\n/**\n * `.transform(fn)`: validate the inner schema, then hand its parsed value to the\n * callback. Zod's transform is a pipe whose `in` failing aborts the whole thing,\n * which is exactly what returning FAIL from the inner build does — so the\n * callback runs only on a clean parse, as `slowEffect` guarantees with its issue\n * count.\n *\n * The IR reaches here only for a synchronous single-argument callback: a\n * `ctx`-taking or async transform is extracted as a `fallback` instead\n * (see extractPipe), so there is no parse context to reproduce.\n */\nfunction buildEffect(ir: SchemaIR & { type: \"effect\" }, input: string, g: BuildGen): Built | null {\n const inner = build(ir.inner, input, g);\n if (inner === null) return null;\n const out = local(g, \"bx\");\n return {\n code: `${inner.code}${out}=${emitEffectCallable(g.ctx, ir)}(${inner.value});`,\n value: out,\n };\n}\n\n/**\n * A coercing and/or overwrite string (`z.coerce.string()`, `.trim()`,\n * `.toLowerCase()`, ...): coerce first, then emit checks one statement at a time\n * in DECLARATION order, interleaved with rewrites, because a rewrite is visible\n * to every check after it — `z.string().trim().min(1)` rejects `\" \"` where\n * `z.string().min(1).trim()` accepts it. That ordering is exactly why the fast\n * path, which sorts checks cheapest-first and returns the input unchanged, has\n * to decline these.\n *\n * Only reached for a rewriting string; a non-coercing, check-only one never\n * enters the rebuild set and is validated in place by `passthrough`.\n */\nfunction buildString(ir: SchemaIR & { type: \"string\" }, input: string, g: BuildGen): Built | null {\n const value = local(g, \"bs\");\n let code =\n ir.coerce === true\n ? `try{${value}=String(${input});}catch(_){return ${g.fail};}`\n : `if(typeof ${input}!==\"string\")return ${g.fail};${value}=${input};`;\n for (const check of ir.checks) {\n switch (check.kind) {\n case \"overwrite_effect\":\n code += `${value}=${emitEffectFn(g.ctx, check.source)}(${value});`;\n break;\n case \"refine_effect\":\n code += `if(!${emitEffectCallable(g.ctx, check)}(${value}))return ${g.fail};`;\n break;\n case \"super_refine_effect\":\n // Rewrites through zod's payload; mutatesBeyondStrip already rejects it.\n return null;\n default: {\n const expr = fastStringCheck(check, value, g.ctx);\n if (expr === null) return null; // z.url(), unknown format\n code += `if(!(${expr}))return ${g.fail};`;\n }\n }\n }\n return { code, value };\n}\n\n/** Primitive nodes whose `coerce` flag rewrites their output before checks run. */\ntype CoercedPrimitiveIR = Extract<SchemaIR, { type: \"number\" | \"boolean\" | \"bigint\" | \"date\" }>;\n\n/**\n * Coerce once into a local, then reuse the ordinary Fast Path as the acceptance\n * predicate over the converted value. The build path only needs a verdict on\n * its hot pass; if it fails, the existing deferred slow walk reruns the original\n * coercing schema and produces Zod-identical issues.\n *\n * Number/BigInt/Date conversion can invoke user hooks and throw. Zod catches\n * those throws and reports invalid_type, so the sentinel branch does the same\n * without allocating an issue. Boolean never invokes conversion hooks.\n */\nfunction buildCoercedPrimitive(ir: CoercedPrimitiveIR, input: string, g: BuildGen): Built | null {\n if (ir.coerce !== true) return null;\n const value = local(g, \"bc\");\n let conversion: string;\n switch (ir.type) {\n case \"number\":\n conversion = `Number(${input})`;\n break;\n case \"boolean\":\n conversion = `Boolean(${input})`;\n break;\n case \"bigint\":\n conversion = `BigInt(${input})`;\n break;\n case \"date\":\n conversion = `new Date(${input})`;\n break;\n }\n\n // A fresh shallow node is intentional: only the coerce flag changes. The\n // existing primitive generator remains the single source of truth for every\n // range, format, refine and finite/valid-date check.\n const predicate = generateFast(\n { ...ir, coerce: false },\n createFastGen(value, g.ctx, true, g.scope),\n );\n if (predicate === null) return null;\n\n const assign = `${value}=${conversion};`;\n const code = ir.type === \"boolean\" ? assign : `try{${assign}}catch(_){return ${g.fail};}`;\n return {\n code: code + (predicate === \"true\" ? \"\" : `if(!(${predicate}))return ${g.fail};`),\n value,\n };\n}\n\n/**\n * `.default(v)`: `undefined` yields the declared value without running the\n * inner schema, anything else parses normally — the same two branches\n * `slowDefault` emits, reading the value off the retained schema so a\n * reference-typed default keeps zod's identity (one shared object, not a copy).\n *\n * The substituted value is not validated, so it makes the fast path a PARTIAL\n * predicate (`fc(undefined)` is false where the schema accepts) — which is why\n * this records {@link CodeGenContext.buildSubstitutesValue}, on which\n * `generateValidator` withholds `.is()`.\n */\nfunction buildDefault(\n ir: SchemaIR & { type: \"default\" },\n input: string,\n g: BuildGen,\n): Built | null {\n const inner = build(ir.inner, input, g);\n if (inner === null) return null;\n // Every `default` node is in the rebuild set, so it is always BUILT and never\n // passed through — which makes this flag an exact record of whether the\n // finished pass substitutes a value.\n g.ctx.buildSubstitutesValue = true;\n const out = local(g, \"bq\");\n const value = defaultValueExpr(ir);\n // Zod re-applies the default when the inner returns undefined for a defined\n // input; only emitted when the inner can actually do that.\n const reapply = needsPostInnerDefault(ir) ? `if(${out}===undefined){${out}=${value};}` : \"\";\n return {\n code:\n `if(${input}===undefined){${out}=${value};}` +\n `else{${inner.code}${out}=${inner.value};${reapply}}`,\n value: out,\n };\n}\n\n/** `optional` / `nullable` around a rebuilding inner: pass the sentinel through. */\nfunction buildSentinel(\n innerIR: SchemaIR,\n input: string,\n g: BuildGen,\n test: string,\n sentinel: string,\n): Built | null {\n const inner = build(innerIR, input, g);\n if (inner === null) return null;\n const out = local(g, \"bw\");\n return {\n code: `if(${input}${test}){${out}=${sentinel};}else{${inner.code}${out}=${inner.value};}`,\n value: out,\n };\n}\n\n/** Allocate a `var` the hosted build function declares. */\nfunction local(g: BuildGen, prefix: string): string {\n const name = emitTemp(g.ctx, prefix);\n g.scope.temps.push(name);\n return name;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA8FA,SAAS,WAAW,MAAuC;CACzD,MAAM,0BAAU,IAAI,IAAsB,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CACrD,MAAM,QAAoB,CAAC;CAC3B,MAAM,uBAAO,IAAI,IAAc;CAC/B,MAAM,WAAW,SAAyB;EACxC,IAAI,KAAK,IAAI,IAAI,GAAG;EACpB,KAAK,IAAI,IAAI;EACb,MAAM,KAAK,IAAI;EACf,IAAI,KAAK,SAAS,mBAAmB,QAAQ,IAAI,KAAK,OAAO,KAAK,KAAK;EACvE,KAAK,MAAM,SAAS,SAAS,IAAI,GAAG,QAAQ,KAAK;CACnD;CACA,QAAQ,IAAI;CAEZ,MAAM,2BAAW,IAAI,IAAc;CACnC,KAAK,IAAI,UAAU,MAAM,UAAU;EACjC,UAAU;EACV,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,SAAS,IAAI,IAAI,GAAG;GACxB,MAAM,SAAS,KAAK,SAAS,iBAAiB,QAAQ,IAAI,KAAK,SAAS,CAAC,IAAI,KAAA;GAsB7E,IApBG,KAAK,SAAS,YAAY,KAAK,qBAAqB,QAKrD,KAAK,SAAS,aAIb,KAAK,SAAS,aACZ,KAAK,WAAW,QAAQ,KAAK,OAAO,MAAM,MAAM,EAAE,SAAS,kBAAkB,OAC9E,KAAK,SAAS,YACd,KAAK,SAAS,aACd,KAAK,SAAS,YACd,KAAK,SAAS,WACd,KAAK,WAAW,QAElB,KAAK,SAAS,YACb,WAAW,KAAA,KAAa,SAAS,IAAI,MAAM,KAC5C,SAAS,IAAI,CAAC,CAAC,MAAM,UAAU,SAAS,IAAI,KAAK,CAAC,GACvC;IACX,SAAS,IAAI,IAAI;IACjB,UAAU;GACZ;EACF;CACF;CACA,OAAO;AACT;;AAGA,SAAgB,eAAe,IAAuB;CACpD,OAAO,WAAW,EAAE,CAAC,CAAC,IAAI,EAAE;AAC9B;;;;;;;;AASA,SAAS,mBAAmB,IAAuB;CACjD,OAAO,YAAY,EAAE,KAAK,SAAS,EAAE,CAAC,CAAC,KAAK,kBAAkB;AAChE;;;;;;AAOA,SAAS,YAAY,IAAuB;CAC1C,QAAQ,GAAG,MAAX;EACE,KAAK,UAIH,OACE,aAAa,GAAG,MAAM,KACtB,GAAG,OAAO,MAAM,MAAM,EAAE,SAAS,mBAAmB,EAAE,WAAW,KAAK;EAE1E,KAAK,UACH,OAAO,aAAa,GAAG,MAAM;EAC/B,KAAK;EACL,KAAK;EACL,KAAK,QACH,OAAO;EAQT,KAAK;EACL,KAAK;EACL,KAAK,cACH,OAAO;EACT,KAAK;EACL,KAAK,SACH,OAAO,aAAa,GAAG,MAAM;EAC/B,SACE,OAAO;CACX;AACF;AAEA,SAAS,aAAa,QAA0D;CAC9E,OAAO,WAAW,KAAA,KAAa,OAAO,MAAM,MAAM,EAAE,SAAS,qBAAqB;AACpF;AAEA,SAAS,SAAS,IAAmC;CACnD,QAAQ,GAAG,MAAX;EACE,KAAK,UACH,OAAO,GAAG,WACN,CAAC,GAAG,OAAO,OAAO,GAAG,UAAU,GAAG,GAAG,QAAQ,IAC7C,OAAO,OAAO,GAAG,UAAU;EACjC,KAAK,SACH,OAAO,CAAC,GAAG,OAAO;EACpB,KAAK,SACH,OAAO,GAAG,SAAS,OAAO,GAAG,QAAQ,CAAC,GAAG,GAAG,OAAO,GAAG,IAAI;EAC5D,KAAK;EACL,KAAK,OACH,OAAO,CAAC,GAAG,SAAS,GAAG,SAAS;EAClC,KAAK,OACH,OAAO,CAAC,GAAG,SAAS;EACtB,KAAK;EACL,KAAK,sBACH,OAAO,GAAG;EACZ,KAAK,gBACH,OAAO,CAAC,GAAG,MAAM,GAAG,KAAK;EAC3B,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,mBACH,OAAO,CAAC,GAAG,KAAK;EAClB,KAAK,QACH,OAAO,CAAC,GAAG,IAAI,GAAG,GAAG;EACvB,SACE,OAAO,CAAC;CACZ;AACF;;;;;;AAOA,SAAgB,cAAc,IAAc,KAAoC;CAC9E,MAAM,WAAW,WAAW,EAAE;CAC9B,IAAI,CAAC,SAAS,IAAI,EAAE,KAAK,mBAAmB,EAAE,GAAG,OAAO;CACxD,MAAM,OAAO,iBAAiB,GAAG;CACjC,MAAM,QAAmB;EAAE,OAAO,CAAC;EAAG,MAAM;CAAE;CAC9C,MAAM,QAAQ,MAAM,IAAI,SAAS;EAAE;EAAK,aAAa;EAAO;EAAM;EAAU;CAAM,CAAC;CACnF,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,OAAO,SAAS,KAAK,IAAI;CAC/B,IAAI,SAAS,KACX,YAAY,KAAK,UAAU,iBAAiB,KAAK,IAAI,MAAM,KAAK,SAAS,MAAM,MAAM,GACvF;CACA,OAAO;AACT;;AAGA,SAAS,iBAAiB,KAA6B;CACrD,IAAI,IAAI,kBAAkB,KAAA,GAAW;EACnC,IAAI,gBAAgB,QAAQ,IAAI;EAChC,IAAI,SAAS,KAAK,OAAO,IAAI,cAAc,KAAK;CAClD;CACA,OAAO,IAAI;AACb;;;;;;;;;;;AAYA,SAAS,MAAM,IAAc,OAAe,GAA2B;CAKrE,IAAI,GAAG,SAAS,gBAAgB,OAAO,mBAAmB,GAAG,SAAS,GAAG,OAAO,CAAC;CACjF,IAAI,GAAG,SAAS,mBAAmB,OAAO,qBAAqB,IAAI,OAAO,CAAC;CAC3E,IAAI,CAAC,EAAE,SAAS,IAAI,EAAE,GAAG,OAAO,YAAY,IAAI,OAAO,CAAC;CAExD,MAAM,QAAS,EAAE,IAAI,kCAAkB,IAAI,QAA0B;CACrE,IACE,EAAE,eACF,EAAE,MAAM,OAAO,oBAAoB,IAAI,MAAM,QAAQ,KAAK,IAAA,SACzD,EAAE,MAAM,OAAA,QAAsB,iBAAiB,IAAI,KAAK,KAAA,OACzD;EACA,MAAM,SAAS,UAAU,IAAI,CAAC;EAC9B,IAAI,WAAW,MAAM;GACnB,MAAM,OAAO,MAAM,GAAG,IAAI;GAC1B,MAAM,OAAO,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,OAAO,KAAK,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK;GACjF,EAAE,MAAM,QAAQ,KAAK;GACrB,OAAO;IAAE;IAAM,OAAO;GAAK;EAC7B;CACF;CAIA,MAAM,SAAS,EAAE,MAAM;CACvB,MAAM,MAAM,YAAY,IAAI,OAAO;EAAE,GAAG;EAAG,aAAa;CAAK,CAAC;CAC9D,IAAI,QAAQ,MAAM,EAAE,MAAM,OAAO,SAAS,IAAI,KAAK;CACnD,OAAO;AACT;;AAGA,SAAS,UAAU,IAAc,GAA4B;CAC3D,MAAM,QAAQ,SAAS,EAAE,KAAK,IAAI;CAClC,MAAM,QAAmB;EAAE,OAAO,CAAC;EAAG,MAAM;CAAE;CAC9C,MAAM,QAAQ,MAAM,IAAI,OAAO;EAAE,GAAG;EAAG,aAAa;EAAO;CAAM,CAAC;CAClE,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,OAAO,SAAS,EAAE,KAAK,IAAI;CACjC,EAAE,IAAI,SAAS,KACb,YAAY,KAAK,GAAG,MAAM,IAAI,iBAAiB,KAAK,IAAI,MAAM,KAAK,SAAS,MAAM,MAAM,GAC1F;CACA,OAAO;AACT;AAEA,SAAS,YAAY,IAAc,OAAe,GAA2B;CAC3E,QAAQ,GAAG,MAAX;EACE,KAAK,UACH,OAAO,YAAY,IAAI,OAAO,CAAC;EACjC,KAAK,SACH,OAAO,WAAW,IAAI,OAAO,CAAC;EAChC,KAAK,SACH,OAAO,WAAW,IAAI,OAAO,CAAC;EAChC,KAAK,UACH,OAAO,YAAY,IAAI,OAAO,CAAC;EACjC,KAAK,YAIH,OAAO,+BAA+B,GAAG,KAAK,IAC1C,MAAM,GAAG,OAAO,OAAO,CAAC,IACxB,cAAc,GAAG,OAAO,OAAO,GAAG,gBAAgB,WAAW;EACnE,KAAK,YAGH,OAAO,cAAc,GAAG,OAAO,OAAO,GAAG,WAAW,MAAM;EAC5D,KAAK,WACH,OAAO,aAAa,IAAI,OAAO,CAAC;EAClC,KAAK,UACH,OAAO,YAAY,IAAI,OAAO,CAAC;EACjC,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,QACH,OAAO,sBAAsB,IAAI,OAAO,CAAC;EAC3C,KAAK,UACH,OAAO,YAAY,IAAI,OAAO,CAAC;EACjC,KAAK,YACH,OAAO,MAAM,GAAG,OAAO,OAAO,CAAC;EACjC,KAAK;EACL,KAAK,sBACH,OAAO,WAAW,IAAI,OAAO,CAAC;EAChC,SAIE,OAAO;CACX;AACF;;;;;AAMA,SAAS,qBACP,IACA,OACA,GACc;CACd,MAAM,QAAS,EAAE,IAAI,kCAAkB,IAAI,IAAoB;CAC/D,IAAI,CAAC,MAAM,IAAI,GAAG,KAAK,GAAG;EACxB,MAAM,OAAO,SAAS,EAAE,KAAK,KAAK;EAClC,MAAM,IAAI,GAAG,OAAO,IAAI;EACxB,MAAM,QAAQ,SAAS,EAAE,KAAK,IAAI;EAClC,MAAM,QAAmB;GAAE,OAAO,CAAC;GAAG,MAAM;EAAE;EAC9C,MAAM,QAAQ,MAAM,GAAG,OAAO,OAAO;GAAE,GAAG;GAAG,aAAa;GAAO;EAAM,CAAC;EACxE,IAAI,UAAU,MAAM;GAClB,MAAM,OAAO,GAAG,KAAK;GACrB,OAAO;EACT;EACA,EAAE,IAAI,SAAS,KACb,YAAY,KAAK,GAAG,MAAM,IAAI,iBAAiB,KAAK,IAAI,MAAM,KAAK,SAAS,MAAM,MAAM,GAC1F;CACF;CACA,OAAO,mBAAmB,GAAG,OAAO,OAAO,CAAC;AAC9C;;AAGA,SAAS,mBAAmB,OAAe,OAAe,GAA2B;CACnF,MAAM,OAAO,EAAE,IAAI,eAAe,IAAI,KAAK;CAC3C,IAAI,SAAS,KAAA,GAAW,OAAO;CAC/B,MAAM,OAAO,MAAM,GAAG,IAAI;CAC1B,OAAO;EACL,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,OAAO,KAAK,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK;EACxE,OAAO;CACT;AACF;;;;;;;;;;;;;;AAeA,SAAS,WACP,IACA,OACA,GACc;CACd,MAAM,SAAmB,CAAC;CAC1B,KAAK,MAAM,UAAU,GAAG,SAAS;EAC/B,MAAM,KAAK,EAAE,SAAS,IAAI,MAAM,IAAI,UAAU,QAAQ,CAAC,IAAI,gBAAgB,QAAQ,CAAC;EACpF,IAAI,OAAO,MAAM,OAAO;EACxB,OAAO,KAAK,EAAE;CAChB;CACA,IAAI,OAAO,WAAW,GAAG,OAAO;CAEhC,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,IAAI,OAAO,GAAG,IAAI,GAAG,OAAO,GAAa,GAAG,MAAM;CAClD,KAAK,MAAM,MAAM,OAAO,MAAM,CAAC,GAC7B,QAAQ,MAAM,IAAI,KAAK,EAAE,KAAK,IAAI,IAAI,GAAG,GAAG,GAAG,MAAM;CAEvD,QAAQ,MAAM,IAAI,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK;CAC/C,OAAO;EAAE;EAAM,OAAO;CAAI;AAC5B;;;;;AAMA,SAAS,gBAAgB,IAAc,GAA4B;CACjE,MAAM,QAAQ,SAAS,EAAE,KAAK,IAAI;CAClC,MAAM,QAAmB;EAAE,OAAO,CAAC;EAAG,MAAM;CAAE;CAC9C,MAAM,OAAO,aAAa,IAAI,cAAc,OAAO,EAAE,KAAK,MAAM,KAAK,CAAC;CACtE,IAAI,SAAS,MAAM,OAAO;CAC1B,MAAM,OAAO,SAAS,EAAE,KAAK,IAAI;CACjC,EAAE,IAAI,SAAS,KACb,YAAY,KAAK,GAAG,MAAM,IAAI,iBAAiB,KAAK,EAAE,SAAS,SAAS,SAAS,QAAQ,IAAI,KAAK,IAAI,MAAM,GAAG,EAAE,OAAO,GAC1H;CACA,OAAO;AACT;;AAGA,SAAS,YAAY,IAAc,OAAe,GAA2B;CAE3E,MAAM,OAAO,aAAa,IADX,cAAc,OAAO,EAAE,KAAK,MAAM,EAAE,KAChB,CAAC;CACpC,IAAI,SAAS,MAAM,OAAO;CAC1B,OAAO;EAAE,MAAM,SAAS,SAAS,KAAK,QAAQ,KAAK,WAAW,EAAE,KAAK;EAAI,OAAO;CAAM;AACxF;;;;;;;AAQA,SAAS,YAAY,IAAc,OAAe,GAA2B;CAC3E,IAAI,GAAG,aAAa,KAAA,GAAW,OAAO;CACtC,IAAI,GAAG,qBAAqB,QAAQ,GAAG,WAAW,MAAM,OAAO;CAC/D,IAAI,GAAG,uBAAuB,KAAA,KAAa,GAAG,mBAAmB,SAAS,GAAG,OAAO;CAIpF,MAAM,UAAU,GAAG,UAAU,CAAC;CAC9B,IAAI,QAAQ,MAAM,UAAU,MAAM,SAAS,eAAe,GAAG,OAAO;CAEpE,IAAI,OAAO,aAAa,MAAM,eAAe,MAAM,yBAAyB,MAAM,WAAW,EAAE,KAAK;CAEpG,IAAI,GAAG,WAAW,MAAM;EACtB,MAAM,SAAS,MAAM,GAAG,IAAI;EAC5B,QAAQ,OAAO,OAAO,MAAM,MAAM,SAAS,kBAAkB,EAAE,KAAK,OAAO,KAAK,GAAG,UAAU,GAAG,MAAM,EAAE,WAAW,EAAE,KAAK;CAC5H;CAEA,MAAM,QAA8D,CAAC;CACrE,KAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAAQ,GAAG,UAAU,GAAG;EACzD,MAAM,SAAS,aAAa,GAAG;EAC/B,MAAM,OAAO,MAAM,GAAG,IAAI;EAC1B,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO;EACnC,MAAM,YAAY,MAAM,QAAQ,MAAM,CAAC;EACvC,IAAI,cAAc,MAAM,OAAO;EAC/B,QAAQ,UAAU;EAClB,MAAM,KAAK;GAAE,QAAQ,oBAAoB,MAAM;GAAG;GAAQ,OAAO,UAAU;EAAM,CAAC;CACpF;CAQA,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,MAAM,UAAoB,CAAC;CAC3B,IAAI,UAAU;CACd,IAAI,UAAU;CACd,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,WAAW,KAAK,QAAQ;GAC1B,QAAQ,KAAK,GAAG,KAAK,OAAO,GAAG,KAAK,OAAO;GAC3C;EACF;EACA,UAAU;EACV,WAAW,KAAK,SACZ,GAAG,IAAI,GAAG,KAAK,OAAO,IAAI,KAAK,MAAM,KACrC,MAAM,KAAK,MAAM,iBAAiB,KAAK,OAAO,MAAM,MAAM,KAAK,IAAI,GAAG,KAAK,OAAO,IAAI,KAAK,MAAM;CACvG;CACA,QAAQ,GAAG,IAAI,IAAI,QAAQ,KAAK,GAAG,EAAE,IAAI;CAIzC,KAAK,MAAM,SAAS,SAClB,QAAQ,OAAO,mBAAmB,EAAE,KAAK,KAA4B,EAAE,GAAG,IAAI,WAAW,EAAE,KAAK;CAElG,OAAO;EAAE;EAAM,OAAO;CAAI;AAC5B;AAEA,SAAS,WAAW,IAAkC,OAAe,GAA2B;CAM9F,IAAI,QAAQ;CACZ,MAAM,UAAiC,CAAC;CACxC,KAAK,MAAM,SAAS,GAAG,QACrB,QAAQ,MAAM,MAAd;EACE,KAAK;GACH,SAAS,MAAM,MAAM,UAAU,MAAM,QAAQ,UAAU,EAAE,KAAK;GAC9D;EACF,KAAK;GACH,SAAS,MAAM,MAAM,UAAU,MAAM,QAAQ,UAAU,EAAE,KAAK;GAC9D;EACF,KAAK;GACH,SAAS,MAAM,MAAM,YAAY,MAAM,OAAO,UAAU,EAAE,KAAK;GAC/D;EACF,KAAK;GACH,QAAQ,KAAK,KAAK;GAClB;EACF,SAEE,OAAO;CACX;CAGF,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,MAAM,QAAQ,MAAM,GAAG,IAAI;CAC3B,MAAM,OAAO,MAAM,GAAG,IAAI;CAC1B,MAAM,QAAQ,MAAM,GAAG,SAAS,MAAM,CAAC;CACvC,IAAI,UAAU,MAAM,OAAO;CAC3B,IAAI,OACF,qBAAqB,MAAM,WAAW,EAAE,KAAK,KAC7C,QACA,GAAG,IAAI,aAAa,MAAM,eACnB,MAAM,KAAK,MAAM,GAAG,MAAM,UAAU,MAAM,MAC9C,KAAK,GAAG,MAAM,GAAG,MAAM,IAAI,MAAM,OAAO,IAAI,GAAG,MAAM,IAAI,MAAM,MAAM;CAG1E,KAAK,MAAM,SAAS,SAClB,QAAQ,OAAO,mBAAmB,EAAE,KAAK,KAAK,EAAE,GAAG,IAAI,WAAW,EAAE,KAAK;CAE3E,OAAO;EAAE;EAAM,OAAO;CAAI;AAC5B;AAEA,SAAS,WAAW,IAAkC,OAAe,GAA2B;CAG9F,IAAI,GAAG,SAAS,MAAM,OAAO;CAC7B,IAAI,GAAG,MAAM,MAAM,SAAS,CAAC,iBAAiB,IAAI,CAAC,GAAG,OAAO;CAE7D,IAAI,OAAO,qBAAqB,MAAM,KAAK,MAAM,YAAY,GAAG,MAAM,OAAO,UAAU,EAAE,KAAK;CAC9F,MAAM,SAAmB,CAAC;CAC1B,KAAK,MAAM,CAAC,OAAO,WAAW,GAAG,MAAM,QAAQ,GAAG;EAChD,MAAM,OAAO,MAAM,GAAG,IAAI;EAC1B,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM;EAClC,MAAM,QAAQ,MAAM,QAAoB,MAAM,CAAC;EAC/C,IAAI,UAAU,MAAM,OAAO;EAC3B,QAAQ,MAAM;EACd,OAAO,KAAK,MAAM,KAAK;CACzB;CACA,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,QAAQ,GAAG,IAAI,IAAI,OAAO,KAAK,GAAG,EAAE;CACpC,OAAO;EAAE;EAAM,OAAO;CAAI;AAC5B;AAEA,SAAS,YAAY,IAAmC,OAAe,GAA2B;CAGhG,IAAI,EADF,GAAG,QAAQ,SAAS,YAAY,GAAG,QAAQ,OAAO,WAAW,KAAK,GAAG,QAAQ,WAAW,OACrE,OAAO;CAE5B,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,MAAM,SAAS,MAAM,GAAG,KAAK;CAC7B,MAAM,SAAS,MAAM,GAAG,KAAK;CAC7B,MAAM,QAAQ,MAAM,GAAG,WAAW,QAAQ,CAAC;CAC3C,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,MAAM,kBAAkB,EAAE,KAAK,WAAW,WAAW;CAM3D,OAAO;EAAE,MAAA,aAJM,MAAM,eAAe,MAAM,yBAAyB,MAAM,WAAW,EAAE,KAAK,GACtF,IAAI,UACA,OAAO,MAAM,MAAM,OAAO,IAAI,QAAQ,MAAM,GAAG,OAAO,KAC1D,OAAO,GAAG,MAAM,GAAG,OAAO,IAAI,MAAM,OAAO,IAAI,GAAG,OAAO,IAAI,MAAM,MAAM;EAC/D,OAAO;CAAI;AAC5B;;;;;;;;;;;;AAaA,SAAS,YAAY,IAAmC,OAAe,GAA2B;CAChG,MAAM,QAAQ,MAAM,GAAG,OAAO,OAAO,CAAC;CACtC,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,OAAO;EACL,MAAM,GAAG,MAAM,OAAO,IAAI,GAAG,mBAAmB,EAAE,KAAK,EAAE,EAAE,GAAG,MAAM,MAAM;EAC1E,OAAO;CACT;AACF;;;;;;;;;;;;;AAcA,SAAS,YAAY,IAAmC,OAAe,GAA2B;CAChG,MAAM,QAAQ,MAAM,GAAG,IAAI;CAC3B,IAAI,OACF,GAAG,WAAW,OACV,OAAO,MAAM,UAAU,MAAM,qBAAqB,EAAE,KAAK,MACzD,aAAa,MAAM,qBAAqB,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM;CACvE,KAAK,MAAM,SAAS,GAAG,QACrB,QAAQ,MAAM,MAAd;EACE,KAAK;GACH,QAAQ,GAAG,MAAM,GAAG,aAAa,EAAE,KAAK,MAAM,MAAM,EAAE,GAAG,MAAM;GAC/D;EACF,KAAK;GACH,QAAQ,OAAO,mBAAmB,EAAE,KAAK,KAAK,EAAE,GAAG,MAAM,WAAW,EAAE,KAAK;GAC3E;EACF,KAAK,uBAEH,OAAO;EACT,SAAS;GACP,MAAM,OAAO,gBAAgB,OAAO,OAAO,EAAE,GAAG;GAChD,IAAI,SAAS,MAAM,OAAO;GAC1B,QAAQ,QAAQ,KAAK,WAAW,EAAE,KAAK;EACzC;CACF;CAEF,OAAO;EAAE;EAAM;CAAM;AACvB;;;;;;;;;;;AAeA,SAAS,sBAAsB,IAAwB,OAAe,GAA2B;CAC/F,IAAI,GAAG,WAAW,MAAM,OAAO;CAC/B,MAAM,QAAQ,MAAM,GAAG,IAAI;CAC3B,IAAI;CACJ,QAAQ,GAAG,MAAX;EACE,KAAK;GACH,aAAa,UAAU,MAAM;GAC7B;EACF,KAAK;GACH,aAAa,WAAW,MAAM;GAC9B;EACF,KAAK;GACH,aAAa,UAAU,MAAM;GAC7B;EACF,KAAK;GACH,aAAa,YAAY,MAAM;GAC/B;CACJ;CAKA,MAAM,YAAY,aAChB;EAAE,GAAG;EAAI,QAAQ;CAAM,GACvB,cAAc,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,CAC3C;CACA,IAAI,cAAc,MAAM,OAAO;CAE/B,MAAM,SAAS,GAAG,MAAM,GAAG,WAAW;CAEtC,OAAO;EACL,OAFW,GAAG,SAAS,YAAY,SAAS,OAAO,OAAO,mBAAmB,EAAE,KAAK,QAEtE,cAAc,SAAS,KAAK,QAAQ,UAAU,WAAW,EAAE,KAAK;EAC9E;CACF;AACF;;;;;;;;;;;;AAaA,SAAS,aACP,IACA,OACA,GACc;CACd,MAAM,QAAQ,MAAM,GAAG,OAAO,OAAO,CAAC;CACtC,IAAI,UAAU,MAAM,OAAO;CAI3B,EAAE,IAAI,wBAAwB;CAC9B,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,MAAM,QAAQ,iBAAiB,EAAE;CAGjC,MAAM,UAAU,sBAAsB,EAAE,IAAI,MAAM,IAAI,gBAAgB,IAAI,GAAG,MAAM,MAAM;CACzF,OAAO;EACL,MACE,MAAM,MAAM,gBAAgB,IAAI,GAAG,MAAM,SACjC,MAAM,OAAO,IAAI,GAAG,MAAM,MAAM,GAAG,QAAQ;EACrD,OAAO;CACT;AACF;;AAGA,SAAS,cACP,SACA,OACA,GACA,MACA,UACc;CACd,MAAM,QAAQ,MAAM,SAAS,OAAO,CAAC;CACrC,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,OAAO;EACL,MAAM,MAAM,QAAQ,KAAK,IAAI,IAAI,GAAG,SAAS,SAAS,MAAM,OAAO,IAAI,GAAG,MAAM,MAAM;EACtF,OAAO;CACT;AACF;;AAGA,SAAS,MAAM,GAAa,QAAwB;CAClD,MAAM,OAAO,SAAS,EAAE,KAAK,MAAM;CACnC,EAAE,MAAM,MAAM,KAAK,IAAI;CACvB,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"build-path.js","names":[],"sources":["../../../src/core/codegen/build-path.ts"],"sourcesContent":["/**\n * Build Path: one uninstrumented pass that VALIDATES and BUILDS rewritten\n * output together, abandoning the whole parse at the first failing check.\n *\n * `z.object()` strips unknown keys, so a successful parse cannot return the\n * input by reference — it must produce a fresh object. That rules out the Fast\n * Path (whose contract is `data === input`), and before this the only remaining\n * option was the eager slow walk: a fully instrumented traversal that collects\n * issues on every parse, valid or not.\n *\n * Two passes are wasteful in either direction. Validating first and building\n * afterwards reads every property twice (measured 29.7 ns vs 21.6 for the slow\n * walk on a 6-field object). Building with issue collection pays the\n * instrumentation even when nothing fails. Doing both in ONE pass, with a\n * sentinel instead of an issues array, beats both — and a failure costs only\n * the checks up to the first bad one, because the issue-producing walk is\n * deferred into `.error` exactly as `__zcFinD` does for mutation-free schemas:\n *\n * object clean object invalid array(8) invalid\n * slow walk 21.6 ns 30.4 ns 197.5 ns\n * build path 18.1 ns 7.7 ns 9.8 ns\n *\n * A subtree that rebuilds nothing is validated with its existing Fast Path\n * expression and passed through by reference, so only nodes that genuinely\n * produce a new value need code here; anything else returns null and keeps the\n * eager walk.\n *\n * Coverage is what decides whether this pass is reached at all, because it is\n * all-or-nothing per schema: ONE unmodelled node anywhere in the tree costs the\n * whole schema its single-pass parse. Modelled, beyond the stripping containers\n * this started with: array size checks and `.refine()`, object-level `.refine()`,\n * `.default()` substitution, ordered string rewrites (`.trim()`,\n * `.toLowerCase()`), sync `.transform()`, `z.stringbool()`, and the five native\n * coercions (`string`, `number`, `boolean`, `bigint`, `date`). Still declined, via\n * {@link mutatesBeyondStrip} — `.catch()` (its callback wants the inner schema's\n * issue list, which this pass never builds), `z.url()`, and `superRefine`.\n */\n\nimport type { ObjectIR, RefineEffectCheckIR, SchemaIR, StringBoolIR } from \"../types.js\";\nimport type { CodeGenContext, FastScope } from \"./context.js\";\nimport {\n declareFastTemps,\n emitEffectCallable,\n emitEffectFn,\n emitRuntimeHelper,\n emitTemp,\n escapeString,\n keyMembershipTest,\n outputAlwaysDefined,\n rejectsUndefined,\n} from \"./context.js\";\nimport { createFastGen, generateFast } from \"./fast-path.js\";\nimport { EXTRACT_CAP, estimateFastCost, MIN_EXTRACT, predictedInlineSize } from \"./fast-size.js\";\nimport { ZC_HOP_DECL } from \"./issue-decls.js\";\nimport { defaultValueExpr, needsPostInnerDefault } from \"./schemas/default.js\";\nimport { innerAppliesDefaultOnUndefined } from \"./schemas/optional.js\";\nimport { fastStringCheck } from \"./schemas/string.js\";\nimport { emitStringBoolMap, stringBoolUsesInline } from \"./schemas/string-bool.js\";\n\n/** Statements that leave the built value in `value`, or `return <FAIL>` on failure. */\ninterface Built {\n code: string;\n value: string;\n}\n\ninterface BuildGen {\n ctx: CodeGenContext;\n /** Identifier of the per-validator FAIL sentinel. */\n fail: string;\n /** `var` temps and running emitted size of the function being assembled. */\n scope: FastScope;\n /**\n * May THIS node be hosted in its own function? False for the node a hosted\n * build was created for — it already IS that function, so re-hosting it would\n * recurse forever. Children are always extractable, letting an oversized\n * helper split further.\n */\n extractable: boolean;\n /** Nodes of the root schema that rebuild (see rebuildSet). */\n rebuilds: ReadonlySet<SchemaIR>;\n}\n\n/**\n * Which nodes of `root` produce a value that is not their input — a stripping\n * object, coercion, codec, default, overwrite or transform, including\n * containers that contain one. Everything else can be validated in place and\n * passed through, which is what keeps this generator small.\n *\n * Computed as a fixpoint rather than a plain walk because of recursion: a\n * `recursiveRef` is a back-edge with no children, so a local walk reads false\n * for it and would pass the whole recursive subtree through by reference —\n * leaving every nested value unstripped while the outermost one was rebuilt.\n * Resolving the ref against its target closes the cycle, and iterating to a\n * fixpoint settles the mutual dependency between the two.\n */\nfunction rebuildSet(root: SchemaIR): ReadonlySet<SchemaIR> {\n const targets = new Map<number, SchemaIR>([[0, root]]);\n const nodes: SchemaIR[] = [];\n const seen = new Set<SchemaIR>();\n const collect = (node: SchemaIR): void => {\n if (seen.has(node)) return;\n seen.add(node);\n nodes.push(node);\n if (node.type === \"recursionTarget\") targets.set(node.refId, node.inner);\n for (const child of children(node)) collect(child);\n };\n collect(root);\n\n const rebuilds = new Set<SchemaIR>();\n for (let changed = true; changed;) {\n changed = false;\n for (const node of nodes) {\n if (rebuilds.has(node)) continue;\n const target = node.type === \"recursiveRef\" ? targets.get(node.refId ?? 0) : undefined;\n const rebuild =\n (node.type === \"object\" && node.stripUnknownKeys === true) ||\n // `.default()` substitutes its own value for `undefined`, so its output\n // is not its input even when the inner schema passes through — it must\n // never be handed to `passthrough`, whose fast check would reject the\n // absent value outright.\n node.type === \"default\" ||\n // `z.stringbool()` replaces its accepted string with a boolean.\n node.type === \"stringBool\" ||\n // An overwrite effect (`.trim()`, `.toLowerCase()`) rewrites the string,\n // so the node's output is a new value: it has to be BUILT rather than\n // validated in place (see buildString).\n (node.type === \"string\" &&\n (node.coerce === true || node.checks.some((c) => c.kind === \"overwrite_effect\"))) ||\n ((node.type === \"number\" ||\n node.type === \"boolean\" ||\n node.type === \"bigint\" ||\n node.type === \"date\") &&\n node.coerce === true) ||\n // `.transform(fn)` replaces the value with the callback's result.\n node.type === \"effect\" ||\n (target !== undefined && rebuilds.has(target)) ||\n children(node).some((child) => rebuilds.has(child));\n if (rebuild) {\n rebuilds.add(node);\n changed = true;\n }\n }\n }\n return rebuilds;\n}\n\n/** Does `ir`, taken as a whole schema, produce a value that is not its input? */\nexport function rebuildsOutput(ir: SchemaIR): boolean {\n return rebuildSet(ir).has(ir);\n}\n\n/**\n * True when the subtree mutates for any reason the build pass cannot reproduce —\n * `.catch()`, `z.url()`, `superRefine`. Those rewrite values in ways this pass\n * (which validates, coerces, decodes string booleans, substitutes declared\n * defaults, applies ordered string rewrites and copies) does not model, so the\n * schema keeps the eager walk.\n */\nfunction mutatesBeyondStrip(ir: SchemaIR): boolean {\n return mutatesHere(ir) || children(ir).some(mutatesBeyondStrip);\n}\n\n/**\n * Does this node rewrite values on its own account (ignoring its children, and\n * ignoring the reshaping a strip object does)? Mirrors the node-local half of\n * `hasMutation`; the recursion above supplies the other half.\n */\nfunction mutatesHere(ir: SchemaIR): boolean {\n switch (ir.type) {\n case \"string\":\n // Coercion and overwrite effects are absent: `buildString` applies them\n // in order. A `z.url()` check still is not — it trims, normalizes and\n // needs its own normalization/error semantics.\n return (\n superRefines(ir.checks) ||\n ir.checks.some((c) => c.kind === \"string_format\" && c.format === \"url\")\n );\n case \"number\":\n return superRefines(ir.checks);\n case \"boolean\":\n case \"bigint\":\n case \"date\":\n return false;\n // `default` and `effect` are absent: substituting a constant for `undefined`\n // and applying a sync transform are both modelled (see buildDefault /\n // buildEffect), and their inners are reached through `children`.\n //\n // `catch` is NOT: its catchValue callback receives a ctx carrying the inner\n // schema's collected issues, and this pass produces a sentinel instead of an\n // issue list — there is nothing to hand it.\n case \"catch\":\n case \"fallback\":\n return true;\n case \"object\":\n case \"array\":\n return superRefines(ir.checks);\n default:\n return false;\n }\n}\n\nfunction superRefines(checks: readonly { kind: string }[] | undefined): boolean {\n return checks !== undefined && checks.some((c) => c.kind === \"super_refine_effect\");\n}\n\nfunction children(ir: SchemaIR): readonly SchemaIR[] {\n switch (ir.type) {\n case \"object\":\n return ir.catchall\n ? [...Object.values(ir.properties), ir.catchall]\n : Object.values(ir.properties);\n case \"array\":\n return [ir.element];\n case \"tuple\":\n return ir.rest === null ? ir.items : [...ir.items, ir.rest];\n case \"record\":\n case \"map\":\n return [ir.keyType, ir.valueType];\n case \"set\":\n return [ir.valueType];\n case \"union\":\n case \"discriminatedUnion\":\n return ir.options;\n case \"intersection\":\n return [ir.left, ir.right];\n case \"optional\":\n case \"nullable\":\n case \"readonly\":\n case \"default\":\n case \"catch\":\n case \"effect\":\n case \"recursionTarget\":\n return [ir.inner];\n case \"pipe\":\n return [ir.in, ir.out];\n default:\n return [];\n }\n}\n\n/**\n * Host the whole schema as `function NAME(input){…}` returning the built value\n * or the FAIL sentinel. Returns the function name, or null when the schema is\n * not expressible as a single build pass.\n */\nexport function generateBuild(ir: SchemaIR, ctx: CodeGenContext): string | null {\n const rebuilds = rebuildSet(ir);\n if (!rebuilds.has(ir) || mutatesBeyondStrip(ir)) return null;\n const fail = emitFailSentinel(ctx);\n const scope: FastScope = { temps: [], used: 0 };\n const built = build(ir, \"input\", { ctx, extractable: false, fail, rebuilds, scope });\n if (built === null) return null;\n const name = emitTemp(ctx, \"vb\");\n ctx.preamble.push(\n `function ${name}(input){${declareFastTemps(scope)}${built.code}return ${built.value};}`,\n );\n return name;\n}\n\n/** One `{}` per validator, compared by identity — no parse output can equal it. */\nfunction emitFailSentinel(ctx: CodeGenContext): string {\n if (ctx.buildFailName === undefined) {\n ctx.buildFailName = `__bf_${ctx.counter++}`;\n ctx.preamble.push(`var ${ctx.buildFailName}={};`);\n }\n return ctx.buildFailName;\n}\n\n/**\n * Statements producing the built value of `ir` read from `input`, or null.\n *\n * Size-gated exactly like the fast path: once inlining `ir` would push the\n * function being assembled past EXTRACT_CAP, the sub-build is hosted as its own\n * `__vb_N(p)` returning value-or-FAIL and replaced by a call. Without this a\n * deeply nested schema emits one enormous build function — measured at 113 KB\n * and 354 KB on the deep fixtures — far past the bytecode size where V8 stops\n * running TurboFan on it, which would forfeit the speed this path exists for.\n */\nfunction build(ir: SchemaIR, input: string, g: BuildGen): Built | null {\n // Resolved before the passthrough shortcut below. A back-edge carries no\n // children, so `rebuildsOutput` reads false for it — and passing it through by\n // reference would leave every nested recursive value unstripped while the\n // outermost one was rebuilt.\n if (ir.type === \"recursiveRef\") return buildRecursiveCall(ir.refId ?? 0, input, g);\n if (ir.type === \"recursionTarget\") return buildRecursionTarget(ir, input, g);\n if (!g.rebuilds.has(ir)) return passthrough(ir, input, g);\n\n const cache = (g.ctx.fastSizeCache ??= new WeakMap<SchemaIR, number>());\n if (\n g.extractable &&\n g.scope.used + predictedInlineSize(ir, input.length, cache) > EXTRACT_CAP &&\n (g.scope.used > EXTRACT_CAP || estimateFastCost(ir, cache) >= MIN_EXTRACT)\n ) {\n const hosted = hostBuild(ir, g);\n if (hosted !== null) {\n const slot = local(g, \"bh\");\n const code = `${slot}=${hosted}(${input});if(${slot}===${g.fail})return ${g.fail};`;\n g.scope.used += code.length;\n return { code, value: slot };\n }\n }\n\n // This node's extraction decision is made; its descendants get to make their\n // own, so an oversized hosted helper keeps splitting.\n const before = g.scope.used;\n const out = buildInline(ir, input, { ...g, extractable: true });\n if (out !== null) g.scope.used = before + out.code.length;\n return out;\n}\n\n/** Host `ir`'s build in its own function over a fresh parameter; returns its name. */\nfunction hostBuild(ir: SchemaIR, g: BuildGen): string | null {\n const param = emitTemp(g.ctx, \"bp\");\n const scope: FastScope = { temps: [], used: 0 };\n const inner = build(ir, param, { ...g, extractable: false, scope });\n if (inner === null) return null;\n const name = emitTemp(g.ctx, \"vb\");\n g.ctx.preamble.push(\n `function ${name}(${param}){${declareFastTemps(scope)}${inner.code}return ${inner.value};}`,\n );\n return name;\n}\n\nfunction buildInline(ir: SchemaIR, input: string, g: BuildGen): Built | null {\n switch (ir.type) {\n case \"object\":\n return buildObject(ir, input, g);\n case \"array\":\n return buildArray(ir, input, g);\n case \"tuple\":\n return buildTuple(ir, input, g);\n case \"record\":\n return buildRecord(ir, input, g);\n case \"optional\":\n // A default further down the chain consumes undefined into a value, so\n // the `undefined → undefined` shortcut must not fire — same rule (and\n // same helper) the slow and fast paths already apply.\n return innerAppliesDefaultOnUndefined(ir.inner)\n ? build(ir.inner, input, g)\n : buildSentinel(ir.inner, input, g, \"===undefined\", \"undefined\");\n case \"nullable\":\n // `null` short-circuits unconditionally in zod, whatever the inner is;\n // undefined flows through, so an inner default still fires.\n return buildSentinel(ir.inner, input, g, \"===null\", \"null\");\n case \"default\":\n return buildDefault(ir, input, g);\n case \"string\":\n return buildString(ir, input, g);\n case \"stringBool\":\n return buildStringBool(ir, input, g);\n case \"number\":\n case \"boolean\":\n case \"bigint\":\n case \"date\":\n return buildCoercedPrimitive(ir, input, g);\n case \"effect\":\n return buildEffect(ir, input, g);\n case \"readonly\":\n return build(ir.inner, input, g);\n case \"union\":\n case \"discriminatedUnion\":\n return buildUnion(ir, input, g);\n default:\n // A rebuilding intersection, map or set: expressible in principle, but\n // each needs its own output-shaping rules, so they keep the eager walk\n // until there is a measured reason to add them.\n return null;\n }\n}\n\n/**\n * Host the target's build once under a name registered BEFORE its body is\n * generated, so the back-edges inside that body resolve to it.\n */\nfunction buildRecursionTarget(\n ir: SchemaIR & { type: \"recursionTarget\" },\n input: string,\n g: BuildGen,\n): Built | null {\n const table = (g.ctx.buildRecNames ??= new Map<number, string>());\n if (!table.has(ir.refId)) {\n const name = emitTemp(g.ctx, \"vbr\");\n table.set(ir.refId, name);\n const param = emitTemp(g.ctx, \"bp\");\n const scope: FastScope = { temps: [], used: 0 };\n const inner = build(ir.inner, param, { ...g, extractable: false, scope });\n if (inner === null) {\n table.delete(ir.refId);\n return null;\n }\n g.ctx.preamble.push(\n `function ${name}(${param}){${declareFastTemps(scope)}${inner.code}return ${inner.value};}`,\n );\n }\n return buildRecursiveCall(ir.refId, input, g);\n}\n\n/** Call the hosted build for a recursion target, propagating its FAIL. */\nfunction buildRecursiveCall(refId: number, input: string, g: BuildGen): Built | null {\n const name = g.ctx.buildRecNames?.get(refId);\n if (name === undefined) return null;\n const slot = local(g, \"bh\");\n return {\n code: `${slot}=${name}(${input});if(${slot}===${g.fail})return ${g.fail};`,\n value: slot,\n };\n}\n\n/**\n * Try each option in declaration order and take the first that builds — which\n * is what zod's union does with the first option that parses.\n *\n * Every option is HOSTED rather than inlined, and that is load-bearing: a\n * failing build signals with `return FAIL`, which inside the enclosing function\n * would abandon the whole parse instead of moving on to the next option. Behind\n * a call, the same signal is just a value to test.\n *\n * A discriminated union gets the same treatment rather than a switch: the\n * options still have to be probed by call, and its dispatch advantage is\n * already spent by the enclosing container's own work.\n */\nfunction buildUnion(\n ir: SchemaIR & { type: \"discriminatedUnion\" | \"union\" },\n input: string,\n g: BuildGen,\n): Built | null {\n const hosted: string[] = [];\n for (const option of ir.options) {\n const fn = g.rebuilds.has(option) ? hostBuild(option, g) : hostPassthrough(option, g);\n if (fn === null) return null;\n hosted.push(fn);\n }\n if (hosted.length === 0) return null;\n\n const out = local(g, \"bu\");\n let code = `${out}=${hosted[0] as string}(${input});`;\n for (const fn of hosted.slice(1)) {\n code += `if(${out}===${g.fail}){${out}=${fn}(${input});}`;\n }\n code += `if(${out}===${g.fail})return ${g.fail};`;\n return { code, value: out };\n}\n\n/**\n * Host a non-rebuilding option as `value-or-FAIL`, so a union can probe it with\n * the same protocol as a rebuilding one.\n */\nfunction hostPassthrough(ir: SchemaIR, g: BuildGen): string | null {\n const param = emitTemp(g.ctx, \"bp\");\n const scope: FastScope = { temps: [], used: 0 };\n const expr = generateFast(ir, createFastGen(param, g.ctx, true, scope));\n if (expr === null) return null;\n const name = emitTemp(g.ctx, \"vp\");\n g.ctx.preamble.push(\n `function ${name}(${param}){${declareFastTemps(scope)}return ${expr === \"true\" ? param : `(${expr})?${param}:${g.fail}`};}`,\n );\n return name;\n}\n\n/** Validate in place with the Fast Path and hand the input straight back. */\nfunction passthrough(ir: SchemaIR, input: string, g: BuildGen): Built | null {\n const scoped = createFastGen(input, g.ctx, true, g.scope);\n const expr = generateFast(ir, scoped);\n if (expr === null) return null;\n return { code: expr === \"true\" ? \"\" : `if(!(${expr}))return ${g.fail};`, value: input };\n}\n\n/**\n * Rebuild from the declared keys. Sound for a stripping object (that IS the\n * output) and for a strict one (unknown keys are rejected, so the declared keys\n * are the whole key set). A loose object or one with a `.catchall()` keeps keys\n * this pass does not enumerate, so those bail.\n */\nfunction buildObject(ir: ObjectIR, input: string, g: BuildGen): Built | null {\n if (ir.catchall !== undefined) return null;\n if (ir.stripUnknownKeys !== true && ir.strict !== true) return null;\n if (ir.suppressAbsentKeys !== undefined && ir.suppressAbsentKeys.length > 0) return null;\n // Object-level `.refine()` runs on the assembled output (below). superRefine\n // rewrites the payload, which this pass does not model — mutatesBeyondStrip\n // already rejects it, so this is a belt-and-braces narrowing of the type.\n const refines = ir.checks ?? [];\n if (refines.some((check) => check.kind !== \"refine_effect\")) return null;\n\n let code = `if(typeof ${input}!==\"object\"||${input}===null||Array.isArray(${input}))return ${g.fail};`;\n\n if (ir.strict === true) {\n const keyVar = local(g, \"bk\");\n code += `for(${keyVar} in ${input}){if(!(${keyMembershipTest(g.ctx, Object.keys(ir.properties), keyVar)}))return ${g.fail};}`;\n }\n\n const slots: { always: boolean; keyStr: string; value: string }[] = [];\n for (const [key, propIR] of Object.entries(ir.properties)) {\n const keyStr = escapeString(key);\n const slot = local(g, \"bv\");\n code += `${slot}=${input}[${keyStr}];`;\n const propBuilt = build(propIR, slot, g);\n if (propBuilt === null) return null;\n code += propBuilt.code;\n slots.push({ always: outputAlwaysDefined(propIR), keyStr, value: propBuilt.value });\n }\n\n // Same assembly the eager strip walk uses: the longest LEADING run of\n // always-present keys goes into one object literal (V8 stamps it from a\n // cached boilerplate map in a single allocation), and everything after the\n // first conditional key is appended so insertion order still matches zod.\n // The per-key test is zod's own — keep the key when the parsed value is\n // defined, or when it was present on the input at all.\n const out = local(g, \"bo\");\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 ? `${out}[${slot.keyStr}]=${slot.value};`\n : `if(${slot.value}!==undefined||(${slot.keyStr} in ${input})){${out}[${slot.keyStr}]=${slot.value};}`;\n }\n code += `${out}={${literal.join(\",\")}};${appends}`;\n // Zod parses the properties into the payload first and skips the check chain\n // when that produced issues, so a bad property suppresses the refine — which\n // this pass gets for free, having already returned FAIL at that property.\n for (const check of refines) {\n code += `if(!${emitEffectCallable(g.ctx, check as RefineEffectCheckIR)}(${out}))return ${g.fail};`;\n }\n return { code, value: out };\n}\n\nfunction buildArray(ir: SchemaIR & { type: \"array\" }, input: string, g: BuildGen): Built | null {\n // Length checks are pure predicates over `input.length`, so they hoist ahead\n // of the element loop: a size mismatch bails before a single element is\n // validated. Zod reports the per-element issue first when both fail, but the\n // build pass produces no issues — only the sentinel — and the deferred walk\n // that does produce them keeps zod's order.\n let sizes = \"\";\n const refines: RefineEffectCheckIR[] = [];\n for (const check of ir.checks) {\n switch (check.kind) {\n case \"min_length\":\n sizes += `if(${input}.length<${check.minimum})return ${g.fail};`;\n break;\n case \"max_length\":\n sizes += `if(${input}.length>${check.maximum})return ${g.fail};`;\n break;\n case \"length_equals\":\n sizes += `if(${input}.length!==${check.length})return ${g.fail};`;\n break;\n case \"refine_effect\":\n refines.push(check);\n break;\n default:\n // super_refine (rewrites the value) or a check kind not modelled here.\n return null;\n }\n }\n\n const out = local(g, \"ba\");\n const index = local(g, \"bi\");\n const elem = local(g, \"be\");\n const inner = build(ir.element, elem, g);\n if (inner === null) return null;\n let code =\n `if(!Array.isArray(${input}))return ${g.fail};` +\n sizes +\n `${out}=new Array(${input}.length);` +\n `for(${index}=0;${index}<${input}.length;${index}++){` +\n `${elem}=${input}[${index}];${inner.code}${out}[${index}]=${inner.value};}`;\n // `.refine()` sees the parsed payload, which for a rebuilding element is the\n // freshly assembled array — the same value zod hands its checks.\n for (const check of refines) {\n code += `if(!${emitEffectCallable(g.ctx, check)}(${out}))return ${g.fail};`;\n }\n return { code, value: out };\n}\n\nfunction buildTuple(ir: SchemaIR & { type: \"tuple\" }, input: string, g: BuildGen): Built | null {\n // Trailing-optional and rest handling shape the output length; keep those on\n // the eager walk rather than restating the rules here.\n if (ir.rest !== null) return null;\n if (ir.items.some((item) => !rejectsUndefined(item))) return null;\n\n let code = `if(!Array.isArray(${input})||${input}.length!==${ir.items.length})return ${g.fail};`;\n const values: string[] = [];\n for (const [index, itemIR] of ir.items.entries()) {\n const slot = local(g, \"bt\");\n code += `${slot}=${input}[${index}];`;\n const inner = build(itemIR as SchemaIR, slot, g);\n if (inner === null) return null;\n code += inner.code;\n values.push(inner.value);\n }\n const out = local(g, \"bl\");\n code += `${out}=[${values.join(\",\")}];`;\n return { code, value: out };\n}\n\nfunction buildRecord(ir: SchemaIR & { type: \"record\" }, input: string, g: BuildGen): Built | null {\n const plainStringKey =\n ir.keyType.type === \"string\" && ir.keyType.checks.length === 0 && ir.keyType.coerce !== true;\n if (!plainStringKey) return null;\n\n const out = local(g, \"br\");\n const keyVar = local(g, \"brk\");\n const valVar = local(g, \"brv\");\n const inner = build(ir.valueType, valVar, g);\n if (inner === null) return null;\n const hop = emitRuntimeHelper(g.ctx, \"__zcHop\", ZC_HOP_DECL);\n const code =\n `if(typeof ${input}!==\"object\"||${input}===null||Array.isArray(${input}))return ${g.fail};` +\n `${out}={};` +\n `for(${keyVar} in ${input}){if(${hop}.call(${input},${keyVar})){` +\n `${valVar}=${input}[${keyVar}];${inner.code}${out}[${keyVar}]=${inner.value};}}`;\n return { code, value: out };\n}\n\n/**\n * `.transform(fn)`: validate the inner schema, then hand its parsed value to the\n * callback. Zod's transform is a pipe whose `in` failing aborts the whole thing,\n * which is exactly what returning FAIL from the inner build does — so the\n * callback runs only on a clean parse, as `slowEffect` guarantees with its issue\n * count.\n *\n * The IR reaches here only for a synchronous single-argument callback: a\n * `ctx`-taking or async transform is extracted as a `fallback` instead\n * (see extractPipe), so there is no parse context to reproduce.\n */\nfunction buildEffect(ir: SchemaIR & { type: \"effect\" }, input: string, g: BuildGen): Built | null {\n const inner = build(ir.inner, input, g);\n if (inner === null) return null;\n const out = local(g, \"bx\");\n return {\n code: `${inner.code}${out}=${emitEffectCallable(g.ctx, ir)}(${inner.value});`,\n value: out,\n };\n}\n\n/**\n * A coercing and/or overwrite string (`z.coerce.string()`, `.trim()`,\n * `.toLowerCase()`, ...): coerce first, then emit checks one statement at a time\n * in DECLARATION order, interleaved with rewrites, because a rewrite is visible\n * to every check after it — `z.string().trim().min(1)` rejects `\" \"` where\n * `z.string().min(1).trim()` accepts it. That ordering is exactly why the fast\n * path, which sorts checks cheapest-first and returns the input unchanged, has\n * to decline these.\n *\n * Only reached for a rewriting string; a non-coercing, check-only one never\n * enters the rebuild set and is validated in place by `passthrough`.\n */\nfunction buildString(ir: SchemaIR & { type: \"string\" }, input: string, g: BuildGen): Built | null {\n const value = local(g, \"bs\");\n let code =\n ir.coerce === true\n ? `try{${value}=String(${input});}catch(_){return ${g.fail};}`\n : `if(typeof ${input}!==\"string\")return ${g.fail};${value}=${input};`;\n for (const check of ir.checks) {\n switch (check.kind) {\n case \"overwrite_effect\":\n code += `${value}=${emitEffectFn(g.ctx, check.source)}(${value});`;\n break;\n case \"refine_effect\":\n code += `if(!${emitEffectCallable(g.ctx, check)}(${value}))return ${g.fail};`;\n break;\n case \"super_refine_effect\":\n // Rewrites through zod's payload; mutatesBeyondStrip already rejects it.\n return null;\n default: {\n const expr = fastStringCheck(check, value, g.ctx);\n if (expr === null) return null; // z.url(), unknown format\n code += `if(!(${expr}))return ${g.fail};`;\n }\n }\n }\n return { code, value };\n}\n\n/**\n * `z.stringbool()`: normalize once, select the declared truthy/falsy side, and\n * return the boolean directly. The ordinary Fast Path cannot host this codec\n * because its contract returns the original input by reference; the build path\n * is designed for exactly this kind of small output rewrite.\n */\nfunction buildStringBool(ir: StringBoolIR, input: string, g: BuildGen): Built {\n let code = `if(typeof ${input}!==\"string\")return ${g.fail};`;\n let normalized = input;\n if (!ir.caseSensitive) {\n normalized = local(g, \"bn\");\n code += `${normalized}=${input}.toLowerCase();`;\n }\n\n const out = local(g, \"bb\");\n if (stringBoolUsesInline(ir)) {\n const membership = (values: readonly string[]): string =>\n values.map((value) => `${normalized}===${escapeString(value)}`).join(\"||\");\n code +=\n `if(${membership(ir.truthy)}){${out}=true;}` +\n `else if(${membership(ir.falsy)}){${out}=false;}` +\n `else{return ${g.fail};}`;\n } else {\n const lookup = emitStringBoolMap(ir, g.ctx);\n code += `${out}=${lookup}.get(${normalized});if(${out}===undefined)return ${g.fail};`;\n }\n return { code, value: out };\n}\n\n/** Primitive nodes whose `coerce` flag rewrites their output before checks run. */\ntype CoercedPrimitiveIR = Extract<SchemaIR, { type: \"number\" | \"boolean\" | \"bigint\" | \"date\" }>;\n\n/**\n * Coerce once into a local, then reuse the ordinary Fast Path as the acceptance\n * predicate over the converted value. The build path only needs a verdict on\n * its hot pass; if it fails, the existing deferred slow walk reruns the original\n * coercing schema and produces Zod-identical issues.\n *\n * Number/BigInt/Date conversion can invoke user hooks and throw. Zod catches\n * those throws and reports invalid_type, so the sentinel branch does the same\n * without allocating an issue. Boolean never invokes conversion hooks.\n */\nfunction buildCoercedPrimitive(ir: CoercedPrimitiveIR, input: string, g: BuildGen): Built | null {\n if (ir.coerce !== true) return null;\n const value = local(g, \"bc\");\n let conversion: string;\n switch (ir.type) {\n case \"number\":\n conversion = `Number(${input})`;\n break;\n case \"boolean\":\n conversion = `Boolean(${input})`;\n break;\n case \"bigint\":\n conversion = `BigInt(${input})`;\n break;\n case \"date\":\n conversion = `new Date(${input})`;\n break;\n }\n\n // A fresh shallow node is intentional: only the coerce flag changes. The\n // existing primitive generator remains the single source of truth for every\n // range, format, refine and finite/valid-date check.\n const predicate = generateFast(\n { ...ir, coerce: false },\n createFastGen(value, g.ctx, true, g.scope),\n );\n if (predicate === null) return null;\n\n const assign = `${value}=${conversion};`;\n const code = ir.type === \"boolean\" ? assign : `try{${assign}}catch(_){return ${g.fail};}`;\n return {\n code: code + (predicate === \"true\" ? \"\" : `if(!(${predicate}))return ${g.fail};`),\n value,\n };\n}\n\n/**\n * `.default(v)`: `undefined` yields the declared value without running the\n * inner schema, anything else parses normally — the same two branches\n * `slowDefault` emits, reading the value off the retained schema so a\n * reference-typed default keeps zod's identity (one shared object, not a copy).\n *\n * The substituted value is not validated, so it makes the fast path a PARTIAL\n * predicate (`fc(undefined)` is false where the schema accepts) — which is why\n * this records {@link CodeGenContext.buildSubstitutesValue}, on which\n * `generateValidator` withholds `.is()`.\n */\nfunction buildDefault(\n ir: SchemaIR & { type: \"default\" },\n input: string,\n g: BuildGen,\n): Built | null {\n const inner = build(ir.inner, input, g);\n if (inner === null) return null;\n // Every `default` node is in the rebuild set, so it is always BUILT and never\n // passed through — which makes this flag an exact record of whether the\n // finished pass substitutes a value.\n g.ctx.buildSubstitutesValue = true;\n const out = local(g, \"bq\");\n const value = defaultValueExpr(ir);\n // Zod re-applies the default when the inner returns undefined for a defined\n // input; only emitted when the inner can actually do that.\n const reapply = needsPostInnerDefault(ir) ? `if(${out}===undefined){${out}=${value};}` : \"\";\n return {\n code:\n `if(${input}===undefined){${out}=${value};}` +\n `else{${inner.code}${out}=${inner.value};${reapply}}`,\n value: out,\n };\n}\n\n/** `optional` / `nullable` around a rebuilding inner: pass the sentinel through. */\nfunction buildSentinel(\n innerIR: SchemaIR,\n input: string,\n g: BuildGen,\n test: string,\n sentinel: string,\n): Built | null {\n const inner = build(innerIR, input, g);\n if (inner === null) return null;\n const out = local(g, \"bw\");\n return {\n code: `if(${input}${test}){${out}=${sentinel};}else{${inner.code}${out}=${inner.value};}`,\n value: out,\n };\n}\n\n/** Allocate a `var` the hosted build function declares. */\nfunction local(g: BuildGen, prefix: string): string {\n const name = emitTemp(g.ctx, prefix);\n g.scope.temps.push(name);\n return name;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA+FA,SAAS,WAAW,MAAuC;CACzD,MAAM,0BAAU,IAAI,IAAsB,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CACrD,MAAM,QAAoB,CAAC;CAC3B,MAAM,uBAAO,IAAI,IAAc;CAC/B,MAAM,WAAW,SAAyB;EACxC,IAAI,KAAK,IAAI,IAAI,GAAG;EACpB,KAAK,IAAI,IAAI;EACb,MAAM,KAAK,IAAI;EACf,IAAI,KAAK,SAAS,mBAAmB,QAAQ,IAAI,KAAK,OAAO,KAAK,KAAK;EACvE,KAAK,MAAM,SAAS,SAAS,IAAI,GAAG,QAAQ,KAAK;CACnD;CACA,QAAQ,IAAI;CAEZ,MAAM,2BAAW,IAAI,IAAc;CACnC,KAAK,IAAI,UAAU,MAAM,UAAU;EACjC,UAAU;EACV,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,SAAS,IAAI,IAAI,GAAG;GACxB,MAAM,SAAS,KAAK,SAAS,iBAAiB,QAAQ,IAAI,KAAK,SAAS,CAAC,IAAI,KAAA;GAwB7E,IAtBG,KAAK,SAAS,YAAY,KAAK,qBAAqB,QAKrD,KAAK,SAAS,aAEd,KAAK,SAAS,gBAIb,KAAK,SAAS,aACZ,KAAK,WAAW,QAAQ,KAAK,OAAO,MAAM,MAAM,EAAE,SAAS,kBAAkB,OAC9E,KAAK,SAAS,YACd,KAAK,SAAS,aACd,KAAK,SAAS,YACd,KAAK,SAAS,WACd,KAAK,WAAW,QAElB,KAAK,SAAS,YACb,WAAW,KAAA,KAAa,SAAS,IAAI,MAAM,KAC5C,SAAS,IAAI,CAAC,CAAC,MAAM,UAAU,SAAS,IAAI,KAAK,CAAC,GACvC;IACX,SAAS,IAAI,IAAI;IACjB,UAAU;GACZ;EACF;CACF;CACA,OAAO;AACT;;AAGA,SAAgB,eAAe,IAAuB;CACpD,OAAO,WAAW,EAAE,CAAC,CAAC,IAAI,EAAE;AAC9B;;;;;;;;AASA,SAAS,mBAAmB,IAAuB;CACjD,OAAO,YAAY,EAAE,KAAK,SAAS,EAAE,CAAC,CAAC,KAAK,kBAAkB;AAChE;;;;;;AAOA,SAAS,YAAY,IAAuB;CAC1C,QAAQ,GAAG,MAAX;EACE,KAAK,UAIH,OACE,aAAa,GAAG,MAAM,KACtB,GAAG,OAAO,MAAM,MAAM,EAAE,SAAS,mBAAmB,EAAE,WAAW,KAAK;EAE1E,KAAK,UACH,OAAO,aAAa,GAAG,MAAM;EAC/B,KAAK;EACL,KAAK;EACL,KAAK,QACH,OAAO;EAQT,KAAK;EACL,KAAK,YACH,OAAO;EACT,KAAK;EACL,KAAK,SACH,OAAO,aAAa,GAAG,MAAM;EAC/B,SACE,OAAO;CACX;AACF;AAEA,SAAS,aAAa,QAA0D;CAC9E,OAAO,WAAW,KAAA,KAAa,OAAO,MAAM,MAAM,EAAE,SAAS,qBAAqB;AACpF;AAEA,SAAS,SAAS,IAAmC;CACnD,QAAQ,GAAG,MAAX;EACE,KAAK,UACH,OAAO,GAAG,WACN,CAAC,GAAG,OAAO,OAAO,GAAG,UAAU,GAAG,GAAG,QAAQ,IAC7C,OAAO,OAAO,GAAG,UAAU;EACjC,KAAK,SACH,OAAO,CAAC,GAAG,OAAO;EACpB,KAAK,SACH,OAAO,GAAG,SAAS,OAAO,GAAG,QAAQ,CAAC,GAAG,GAAG,OAAO,GAAG,IAAI;EAC5D,KAAK;EACL,KAAK,OACH,OAAO,CAAC,GAAG,SAAS,GAAG,SAAS;EAClC,KAAK,OACH,OAAO,CAAC,GAAG,SAAS;EACtB,KAAK;EACL,KAAK,sBACH,OAAO,GAAG;EACZ,KAAK,gBACH,OAAO,CAAC,GAAG,MAAM,GAAG,KAAK;EAC3B,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,mBACH,OAAO,CAAC,GAAG,KAAK;EAClB,KAAK,QACH,OAAO,CAAC,GAAG,IAAI,GAAG,GAAG;EACvB,SACE,OAAO,CAAC;CACZ;AACF;;;;;;AAOA,SAAgB,cAAc,IAAc,KAAoC;CAC9E,MAAM,WAAW,WAAW,EAAE;CAC9B,IAAI,CAAC,SAAS,IAAI,EAAE,KAAK,mBAAmB,EAAE,GAAG,OAAO;CACxD,MAAM,OAAO,iBAAiB,GAAG;CACjC,MAAM,QAAmB;EAAE,OAAO,CAAC;EAAG,MAAM;CAAE;CAC9C,MAAM,QAAQ,MAAM,IAAI,SAAS;EAAE;EAAK,aAAa;EAAO;EAAM;EAAU;CAAM,CAAC;CACnF,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,OAAO,SAAS,KAAK,IAAI;CAC/B,IAAI,SAAS,KACX,YAAY,KAAK,UAAU,iBAAiB,KAAK,IAAI,MAAM,KAAK,SAAS,MAAM,MAAM,GACvF;CACA,OAAO;AACT;;AAGA,SAAS,iBAAiB,KAA6B;CACrD,IAAI,IAAI,kBAAkB,KAAA,GAAW;EACnC,IAAI,gBAAgB,QAAQ,IAAI;EAChC,IAAI,SAAS,KAAK,OAAO,IAAI,cAAc,KAAK;CAClD;CACA,OAAO,IAAI;AACb;;;;;;;;;;;AAYA,SAAS,MAAM,IAAc,OAAe,GAA2B;CAKrE,IAAI,GAAG,SAAS,gBAAgB,OAAO,mBAAmB,GAAG,SAAS,GAAG,OAAO,CAAC;CACjF,IAAI,GAAG,SAAS,mBAAmB,OAAO,qBAAqB,IAAI,OAAO,CAAC;CAC3E,IAAI,CAAC,EAAE,SAAS,IAAI,EAAE,GAAG,OAAO,YAAY,IAAI,OAAO,CAAC;CAExD,MAAM,QAAS,EAAE,IAAI,kCAAkB,IAAI,QAA0B;CACrE,IACE,EAAE,eACF,EAAE,MAAM,OAAO,oBAAoB,IAAI,MAAM,QAAQ,KAAK,IAAA,SACzD,EAAE,MAAM,OAAA,QAAsB,iBAAiB,IAAI,KAAK,KAAA,OACzD;EACA,MAAM,SAAS,UAAU,IAAI,CAAC;EAC9B,IAAI,WAAW,MAAM;GACnB,MAAM,OAAO,MAAM,GAAG,IAAI;GAC1B,MAAM,OAAO,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,OAAO,KAAK,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK;GACjF,EAAE,MAAM,QAAQ,KAAK;GACrB,OAAO;IAAE;IAAM,OAAO;GAAK;EAC7B;CACF;CAIA,MAAM,SAAS,EAAE,MAAM;CACvB,MAAM,MAAM,YAAY,IAAI,OAAO;EAAE,GAAG;EAAG,aAAa;CAAK,CAAC;CAC9D,IAAI,QAAQ,MAAM,EAAE,MAAM,OAAO,SAAS,IAAI,KAAK;CACnD,OAAO;AACT;;AAGA,SAAS,UAAU,IAAc,GAA4B;CAC3D,MAAM,QAAQ,SAAS,EAAE,KAAK,IAAI;CAClC,MAAM,QAAmB;EAAE,OAAO,CAAC;EAAG,MAAM;CAAE;CAC9C,MAAM,QAAQ,MAAM,IAAI,OAAO;EAAE,GAAG;EAAG,aAAa;EAAO;CAAM,CAAC;CAClE,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,OAAO,SAAS,EAAE,KAAK,IAAI;CACjC,EAAE,IAAI,SAAS,KACb,YAAY,KAAK,GAAG,MAAM,IAAI,iBAAiB,KAAK,IAAI,MAAM,KAAK,SAAS,MAAM,MAAM,GAC1F;CACA,OAAO;AACT;AAEA,SAAS,YAAY,IAAc,OAAe,GAA2B;CAC3E,QAAQ,GAAG,MAAX;EACE,KAAK,UACH,OAAO,YAAY,IAAI,OAAO,CAAC;EACjC,KAAK,SACH,OAAO,WAAW,IAAI,OAAO,CAAC;EAChC,KAAK,SACH,OAAO,WAAW,IAAI,OAAO,CAAC;EAChC,KAAK,UACH,OAAO,YAAY,IAAI,OAAO,CAAC;EACjC,KAAK,YAIH,OAAO,+BAA+B,GAAG,KAAK,IAC1C,MAAM,GAAG,OAAO,OAAO,CAAC,IACxB,cAAc,GAAG,OAAO,OAAO,GAAG,gBAAgB,WAAW;EACnE,KAAK,YAGH,OAAO,cAAc,GAAG,OAAO,OAAO,GAAG,WAAW,MAAM;EAC5D,KAAK,WACH,OAAO,aAAa,IAAI,OAAO,CAAC;EAClC,KAAK,UACH,OAAO,YAAY,IAAI,OAAO,CAAC;EACjC,KAAK,cACH,OAAO,gBAAgB,IAAI,OAAO,CAAC;EACrC,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,QACH,OAAO,sBAAsB,IAAI,OAAO,CAAC;EAC3C,KAAK,UACH,OAAO,YAAY,IAAI,OAAO,CAAC;EACjC,KAAK,YACH,OAAO,MAAM,GAAG,OAAO,OAAO,CAAC;EACjC,KAAK;EACL,KAAK,sBACH,OAAO,WAAW,IAAI,OAAO,CAAC;EAChC,SAIE,OAAO;CACX;AACF;;;;;AAMA,SAAS,qBACP,IACA,OACA,GACc;CACd,MAAM,QAAS,EAAE,IAAI,kCAAkB,IAAI,IAAoB;CAC/D,IAAI,CAAC,MAAM,IAAI,GAAG,KAAK,GAAG;EACxB,MAAM,OAAO,SAAS,EAAE,KAAK,KAAK;EAClC,MAAM,IAAI,GAAG,OAAO,IAAI;EACxB,MAAM,QAAQ,SAAS,EAAE,KAAK,IAAI;EAClC,MAAM,QAAmB;GAAE,OAAO,CAAC;GAAG,MAAM;EAAE;EAC9C,MAAM,QAAQ,MAAM,GAAG,OAAO,OAAO;GAAE,GAAG;GAAG,aAAa;GAAO;EAAM,CAAC;EACxE,IAAI,UAAU,MAAM;GAClB,MAAM,OAAO,GAAG,KAAK;GACrB,OAAO;EACT;EACA,EAAE,IAAI,SAAS,KACb,YAAY,KAAK,GAAG,MAAM,IAAI,iBAAiB,KAAK,IAAI,MAAM,KAAK,SAAS,MAAM,MAAM,GAC1F;CACF;CACA,OAAO,mBAAmB,GAAG,OAAO,OAAO,CAAC;AAC9C;;AAGA,SAAS,mBAAmB,OAAe,OAAe,GAA2B;CACnF,MAAM,OAAO,EAAE,IAAI,eAAe,IAAI,KAAK;CAC3C,IAAI,SAAS,KAAA,GAAW,OAAO;CAC/B,MAAM,OAAO,MAAM,GAAG,IAAI;CAC1B,OAAO;EACL,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,OAAO,KAAK,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK;EACxE,OAAO;CACT;AACF;;;;;;;;;;;;;;AAeA,SAAS,WACP,IACA,OACA,GACc;CACd,MAAM,SAAmB,CAAC;CAC1B,KAAK,MAAM,UAAU,GAAG,SAAS;EAC/B,MAAM,KAAK,EAAE,SAAS,IAAI,MAAM,IAAI,UAAU,QAAQ,CAAC,IAAI,gBAAgB,QAAQ,CAAC;EACpF,IAAI,OAAO,MAAM,OAAO;EACxB,OAAO,KAAK,EAAE;CAChB;CACA,IAAI,OAAO,WAAW,GAAG,OAAO;CAEhC,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,IAAI,OAAO,GAAG,IAAI,GAAG,OAAO,GAAa,GAAG,MAAM;CAClD,KAAK,MAAM,MAAM,OAAO,MAAM,CAAC,GAC7B,QAAQ,MAAM,IAAI,KAAK,EAAE,KAAK,IAAI,IAAI,GAAG,GAAG,GAAG,MAAM;CAEvD,QAAQ,MAAM,IAAI,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK;CAC/C,OAAO;EAAE;EAAM,OAAO;CAAI;AAC5B;;;;;AAMA,SAAS,gBAAgB,IAAc,GAA4B;CACjE,MAAM,QAAQ,SAAS,EAAE,KAAK,IAAI;CAClC,MAAM,QAAmB;EAAE,OAAO,CAAC;EAAG,MAAM;CAAE;CAC9C,MAAM,OAAO,aAAa,IAAI,cAAc,OAAO,EAAE,KAAK,MAAM,KAAK,CAAC;CACtE,IAAI,SAAS,MAAM,OAAO;CAC1B,MAAM,OAAO,SAAS,EAAE,KAAK,IAAI;CACjC,EAAE,IAAI,SAAS,KACb,YAAY,KAAK,GAAG,MAAM,IAAI,iBAAiB,KAAK,EAAE,SAAS,SAAS,SAAS,QAAQ,IAAI,KAAK,IAAI,MAAM,GAAG,EAAE,OAAO,GAC1H;CACA,OAAO;AACT;;AAGA,SAAS,YAAY,IAAc,OAAe,GAA2B;CAE3E,MAAM,OAAO,aAAa,IADX,cAAc,OAAO,EAAE,KAAK,MAAM,EAAE,KAChB,CAAC;CACpC,IAAI,SAAS,MAAM,OAAO;CAC1B,OAAO;EAAE,MAAM,SAAS,SAAS,KAAK,QAAQ,KAAK,WAAW,EAAE,KAAK;EAAI,OAAO;CAAM;AACxF;;;;;;;AAQA,SAAS,YAAY,IAAc,OAAe,GAA2B;CAC3E,IAAI,GAAG,aAAa,KAAA,GAAW,OAAO;CACtC,IAAI,GAAG,qBAAqB,QAAQ,GAAG,WAAW,MAAM,OAAO;CAC/D,IAAI,GAAG,uBAAuB,KAAA,KAAa,GAAG,mBAAmB,SAAS,GAAG,OAAO;CAIpF,MAAM,UAAU,GAAG,UAAU,CAAC;CAC9B,IAAI,QAAQ,MAAM,UAAU,MAAM,SAAS,eAAe,GAAG,OAAO;CAEpE,IAAI,OAAO,aAAa,MAAM,eAAe,MAAM,yBAAyB,MAAM,WAAW,EAAE,KAAK;CAEpG,IAAI,GAAG,WAAW,MAAM;EACtB,MAAM,SAAS,MAAM,GAAG,IAAI;EAC5B,QAAQ,OAAO,OAAO,MAAM,MAAM,SAAS,kBAAkB,EAAE,KAAK,OAAO,KAAK,GAAG,UAAU,GAAG,MAAM,EAAE,WAAW,EAAE,KAAK;CAC5H;CAEA,MAAM,QAA8D,CAAC;CACrE,KAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAAQ,GAAG,UAAU,GAAG;EACzD,MAAM,SAAS,aAAa,GAAG;EAC/B,MAAM,OAAO,MAAM,GAAG,IAAI;EAC1B,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO;EACnC,MAAM,YAAY,MAAM,QAAQ,MAAM,CAAC;EACvC,IAAI,cAAc,MAAM,OAAO;EAC/B,QAAQ,UAAU;EAClB,MAAM,KAAK;GAAE,QAAQ,oBAAoB,MAAM;GAAG;GAAQ,OAAO,UAAU;EAAM,CAAC;CACpF;CAQA,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,MAAM,UAAoB,CAAC;CAC3B,IAAI,UAAU;CACd,IAAI,UAAU;CACd,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,WAAW,KAAK,QAAQ;GAC1B,QAAQ,KAAK,GAAG,KAAK,OAAO,GAAG,KAAK,OAAO;GAC3C;EACF;EACA,UAAU;EACV,WAAW,KAAK,SACZ,GAAG,IAAI,GAAG,KAAK,OAAO,IAAI,KAAK,MAAM,KACrC,MAAM,KAAK,MAAM,iBAAiB,KAAK,OAAO,MAAM,MAAM,KAAK,IAAI,GAAG,KAAK,OAAO,IAAI,KAAK,MAAM;CACvG;CACA,QAAQ,GAAG,IAAI,IAAI,QAAQ,KAAK,GAAG,EAAE,IAAI;CAIzC,KAAK,MAAM,SAAS,SAClB,QAAQ,OAAO,mBAAmB,EAAE,KAAK,KAA4B,EAAE,GAAG,IAAI,WAAW,EAAE,KAAK;CAElG,OAAO;EAAE;EAAM,OAAO;CAAI;AAC5B;AAEA,SAAS,WAAW,IAAkC,OAAe,GAA2B;CAM9F,IAAI,QAAQ;CACZ,MAAM,UAAiC,CAAC;CACxC,KAAK,MAAM,SAAS,GAAG,QACrB,QAAQ,MAAM,MAAd;EACE,KAAK;GACH,SAAS,MAAM,MAAM,UAAU,MAAM,QAAQ,UAAU,EAAE,KAAK;GAC9D;EACF,KAAK;GACH,SAAS,MAAM,MAAM,UAAU,MAAM,QAAQ,UAAU,EAAE,KAAK;GAC9D;EACF,KAAK;GACH,SAAS,MAAM,MAAM,YAAY,MAAM,OAAO,UAAU,EAAE,KAAK;GAC/D;EACF,KAAK;GACH,QAAQ,KAAK,KAAK;GAClB;EACF,SAEE,OAAO;CACX;CAGF,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,MAAM,QAAQ,MAAM,GAAG,IAAI;CAC3B,MAAM,OAAO,MAAM,GAAG,IAAI;CAC1B,MAAM,QAAQ,MAAM,GAAG,SAAS,MAAM,CAAC;CACvC,IAAI,UAAU,MAAM,OAAO;CAC3B,IAAI,OACF,qBAAqB,MAAM,WAAW,EAAE,KAAK,KAC7C,QACA,GAAG,IAAI,aAAa,MAAM,eACnB,MAAM,KAAK,MAAM,GAAG,MAAM,UAAU,MAAM,MAC9C,KAAK,GAAG,MAAM,GAAG,MAAM,IAAI,MAAM,OAAO,IAAI,GAAG,MAAM,IAAI,MAAM,MAAM;CAG1E,KAAK,MAAM,SAAS,SAClB,QAAQ,OAAO,mBAAmB,EAAE,KAAK,KAAK,EAAE,GAAG,IAAI,WAAW,EAAE,KAAK;CAE3E,OAAO;EAAE;EAAM,OAAO;CAAI;AAC5B;AAEA,SAAS,WAAW,IAAkC,OAAe,GAA2B;CAG9F,IAAI,GAAG,SAAS,MAAM,OAAO;CAC7B,IAAI,GAAG,MAAM,MAAM,SAAS,CAAC,iBAAiB,IAAI,CAAC,GAAG,OAAO;CAE7D,IAAI,OAAO,qBAAqB,MAAM,KAAK,MAAM,YAAY,GAAG,MAAM,OAAO,UAAU,EAAE,KAAK;CAC9F,MAAM,SAAmB,CAAC;CAC1B,KAAK,MAAM,CAAC,OAAO,WAAW,GAAG,MAAM,QAAQ,GAAG;EAChD,MAAM,OAAO,MAAM,GAAG,IAAI;EAC1B,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM;EAClC,MAAM,QAAQ,MAAM,QAAoB,MAAM,CAAC;EAC/C,IAAI,UAAU,MAAM,OAAO;EAC3B,QAAQ,MAAM;EACd,OAAO,KAAK,MAAM,KAAK;CACzB;CACA,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,QAAQ,GAAG,IAAI,IAAI,OAAO,KAAK,GAAG,EAAE;CACpC,OAAO;EAAE;EAAM,OAAO;CAAI;AAC5B;AAEA,SAAS,YAAY,IAAmC,OAAe,GAA2B;CAGhG,IAAI,EADF,GAAG,QAAQ,SAAS,YAAY,GAAG,QAAQ,OAAO,WAAW,KAAK,GAAG,QAAQ,WAAW,OACrE,OAAO;CAE5B,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,MAAM,SAAS,MAAM,GAAG,KAAK;CAC7B,MAAM,SAAS,MAAM,GAAG,KAAK;CAC7B,MAAM,QAAQ,MAAM,GAAG,WAAW,QAAQ,CAAC;CAC3C,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,MAAM,kBAAkB,EAAE,KAAK,WAAW,WAAW;CAM3D,OAAO;EAAE,MAAA,aAJM,MAAM,eAAe,MAAM,yBAAyB,MAAM,WAAW,EAAE,KAAK,GACtF,IAAI,UACA,OAAO,MAAM,MAAM,OAAO,IAAI,QAAQ,MAAM,GAAG,OAAO,KAC1D,OAAO,GAAG,MAAM,GAAG,OAAO,IAAI,MAAM,OAAO,IAAI,GAAG,OAAO,IAAI,MAAM,MAAM;EAC/D,OAAO;CAAI;AAC5B;;;;;;;;;;;;AAaA,SAAS,YAAY,IAAmC,OAAe,GAA2B;CAChG,MAAM,QAAQ,MAAM,GAAG,OAAO,OAAO,CAAC;CACtC,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,OAAO;EACL,MAAM,GAAG,MAAM,OAAO,IAAI,GAAG,mBAAmB,EAAE,KAAK,EAAE,EAAE,GAAG,MAAM,MAAM;EAC1E,OAAO;CACT;AACF;;;;;;;;;;;;;AAcA,SAAS,YAAY,IAAmC,OAAe,GAA2B;CAChG,MAAM,QAAQ,MAAM,GAAG,IAAI;CAC3B,IAAI,OACF,GAAG,WAAW,OACV,OAAO,MAAM,UAAU,MAAM,qBAAqB,EAAE,KAAK,MACzD,aAAa,MAAM,qBAAqB,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM;CACvE,KAAK,MAAM,SAAS,GAAG,QACrB,QAAQ,MAAM,MAAd;EACE,KAAK;GACH,QAAQ,GAAG,MAAM,GAAG,aAAa,EAAE,KAAK,MAAM,MAAM,EAAE,GAAG,MAAM;GAC/D;EACF,KAAK;GACH,QAAQ,OAAO,mBAAmB,EAAE,KAAK,KAAK,EAAE,GAAG,MAAM,WAAW,EAAE,KAAK;GAC3E;EACF,KAAK,uBAEH,OAAO;EACT,SAAS;GACP,MAAM,OAAO,gBAAgB,OAAO,OAAO,EAAE,GAAG;GAChD,IAAI,SAAS,MAAM,OAAO;GAC1B,QAAQ,QAAQ,KAAK,WAAW,EAAE,KAAK;EACzC;CACF;CAEF,OAAO;EAAE;EAAM;CAAM;AACvB;;;;;;;AAQA,SAAS,gBAAgB,IAAkB,OAAe,GAAoB;CAC5E,IAAI,OAAO,aAAa,MAAM,qBAAqB,EAAE,KAAK;CAC1D,IAAI,aAAa;CACjB,IAAI,CAAC,GAAG,eAAe;EACrB,aAAa,MAAM,GAAG,IAAI;EAC1B,QAAQ,GAAG,WAAW,GAAG,MAAM;CACjC;CAEA,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,IAAI,qBAAqB,EAAE,GAAG;EAC5B,MAAM,cAAc,WAClB,OAAO,KAAK,UAAU,GAAG,WAAW,KAAK,aAAa,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI;EAC3E,QACE,MAAM,WAAW,GAAG,MAAM,EAAE,IAAI,IAAI,iBACzB,WAAW,GAAG,KAAK,EAAE,IAAI,IAAI,sBACzB,EAAE,KAAK;CAC1B,OAAO;EACL,MAAM,SAAS,kBAAkB,IAAI,EAAE,GAAG;EAC1C,QAAQ,GAAG,IAAI,GAAG,OAAO,OAAO,WAAW,OAAO,IAAI,sBAAsB,EAAE,KAAK;CACrF;CACA,OAAO;EAAE;EAAM,OAAO;CAAI;AAC5B;;;;;;;;;;;AAeA,SAAS,sBAAsB,IAAwB,OAAe,GAA2B;CAC/F,IAAI,GAAG,WAAW,MAAM,OAAO;CAC/B,MAAM,QAAQ,MAAM,GAAG,IAAI;CAC3B,IAAI;CACJ,QAAQ,GAAG,MAAX;EACE,KAAK;GACH,aAAa,UAAU,MAAM;GAC7B;EACF,KAAK;GACH,aAAa,WAAW,MAAM;GAC9B;EACF,KAAK;GACH,aAAa,UAAU,MAAM;GAC7B;EACF,KAAK;GACH,aAAa,YAAY,MAAM;GAC/B;CACJ;CAKA,MAAM,YAAY,aAChB;EAAE,GAAG;EAAI,QAAQ;CAAM,GACvB,cAAc,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,CAC3C;CACA,IAAI,cAAc,MAAM,OAAO;CAE/B,MAAM,SAAS,GAAG,MAAM,GAAG,WAAW;CAEtC,OAAO;EACL,OAFW,GAAG,SAAS,YAAY,SAAS,OAAO,OAAO,mBAAmB,EAAE,KAAK,QAEtE,cAAc,SAAS,KAAK,QAAQ,UAAU,WAAW,EAAE,KAAK;EAC9E;CACF;AACF;;;;;;;;;;;;AAaA,SAAS,aACP,IACA,OACA,GACc;CACd,MAAM,QAAQ,MAAM,GAAG,OAAO,OAAO,CAAC;CACtC,IAAI,UAAU,MAAM,OAAO;CAI3B,EAAE,IAAI,wBAAwB;CAC9B,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,MAAM,QAAQ,iBAAiB,EAAE;CAGjC,MAAM,UAAU,sBAAsB,EAAE,IAAI,MAAM,IAAI,gBAAgB,IAAI,GAAG,MAAM,MAAM;CACzF,OAAO;EACL,MACE,MAAM,MAAM,gBAAgB,IAAI,GAAG,MAAM,SACjC,MAAM,OAAO,IAAI,GAAG,MAAM,MAAM,GAAG,QAAQ;EACrD,OAAO;CACT;AACF;;AAGA,SAAS,cACP,SACA,OACA,GACA,MACA,UACc;CACd,MAAM,QAAQ,MAAM,SAAS,OAAO,CAAC;CACrC,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,MAAM,MAAM,GAAG,IAAI;CACzB,OAAO;EACL,MAAM,MAAM,QAAQ,KAAK,IAAI,IAAI,GAAG,SAAS,SAAS,MAAM,OAAO,IAAI,GAAG,MAAM,MAAM;EACtF,OAAO;CACT;AACF;;AAGA,SAAS,MAAM,GAAa,QAAwB;CAClD,MAAM,OAAO,SAAS,EAAE,KAAK,MAAM;CACnC,EAAE,MAAM,MAAM,KAAK,IAAI;CACvB,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dedupe.js","names":[],"sources":["../../../src/core/codegen/dedupe.ts"],"sourcesContent":["/**\n * File-level schema deduplication.\n *\n * When several schemas in one file share structurally identical sub-trees\n * (a reused `Address` object, a `{ id, name }` row, an exported schema that is\n * also embedded in another), the error-collecting \"slow walk\" for that shape\n * is generated ONCE as a shared `__zcSw_N(input, path, issues)` function and\n * every occurrence calls it instead of inlining a fresh copy.\n *\n * Two invariants keep this free of runtime cost and footguns:\n *\n * 1. **Slow walk only.** The fast path (the `&&` boolean chain that validates\n * valid input with zero allocations) is NEVER shared — it stays fully\n * inlined so the hot path is byte-for-byte what it was before. Only the\n * cold, bulky error walk (64–77% of generated bytes) is shared.\n *\n * 2. **No per-export references.** A shared body lives at MODULE scope, while\n * `__rf[]` — the array of retained Zod schemas and captured callbacks — is\n * declared inside each export's IIFE. So a shape may be shared only when\n * nothing in it resolves through `__rf`: no `default`, `catch`, `fallback`,\n * captured `transform`, or captured `refine`/`superRefine`. Zero-capture\n * callbacks are fine — they are hosted from source text into the shared\n * block's own preamble.\n *\n * Sharing used to additionally require the whole ROOT to be mutation-free, so\n * that a body which wrote nothing back could not lose an output value. That\n * excluded a genuine `z.object()` — it strips, so its walk PRODUCES a rebuilt\n * object — which, once `z.object()` began always stripping, meant the feature\n * stopped firing for essentially every real schema: a realistic module of four\n * schemas reusing two sub-shapes shared nothing and shipped 29.6 KB where the\n * `strictObject` spelling of the same thing shipped 16.7 KB.\n *\n * The write-back problem is solved directly instead: a shared walk RETURNS its\n * value and call sites assign it (`slot = __zcSw_N(slot, path, issues)`), so a\n * stripping object, a coercion or a `.trim()` delivers its rewritten value\n * through the call exactly as an inlined walk delivers it through `g.output`.\n */\n\nimport type { SchemaIR } from \"../types.js\";\nimport type { CodeGenContext, CodegenMode } from \"./context.js\";\nimport { createSlowGen, generateSlow } from \"./slow-path.js\";\n\n/** A shared slow-walk validator hosted at module scope. */\nexport interface SharedSchemaRef {\n /** Name of the `function __zcSw_N(input, path, issues)` declaration. */\n readonly name: string;\n}\n\nexport interface SharedSchemaPlan {\n /**\n * Shared ref for an IR node, or undefined when the node is not a shared\n * candidate. Keyed by structural identity (memoized), so repeated lookups\n * during codegen are O(1).\n */\n refFor(ir: SchemaIR): SharedSchemaRef | undefined;\n /** Module-scope declarations (shared preamble + `__zcSw_N` functions). `\"\"` when empty. */\n readonly code: string;\n /** Runtime helper names referenced by the shared block (lean mode). */\n readonly usedHelpers: Set<string>;\n readonly isEmpty: boolean;\n}\n\n/**\n * Minimum subtree weight (≈ node count, +1 per check) for a repeated shape to\n * be worth a shared function. Below this the call + dynamic-path overhead\n * outweighs the duplicated inline walk; ~4 admits a 3-field object or an\n * array-of-object while skipping bare primitives and thin wrappers.\n */\nconst MIN_SHARED_WEIGHT = 4;\n\n/** Leading marker of a shared block; used to skip re-injection on watch/HMR re-runs. */\nexport const SHARED_BLOCK_MARKER = \"/* zod-compiler shared */\";\n\nconst EMPTY_PLAN: SharedSchemaPlan = {\n refFor: () => undefined,\n code: \"\",\n usedHelpers: new Set(),\n isEmpty: true,\n};\n\n/**\n * Build the shared-schema plan for one file's root schemas. Returns\n * {@link EMPTY_PLAN} (a no-op) when nothing repeats — the common case, so files\n * without internal duplication pay only a single linear analysis pass.\n */\nexport function createSharedSchemaPlan(\n roots: readonly SchemaIR[],\n mode: CodegenMode,\n): SharedSchemaPlan {\n const keyMemo = new Map<SchemaIR, string>();\n const weightMemo = new Map<SchemaIR, number>();\n const stats = new Map<string, { ir: SchemaIR; count: number; weight: number }>();\n\n for (const root of roots) {\n collect(root, keyMemo, weightMemo, stats);\n }\n\n const selected = [...stats.values()]\n .filter(\n (c) =>\n c.count >= 2 &&\n c.weight >= MIN_SHARED_WEIGHT &&\n !containsRecursiveRef(c.ir) &&\n !referencesExportRefs(c.ir),\n )\n // Largest shapes first so the biggest wins get the lowest indices; tie-break\n // on the structural key for deterministic, reproducible output.\n .sort((a, b) => b.weight - a.weight || cmp(keyOf(a.ir, keyMemo), keyOf(b.ir, keyMemo)));\n\n if (selected.length === 0) return EMPTY_PLAN;\n\n const refs = new Map<string, SharedSchemaRef>();\n selected.forEach((c, i) => refs.set(keyOf(c.ir, keyMemo), { name: `__zcSw_${i}` }));\n\n const refFor = (ir: SchemaIR): SharedSchemaRef | undefined => refs.get(keyOf(ir, keyMemo));\n\n const ctx: CodeGenContext = {\n preamble: [],\n counter: 0,\n fnName: \"__zcShared\",\n regexCache: new Map(),\n mode,\n usedHelpers: new Set(),\n };\n // Generation consults the plan so a shared candidate nested inside another\n // shared candidate becomes a `__zcSw_*` call rather than a second inline copy.\n // Containment is strictly size-decreasing, so the shared functions form a DAG;\n // hoisted declarations make call order irrelevant.\n ctx.sharedSchemas = { refFor, code: \"\", usedHelpers: ctx.usedHelpers, isEmpty: false };\n\n const fns: string[] = [];\n for (const c of selected) {\n const ref = refs.get(keyOf(c.ir, keyMemo));\n if (ref === undefined) continue;\n // Generate the candidate's body INLINE (the root is passed to generateSlow,\n // not visit(), so it never replaces itself with a call); only its children\n // route through visit() and may resolve to other shared refs.\n const body = generateSlow(c.ir, createSlowGen(\"input\", \"input\", \"path\", \"_e\", ctx));\n // `input` is both the read and the write slot (createSlowGen above passes it\n // as input AND output), so a walk that rewrites — a stripping object, a\n // coercion, an overwrite check — leaves its result there. Returning it is\n // what lets the caller receive that value; a walk that rewrites nothing\n // returns its argument unchanged.\n fns.push(`function ${ref.name}(input,path,_e){\\n${body}\\nreturn input;\\n}`);\n }\n\n return {\n refFor,\n code: [SHARED_BLOCK_MARKER, ...ctx.preamble, ...fns].join(\"\\n\"),\n usedHelpers: ctx.usedHelpers,\n isEmpty: false,\n };\n}\n\n// A shared sub-schema is called as `slot = __zcSw_N(slot, path, issues)`: the\n// walk pushes directly into the caller's `issues` array (no result object, no\n// per-call issue copy) and returns the parsed value, which the call site assigns\n// back into the slot the inlined walk would have written. Call sites are emitted\n// by slow-path.ts (nested occurrences) and index.ts (a shared root), keyed off\n// `ref.name`.\n\n/**\n * Does the subtree resolve anything through the per-export `__rf[]` array —\n * a retained Zod schema (`default`, `catch`, `fallback`) or a captured callback\n * (`transform`, `refine`, `superRefine`)?\n *\n * Those names exist only inside an export's own IIFE, so a module-scope shared\n * body referencing them would be unbound. A zero-capture callback carries\n * `source` instead of `refIndex` and is hosted into the shared block's preamble,\n * so it does not disqualify a shape.\n */\nfunction referencesExportRefs(ir: SchemaIR): boolean {\n switch (ir.type) {\n case \"default\":\n case \"catch\":\n case \"fallback\":\n // `refIndex` is mandatory on these — the value/schema lives on the export.\n return true;\n case \"effect\":\n if (ir.refIndex !== undefined) return true;\n break;\n case \"string\":\n case \"number\":\n case \"object\":\n case \"array\":\n if (ir.checks?.some((c) => \"refIndex\" in c && c.refIndex !== undefined)) return true;\n break;\n default:\n break;\n }\n return childSchemas(ir).some(referencesExportRefs);\n}\n\n// ─── Candidate collection ────────────────────────────────────────────────────\n\nfunction collect(\n ir: SchemaIR,\n keyMemo: Map<SchemaIR, string>,\n weightMemo: Map<SchemaIR, number>,\n stats: Map<string, { ir: SchemaIR; count: number; weight: number }>,\n): void {\n const key = keyOf(ir, keyMemo);\n const existing = stats.get(key);\n if (existing) {\n existing.count++;\n } else {\n stats.set(key, { ir, count: 1, weight: weightOf(ir, weightMemo) });\n }\n for (const child of childSchemas(ir)) collect(child, keyMemo, weightMemo, stats);\n}\n\n/** Direct child SchemaIR nodes (the structural recursion shared by every pass). */\nfunction childSchemas(ir: SchemaIR): SchemaIR[] {\n switch (ir.type) {\n case \"object\":\n return Object.values(ir.properties);\n case \"array\":\n return [ir.element];\n case \"tuple\":\n return ir.rest === null ? ir.items : [...ir.items, ir.rest];\n case \"record\":\n case \"map\":\n return [ir.keyType, ir.valueType];\n case \"set\":\n return [ir.valueType];\n case \"union\":\n case \"discriminatedUnion\":\n return ir.options;\n case \"intersection\":\n return [ir.left, ir.right];\n case \"optional\":\n case \"nullable\":\n case \"readonly\":\n case \"default\":\n case \"catch\":\n return [ir.inner];\n case \"pipe\":\n return [ir.in, ir.out];\n case \"effect\":\n case \"recursionTarget\":\n return [ir.inner];\n default:\n return [];\n }\n}\n\n/** Subtree weight ≈ node count plus one per check; memoized for O(total) cost. */\nfunction weightOf(ir: SchemaIR, memo: Map<SchemaIR, number>): number {\n const hit = memo.get(ir);\n if (hit !== undefined) return hit;\n const checks = (ir as { checks?: readonly unknown[] }).checks;\n let weight = 1 + (Array.isArray(checks) ? checks.length : 0);\n for (const child of childSchemas(ir)) weight += weightOf(child, memo);\n memo.set(ir, weight);\n return weight;\n}\n\nfunction containsRecursiveRef(ir: SchemaIR): boolean {\n if (ir.type === \"recursiveRef\") return true;\n return childSchemas(ir).some(containsRecursiveRef);\n}\n\n// ─── Structural keys ─────────────────────────────────────────────────────────\n\n/**\n * Stable structural key for an IR node, memoized by node identity so each\n * subtree is serialized exactly once (the whole pass is O(total nodes), not\n * O(nodes × depth)). Two nodes share a key iff they are structurally identical\n * — including every check, literal value, and error message — so deduplicated\n * schemas produce byte-identical validation and issues.\n */\nfunction keyOf(ir: SchemaIR, memo: Map<SchemaIR, string>): string {\n const hit = memo.get(ir);\n if (hit !== undefined) return hit;\n const obj = ir as unknown as Record<string, unknown>;\n const key = `{${Object.keys(obj)\n .filter((k) => obj[k] !== undefined)\n .sort()\n .map((k) => `${k}:${encodeValue(obj[k], memo)}`)\n .join(\",\")}}`;\n memo.set(ir, key);\n return key;\n}\n\n/**\n * Encode any IR field value. A nested SchemaIR is detected by a string `type`\n * field (checks carry `kind`, union `cases` carry `value`/`option` — only real\n * schema nodes have `type`) and routed through {@link keyOf} for subtree\n * memoization; everything else is serialized in sorted-key order.\n */\nfunction encodeValue(value: unknown, memo: Map<SchemaIR, string>): string {\n if (typeof value === \"bigint\") return `#${value}`;\n if (value === null) return \"null\";\n // Non-finite numbers must stay distinct: JSON.stringify maps Infinity, -Infinity\n // and NaN all to \"null\", which would merge e.g. .min(Infinity) with .min(NaN).\n if (typeof value === \"number\" && !Number.isFinite(value)) return `n!${value}`;\n if (Array.isArray(value)) return `[${value.map((v) => encodeValue(v, memo)).join(\",\")}]`;\n if (typeof value === \"object\") {\n if (typeof (value as { type?: unknown }).type === \"string\") {\n return keyOf(value as SchemaIR, memo);\n }\n const obj = value as Record<string, unknown>;\n return `{${Object.keys(obj)\n .filter((k) => obj[k] !== undefined)\n .sort()\n .map((k) => `${k}:${encodeValue(obj[k], memo)}`)\n .join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"undefined\";\n}\n\nfunction cmp(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dedupe.js","names":[],"sources":["../../../src/core/codegen/dedupe.ts"],"sourcesContent":["/**\n * File-level schema deduplication.\n *\n * When several schemas in one file share structurally identical sub-trees\n * (a reused `Address` object, a `{ id, name }` row, an exported schema that is\n * also embedded in another), the error-collecting \"slow walk\" for that shape\n * is generated ONCE as a shared `__zcSw_N(input, path, issues)` function and\n * every occurrence calls it instead of inlining a fresh copy.\n *\n * Two invariants keep this free of runtime cost and footguns:\n *\n * 1. **Slow walk only.** The fast path (the `&&` boolean chain that validates\n * valid input with zero allocations) is NEVER shared — it stays fully\n * inlined so the hot path is byte-for-byte what it was before. Only the\n * cold, bulky error walk (64–77% of generated bytes) is shared.\n *\n * 2. **No per-export references.** A shared body lives at MODULE scope, while\n * `__rf[]` — the array of retained Zod schemas and captured callbacks — is\n * declared inside each export's IIFE. So a shape may be shared only when\n * nothing in it resolves through `__rf`: no `default`, `catch`, `fallback`,\n * captured `transform`, or captured `refine`/`superRefine`. Zero-capture\n * callbacks are fine — they are hosted from source text into the shared\n * block's own preamble.\n *\n * Sharing used to additionally require the whole ROOT to be mutation-free, so\n * that a body which wrote nothing back could not lose an output value. That\n * excluded a genuine `z.object()` — it strips, so its walk PRODUCES a rebuilt\n * object — which, once `z.object()` began always stripping, meant the feature\n * stopped firing for essentially every real schema: a realistic module of four\n * schemas reusing two sub-shapes shared nothing and shipped 29.6 KB where the\n * `strictObject` spelling of the same thing shipped 16.7 KB.\n *\n * The write-back problem is solved directly instead: a shared walk RETURNS its\n * value and call sites assign it (`slot = __zcSw_N(slot, path, issues)`), so a\n * stripping object, a coercion or a `.trim()` delivers its rewritten value\n * through the call exactly as an inlined walk delivers it through `g.output`.\n */\n\nimport type { SchemaIR } from \"../types.js\";\nimport type { CodeGenContext, CodegenMode } from \"./context.js\";\nimport { createSlowGen, generateSlow } from \"./slow-path.js\";\n\n/** A shared slow-walk validator hosted at module scope. */\nexport interface SharedSchemaRef {\n /** Name of the `function __zcSw_N(input, path, issues)` declaration. */\n readonly name: string;\n}\n\nexport interface SharedSchemaPlan {\n /**\n * Shared ref for an IR node, or undefined when the node is not a shared\n * candidate. Keyed by structural identity (memoized), so repeated lookups\n * during codegen are O(1).\n */\n refFor(ir: SchemaIR): SharedSchemaRef | undefined;\n /** Module-scope declarations (shared preamble + `__zcSw_N` functions). `\"\"` when empty. */\n readonly code: string;\n /** Runtime helper names referenced by the shared block (lean mode). */\n readonly usedHelpers: Set<string>;\n readonly isEmpty: boolean;\n}\n\n/**\n * Minimum subtree weight (≈ node count, +1 per check) for a repeated shape to\n * be worth a shared function. Below this the call + dynamic-path overhead\n * outweighs the duplicated inline walk; ~4 admits a 3-field object or an\n * array-of-object while skipping bare primitives and thin wrappers.\n */\nconst MIN_SHARED_WEIGHT = 4;\n\n/** Leading marker of a shared block; used to skip re-injection on watch/HMR re-runs. */\nexport const SHARED_BLOCK_MARKER = \"/* zod-compiler shared */\";\n\nconst EMPTY_PLAN: SharedSchemaPlan = {\n refFor: () => undefined,\n code: \"\",\n usedHelpers: new Set(),\n isEmpty: true,\n};\n\n/**\n * Build the shared-schema plan for one file's root schemas. Returns\n * {@link EMPTY_PLAN} (a no-op) when nothing repeats — the common case, so files\n * without internal duplication pay only a single linear analysis pass.\n */\nexport function createSharedSchemaPlan(\n roots: readonly SchemaIR[],\n mode: CodegenMode,\n): SharedSchemaPlan {\n const keyMemo = new Map<SchemaIR, string>();\n const weightMemo = new Map<SchemaIR, number>();\n const stats = new Map<string, { ir: SchemaIR; count: number; weight: number }>();\n\n for (const root of roots) {\n collect(root, keyMemo, weightMemo, stats);\n }\n\n const selected = [...stats.values()]\n .filter(\n (c) =>\n c.count >= 2 &&\n c.weight >= MIN_SHARED_WEIGHT &&\n !containsRecursiveRef(c.ir) &&\n !referencesExportRefs(c.ir),\n )\n // Largest shapes first so the biggest wins get the lowest indices; tie-break\n // on the structural key for deterministic, reproducible output.\n .sort((a, b) => b.weight - a.weight || cmp(keyOf(a.ir, keyMemo), keyOf(b.ir, keyMemo)));\n\n if (selected.length === 0) return EMPTY_PLAN;\n\n const refs = new Map<string, SharedSchemaRef>();\n selected.forEach((c, i) => refs.set(keyOf(c.ir, keyMemo), { name: `__zcSw_${i}` }));\n\n const refFor = (ir: SchemaIR): SharedSchemaRef | undefined => refs.get(keyOf(ir, keyMemo));\n\n const ctx: CodeGenContext = {\n preamble: [],\n counter: 0,\n fnName: \"__zcShared\",\n regexCache: new Map(),\n mode,\n usedHelpers: new Set(),\n };\n // Generation consults the plan so a shared candidate nested inside another\n // shared candidate becomes a `__zcSw_*` call rather than a second inline copy.\n // Containment is strictly size-decreasing, so the shared functions form a DAG;\n // hoisted declarations make call order irrelevant.\n ctx.sharedSchemas = { refFor, code: \"\", usedHelpers: ctx.usedHelpers, isEmpty: false };\n\n const fns: string[] = [];\n for (const c of selected) {\n const ref = refs.get(keyOf(c.ir, keyMemo));\n if (ref === undefined) continue;\n // Generate the candidate's body INLINE (the root is passed to generateSlow,\n // not visit(), so it never replaces itself with a call); only its children\n // route through visit() and may resolve to other shared refs.\n const body = generateSlow(c.ir, createSlowGen(\"input\", \"input\", \"path\", \"_e\", ctx));\n // `input` is both the read and the write slot (createSlowGen above passes it\n // as input AND output), so a walk that rewrites — a stripping object, a\n // coercion, an overwrite check — leaves its result there. Returning it is\n // what lets the caller receive that value; a walk that rewrites nothing\n // returns its argument unchanged.\n fns.push(`function ${ref.name}(input,path,_e){\\n${body}\\nreturn input;\\n}`);\n }\n\n return {\n refFor,\n code: [SHARED_BLOCK_MARKER, ...ctx.preamble, ...fns].join(\"\\n\"),\n usedHelpers: ctx.usedHelpers,\n isEmpty: false,\n };\n}\n\n// A shared sub-schema is called as `slot = __zcSw_N(slot, path, issues)`: the\n// walk pushes directly into the caller's `issues` array (no result object, no\n// per-call issue copy) and returns the parsed value, which the call site assigns\n// back into the slot the inlined walk would have written. Call sites are emitted\n// by slow-path.ts (nested occurrences) and index.ts (a shared root), keyed off\n// `ref.name`.\n\n/**\n * Does the subtree resolve anything through the per-export `__rf[]` array —\n * a retained Zod schema (`default`, `catch`, `fallback`) or a captured callback\n * (`transform`, `refine`, `superRefine`)?\n *\n * Those names exist only inside an export's own IIFE, so a module-scope shared\n * body referencing them would be unbound. A zero-capture callback carries\n * `source` instead of `refIndex` and is hosted into the shared block's preamble,\n * so it does not disqualify a shape.\n */\nfunction referencesExportRefs(ir: SchemaIR): boolean {\n switch (ir.type) {\n case \"default\":\n case \"catch\":\n case \"fallback\":\n // `refIndex` is mandatory on these — the value/schema lives on the export.\n return true;\n case \"custom\":\n // Both the predicate and pristine error delegate live on the export.\n return true;\n case \"effect\":\n if (ir.refIndex !== undefined) return true;\n break;\n case \"string\":\n case \"number\":\n case \"object\":\n case \"array\":\n if (ir.checks?.some((c) => \"refIndex\" in c && c.refIndex !== undefined)) return true;\n break;\n default:\n break;\n }\n return childSchemas(ir).some(referencesExportRefs);\n}\n\n// ─── Candidate collection ────────────────────────────────────────────────────\n\nfunction collect(\n ir: SchemaIR,\n keyMemo: Map<SchemaIR, string>,\n weightMemo: Map<SchemaIR, number>,\n stats: Map<string, { ir: SchemaIR; count: number; weight: number }>,\n): void {\n const key = keyOf(ir, keyMemo);\n const existing = stats.get(key);\n if (existing) {\n existing.count++;\n } else {\n stats.set(key, { ir, count: 1, weight: weightOf(ir, weightMemo) });\n }\n for (const child of childSchemas(ir)) collect(child, keyMemo, weightMemo, stats);\n}\n\n/** Direct child SchemaIR nodes (the structural recursion shared by every pass). */\nfunction childSchemas(ir: SchemaIR): SchemaIR[] {\n switch (ir.type) {\n case \"object\":\n return Object.values(ir.properties);\n case \"array\":\n return [ir.element];\n case \"tuple\":\n return ir.rest === null ? ir.items : [...ir.items, ir.rest];\n case \"record\":\n case \"map\":\n return [ir.keyType, ir.valueType];\n case \"set\":\n return [ir.valueType];\n case \"union\":\n case \"discriminatedUnion\":\n return ir.options;\n case \"intersection\":\n return [ir.left, ir.right];\n case \"optional\":\n case \"nullable\":\n case \"readonly\":\n case \"default\":\n case \"catch\":\n return [ir.inner];\n case \"pipe\":\n return [ir.in, ir.out];\n case \"effect\":\n case \"recursionTarget\":\n return [ir.inner];\n default:\n return [];\n }\n}\n\n/** Subtree weight ≈ node count plus one per check; memoized for O(total) cost. */\nfunction weightOf(ir: SchemaIR, memo: Map<SchemaIR, number>): number {\n const hit = memo.get(ir);\n if (hit !== undefined) return hit;\n const checks = (ir as { checks?: readonly unknown[] }).checks;\n let weight = 1 + (Array.isArray(checks) ? checks.length : 0);\n for (const child of childSchemas(ir)) weight += weightOf(child, memo);\n memo.set(ir, weight);\n return weight;\n}\n\nfunction containsRecursiveRef(ir: SchemaIR): boolean {\n if (ir.type === \"recursiveRef\") return true;\n return childSchemas(ir).some(containsRecursiveRef);\n}\n\n// ─── Structural keys ─────────────────────────────────────────────────────────\n\n/**\n * Stable structural key for an IR node, memoized by node identity so each\n * subtree is serialized exactly once (the whole pass is O(total nodes), not\n * O(nodes × depth)). Two nodes share a key iff they are structurally identical\n * — including every check, literal value, and error message — so deduplicated\n * schemas produce byte-identical validation and issues.\n */\nfunction keyOf(ir: SchemaIR, memo: Map<SchemaIR, string>): string {\n const hit = memo.get(ir);\n if (hit !== undefined) return hit;\n const obj = ir as unknown as Record<string, unknown>;\n const key = `{${Object.keys(obj)\n .filter((k) => obj[k] !== undefined)\n .sort()\n .map((k) => `${k}:${encodeValue(obj[k], memo)}`)\n .join(\",\")}}`;\n memo.set(ir, key);\n return key;\n}\n\n/**\n * Encode any IR field value. A nested SchemaIR is detected by a string `type`\n * field (checks carry `kind`, union `cases` carry `value`/`option` — only real\n * schema nodes have `type`) and routed through {@link keyOf} for subtree\n * memoization; everything else is serialized in sorted-key order.\n */\nfunction encodeValue(value: unknown, memo: Map<SchemaIR, string>): string {\n if (typeof value === \"bigint\") return `#${value}`;\n if (value === null) return \"null\";\n // Non-finite numbers must stay distinct: JSON.stringify maps Infinity, -Infinity\n // and NaN all to \"null\", which would merge e.g. .min(Infinity) with .min(NaN).\n if (typeof value === \"number\" && !Number.isFinite(value)) return `n!${value}`;\n if (Array.isArray(value)) return `[${value.map((v) => encodeValue(v, memo)).join(\",\")}]`;\n if (typeof value === \"object\") {\n if (typeof (value as { type?: unknown }).type === \"string\") {\n return keyOf(value as SchemaIR, memo);\n }\n const obj = value as Record<string, unknown>;\n return `{${Object.keys(obj)\n .filter((k) => obj[k] !== undefined)\n .sort()\n .map((k) => `${k}:${encodeValue(obj[k], memo)}`)\n .join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"undefined\";\n}\n\nfunction cmp(a: string, b: string): number {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-path.d.ts","names":[],"sources":["../../../src/core/codegen/fast-path.ts"],"mappings":";;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"fast-path.d.ts","names":[],"sources":["../../../src/core/codegen/fast-path.ts"],"mappings":";;;;;;;;;;;;;iBAwHgB,cACd,mBACA,KAAK,gBACL,uBACA,QAAO,WACP,uBACC;;;;;;;;;;;;;;;;;iBA8Ca,aAAa,IAAI,UAAU,GAAG"}
|
|
@@ -4,6 +4,7 @@ import { fastArray } from "./schemas/array.js";
|
|
|
4
4
|
import { fastBigInt } from "./schemas/bigint.js";
|
|
5
5
|
import { fastBoolean } from "./schemas/boolean.js";
|
|
6
6
|
import { fastCatch } from "./schemas/catch.js";
|
|
7
|
+
import { fastCustom } from "./schemas/custom.js";
|
|
7
8
|
import { fastDate } from "./schemas/date.js";
|
|
8
9
|
import { fastDefault } from "./schemas/default.js";
|
|
9
10
|
import { fastDiscriminatedUnion } from "./schemas/discriminated-union.js";
|
|
@@ -66,6 +67,7 @@ const fastRegistry = {
|
|
|
66
67
|
default: fastDefault,
|
|
67
68
|
pipe: fastPipe,
|
|
68
69
|
effect: null,
|
|
70
|
+
custom: fastCustom,
|
|
69
71
|
templateLiteral: fastTemplateLiteral,
|
|
70
72
|
catch: fastCatch,
|
|
71
73
|
fallback: null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-path.js","names":[],"sources":["../../../src/core/codegen/fast-path.ts"],"sourcesContent":["/**\n * Fast Path: produces a boolean expression string for eligible schemas.\n * When the expression evaluates to `true` at runtime, the input is valid and\n * safeParse can return success immediately without allocating an issues array.\n *\n * Returns `null` if the schema is not eligible for Fast Path (contains coerce,\n * default, transform, or other non-pure constructs).\n */\n\nimport type { SchemaIR } from \"../types.js\";\nimport type { CodeGenContext, FastGen, FastGenerator, FastScope } from \"./context.js\";\nimport { declareFastTemps, emitRegex, emitTemp } from \"./context.js\";\nimport {\n CALL_COST,\n EXTRACT_CAP,\n estimateFastCost,\n HOISTABLE,\n MIN_EXTRACT,\n predictedInlineSize,\n} from \"./fast-size.js\";\nimport { fastAny } from \"./schemas/any.js\";\nimport { fastArray } from \"./schemas/array.js\";\nimport { fastBigInt } from \"./schemas/bigint.js\";\nimport { fastBoolean } from \"./schemas/boolean.js\";\nimport { fastCatch } from \"./schemas/catch.js\";\nimport { fastDate } from \"./schemas/date.js\";\nimport { fastDefault } from \"./schemas/default.js\";\nimport { fastDiscriminatedUnion } from \"./schemas/discriminated-union.js\";\nimport { fastEnum } from \"./schemas/enum.js\";\nimport { fastFile } from \"./schemas/file.js\";\nimport { fastIntersection } from \"./schemas/intersection.js\";\nimport { fastLiteral } from \"./schemas/literal.js\";\nimport { fastMap } from \"./schemas/map.js\";\nimport { fastNan } from \"./schemas/nan.js\";\nimport { fastNever } from \"./schemas/never.js\";\nimport { fastNull } from \"./schemas/null.js\";\nimport { fastNullable } from \"./schemas/nullable.js\";\nimport { fastNumber } from \"./schemas/number.js\";\nimport { fastObject } from \"./schemas/object.js\";\nimport { fastOptional } from \"./schemas/optional.js\";\nimport { fastPipe } from \"./schemas/pipe.js\";\nimport { fastReadonly } from \"./schemas/readonly.js\";\nimport { fastRecord } from \"./schemas/record.js\";\nimport { fastRecursionTarget, fastRecursiveRef } from \"./schemas/recursive-ref.js\";\nimport { fastSet } from \"./schemas/set.js\";\nimport { fastString } from \"./schemas/string.js\";\nimport { fastSymbol } from \"./schemas/symbol.js\";\nimport { fastTemplateLiteral } from \"./schemas/template-literal.js\";\nimport { fastTuple } from \"./schemas/tuple.js\";\nimport { fastUndefined } from \"./schemas/undefined.js\";\nimport { fastUnion } from \"./schemas/union.js\";\nimport { fastUnknown } from \"./schemas/unknown.js\";\nimport { fastVoid } from \"./schemas/void.js\";\n\n// ─── Typed registry ─────────────────────────────────────────────────────────\n// `null` = statically ineligible (the type NEVER has a fast path).\n// Non-null function = may return null at runtime (dynamically ineligible, e.g. when coerce is present).\n\nconst fastRegistry = {\n // Primitives (order follows SchemaIR union in types.ts)\n string: fastString,\n number: fastNumber,\n boolean: fastBoolean,\n bigint: fastBigInt,\n date: fastDate,\n symbol: fastSymbol,\n null: fastNull,\n undefined: fastUndefined,\n void: fastVoid,\n nan: fastNan,\n never: fastNever,\n any: fastAny,\n unknown: fastUnknown,\n literal: fastLiteral,\n enum: fastEnum,\n // Containers\n object: fastObject,\n array: fastArray,\n tuple: fastTuple,\n record: fastRecord,\n set: fastSet,\n map: fastMap,\n file: fastFile,\n // Unions & Intersections\n union: fastUnion,\n discriminatedUnion: fastDiscriminatedUnion,\n intersection: fastIntersection,\n // Modifiers\n optional: fastOptional,\n nullable: fastNullable,\n readonly: fastReadonly,\n default: fastDefault,\n pipe: fastPipe,\n // Effects\n effect: null, // statically ineligible\n // Special\n templateLiteral: fastTemplateLiteral,\n catch: fastCatch,\n fallback: null, // statically ineligible\n recursiveRef: fastRecursiveRef,\n recursionTarget: fastRecursionTarget,\n stringBool: null, // statically ineligible — output type (boolean) differs from input (string)\n} satisfies {\n [K in SchemaIR[\"type\"]]: FastGenerator<Extract<SchemaIR, { type: K }>> | null;\n};\n\n// ─── Factory ─────────────────────────────────────────────────────────────────\n\n/**\n * @param inputExpr the input expression this node validates\n * @param ctx shared codegen context\n * @param extractable whether THIS node may be hoisted (false for the root and a\n * helper's own top node — see generateFast); children are always extractable\n * @param scope size accumulator for the function being assembled (a child\n * visit shares the parent's scope; a hoisted helper body gets a fresh one)\n * @param discSkipKey discriminated-union option marker (see FastGen.discSkipKey);\n * carried only on the option's own node, never auto-propagated to children\n */\nexport function createFastGen(\n inputExpr: string,\n ctx: CodeGenContext,\n extractable = false,\n scope: FastScope = { temps: [], used: 0 },\n discSkipKey?: string,\n): FastGen {\n return {\n input: inputExpr,\n ctx,\n extractable,\n scope,\n discSkipKey,\n visit(ir, overrides) {\n // A child shares this function's scope and is itself extractable. The\n // discriminated-union skip is NOT inherited implicitly — only a caller\n // that explicitly passes overrides.discSkipKey (fastDiscriminatedUnion)\n // sets it, so nested objects keep their own type-guard.\n return generateFast(\n ir,\n createFastGen(overrides?.input ?? inputExpr, ctx, true, scope, overrides?.discSkipKey),\n );\n },\n scoped: (input) => createFastGen(input, ctx, true, { temps: [], used: 0 }),\n temp: (prefix) => emitTemp(ctx, prefix),\n local(prefix) {\n const name = emitTemp(ctx, prefix);\n scope.temps.push(name);\n return name;\n },\n regex: (prefix, pattern, flags) => emitRegex(ctx, prefix, pattern, flags),\n };\n}\n\n// ─── Dispatch ────────────────────────────────────────────────────────────────\n\n/**\n * Generate a boolean expression string that validates input against the schema.\n * Returns `null` if the schema (or any nested part) is not eligible for fast checking.\n *\n * Size-gated extraction: when inlining `ir` would push the function being\n * assembled past EXTRACT_CAP, it is hoisted into its own boolean helper\n * `function __fo_N(p){return <expr over p>;}` and replaced by a call. This keeps\n * every emitted function under V8's TurboFan optimization budget — a single\n * giant fast-check otherwise drops to the slower Maglev tier (measured ~3-4.5x\n * on deeply-nested schemas). The running total `g.scope.used` is EXACT emitted\n * chars (the returned string's length); only the extract look-ahead is an\n * estimate, scaled for access-path length. Small schemas never approach the\n * cap, so their output is byte-identical to the fully-inlined form; large\n * aggregates do split (still semantically identical — only the boolean's shape\n * changes).\n */\nexport function generateFast(ir: SchemaIR, g: FastGen): string | null {\n const gen = fastRegistry[ir.type];\n if (gen === null) return null;\n\n if (g.extractable && HOISTABLE.has(ir.type)) {\n const cache = fastSizeCache(g.ctx);\n const overBudget = g.scope.used + predictedInlineSize(ir, g.input.length, cache) > EXTRACT_CAP;\n // Extract a sub-schema big enough to be worth a call; but once the function\n // is ALREADY over budget, extract any hoistable child regardless of size —\n // otherwise a crowd of small siblings (e.g. a discriminated union of many\n // small options) grows the function unbounded since none alone clears\n // MIN_EXTRACT.\n if (overBudget && (g.scope.used > EXTRACT_CAP || estimateFastCost(ir, cache) >= MIN_EXTRACT)) {\n const param = emitTemp(g.ctx, \"op\");\n // Generate the body relative to the helper's parameter, in a fresh scope;\n // the top node is non-extractable (it IS this helper) while its children\n // stay extractable, so an oversized helper splits further recursively.\n // Carry discSkipKey so an extracted discriminated-union option still drops\n // its redundant guard inside the hoisted helper.\n const innerGen = createFastGen(param, g.ctx, false, { temps: [], used: 0 }, g.discSkipKey);\n const inner = generateFast(ir, innerGen);\n if (inner === null) return null; // ineligible sub-schema disables the whole fast path\n const fnName = emitTemp(g.ctx, \"fo\");\n g.ctx.preamble.push(\n `function ${fnName}(${param}){${declareFastTemps(innerGen.scope)}return ${inner};}`,\n );\n g.scope.used += CALL_COST;\n return `${fnName}(${g.input})`;\n }\n }\n\n // Inlined: charge the enclosing function this node's EXACT emitted size. The\n // node's own text plus its inlined descendants is exactly the returned string;\n // children mutated the shared scope as they were visited, but the parent's\n // length is authoritative, so overwrite rather than add.\n const before = g.scope.used;\n // oxlint-disable-next-line typescript/no-explicit-any -- registry dispatch requires type erasure at call site\n const out = (gen as any)(ir, g) as string | null;\n if (out === null) return null;\n g.scope.used = before + out.length;\n return out;\n}\n\n/** Per-compile memo for estimateFastCost, stashed on the shared context. */\nfunction fastSizeCache(ctx: CodeGenContext): WeakMap<SchemaIR, number> {\n return (ctx.fastSizeCache ??= new WeakMap<SchemaIR, number>());\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,MAAM,eAAe;CAEnB,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,MAAM;CACN,QAAQ;CACR,MAAM;CACN,WAAW;CACX,MAAM;CACN,KAAK;CACL,OAAO;CACP,KAAK;CACL,SAAS;CACT,SAAS;CACT,MAAM;CAEN,QAAQ;CACR,OAAO;CACP,OAAO;CACP,QAAQ;CACR,KAAK;CACL,KAAK;CACL,MAAM;CAEN,OAAO;CACP,oBAAoB;CACpB,cAAc;CAEd,UAAU;CACV,UAAU;CACV,UAAU;CACV,SAAS;CACT,MAAM;CAEN,QAAQ;CAER,iBAAiB;CACjB,OAAO;CACP,UAAU;CACV,cAAc;CACd,iBAAiB;CACjB,YAAY;AACd;;;;;;;;;;;AAgBA,SAAgB,cACd,WACA,KACA,cAAc,OACd,QAAmB;CAAE,OAAO,CAAC;CAAG,MAAM;AAAE,GACxC,aACS;CACT,OAAO;EACL,OAAO;EACP;EACA;EACA;EACA;EACA,MAAM,IAAI,WAAW;GAKnB,OAAO,aACL,IACA,cAAc,WAAW,SAAS,WAAW,KAAK,MAAM,OAAO,WAAW,WAAW,CACvF;EACF;EACA,SAAS,UAAU,cAAc,OAAO,KAAK,MAAM;GAAE,OAAO,CAAC;GAAG,MAAM;EAAE,CAAC;EACzE,OAAO,WAAW,SAAS,KAAK,MAAM;EACtC,MAAM,QAAQ;GACZ,MAAM,OAAO,SAAS,KAAK,MAAM;GACjC,MAAM,MAAM,KAAK,IAAI;GACrB,OAAO;EACT;EACA,QAAQ,QAAQ,SAAS,UAAU,UAAU,KAAK,QAAQ,SAAS,KAAK;CAC1E;AACF;;;;;;;;;;;;;;;;;AAoBA,SAAgB,aAAa,IAAc,GAA2B;CACpE,MAAM,MAAM,aAAa,GAAG;CAC5B,IAAI,QAAQ,MAAM,OAAO;CAEzB,IAAI,EAAE,eAAe,UAAU,IAAI,GAAG,IAAI,GAAG;EAC3C,MAAM,QAAQ,cAAc,EAAE,GAAG;EAOjC,IANmB,EAAE,MAAM,OAAO,oBAAoB,IAAI,EAAE,MAAM,QAAQ,KAAK,IAAA,SAM5D,EAAE,MAAM,OAAA,QAAsB,iBAAiB,IAAI,KAAK,KAAA,OAAmB;GAC5F,MAAM,QAAQ,SAAS,EAAE,KAAK,IAAI;GAMlC,MAAM,WAAW,cAAc,OAAO,EAAE,KAAK,OAAO;IAAE,OAAO,CAAC;IAAG,MAAM;GAAE,GAAG,EAAE,WAAW;GACzF,MAAM,QAAQ,aAAa,IAAI,QAAQ;GACvC,IAAI,UAAU,MAAM,OAAO;GAC3B,MAAM,SAAS,SAAS,EAAE,KAAK,IAAI;GACnC,EAAE,IAAI,SAAS,KACb,YAAY,OAAO,GAAG,MAAM,IAAI,iBAAiB,SAAS,KAAK,EAAE,SAAS,MAAM,GAClF;GACA,EAAE,MAAM,QAAA;GACR,OAAO,GAAG,OAAO,GAAG,EAAE,MAAM;EAC9B;CACF;CAMA,MAAM,SAAS,EAAE,MAAM;CAEvB,MAAM,MAAO,IAAY,IAAI,CAAC;CAC9B,IAAI,QAAQ,MAAM,OAAO;CACzB,EAAE,MAAM,OAAO,SAAS,IAAI;CAC5B,OAAO;AACT;;AAGA,SAAS,cAAc,KAAgD;CACrE,OAAQ,IAAI,kCAAkB,IAAI,QAA0B;AAC9D"}
|
|
1
|
+
{"version":3,"file":"fast-path.js","names":[],"sources":["../../../src/core/codegen/fast-path.ts"],"sourcesContent":["/**\n * Fast Path: produces a boolean expression string for eligible schemas.\n * When the expression evaluates to `true` at runtime, the input is valid and\n * safeParse can return success immediately without allocating an issues array.\n *\n * Returns `null` if the schema is not eligible for Fast Path (contains coerce,\n * default, transform, or other non-pure constructs).\n */\n\nimport type { SchemaIR } from \"../types.js\";\nimport type { CodeGenContext, FastGen, FastGenerator, FastScope } from \"./context.js\";\nimport { declareFastTemps, emitRegex, emitTemp } from \"./context.js\";\nimport {\n CALL_COST,\n EXTRACT_CAP,\n estimateFastCost,\n HOISTABLE,\n MIN_EXTRACT,\n predictedInlineSize,\n} from \"./fast-size.js\";\nimport { fastAny } from \"./schemas/any.js\";\nimport { fastArray } from \"./schemas/array.js\";\nimport { fastBigInt } from \"./schemas/bigint.js\";\nimport { fastBoolean } from \"./schemas/boolean.js\";\nimport { fastCatch } from \"./schemas/catch.js\";\nimport { fastCustom } from \"./schemas/custom.js\";\nimport { fastDate } from \"./schemas/date.js\";\nimport { fastDefault } from \"./schemas/default.js\";\nimport { fastDiscriminatedUnion } from \"./schemas/discriminated-union.js\";\nimport { fastEnum } from \"./schemas/enum.js\";\nimport { fastFile } from \"./schemas/file.js\";\nimport { fastIntersection } from \"./schemas/intersection.js\";\nimport { fastLiteral } from \"./schemas/literal.js\";\nimport { fastMap } from \"./schemas/map.js\";\nimport { fastNan } from \"./schemas/nan.js\";\nimport { fastNever } from \"./schemas/never.js\";\nimport { fastNull } from \"./schemas/null.js\";\nimport { fastNullable } from \"./schemas/nullable.js\";\nimport { fastNumber } from \"./schemas/number.js\";\nimport { fastObject } from \"./schemas/object.js\";\nimport { fastOptional } from \"./schemas/optional.js\";\nimport { fastPipe } from \"./schemas/pipe.js\";\nimport { fastReadonly } from \"./schemas/readonly.js\";\nimport { fastRecord } from \"./schemas/record.js\";\nimport { fastRecursionTarget, fastRecursiveRef } from \"./schemas/recursive-ref.js\";\nimport { fastSet } from \"./schemas/set.js\";\nimport { fastString } from \"./schemas/string.js\";\nimport { fastSymbol } from \"./schemas/symbol.js\";\nimport { fastTemplateLiteral } from \"./schemas/template-literal.js\";\nimport { fastTuple } from \"./schemas/tuple.js\";\nimport { fastUndefined } from \"./schemas/undefined.js\";\nimport { fastUnion } from \"./schemas/union.js\";\nimport { fastUnknown } from \"./schemas/unknown.js\";\nimport { fastVoid } from \"./schemas/void.js\";\n\n// ─── Typed registry ─────────────────────────────────────────────────────────\n// `null` = statically ineligible (the type NEVER has a fast path).\n// Non-null function = may return null at runtime (dynamically ineligible, e.g. when coerce is present).\n\nconst fastRegistry = {\n // Primitives (order follows SchemaIR union in types.ts)\n string: fastString,\n number: fastNumber,\n boolean: fastBoolean,\n bigint: fastBigInt,\n date: fastDate,\n symbol: fastSymbol,\n null: fastNull,\n undefined: fastUndefined,\n void: fastVoid,\n nan: fastNan,\n never: fastNever,\n any: fastAny,\n unknown: fastUnknown,\n literal: fastLiteral,\n enum: fastEnum,\n // Containers\n object: fastObject,\n array: fastArray,\n tuple: fastTuple,\n record: fastRecord,\n set: fastSet,\n map: fastMap,\n file: fastFile,\n // Unions & Intersections\n union: fastUnion,\n discriminatedUnion: fastDiscriminatedUnion,\n intersection: fastIntersection,\n // Modifiers\n optional: fastOptional,\n nullable: fastNullable,\n readonly: fastReadonly,\n default: fastDefault,\n pipe: fastPipe,\n // Effects\n effect: null, // statically ineligible\n // Special\n custom: fastCustom,\n templateLiteral: fastTemplateLiteral,\n catch: fastCatch,\n fallback: null, // statically ineligible\n recursiveRef: fastRecursiveRef,\n recursionTarget: fastRecursionTarget,\n stringBool: null, // statically ineligible — output type (boolean) differs from input (string)\n} satisfies {\n [K in SchemaIR[\"type\"]]: FastGenerator<Extract<SchemaIR, { type: K }>> | null;\n};\n\n// ─── Factory ─────────────────────────────────────────────────────────────────\n\n/**\n * @param inputExpr the input expression this node validates\n * @param ctx shared codegen context\n * @param extractable whether THIS node may be hoisted (false for the root and a\n * helper's own top node — see generateFast); children are always extractable\n * @param scope size accumulator for the function being assembled (a child\n * visit shares the parent's scope; a hoisted helper body gets a fresh one)\n * @param discSkipKey discriminated-union option marker (see FastGen.discSkipKey);\n * carried only on the option's own node, never auto-propagated to children\n */\nexport function createFastGen(\n inputExpr: string,\n ctx: CodeGenContext,\n extractable = false,\n scope: FastScope = { temps: [], used: 0 },\n discSkipKey?: string,\n): FastGen {\n return {\n input: inputExpr,\n ctx,\n extractable,\n scope,\n discSkipKey,\n visit(ir, overrides) {\n // A child shares this function's scope and is itself extractable. The\n // discriminated-union skip is NOT inherited implicitly — only a caller\n // that explicitly passes overrides.discSkipKey (fastDiscriminatedUnion)\n // sets it, so nested objects keep their own type-guard.\n return generateFast(\n ir,\n createFastGen(overrides?.input ?? inputExpr, ctx, true, scope, overrides?.discSkipKey),\n );\n },\n scoped: (input) => createFastGen(input, ctx, true, { temps: [], used: 0 }),\n temp: (prefix) => emitTemp(ctx, prefix),\n local(prefix) {\n const name = emitTemp(ctx, prefix);\n scope.temps.push(name);\n return name;\n },\n regex: (prefix, pattern, flags) => emitRegex(ctx, prefix, pattern, flags),\n };\n}\n\n// ─── Dispatch ────────────────────────────────────────────────────────────────\n\n/**\n * Generate a boolean expression string that validates input against the schema.\n * Returns `null` if the schema (or any nested part) is not eligible for fast checking.\n *\n * Size-gated extraction: when inlining `ir` would push the function being\n * assembled past EXTRACT_CAP, it is hoisted into its own boolean helper\n * `function __fo_N(p){return <expr over p>;}` and replaced by a call. This keeps\n * every emitted function under V8's TurboFan optimization budget — a single\n * giant fast-check otherwise drops to the slower Maglev tier (measured ~3-4.5x\n * on deeply-nested schemas). The running total `g.scope.used` is EXACT emitted\n * chars (the returned string's length); only the extract look-ahead is an\n * estimate, scaled for access-path length. Small schemas never approach the\n * cap, so their output is byte-identical to the fully-inlined form; large\n * aggregates do split (still semantically identical — only the boolean's shape\n * changes).\n */\nexport function generateFast(ir: SchemaIR, g: FastGen): string | null {\n const gen = fastRegistry[ir.type];\n if (gen === null) return null;\n\n if (g.extractable && HOISTABLE.has(ir.type)) {\n const cache = fastSizeCache(g.ctx);\n const overBudget = g.scope.used + predictedInlineSize(ir, g.input.length, cache) > EXTRACT_CAP;\n // Extract a sub-schema big enough to be worth a call; but once the function\n // is ALREADY over budget, extract any hoistable child regardless of size —\n // otherwise a crowd of small siblings (e.g. a discriminated union of many\n // small options) grows the function unbounded since none alone clears\n // MIN_EXTRACT.\n if (overBudget && (g.scope.used > EXTRACT_CAP || estimateFastCost(ir, cache) >= MIN_EXTRACT)) {\n const param = emitTemp(g.ctx, \"op\");\n // Generate the body relative to the helper's parameter, in a fresh scope;\n // the top node is non-extractable (it IS this helper) while its children\n // stay extractable, so an oversized helper splits further recursively.\n // Carry discSkipKey so an extracted discriminated-union option still drops\n // its redundant guard inside the hoisted helper.\n const innerGen = createFastGen(param, g.ctx, false, { temps: [], used: 0 }, g.discSkipKey);\n const inner = generateFast(ir, innerGen);\n if (inner === null) return null; // ineligible sub-schema disables the whole fast path\n const fnName = emitTemp(g.ctx, \"fo\");\n g.ctx.preamble.push(\n `function ${fnName}(${param}){${declareFastTemps(innerGen.scope)}return ${inner};}`,\n );\n g.scope.used += CALL_COST;\n return `${fnName}(${g.input})`;\n }\n }\n\n // Inlined: charge the enclosing function this node's EXACT emitted size. The\n // node's own text plus its inlined descendants is exactly the returned string;\n // children mutated the shared scope as they were visited, but the parent's\n // length is authoritative, so overwrite rather than add.\n const before = g.scope.used;\n // oxlint-disable-next-line typescript/no-explicit-any -- registry dispatch requires type erasure at call site\n const out = (gen as any)(ir, g) as string | null;\n if (out === null) return null;\n g.scope.used = before + out.length;\n return out;\n}\n\n/** Per-compile memo for estimateFastCost, stashed on the shared context. */\nfunction fastSizeCache(ctx: CodeGenContext): WeakMap<SchemaIR, number> {\n return (ctx.fastSizeCache ??= new WeakMap<SchemaIR, number>());\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2DA,MAAM,eAAe;CAEnB,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,MAAM;CACN,QAAQ;CACR,MAAM;CACN,WAAW;CACX,MAAM;CACN,KAAK;CACL,OAAO;CACP,KAAK;CACL,SAAS;CACT,SAAS;CACT,MAAM;CAEN,QAAQ;CACR,OAAO;CACP,OAAO;CACP,QAAQ;CACR,KAAK;CACL,KAAK;CACL,MAAM;CAEN,OAAO;CACP,oBAAoB;CACpB,cAAc;CAEd,UAAU;CACV,UAAU;CACV,UAAU;CACV,SAAS;CACT,MAAM;CAEN,QAAQ;CAER,QAAQ;CACR,iBAAiB;CACjB,OAAO;CACP,UAAU;CACV,cAAc;CACd,iBAAiB;CACjB,YAAY;AACd;;;;;;;;;;;AAgBA,SAAgB,cACd,WACA,KACA,cAAc,OACd,QAAmB;CAAE,OAAO,CAAC;CAAG,MAAM;AAAE,GACxC,aACS;CACT,OAAO;EACL,OAAO;EACP;EACA;EACA;EACA;EACA,MAAM,IAAI,WAAW;GAKnB,OAAO,aACL,IACA,cAAc,WAAW,SAAS,WAAW,KAAK,MAAM,OAAO,WAAW,WAAW,CACvF;EACF;EACA,SAAS,UAAU,cAAc,OAAO,KAAK,MAAM;GAAE,OAAO,CAAC;GAAG,MAAM;EAAE,CAAC;EACzE,OAAO,WAAW,SAAS,KAAK,MAAM;EACtC,MAAM,QAAQ;GACZ,MAAM,OAAO,SAAS,KAAK,MAAM;GACjC,MAAM,MAAM,KAAK,IAAI;GACrB,OAAO;EACT;EACA,QAAQ,QAAQ,SAAS,UAAU,UAAU,KAAK,QAAQ,SAAS,KAAK;CAC1E;AACF;;;;;;;;;;;;;;;;;AAoBA,SAAgB,aAAa,IAAc,GAA2B;CACpE,MAAM,MAAM,aAAa,GAAG;CAC5B,IAAI,QAAQ,MAAM,OAAO;CAEzB,IAAI,EAAE,eAAe,UAAU,IAAI,GAAG,IAAI,GAAG;EAC3C,MAAM,QAAQ,cAAc,EAAE,GAAG;EAOjC,IANmB,EAAE,MAAM,OAAO,oBAAoB,IAAI,EAAE,MAAM,QAAQ,KAAK,IAAA,SAM5D,EAAE,MAAM,OAAA,QAAsB,iBAAiB,IAAI,KAAK,KAAA,OAAmB;GAC5F,MAAM,QAAQ,SAAS,EAAE,KAAK,IAAI;GAMlC,MAAM,WAAW,cAAc,OAAO,EAAE,KAAK,OAAO;IAAE,OAAO,CAAC;IAAG,MAAM;GAAE,GAAG,EAAE,WAAW;GACzF,MAAM,QAAQ,aAAa,IAAI,QAAQ;GACvC,IAAI,UAAU,MAAM,OAAO;GAC3B,MAAM,SAAS,SAAS,EAAE,KAAK,IAAI;GACnC,EAAE,IAAI,SAAS,KACb,YAAY,OAAO,GAAG,MAAM,IAAI,iBAAiB,SAAS,KAAK,EAAE,SAAS,MAAM,GAClF;GACA,EAAE,MAAM,QAAA;GACR,OAAO,GAAG,OAAO,GAAG,EAAE,MAAM;EAC9B;CACF;CAMA,MAAM,SAAS,EAAE,MAAM;CAEvB,MAAM,MAAO,IAAY,IAAI,CAAC;CAC9B,IAAI,QAAQ,MAAM,OAAO;CACzB,EAAE,MAAM,OAAO,SAAS,IAAI;CAC5B,OAAO;AACT;;AAGA,SAAS,cAAc,KAAgD;CACrE,OAAQ,IAAI,kCAAkB,IAAI,QAA0B;AAC9D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-size.d.ts","names":[],"sources":["../../../src/core/codegen/fast-size.ts"],"mappings":";;;;;;;;;cA2Ba;;;;;;;iBAiBG,oBACd,IAAI,UACJ,kBACA,OAAO,QAAQ;;;;;;cAUJ;;cAGA;;;;;;;cAQA,WAAW,YAAY;;;;;;;
|
|
1
|
+
{"version":3,"file":"fast-size.d.ts","names":[],"sources":["../../../src/core/codegen/fast-size.ts"],"mappings":";;;;;;;;;cA2Ba;;;;;;;iBAiBG,oBACd,IAAI,UACJ,kBACA,OAAO,QAAQ;;;;;;cAUJ;;cAGA;;;;;;;cAQA,WAAW,YAAY;;;;;;;iBA6FpB,iBAAiB,IAAI,UAAU,OAAO,QAAQ;;;;;;iBA0F9C,mBAAmB,GACjC,gBAAgB,KAChB,OAAO,MAAM,MAAM,UACnB,KAAK,iBACJ"}
|
|
@@ -63,6 +63,7 @@ function shallowFastCost(ir) {
|
|
|
63
63
|
case "map": return 55;
|
|
64
64
|
case "file":
|
|
65
65
|
case "templateLiteral": return 40;
|
|
66
|
+
case "custom": return 30;
|
|
66
67
|
case "literal":
|
|
67
68
|
case "optional":
|
|
68
69
|
case "nullable":
|
|
@@ -142,6 +143,7 @@ function shallowRuntimeCost(ir) {
|
|
|
142
143
|
case "map":
|
|
143
144
|
case "tuple": return 3;
|
|
144
145
|
case "templateLiteral": return 100;
|
|
146
|
+
case "custom": return 20;
|
|
145
147
|
case "recursiveRef":
|
|
146
148
|
case "recursionTarget": return 200;
|
|
147
149
|
default: return 1;
|