zod-compiler 1.23.3 → 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 +5 -3
- 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/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 |
|
|
@@ -438,6 +438,8 @@ Schema-level `error` and `z.config()` maps are unaffected; for a per-call map us
|
|
|
438
438
|
| coerced query object (invalid) | 1.1M | 162K | **10.1M** | — | — | **62x** |
|
|
439
439
|
| stringbool config object (valid) | — | 1.9M | **6.0M** | — | — | 3.1x |
|
|
440
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** |
|
|
441
443
|
|
|
442
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),
|
|
443
445
|
best of three runs. The harness costs ~55 ns per iteration, so the fastest rows sit at that floor and gaps
|
|
@@ -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;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-size.js","names":[],"sources":["../../../src/core/codegen/fast-size.ts"],"sourcesContent":["/**\n * Fast-path size estimation, used to bound the size of each emitted fast-check\n * function. V8's TurboFan refuses to optimize functions whose bytecode exceeds\n * ~60KB and falls back to the weaker Maglev tier — measured ~1.3-1.8x slower on\n * deeply-nested schemas whose monolithic fast-check crosses that budget. By\n * extracting large, self-contained sub-schemas into their own boolean helpers\n * (`generateFast`), each function stays small enough to TurboFan.\n *\n * `estimateFastCost` is a path-agnostic subtree estimate in approximate\n * generated-character units (it can't see the access-path prefix the node will\n * be emitted under, since that lives in the call site, not the IR). The actual\n * emitted size of an inlined node grows with that prefix length — `input[\"a\"]\n * [\"b\"][\"c\"]…` repeated per check — so the extraction decision scales the\n * estimate by `1 + inputLen/PATH_GROWTH_DIVISOR` (see `predictedInlineSize`).\n * The accumulator itself (generateFast) tracks EXACT emitted chars via the\n * returned string length, so only the look-ahead prediction is approximate.\n */\n\nimport type { SchemaIR } from \"../types.js\";\nimport type { CodeGenContext } from \"./context.js\";\n\n/**\n * Per-function soft size cap, in emitted characters. Once the fast-check being\n * assembled would exceed this, the next large hoistable sub-schema is split into\n * its own helper. Set below the ~60KB bytecode point where TurboFan bails (chars\n * over-approximate bytecode) with margin for one look-ahead mis-prediction.\n */\nexport const EXTRACT_CAP = 38_000;\n\n/**\n * Each nesting level lengthens every access path inside a node (`x[\"a\"]` →\n * `x[\"a\"][\"b\"]`), inflating real emitted chars above the path-agnostic estimate.\n * The extraction look-ahead multiplies the estimate by `1 + inputLen/this` so a\n * deep subtree is split before it inlines into an over-budget function. Smaller\n * ⇒ more eager splitting at depth. Calibrated against deeply-nested fixtures.\n */\nconst PATH_GROWTH_DIVISOR = 26;\n\n/**\n * Predicted emitted size of inlining `ir` at a call site whose input expression\n * is `inputLen` chars long — the path-agnostic subtree estimate scaled up for\n * the access-path prefix that estimate omits. Used only for the extract/inline\n * decision; the running total uses exact emitted lengths.\n */\nexport function predictedInlineSize(\n ir: SchemaIR,\n inputLen: number,\n cache: WeakMap<SchemaIR, number>,\n): number {\n return estimateFastCost(ir, cache) * (1 + inputLen / PATH_GROWTH_DIVISOR);\n}\n\n/**\n * Floor for extraction: never hoist a sub-schema smaller than this even when\n * the enclosing function is over the cap — a tiny helper trades a real call for\n * no optimization benefit. Only sub-schemas big enough to matter are split.\n */\nexport const MIN_EXTRACT = 1_200;\n\n/** Approximate size contributed to the enclosing function by an extracted call `__fo_N(expr)`. */\nexport const CALL_COST = 24;\n\n/**\n * Node types whose fast-check is a self-contained boolean over a single input\n * expression and large enough to be worth hoisting into `function f(p){return …}`.\n * Thin wrappers (optional/nullable/default/…) are omitted: their inner schema is\n * visited through `generateFast` and gets hoisted on its own when large.\n */\nexport const HOISTABLE: ReadonlySet<SchemaIR[\"type\"]> = new Set([\n \"object\",\n \"record\",\n \"tuple\",\n \"discriminatedUnion\",\n \"union\",\n \"intersection\",\n]);\n\n/** A node's own contribution to the emitted fast-check, EXCLUDING its children. */\nfunction shallowFastCost(ir: SchemaIR): number {\n switch (ir.type) {\n case \"object\":\n return 50 + Object.keys(ir.properties).length * 25 + (ir.strict ? 120 : 0);\n case \"string\":\n return 20 + ir.checks.length * 35;\n case \"number\":\n return 25 + ir.checks.length * 30;\n case \"bigint\":\n case \"date\":\n return 25 + (ir.checks?.length ?? 0) * 25;\n case \"enum\":\n return 15 + ir.values.length * 12;\n case \"tuple\":\n return 40 + ir.items.length * 15;\n case \"union\":\n return 12 + ir.options.length * 6;\n case \"discriminatedUnion\":\n return 45 + ir.cases.length * 12;\n case \"array\":\n case \"record\":\n case \"set\":\n case \"map\":\n return 55;\n case \"file\":\n case \"templateLiteral\":\n return 40;\n case \"literal\":\n case \"optional\":\n case \"nullable\":\n case \"default\":\n return 22;\n case \"recursiveRef\":\n case \"recursionTarget\":\n // Both emit just a call to a hosted helper; the target's `inner` is sized\n // independently when its own helper body is generated, so it is NOT\n // counted against the enclosing function here (see fastChildren).\n return 15;\n default:\n return 18;\n }\n}\n\n/** Child IR nodes that the fast generator recurses into (mirrors the fast schema generators). */\nfunction fastChildren(ir: SchemaIR): readonly 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.rest] : ir.items;\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 \"pipe\":\n return [ir.in, ir.out];\n case \"optional\":\n case \"nullable\":\n case \"readonly\":\n case \"default\":\n case \"catch\":\n return [ir.inner];\n default:\n return [];\n }\n}\n\n/**\n * Estimated total size of a node's fast-check (the node plus everything inlined\n * beneath it). Memoized per IR node: schemas dedupe shared sub-trees and the\n * estimate is consulted at every nesting level, so without the cache a deep tree\n * would be re-walked quadratically.\n */\nexport function estimateFastCost(ir: SchemaIR, cache: WeakMap<SchemaIR, number>): number {\n const cached = cache.get(ir);\n if (cached !== undefined) return cached;\n // Defensive cycle break (the IR is a tree today; recursiveRef is a leaf).\n cache.set(ir, 0);\n let total = shallowFastCost(ir);\n for (const child of fastChildren(ir)) total += estimateFastCost(child, cache);\n cache.set(ir, total);\n return total;\n}\n\n// ─── Runtime cost (check ordering) ───────────────────────────────────────────\n\n/**\n * Assumed element count for unbounded collections, used only to weight a\n * container against its siblings when ordering an `&&` chain.\n */\nconst ASSUMED_ELEMENTS = 4;\n\n/** Runtime weight of one check on an already-type-confirmed value. */\nfunction checkRuntimeCost(kind: string): number {\n switch (kind) {\n // A `.test()` call costs ~9 ns of dispatch before it matches a character —\n // an order of magnitude above every other check, and the reason ordering\n // matters at all.\n case \"string_format\":\n return 100;\n // Zero-capture user predicate: a real call, cheaper than a regex.\n case \"refine_effect\":\n return 20;\n case \"includes\":\n case \"starts_with\":\n case \"ends_with\":\n return 6;\n case \"multiple_of\":\n case \"bigint_multiple_of\":\n return 4;\n default:\n // length/size/range comparisons: a load and a compare.\n return 1;\n }\n}\n\n/** A node's own runtime weight, EXCLUDING its children. */\nfunction shallowRuntimeCost(ir: SchemaIR): number {\n switch (ir.type) {\n case \"string\":\n case \"number\":\n return 2 + ir.checks.reduce((sum, c) => sum + checkRuntimeCost(c.kind), 0);\n case \"bigint\":\n case \"date\":\n return 3 + (ir.checks?.length ?? 0);\n case \"file\":\n return 3 + (ir.checks?.length ?? 0) * 2;\n case \"enum\":\n // Inlined `===` chain (or one hashed lookup) — sub-nanosecond either way.\n return 1 + Math.min(ir.values.length, 8);\n case \"object\":\n // Type guard, plus the strict pass's per-key membership test.\n return 3 + (ir.strict === true ? Object.keys(ir.properties).length : 0);\n case \"array\":\n case \"set\":\n case \"record\":\n case \"map\":\n case \"tuple\":\n return 3;\n case \"templateLiteral\":\n return 100;\n case \"recursiveRef\":\n case \"recursionTarget\":\n // A recursive call expands into an unknown amount of work; treat it as\n // the most expensive sibling so it is probed last.\n return 200;\n default:\n return 1;\n }\n}\n\n/** estimateRuntimeCost against the per-compile memo hanging off the context. */\nfunction runtimeCostOf(ir: SchemaIR, ctx: CodeGenContext): number {\n return estimateRuntimeCost(ir, (ctx.fastRuntimeCostCache ??= new WeakMap<SchemaIR, number>()));\n}\n\n/**\n * Sort a fast-check's sibling conjuncts (or union options) cheapest-first.\n * Returns a new array; ties keep source order (Array#sort is stable), so a\n * schema whose members all cost the same emits byte-identical output.\n */\nexport function orderByRuntimeCost<T>(\n items: readonly T[],\n irOf: (item: T) => SchemaIR,\n ctx: CodeGenContext,\n): T[] {\n if (items.length < 2) return [...items];\n return [...items].sort((a, b) => runtimeCostOf(irOf(a), ctx) - runtimeCostOf(irOf(b), ctx));\n}\n\n/**\n * Estimated runtime cost of a node's fast-check, in arbitrary units calibrated\n * so a regex format check dominates a handful of type guards. Memoized per IR\n * node, like estimateFastCost — the estimate is consulted at every nesting\n * level, and shared sub-trees are common.\n *\n * Used to order the conjuncts of an `&&` chain cheapest-first. Accepting input\n * runs every conjunct regardless, so ordering is free there; REJECTING input\n * stops at the first false one, which is where a schema pays for probing a\n * `z.email()` before the `kind` literal that actually discriminates. Measured\n * on a 3-option union of objects that each declare `{email, kind, note}`:\n * 102 ns → 39 ns for an input matching the last option, and 102 ns → 6 ns for\n * one matching none.\n */\nfunction estimateRuntimeCost(ir: SchemaIR, cache: WeakMap<SchemaIR, number>): number {\n const cached = cache.get(ir);\n if (cached !== undefined) return cached;\n cache.set(ir, 0); // cycle break (mirrors estimateFastCost)\n let total = shallowRuntimeCost(ir);\n const children = fastChildren(ir);\n switch (ir.type) {\n case \"array\":\n case \"set\":\n case \"record\":\n case \"map\":\n // Per-element work, repeated for an assumed handful of entries.\n for (const child of children) total += estimateRuntimeCost(child, cache) * ASSUMED_ELEMENTS;\n break;\n case \"discriminatedUnion\":\n // O(1) switch: only the matched case runs. Charge the priciest option so\n // the estimate stays an upper bound.\n total += children.reduce((max, c) => Math.max(max, estimateRuntimeCost(c, cache)), 0);\n break;\n default:\n // object/tuple/union/intersection/wrappers: every child is on the path\n // (a union probes options until one matches — worst case, all of them).\n for (const child of children) total += estimateRuntimeCost(child, cache);\n break;\n }\n cache.set(ir, total);\n return total;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fast-size.js","names":[],"sources":["../../../src/core/codegen/fast-size.ts"],"sourcesContent":["/**\n * Fast-path size estimation, used to bound the size of each emitted fast-check\n * function. V8's TurboFan refuses to optimize functions whose bytecode exceeds\n * ~60KB and falls back to the weaker Maglev tier — measured ~1.3-1.8x slower on\n * deeply-nested schemas whose monolithic fast-check crosses that budget. By\n * extracting large, self-contained sub-schemas into their own boolean helpers\n * (`generateFast`), each function stays small enough to TurboFan.\n *\n * `estimateFastCost` is a path-agnostic subtree estimate in approximate\n * generated-character units (it can't see the access-path prefix the node will\n * be emitted under, since that lives in the call site, not the IR). The actual\n * emitted size of an inlined node grows with that prefix length — `input[\"a\"]\n * [\"b\"][\"c\"]…` repeated per check — so the extraction decision scales the\n * estimate by `1 + inputLen/PATH_GROWTH_DIVISOR` (see `predictedInlineSize`).\n * The accumulator itself (generateFast) tracks EXACT emitted chars via the\n * returned string length, so only the look-ahead prediction is approximate.\n */\n\nimport type { SchemaIR } from \"../types.js\";\nimport type { CodeGenContext } from \"./context.js\";\n\n/**\n * Per-function soft size cap, in emitted characters. Once the fast-check being\n * assembled would exceed this, the next large hoistable sub-schema is split into\n * its own helper. Set below the ~60KB bytecode point where TurboFan bails (chars\n * over-approximate bytecode) with margin for one look-ahead mis-prediction.\n */\nexport const EXTRACT_CAP = 38_000;\n\n/**\n * Each nesting level lengthens every access path inside a node (`x[\"a\"]` →\n * `x[\"a\"][\"b\"]`), inflating real emitted chars above the path-agnostic estimate.\n * The extraction look-ahead multiplies the estimate by `1 + inputLen/this` so a\n * deep subtree is split before it inlines into an over-budget function. Smaller\n * ⇒ more eager splitting at depth. Calibrated against deeply-nested fixtures.\n */\nconst PATH_GROWTH_DIVISOR = 26;\n\n/**\n * Predicted emitted size of inlining `ir` at a call site whose input expression\n * is `inputLen` chars long — the path-agnostic subtree estimate scaled up for\n * the access-path prefix that estimate omits. Used only for the extract/inline\n * decision; the running total uses exact emitted lengths.\n */\nexport function predictedInlineSize(\n ir: SchemaIR,\n inputLen: number,\n cache: WeakMap<SchemaIR, number>,\n): number {\n return estimateFastCost(ir, cache) * (1 + inputLen / PATH_GROWTH_DIVISOR);\n}\n\n/**\n * Floor for extraction: never hoist a sub-schema smaller than this even when\n * the enclosing function is over the cap — a tiny helper trades a real call for\n * no optimization benefit. Only sub-schemas big enough to matter are split.\n */\nexport const MIN_EXTRACT = 1_200;\n\n/** Approximate size contributed to the enclosing function by an extracted call `__fo_N(expr)`. */\nexport const CALL_COST = 24;\n\n/**\n * Node types whose fast-check is a self-contained boolean over a single input\n * expression and large enough to be worth hoisting into `function f(p){return …}`.\n * Thin wrappers (optional/nullable/default/…) are omitted: their inner schema is\n * visited through `generateFast` and gets hoisted on its own when large.\n */\nexport const HOISTABLE: ReadonlySet<SchemaIR[\"type\"]> = new Set([\n \"object\",\n \"record\",\n \"tuple\",\n \"discriminatedUnion\",\n \"union\",\n \"intersection\",\n]);\n\n/** A node's own contribution to the emitted fast-check, EXCLUDING its children. */\nfunction shallowFastCost(ir: SchemaIR): number {\n switch (ir.type) {\n case \"object\":\n return 50 + Object.keys(ir.properties).length * 25 + (ir.strict ? 120 : 0);\n case \"string\":\n return 20 + ir.checks.length * 35;\n case \"number\":\n return 25 + ir.checks.length * 30;\n case \"bigint\":\n case \"date\":\n return 25 + (ir.checks?.length ?? 0) * 25;\n case \"enum\":\n return 15 + ir.values.length * 12;\n case \"tuple\":\n return 40 + ir.items.length * 15;\n case \"union\":\n return 12 + ir.options.length * 6;\n case \"discriminatedUnion\":\n return 45 + ir.cases.length * 12;\n case \"array\":\n case \"record\":\n case \"set\":\n case \"map\":\n return 55;\n case \"file\":\n case \"templateLiteral\":\n return 40;\n case \"custom\":\n return 30;\n case \"literal\":\n case \"optional\":\n case \"nullable\":\n case \"default\":\n return 22;\n case \"recursiveRef\":\n case \"recursionTarget\":\n // Both emit just a call to a hosted helper; the target's `inner` is sized\n // independently when its own helper body is generated, so it is NOT\n // counted against the enclosing function here (see fastChildren).\n return 15;\n default:\n return 18;\n }\n}\n\n/** Child IR nodes that the fast generator recurses into (mirrors the fast schema generators). */\nfunction fastChildren(ir: SchemaIR): readonly 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.rest] : ir.items;\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 \"pipe\":\n return [ir.in, ir.out];\n case \"optional\":\n case \"nullable\":\n case \"readonly\":\n case \"default\":\n case \"catch\":\n return [ir.inner];\n default:\n return [];\n }\n}\n\n/**\n * Estimated total size of a node's fast-check (the node plus everything inlined\n * beneath it). Memoized per IR node: schemas dedupe shared sub-trees and the\n * estimate is consulted at every nesting level, so without the cache a deep tree\n * would be re-walked quadratically.\n */\nexport function estimateFastCost(ir: SchemaIR, cache: WeakMap<SchemaIR, number>): number {\n const cached = cache.get(ir);\n if (cached !== undefined) return cached;\n // Defensive cycle break (the IR is a tree today; recursiveRef is a leaf).\n cache.set(ir, 0);\n let total = shallowFastCost(ir);\n for (const child of fastChildren(ir)) total += estimateFastCost(child, cache);\n cache.set(ir, total);\n return total;\n}\n\n// ─── Runtime cost (check ordering) ───────────────────────────────────────────\n\n/**\n * Assumed element count for unbounded collections, used only to weight a\n * container against its siblings when ordering an `&&` chain.\n */\nconst ASSUMED_ELEMENTS = 4;\n\n/** Runtime weight of one check on an already-type-confirmed value. */\nfunction checkRuntimeCost(kind: string): number {\n switch (kind) {\n // A `.test()` call costs ~9 ns of dispatch before it matches a character —\n // an order of magnitude above every other check, and the reason ordering\n // matters at all.\n case \"string_format\":\n return 100;\n // Zero-capture user predicate: a real call, cheaper than a regex.\n case \"refine_effect\":\n return 20;\n case \"includes\":\n case \"starts_with\":\n case \"ends_with\":\n return 6;\n case \"multiple_of\":\n case \"bigint_multiple_of\":\n return 4;\n default:\n // length/size/range comparisons: a load and a compare.\n return 1;\n }\n}\n\n/** A node's own runtime weight, EXCLUDING its children. */\nfunction shallowRuntimeCost(ir: SchemaIR): number {\n switch (ir.type) {\n case \"string\":\n case \"number\":\n return 2 + ir.checks.reduce((sum, c) => sum + checkRuntimeCost(c.kind), 0);\n case \"bigint\":\n case \"date\":\n return 3 + (ir.checks?.length ?? 0);\n case \"file\":\n return 3 + (ir.checks?.length ?? 0) * 2;\n case \"enum\":\n // Inlined `===` chain (or one hashed lookup) — sub-nanosecond either way.\n return 1 + Math.min(ir.values.length, 8);\n case \"object\":\n // Type guard, plus the strict pass's per-key membership test.\n return 3 + (ir.strict === true ? Object.keys(ir.properties).length : 0);\n case \"array\":\n case \"set\":\n case \"record\":\n case \"map\":\n case \"tuple\":\n return 3;\n case \"templateLiteral\":\n return 100;\n case \"custom\":\n return 20;\n case \"recursiveRef\":\n case \"recursionTarget\":\n // A recursive call expands into an unknown amount of work; treat it as\n // the most expensive sibling so it is probed last.\n return 200;\n default:\n return 1;\n }\n}\n\n/** estimateRuntimeCost against the per-compile memo hanging off the context. */\nfunction runtimeCostOf(ir: SchemaIR, ctx: CodeGenContext): number {\n return estimateRuntimeCost(ir, (ctx.fastRuntimeCostCache ??= new WeakMap<SchemaIR, number>()));\n}\n\n/**\n * Sort a fast-check's sibling conjuncts (or union options) cheapest-first.\n * Returns a new array; ties keep source order (Array#sort is stable), so a\n * schema whose members all cost the same emits byte-identical output.\n */\nexport function orderByRuntimeCost<T>(\n items: readonly T[],\n irOf: (item: T) => SchemaIR,\n ctx: CodeGenContext,\n): T[] {\n if (items.length < 2) return [...items];\n return [...items].sort((a, b) => runtimeCostOf(irOf(a), ctx) - runtimeCostOf(irOf(b), ctx));\n}\n\n/**\n * Estimated runtime cost of a node's fast-check, in arbitrary units calibrated\n * so a regex format check dominates a handful of type guards. Memoized per IR\n * node, like estimateFastCost — the estimate is consulted at every nesting\n * level, and shared sub-trees are common.\n *\n * Used to order the conjuncts of an `&&` chain cheapest-first. Accepting input\n * runs every conjunct regardless, so ordering is free there; REJECTING input\n * stops at the first false one, which is where a schema pays for probing a\n * `z.email()` before the `kind` literal that actually discriminates. Measured\n * on a 3-option union of objects that each declare `{email, kind, note}`:\n * 102 ns → 39 ns for an input matching the last option, and 102 ns → 6 ns for\n * one matching none.\n */\nfunction estimateRuntimeCost(ir: SchemaIR, cache: WeakMap<SchemaIR, number>): number {\n const cached = cache.get(ir);\n if (cached !== undefined) return cached;\n cache.set(ir, 0); // cycle break (mirrors estimateFastCost)\n let total = shallowRuntimeCost(ir);\n const children = fastChildren(ir);\n switch (ir.type) {\n case \"array\":\n case \"set\":\n case \"record\":\n case \"map\":\n // Per-element work, repeated for an assumed handful of entries.\n for (const child of children) total += estimateRuntimeCost(child, cache) * ASSUMED_ELEMENTS;\n break;\n case \"discriminatedUnion\":\n // O(1) switch: only the matched case runs. Charge the priciest option so\n // the estimate stays an upper bound.\n total += children.reduce((max, c) => Math.max(max, estimateRuntimeCost(c, cache)), 0);\n break;\n default:\n // object/tuple/union/intersection/wrappers: every child is on the path\n // (a union probes options until one matches — worst case, all of them).\n for (const child of children) total += estimateRuntimeCost(child, cache);\n break;\n }\n cache.set(ir, total);\n return total;\n}\n"],"mappings":"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"}
|
|
@@ -104,6 +104,12 @@ declare const ZC_SR_OK_DECL: string;
|
|
|
104
104
|
* callbacks return undefined, so the guard short-circuits on the first operand.
|
|
105
105
|
*/
|
|
106
106
|
declare const ZC_SR_RUN_DECL: string;
|
|
107
|
+
/**
|
|
108
|
+
* z.custom()/z.instanceof() fast verdict. Zod treats truthy predicate returns
|
|
109
|
+
* as success and raises $ZodAsyncError when a synchronous parse encounters a
|
|
110
|
+
* thenable, including a non-async function that happens to return a Promise.
|
|
111
|
+
*/
|
|
112
|
+
declare const ZC_CUSTOM_OK_DECL: string;
|
|
107
113
|
/**
|
|
108
114
|
* superRefine slow-path merge: run the callback, then move its issues onto the
|
|
109
115
|
* validator's list the way zod's finalizeIssue does — the node's path prefixed
|
|
@@ -123,5 +129,5 @@ declare const ZC_SR_DECL: string;
|
|
|
123
129
|
/** Non-issue runtime helper declarations hosted in the virtual module. */
|
|
124
130
|
declare const RUNTIME_HELPER_DECLS: Readonly<Record<string, string>>;
|
|
125
131
|
//#endregion
|
|
126
|
-
export { ISSUE_DECLS, RUNTIME_HELPER_DECLS, ZC_AB_DECL, ZC_FSR_DECL, ZC_FZ_DECL, ZC_HOP_DECL, ZC_PFX_DECL, ZC_SR_DECL, ZC_SR_OK_DECL, ZC_SR_RUN_DECL, abortingCodeTest, propertyKeyTest };
|
|
132
|
+
export { ISSUE_DECLS, RUNTIME_HELPER_DECLS, ZC_AB_DECL, ZC_CUSTOM_OK_DECL, ZC_FSR_DECL, ZC_FZ_DECL, ZC_HOP_DECL, ZC_PFX_DECL, ZC_SR_DECL, ZC_SR_OK_DECL, ZC_SR_RUN_DECL, abortingCodeTest, propertyKeyTest };
|
|
127
133
|
//# sourceMappingURL=issue-decls.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"issue-decls.d.ts","names":[],"sources":["../../../src/core/codegen/issue-decls.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;cA4Ca,aAAa,SAAS;;;;;;cAgBtB;;;;;;;;;;cAYA;;iBAyBG,iBAAiB;;;;;;;;;;;;;;;cAkBpB;;;;;;;;;;;;cAaA;;;;;;;;;cAaA;;iBAIG,gBAAgB;;;;;;;;;;;;;;;;cAmBnB;;;;;;;;;;;;;cAgBA;;;;;;;;;;;;;;;;cAmBA;;cAQA,sBAAsB,SAAS"}
|
|
1
|
+
{"version":3,"file":"issue-decls.d.ts","names":[],"sources":["../../../src/core/codegen/issue-decls.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;cA4Ca,aAAa,SAAS;;;;;;cAgBtB;;;;;;;;;;cAYA;;iBAyBG,iBAAiB;;;;;;;;;;;;;;;cAkBpB;;;;;;;;;;;;cAaA;;;;;;;;;cAaA;;iBAIG,gBAAgB;;;;;;;;;;;;;;;;cAmBnB;;;;;;;;;;;;;cAgBA;;;;;;cASA;;;;;;;;;;;;;;;;cAmBA;;cAQA,sBAAsB,SAAS"}
|
|
@@ -120,6 +120,12 @@ const ZC_SR_OK_DECL = "function __zcSrOk(f,v){var p={value:v,issues:[]};__zcSrRu
|
|
|
120
120
|
*/
|
|
121
121
|
const ZC_SR_RUN_DECL = "function __zcSrRun(f,p){var r=f(p);if(r&&typeof r.then===\"function\"){throw new __zcCore.$ZodAsyncError();}}";
|
|
122
122
|
/**
|
|
123
|
+
* z.custom()/z.instanceof() fast verdict. Zod treats truthy predicate returns
|
|
124
|
+
* as success and raises $ZodAsyncError when a synchronous parse encounters a
|
|
125
|
+
* thenable, including a non-async function that happens to return a Promise.
|
|
126
|
+
*/
|
|
127
|
+
const ZC_CUSTOM_OK_DECL = "function __zcCu(f,v){var r=f(v);if(r&&typeof r.then===\"function\"){throw new __zcCore.$ZodAsyncError();}return !!r;}";
|
|
128
|
+
/**
|
|
123
129
|
* superRefine slow-path merge: run the callback, then move its issues onto the
|
|
124
130
|
* validator's list the way zod's finalizeIssue does — the node's path prefixed
|
|
125
131
|
* onto any path the user supplied, and the internal `inst`/`continue` fields
|
|
@@ -142,10 +148,11 @@ const RUNTIME_HELPER_DECLS = {
|
|
|
142
148
|
__zcFz: ZC_FZ_DECL,
|
|
143
149
|
__zcHop: ZC_HOP_DECL,
|
|
144
150
|
__zcPfx: ZC_PFX_DECL,
|
|
151
|
+
__zcCu: ZC_CUSTOM_OK_DECL,
|
|
145
152
|
__zcSr: ZC_SR_DECL,
|
|
146
153
|
__zcSrOk: ZC_SR_OK_DECL
|
|
147
154
|
};
|
|
148
155
|
//#endregion
|
|
149
|
-
export { ISSUE_DECLS, RUNTIME_HELPER_DECLS, ZC_AB_DECL, ZC_FSR_DECL, ZC_FZ_DECL, ZC_HOP_DECL, ZC_PFX_DECL, ZC_SR_DECL, ZC_SR_OK_DECL, ZC_SR_RUN_DECL, abortingCodeTest, propertyKeyTest };
|
|
156
|
+
export { ISSUE_DECLS, RUNTIME_HELPER_DECLS, ZC_AB_DECL, ZC_CUSTOM_OK_DECL, ZC_FSR_DECL, ZC_FZ_DECL, ZC_HOP_DECL, ZC_PFX_DECL, ZC_SR_DECL, ZC_SR_OK_DECL, ZC_SR_RUN_DECL, abortingCodeTest, propertyKeyTest };
|
|
150
157
|
|
|
151
158
|
//# sourceMappingURL=issue-decls.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"issue-decls.js","names":[],"sources":["../../../src/core/codegen/issue-decls.ts"],"sourcesContent":["/**\n * Issue factory function bodies (statement form).\n *\n * These functions produce the same `{code, ...}` shapes that lean-mode\n * generated code would otherwise inline at every check site.\n * Hosted in \"virtual:zod-compiler/runtime\" and called as `__zcTS(...)` etc.\n *\n * Argument convention (positional, kept short to minimize call-site bytes):\n * __zcTS(minimum, origin, inclusive, input, path, msg?) — too_small\n * __zcTB(maximum, origin, inclusive, input, path, msg?) — too_big\n * __zcIT(expected, input, path, msg?) — invalid_type\n * __zcIF(format, input, path, extra?, msg?) — invalid_format (extra merged into result)\n * __zcIV(values, input, path, msg?) — invalid_value\n * __zcUK(keys, input, path, msg?) — unrecognized_keys\n *\n * The trailing msg argument carries a static custom error message; when\n * absent, the __zcFin finalizer applies the configured locale default.\n */\n\nconst ZC_TS_DECL =\n 'function __zcTS(m,o,i,inp,p,msg){var r={code:\"too_small\",minimum:m,origin:o,inclusive:i,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}';\n\nconst ZC_TS_EXACT_DECL =\n 'function __zcTSx(m,o,inp,p,msg){var r={code:\"too_small\",minimum:m,origin:o,inclusive:true,exact:true,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}';\n\nconst ZC_TB_DECL =\n 'function __zcTB(m,o,i,inp,p,msg){var r={code:\"too_big\",maximum:m,origin:o,inclusive:i,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}';\n\nconst ZC_TB_EXACT_DECL =\n 'function __zcTBx(m,o,inp,p,msg){var r={code:\"too_big\",maximum:m,origin:o,inclusive:true,exact:true,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}';\n\nconst ZC_IT_DECL =\n 'function __zcIT(e,inp,p,msg){var r={code:\"invalid_type\",expected:e,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}';\n\nconst ZC_IF_DECL =\n 'function __zcIF(f,inp,p,extra,msg){var r={code:\"invalid_format\",format:f,input:inp,path:p};if(extra)Object.assign(r,extra);if(msg!==undefined)r.message=msg;return r;}';\n\nconst ZC_IV_DECL =\n 'function __zcIV(values,inp,p,msg){var r={code:\"invalid_value\",values:values,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}';\n\nconst ZC_UK_DECL =\n 'function __zcUK(k,inp,p,msg){var r={code:\"unrecognized_keys\",keys:k,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}';\n\n/** All issue factory declarations indexed by helper name. */\nexport const ISSUE_DECLS: Readonly<Record<string, string>> = {\n __zcTS: ZC_TS_DECL,\n __zcTSx: ZC_TS_EXACT_DECL,\n __zcTB: ZC_TB_DECL,\n __zcTBx: ZC_TB_EXACT_DECL,\n __zcIT: ZC_IT_DECL,\n __zcIF: ZC_IF_DECL,\n __zcIV: ZC_IV_DECL,\n __zcUK: ZC_UK_DECL,\n};\n\n/**\n * Float-safe remainder — byte-for-byte port of zod's util.floatSafeRemainder.\n * Raw `%` mis-rejects valid multiples of decimal steps (0.3 % 0.1 !== 0);\n * zod scales both operands to integers by their decimal-place count first.\n */\nexport const ZC_FSR_DECL =\n 'function __zcFsr(v,s){var vd=((\"\"+v).split(\".\")[1]||\"\").length;var ss=\"\"+s;var sd=(ss.split(\".\")[1]||\"\").length;if(sd===0&&/\\\\d?e-\\\\d?/.test(ss)){var m=ss.match(/\\\\d?e-(\\\\d?)/);if(m&&m[1]){sd=parseInt(m[1],10);}}var d=vd>sd?vd:sd;var vi=parseInt(v.toFixed(d).replace(\".\",\"\"),10);var si=parseInt(s.toFixed(d).replace(\".\",\"\"),10);return (vi%si)/Math.pow(10,d);}';\n\n/**\n * Hoisted `Object.prototype.hasOwnProperty` reference. Record fast/slow paths\n * iterate keys with `for(k in o)` (no `Object.keys` array allocation) and guard\n * each key with `__zcHop.call(o,k)` to skip inherited enumerable properties —\n * yielding the exact own-enumerable string-key set `Object.keys` would, so\n * fast/slow stay in agreement and parity with zod's own-key record semantics is\n * preserved. The hoisted reference inlines in V8; reading the prototype property\n * per call would not.\n */\nexport const ZC_HOP_DECL = \"const __zcHop=Object.prototype.hasOwnProperty;\";\n\n/**\n * Issue codes zod raises from a schema's `_zod.parse` rather than from a check,\n * and which therefore carry `continue !== true` — what `util.aborted` looks for.\n *\n * Everything a CHECK produces (`too_small`, `too_big`, `invalid_format`,\n * `not_multiple_of`, a refine's `custom`) is continuable, because `$ZodCheck`\n * sets `continue: !def.abort` and an `abort: true` check costs the schema its\n * compiled path anyway — so classifying by code alone is exact for generated\n * issues.\n *\n * Read by {@link ZC_AB_DECL} and by the union's option-pruning loop, which\n * applies the same rule inline over a per-option issue array.\n */\nconst ABORTING_ISSUE_CODES: readonly string[] = [\n \"invalid_type\",\n \"invalid_value\",\n \"invalid_union\",\n \"unrecognized_keys\",\n \"invalid_key\",\n \"invalid_element\",\n];\n\n/** `c===\"invalid_type\"||c===\"invalid_value\"||…` over the code held in `codeExpr`. */\nexport function abortingCodeTest(codeExpr: string): string {\n return ABORTING_ISSUE_CODES.map((code) => `${codeExpr}===${JSON.stringify(code)}`).join(\"||\");\n}\n\n/**\n * Port of zod's `util.aborted(payload, startIndex)`: has anything since\n * `startIndex` produced a NON-continuable issue?\n *\n * zod gates a schema's check chain on this (`runChecks`: `else if (isAborted)\n * continue`), which is what makes a container's `.refine()` still run after a\n * property or element failed its own `min`/`max`/format check, and stop running\n * once one failed to parse at all. The `startIndex` is the issue count when the\n * node was entered, mirroring the fresh payload zod hands each sub-schema.\n *\n * Size/length checks are exempt in zod — they declare a `when` predicate, which\n * bypasses the abort gate — so generated code leaves those ungated and only\n * wraps the refine/superRefine effects.\n */\nexport const ZC_AB_DECL = `function __zcAb(e,i){for(;i<e.length;i++){var c=e[i].code;if(${abortingCodeTest(\"c\")})return true;}return false;}`;\n\n/**\n * Port of zod's `util.finalizeIssue` for issues NESTED inside an\n * `invalid_key` / `invalid_element` wrapper. Those never reach the top-level\n * finalization loop (which walks only the outer array), so zod finalizes them\n * where it builds the wrapper: locale message applied when none was baked in,\n * `input` cleared. Their `path` stays RELATIVE to the key or value schema — zod\n * ran it on a fresh payload — so nothing rewrites it.\n *\n * `input` is cleared by assignment rather than `delete`, matching the top-level\n * finalizer and the union's per-option loop.\n */\nexport const ZC_FZ_DECL =\n \"function __zcFz(e){for(var i=0;i<e.length;i++){var s=e[i];\" +\n 'if(s.message===undefined&&typeof __zcMsg===\"function\")s.message=__zcMsg(s);' +\n \"s.input=undefined;}return e;}\";\n\n/**\n * Port of zod's `util.prefixIssues(key, issues)` for a map entry whose key is a\n * property-key type: each issue's RELATIVE path is spliced onto the map's own\n * path plus the key, and the issue moves into the parent's array.\n *\n * `b.concat(k,x.path)` flattens `x.path` one level, giving exactly\n * `[...base, key, ...relative]`.\n */\nexport const ZC_PFX_DECL =\n \"function __zcPfx(d,s,b,k){for(var i=0;i<s.length;i++){var x=s[i];x.path=b.concat(k,x.path);d.push(x);}}\";\n\n/** Does `keyExpr` hold one of zod's `util.propertyKeyTypes` (string|number|symbol)? */\nexport function propertyKeyTest(keyExpr: string): string {\n return `typeof ${keyExpr}===\"string\"||typeof ${keyExpr}===\"number\"||typeof ${keyExpr}===\"symbol\"`;\n}\n\n/**\n * superRefine fast-check: run the payload callback on a throwaway payload and\n * report whether it both added nothing AND left the value alone. zod's own\n * wrapper (the referenced function) installs `addIssue` and normalizes what the\n * user adds, so the verdict is zod's; the issues themselves are re-collected by\n * the slow walk.\n *\n * The `p.value===v` half is what lets a superRefine node keep a fast path at\n * all. `value` is writable public API ($RefinementCtx extends ParsePayload), so\n * a callback may rewrite it, and the fast path's caller returns the ORIGINAL\n * input on success — which would then be stale. Reporting false when the value\n * moved routes those parses into the slow walk, which propagates the new value\n * (see ZC_SR_DECL). Callbacks that only validate — effectively all of them —\n * still take the fast exit.\n */\nexport const ZC_SR_OK_DECL =\n \"function __zcSrOk(f,v){var p={value:v,issues:[]};__zcSrRun(f,p);\" +\n \"return p.issues.length===0&&p.value===v;}\";\n\n/**\n * Module-local (never imported by generated code) — __zcSr/__zcSrOk call it.\n * Lean mode declares it in the runtime module beside them; inline mode pushes it\n * into the preamble alongside whichever of the two is used.\n *\n * Invoke the referenced wrapper, reproducing zod's synchronous-parse contract:\n * a callback that returns a promise makes zod raise $ZodAsyncError rather than\n * silently accepting. Because the ref points at zod's superRefine WRAPPER, not\n * the user's function, an async callback cannot be detected while extracting —\n * the returned thenable is the only evidence, so the test lives here. Sync\n * callbacks return undefined, so the guard short-circuits on the first operand.\n */\nexport const ZC_SR_RUN_DECL =\n \"function __zcSrRun(f,p){var r=f(p);\" +\n 'if(r&&typeof r.then===\"function\"){throw new __zcCore.$ZodAsyncError();}}';\n\n/**\n * superRefine slow-path merge: run the callback, then move its issues onto the\n * validator's list the way zod's finalizeIssue does — the node's path prefixed\n * onto any path the user supplied, and the internal `inst`/`continue` fields\n * dropped (they are zod bookkeeping, deleted before the issue is user-visible).\n *\n * Returns the payload, so the caller can write `.value` back (the callback may\n * have rewritten it) and read `.aborted`. Aborted is set when any issue aborts\n * in zod's sense (`continue !== true`, which covers `fatal: true` and the string\n * shorthand, whose issue carries no `continue` at all) — or when the callback\n * set it directly, also public payload API. A union option uses it to mark\n * itself aborted, matching how zod prunes option errors; without it an option\n * failing only through superRefine would be surfaced directly instead of inside\n * `invalid_union`.\n */\nexport const ZC_SR_DECL =\n \"function __zcSr(f,v,p,e){var q={value:v,issues:[]};__zcSrRun(f,q);\" +\n \"for(var i=0;i<q.issues.length;i++){var s=q.issues[i],t={};\" +\n 'for(var k in s){if(k!==\"inst\"&&k!==\"continue\")t[k]=s[k];}' +\n \"if(s.continue!==true)q.aborted=true;\" +\n \"t.path=s.path&&s.path.length?p.concat(s.path):p;e.push(t);}return q;}\";\n\n/** Non-issue runtime helper declarations hosted in the virtual module. */\nexport const RUNTIME_HELPER_DECLS: Readonly<Record<string, string>> = {\n __zcAb: ZC_AB_DECL,\n __zcFsr: ZC_FSR_DECL,\n __zcFz: ZC_FZ_DECL,\n __zcHop: ZC_HOP_DECL,\n __zcPfx: ZC_PFX_DECL,\n __zcSr: ZC_SR_DECL,\n __zcSrOk: ZC_SR_OK_DECL,\n};\n"],"mappings":";AA4CA,MAAa,cAAgD;CAC3D,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;AACV;;;;;;AAOA,MAAa,cACX;;;;;;;;;;AAWF,MAAa,cAAc;;;;;;;;;;;;;;AAe3B,MAAM,uBAA0C;CAC9C;CACA;CACA;CACA;CACA;CACA;AACF;;AAGA,SAAgB,iBAAiB,UAA0B;CACzD,OAAO,qBAAqB,KAAK,SAAS,GAAG,SAAS,KAAK,KAAK,UAAU,IAAI,GAAG,CAAC,CAAC,KAAK,IAAI;AAC9F;;;;;;;;;;;;;;;AAgBA,MAAa,aAAa,gEAAgE,iBAAiB,GAAG,EAAE;;;;;;;;;;;;AAahH,MAAa,aACX;;;;;;;;;AAYF,MAAa,cACX;;AAGF,SAAgB,gBAAgB,SAAyB;CACvD,OAAO,UAAU,QAAQ,sBAAsB,QAAQ,sBAAsB,QAAQ;AACvF;;;;;;;;;;;;;;;;AAiBA,MAAa,gBACX;;;;;;;;;;;;;AAeF,MAAa,iBACX;;;;;;;;;;;;;;;;AAkBF,MAAa,aACX;;AAOF,MAAa,uBAAyD;CACpE,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,SAAS;CACT,SAAS;CACT,QAAQ;CACR,UAAU;AACZ"}
|
|
1
|
+
{"version":3,"file":"issue-decls.js","names":[],"sources":["../../../src/core/codegen/issue-decls.ts"],"sourcesContent":["/**\n * Issue factory function bodies (statement form).\n *\n * These functions produce the same `{code, ...}` shapes that lean-mode\n * generated code would otherwise inline at every check site.\n * Hosted in \"virtual:zod-compiler/runtime\" and called as `__zcTS(...)` etc.\n *\n * Argument convention (positional, kept short to minimize call-site bytes):\n * __zcTS(minimum, origin, inclusive, input, path, msg?) — too_small\n * __zcTB(maximum, origin, inclusive, input, path, msg?) — too_big\n * __zcIT(expected, input, path, msg?) — invalid_type\n * __zcIF(format, input, path, extra?, msg?) — invalid_format (extra merged into result)\n * __zcIV(values, input, path, msg?) — invalid_value\n * __zcUK(keys, input, path, msg?) — unrecognized_keys\n *\n * The trailing msg argument carries a static custom error message; when\n * absent, the __zcFin finalizer applies the configured locale default.\n */\n\nconst ZC_TS_DECL =\n 'function __zcTS(m,o,i,inp,p,msg){var r={code:\"too_small\",minimum:m,origin:o,inclusive:i,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}';\n\nconst ZC_TS_EXACT_DECL =\n 'function __zcTSx(m,o,inp,p,msg){var r={code:\"too_small\",minimum:m,origin:o,inclusive:true,exact:true,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}';\n\nconst ZC_TB_DECL =\n 'function __zcTB(m,o,i,inp,p,msg){var r={code:\"too_big\",maximum:m,origin:o,inclusive:i,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}';\n\nconst ZC_TB_EXACT_DECL =\n 'function __zcTBx(m,o,inp,p,msg){var r={code:\"too_big\",maximum:m,origin:o,inclusive:true,exact:true,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}';\n\nconst ZC_IT_DECL =\n 'function __zcIT(e,inp,p,msg){var r={code:\"invalid_type\",expected:e,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}';\n\nconst ZC_IF_DECL =\n 'function __zcIF(f,inp,p,extra,msg){var r={code:\"invalid_format\",format:f,input:inp,path:p};if(extra)Object.assign(r,extra);if(msg!==undefined)r.message=msg;return r;}';\n\nconst ZC_IV_DECL =\n 'function __zcIV(values,inp,p,msg){var r={code:\"invalid_value\",values:values,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}';\n\nconst ZC_UK_DECL =\n 'function __zcUK(k,inp,p,msg){var r={code:\"unrecognized_keys\",keys:k,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}';\n\n/** All issue factory declarations indexed by helper name. */\nexport const ISSUE_DECLS: Readonly<Record<string, string>> = {\n __zcTS: ZC_TS_DECL,\n __zcTSx: ZC_TS_EXACT_DECL,\n __zcTB: ZC_TB_DECL,\n __zcTBx: ZC_TB_EXACT_DECL,\n __zcIT: ZC_IT_DECL,\n __zcIF: ZC_IF_DECL,\n __zcIV: ZC_IV_DECL,\n __zcUK: ZC_UK_DECL,\n};\n\n/**\n * Float-safe remainder — byte-for-byte port of zod's util.floatSafeRemainder.\n * Raw `%` mis-rejects valid multiples of decimal steps (0.3 % 0.1 !== 0);\n * zod scales both operands to integers by their decimal-place count first.\n */\nexport const ZC_FSR_DECL =\n 'function __zcFsr(v,s){var vd=((\"\"+v).split(\".\")[1]||\"\").length;var ss=\"\"+s;var sd=(ss.split(\".\")[1]||\"\").length;if(sd===0&&/\\\\d?e-\\\\d?/.test(ss)){var m=ss.match(/\\\\d?e-(\\\\d?)/);if(m&&m[1]){sd=parseInt(m[1],10);}}var d=vd>sd?vd:sd;var vi=parseInt(v.toFixed(d).replace(\".\",\"\"),10);var si=parseInt(s.toFixed(d).replace(\".\",\"\"),10);return (vi%si)/Math.pow(10,d);}';\n\n/**\n * Hoisted `Object.prototype.hasOwnProperty` reference. Record fast/slow paths\n * iterate keys with `for(k in o)` (no `Object.keys` array allocation) and guard\n * each key with `__zcHop.call(o,k)` to skip inherited enumerable properties —\n * yielding the exact own-enumerable string-key set `Object.keys` would, so\n * fast/slow stay in agreement and parity with zod's own-key record semantics is\n * preserved. The hoisted reference inlines in V8; reading the prototype property\n * per call would not.\n */\nexport const ZC_HOP_DECL = \"const __zcHop=Object.prototype.hasOwnProperty;\";\n\n/**\n * Issue codes zod raises from a schema's `_zod.parse` rather than from a check,\n * and which therefore carry `continue !== true` — what `util.aborted` looks for.\n *\n * Everything a CHECK produces (`too_small`, `too_big`, `invalid_format`,\n * `not_multiple_of`, a refine's `custom`) is continuable, because `$ZodCheck`\n * sets `continue: !def.abort` and an `abort: true` check costs the schema its\n * compiled path anyway — so classifying by code alone is exact for generated\n * issues.\n *\n * Read by {@link ZC_AB_DECL} and by the union's option-pruning loop, which\n * applies the same rule inline over a per-option issue array.\n */\nconst ABORTING_ISSUE_CODES: readonly string[] = [\n \"invalid_type\",\n \"invalid_value\",\n \"invalid_union\",\n \"unrecognized_keys\",\n \"invalid_key\",\n \"invalid_element\",\n];\n\n/** `c===\"invalid_type\"||c===\"invalid_value\"||…` over the code held in `codeExpr`. */\nexport function abortingCodeTest(codeExpr: string): string {\n return ABORTING_ISSUE_CODES.map((code) => `${codeExpr}===${JSON.stringify(code)}`).join(\"||\");\n}\n\n/**\n * Port of zod's `util.aborted(payload, startIndex)`: has anything since\n * `startIndex` produced a NON-continuable issue?\n *\n * zod gates a schema's check chain on this (`runChecks`: `else if (isAborted)\n * continue`), which is what makes a container's `.refine()` still run after a\n * property or element failed its own `min`/`max`/format check, and stop running\n * once one failed to parse at all. The `startIndex` is the issue count when the\n * node was entered, mirroring the fresh payload zod hands each sub-schema.\n *\n * Size/length checks are exempt in zod — they declare a `when` predicate, which\n * bypasses the abort gate — so generated code leaves those ungated and only\n * wraps the refine/superRefine effects.\n */\nexport const ZC_AB_DECL = `function __zcAb(e,i){for(;i<e.length;i++){var c=e[i].code;if(${abortingCodeTest(\"c\")})return true;}return false;}`;\n\n/**\n * Port of zod's `util.finalizeIssue` for issues NESTED inside an\n * `invalid_key` / `invalid_element` wrapper. Those never reach the top-level\n * finalization loop (which walks only the outer array), so zod finalizes them\n * where it builds the wrapper: locale message applied when none was baked in,\n * `input` cleared. Their `path` stays RELATIVE to the key or value schema — zod\n * ran it on a fresh payload — so nothing rewrites it.\n *\n * `input` is cleared by assignment rather than `delete`, matching the top-level\n * finalizer and the union's per-option loop.\n */\nexport const ZC_FZ_DECL =\n \"function __zcFz(e){for(var i=0;i<e.length;i++){var s=e[i];\" +\n 'if(s.message===undefined&&typeof __zcMsg===\"function\")s.message=__zcMsg(s);' +\n \"s.input=undefined;}return e;}\";\n\n/**\n * Port of zod's `util.prefixIssues(key, issues)` for a map entry whose key is a\n * property-key type: each issue's RELATIVE path is spliced onto the map's own\n * path plus the key, and the issue moves into the parent's array.\n *\n * `b.concat(k,x.path)` flattens `x.path` one level, giving exactly\n * `[...base, key, ...relative]`.\n */\nexport const ZC_PFX_DECL =\n \"function __zcPfx(d,s,b,k){for(var i=0;i<s.length;i++){var x=s[i];x.path=b.concat(k,x.path);d.push(x);}}\";\n\n/** Does `keyExpr` hold one of zod's `util.propertyKeyTypes` (string|number|symbol)? */\nexport function propertyKeyTest(keyExpr: string): string {\n return `typeof ${keyExpr}===\"string\"||typeof ${keyExpr}===\"number\"||typeof ${keyExpr}===\"symbol\"`;\n}\n\n/**\n * superRefine fast-check: run the payload callback on a throwaway payload and\n * report whether it both added nothing AND left the value alone. zod's own\n * wrapper (the referenced function) installs `addIssue` and normalizes what the\n * user adds, so the verdict is zod's; the issues themselves are re-collected by\n * the slow walk.\n *\n * The `p.value===v` half is what lets a superRefine node keep a fast path at\n * all. `value` is writable public API ($RefinementCtx extends ParsePayload), so\n * a callback may rewrite it, and the fast path's caller returns the ORIGINAL\n * input on success — which would then be stale. Reporting false when the value\n * moved routes those parses into the slow walk, which propagates the new value\n * (see ZC_SR_DECL). Callbacks that only validate — effectively all of them —\n * still take the fast exit.\n */\nexport const ZC_SR_OK_DECL =\n \"function __zcSrOk(f,v){var p={value:v,issues:[]};__zcSrRun(f,p);\" +\n \"return p.issues.length===0&&p.value===v;}\";\n\n/**\n * Module-local (never imported by generated code) — __zcSr/__zcSrOk call it.\n * Lean mode declares it in the runtime module beside them; inline mode pushes it\n * into the preamble alongside whichever of the two is used.\n *\n * Invoke the referenced wrapper, reproducing zod's synchronous-parse contract:\n * a callback that returns a promise makes zod raise $ZodAsyncError rather than\n * silently accepting. Because the ref points at zod's superRefine WRAPPER, not\n * the user's function, an async callback cannot be detected while extracting —\n * the returned thenable is the only evidence, so the test lives here. Sync\n * callbacks return undefined, so the guard short-circuits on the first operand.\n */\nexport const ZC_SR_RUN_DECL =\n \"function __zcSrRun(f,p){var r=f(p);\" +\n 'if(r&&typeof r.then===\"function\"){throw new __zcCore.$ZodAsyncError();}}';\n\n/**\n * z.custom()/z.instanceof() fast verdict. Zod treats truthy predicate returns\n * as success and raises $ZodAsyncError when a synchronous parse encounters a\n * thenable, including a non-async function that happens to return a Promise.\n */\nexport const ZC_CUSTOM_OK_DECL =\n \"function __zcCu(f,v){var r=f(v);\" +\n 'if(r&&typeof r.then===\"function\"){throw new __zcCore.$ZodAsyncError();}return !!r;}';\n\n/**\n * superRefine slow-path merge: run the callback, then move its issues onto the\n * validator's list the way zod's finalizeIssue does — the node's path prefixed\n * onto any path the user supplied, and the internal `inst`/`continue` fields\n * dropped (they are zod bookkeeping, deleted before the issue is user-visible).\n *\n * Returns the payload, so the caller can write `.value` back (the callback may\n * have rewritten it) and read `.aborted`. Aborted is set when any issue aborts\n * in zod's sense (`continue !== true`, which covers `fatal: true` and the string\n * shorthand, whose issue carries no `continue` at all) — or when the callback\n * set it directly, also public payload API. A union option uses it to mark\n * itself aborted, matching how zod prunes option errors; without it an option\n * failing only through superRefine would be surfaced directly instead of inside\n * `invalid_union`.\n */\nexport const ZC_SR_DECL =\n \"function __zcSr(f,v,p,e){var q={value:v,issues:[]};__zcSrRun(f,q);\" +\n \"for(var i=0;i<q.issues.length;i++){var s=q.issues[i],t={};\" +\n 'for(var k in s){if(k!==\"inst\"&&k!==\"continue\")t[k]=s[k];}' +\n \"if(s.continue!==true)q.aborted=true;\" +\n \"t.path=s.path&&s.path.length?p.concat(s.path):p;e.push(t);}return q;}\";\n\n/** Non-issue runtime helper declarations hosted in the virtual module. */\nexport const RUNTIME_HELPER_DECLS: Readonly<Record<string, string>> = {\n __zcAb: ZC_AB_DECL,\n __zcFsr: ZC_FSR_DECL,\n __zcFz: ZC_FZ_DECL,\n __zcHop: ZC_HOP_DECL,\n __zcPfx: ZC_PFX_DECL,\n __zcCu: ZC_CUSTOM_OK_DECL,\n __zcSr: ZC_SR_DECL,\n __zcSrOk: ZC_SR_OK_DECL,\n};\n"],"mappings":";AA4CA,MAAa,cAAgD;CAC3D,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,QAAQ;AACV;;;;;;AAOA,MAAa,cACX;;;;;;;;;;AAWF,MAAa,cAAc;;;;;;;;;;;;;;AAe3B,MAAM,uBAA0C;CAC9C;CACA;CACA;CACA;CACA;CACA;AACF;;AAGA,SAAgB,iBAAiB,UAA0B;CACzD,OAAO,qBAAqB,KAAK,SAAS,GAAG,SAAS,KAAK,KAAK,UAAU,IAAI,GAAG,CAAC,CAAC,KAAK,IAAI;AAC9F;;;;;;;;;;;;;;;AAgBA,MAAa,aAAa,gEAAgE,iBAAiB,GAAG,EAAE;;;;;;;;;;;;AAahH,MAAa,aACX;;;;;;;;;AAYF,MAAa,cACX;;AAGF,SAAgB,gBAAgB,SAAyB;CACvD,OAAO,UAAU,QAAQ,sBAAsB,QAAQ,sBAAsB,QAAQ;AACvF;;;;;;;;;;;;;;;;AAiBA,MAAa,gBACX;;;;;;;;;;;;;AAeF,MAAa,iBACX;;;;;;AAQF,MAAa,oBACX;;;;;;;;;;;;;;;;AAkBF,MAAa,aACX;;AAOF,MAAa,uBAAyD;CACpE,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,SAAS;CACT,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,UAAU;AACZ"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CustomIR } from "../../types.js";
|
|
2
|
+
import { FastGen, SlowGen } from "../context.js";
|
|
3
|
+
//#region src/core/codegen/schemas/custom.d.ts
|
|
4
|
+
/** Total hot-path predicate for z.custom() and z.instanceof(). */
|
|
5
|
+
declare function fastCustom(ir: CustomIR, g: FastGen): string;
|
|
6
|
+
/** Let Zod construct the exact custom/instanceof issue only after rejection. */
|
|
7
|
+
declare function slowCustom(ir: CustomIR, g: SlowGen): string;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { fastCustom, slowCustom };
|
|
10
|
+
//# sourceMappingURL=custom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/custom.ts"],"mappings":";;;;iBAOgB,WAAW,IAAI,UAAU,GAAG;;iBAM5B,WAAW,IAAI,UAAU,GAAG"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { emitEffectCallable, emitRuntimeHelper } from "../context.js";
|
|
2
|
+
import { ZC_CUSTOM_OK_DECL } from "../issue-decls.js";
|
|
3
|
+
import { slowZodDelegate } from "./fallback.js";
|
|
4
|
+
//#region src/core/codegen/schemas/custom.ts
|
|
5
|
+
/** Total hot-path predicate for z.custom() and z.instanceof(). */
|
|
6
|
+
function fastCustom(ir, g) {
|
|
7
|
+
return `${emitRuntimeHelper(g.ctx, "__zcCu", ZC_CUSTOM_OK_DECL)}(${emitEffectCallable(g.ctx, ir)},${g.input})`;
|
|
8
|
+
}
|
|
9
|
+
/** Let Zod construct the exact custom/instanceof issue only after rejection. */
|
|
10
|
+
function slowCustom(ir, g) {
|
|
11
|
+
const onFailure = ir.abort && g.aborted ? `${g.aborted}=true;` : "";
|
|
12
|
+
return slowZodDelegate(ir.schemaRefIndex, g, onFailure);
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { fastCustom, slowCustom };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=custom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.js","names":[],"sources":["../../../../src/core/codegen/schemas/custom.ts"],"sourcesContent":["import type { CustomIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport { emitEffectCallable, emitRuntimeHelper } from \"../context.js\";\nimport { ZC_CUSTOM_OK_DECL } from \"../issue-decls.js\";\nimport { slowZodDelegate } from \"./fallback.js\";\n\n/** Total hot-path predicate for z.custom() and z.instanceof(). */\nexport function fastCustom(ir: CustomIR, g: FastGen): string {\n const ok = emitRuntimeHelper(g.ctx, \"__zcCu\", ZC_CUSTOM_OK_DECL);\n return `${ok}(${emitEffectCallable(g.ctx, ir)},${g.input})`;\n}\n\n/** Let Zod construct the exact custom/instanceof issue only after rejection. */\nexport function slowCustom(ir: CustomIR, g: SlowGen): string {\n const onFailure = ir.abort && g.aborted ? `${g.aborted}=true;` : \"\";\n return slowZodDelegate(ir.schemaRefIndex, g, onFailure);\n}\n"],"mappings":";;;;;AAOA,SAAgB,WAAW,IAAc,GAAoB;CAE3D,OAAO,GADI,kBAAkB,EAAE,KAAK,UAAU,iBACnC,EAAE,GAAG,mBAAmB,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM;AAC3D;;AAGA,SAAgB,WAAW,IAAc,GAAoB;CAC3D,MAAM,YAAY,GAAG,SAAS,EAAE,UAAU,GAAG,EAAE,QAAQ,UAAU;CACjE,OAAO,gBAAgB,GAAG,gBAAgB,GAAG,SAAS;AACxD"}
|
|
@@ -2,6 +2,8 @@ import { FallbackIR } from "../../types.js";
|
|
|
2
2
|
import { SlowGen } from "../context.js";
|
|
3
3
|
//#region src/core/codegen/schemas/fallback.d.ts
|
|
4
4
|
declare function slowFallback(ir: FallbackIR, g: SlowGen): string;
|
|
5
|
+
/** Delegate a cold slow-walk leaf to a pristine retained Zod schema. */
|
|
6
|
+
declare function slowZodDelegate(refIndex: number, g: SlowGen, onFailure?: string): string;
|
|
5
7
|
//#endregion
|
|
6
|
-
export { slowFallback };
|
|
8
|
+
export { slowFallback, slowZodDelegate };
|
|
7
9
|
//# sourceMappingURL=fallback.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fallback.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/fallback.ts"],"mappings":";;;iBAKgB,aAAa,IAAI,YAAY,GAAG"}
|
|
1
|
+
{"version":3,"file":"fallback.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/fallback.ts"],"mappings":";;;iBAKgB,aAAa,IAAI,YAAY,GAAG;;iBAQhC,gBAAgB,kBAAkB,GAAG,SAAS"}
|
|
@@ -2,28 +2,31 @@ import { emitRfDelegate } from "../context.js";
|
|
|
2
2
|
import { emit } from "../emit.js";
|
|
3
3
|
//#region src/core/codegen/schemas/fallback.ts
|
|
4
4
|
function slowFallback(ir, g) {
|
|
5
|
-
if (ir.refIndex !== void 0)
|
|
6
|
-
const idx = ir.refIndex;
|
|
7
|
-
const delegate = emitRfDelegate(g.ctx, idx);
|
|
8
|
-
const rVar = `__rf_r${idx}`;
|
|
9
|
-
const iVar = `__rf_i${idx}`;
|
|
10
|
-
const jVar = `__rf_j${idx}`;
|
|
11
|
-
return `${emit`
|
|
12
|
-
var ${rVar}=${delegate}(${g.input});
|
|
13
|
-
if(!${rVar}.success){
|
|
14
|
-
var ${iVar}=${rVar}.error.issues;
|
|
15
|
-
for(var ${jVar}=0;${jVar}<${iVar}.length;${jVar}++){
|
|
16
|
-
${g.issues}.push({...${iVar}[${jVar}],
|
|
17
|
-
path:${g.path}.concat(${iVar}[${jVar}].path)});
|
|
18
|
-
}
|
|
19
|
-
}else{
|
|
20
|
-
${g.output}=${rVar}.data;
|
|
21
|
-
}
|
|
22
|
-
`}\n`;
|
|
23
|
-
}
|
|
5
|
+
if (ir.refIndex !== void 0) return slowZodDelegate(ir.refIndex, g);
|
|
24
6
|
return `${g.issues}.push({code:"custom",path:${g.path},message:"Fallback schema: ${ir.reason}"});\n`;
|
|
25
7
|
}
|
|
8
|
+
/** Delegate a cold slow-walk leaf to a pristine retained Zod schema. */
|
|
9
|
+
function slowZodDelegate(refIndex, g, onFailure = "") {
|
|
10
|
+
const idx = refIndex;
|
|
11
|
+
const delegate = emitRfDelegate(g.ctx, idx);
|
|
12
|
+
const rVar = `__rf_r${idx}`;
|
|
13
|
+
const iVar = `__rf_i${idx}`;
|
|
14
|
+
const jVar = `__rf_j${idx}`;
|
|
15
|
+
return `${emit`
|
|
16
|
+
var ${rVar}=${delegate}(${g.input});
|
|
17
|
+
if(!${rVar}.success){
|
|
18
|
+
${onFailure}
|
|
19
|
+
var ${iVar}=${rVar}.error.issues;
|
|
20
|
+
for(var ${jVar}=0;${jVar}<${iVar}.length;${jVar}++){
|
|
21
|
+
${g.issues}.push({...${iVar}[${jVar}],
|
|
22
|
+
path:${g.path}.concat(${iVar}[${jVar}].path)});
|
|
23
|
+
}
|
|
24
|
+
}else{
|
|
25
|
+
${g.output}=${rVar}.data;
|
|
26
|
+
}
|
|
27
|
+
`}\n`;
|
|
28
|
+
}
|
|
26
29
|
//#endregion
|
|
27
|
-
export { slowFallback };
|
|
30
|
+
export { slowFallback, slowZodDelegate };
|
|
28
31
|
|
|
29
32
|
//# sourceMappingURL=fallback.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fallback.js","names":[],"sources":["../../../../src/core/codegen/schemas/fallback.ts"],"sourcesContent":["import type { FallbackIR } from \"../../types.js\";\nimport type { SlowGen } from \"../context.js\";\nimport { emitRfDelegate } from \"../context.js\";\nimport { emit } from \"../emit.js\";\n\nexport function slowFallback(ir: FallbackIR, g: SlowGen): string {\n if (ir.refIndex !== undefined) {\n const idx =
|
|
1
|
+
{"version":3,"file":"fallback.js","names":[],"sources":["../../../../src/core/codegen/schemas/fallback.ts"],"sourcesContent":["import type { FallbackIR } from \"../../types.js\";\nimport type { SlowGen } from \"../context.js\";\nimport { emitRfDelegate } from \"../context.js\";\nimport { emit } from \"../emit.js\";\n\nexport function slowFallback(ir: FallbackIR, g: SlowGen): string {\n if (ir.refIndex !== undefined) {\n return slowZodDelegate(ir.refIndex, g);\n }\n return `${g.issues}.push({code:\"custom\",path:${g.path},message:\"Fallback schema: ${ir.reason}\"});\\n`;\n}\n\n/** Delegate a cold slow-walk leaf to a pristine retained Zod schema. */\nexport function slowZodDelegate(refIndex: number, g: SlowGen, onFailure = \"\"): string {\n const idx = refIndex;\n // Captured pre-__zcMkv (emitRfDelegate): a per-parse `__rf[N].safeParse`\n // read resolves to the compiled validator itself when CSE/dedup or an\n // identifier schemaExpr makes the entry alias the mutated schema object.\n const delegate = emitRfDelegate(g.ctx, idx);\n const rVar = `__rf_r${idx}`;\n const iVar = `__rf_i${idx}`;\n const jVar = `__rf_j${idx}`;\n return `${emit`\n var ${rVar}=${delegate}(${g.input});\n if(!${rVar}.success){\n ${onFailure}\n var ${iVar}=${rVar}.error.issues;\n for(var ${jVar}=0;${jVar}<${iVar}.length;${jVar}++){\n ${g.issues}.push({...${iVar}[${jVar}],\n path:${g.path}.concat(${iVar}[${jVar}].path)});\n }\n }else{\n ${g.output}=${rVar}.data;\n }\n `}\\n`;\n}\n"],"mappings":";;;AAKA,SAAgB,aAAa,IAAgB,GAAoB;CAC/D,IAAI,GAAG,aAAa,KAAA,GAClB,OAAO,gBAAgB,GAAG,UAAU,CAAC;CAEvC,OAAO,GAAG,EAAE,OAAO,4BAA4B,EAAE,KAAK,6BAA6B,GAAG,OAAO;AAC/F;;AAGA,SAAgB,gBAAgB,UAAkB,GAAY,YAAY,IAAY;CACpF,MAAM,MAAM;CAIZ,MAAM,WAAW,eAAe,EAAE,KAAK,GAAG;CAC1C,MAAM,OAAO,SAAS;CACtB,MAAM,OAAO,SAAS;CACtB,MAAM,OAAO,SAAS;CACtB,OAAO,GAAG,IAAI;UACN,KAAK,GAAG,SAAS,GAAG,EAAE,MAAM;UAC5B,KAAK;QACP,UAAU;YACN,KAAK,GAAG,KAAK;gBACT,KAAK,KAAK,KAAK,GAAG,KAAK,UAAU,KAAK;UAC5C,EAAE,OAAO,YAAY,KAAK,GAAG,KAAK;iBAC3B,EAAE,KAAK,UAAU,KAAK,GAAG,KAAK;;;QAGvC,EAAE,OAAO,GAAG,KAAK;;IAErB;AACJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slow-path.d.ts","names":[],"sources":["../../../src/core/codegen/slow-path.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"slow-path.d.ts","names":[],"sources":["../../../src/core/codegen/slow-path.ts"],"mappings":";;;iBAmGgB,cACd,mBACA,oBACA,kBACA,mBACA,KAAK,gBACL,sBACC;iBAyCa,aAAa,IAAI,UAAU,GAAG"}
|
|
@@ -5,11 +5,12 @@ import { slowArray } from "./schemas/array.js";
|
|
|
5
5
|
import { slowBigInt } from "./schemas/bigint.js";
|
|
6
6
|
import { slowBoolean } from "./schemas/boolean.js";
|
|
7
7
|
import { slowCatch } from "./schemas/catch.js";
|
|
8
|
+
import { slowFallback } from "./schemas/fallback.js";
|
|
9
|
+
import { slowCustom } from "./schemas/custom.js";
|
|
8
10
|
import { slowDate } from "./schemas/date.js";
|
|
9
11
|
import { slowDefault } from "./schemas/default.js";
|
|
10
12
|
import { slowDiscriminatedUnion } from "./schemas/discriminated-union.js";
|
|
11
13
|
import { slowEnum } from "./schemas/enum.js";
|
|
12
|
-
import { slowFallback } from "./schemas/fallback.js";
|
|
13
14
|
import { slowFile } from "./schemas/file.js";
|
|
14
15
|
import { slowIntersection } from "./schemas/intersection.js";
|
|
15
16
|
import { slowLiteral } from "./schemas/literal.js";
|
|
@@ -68,6 +69,7 @@ const slowRegistry = {
|
|
|
68
69
|
default: slowDefault,
|
|
69
70
|
pipe: slowPipe,
|
|
70
71
|
effect: slowEffect,
|
|
72
|
+
custom: slowCustom,
|
|
71
73
|
templateLiteral: slowTemplateLiteral,
|
|
72
74
|
catch: slowCatch,
|
|
73
75
|
fallback: slowFallback,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slow-path.js","names":[],"sources":["../../../src/core/codegen/slow-path.ts"],"sourcesContent":["import type { SchemaIR } from \"../types.js\";\nimport type { CodeGenContext, SlowGen, SlowGenerator } from \"./context.js\";\nimport { emitRegex, emitSet, emitTemp } from \"./context.js\";\nimport { slowAny } from \"./schemas/any.js\";\nimport { slowArray } from \"./schemas/array.js\";\nimport { slowBigInt } from \"./schemas/bigint.js\";\nimport { slowBoolean } from \"./schemas/boolean.js\";\nimport { slowCatch } from \"./schemas/catch.js\";\nimport { slowDate } from \"./schemas/date.js\";\nimport { slowDefault } from \"./schemas/default.js\";\nimport { slowDiscriminatedUnion } from \"./schemas/discriminated-union.js\";\nimport { slowEffect } from \"./schemas/effect.js\";\nimport { slowEnum } from \"./schemas/enum.js\";\nimport { slowFallback } from \"./schemas/fallback.js\";\nimport { slowFile } from \"./schemas/file.js\";\nimport { slowIntersection } from \"./schemas/intersection.js\";\nimport { slowLiteral } from \"./schemas/literal.js\";\nimport { slowMap } from \"./schemas/map.js\";\nimport { slowNan } from \"./schemas/nan.js\";\nimport { slowNever } from \"./schemas/never.js\";\nimport { slowNull } from \"./schemas/null.js\";\nimport { slowNullable } from \"./schemas/nullable.js\";\nimport { slowNumber } from \"./schemas/number.js\";\nimport { slowObject } from \"./schemas/object.js\";\nimport { slowOptional } from \"./schemas/optional.js\";\nimport { slowPipe } from \"./schemas/pipe.js\";\nimport { slowReadonly } from \"./schemas/readonly.js\";\nimport { slowRecord } from \"./schemas/record.js\";\nimport { slowRecursionTarget, slowRecursiveRef } from \"./schemas/recursive-ref.js\";\nimport { slowSet } from \"./schemas/set.js\";\nimport { slowString } from \"./schemas/string.js\";\nimport { slowStringBool } from \"./schemas/string-bool.js\";\nimport { slowSymbol } from \"./schemas/symbol.js\";\nimport { slowTemplateLiteral } from \"./schemas/template-literal.js\";\nimport { slowTuple } from \"./schemas/tuple.js\";\nimport { slowUndefined } from \"./schemas/undefined.js\";\nimport { slowUnion } from \"./schemas/union.js\";\nimport { slowUnknown } from \"./schemas/unknown.js\";\nimport { slowVoid } from \"./schemas/void.js\";\n\n// ─── Typed registry ─────────────────────────────────────────────────────────\n// Adding a new SchemaIR type without registering a generator here causes a\n// compile error, preventing the silent-missing-case bugs that plagued the old\n// switch-based dispatch.\n\nconst slowRegistry = {\n // Primitives (order follows SchemaIR union in types.ts)\n string: slowString,\n number: slowNumber,\n boolean: slowBoolean,\n bigint: slowBigInt,\n date: slowDate,\n symbol: slowSymbol,\n null: slowNull,\n undefined: slowUndefined,\n void: slowVoid,\n nan: slowNan,\n never: slowNever,\n any: slowAny,\n unknown: slowUnknown,\n literal: slowLiteral,\n enum: slowEnum,\n // Containers\n object: slowObject,\n array: slowArray,\n tuple: slowTuple,\n record: slowRecord,\n set: slowSet,\n map: slowMap,\n file: slowFile,\n // Unions & Intersections\n union: slowUnion,\n discriminatedUnion: slowDiscriminatedUnion,\n intersection: slowIntersection,\n // Modifiers\n optional: slowOptional,\n nullable: slowNullable,\n readonly: slowReadonly,\n default: slowDefault,\n pipe: slowPipe,\n // Effects\n effect: slowEffect,\n // Special\n templateLiteral: slowTemplateLiteral,\n catch: slowCatch,\n fallback: slowFallback,\n recursiveRef: slowRecursiveRef,\n recursionTarget: slowRecursionTarget,\n stringBool: slowStringBool,\n} satisfies {\n [K in SchemaIR[\"type\"]]: SlowGenerator<Extract<SchemaIR, { type: K }>>;\n};\n\n// ─── Factory ─────────────────────────────────────────────────────────────────\n// Lives here (not in context.ts) to avoid circular imports:\n// visit() → generateSlow() → imports from context.ts\n\nexport function createSlowGen(\n inputExpr: string,\n outputExpr: string,\n pathExpr: string,\n issuesVar: string,\n ctx: CodeGenContext,\n abortedVar?: string,\n): SlowGen {\n return {\n input: inputExpr,\n output: outputExpr,\n path: pathExpr,\n issues: issuesVar,\n aborted: abortedVar,\n ctx,\n visit(ir, overrides) {\n const input = overrides?.input ?? inputExpr;\n const output = overrides?.output ?? outputExpr;\n const path = overrides?.path ?? pathExpr;\n const issues = overrides?.issues ?? issuesVar;\n // `aborted` is deliberately NOT inherited (see SlowGen.aborted): a child\n // only tracks abort when the parent explicitly forwards it, so a pipe\n // buried inside a container never trips its ancestor union's abort flag.\n const aborted = overrides?.aborted;\n // Shared sub-schema: call the file-level `__zcSw_N(input, path, issues)`\n // walk (signature defined in dedupe.ts) instead of inlining a duplicate.\n // It returns the parsed value, so the assignment delivers whatever an\n // inlined walk would have written to `output` — a stripping object's\n // rebuilt result, a coerced number, a trimmed string.\n //\n // But the shared walk's 3-arg signature can't carry this call site's\n // abort flag — a shared pipe option would silently drop it. When we're in\n // an abort-tracking position (`aborted` set), inline instead of sharing so\n // the pipe's abort reaches the union. Non-tracking sites still share.\n const ref = aborted === undefined ? ctx.sharedSchemas?.refFor(ir) : undefined;\n if (ref !== undefined) {\n return `${output}=${ref.name}(${input},${path},${issues});`;\n }\n return generateSlow(ir, createSlowGen(input, output, path, issues, ctx, aborted));\n },\n temp: (prefix) => emitTemp(ctx, prefix),\n regex: (prefix, pattern, flags) => emitRegex(ctx, prefix, pattern, flags),\n set: (prefix, values) => emitSet(ctx, prefix, values),\n };\n}\n\n// ─── Dispatch ────────────────────────────────────────────────────────────────\n\nexport function generateSlow(ir: SchemaIR, g: SlowGen): string {\n // Surface the node's schema-level error message to issue emission. visit()\n // always builds a fresh context, so typeMsg never leaks into child nodes.\n const gen0 = ir.typeMessage !== g.typeMsg ? { ...g, typeMsg: ir.typeMessage } : g;\n const gen = slowRegistry[ir.type];\n // oxlint-disable-next-line typescript/no-explicit-any -- registry dispatch requires type erasure at call site\n return (gen as any)(ir, gen0);\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"slow-path.js","names":[],"sources":["../../../src/core/codegen/slow-path.ts"],"sourcesContent":["import type { SchemaIR } from \"../types.js\";\nimport type { CodeGenContext, SlowGen, SlowGenerator } from \"./context.js\";\nimport { emitRegex, emitSet, emitTemp } from \"./context.js\";\nimport { slowAny } from \"./schemas/any.js\";\nimport { slowArray } from \"./schemas/array.js\";\nimport { slowBigInt } from \"./schemas/bigint.js\";\nimport { slowBoolean } from \"./schemas/boolean.js\";\nimport { slowCatch } from \"./schemas/catch.js\";\nimport { slowCustom } from \"./schemas/custom.js\";\nimport { slowDate } from \"./schemas/date.js\";\nimport { slowDefault } from \"./schemas/default.js\";\nimport { slowDiscriminatedUnion } from \"./schemas/discriminated-union.js\";\nimport { slowEffect } from \"./schemas/effect.js\";\nimport { slowEnum } from \"./schemas/enum.js\";\nimport { slowFallback } from \"./schemas/fallback.js\";\nimport { slowFile } from \"./schemas/file.js\";\nimport { slowIntersection } from \"./schemas/intersection.js\";\nimport { slowLiteral } from \"./schemas/literal.js\";\nimport { slowMap } from \"./schemas/map.js\";\nimport { slowNan } from \"./schemas/nan.js\";\nimport { slowNever } from \"./schemas/never.js\";\nimport { slowNull } from \"./schemas/null.js\";\nimport { slowNullable } from \"./schemas/nullable.js\";\nimport { slowNumber } from \"./schemas/number.js\";\nimport { slowObject } from \"./schemas/object.js\";\nimport { slowOptional } from \"./schemas/optional.js\";\nimport { slowPipe } from \"./schemas/pipe.js\";\nimport { slowReadonly } from \"./schemas/readonly.js\";\nimport { slowRecord } from \"./schemas/record.js\";\nimport { slowRecursionTarget, slowRecursiveRef } from \"./schemas/recursive-ref.js\";\nimport { slowSet } from \"./schemas/set.js\";\nimport { slowString } from \"./schemas/string.js\";\nimport { slowStringBool } from \"./schemas/string-bool.js\";\nimport { slowSymbol } from \"./schemas/symbol.js\";\nimport { slowTemplateLiteral } from \"./schemas/template-literal.js\";\nimport { slowTuple } from \"./schemas/tuple.js\";\nimport { slowUndefined } from \"./schemas/undefined.js\";\nimport { slowUnion } from \"./schemas/union.js\";\nimport { slowUnknown } from \"./schemas/unknown.js\";\nimport { slowVoid } from \"./schemas/void.js\";\n\n// ─── Typed registry ─────────────────────────────────────────────────────────\n// Adding a new SchemaIR type without registering a generator here causes a\n// compile error, preventing the silent-missing-case bugs that plagued the old\n// switch-based dispatch.\n\nconst slowRegistry = {\n // Primitives (order follows SchemaIR union in types.ts)\n string: slowString,\n number: slowNumber,\n boolean: slowBoolean,\n bigint: slowBigInt,\n date: slowDate,\n symbol: slowSymbol,\n null: slowNull,\n undefined: slowUndefined,\n void: slowVoid,\n nan: slowNan,\n never: slowNever,\n any: slowAny,\n unknown: slowUnknown,\n literal: slowLiteral,\n enum: slowEnum,\n // Containers\n object: slowObject,\n array: slowArray,\n tuple: slowTuple,\n record: slowRecord,\n set: slowSet,\n map: slowMap,\n file: slowFile,\n // Unions & Intersections\n union: slowUnion,\n discriminatedUnion: slowDiscriminatedUnion,\n intersection: slowIntersection,\n // Modifiers\n optional: slowOptional,\n nullable: slowNullable,\n readonly: slowReadonly,\n default: slowDefault,\n pipe: slowPipe,\n // Effects\n effect: slowEffect,\n // Special\n custom: slowCustom,\n templateLiteral: slowTemplateLiteral,\n catch: slowCatch,\n fallback: slowFallback,\n recursiveRef: slowRecursiveRef,\n recursionTarget: slowRecursionTarget,\n stringBool: slowStringBool,\n} satisfies {\n [K in SchemaIR[\"type\"]]: SlowGenerator<Extract<SchemaIR, { type: K }>>;\n};\n\n// ─── Factory ─────────────────────────────────────────────────────────────────\n// Lives here (not in context.ts) to avoid circular imports:\n// visit() → generateSlow() → imports from context.ts\n\nexport function createSlowGen(\n inputExpr: string,\n outputExpr: string,\n pathExpr: string,\n issuesVar: string,\n ctx: CodeGenContext,\n abortedVar?: string,\n): SlowGen {\n return {\n input: inputExpr,\n output: outputExpr,\n path: pathExpr,\n issues: issuesVar,\n aborted: abortedVar,\n ctx,\n visit(ir, overrides) {\n const input = overrides?.input ?? inputExpr;\n const output = overrides?.output ?? outputExpr;\n const path = overrides?.path ?? pathExpr;\n const issues = overrides?.issues ?? issuesVar;\n // `aborted` is deliberately NOT inherited (see SlowGen.aborted): a child\n // only tracks abort when the parent explicitly forwards it, so a pipe\n // buried inside a container never trips its ancestor union's abort flag.\n const aborted = overrides?.aborted;\n // Shared sub-schema: call the file-level `__zcSw_N(input, path, issues)`\n // walk (signature defined in dedupe.ts) instead of inlining a duplicate.\n // It returns the parsed value, so the assignment delivers whatever an\n // inlined walk would have written to `output` — a stripping object's\n // rebuilt result, a coerced number, a trimmed string.\n //\n // But the shared walk's 3-arg signature can't carry this call site's\n // abort flag — a shared pipe option would silently drop it. When we're in\n // an abort-tracking position (`aborted` set), inline instead of sharing so\n // the pipe's abort reaches the union. Non-tracking sites still share.\n const ref = aborted === undefined ? ctx.sharedSchemas?.refFor(ir) : undefined;\n if (ref !== undefined) {\n return `${output}=${ref.name}(${input},${path},${issues});`;\n }\n return generateSlow(ir, createSlowGen(input, output, path, issues, ctx, aborted));\n },\n temp: (prefix) => emitTemp(ctx, prefix),\n regex: (prefix, pattern, flags) => emitRegex(ctx, prefix, pattern, flags),\n set: (prefix, values) => emitSet(ctx, prefix, values),\n };\n}\n\n// ─── Dispatch ────────────────────────────────────────────────────────────────\n\nexport function generateSlow(ir: SchemaIR, g: SlowGen): string {\n // Surface the node's schema-level error message to issue emission. visit()\n // always builds a fresh context, so typeMsg never leaks into child nodes.\n const gen0 = ir.typeMessage !== g.typeMsg ? { ...g, typeMsg: ir.typeMessage } : g;\n const gen = slowRegistry[ir.type];\n // oxlint-disable-next-line typescript/no-explicit-any -- registry dispatch requires type erasure at call site\n return (gen as any)(ir, gen0);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,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;AAQA,SAAgB,cACd,WACA,YACA,UACA,WACA,KACA,YACS;CACT,OAAO;EACL,OAAO;EACP,QAAQ;EACR,MAAM;EACN,QAAQ;EACR,SAAS;EACT;EACA,MAAM,IAAI,WAAW;GACnB,MAAM,QAAQ,WAAW,SAAS;GAClC,MAAM,SAAS,WAAW,UAAU;GACpC,MAAM,OAAO,WAAW,QAAQ;GAChC,MAAM,SAAS,WAAW,UAAU;GAIpC,MAAM,UAAU,WAAW;GAW3B,MAAM,MAAM,YAAY,KAAA,IAAY,IAAI,eAAe,OAAO,EAAE,IAAI,KAAA;GACpE,IAAI,QAAQ,KAAA,GACV,OAAO,GAAG,OAAO,GAAG,IAAI,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO;GAE1D,OAAO,aAAa,IAAI,cAAc,OAAO,QAAQ,MAAM,QAAQ,KAAK,OAAO,CAAC;EAClF;EACA,OAAO,WAAW,SAAS,KAAK,MAAM;EACtC,QAAQ,QAAQ,SAAS,UAAU,UAAU,KAAK,QAAQ,SAAS,KAAK;EACxE,MAAM,QAAQ,WAAW,QAAQ,KAAK,QAAQ,MAAM;CACtD;AACF;AAIA,SAAgB,aAAa,IAAc,GAAoB;CAG7D,MAAM,OAAO,GAAG,gBAAgB,EAAE,UAAU;EAAE,GAAG;EAAG,SAAS,GAAG;CAAY,IAAI;CAChF,MAAM,MAAM,aAAa,GAAG;CAE5B,OAAQ,IAAY,IAAI,IAAI;AAC9B"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SchemaIR } from "../../types.js";
|
|
2
|
+
import { ExtractorContext, ZodDef } from "../types.js";
|
|
3
|
+
//#region src/core/extract/extractors/custom.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Compile z.custom() and z.instanceof() as pure predicates. The predicate is
|
|
6
|
+
* enough for the total hot-path verdict; the original schema is retained so a
|
|
7
|
+
* failed parse can delegate issue construction to Zod on the cold path.
|
|
8
|
+
*/
|
|
9
|
+
declare function extractCustom(def: ZodDef, ctx: ExtractorContext): SchemaIR;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { extractCustom };
|
|
12
|
+
//# sourceMappingURL=custom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/custom.ts"],"mappings":";;;;;;;;iBASgB,cAAc,KAAK,QAAQ,KAAK,mBAAmB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { isReferenceablePredicate, tryCompileEffect } from "../effects.js";
|
|
2
|
+
//#region src/core/extract/extractors/custom.ts
|
|
3
|
+
/**
|
|
4
|
+
* Compile z.custom() and z.instanceof() as pure predicates. The predicate is
|
|
5
|
+
* enough for the total hot-path verdict; the original schema is retained so a
|
|
6
|
+
* failed parse can delegate issue construction to Zod on the cold path.
|
|
7
|
+
*/
|
|
8
|
+
function extractCustom(def, ctx) {
|
|
9
|
+
const refs = ctx.refs;
|
|
10
|
+
if (!refs || !isReferenceablePredicate(def.fn)) return ctx.fallback("custom");
|
|
11
|
+
const source = tryCompileEffect(def.fn);
|
|
12
|
+
let refIndex;
|
|
13
|
+
if (source === void 0) {
|
|
14
|
+
refIndex = refs.length;
|
|
15
|
+
refs.push({
|
|
16
|
+
schema: def.fn,
|
|
17
|
+
accessPath: `${ctx.path}._zod.def.fn`
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const schemaRefIndex = refs.length;
|
|
21
|
+
refs.push({
|
|
22
|
+
schema: ctx.schema,
|
|
23
|
+
accessPath: ctx.path
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
type: "custom",
|
|
27
|
+
...source === void 0 ? { refIndex } : { source },
|
|
28
|
+
schemaRefIndex,
|
|
29
|
+
abort: def.abort !== false
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { extractCustom };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=custom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.js","names":[],"sources":["../../../../src/core/extract/extractors/custom.ts"],"sourcesContent":["import type { CustomIR, SchemaIR } from \"../../types.js\";\nimport { isReferenceablePredicate, tryCompileEffect } from \"../effects.js\";\nimport type { ExtractorContext, ZodDef } from \"../types.js\";\n\n/**\n * Compile z.custom() and z.instanceof() as pure predicates. The predicate is\n * enough for the total hot-path verdict; the original schema is retained so a\n * failed parse can delegate issue construction to Zod on the cold path.\n */\nexport function extractCustom(def: ZodDef, ctx: ExtractorContext): SchemaIR {\n const refs = ctx.refs;\n if (!refs || !isReferenceablePredicate(def.fn)) return ctx.fallback(\"custom\");\n\n const source = tryCompileEffect(def.fn);\n let refIndex: number | undefined;\n if (source === undefined) {\n refIndex = refs.length;\n refs.push({ schema: def.fn, accessPath: `${ctx.path}._zod.def.fn` });\n }\n\n const schemaRefIndex = refs.length;\n refs.push({ schema: ctx.schema, accessPath: ctx.path });\n\n return {\n type: \"custom\",\n ...(source === undefined ? { refIndex: refIndex as number } : { source }),\n schemaRefIndex,\n abort: def.abort !== false,\n } satisfies CustomIR;\n}\n"],"mappings":";;;;;;;AASA,SAAgB,cAAc,KAAa,KAAiC;CAC1E,MAAM,OAAO,IAAI;CACjB,IAAI,CAAC,QAAQ,CAAC,yBAAyB,IAAI,EAAE,GAAG,OAAO,IAAI,SAAS,QAAQ;CAE5E,MAAM,SAAS,iBAAiB,IAAI,EAAE;CACtC,IAAI;CACJ,IAAI,WAAW,KAAA,GAAW;EACxB,WAAW,KAAK;EAChB,KAAK,KAAK;GAAE,QAAQ,IAAI;GAAI,YAAY,GAAG,IAAI,KAAK;EAAc,CAAC;CACrE;CAEA,MAAM,iBAAiB,KAAK;CAC5B,KAAK,KAAK;EAAE,QAAQ,IAAI;EAAQ,YAAY,IAAI;CAAK,CAAC;CAEtD,OAAO;EACL,MAAM;EACN,GAAI,WAAW,KAAA,IAAY,EAAY,SAAmB,IAAI,EAAE,OAAO;EACvE;EACA,OAAO,IAAI,UAAU;CACvB;AACF"}
|
|
@@ -3,6 +3,7 @@ import { Extractor, RecursionState, RefEntry } from "./types.js";
|
|
|
3
3
|
import { extractArray } from "./extractors/array.js";
|
|
4
4
|
import { extractBigint } from "./extractors/bigint.js";
|
|
5
5
|
import { extractCatch } from "./extractors/catch.js";
|
|
6
|
+
import { extractCustom } from "./extractors/custom.js";
|
|
6
7
|
import { extractDate } from "./extractors/date.js";
|
|
7
8
|
import { extractDefault } from "./extractors/default.js";
|
|
8
9
|
import { extractFile } from "./extractors/file.js";
|
|
@@ -34,6 +35,7 @@ declare const extractRegistry: {
|
|
|
34
35
|
nullable: Extractor;
|
|
35
36
|
readonly: Extractor;
|
|
36
37
|
intersection: Extractor;
|
|
38
|
+
custom: typeof extractCustom;
|
|
37
39
|
string: typeof extractString;
|
|
38
40
|
number: typeof extractNumber;
|
|
39
41
|
bigint: typeof extractBigint;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","names":[],"sources":["../../../src/core/extract/registry.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"registry.d.ts","names":[],"sources":["../../../src/core/extract/registry.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;cAsDa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0FG,SACd,oBACA,cACA,MAAM,wBACN,UAAU,cACV,WAAW,iBACV"}
|
|
@@ -4,6 +4,7 @@ import { extractArray } from "./extractors/array.js";
|
|
|
4
4
|
import { extractBigint } from "./extractors/bigint.js";
|
|
5
5
|
import { extractBoolean } from "./extractors/boolean.js";
|
|
6
6
|
import { extractCatch } from "./extractors/catch.js";
|
|
7
|
+
import { extractCustom } from "./extractors/custom.js";
|
|
7
8
|
import { extractDate } from "./extractors/date.js";
|
|
8
9
|
import { extractDefault } from "./extractors/default.js";
|
|
9
10
|
import { extractEnum } from "./extractors/enum.js";
|
|
@@ -49,6 +50,7 @@ const extractRegistry = {
|
|
|
49
50
|
nullable: extractNullable,
|
|
50
51
|
readonly: extractReadonly,
|
|
51
52
|
intersection: extractIntersection,
|
|
53
|
+
custom: extractCustom,
|
|
52
54
|
string: extractString,
|
|
53
55
|
number: extractNumber,
|
|
54
56
|
bigint: extractBigint,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","names":["schema"],"sources":["../../../src/core/extract/registry.ts"],"sourcesContent":["import type { FallbackIR, SchemaIR } from \"../types.js\";\nimport { resolveCheckMessage } from \"./checks.js\";\nimport { extractAny } from \"./extractors/any.js\";\nimport { extractArray } from \"./extractors/array.js\";\nimport { extractBigint } from \"./extractors/bigint.js\";\nimport { extractBoolean } from \"./extractors/boolean.js\";\nimport { extractCatch } from \"./extractors/catch.js\";\nimport { extractDate } from \"./extractors/date.js\";\nimport { extractDefault } from \"./extractors/default.js\";\nimport { extractEnum } from \"./extractors/enum.js\";\nimport { extractFile } from \"./extractors/file.js\";\nimport { extractIntersection } from \"./extractors/intersection.js\";\nimport { extractLazy } from \"./extractors/lazy.js\";\nimport { extractLiteral } from \"./extractors/literal.js\";\nimport { extractMap } from \"./extractors/map.js\";\nimport { extractNan } from \"./extractors/nan.js\";\nimport { extractNever } from \"./extractors/never.js\";\nimport { extractNull } from \"./extractors/null.js\";\nimport { extractNullable } from \"./extractors/nullable.js\";\nimport { extractNumber } from \"./extractors/number.js\";\nimport { extractObject } from \"./extractors/object.js\";\nimport { extractOptional } from \"./extractors/optional.js\";\nimport { extractPipe } from \"./extractors/pipe.js\";\nimport { extractReadonly } from \"./extractors/readonly.js\";\nimport { extractRecord } from \"./extractors/record.js\";\nimport { extractSet } from \"./extractors/set.js\";\nimport { extractString } from \"./extractors/string.js\";\nimport { extractSymbol } from \"./extractors/symbol.js\";\nimport { extractTemplateLiteral } from \"./extractors/template-literal.js\";\nimport { extractTuple } from \"./extractors/tuple.js\";\nimport { extractUndefined } from \"./extractors/undefined.js\";\nimport { extractUnion } from \"./extractors/union.js\";\nimport { extractUnknown } from \"./extractors/unknown.js\";\nimport { extractVoid } from \"./extractors/void.js\";\nimport { makeFallback } from \"./fallback.js\";\nimport type {\n Extractor,\n ExtractorContext,\n RecursionState,\n RefEntry,\n SupportedZodDefType,\n ZodSchema,\n} from \"./types.js\";\n\n// ─── Typed registry ─────────────────────────────────────────────────────────\n// Adding a new SupportedZodDefType without registering an extractor here causes\n// a compile error, preventing silent-missing-case bugs.\n//\n// Note: SupportedZodDefType covers Zod's def.type values, not SchemaIR types.\n// SchemaIR types like discriminatedUnion, recursiveRef, effect, and fallback are\n// produced by extractors (e.g. union emits discriminatedUnion, lazy emits\n// recursiveRef) but have no corresponding Zod def.type.\n\nexport const extractRegistry = {\n // Primitives (order follows SupportedZodDefType union in types.ts)\n boolean: extractBoolean,\n null: extractNull,\n undefined: extractUndefined,\n any: extractAny,\n unknown: extractUnknown,\n symbol: extractSymbol,\n void: extractVoid,\n nan: extractNan,\n never: extractNever,\n literal: extractLiteral,\n enum: extractEnum,\n optional: extractOptional,\n nullable: extractNullable,\n readonly: extractReadonly,\n intersection: extractIntersection,\n // Complex extractors\n string: extractString,\n number: extractNumber,\n bigint: extractBigint,\n date: extractDate,\n object: extractObject,\n array: extractArray,\n tuple: extractTuple,\n record: extractRecord,\n set: extractSet,\n map: extractMap,\n union: extractUnion,\n default: extractDefault,\n pipe: extractPipe,\n lazy: extractLazy,\n catch: extractCatch,\n template_literal: extractTemplateLiteral,\n file: extractFile,\n} satisfies Record<SupportedZodDefType, Extractor>;\n\n// ─── Factory ────────────────────────────────────────────────────────────────\n// Lives here (not in types.ts) to avoid circular imports:\n// visit() → dispatch() → imports from types.ts\n\nfunction createExtractorContext(\n schema: unknown,\n path: string,\n refs: RefEntry[] | undefined,\n visiting: Set<unknown>,\n recursion: RecursionState,\n): ExtractorContext {\n return {\n schema,\n path,\n refs,\n visiting,\n recursion,\n visit(childSchema: unknown, pathSuffix?: string): SchemaIR {\n const childPath = pathSuffix ? `${path}${pathSuffix}` : path;\n return dispatch(childSchema, childPath, refs, visiting, recursion);\n },\n fallback(reason: FallbackIR[\"reason\"]) {\n return makeFallback(reason, schema, refs, path);\n },\n };\n}\n\n// ─── Dispatch ───────────────────────────────────────────────────────────────\n\n/**\n * def.type values whose extractors consume `def.checks`. In Zod v4, `.refine()`\n * (and `.check()`) appends to the checks array of WHATEVER schema type it is\n * called on — z.string().nullable().refine(...) puts the check on the nullable.\n * For every other type, checks present on the def would be silently dropped by\n * extraction, so dispatch() forces a Zod fallback instead (validation hole\n * otherwise: compiled output accepts input Zod rejects).\n */\nconst CHECKS_AWARE_TYPES: ReadonlySet<string> = new Set([\n \"string\",\n \"number\",\n \"bigint\",\n \"date\",\n \"object\",\n \"array\",\n \"set\",\n \"file\",\n]);\n\n/**\n * Central dispatch — replaces extractSchemaInner().\n * visit() and dispatch() form a mutually recursive pair, both in this file.\n */\nexport function dispatch(\n zodSchema: unknown,\n path: string,\n refs: RefEntry[] | undefined,\n visiting: Set<unknown>,\n recursion: RecursionState,\n): SchemaIR {\n const schema = zodSchema as ZodSchema;\n const def = schema._zod.def;\n\n visiting.add(zodSchema);\n try {\n const extractor = extractRegistry[def.type as SupportedZodDefType];\n const ctx = createExtractorContext(zodSchema, path, refs, visiting, recursion);\n let ir = extractor ? extractor(def, ctx) : makeFallback(\"unsupported\", zodSchema, refs, path);\n if (ir.type === \"fallback\") return ir;\n\n // Safety net: checks attached to a type whose extractor ignores them.\n if (def.checks && def.checks.length > 0 && !CHECKS_AWARE_TYPES.has(def.type)) {\n return makeFallback(\"refine\", zodSchema, refs, path);\n }\n\n // Schema-level custom error (z.string({ error: \"...\" })): bake static\n // messages as the node's default issue message; dynamic error maps can't\n // be compiled — delegate to Zod so messages stay exact.\n if (def.error !== undefined) {\n const resolved = resolveCheckMessage(def.error);\n if (resolved.kind === \"dynamic\") {\n return makeFallback(\"refine\", zodSchema, refs, path);\n }\n if (resolved.kind === \"static\") {\n ir = { ...ir, typeMessage: resolved.message };\n }\n }\n\n // This schema is the target of a non-root recursive cycle (a `recursiveRef`\n // with refId ≥ 1 was emitted for it deeper in the walk): wrap its IR as the\n // OUTERMOST transformation so codegen hosts it as a standalone validator.\n // Done last — after the fallback post-processing above — so a target that\n // delegates to Zod returns its fallback leaf, discarding the whole subtree\n // (including the inner `recursiveRef`) instead of leaving a dangling ref.\n // The root target (refId 0) is the schema's own function and is never\n // wrapped.\n const refId = recursion.targets.get(zodSchema);\n if (refId !== undefined) {\n return { type: \"recursionTarget\", refId, inner: ir };\n }\n return ir;\n } finally {\n visiting.delete(zodSchema);\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"registry.js","names":["schema"],"sources":["../../../src/core/extract/registry.ts"],"sourcesContent":["import type { FallbackIR, SchemaIR } from \"../types.js\";\nimport { resolveCheckMessage } from \"./checks.js\";\nimport { extractAny } from \"./extractors/any.js\";\nimport { extractArray } from \"./extractors/array.js\";\nimport { extractBigint } from \"./extractors/bigint.js\";\nimport { extractBoolean } from \"./extractors/boolean.js\";\nimport { extractCatch } from \"./extractors/catch.js\";\nimport { extractCustom } from \"./extractors/custom.js\";\nimport { extractDate } from \"./extractors/date.js\";\nimport { extractDefault } from \"./extractors/default.js\";\nimport { extractEnum } from \"./extractors/enum.js\";\nimport { extractFile } from \"./extractors/file.js\";\nimport { extractIntersection } from \"./extractors/intersection.js\";\nimport { extractLazy } from \"./extractors/lazy.js\";\nimport { extractLiteral } from \"./extractors/literal.js\";\nimport { extractMap } from \"./extractors/map.js\";\nimport { extractNan } from \"./extractors/nan.js\";\nimport { extractNever } from \"./extractors/never.js\";\nimport { extractNull } from \"./extractors/null.js\";\nimport { extractNullable } from \"./extractors/nullable.js\";\nimport { extractNumber } from \"./extractors/number.js\";\nimport { extractObject } from \"./extractors/object.js\";\nimport { extractOptional } from \"./extractors/optional.js\";\nimport { extractPipe } from \"./extractors/pipe.js\";\nimport { extractReadonly } from \"./extractors/readonly.js\";\nimport { extractRecord } from \"./extractors/record.js\";\nimport { extractSet } from \"./extractors/set.js\";\nimport { extractString } from \"./extractors/string.js\";\nimport { extractSymbol } from \"./extractors/symbol.js\";\nimport { extractTemplateLiteral } from \"./extractors/template-literal.js\";\nimport { extractTuple } from \"./extractors/tuple.js\";\nimport { extractUndefined } from \"./extractors/undefined.js\";\nimport { extractUnion } from \"./extractors/union.js\";\nimport { extractUnknown } from \"./extractors/unknown.js\";\nimport { extractVoid } from \"./extractors/void.js\";\nimport { makeFallback } from \"./fallback.js\";\nimport type {\n Extractor,\n ExtractorContext,\n RecursionState,\n RefEntry,\n SupportedZodDefType,\n ZodSchema,\n} from \"./types.js\";\n\n// ─── Typed registry ─────────────────────────────────────────────────────────\n// Adding a new SupportedZodDefType without registering an extractor here causes\n// a compile error, preventing silent-missing-case bugs.\n//\n// Note: SupportedZodDefType covers Zod's def.type values, not SchemaIR types.\n// SchemaIR types like discriminatedUnion, recursiveRef, effect, and fallback are\n// produced by extractors (e.g. union emits discriminatedUnion, lazy emits\n// recursiveRef) but have no corresponding Zod def.type.\n\nexport const extractRegistry = {\n // Primitives (order follows SupportedZodDefType union in types.ts)\n boolean: extractBoolean,\n null: extractNull,\n undefined: extractUndefined,\n any: extractAny,\n unknown: extractUnknown,\n symbol: extractSymbol,\n void: extractVoid,\n nan: extractNan,\n never: extractNever,\n literal: extractLiteral,\n enum: extractEnum,\n optional: extractOptional,\n nullable: extractNullable,\n readonly: extractReadonly,\n intersection: extractIntersection,\n custom: extractCustom,\n // Complex extractors\n string: extractString,\n number: extractNumber,\n bigint: extractBigint,\n date: extractDate,\n object: extractObject,\n array: extractArray,\n tuple: extractTuple,\n record: extractRecord,\n set: extractSet,\n map: extractMap,\n union: extractUnion,\n default: extractDefault,\n pipe: extractPipe,\n lazy: extractLazy,\n catch: extractCatch,\n template_literal: extractTemplateLiteral,\n file: extractFile,\n} satisfies Record<SupportedZodDefType, Extractor>;\n\n// ─── Factory ────────────────────────────────────────────────────────────────\n// Lives here (not in types.ts) to avoid circular imports:\n// visit() → dispatch() → imports from types.ts\n\nfunction createExtractorContext(\n schema: unknown,\n path: string,\n refs: RefEntry[] | undefined,\n visiting: Set<unknown>,\n recursion: RecursionState,\n): ExtractorContext {\n return {\n schema,\n path,\n refs,\n visiting,\n recursion,\n visit(childSchema: unknown, pathSuffix?: string): SchemaIR {\n const childPath = pathSuffix ? `${path}${pathSuffix}` : path;\n return dispatch(childSchema, childPath, refs, visiting, recursion);\n },\n fallback(reason: FallbackIR[\"reason\"]) {\n return makeFallback(reason, schema, refs, path);\n },\n };\n}\n\n// ─── Dispatch ───────────────────────────────────────────────────────────────\n\n/**\n * def.type values whose extractors consume `def.checks`. In Zod v4, `.refine()`\n * (and `.check()`) appends to the checks array of WHATEVER schema type it is\n * called on — z.string().nullable().refine(...) puts the check on the nullable.\n * For every other type, checks present on the def would be silently dropped by\n * extraction, so dispatch() forces a Zod fallback instead (validation hole\n * otherwise: compiled output accepts input Zod rejects).\n */\nconst CHECKS_AWARE_TYPES: ReadonlySet<string> = new Set([\n \"string\",\n \"number\",\n \"bigint\",\n \"date\",\n \"object\",\n \"array\",\n \"set\",\n \"file\",\n]);\n\n/**\n * Central dispatch — replaces extractSchemaInner().\n * visit() and dispatch() form a mutually recursive pair, both in this file.\n */\nexport function dispatch(\n zodSchema: unknown,\n path: string,\n refs: RefEntry[] | undefined,\n visiting: Set<unknown>,\n recursion: RecursionState,\n): SchemaIR {\n const schema = zodSchema as ZodSchema;\n const def = schema._zod.def;\n\n visiting.add(zodSchema);\n try {\n const extractor = extractRegistry[def.type as SupportedZodDefType];\n const ctx = createExtractorContext(zodSchema, path, refs, visiting, recursion);\n let ir = extractor ? extractor(def, ctx) : makeFallback(\"unsupported\", zodSchema, refs, path);\n if (ir.type === \"fallback\") return ir;\n\n // Safety net: checks attached to a type whose extractor ignores them.\n if (def.checks && def.checks.length > 0 && !CHECKS_AWARE_TYPES.has(def.type)) {\n return makeFallback(\"refine\", zodSchema, refs, path);\n }\n\n // Schema-level custom error (z.string({ error: \"...\" })): bake static\n // messages as the node's default issue message; dynamic error maps can't\n // be compiled — delegate to Zod so messages stay exact.\n if (def.error !== undefined) {\n const resolved = resolveCheckMessage(def.error);\n if (resolved.kind === \"dynamic\") {\n return makeFallback(\"refine\", zodSchema, refs, path);\n }\n if (resolved.kind === \"static\") {\n ir = { ...ir, typeMessage: resolved.message };\n }\n }\n\n // This schema is the target of a non-root recursive cycle (a `recursiveRef`\n // with refId ≥ 1 was emitted for it deeper in the walk): wrap its IR as the\n // OUTERMOST transformation so codegen hosts it as a standalone validator.\n // Done last — after the fallback post-processing above — so a target that\n // delegates to Zod returns its fallback leaf, discarding the whole subtree\n // (including the inner `recursiveRef`) instead of leaving a dangling ref.\n // The root target (refId 0) is the schema's own function and is never\n // wrapped.\n const refId = recursion.targets.get(zodSchema);\n if (refId !== undefined) {\n return { type: \"recursionTarget\", refId, inner: ir };\n }\n return ir;\n } finally {\n visiting.delete(zodSchema);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,MAAa,kBAAkB;CAE7B,SAAS;CACT,MAAM;CACN,WAAW;CACX,KAAK;CACL,SAAS;CACT,QAAQ;CACR,MAAM;CACN,KAAK;CACL,OAAO;CACP,SAAS;CACT,MAAM;CACN,UAAU;CACV,UAAU;CACV,UAAU;CACV,cAAc;CACd,QAAQ;CAER,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,MAAM;CACN,QAAQ;CACR,OAAO;CACP,OAAO;CACP,QAAQ;CACR,KAAK;CACL,KAAK;CACL,OAAO;CACP,SAAS;CACT,MAAM;CACN,MAAM;CACN,OAAO;CACP,kBAAkB;CAClB,MAAM;AACR;AAMA,SAAS,uBACP,QACA,MACA,MACA,UACA,WACkB;CAClB,OAAO;EACL;EACA;EACA;EACA;EACA;EACA,MAAM,aAAsB,YAA+B;GAEzD,OAAO,SAAS,aADE,aAAa,GAAG,OAAO,eAAe,MAChB,MAAM,UAAU,SAAS;EACnE;EACA,SAAS,QAA8B;GACrC,OAAO,aAAa,QAAQ,QAAQ,MAAM,IAAI;EAChD;CACF;AACF;;;;;;;;;AAYA,MAAM,qCAA0C,IAAI,IAAI;CACtD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;AAMD,SAAgB,SACd,WACA,MACA,MACA,UACA,WACU;CAEV,MAAM,MAAMA,UAAO,KAAK;CAExB,SAAS,IAAI,SAAS;CACtB,IAAI;EACF,MAAM,YAAY,gBAAgB,IAAI;EACtC,MAAM,MAAM,uBAAuB,WAAW,MAAM,MAAM,UAAU,SAAS;EAC7E,IAAI,KAAK,YAAY,UAAU,KAAK,GAAG,IAAI,aAAa,eAAe,WAAW,MAAM,IAAI;EAC5F,IAAI,GAAG,SAAS,YAAY,OAAO;EAGnC,IAAI,IAAI,UAAU,IAAI,OAAO,SAAS,KAAK,CAAC,mBAAmB,IAAI,IAAI,IAAI,GACzE,OAAO,aAAa,UAAU,WAAW,MAAM,IAAI;EAMrD,IAAI,IAAI,UAAU,KAAA,GAAW;GAC3B,MAAM,WAAW,oBAAoB,IAAI,KAAK;GAC9C,IAAI,SAAS,SAAS,WACpB,OAAO,aAAa,UAAU,WAAW,MAAM,IAAI;GAErD,IAAI,SAAS,SAAS,UACpB,KAAK;IAAE,GAAG;IAAI,aAAa,SAAS;GAAQ;EAEhD;EAUA,MAAM,QAAQ,UAAU,QAAQ,IAAI,SAAS;EAC7C,IAAI,UAAU,KAAA,GACZ,OAAO;GAAE,MAAM;GAAmB;GAAO,OAAO;EAAG;EAErD,OAAO;CACT,UAAU;EACR,SAAS,OAAO,SAAS;CAC3B;AACF"}
|
|
@@ -44,6 +44,12 @@ interface ZodCheckSchema {
|
|
|
44
44
|
}
|
|
45
45
|
interface ZodDef {
|
|
46
46
|
type: string;
|
|
47
|
+
/** Predicate function on z.custom() / z.instanceof(). */
|
|
48
|
+
fn?: unknown;
|
|
49
|
+
/** Whether a failing custom schema aborts its containing union option. */
|
|
50
|
+
abort?: boolean;
|
|
51
|
+
/** Custom issue path suffix on z.custom(). */
|
|
52
|
+
path?: unknown;
|
|
47
53
|
checks: ZodCheckSchema[];
|
|
48
54
|
check: string;
|
|
49
55
|
format: string;
|
|
@@ -103,7 +109,7 @@ interface RefEntry {
|
|
|
103
109
|
accessPath: string;
|
|
104
110
|
}
|
|
105
111
|
/** All Zod v4 def.type values that zod-compiler supports. */
|
|
106
|
-
type SupportedZodDefType = "boolean" | "null" | "undefined" | "any" | "unknown" | "symbol" | "void" | "nan" | "never" | "literal" | "enum" | "optional" | "nullable" | "readonly" | "intersection" | "string" | "number" | "bigint" | "date" | "object" | "array" | "tuple" | "record" | "set" | "map" | "union" | "default" | "pipe" | "lazy" | "catch" | "template_literal" | "file";
|
|
112
|
+
type SupportedZodDefType = "boolean" | "null" | "undefined" | "any" | "unknown" | "symbol" | "void" | "nan" | "never" | "literal" | "enum" | "optional" | "nullable" | "readonly" | "intersection" | "custom" | "string" | "number" | "bigint" | "date" | "object" | "array" | "tuple" | "record" | "set" | "map" | "union" | "default" | "pipe" | "lazy" | "catch" | "template_literal" | "file";
|
|
107
113
|
/**
|
|
108
114
|
* Shared, mutable recursion-target bookkeeping for one {@link extractSchema}
|
|
109
115
|
* call. Lazy cycle detection assigns each NON-root recursion target a stable
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/core/extract/types.ts"],"mappings":";;UAAiB;EACf;EACA;EACA;EACA,SAAS;EACT;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA;;EAEA;;EAEA;;EAEA;EACA;;EAEA,WAAW;EACX,WAAW;EACX;EACA;;UAGe;EACf;IACE,KAAK;;;;;;IAML;;;UAIa;EACf;EACA,QAAQ;EACR;EACA;EACA,SAAS;;EAET;;EAEA,WAAW;EACX,OAAO,eAAe;EACtB,SAAS;EACT,SAAS;EACT,WAAW;EACX;EACA,SAAS;EACT,IAAI;EACJ,KAAK;EACL,OAAO;EACP,MAAM;EACN,SAAS;EACT,WAAW;EACX,MAAM;EACN,OAAO;EACP;;EAEA;EACA;EACA;EACA,cAAc;;EAEd;;EAEA;;UAGe;EACf;IACE,KAAK;IACL,MAAM;;IAEN,YAAY;;IAEZ,UAAU;;IAEV,SAAS;;IAET,SAAS;;IAET;;IAEA,aAAa,eAAe;;;;UAKf;;EAEf;;EAEA;;;KAMU;;;;;;;;
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/core/extract/types.ts"],"mappings":";;UAAiB;EACf;EACA;EACA;EACA,SAAS;EACT;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA;;EAEA;;EAEA;;EAEA;EACA;;EAEA,WAAW;EACX,WAAW;EACX;EACA;;UAGe;EACf;IACE,KAAK;;;;;;IAML;;;UAIa;EACf;;EAEA;;EAEA;;EAEA;EACA,QAAQ;EACR;EACA;EACA,SAAS;;EAET;;EAEA,WAAW;EACX,OAAO,eAAe;EACtB,SAAS;EACT,SAAS;EACT,WAAW;EACX;EACA,SAAS;EACT,IAAI;EACJ,KAAK;EACL,OAAO;EACP,MAAM;EACN,SAAS;EACT,WAAW;EACX,MAAM;EACN,OAAO;EACP;;EAEA;EACA;EACA;EACA,cAAc;;EAEd;;EAEA;;UAGe;EACf;IACE,KAAK;IACL,MAAM;;IAEN,YAAY;;IAEZ,UAAU;;IAEV,SAAS;;IAET,SAAS;;IAET;;IAEA,aAAa,eAAe;;;;UAKf;;EAEf;;EAEA;;;KAMU;;;;;;;;UA4CK;;WAEN;;WAEA,SAAS;;EAElB;;;UAMe;;WAEN;;WAEA;;WAEA,MAAM;;WAEN,UAAU;;WAEV,WAAW;;EAGpB,MAAM,sBAAsB,sBAHR;;EAMpB,SAAS,QAH+D,uBAGjC;;;KAI7B,aAAa,KAAK,QAAQ,KAAK,qBAAA"}
|
package/dist/core/iife.d.ts
CHANGED
|
@@ -2,10 +2,9 @@ import { CompiledSchemaInfo } from "./pipeline.js";
|
|
|
2
2
|
//#region src/core/iife.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Import statement required by generateIIFE output (references
|
|
5
|
-
* __zodCompilerConfig). `core` is bound for $ZodAsyncError:
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* parse raises (see ZC_SR_DECL).
|
|
5
|
+
* __zodCompilerConfig). `core` is bound for $ZodAsyncError: superRefine and
|
|
6
|
+
* custom callbacks may only reveal that they are async through the promise
|
|
7
|
+
* they return, at which point zod's own synchronous parse raises.
|
|
9
8
|
*/
|
|
10
9
|
declare const ZOD_CONFIG_IMPORT = "import { config as __zodCompilerConfig, core as __zcCore, ZodRealError as __zcZodError } from \"zod\";";
|
|
11
10
|
/**
|
package/dist/core/iife.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iife.d.ts","names":[],"sources":["../../src/core/iife.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"iife.d.ts","names":[],"sources":["../../src/core/iife.ts"],"mappings":";;;;;;;;cAaa;;;;;;;;;;;;;;;;;;;;cAsBA;;;;;;;;;;;;;;;;;;;;;;;;;;;cAiCA;;;cAUA;;;;;;;;;;;;;;;;;;;;;;;cAyBA;;;;;;;;;;;;;;;;;;;;;;;cAwBA;;cAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAyDA;;;;;;;;;iBAsBG,aACd,oBACA,QAAQ,oBACR;EAAY"}
|
package/dist/core/iife.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
//#region src/core/iife.ts
|
|
2
2
|
/**
|
|
3
3
|
* Import statement required by generateIIFE output (references
|
|
4
|
-
* __zodCompilerConfig). `core` is bound for $ZodAsyncError:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* parse raises (see ZC_SR_DECL).
|
|
4
|
+
* __zodCompilerConfig). `core` is bound for $ZodAsyncError: superRefine and
|
|
5
|
+
* custom callbacks may only reveal that they are async through the promise
|
|
6
|
+
* they return, at which point zod's own synchronous parse raises.
|
|
8
7
|
*/
|
|
9
8
|
const ZOD_CONFIG_IMPORT = "import { config as __zodCompilerConfig, core as __zcCore, ZodRealError as __zcZodError } from \"zod\";";
|
|
10
9
|
/**
|
package/dist/core/iife.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iife.js","names":[],"sources":["../../src/core/iife.ts"],"sourcesContent":["/**\n * Shared CompiledSchema<T> IIFE generation.\n * Used by both CLI emitter and unplugin transform.\n */\n\nimport type { CompiledSchemaInfo } from \"./pipeline.js\";\n\n/**\n * Import statement required by generateIIFE output (references\n * __zodCompilerConfig). `core` is bound for $ZodAsyncError: a superRefine\n * callback is reached through zod's wrapper, so an async one is only\n * detectable by the promise it returns, at which point zod's own synchronous\n * parse raises (see ZC_SR_DECL).\n */\nexport const ZOD_CONFIG_IMPORT =\n 'import { config as __zodCompilerConfig, core as __zcCore, ZodRealError as __zcZodError } from \"zod\";';\n\n/**\n * File-level `__zcMsg` declaration (must appear once after ZOD_CONFIG_IMPORT):\n * the message an issue gets when nothing was baked into it at build time.\n *\n * Resolves zod's tail of `finalizeIssue` — `config.customError` then\n * `config.localeError` then \"Invalid input\" — and does it PER CALL, because the\n * config is mutable: `z.config({ localeError })` in an entry point runs after\n * the schema modules it imports, so a value snapshotted at module init misses\n * it. Reading a captured `localeError` alone also dropped `customError`\n * outright, silently ignoring the global map most i18n setups install.\n *\n * The head of zod's chain — the schema's own `error` option — is baked into the\n * issue at build time and short-circuits this. The one link that cannot be\n * reproduced is a per-CALL `ctx.error`, which would have to travel through\n * `safeParse`; that entry point sits at V8's inlining budget, where even an\n * unused extra parameter measured ~12% on every parse.\n *\n * Only ever called while building an error, never on a successful parse.\n */\nexport const ZOD_MSG_DECLARATION =\n 'function __zcUw(m){return typeof m===\"string\"?m:(m===undefined||m===null?undefined:m.message);}' +\n \"var __zcMsg=function(iss){var c=__zodCompilerConfig(),m;\" +\n \"if(c.customError){m=__zcUw(c.customError(iss));if(m!==undefined&&m!==null)return m;}\" +\n \"if(c.localeError){m=__zcUw(c.localeError(iss));if(m!==undefined&&m!==null)return m;}\" +\n 'return \"Invalid input\";};';\n\n/**\n * Shared failure-result for __zcFin / __zcFinD. Inline mode (CLI emitter)\n * declares it once per compiled file; lean mode (all unplugin bundlers) declares\n * it once per bundle in the plugin-materialized runtime module (module-local —\n * generated code only ever references __zcFin/__zcFinD, never __ZcFail).\n *\n * Why a prototype getter and not `{success:false, get error(){...}}`: an object\n * literal with an inline accessor forces V8 down its slow accessor-defining\n * allocation path — ~110ns per failure, measured — which dominates the entire\n * invalid-input cost whenever callers never read `.error`. Hosting `error` on\n * the prototype turns each failure into a plain field-only instantiation (~2ns,\n * ~13x), with the lazy-cache semantics intact. (Trade-off: `error` is a\n * prototype accessor, so it no longer shows up in `Object.keys(result)` / spread\n * / JSON.stringify of the result wrapper — `.success`/`.error`/`.data` access,\n * destructuring, and `in` are unaffected.)\n *\n * One class serves both finalizers, so the instances share one hidden class:\n * __zcFin passes pre-collected issues in `_e` (with `_f===null`); __zcFinD\n * passes the hosted slow-walk in `_f` plus the input in `_i`, and the getter\n * runs the walk on first `.error` read. The whole finalization — locale fill\n * (__zcMsg applied ONLY when an issue carries no message, never overwriting a\n * baked-in custom/fallback message), input strip, and ZodError construction\n * (zod v4 JSON.stringifies every issue into `message` and captures a stack\n * trace) — stays deferred inside the cached accessor exactly as before, since\n * the issues array is observable solely through `.error`.\n */\nexport const FAIL_CLASS_DECL =\n \"function __ZcFail(e,f,i){this.success=false;this._e=e;this._f=f;this._i=i;this._c=undefined;}\" +\n 'Object.defineProperty(__ZcFail.prototype,\"error\",{configurable:true,get:function(){' +\n \"if(this._c)return this._c;\" +\n \"var e=this._f!==null?this._f(this._i):this._e;\" +\n 'for(var i=0;i<e.length;i++){if(e[i].message===undefined&&typeof __zcMsg===\"function\")e[i].message=__zcMsg(e[i]);e[i].input=undefined;}' +\n \"return this._c=new __zcZodError(e);}});\";\n\n/** Eager finalizer (mutation / partial-fast-path schemas): issues already\n * collected in `e`; success short-circuits to a plain result literal. */\nexport const FIN_DECL =\n \"function __zcFin(e,d){if(!e.length)return{success:true,data:d};return new __ZcFail(e,null,null);}\";\n\n/**\n * Deferred-collection finalizer for Fast-Path-eligible schemas. When the\n * fast check fails, the ENTIRE slow path (the issue-collecting re-walk) is\n * pushed into the cached `.error` accessor instead of running eagerly:\n * fast-eligible schemas never mutate, so the walk's only output is the\n * issues array, which is observable solely through `.error` — one step\n * further along the same lazy boundary `__zcFin` already established (locale\n * fill, input strip, ZodError construction). A failed safeParse whose\n * `.error` is never read costs the fast check alone.\n *\n * Takes the schema's HOSTED slow-walk function plus the input — NOT a\n * per-call closure: `__zcFinD(__sw_N, input)` allocates only the result\n * object, where `__zcFinD(function(){...})` paid a closure environment and\n * function object per failure. Hosting the walk also shrinks safeParse to\n * two statements, within V8's inlining budget (the success-path result\n * literal becomes escape-analyzable at monomorphic call sites).\n *\n * The walk re-reads `input` at `.error`-read time; a caller that mutates\n * the input between safeParse and reading `.error` sees issues for the\n * mutated value (zod materializes at parse time). Same caveat class as the\n * documented __zcFin deferral.\n */\nexport const FIN_DEFERRED_DECL = \"function __zcFinD(f,inp){return new __ZcFail(null,f,inp);}\";\n\n/**\n * Compact-mode failure class — a lazy failure that delegates error reporting to\n * the ORIGINAL Zod schema's `safeParse`. Used by `output: \"compact\"`, where the\n * compiled slow walk is dropped entirely: a mutation-free schema's fast check\n * is the only generated validation, and on a fast-check failure the cold error\n * path is produced by the retained Zod schema itself (`zod` is the source of\n * truth, so the issues are byte-identical — no second validation engine).\n *\n * `_z` is the schema's PRISTINE bound safeParse (`__rf[N].safeParse.bind(...)`,\n * captured pre-`__zcMkv` by emitRfDelegate — see context.ts — so it is zod's\n * own implementation, never the compiled delegate, avoiding infinite\n * recursion). The zod parse is deferred until `.error` is read and cached, so\n * the common `safeParse(x).success`/`.is(x)` checks on invalid input cost only\n * the fast check (zod never runs) — the same deferral boundary `__zcFinD`\n * establishes for the compiled slow walk. Sound because compact mode is gated\n * on a TOTAL fast path: `fc(input) === false` ⟹ zod rejects, so `success:false`\n * holds without consulting zod.\n *\n * The getter returns zod's OWN ZodError verbatim (no locale fill / input strip /\n * re-wrap — zod already finalized it), so a delegated failure is exactly what\n * the unaltered schema would have produced.\n */\nexport const FAILZ_CLASS_DECL =\n \"function __ZcFailZ(z,i){this.success=false;this._z=z;this._i=i;this._c=undefined;}\" +\n 'Object.defineProperty(__ZcFailZ.prototype,\"error\",{configurable:true,get:function(){' +\n \"return this._c||(this._c=this._z(this._i).error);}});\";\n\n/** Compact-mode finalizer: wrap a pristine bound safeParse + input into a lazy delegated failure. */\nexport const FINZ_DECL = \"function __zcFinZ(z,i){return new __ZcFailZ(z,i);}\";\n\n/**\n * Validator factory. Inline mode (CLI emitter) declares it once per compiled\n * file; lean mode (all unplugin bundlers) exports it once per bundle from the\n * plugin-materialized runtime module — generated code never imports it from\n * the zod-compiler package itself, so zod-compiler stays a devDependency and\n * the helper set is always version-locked to the codegen that calls it.\n * Wraps a safeParse function into the CompiledSchema interface.\n *\n * IDENTITY-PRESERVING: with zodCompat (schema != null) the compiled\n * parse/safeParse/parseAsync/safeParseAsync are installed as OWN properties\n * on the original schema object, which is returned as-is. zod v4 keys\n * several APIs on object identity — toJSONSchema's ctx.seen registers the\n * object it is handed while each processor closure captures the original\n * inst (a wrapper crashes `optionalProcessor` with \"Cannot set properties\n * of undefined (setting 'ref')\" the moment a compiled schema is composed\n * into another schema), and globalRegistry/.meta() is a WeakMap keyed by\n * the schema instance (a wrapper silently loses OpenAPI titles/ids). An\n * Object.create wrapper breaks both; mutating the original breaks neither\n * (zod's internal parsing flows through _zod.run, never the public\n * methods, and derived schemas — .optional(), .extend() — are fresh\n * instances that fall back to plain zod). schema=null (zodCompat: false)\n * still produces a plain method-bag object.\n *\n * fc is the schema's hosted fast-check boolean function (null when no Fast\n * Path exists). parse()/parseAsync() try it first and return the input\n * directly on success: fc is small enough for V8 to inline, so the hot parse\n * path runs with zero allocations — calling fn would allocate an intermediate\n * SafeParseResult that escape analysis cannot remove (fn never inlines).\n * Fast-path-eligible schemas never mutate, so fc(input) ⟹ data === input.\n *\n * is is the TOTAL fast-check predicate (fc when the fast path is total, else\n * null). Installed as `.is()` — a zero-allocation boolean type guard. When\n * null (partial fast path or none) `.is()` derives from fn(input).success: a\n * partial fc can pass-through valid input but its `false` does not imply\n * rejection (a default/catch may still succeed), so it would be unsound as a\n * standalone guard.\n *\n * `~standard` is REPLACED, not merely preserved. Zod builds it lazily as\n * `validate: (v) => safeParse(inst, v)` — the core FUNCTION, which goes straight\n * to `inst._zod.run`. It never reads the schema's own `safeParse` property, so\n * installing the compiled one leaves this route entirely uncompiled: measured\n * 271.7 ns against the compiled 26.6 ns on the same schema, i.e. Standard Schema\n * consumers (tRPC, Hono, TanStack) were getting plain Zod.\n *\n * Zod's own validate is captured first and kept as the throw path: it catches a\n * synchronous throw and retries through `safeParseAsync`, which is how an async\n * refinement resolves and how a throwing check surfaces as a rejected promise\n * rather than a synchronous throw. The compiled validator cannot reproduce that\n * (async schemas delegate to Zod anyway), so deferring to the original is both\n * simpler and exact.\n *\n * Installed with defineProperty rather than assignment: Zod's lazy setter\n * redefines the slot as non-writable, so a second `__zcMkv` on the same schema\n * object — two exports aliasing one schema — would throw under ESM strict mode.\n */\nexport const MK_VALIDATOR_DECL =\n \"function __zcMkv(fn,schema,fc,is){var w=schema||{};w.parse=fc?function(input){if(fc(input))return input;var r=fn(input);if(r.success)return r.data;throw r.error;}:function(input){var r=fn(input);if(r.success)return r.data;throw r.error;};w.safeParse=fn;w.safeParseAsync=function(input){return Promise.resolve(fn(input));};w.parseAsync=fc?function(input){if(fc(input))return Promise.resolve(input);var r=fn(input);if(r.success)return Promise.resolve(r.data);return Promise.reject(r.error);}:function(input){var r=fn(input);if(r.success)return Promise.resolve(r.data);return Promise.reject(r.error);};w.is=is||function(input){return fn(input).success;};\" +\n 'var s=w[\"~standard\"],zv=s&&s.validate;' +\n 'Object.defineProperty(w,\"~standard\",{configurable:true,value:{version:1,vendor:(s&&s.vendor)||\"zod\",validate:function(input){var r;try{if(fc&&fc(input))return{value:input};r=fn(input);}catch(e){if(zv)return zv(input);throw e;}return r.success?{value:r.data}:{issues:r.error.issues};}}});' +\n \"return w;}\";\n\nfunction extractFunctionName(functionDef: string): string {\n const match = /^function\\s+(\\w+)\\s*\\(/.exec(functionDef);\n if (!match?.[1]) {\n throw new Error(\"Cannot extract function name from generated code\");\n }\n return match[1];\n}\n\n/**\n * Generate a `/* @__PURE__ * /` IIFE wrapping a compiled validator.\n *\n * @param schemaExpr - Expression resolving to the original Zod schema\n * (e.g. `\"UserSchema\"` in unplugin, `\"(__src_X as any).schema\"` in CLI)\n * @param schema\n * @param options\n */\nexport function generateIIFE(\n schemaExpr: string,\n schema: CompiledSchemaInfo,\n options?: { zodCompat?: boolean | undefined },\n): string {\n const { codegenResult, refEntries } = schema;\n const fnName = extractFunctionName(codegenResult.functionDef);\n const zodCompat = options?.zodCompat !== false;\n const schemaArg = zodCompat ? schemaExpr : \"null\";\n const fcArg = codegenResult.fastFnName ?? \"null\";\n // `.is()` gets the fast-check directly only when it is a total predicate;\n // partial/none falls back to safeParse().success inside __zcMkv. A rebuilding\n // schema has no by-reference `fc` but still names its predicate separately.\n const isArg = codegenResult.isFnName ?? (codegenResult.fastTotal ? fcArg : \"null\");\n\n return [\n \"/* @__PURE__ */ (() => {\",\n ...(refEntries.length > 0\n ? [`var __rf=[${refEntries.map((fb) => `${schemaExpr}${fb.accessPath}`).join(\",\")}];`]\n : []),\n ...codegenResult.code\n .split(\"\\n\")\n .filter((l) => l.trim() !== \"\" && l.trim() !== \"/* zod-compiler */\"),\n codegenResult.functionDef,\n `return __zcMkv(${fnName},${schemaArg},${fcArg},${isArg});`,\n \"})()\",\n ].join(\"\\n\");\n}\n"],"mappings":";;;;;;;;AAcA,MAAa,oBACX;;;;;;;;;;;;;;;;;;;;AAqBF,MAAa,sBACX;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCF,MAAa,kBACX;;;AASF,MAAa,WACX;;;;;;;;;;;;;;;;;;;;;;;AAwBF,MAAa,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;AAwBjC,MAAa,mBACX;;AAKF,MAAa,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDzB,MAAa,oBACX;AAKF,SAAS,oBAAoB,aAA6B;CACxD,MAAM,QAAQ,yBAAyB,KAAK,WAAW;CACvD,IAAI,CAAC,QAAQ,IACX,MAAM,IAAI,MAAM,kDAAkD;CAEpE,OAAO,MAAM;AACf;;;;;;;;;AAUA,SAAgB,aACd,YACA,QACA,SACQ;CACR,MAAM,EAAE,eAAe,eAAe;CACtC,MAAM,SAAS,oBAAoB,cAAc,WAAW;CAE5D,MAAM,YADY,SAAS,cAAc,QACX,aAAa;CAC3C,MAAM,QAAQ,cAAc,cAAc;CAI1C,MAAM,QAAQ,cAAc,aAAa,cAAc,YAAY,QAAQ;CAE3E,OAAO;EACL;EACA,GAAI,WAAW,SAAS,IACpB,CAAC,aAAa,WAAW,KAAK,OAAO,GAAG,aAAa,GAAG,YAAY,CAAC,CAAC,KAAK,GAAG,EAAE,GAAG,IACnF,CAAC;EACL,GAAG,cAAc,KACd,MAAM,IAAI,CAAC,CACX,QAAQ,MAAM,EAAE,KAAK,MAAM,MAAM,EAAE,KAAK,MAAM,oBAAoB;EACrE,cAAc;EACd,kBAAkB,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM;EACxD;CACF,CAAC,CAAC,KAAK,IAAI;AACb"}
|
|
1
|
+
{"version":3,"file":"iife.js","names":[],"sources":["../../src/core/iife.ts"],"sourcesContent":["/**\n * Shared CompiledSchema<T> IIFE generation.\n * Used by both CLI emitter and unplugin transform.\n */\n\nimport type { CompiledSchemaInfo } from \"./pipeline.js\";\n\n/**\n * Import statement required by generateIIFE output (references\n * __zodCompilerConfig). `core` is bound for $ZodAsyncError: superRefine and\n * custom callbacks may only reveal that they are async through the promise\n * they return, at which point zod's own synchronous parse raises.\n */\nexport const ZOD_CONFIG_IMPORT =\n 'import { config as __zodCompilerConfig, core as __zcCore, ZodRealError as __zcZodError } from \"zod\";';\n\n/**\n * File-level `__zcMsg` declaration (must appear once after ZOD_CONFIG_IMPORT):\n * the message an issue gets when nothing was baked into it at build time.\n *\n * Resolves zod's tail of `finalizeIssue` — `config.customError` then\n * `config.localeError` then \"Invalid input\" — and does it PER CALL, because the\n * config is mutable: `z.config({ localeError })` in an entry point runs after\n * the schema modules it imports, so a value snapshotted at module init misses\n * it. Reading a captured `localeError` alone also dropped `customError`\n * outright, silently ignoring the global map most i18n setups install.\n *\n * The head of zod's chain — the schema's own `error` option — is baked into the\n * issue at build time and short-circuits this. The one link that cannot be\n * reproduced is a per-CALL `ctx.error`, which would have to travel through\n * `safeParse`; that entry point sits at V8's inlining budget, where even an\n * unused extra parameter measured ~12% on every parse.\n *\n * Only ever called while building an error, never on a successful parse.\n */\nexport const ZOD_MSG_DECLARATION =\n 'function __zcUw(m){return typeof m===\"string\"?m:(m===undefined||m===null?undefined:m.message);}' +\n \"var __zcMsg=function(iss){var c=__zodCompilerConfig(),m;\" +\n \"if(c.customError){m=__zcUw(c.customError(iss));if(m!==undefined&&m!==null)return m;}\" +\n \"if(c.localeError){m=__zcUw(c.localeError(iss));if(m!==undefined&&m!==null)return m;}\" +\n 'return \"Invalid input\";};';\n\n/**\n * Shared failure-result for __zcFin / __zcFinD. Inline mode (CLI emitter)\n * declares it once per compiled file; lean mode (all unplugin bundlers) declares\n * it once per bundle in the plugin-materialized runtime module (module-local —\n * generated code only ever references __zcFin/__zcFinD, never __ZcFail).\n *\n * Why a prototype getter and not `{success:false, get error(){...}}`: an object\n * literal with an inline accessor forces V8 down its slow accessor-defining\n * allocation path — ~110ns per failure, measured — which dominates the entire\n * invalid-input cost whenever callers never read `.error`. Hosting `error` on\n * the prototype turns each failure into a plain field-only instantiation (~2ns,\n * ~13x), with the lazy-cache semantics intact. (Trade-off: `error` is a\n * prototype accessor, so it no longer shows up in `Object.keys(result)` / spread\n * / JSON.stringify of the result wrapper — `.success`/`.error`/`.data` access,\n * destructuring, and `in` are unaffected.)\n *\n * One class serves both finalizers, so the instances share one hidden class:\n * __zcFin passes pre-collected issues in `_e` (with `_f===null`); __zcFinD\n * passes the hosted slow-walk in `_f` plus the input in `_i`, and the getter\n * runs the walk on first `.error` read. The whole finalization — locale fill\n * (__zcMsg applied ONLY when an issue carries no message, never overwriting a\n * baked-in custom/fallback message), input strip, and ZodError construction\n * (zod v4 JSON.stringifies every issue into `message` and captures a stack\n * trace) — stays deferred inside the cached accessor exactly as before, since\n * the issues array is observable solely through `.error`.\n */\nexport const FAIL_CLASS_DECL =\n \"function __ZcFail(e,f,i){this.success=false;this._e=e;this._f=f;this._i=i;this._c=undefined;}\" +\n 'Object.defineProperty(__ZcFail.prototype,\"error\",{configurable:true,get:function(){' +\n \"if(this._c)return this._c;\" +\n \"var e=this._f!==null?this._f(this._i):this._e;\" +\n 'for(var i=0;i<e.length;i++){if(e[i].message===undefined&&typeof __zcMsg===\"function\")e[i].message=__zcMsg(e[i]);e[i].input=undefined;}' +\n \"return this._c=new __zcZodError(e);}});\";\n\n/** Eager finalizer (mutation / partial-fast-path schemas): issues already\n * collected in `e`; success short-circuits to a plain result literal. */\nexport const FIN_DECL =\n \"function __zcFin(e,d){if(!e.length)return{success:true,data:d};return new __ZcFail(e,null,null);}\";\n\n/**\n * Deferred-collection finalizer for Fast-Path-eligible schemas. When the\n * fast check fails, the ENTIRE slow path (the issue-collecting re-walk) is\n * pushed into the cached `.error` accessor instead of running eagerly:\n * fast-eligible schemas never mutate, so the walk's only output is the\n * issues array, which is observable solely through `.error` — one step\n * further along the same lazy boundary `__zcFin` already established (locale\n * fill, input strip, ZodError construction). A failed safeParse whose\n * `.error` is never read costs the fast check alone.\n *\n * Takes the schema's HOSTED slow-walk function plus the input — NOT a\n * per-call closure: `__zcFinD(__sw_N, input)` allocates only the result\n * object, where `__zcFinD(function(){...})` paid a closure environment and\n * function object per failure. Hosting the walk also shrinks safeParse to\n * two statements, within V8's inlining budget (the success-path result\n * literal becomes escape-analyzable at monomorphic call sites).\n *\n * The walk re-reads `input` at `.error`-read time; a caller that mutates\n * the input between safeParse and reading `.error` sees issues for the\n * mutated value (zod materializes at parse time). Same caveat class as the\n * documented __zcFin deferral.\n */\nexport const FIN_DEFERRED_DECL = \"function __zcFinD(f,inp){return new __ZcFail(null,f,inp);}\";\n\n/**\n * Compact-mode failure class — a lazy failure that delegates error reporting to\n * the ORIGINAL Zod schema's `safeParse`. Used by `output: \"compact\"`, where the\n * compiled slow walk is dropped entirely: a mutation-free schema's fast check\n * is the only generated validation, and on a fast-check failure the cold error\n * path is produced by the retained Zod schema itself (`zod` is the source of\n * truth, so the issues are byte-identical — no second validation engine).\n *\n * `_z` is the schema's PRISTINE bound safeParse (`__rf[N].safeParse.bind(...)`,\n * captured pre-`__zcMkv` by emitRfDelegate — see context.ts — so it is zod's\n * own implementation, never the compiled delegate, avoiding infinite\n * recursion). The zod parse is deferred until `.error` is read and cached, so\n * the common `safeParse(x).success`/`.is(x)` checks on invalid input cost only\n * the fast check (zod never runs) — the same deferral boundary `__zcFinD`\n * establishes for the compiled slow walk. Sound because compact mode is gated\n * on a TOTAL fast path: `fc(input) === false` ⟹ zod rejects, so `success:false`\n * holds without consulting zod.\n *\n * The getter returns zod's OWN ZodError verbatim (no locale fill / input strip /\n * re-wrap — zod already finalized it), so a delegated failure is exactly what\n * the unaltered schema would have produced.\n */\nexport const FAILZ_CLASS_DECL =\n \"function __ZcFailZ(z,i){this.success=false;this._z=z;this._i=i;this._c=undefined;}\" +\n 'Object.defineProperty(__ZcFailZ.prototype,\"error\",{configurable:true,get:function(){' +\n \"return this._c||(this._c=this._z(this._i).error);}});\";\n\n/** Compact-mode finalizer: wrap a pristine bound safeParse + input into a lazy delegated failure. */\nexport const FINZ_DECL = \"function __zcFinZ(z,i){return new __ZcFailZ(z,i);}\";\n\n/**\n * Validator factory. Inline mode (CLI emitter) declares it once per compiled\n * file; lean mode (all unplugin bundlers) exports it once per bundle from the\n * plugin-materialized runtime module — generated code never imports it from\n * the zod-compiler package itself, so zod-compiler stays a devDependency and\n * the helper set is always version-locked to the codegen that calls it.\n * Wraps a safeParse function into the CompiledSchema interface.\n *\n * IDENTITY-PRESERVING: with zodCompat (schema != null) the compiled\n * parse/safeParse/parseAsync/safeParseAsync are installed as OWN properties\n * on the original schema object, which is returned as-is. zod v4 keys\n * several APIs on object identity — toJSONSchema's ctx.seen registers the\n * object it is handed while each processor closure captures the original\n * inst (a wrapper crashes `optionalProcessor` with \"Cannot set properties\n * of undefined (setting 'ref')\" the moment a compiled schema is composed\n * into another schema), and globalRegistry/.meta() is a WeakMap keyed by\n * the schema instance (a wrapper silently loses OpenAPI titles/ids). An\n * Object.create wrapper breaks both; mutating the original breaks neither\n * (zod's internal parsing flows through _zod.run, never the public\n * methods, and derived schemas — .optional(), .extend() — are fresh\n * instances that fall back to plain zod). schema=null (zodCompat: false)\n * still produces a plain method-bag object.\n *\n * fc is the schema's hosted fast-check boolean function (null when no Fast\n * Path exists). parse()/parseAsync() try it first and return the input\n * directly on success: fc is small enough for V8 to inline, so the hot parse\n * path runs with zero allocations — calling fn would allocate an intermediate\n * SafeParseResult that escape analysis cannot remove (fn never inlines).\n * Fast-path-eligible schemas never mutate, so fc(input) ⟹ data === input.\n *\n * is is the TOTAL fast-check predicate (fc when the fast path is total, else\n * null). Installed as `.is()` — a zero-allocation boolean type guard. When\n * null (partial fast path or none) `.is()` derives from fn(input).success: a\n * partial fc can pass-through valid input but its `false` does not imply\n * rejection (a default/catch may still succeed), so it would be unsound as a\n * standalone guard.\n *\n * `~standard` is REPLACED, not merely preserved. Zod builds it lazily as\n * `validate: (v) => safeParse(inst, v)` — the core FUNCTION, which goes straight\n * to `inst._zod.run`. It never reads the schema's own `safeParse` property, so\n * installing the compiled one leaves this route entirely uncompiled: measured\n * 271.7 ns against the compiled 26.6 ns on the same schema, i.e. Standard Schema\n * consumers (tRPC, Hono, TanStack) were getting plain Zod.\n *\n * Zod's own validate is captured first and kept as the throw path: it catches a\n * synchronous throw and retries through `safeParseAsync`, which is how an async\n * refinement resolves and how a throwing check surfaces as a rejected promise\n * rather than a synchronous throw. The compiled validator cannot reproduce that\n * (async schemas delegate to Zod anyway), so deferring to the original is both\n * simpler and exact.\n *\n * Installed with defineProperty rather than assignment: Zod's lazy setter\n * redefines the slot as non-writable, so a second `__zcMkv` on the same schema\n * object — two exports aliasing one schema — would throw under ESM strict mode.\n */\nexport const MK_VALIDATOR_DECL =\n \"function __zcMkv(fn,schema,fc,is){var w=schema||{};w.parse=fc?function(input){if(fc(input))return input;var r=fn(input);if(r.success)return r.data;throw r.error;}:function(input){var r=fn(input);if(r.success)return r.data;throw r.error;};w.safeParse=fn;w.safeParseAsync=function(input){return Promise.resolve(fn(input));};w.parseAsync=fc?function(input){if(fc(input))return Promise.resolve(input);var r=fn(input);if(r.success)return Promise.resolve(r.data);return Promise.reject(r.error);}:function(input){var r=fn(input);if(r.success)return Promise.resolve(r.data);return Promise.reject(r.error);};w.is=is||function(input){return fn(input).success;};\" +\n 'var s=w[\"~standard\"],zv=s&&s.validate;' +\n 'Object.defineProperty(w,\"~standard\",{configurable:true,value:{version:1,vendor:(s&&s.vendor)||\"zod\",validate:function(input){var r;try{if(fc&&fc(input))return{value:input};r=fn(input);}catch(e){if(zv)return zv(input);throw e;}return r.success?{value:r.data}:{issues:r.error.issues};}}});' +\n \"return w;}\";\n\nfunction extractFunctionName(functionDef: string): string {\n const match = /^function\\s+(\\w+)\\s*\\(/.exec(functionDef);\n if (!match?.[1]) {\n throw new Error(\"Cannot extract function name from generated code\");\n }\n return match[1];\n}\n\n/**\n * Generate a `/* @__PURE__ * /` IIFE wrapping a compiled validator.\n *\n * @param schemaExpr - Expression resolving to the original Zod schema\n * (e.g. `\"UserSchema\"` in unplugin, `\"(__src_X as any).schema\"` in CLI)\n * @param schema\n * @param options\n */\nexport function generateIIFE(\n schemaExpr: string,\n schema: CompiledSchemaInfo,\n options?: { zodCompat?: boolean | undefined },\n): string {\n const { codegenResult, refEntries } = schema;\n const fnName = extractFunctionName(codegenResult.functionDef);\n const zodCompat = options?.zodCompat !== false;\n const schemaArg = zodCompat ? schemaExpr : \"null\";\n const fcArg = codegenResult.fastFnName ?? \"null\";\n // `.is()` gets the fast-check directly only when it is a total predicate;\n // partial/none falls back to safeParse().success inside __zcMkv. A rebuilding\n // schema has no by-reference `fc` but still names its predicate separately.\n const isArg = codegenResult.isFnName ?? (codegenResult.fastTotal ? fcArg : \"null\");\n\n return [\n \"/* @__PURE__ */ (() => {\",\n ...(refEntries.length > 0\n ? [`var __rf=[${refEntries.map((fb) => `${schemaExpr}${fb.accessPath}`).join(\",\")}];`]\n : []),\n ...codegenResult.code\n .split(\"\\n\")\n .filter((l) => l.trim() !== \"\" && l.trim() !== \"/* zod-compiler */\"),\n codegenResult.functionDef,\n `return __zcMkv(${fnName},${schemaArg},${fcArg},${isArg});`,\n \"})()\",\n ].join(\"\\n\");\n}\n"],"mappings":";;;;;;;AAaA,MAAa,oBACX;;;;;;;;;;;;;;;;;;;;AAqBF,MAAa,sBACX;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCF,MAAa,kBACX;;;AASF,MAAa,WACX;;;;;;;;;;;;;;;;;;;;;;;AAwBF,MAAa,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;AAwBjC,MAAa,mBACX;;AAKF,MAAa,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDzB,MAAa,oBACX;AAKF,SAAS,oBAAoB,aAA6B;CACxD,MAAM,QAAQ,yBAAyB,KAAK,WAAW;CACvD,IAAI,CAAC,QAAQ,IACX,MAAM,IAAI,MAAM,kDAAkD;CAEpE,OAAO,MAAM;AACf;;;;;;;;;AAUA,SAAgB,aACd,YACA,QACA,SACQ;CACR,MAAM,EAAE,eAAe,eAAe;CACtC,MAAM,SAAS,oBAAoB,cAAc,WAAW;CAE5D,MAAM,YADY,SAAS,cAAc,QACX,aAAa;CAC3C,MAAM,QAAQ,cAAc,cAAc;CAI1C,MAAM,QAAQ,cAAc,aAAa,cAAc,YAAY,QAAQ;CAE3E,OAAO;EACL;EACA,GAAI,WAAW,SAAS,IACpB,CAAC,aAAa,WAAW,KAAK,OAAO,GAAG,aAAa,GAAG,YAAY,CAAC,CAAC,KAAK,GAAG,EAAE,GAAG,IACnF,CAAC;EACL,GAAG,cAAc,KACd,MAAM,IAAI,CAAC,CACX,QAAQ,MAAM,EAAE,KAAK,MAAM,MAAM,EAAE,KAAK,MAAM,oBAAoB;EACrE,cAAc;EACd,kBAAkB,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM;EACxD;CACF,CAAC,CAAC,KAAK,IAAI;AACb"}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -372,6 +372,21 @@ interface TransformEffectIR {
|
|
|
372
372
|
/** The input schema to validate before applying the transform */
|
|
373
373
|
inner: SchemaIR;
|
|
374
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* z.custom() / z.instanceof(): call the predicate on the hot path and retain
|
|
377
|
+
* the original schema solely for exact, cold-path issue production.
|
|
378
|
+
*/
|
|
379
|
+
interface CustomIR {
|
|
380
|
+
type: "custom";
|
|
381
|
+
/** Zero-capture predicate source, mutually exclusive with refIndex. */
|
|
382
|
+
source?: string;
|
|
383
|
+
/** __rf[] index of a captured predicate, mutually exclusive with source. */
|
|
384
|
+
refIndex?: number;
|
|
385
|
+
/** __rf[] index of the pristine Zod schema used by the slow error walk. */
|
|
386
|
+
schemaRefIndex: number;
|
|
387
|
+
/** z.custom() defaults to aborting a failed union option. */
|
|
388
|
+
abort: boolean;
|
|
389
|
+
}
|
|
375
390
|
interface FallbackIR {
|
|
376
391
|
type: "fallback";
|
|
377
392
|
reason: "transform" | "refine" | "superRefine" | "custom" | "lazy" | "unsupported" | "coalesced";
|
|
@@ -429,7 +444,7 @@ interface StringBoolIR {
|
|
|
429
444
|
interface TypeMessageCarrier {
|
|
430
445
|
typeMessage?: string;
|
|
431
446
|
}
|
|
432
|
-
type SchemaIR = TypeMessageCarrier & (StringIR | NumberIR | BooleanIR | BigIntIR | DateIR | SymbolIR | NullIR | UndefinedIR | VoidIR | NanIR | NeverIR | AnyIR | UnknownIR | LiteralIR | EnumIR | ObjectIR | ArrayIR | TupleIR | RecordIR | SetIR | MapIR | FileIR | UnionIR | DiscriminatedUnionIR | IntersectionIR | OptionalIR | NullableIR | ReadonlyIR | DefaultIR | PipeIR | TransformEffectIR | TemplateLiteralIR | CatchIR | FallbackIR | RecursiveRefIR | RecursionTargetIR | StringBoolIR);
|
|
447
|
+
type SchemaIR = TypeMessageCarrier & (StringIR | NumberIR | BooleanIR | BigIntIR | DateIR | SymbolIR | NullIR | UndefinedIR | VoidIR | NanIR | NeverIR | AnyIR | UnknownIR | LiteralIR | EnumIR | ObjectIR | ArrayIR | TupleIR | RecordIR | SetIR | MapIR | FileIR | UnionIR | DiscriminatedUnionIR | IntersectionIR | OptionalIR | NullableIR | ReadonlyIR | DefaultIR | PipeIR | TransformEffectIR | CustomIR | TemplateLiteralIR | CatchIR | FallbackIR | RecursiveRefIR | RecursionTargetIR | StringBoolIR);
|
|
433
448
|
interface SafeParseSuccess<T> {
|
|
434
449
|
success: true;
|
|
435
450
|
data: T;
|
|
@@ -469,5 +484,5 @@ interface CompiledSchema<T> {
|
|
|
469
484
|
is(input: unknown): input is T;
|
|
470
485
|
}
|
|
471
486
|
//#endregion
|
|
472
|
-
export { AnyIR, ArrayIR, BigIntCheckIR, BigIntIR, BooleanIR, CatchIR, CheckBase, CheckBigIntGreaterThan, CheckBigIntLessThan, CheckBigIntMultipleOf, CheckDateGreaterThan, CheckDateLessThan, CheckEndsWith, CheckGreaterThan, CheckIR, CheckIncludes, CheckLengthEquals, CheckLessThan, CheckMaxLength, CheckMaxSize, CheckMimeType, CheckMinLength, CheckMinSize, CheckMultipleOf, CheckNumberFormat, CheckOrEffectIR, CheckSizeEquals, CheckStartsWith, CheckStringFormat, CompiledSchema, DateCheckIR, DateIR, DefaultIR, DiscoveredSchema, DiscriminatedUnionIR, EnumIR, FallbackIR, FileCheckIR, FileIR, IntersectionIR, LiteralIR, MapIR, NanIR, NeverIR, NullIR, NullableIR, NumberIR, ObjectIR, OptionalIR, OverwriteEffectCheckIR, PipeIR, ReadonlyIR, RecordIR, RecursionTargetIR, RecursiveRefIR, RefineEffectCheckIR, SafeParseError, SafeParseResult, SafeParseSuccess, SchemaIR, SetCheckIR, SetIR, StringBoolIR, StringIR, SuperRefineEffectCheckIR, SymbolIR, TemplateLiteralIR, TransformEffectIR, TupleIR, TypeMessageCarrier, UndefinedIR, UnionIR, UnknownIR, VoidIR, ZodErrorLike, ZodIssueLike };
|
|
487
|
+
export { AnyIR, ArrayIR, BigIntCheckIR, BigIntIR, BooleanIR, CatchIR, CheckBase, CheckBigIntGreaterThan, CheckBigIntLessThan, CheckBigIntMultipleOf, CheckDateGreaterThan, CheckDateLessThan, CheckEndsWith, CheckGreaterThan, CheckIR, CheckIncludes, CheckLengthEquals, CheckLessThan, CheckMaxLength, CheckMaxSize, CheckMimeType, CheckMinLength, CheckMinSize, CheckMultipleOf, CheckNumberFormat, CheckOrEffectIR, CheckSizeEquals, CheckStartsWith, CheckStringFormat, CompiledSchema, CustomIR, DateCheckIR, DateIR, DefaultIR, DiscoveredSchema, DiscriminatedUnionIR, EnumIR, FallbackIR, FileCheckIR, FileIR, IntersectionIR, LiteralIR, MapIR, NanIR, NeverIR, NullIR, NullableIR, NumberIR, ObjectIR, OptionalIR, OverwriteEffectCheckIR, PipeIR, ReadonlyIR, RecordIR, RecursionTargetIR, RecursiveRefIR, RefineEffectCheckIR, SafeParseError, SafeParseResult, SafeParseSuccess, SchemaIR, SetCheckIR, SetIR, StringBoolIR, StringIR, SuperRefineEffectCheckIR, SymbolIR, TemplateLiteralIR, TransformEffectIR, TupleIR, TypeMessageCarrier, UndefinedIR, UnionIR, UnknownIR, VoidIR, ZodErrorLike, ZodIssueLike };
|
|
473
488
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/core/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/core/types.ts"],"mappings":";;;;;;UAQiB;;EAEf;;UAGe,uBAAuB;EACtC;EACA;;UAGe,uBAAuB;EACtC;EACA;;UAGe,0BAA0B;EACzC;EACA;;UAGe,yBAAyB;EACxC;EACA;EACA;;UAGe,sBAAsB;EACrC;EACA;EACA;;UAGe,wBAAwB;EACvC;EACA;;UAGe,0BAA0B;EACzC;EACA;;UAGe,0BAA0B;EACzC;EACA;EACA;;EAEA;;EAEA;EACA;;EAEA;EACA;;EAEA;;UAGe,sBAAsB;EACrC;EACA;EACA;;UAGe,wBAAwB;EACvC;EACA;;UAGe,sBAAsB;EACrC;EACA;;KAGU,UACR,iBACA,iBACA,oBACA,mBACA,gBACA,kBACA,oBACA,oBACA,gBACA,kBACA;UAMa;EACf;;;;;;EAMA;;;;;;;;;EASA;;EAEA;;;;;;EAMA;;;;;;;;;;;;;UAce;EACf;;EAEA;;;;;;UAOe;EACf;;EAEA;;;KAIU,kBACR,UACA,sBACA,2BACA;UAIa,6BAA6B;EAC5C;EACA;EACA;EACA;;UAGe,0BAA0B;EACzC;EACA;EACA;EACA;;KAGU,cAAc,uBAAuB;UAIhC,+BAA+B;EAC9C;;EAEA;EACA;;UAGe,4BAA4B;EAC3C;;EAEA;EACA;;UAGe,8BAA8B;EAC7C;;EAEA;;KAGU,gBAAgB,yBAAyB,sBAAsB;UAI1D,qBAAqB;EACpC;EACA;;UAGe,qBAAqB;EACpC;EACA;;UAGe,wBAAwB;EACvC;EACA;;KAGU,aAAa,eAAe,eAAe;UAItC,sBAAsB;EACrC;EACA;;KAGU,cAAc,eAAe,eAAe;UAIvC;EACf;EACA,QAAQ;EACR;;UAGe;EACf;EACA,QAAQ;EACR;;UAGe;EACf;EACA;;UAGe;EACf;EACA,QAAQ;EACR;;UAGe;EACf;EACA,QAAQ;EACR;;UAGe;EACf;;UAGe;EACf;;UAGe;EACf;;UAGe;EACf;;UAGe;EACf;;UAGe;EACf;;UAGe;EACf;;UAGe;EACf;;UAGe;EACf;EACA;;UAGe;EACf;;EAEA;;UAKe;EACf;EACA,YAAY,eAAe;;;;;;;;;;EAU3B;;;;;;;;;;;;;EAaA;;EAEA,UAAU,sBAAsB;;;;;;;EAOhC,WAAW;;;;;;;EAOX;;UAGe;EACf;EACA,SAAS;EACT,QAAQ;;UAGO;EACf;EACA,OAAO;EACP,MAAM;;UAGS;EACf;EACA,SAAS;EACT,WAAW;;UAGI;EACf;EACA,WAAW;EACX,SAAS;;UAGM;EACf;EACA,SAAS;EACT,WAAW;;UAGI;EACf;EACA,SAAS;;UAKM;EACf;EACA,SAAS;;UAGM;EACf;EACA;EACA,SAAS;;;;;;EAMT;IAAS;IAA8D;;;UAGxD;EACf;EACA,MAAM;EACN,OAAO;;UAKQ;EACf;EACA,OAAO;;UAGQ;EACf;EACA,OAAO;;UAGQ;EACf;EACA,OAAO;;UAGQ;EACf;EACA,OAAO;EACP;;;;;;;;;;;EAWA;;UAGe;EACf;EACA,IAAI;EACJ,KAAK;;UAKU;EACf;EACA;;;;;EAKA;;;;;;;EAOA;;EAEA,OAAO;;
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/core/types.ts"],"mappings":"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"}
|