zod-compiler 1.23.4 → 1.23.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +27 -16
  2. package/dist/core/codegen/build-path.d.ts.map +1 -1
  3. package/dist/core/codegen/build-path.js +15 -5
  4. package/dist/core/codegen/build-path.js.map +1 -1
  5. package/dist/core/codegen/context.d.ts.map +1 -1
  6. package/dist/core/codegen/context.js +4 -2
  7. package/dist/core/codegen/context.js.map +1 -1
  8. package/dist/core/codegen/dedupe.js +3 -1
  9. package/dist/core/codegen/dedupe.js.map +1 -1
  10. package/dist/core/codegen/fast-path.d.ts.map +1 -1
  11. package/dist/core/codegen/fast-path.js +2 -0
  12. package/dist/core/codegen/fast-path.js.map +1 -1
  13. package/dist/core/codegen/fast-size.d.ts.map +1 -1
  14. package/dist/core/codegen/fast-size.js +2 -1
  15. package/dist/core/codegen/fast-size.js.map +1 -1
  16. package/dist/core/codegen/index.js +18 -1
  17. package/dist/core/codegen/index.js.map +1 -1
  18. package/dist/core/codegen/schemas/effect.d.ts +2 -2
  19. package/dist/core/codegen/schemas/effect.d.ts.map +1 -1
  20. package/dist/core/codegen/schemas/effect.js +12 -0
  21. package/dist/core/codegen/schemas/effect.js.map +1 -1
  22. package/dist/core/codegen/schemas/zod-delegate.d.ts +12 -0
  23. package/dist/core/codegen/schemas/zod-delegate.d.ts.map +1 -0
  24. package/dist/core/codegen/schemas/zod-delegate.js +14 -0
  25. package/dist/core/codegen/schemas/zod-delegate.js.map +1 -0
  26. package/dist/core/codegen/slow-path.d.ts.map +1 -1
  27. package/dist/core/codegen/slow-path.js +2 -0
  28. package/dist/core/codegen/slow-path.js.map +1 -1
  29. package/dist/core/diagnostic.d.ts.map +1 -1
  30. package/dist/core/diagnostic.js +3 -0
  31. package/dist/core/diagnostic.js.map +1 -1
  32. package/dist/core/extract/effects.d.ts +7 -1
  33. package/dist/core/extract/effects.d.ts.map +1 -1
  34. package/dist/core/extract/effects.js +24 -1
  35. package/dist/core/extract/effects.js.map +1 -1
  36. package/dist/core/extract/extractors/intersection.d.ts.map +1 -1
  37. package/dist/core/extract/extractors/intersection.js +32 -0
  38. package/dist/core/extract/extractors/intersection.js.map +1 -1
  39. package/dist/core/extract/extractors/pipe.d.ts.map +1 -1
  40. package/dist/core/extract/extractors/pipe.js +24 -1
  41. package/dist/core/extract/extractors/pipe.js.map +1 -1
  42. package/dist/core/types.d.ts +23 -2
  43. package/dist/core/types.d.ts.map +1 -1
  44. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/core/codegen/index.ts"],"sourcesContent":["import type { SchemaIR } from \"../types.js\";\nimport type { CodeGenContext, CodeGenResult, CodegenMode, RecTargetGen } from \"./context.js\";\nimport { generateBuild, rebuildsOutput } from \"./build-path.js\";\nimport { declareFastTemps, emitRfDelegate, hasMutation } from \"./context.js\";\nimport type { SharedSchemaPlan } from \"./dedupe.js\";\nimport { createFastGen, generateFast } from \"./fast-path.js\";\nimport { createSlowGen, generateSlow } from \"./slow-path.js\";\n\nexport type { CodeGenResult } from \"./context.js\";\n\nexport interface GenerateValidatorOptions {\n refCount?: number;\n /** Codegen output mode. Defaults to \"inline\". */\n mode?: CodegenMode;\n /**\n * File-level shared slow-walk plan (schema deduplication). Applied only to\n * mutation-free schemas, so shared walks stay on the deferred cold path.\n */\n sharedSchemas?: SharedSchemaPlan | undefined;\n /**\n * Compact mode (`output: \"compact\"`). Drop the compiled slow walk for\n * mutation-free schemas with a TOTAL fast path and delegate the cold error\n * path to the retained Zod schema (`__zcFinZ`). The fast (hot) path is\n * unchanged; only the bulky error-collecting walk — 64–77% of generated\n * bytes — is replaced by a few bytes of zod delegation. See\n * {@link CodeGenResult.rootDelegateRefIndex}.\n */\n compact?: boolean | undefined;\n}\n\n/**\n * Generate optimized validation code from SchemaIR.\n *\n * - `code`: preamble declarations (Sets, RegExps, etc.) — deterministic for the same IR\n * - `functionDef`: full function expression string referencing preamble vars via closure\n * - `usedHelpers`: helper names from \"virtual:zod-compiler/runtime\" referenced (lean mode only)\n *\n * Usage: `new Function(code + \"\\nreturn \" + functionDef + \";\")()`\n */\nexport function generateValidator(\n ir: SchemaIR,\n name: string,\n options?: GenerateValidatorOptions,\n): CodeGenResult {\n const fnName = `safeParse_${name}`;\n const mode: CodegenMode = options?.mode ?? \"inline\";\n const ctx: CodeGenContext = {\n preamble: [],\n counter: 0,\n fnName,\n regexCache: new Map(),\n mode,\n usedHelpers: new Set(),\n };\n\n // Slow-walk sharing. The plan already excludes any shape that would reach for\n // this export's `__rf[]`, and a shared walk returns its parsed value, so a\n // rewriting shape (a stripping object above all) delivers its result through\n // the call. Nothing left to gate on here.\n if (options?.sharedSchemas !== undefined) {\n ctx.sharedSchemas = options.sharedSchemas;\n }\n\n // Root-level fallback: the whole schema delegates to Zod, so zod's own\n // safeParse result IS the result. Returning it directly skips the issue\n // copy loop (which would force zod's eager ZodError construction), the\n // pointless [].concat(path) rewrites, and the __zcFin re-wrap. Delegation\n // goes through the pre-mutation capture (emitRfDelegate) — here __rf[0]\n // and the __zcMkv target are routinely the SAME object.\n if (ir.type === \"fallback\" && ir.refIndex !== undefined) {\n const delegate = emitRfDelegate(ctx, ir.refIndex);\n return {\n code: [\"/* zod-compiler */\", ...ctx.preamble].join(\"\\n\"),\n functionDef: `function ${fnName}(input){return ${delegate}(input);}`,\n refCount: options?.refCount ?? 0,\n usedHelpers: ctx.usedHelpers,\n fastFnName: null,\n fastTotal: false,\n };\n }\n\n // Recursion-target table. The root (refId 0) reuses the schema's own\n // `safeParse_<name>` / hosted fast-check, so it needs no separate helper and\n // its fast name is allocated lazily during the walk (recFastName). Non-root\n // targets — recursive sub-schemas nested in a larger root, multiple distinct\n // recursive shapes, mutual recursion — are each hosted as a standalone\n // `__rsp_N` (slow) / `__fcr_N` (fast) validator the cycle calls by name.\n // Common directly-self-recursive schemas have no non-root targets, so this\n // leaves their generated output byte-identical.\n const nonRootTargets = collectRecursionTargets(ir);\n ctx.recTargets = new Map<number, RecTargetGen>([[0, { isRoot: true, slowName: fnName }]]);\n for (const [refId, inner] of nonRootTargets) {\n ctx.recTargets.set(refId, {\n isRoot: false,\n inner,\n slowName: `__rsp_${ctx.counter++}`,\n fastName: `__fcr_${ctx.counter++}`,\n });\n }\n const hasNonRootTargets = nonRootTargets.size > 0;\n\n // Fast Path: generate a boolean expression for eligible schemas.\n //\n // generateFast mutates ctx as it walks (extracted __fo_ helpers + regex/effect\n // decls pushed to the preamble, a recursive __fcr_ name reserved, dedup caches\n // populated). The walk is all-or-nothing: a later fast-ineligible node makes it\n // return null AFTER those side effects already landed. Without a rollback the\n // discarded fast path leaves dead __fo_ helpers in the output — and, when one\n // referenced the recursive __fcr_ host that the `fastExpr !== null` branch below\n // never emits, a dangling reference to an undefined identifier. Snapshot the\n // mutable state and restore it on abort so the slow path re-declares from clean.\n const fastPreambleLen = ctx.preamble.length;\n const fastRegexCache = new Map(ctx.regexCache);\n const fastEffectCache = ctx.effectFnCache && new Map(ctx.effectFnCache);\n const fastValueCache = ctx.valueCache && new Map(ctx.valueCache);\n const fastRecName = ctx.recFastName;\n const fg = createFastGen(\"input\", ctx);\n let fastExpr = generateFast(ir, fg);\n if (fastExpr !== null && hasNonRootTargets) {\n // Host each non-root recursion target as a boolean fast-check helper. A\n // single fast-ineligible target (e.g. one whose recursive shape contains a\n // fallback) disables the WHOLE fast path: the root expression already emits\n // calls to these names, so a missing body would dangle. The shared\n // rollback below then restores clean state for the slow-only path.\n for (const t of ctx.recTargets.values()) {\n if (t.isRoot) continue;\n const targetGen = createFastGen(\"input\", ctx, false);\n const body = generateFast(t.inner as SchemaIR, targetGen);\n if (body === null) {\n fastExpr = null;\n break;\n }\n ctx.preamble.push(\n `function ${t.fastName}(input){${declareFastTemps(targetGen.scope)}return ${body};}`,\n );\n }\n }\n if (fastExpr === null) {\n ctx.preamble.length = fastPreambleLen;\n ctx.regexCache = fastRegexCache;\n if (fastEffectCache === undefined) delete ctx.effectFnCache;\n else ctx.effectFnCache = fastEffectCache;\n // Value declarations the abandoned walk emitted are truncated above, so\n // their cache entries would name identifiers that no longer exist.\n if (fastValueCache === undefined) delete ctx.valueCache;\n else ctx.valueCache = fastValueCache;\n if (fastRecName === undefined) delete ctx.recFastName;\n else ctx.recFastName = fastRecName;\n }\n\n // Host the fast expression in a named boolean helper. Self-recursive\n // schemas need it so recursive refs can call it; every other eligible\n // schema benefits too: __zcMkv wires it into parse()/parseAsync(), whose\n // success paths then return the input directly — no intermediate\n // SafeParseResult allocation (the safeParse function body is far past\n // V8's inlining budget, so escape analysis never removes it).\n let fastFnName: string | null = null;\n if (fastExpr !== null && fastExpr !== \"true\") {\n fastFnName = ctx.recFastName ?? `__fc_${ctx.counter++}`;\n ctx.preamble.push(\n `function ${fastFnName}(input){${declareFastTemps(fg.scope)}return ${fastExpr};}`,\n );\n fastExpr = `${fastFnName}(input)`;\n }\n\n // Build Path: one pass that validates and assembles rewritten output, bailing\n // on the first failure (see build-path). Null unless every mutation in the\n // schema is modelled — so this emits nothing for the mutation-free schemas\n // the compact branch takes.\n const buildFnName = generateBuild(ir, ctx);\n\n // `.is()` for a build-path schema is the fast expression — stripping reshapes\n // the payload, never the verdict. A substituted `.default()` breaks that: the\n // fast check demands a present value where the schema accepts its absence, so\n // the predicate is partial and `.is()` falls back to safeParse().success\n // (which runs the build pass, so it is no slower than the eager walk was).\n const buildIsFnName = ctx.buildSubstitutesValue === true ? null : fastFnName;\n\n const baseRefCount = options?.refCount ?? 0;\n\n // Compact mode: a mutation-free schema with a TOTAL fast path needs no\n // compiled slow walk — its only purpose is reproducing zod's issues on\n // failure, and the original zod schema (retained in `output: \"compact\"`) does\n // that exactly. Emit the fast check and, on failure, delegate to a fresh root\n // RefEntry (the schema itself) via the pristine-bound safeParse capture, then\n // wrap it in the lazy `__zcFinZ` failure. Drops 64–77% of generated bytes\n // with zero hot-path cost (fc and `.is()` are unchanged) and zod-identical\n // errors. Excluded: schemas with non-root recursion targets (mutual/nested\n // recursion still hosts standalone slow validators) and any non-total fast\n // path (mutation/default/catch/fallback) — those keep the compiled path.\n if (\n options?.compact === true &&\n fastExpr !== null &&\n fastExpr !== \"true\" &&\n !hasMutation(ir) &&\n !hasNonRootTargets\n ) {\n const delegate = emitRfDelegate(ctx, baseRefCount);\n ctx.usedHelpers.add(\"__zcFinZ\");\n return {\n code: [\"/* zod-compiler */\", ...ctx.preamble].join(\"\\n\"),\n functionDef: [\n `function ${fnName}(input){`,\n `if(${fastExpr}){return{success:true,data:input};}`,\n `return __zcFinZ(${delegate},input);`,\n `}`,\n ].join(\"\\n\"),\n refCount: baseRefCount + 1,\n usedHelpers: ctx.usedHelpers,\n fastFnName,\n // Mutation-free total fast path: fc(input) ⟺ accepts(input), so `.is()`\n // installs fc directly (compact never weakens the guard).\n fastTotal: true,\n rootDelegateRefIndex: baseRefCount,\n };\n }\n\n // Compact + Build Path: same bargain for a schema with modelled rewrites. The\n // build pass still has to run (it produces the payload, which zod's schema\n // would only reproduce by parsing again), but the compiled issue walk is what\n // compact drops, and delegating that to the retained zod schema costs a few\n // bytes.\n if (\n options?.compact === true &&\n buildFnName !== null &&\n ctx.buildFailName !== undefined &&\n !hasNonRootTargets\n ) {\n const delegate = emitRfDelegate(ctx, baseRefCount);\n ctx.usedHelpers.add(\"__zcFinZ\");\n const built = `__bd_${ctx.counter++}`;\n return {\n code: [\"/* zod-compiler */\", ...ctx.preamble].join(\"\\n\"),\n functionDef: [\n `function ${fnName}(input){`,\n `var ${built}=${buildFnName}(input);`,\n `if(${built}!==${ctx.buildFailName}){return{success:true,data:${built}};}`,\n `return __zcFinZ(${delegate},input);`,\n `}`,\n ].join(\"\\n\"),\n refCount: baseRefCount + 1,\n usedHelpers: ctx.usedHelpers,\n fastFnName: null,\n fastTotal: false,\n isFnName: buildIsFnName,\n rootDelegateRefIndex: baseRefCount,\n };\n }\n\n // Host each non-root recursion target as a safeParse-shaped slow validator,\n // mirroring the root's eager body: collect issues, return success+data or a\n // deferred-error result. Always emitted (the slow path always exists); the\n // recursion call sites read `.success` / `.error.issues` / `.data`. Hoisted\n // function declarations, so their order relative to the root is irrelevant.\n if (hasNonRootTargets) {\n for (const t of ctx.recTargets.values()) {\n if (t.isRoot) continue;\n const body = generateSlow(t.inner as SchemaIR, createSlowGen(\"_d\", \"_d\", \"[]\", \"_e\", ctx));\n ctx.usedHelpers.add(\"__zcFin\");\n ctx.preamble.push(\n `function ${t.slowName}(input){var _e=[];\\nvar _d=input;\\n${body}\\n` +\n `if(_e.length===0){return{success:true,data:_d};}\\nreturn __zcFin(_e,_d);}`,\n );\n }\n }\n\n const sg = createSlowGen(\"_d\", \"_d\", \"[]\", \"_e\", ctx);\n // When the root schema's own shape is shared (it recurs as a sub-schema of\n // another export, or as a duplicate root), its slow walk delegates to the\n // shared function instead of emitting a second full copy. The fast path is\n // still generated inline above — only the cold walk is shared.\n const rootRef = ctx.sharedSchemas?.refFor(ir);\n const slowCode = rootRef !== undefined ? `_d=${rootRef.name}(_d,[],_e);` : generateSlow(ir, sg);\n\n const buildCode = (): string => [\"/* zod-compiler */\", ...ctx.preamble].join(\"\\n\");\n\n const functionDefParts = [`function ${fnName}(input){`];\n\n // Build Path: every mutation is modelled, so one pass can validate and\n // assemble the output together and bail on the first failure, leaving the\n // issue-producing walk deferred behind `.error` (see build-path).\n // The fast check stays out of `safeParse` entirely — running it first would\n // read every property twice — but it is still an EXACT acceptance predicate\n // (stripping reshapes the output, never the verdict), so `.is()` installs it.\n if (buildFnName !== null && ctx.buildFailName !== undefined) {\n ctx.usedHelpers.add(\"__zcFinD\");\n const built = `__bd_${ctx.counter++}`;\n // A self-recursive walk calls this validator BY NAME (slowRecursiveRef),\n // and that binding exists only inside the named function expression — so\n // there the walk stays a per-call closure. Everything else hosts it in the\n // preamble, keeping safeParse to three statements. Mirrors the same split\n // in the mutation-free branch below.\n const recursive = slowCode.includes(fnName);\n let deferred: string;\n if (recursive) {\n deferred = `__zcFinD(function(input){var _e=[];\\nvar _d=input;\\n${slowCode}\\nreturn _e;},input)`;\n } else {\n const walkName = `__sw_${ctx.counter++}`;\n ctx.preamble.push(\n `function ${walkName}(input){var _e=[];\\nvar _d=input;\\n${slowCode}\\nreturn _e;}`,\n );\n deferred = `__zcFinD(${walkName},input)`;\n }\n return {\n code: buildCode(),\n functionDef: [\n `function ${fnName}(input){`,\n `var ${built}=${buildFnName}(input);`,\n `if(${built}!==${ctx.buildFailName}){return{success:true,data:${built}};}`,\n `return ${deferred};`,\n `}`,\n ].join(\"\\n\"),\n refCount: options?.refCount ?? 0,\n usedHelpers: ctx.usedHelpers,\n // No by-reference shortcut: `data` is the freshly built object, so\n // parse() must go through safeParse rather than returning its input.\n fastFnName: null,\n // `fastTotal` qualifies `fastFnName`, which is null here; the predicate\n // travels in `isFnName` instead.\n fastTotal: false,\n isFnName: buildIsFnName,\n };\n }\n\n if (fastExpr === \"true\") {\n // Schema always succeeds (any/unknown) — skip slow path entirely\n functionDefParts.push(`return{success:true,data:input};`);\n functionDefParts.push(`}`);\n return {\n code: buildCode(),\n functionDef: functionDefParts.join(\"\\n\"),\n refCount: options?.refCount ?? 0,\n usedHelpers: ctx.usedHelpers,\n fastFnName: null,\n // any/unknown always succeed; `.is()` derives `true` from the fn fallback.\n fastTotal: false,\n };\n }\n\n if (fastExpr !== null && !hasMutation(ir)) {\n // Mutation-free schemas with a fast path: a fast-check failure can never\n // become a slow-path success (both are generated from the same checks —\n // unlike default/catch schemas, whose partial fast path requires value\n // presence while the slow path SUCCEEDS by applying the fallback).\n // The slow path's only output is therefore the issues array, observable\n // solely through `.error` — defer the whole re-walk into the cached\n // accessor (__zcFinD): a failed safeParse whose `.error` is never read\n // costs the fast check alone.\n //\n // The walk is HOSTED as a named preamble function rather than a per-call\n // closure, for two measured reasons: (1) a failed safeParse no longer\n // allocates a closure environment + function object before the deferral\n // even starts; (2) the safeParse body shrinks to two statements, putting\n // it within V8's inlining budget — callers in hot loops get the\n // success-path result object escape-analyzed away entirely, which the\n // old shape (slow walk inlined into the body) made impossible.\n ctx.usedHelpers.add(\"__zcFinD\");\n if (slowCode.includes(fnName)) {\n // Self-recursive slow paths call the safeParse function by NAME\n // (slowRecursiveRef) — that binding exists only inside the named\n // function expression under the documented evaluation contract\n // (`new Function(code + \"return \" + functionDef)`), so the walk stays\n // a per-call closure for recursive schemas. Recursion is the rare\n // shape; everything else gets the hosted walk.\n functionDefParts.push(\n `if(${fastExpr}){return{success:true,data:input};}`,\n `return __zcFinD(function(input){`,\n `var _e=[];`,\n `var _d=input;`,\n slowCode,\n `return _e;`,\n `},input);`,\n `}`,\n );\n } else {\n const walkName = `__sw_${ctx.counter++}`;\n ctx.preamble.push(\n `function ${walkName}(input){var _e=[];\\nvar _d=input;\\n${slowCode}\\nreturn _e;}`,\n );\n functionDefParts.push(\n `if(${fastExpr}){return{success:true,data:input};}`,\n `return __zcFinD(${walkName},input);`,\n `}`,\n );\n }\n return {\n code: buildCode(),\n functionDef: functionDefParts.join(\"\\n\"),\n refCount: options?.refCount ?? 0,\n usedHelpers: ctx.usedHelpers,\n fastFnName,\n // Total predicate: mutation-free fast path, fc(input) ⟺ accepts(input).\n // generateIIFE installs fc directly as the zero-allocation `.is()`.\n fastTotal: true,\n };\n }\n\n if (fastExpr !== null && !rebuildsOutput(ir)) {\n // Partial fast path (default/catch/... present-value shortcut): the slow\n // path must run eagerly — it can succeed where the fast check failed.\n //\n // Withheld when the schema rebuilds its output: there the fast check is a\n // sound VERDICT but says nothing about the payload, and `data:input` would\n // hand back the unstripped input. Such schemas reach here only when the\n // build path declined them, so the eager walk produces the output instead.\n functionDefParts.push(`if(${fastExpr}){return{success:true,data:input};}`);\n }\n\n // Success branch inlined at the call site instead of inside __zcFin: the\n // eager path (mutation schemas — coerce/default/trim/transform) returns\n // here on EVERY parse, and the inline literal keeps the hot exit free of a\n // cross-function call; __zcFin is reached only on failure.\n functionDefParts.push(\n `var _e=[];`,\n `var _d=input;`,\n slowCode,\n `if(_e.length===0){return{success:true,data:_d};}`,\n `return __zcFin(_e,_d);`,\n `}`,\n );\n\n const functionDef = functionDefParts.join(\"\\n\");\n\n return {\n code: buildCode(),\n functionDef,\n refCount: options?.refCount ?? 0,\n usedHelpers: ctx.usedHelpers,\n fastFnName,\n // Partial fast path (default/catch) or none (including coercion): a false fc\n // result does not imply rejection, so `.is()` derives from\n // safeParse(input).success.\n fastTotal: false,\n };\n}\n\n/**\n * Find every non-root recursion target (a `recursionTarget` node, refId ≥ 1)\n * reachable from `root`, mapping refId → the inner IR to host as a standalone\n * validator. The root target (refId 0) is the schema's own function and is\n * never wrapped, so it never appears here. The same target may be wrapped at\n * several sites (a recursive schema reached from sibling positions); the first\n * inner wins — they are structurally identical extractions of one schema.\n */\nfunction collectRecursionTargets(root: SchemaIR): Map<number, SchemaIR> {\n const out = new Map<number, SchemaIR>();\n const seen = new Set<SchemaIR>();\n const walk = (ir: SchemaIR): void => {\n if (seen.has(ir)) return;\n seen.add(ir);\n if (ir.type === \"recursionTarget\" && !out.has(ir.refId)) {\n out.set(ir.refId, ir.inner);\n }\n for (const child of childIRs(ir)) walk(child);\n };\n walk(root);\n return out;\n}\n\n/** Direct child SchemaIR nodes, covering every node type (including the new wrapper). */\nfunction childIRs(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.items, ir.rest];\n case \"record\":\n case \"map\":\n return [ir.keyType, ir.valueType];\n case \"set\":\n return [ir.valueType];\n case \"union\":\n case \"discriminatedUnion\":\n return ir.options;\n case \"intersection\":\n return [ir.left, ir.right];\n case \"optional\":\n case \"nullable\":\n case \"readonly\":\n case \"default\":\n case \"catch\":\n case \"effect\":\n case \"recursionTarget\":\n return [ir.inner];\n case \"pipe\":\n return [ir.in, ir.out];\n default:\n return [];\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/core/codegen/index.ts"],"sourcesContent":["import type { SchemaIR } from \"../types.js\";\nimport type { CodeGenContext, CodeGenResult, CodegenMode, RecTargetGen } from \"./context.js\";\nimport { generateBuild, rebuildsOutput } from \"./build-path.js\";\nimport { declareFastTemps, emitRfDelegate, hasMutation } from \"./context.js\";\nimport type { SharedSchemaPlan } from \"./dedupe.js\";\nimport { createFastGen, generateFast } from \"./fast-path.js\";\nimport { createSlowGen, generateSlow } from \"./slow-path.js\";\n\nexport type { CodeGenResult } from \"./context.js\";\n\nexport interface GenerateValidatorOptions {\n refCount?: number;\n /** Codegen output mode. Defaults to \"inline\". */\n mode?: CodegenMode;\n /**\n * File-level shared slow-walk plan (schema deduplication). Applied only to\n * mutation-free schemas, so shared walks stay on the deferred cold path.\n */\n sharedSchemas?: SharedSchemaPlan | undefined;\n /**\n * Compact mode (`output: \"compact\"`). Drop the compiled slow walk for\n * mutation-free schemas with a TOTAL fast path and delegate the cold error\n * path to the retained Zod schema (`__zcFinZ`). The fast (hot) path is\n * unchanged; only the bulky error-collecting walk — 64–77% of generated\n * bytes — is replaced by a few bytes of zod delegation. See\n * {@link CodeGenResult.rootDelegateRefIndex}.\n */\n compact?: boolean | undefined;\n}\n\n/**\n * Generate optimized validation code from SchemaIR.\n *\n * - `code`: preamble declarations (Sets, RegExps, etc.) — deterministic for the same IR\n * - `functionDef`: full function expression string referencing preamble vars via closure\n * - `usedHelpers`: helper names from \"virtual:zod-compiler/runtime\" referenced (lean mode only)\n *\n * Usage: `new Function(code + \"\\nreturn \" + functionDef + \";\")()`\n */\nexport function generateValidator(\n ir: SchemaIR,\n name: string,\n options?: GenerateValidatorOptions,\n): CodeGenResult {\n const fnName = `safeParse_${name}`;\n const mode: CodegenMode = options?.mode ?? \"inline\";\n const ctx: CodeGenContext = {\n preamble: [],\n counter: 0,\n fnName,\n regexCache: new Map(),\n mode,\n usedHelpers: new Set(),\n };\n\n // Slow-walk sharing. The plan already excludes any shape that would reach for\n // this export's `__rf[]`, and a shared walk returns its parsed value, so a\n // rewriting shape (a stripping object above all) delivers its result through\n // the call. Nothing left to gate on here.\n if (options?.sharedSchemas !== undefined) {\n ctx.sharedSchemas = options.sharedSchemas;\n }\n\n // Root-level fallback: the whole schema delegates to Zod, so zod's own\n // safeParse result IS the result. Returning it directly skips the issue\n // copy loop (which would force zod's eager ZodError construction), the\n // pointless [].concat(path) rewrites, and the __zcFin re-wrap. Delegation\n // goes through the pre-mutation capture (emitRfDelegate) — here __rf[0]\n // and the __zcMkv target are routinely the SAME object.\n if (ir.type === \"fallback\" && ir.refIndex !== undefined) {\n const delegate = emitRfDelegate(ctx, ir.refIndex);\n return {\n code: [\"/* zod-compiler */\", ...ctx.preamble].join(\"\\n\"),\n functionDef: `function ${fnName}(input){return ${delegate}(input);}`,\n refCount: options?.refCount ?? 0,\n usedHelpers: ctx.usedHelpers,\n fastFnName: null,\n fastTotal: false,\n };\n }\n\n // Recursion-target table. The root (refId 0) reuses the schema's own\n // `safeParse_<name>` / hosted fast-check, so it needs no separate helper and\n // its fast name is allocated lazily during the walk (recFastName). Non-root\n // targets — recursive sub-schemas nested in a larger root, multiple distinct\n // recursive shapes, mutual recursion — are each hosted as a standalone\n // `__rsp_N` (slow) / `__fcr_N` (fast) validator the cycle calls by name.\n // Common directly-self-recursive schemas have no non-root targets, so this\n // leaves their generated output byte-identical.\n const nonRootTargets = collectRecursionTargets(ir);\n ctx.recTargets = new Map<number, RecTargetGen>([[0, { isRoot: true, slowName: fnName }]]);\n for (const [refId, inner] of nonRootTargets) {\n ctx.recTargets.set(refId, {\n isRoot: false,\n inner,\n slowName: `__rsp_${ctx.counter++}`,\n fastName: `__fcr_${ctx.counter++}`,\n });\n }\n const hasNonRootTargets = nonRootTargets.size > 0;\n\n // Fast Path: generate a boolean expression for eligible schemas.\n //\n // generateFast mutates ctx as it walks (extracted __fo_ helpers + regex/effect\n // decls pushed to the preamble, a recursive __fcr_ name reserved, dedup caches\n // populated). The walk is all-or-nothing: a later fast-ineligible node makes it\n // return null AFTER those side effects already landed. Without a rollback the\n // discarded fast path leaves dead __fo_ helpers in the output — and, when one\n // referenced the recursive __fcr_ host that the `fastExpr !== null` branch below\n // never emits, a dangling reference to an undefined identifier. Snapshot the\n // mutable state and restore it on abort so the slow path re-declares from clean.\n const fastPreambleLen = ctx.preamble.length;\n const fastRegexCache = new Map(ctx.regexCache);\n const fastEffectCache = ctx.effectFnCache && new Map(ctx.effectFnCache);\n const fastValueCache = ctx.valueCache && new Map(ctx.valueCache);\n const fastRecName = ctx.recFastName;\n const fg = createFastGen(\"input\", ctx);\n let fastExpr = generateFast(ir, fg);\n if (fastExpr !== null && hasNonRootTargets) {\n // Host each non-root recursion target as a boolean fast-check helper. A\n // single fast-ineligible target (e.g. one whose recursive shape contains a\n // fallback) disables the WHOLE fast path: the root expression already emits\n // calls to these names, so a missing body would dangle. The shared\n // rollback below then restores clean state for the slow-only path.\n for (const t of ctx.recTargets.values()) {\n if (t.isRoot) continue;\n const targetGen = createFastGen(\"input\", ctx, false);\n const body = generateFast(t.inner as SchemaIR, targetGen);\n if (body === null) {\n fastExpr = null;\n break;\n }\n ctx.preamble.push(\n `function ${t.fastName}(input){${declareFastTemps(targetGen.scope)}return ${body};}`,\n );\n }\n }\n if (fastExpr === null) {\n ctx.preamble.length = fastPreambleLen;\n ctx.regexCache = fastRegexCache;\n if (fastEffectCache === undefined) delete ctx.effectFnCache;\n else ctx.effectFnCache = fastEffectCache;\n // Value declarations the abandoned walk emitted are truncated above, so\n // their cache entries would name identifiers that no longer exist.\n if (fastValueCache === undefined) delete ctx.valueCache;\n else ctx.valueCache = fastValueCache;\n if (fastRecName === undefined) delete ctx.recFastName;\n else ctx.recFastName = fastRecName;\n }\n\n // Host the fast expression in a named boolean helper. Self-recursive\n // schemas need it so recursive refs can call it; every other eligible\n // schema benefits too: __zcMkv wires it into parse()/parseAsync(), whose\n // success paths then return the input directly — no intermediate\n // SafeParseResult allocation (the safeParse function body is far past\n // V8's inlining budget, so escape analysis never removes it).\n let fastFnName: string | null = null;\n if (fastExpr !== null && fastExpr !== \"true\") {\n fastFnName = ctx.recFastName ?? `__fc_${ctx.counter++}`;\n ctx.preamble.push(\n `function ${fastFnName}(input){${declareFastTemps(fg.scope)}return ${fastExpr};}`,\n );\n fastExpr = `${fastFnName}(input)`;\n }\n\n // Build Path: one pass that validates and assembles rewritten output, bailing\n // on the first failure (see build-path). Null unless every mutation in the\n // schema is modelled — so this emits nothing for the mutation-free schemas\n // the compact branch takes.\n const buildFnName = generateBuild(ir, ctx);\n\n // A zodDelegate with a rebuilding inner exists only to accelerate that\n // build. If an unmodelled descendant made the all-or-nothing build decline,\n // preserve the old root-fallback shape instead of wrapping the same pristine\n // Zod call in an issues array/copy/finalizer. This keeps unsupported object\n // intersections neutral rather than making compilation slower than Zod.\n if (ir.type === \"zodDelegate\" && rebuildsOutput(ir.inner) && buildFnName === null) {\n ctx.preamble.length = 0;\n ctx.usedHelpers.clear();\n ctx.regexCache.clear();\n ctx.effectFnCache?.clear();\n ctx.valueCache?.clear();\n const delegate = emitRfDelegate(ctx, ir.refIndex);\n return {\n code: [\"/* zod-compiler */\", ...ctx.preamble].join(\"\\n\"),\n functionDef: `function ${fnName}(input){return ${delegate}(input);}`,\n refCount: options?.refCount ?? 0,\n usedHelpers: ctx.usedHelpers,\n fastFnName: null,\n fastTotal: false,\n };\n }\n\n // `.is()` for a build-path schema is the fast expression — stripping reshapes\n // the payload, never the verdict. A substituted `.default()` breaks that: the\n // fast check demands a present value where the schema accepts its absence, so\n // the predicate is partial and `.is()` falls back to safeParse().success\n // (which runs the build pass, so it is no slower than the eager walk was).\n const buildIsFnName = ctx.buildSubstitutesValue === true ? null : fastFnName;\n\n const baseRefCount = options?.refCount ?? 0;\n\n // Compact mode: a mutation-free schema with a TOTAL fast path needs no\n // compiled slow walk — its only purpose is reproducing zod's issues on\n // failure, and the original zod schema (retained in `output: \"compact\"`) does\n // that exactly. Emit the fast check and, on failure, delegate to a fresh root\n // RefEntry (the schema itself) via the pristine-bound safeParse capture, then\n // wrap it in the lazy `__zcFinZ` failure. Drops 64–77% of generated bytes\n // with zero hot-path cost (fc and `.is()` are unchanged) and zod-identical\n // errors. Excluded: schemas with non-root recursion targets (mutual/nested\n // recursion still hosts standalone slow validators) and any non-total fast\n // path (mutation/default/catch/fallback) — those keep the compiled path.\n if (\n options?.compact === true &&\n fastExpr !== null &&\n fastExpr !== \"true\" &&\n !hasMutation(ir) &&\n !hasNonRootTargets\n ) {\n const delegate = emitRfDelegate(ctx, baseRefCount);\n ctx.usedHelpers.add(\"__zcFinZ\");\n return {\n code: [\"/* zod-compiler */\", ...ctx.preamble].join(\"\\n\"),\n functionDef: [\n `function ${fnName}(input){`,\n `if(${fastExpr}){return{success:true,data:input};}`,\n `return __zcFinZ(${delegate},input);`,\n `}`,\n ].join(\"\\n\"),\n refCount: baseRefCount + 1,\n usedHelpers: ctx.usedHelpers,\n fastFnName,\n // Mutation-free total fast path: fc(input) ⟺ accepts(input), so `.is()`\n // installs fc directly (compact never weakens the guard).\n fastTotal: true,\n rootDelegateRefIndex: baseRefCount,\n };\n }\n\n // Compact + Build Path: same bargain for a schema with modelled rewrites. The\n // build pass still has to run (it produces the payload, which zod's schema\n // would only reproduce by parsing again), but the compiled issue walk is what\n // compact drops, and delegating that to the retained zod schema costs a few\n // bytes.\n if (\n options?.compact === true &&\n buildFnName !== null &&\n ctx.buildFailName !== undefined &&\n !hasNonRootTargets\n ) {\n const delegate = emitRfDelegate(ctx, baseRefCount);\n ctx.usedHelpers.add(\"__zcFinZ\");\n const built = `__bd_${ctx.counter++}`;\n return {\n code: [\"/* zod-compiler */\", ...ctx.preamble].join(\"\\n\"),\n functionDef: [\n `function ${fnName}(input){`,\n `var ${built}=${buildFnName}(input);`,\n `if(${built}!==${ctx.buildFailName}){return{success:true,data:${built}};}`,\n `return __zcFinZ(${delegate},input);`,\n `}`,\n ].join(\"\\n\"),\n refCount: baseRefCount + 1,\n usedHelpers: ctx.usedHelpers,\n fastFnName: null,\n fastTotal: false,\n isFnName: buildIsFnName,\n rootDelegateRefIndex: baseRefCount,\n };\n }\n\n // Host each non-root recursion target as a safeParse-shaped slow validator,\n // mirroring the root's eager body: collect issues, return success+data or a\n // deferred-error result. Always emitted (the slow path always exists); the\n // recursion call sites read `.success` / `.error.issues` / `.data`. Hoisted\n // function declarations, so their order relative to the root is irrelevant.\n if (hasNonRootTargets) {\n for (const t of ctx.recTargets.values()) {\n if (t.isRoot) continue;\n const body = generateSlow(t.inner as SchemaIR, createSlowGen(\"_d\", \"_d\", \"[]\", \"_e\", ctx));\n ctx.usedHelpers.add(\"__zcFin\");\n ctx.preamble.push(\n `function ${t.slowName}(input){var _e=[];\\nvar _d=input;\\n${body}\\n` +\n `if(_e.length===0){return{success:true,data:_d};}\\nreturn __zcFin(_e,_d);}`,\n );\n }\n }\n\n const sg = createSlowGen(\"_d\", \"_d\", \"[]\", \"_e\", ctx);\n // When the root schema's own shape is shared (it recurs as a sub-schema of\n // another export, or as a duplicate root), its slow walk delegates to the\n // shared function instead of emitting a second full copy. The fast path is\n // still generated inline above — only the cold walk is shared.\n const rootRef = ctx.sharedSchemas?.refFor(ir);\n const slowCode = rootRef !== undefined ? `_d=${rootRef.name}(_d,[],_e);` : generateSlow(ir, sg);\n\n const buildCode = (): string => [\"/* zod-compiler */\", ...ctx.preamble].join(\"\\n\");\n\n const functionDefParts = [`function ${fnName}(input){`];\n\n // Build Path: every mutation is modelled, so one pass can validate and\n // assemble the output together and bail on the first failure, leaving the\n // issue-producing walk deferred behind `.error` (see build-path).\n // The fast check stays out of `safeParse` entirely — running it first would\n // read every property twice — but it is still an EXACT acceptance predicate\n // (stripping reshapes the output, never the verdict), so `.is()` installs it.\n if (buildFnName !== null && ctx.buildFailName !== undefined) {\n ctx.usedHelpers.add(\"__zcFinD\");\n const built = `__bd_${ctx.counter++}`;\n // A self-recursive walk calls this validator BY NAME (slowRecursiveRef),\n // and that binding exists only inside the named function expression — so\n // there the walk stays a per-call closure. Everything else hosts it in the\n // preamble, keeping safeParse to three statements. Mirrors the same split\n // in the mutation-free branch below.\n const recursive = slowCode.includes(fnName);\n let deferred: string;\n if (recursive) {\n deferred = `__zcFinD(function(input){var _e=[];\\nvar _d=input;\\n${slowCode}\\nreturn _e;},input)`;\n } else {\n const walkName = `__sw_${ctx.counter++}`;\n ctx.preamble.push(\n `function ${walkName}(input){var _e=[];\\nvar _d=input;\\n${slowCode}\\nreturn _e;}`,\n );\n deferred = `__zcFinD(${walkName},input)`;\n }\n return {\n code: buildCode(),\n functionDef: [\n `function ${fnName}(input){`,\n `var ${built}=${buildFnName}(input);`,\n `if(${built}!==${ctx.buildFailName}){return{success:true,data:${built}};}`,\n `return ${deferred};`,\n `}`,\n ].join(\"\\n\"),\n refCount: options?.refCount ?? 0,\n usedHelpers: ctx.usedHelpers,\n // No by-reference shortcut: `data` is the freshly built object, so\n // parse() must go through safeParse rather than returning its input.\n fastFnName: null,\n // `fastTotal` qualifies `fastFnName`, which is null here; the predicate\n // travels in `isFnName` instead.\n fastTotal: false,\n isFnName: buildIsFnName,\n };\n }\n\n if (fastExpr === \"true\") {\n // Schema always succeeds (any/unknown) — skip slow path entirely\n functionDefParts.push(`return{success:true,data:input};`);\n functionDefParts.push(`}`);\n return {\n code: buildCode(),\n functionDef: functionDefParts.join(\"\\n\"),\n refCount: options?.refCount ?? 0,\n usedHelpers: ctx.usedHelpers,\n fastFnName: null,\n // any/unknown always succeed; `.is()` derives `true` from the fn fallback.\n fastTotal: false,\n };\n }\n\n if (fastExpr !== null && !hasMutation(ir)) {\n // Mutation-free schemas with a fast path: a fast-check failure can never\n // become a slow-path success (both are generated from the same checks —\n // unlike default/catch schemas, whose partial fast path requires value\n // presence while the slow path SUCCEEDS by applying the fallback).\n // The slow path's only output is therefore the issues array, observable\n // solely through `.error` — defer the whole re-walk into the cached\n // accessor (__zcFinD): a failed safeParse whose `.error` is never read\n // costs the fast check alone.\n //\n // The walk is HOSTED as a named preamble function rather than a per-call\n // closure, for two measured reasons: (1) a failed safeParse no longer\n // allocates a closure environment + function object before the deferral\n // even starts; (2) the safeParse body shrinks to two statements, putting\n // it within V8's inlining budget — callers in hot loops get the\n // success-path result object escape-analyzed away entirely, which the\n // old shape (slow walk inlined into the body) made impossible.\n ctx.usedHelpers.add(\"__zcFinD\");\n if (slowCode.includes(fnName)) {\n // Self-recursive slow paths call the safeParse function by NAME\n // (slowRecursiveRef) — that binding exists only inside the named\n // function expression under the documented evaluation contract\n // (`new Function(code + \"return \" + functionDef)`), so the walk stays\n // a per-call closure for recursive schemas. Recursion is the rare\n // shape; everything else gets the hosted walk.\n functionDefParts.push(\n `if(${fastExpr}){return{success:true,data:input};}`,\n `return __zcFinD(function(input){`,\n `var _e=[];`,\n `var _d=input;`,\n slowCode,\n `return _e;`,\n `},input);`,\n `}`,\n );\n } else {\n const walkName = `__sw_${ctx.counter++}`;\n ctx.preamble.push(\n `function ${walkName}(input){var _e=[];\\nvar _d=input;\\n${slowCode}\\nreturn _e;}`,\n );\n functionDefParts.push(\n `if(${fastExpr}){return{success:true,data:input};}`,\n `return __zcFinD(${walkName},input);`,\n `}`,\n );\n }\n return {\n code: buildCode(),\n functionDef: functionDefParts.join(\"\\n\"),\n refCount: options?.refCount ?? 0,\n usedHelpers: ctx.usedHelpers,\n fastFnName,\n // Total predicate: mutation-free fast path, fc(input) ⟺ accepts(input).\n // generateIIFE installs fc directly as the zero-allocation `.is()`.\n fastTotal: true,\n };\n }\n\n if (fastExpr !== null && !rebuildsOutput(ir)) {\n // Partial fast path (default/catch/... present-value shortcut): the slow\n // path must run eagerly — it can succeed where the fast check failed.\n //\n // Withheld when the schema rebuilds its output: there the fast check is a\n // sound VERDICT but says nothing about the payload, and `data:input` would\n // hand back the unstripped input. Such schemas reach here only when the\n // build path declined them, so the eager walk produces the output instead.\n functionDefParts.push(`if(${fastExpr}){return{success:true,data:input};}`);\n }\n\n // Success branch inlined at the call site instead of inside __zcFin: the\n // eager path (mutation schemas — coerce/default/trim/transform) returns\n // here on EVERY parse, and the inline literal keeps the hot exit free of a\n // cross-function call; __zcFin is reached only on failure.\n functionDefParts.push(\n `var _e=[];`,\n `var _d=input;`,\n slowCode,\n `if(_e.length===0){return{success:true,data:_d};}`,\n `return __zcFin(_e,_d);`,\n `}`,\n );\n\n const functionDef = functionDefParts.join(\"\\n\");\n\n return {\n code: buildCode(),\n functionDef,\n refCount: options?.refCount ?? 0,\n usedHelpers: ctx.usedHelpers,\n fastFnName,\n // Partial fast path (default/catch) or none (including coercion): a false fc\n // result does not imply rejection, so `.is()` derives from\n // safeParse(input).success.\n fastTotal: false,\n };\n}\n\n/**\n * Find every non-root recursion target (a `recursionTarget` node, refId ≥ 1)\n * reachable from `root`, mapping refId → the inner IR to host as a standalone\n * validator. The root target (refId 0) is the schema's own function and is\n * never wrapped, so it never appears here. The same target may be wrapped at\n * several sites (a recursive schema reached from sibling positions); the first\n * inner wins — they are structurally identical extractions of one schema.\n */\nfunction collectRecursionTargets(root: SchemaIR): Map<number, SchemaIR> {\n const out = new Map<number, SchemaIR>();\n const seen = new Set<SchemaIR>();\n const walk = (ir: SchemaIR): void => {\n if (seen.has(ir)) return;\n seen.add(ir);\n if (ir.type === \"recursionTarget\" && !out.has(ir.refId)) {\n out.set(ir.refId, ir.inner);\n }\n for (const child of childIRs(ir)) walk(child);\n };\n walk(root);\n return out;\n}\n\n/** Direct child SchemaIR nodes, covering every node type (including the new wrapper). */\nfunction childIRs(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.items, ir.rest];\n case \"record\":\n case \"map\":\n return [ir.keyType, ir.valueType];\n case \"set\":\n return [ir.valueType];\n case \"union\":\n case \"discriminatedUnion\":\n return ir.options;\n case \"intersection\":\n return [ir.left, ir.right];\n case \"optional\":\n case \"nullable\":\n case \"readonly\":\n case \"default\":\n case \"catch\":\n case \"effect\":\n case \"recursionTarget\":\n case \"zodDelegate\":\n return [ir.inner];\n case \"pipe\":\n return [ir.in, ir.out];\n default:\n return [];\n }\n}\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
- import { RefineEffectCheckIR, SuperRefineEffectCheckIR, TransformEffectIR } from "../../types.js";
1
+ import { PreprocessEffectIR, RefineEffectCheckIR, SuperRefineEffectCheckIR, TransformEffectIR } from "../../types.js";
2
2
  import { FastGen, SlowGen } from "../context.js";
3
3
  //#region src/core/codegen/schemas/effect.d.ts
4
4
  /**
5
5
  * Generate code for a TransformEffectIR node.
6
6
  * Validates the inner schema, then applies the transform function and writes back the result.
7
7
  */
8
- declare function slowEffect(ir: TransformEffectIR, g: SlowGen): string;
8
+ declare function slowEffect(ir: TransformEffectIR | PreprocessEffectIR, g: SlowGen): string;
9
9
  /**
10
10
  * Generate code for a RefineEffectCheckIR (inline refine function call).
11
11
  * Called from string/number/object check loops when a refine_effect is encountered.
@@ -1 +1 @@
1
- {"version":3,"file":"effect.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/effect.ts"],"mappings":";;;;;;;iBAmBgB,WAAW,IAAI,mBAAmB,GAAG;;;;;;;;;iBA0BrC,YAAY,OAAO,qBAAqB,cAAc,GAAG;;;;;iBAoCzD,oBACd,OAAO,0BACP,cACA,GAAG;;;;;;;;;;;;;iBAmBW,iBACd,OAAO,0BACP,cACA,GAAG"}
1
+ {"version":3,"file":"effect.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/effect.ts"],"mappings":";;;;;;;iBAoBgB,WAAW,IAAI,oBAAoB,oBAAoB,GAAG;;;;;;;;;iBAmC1D,YAAY,OAAO,qBAAqB,cAAc,GAAG;;;;;iBAoCzD,oBACd,OAAO,0BACP,cACA,GAAG;;;;;;;;;;;;;iBAmBW,iBACd,OAAO,0BACP,cACA,GAAG"}
@@ -7,6 +7,18 @@ import { ZC_SR_DECL, ZC_SR_OK_DECL, ZC_SR_RUN_DECL } from "../issue-decls.js";
7
7
  * Validates the inner schema, then applies the transform function and writes back the result.
8
8
  */
9
9
  function slowEffect(ir, g) {
10
+ if (ir.effectKind === "preprocess") {
11
+ const valueVar = g.temp("pv");
12
+ return `${emit`
13
+ var ${valueVar}=${emitEffectCallable(g.ctx, ir)}(${g.input});
14
+ ${g.output}=${valueVar};
15
+ ${g.visit(ir.inner, {
16
+ input: valueVar,
17
+ output: g.output,
18
+ aborted: g.aborted
19
+ })}
20
+ `}\n`;
21
+ }
10
22
  const beforeVar = g.temp("ib");
11
23
  const innerCode = g.visit(ir.inner);
12
24
  const abortBranch = g.aborted ? `else{${g.aborted}=true;}` : "";
@@ -1 +1 @@
1
- {"version":3,"file":"effect.js","names":[],"sources":["../../../../src/core/codegen/schemas/effect.ts"],"sourcesContent":["import type {\n RefineEffectCheckIR,\n SuperRefineEffectCheckIR,\n TransformEffectIR,\n} from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n emitEffectCallable,\n emitRuntimeHelper,\n extendStaticPath,\n extendStaticPathIndex,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { ZC_SR_DECL, ZC_SR_OK_DECL, ZC_SR_RUN_DECL } from \"../issue-decls.js\";\n\n/**\n * Generate code for a TransformEffectIR node.\n * Validates the inner schema, then applies the transform function and writes back the result.\n */\nexport function slowEffect(ir: TransformEffectIR, g: SlowGen): string {\n const beforeVar = g.temp(\"ib\");\n const innerCode = g.visit(ir.inner);\n // A transform is `inner.transform(fn)` = a pipe(inner, transform): zod's\n // handlePipeResult aborts when `inner` produces any issue. Inside a union the\n // option must therefore count as aborted even if `inner`'s only issue is a\n // non-aborting `custom`/check-level code (mirrors slowPipe's abort branch).\n const abortBranch = g.aborted ? `else{${g.aborted}=true;}` : \"\";\n\n return `${emit`\n var ${beforeVar}=${g.issues}.length;\n ${innerCode}\n if(${g.issues}.length===${beforeVar}){\n ${g.output}=${emitEffectCallable(g.ctx, ir)}(${g.output});\n }${abortBranch}\n `}\\n`;\n}\n\n/**\n * Generate code for a RefineEffectCheckIR (inline refine function call).\n * Called from string/number/object check loops when a refine_effect is encountered.\n *\n * @param check - The refine effect check IR\n * @param expr - The expression to validate (may differ from g.input, e.g. objVar in object generators)\n * @param g - SlowGen context (provides path, issues)\n */\nexport function refineCheck(check: RefineEffectCheckIR, expr: string, g: SlowGen): string {\n // Custom issues are created by the refine check instance, so only the\n // refine's own message applies (never the schema-level error). With no\n // message baked in, __zcFin applies the locale default (\"Invalid input\").\n const messageProp =\n check.message === undefined ? \"\" : `,message:${JSON.stringify(check.message)}`;\n // `.refine(fn, { path })` reports against a member of the refined value, so\n // the configured segments extend this node's path.\n const path = (check.path ?? []).reduce<string>(\n (acc, segment) =>\n typeof segment === \"number\"\n ? extendStaticPathIndex(acc, segment)\n : extendStaticPath(acc, segment),\n g.path,\n );\n return emit`\n if(!${emitEffectCallable(g.ctx, check)}(${expr})){\n ${g.issues}.push({code:\"custom\",path:${path}${messageProp},input:${expr}});\n }`;\n}\n\n/**\n * Declare the shared invoker both superRefine helpers call. It is module-local\n * rather than an imported helper (lean mode declares it in the runtime module\n * beside them), so inline mode must place it in the preamble itself.\n */\nfunction emitSuperRefineRunner(ctx: SlowGen[\"ctx\"]): void {\n if (ctx.mode !== \"lean\" && !ctx.preamble.includes(ZC_SR_RUN_DECL)) {\n ctx.preamble.push(ZC_SR_RUN_DECL);\n }\n}\n\n/**\n * Fast-path test for a SuperRefineEffectCheckIR: a boolean term reporting that\n * the callback added no issue and left the value alone (see ZC_SR_OK_DECL).\n */\nexport function superRefineFastTest(\n check: SuperRefineEffectCheckIR,\n expr: string,\n g: FastGen,\n): string {\n emitSuperRefineRunner(g.ctx);\n const ok = emitRuntimeHelper(g.ctx, \"__zcSrOk\", ZC_SR_OK_DECL);\n return `${ok}(${emitEffectCallable(g.ctx, check)},${expr})`;\n}\n\n/**\n * Generate code for a SuperRefineEffectCheckIR: call the payload-taking\n * callback with a synthesized `{ value, issues }` and merge what it collected.\n * zod's own wrapper installs `addIssue` and normalizes the result, so the\n * issues are zod's — the helper only reprojects them onto this node's path and\n * strips the internal bookkeeping zod deletes before they become visible.\n *\n * The payload's `value` is writable public API, so it is written back to the\n * output slot the way an overwrite effect is. This walk is always the eager one\n * (the node reports as mutating, see hasMutation), so the write-back lands on\n * every parse that reaches it — not only on failures.\n */\nexport function superRefineCheck(\n check: SuperRefineEffectCheckIR,\n expr: string,\n g: SlowGen,\n): string {\n emitSuperRefineRunner(g.ctx);\n const helper = emitRuntimeHelper(g.ctx, \"__zcSr\", ZC_SR_DECL);\n const fn = emitEffectCallable(g.ctx, check);\n const p = g.temp(\"sp\");\n let code = `var ${p}=${helper}(${fn},${expr},${g.path},${g.issues});${g.output}=${p}.value;`;\n // Inside a union option, an aborting issue must mark the option aborted so\n // pruning matches zod (see ZC_SR_DECL); elsewhere the flag is unobserved.\n if (g.aborted) code += `if(${p}.aborted){${g.aborted}=true;}`;\n return code;\n}\n"],"mappings":";;;;;;;;AAmBA,SAAgB,WAAW,IAAuB,GAAoB;CACpE,MAAM,YAAY,EAAE,KAAK,IAAI;CAC7B,MAAM,YAAY,EAAE,MAAM,GAAG,KAAK;CAKlC,MAAM,cAAc,EAAE,UAAU,QAAQ,EAAE,QAAQ,WAAW;CAE7D,OAAO,GAAG,IAAI;UACN,UAAU,GAAG,EAAE,OAAO;MAC1B,UAAU;SACP,EAAE,OAAO,YAAY,UAAU;QAChC,EAAE,OAAO,GAAG,mBAAmB,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,OAAO;OACvD,YAAY;IACf;AACJ;;;;;;;;;AAUA,SAAgB,YAAY,OAA4B,MAAc,GAAoB;CAIxF,MAAM,cACJ,MAAM,YAAY,KAAA,IAAY,KAAK,YAAY,KAAK,UAAU,MAAM,OAAO;CAG7E,MAAM,QAAQ,MAAM,QAAQ,CAAC,EAAA,CAAG,QAC7B,KAAK,YACJ,OAAO,YAAY,WACf,sBAAsB,KAAK,OAAO,IAClC,iBAAiB,KAAK,OAAO,GACnC,EAAE,IACJ;CACA,OAAO,IAAI;UACH,mBAAmB,EAAE,KAAK,KAAK,EAAE,GAAG,KAAK;QAC3C,EAAE,OAAO,4BAA4B,OAAO,YAAY,SAAS,KAAK;;AAE9E;;;;;;AAOA,SAAS,sBAAsB,KAA2B;CACxD,IAAI,IAAI,SAAS,UAAU,CAAC,IAAI,SAAS,SAAA,+GAAuB,GAC9D,IAAI,SAAS,KAAK,cAAc;AAEpC;;;;;AAMA,SAAgB,oBACd,OACA,MACA,GACQ;CACR,sBAAsB,EAAE,GAAG;CAE3B,OAAO,GADI,kBAAkB,EAAE,KAAK,YAAY,aACrC,EAAE,GAAG,mBAAmB,EAAE,KAAK,KAAK,EAAE,GAAG,KAAK;AAC3D;;;;;;;;;;;;;AAcA,SAAgB,iBACd,OACA,MACA,GACQ;CACR,sBAAsB,EAAE,GAAG;CAC3B,MAAM,SAAS,kBAAkB,EAAE,KAAK,UAAU,UAAU;CAC5D,MAAM,KAAK,mBAAmB,EAAE,KAAK,KAAK;CAC1C,MAAM,IAAI,EAAE,KAAK,IAAI;CACrB,IAAI,OAAO,OAAO,EAAE,GAAG,OAAO,GAAG,GAAG,GAAG,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,OAAO,IAAI,EAAE,OAAO,GAAG,EAAE;CAGpF,IAAI,EAAE,SAAS,QAAQ,MAAM,EAAE,YAAY,EAAE,QAAQ;CACrD,OAAO;AACT"}
1
+ {"version":3,"file":"effect.js","names":[],"sources":["../../../../src/core/codegen/schemas/effect.ts"],"sourcesContent":["import type {\n PreprocessEffectIR,\n RefineEffectCheckIR,\n SuperRefineEffectCheckIR,\n TransformEffectIR,\n} from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n emitEffectCallable,\n emitRuntimeHelper,\n extendStaticPath,\n extendStaticPathIndex,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { ZC_SR_DECL, ZC_SR_OK_DECL, ZC_SR_RUN_DECL } from \"../issue-decls.js\";\n\n/**\n * Generate code for a TransformEffectIR node.\n * Validates the inner schema, then applies the transform function and writes back the result.\n */\nexport function slowEffect(ir: TransformEffectIR | PreprocessEffectIR, g: SlowGen): string {\n if (ir.effectKind === \"preprocess\") {\n const valueVar = g.temp(\"pv\");\n return `${emit`\n var ${valueVar}=${emitEffectCallable(g.ctx, ir)}(${g.input});\n ${g.output}=${valueVar};\n ${g.visit(ir.inner, { input: valueVar, output: g.output, aborted: g.aborted })}\n `}\\n`;\n }\n\n const beforeVar = g.temp(\"ib\");\n const innerCode = g.visit(ir.inner);\n // A transform is `inner.transform(fn)` = a pipe(inner, transform): zod's\n // handlePipeResult aborts when `inner` produces any issue. Inside a union the\n // option must therefore count as aborted even if `inner`'s only issue is a\n // non-aborting `custom`/check-level code (mirrors slowPipe's abort branch).\n const abortBranch = g.aborted ? `else{${g.aborted}=true;}` : \"\";\n\n return `${emit`\n var ${beforeVar}=${g.issues}.length;\n ${innerCode}\n if(${g.issues}.length===${beforeVar}){\n ${g.output}=${emitEffectCallable(g.ctx, ir)}(${g.output});\n }${abortBranch}\n `}\\n`;\n}\n\n/**\n * Generate code for a RefineEffectCheckIR (inline refine function call).\n * Called from string/number/object check loops when a refine_effect is encountered.\n *\n * @param check - The refine effect check IR\n * @param expr - The expression to validate (may differ from g.input, e.g. objVar in object generators)\n * @param g - SlowGen context (provides path, issues)\n */\nexport function refineCheck(check: RefineEffectCheckIR, expr: string, g: SlowGen): string {\n // Custom issues are created by the refine check instance, so only the\n // refine's own message applies (never the schema-level error). With no\n // message baked in, __zcFin applies the locale default (\"Invalid input\").\n const messageProp =\n check.message === undefined ? \"\" : `,message:${JSON.stringify(check.message)}`;\n // `.refine(fn, { path })` reports against a member of the refined value, so\n // the configured segments extend this node's path.\n const path = (check.path ?? []).reduce<string>(\n (acc, segment) =>\n typeof segment === \"number\"\n ? extendStaticPathIndex(acc, segment)\n : extendStaticPath(acc, segment),\n g.path,\n );\n return emit`\n if(!${emitEffectCallable(g.ctx, check)}(${expr})){\n ${g.issues}.push({code:\"custom\",path:${path}${messageProp},input:${expr}});\n }`;\n}\n\n/**\n * Declare the shared invoker both superRefine helpers call. It is module-local\n * rather than an imported helper (lean mode declares it in the runtime module\n * beside them), so inline mode must place it in the preamble itself.\n */\nfunction emitSuperRefineRunner(ctx: SlowGen[\"ctx\"]): void {\n if (ctx.mode !== \"lean\" && !ctx.preamble.includes(ZC_SR_RUN_DECL)) {\n ctx.preamble.push(ZC_SR_RUN_DECL);\n }\n}\n\n/**\n * Fast-path test for a SuperRefineEffectCheckIR: a boolean term reporting that\n * the callback added no issue and left the value alone (see ZC_SR_OK_DECL).\n */\nexport function superRefineFastTest(\n check: SuperRefineEffectCheckIR,\n expr: string,\n g: FastGen,\n): string {\n emitSuperRefineRunner(g.ctx);\n const ok = emitRuntimeHelper(g.ctx, \"__zcSrOk\", ZC_SR_OK_DECL);\n return `${ok}(${emitEffectCallable(g.ctx, check)},${expr})`;\n}\n\n/**\n * Generate code for a SuperRefineEffectCheckIR: call the payload-taking\n * callback with a synthesized `{ value, issues }` and merge what it collected.\n * zod's own wrapper installs `addIssue` and normalizes the result, so the\n * issues are zod's — the helper only reprojects them onto this node's path and\n * strips the internal bookkeeping zod deletes before they become visible.\n *\n * The payload's `value` is writable public API, so it is written back to the\n * output slot the way an overwrite effect is. This walk is always the eager one\n * (the node reports as mutating, see hasMutation), so the write-back lands on\n * every parse that reaches it — not only on failures.\n */\nexport function superRefineCheck(\n check: SuperRefineEffectCheckIR,\n expr: string,\n g: SlowGen,\n): string {\n emitSuperRefineRunner(g.ctx);\n const helper = emitRuntimeHelper(g.ctx, \"__zcSr\", ZC_SR_DECL);\n const fn = emitEffectCallable(g.ctx, check);\n const p = g.temp(\"sp\");\n let code = `var ${p}=${helper}(${fn},${expr},${g.path},${g.issues});${g.output}=${p}.value;`;\n // Inside a union option, an aborting issue must mark the option aborted so\n // pruning matches zod (see ZC_SR_DECL); elsewhere the flag is unobserved.\n if (g.aborted) code += `if(${p}.aborted){${g.aborted}=true;}`;\n return code;\n}\n"],"mappings":";;;;;;;;AAoBA,SAAgB,WAAW,IAA4C,GAAoB;CACzF,IAAI,GAAG,eAAe,cAAc;EAClC,MAAM,WAAW,EAAE,KAAK,IAAI;EAC5B,OAAO,GAAG,IAAI;YACN,SAAS,GAAG,mBAAmB,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM;QACzD,EAAE,OAAO,GAAG,SAAS;QACrB,EAAE,MAAM,GAAG,OAAO;GAAE,OAAO;GAAU,QAAQ,EAAE;GAAQ,SAAS,EAAE;EAAQ,CAAC,EAAE;MAC/E;CACJ;CAEA,MAAM,YAAY,EAAE,KAAK,IAAI;CAC7B,MAAM,YAAY,EAAE,MAAM,GAAG,KAAK;CAKlC,MAAM,cAAc,EAAE,UAAU,QAAQ,EAAE,QAAQ,WAAW;CAE7D,OAAO,GAAG,IAAI;UACN,UAAU,GAAG,EAAE,OAAO;MAC1B,UAAU;SACP,EAAE,OAAO,YAAY,UAAU;QAChC,EAAE,OAAO,GAAG,mBAAmB,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,OAAO;OACvD,YAAY;IACf;AACJ;;;;;;;;;AAUA,SAAgB,YAAY,OAA4B,MAAc,GAAoB;CAIxF,MAAM,cACJ,MAAM,YAAY,KAAA,IAAY,KAAK,YAAY,KAAK,UAAU,MAAM,OAAO;CAG7E,MAAM,QAAQ,MAAM,QAAQ,CAAC,EAAA,CAAG,QAC7B,KAAK,YACJ,OAAO,YAAY,WACf,sBAAsB,KAAK,OAAO,IAClC,iBAAiB,KAAK,OAAO,GACnC,EAAE,IACJ;CACA,OAAO,IAAI;UACH,mBAAmB,EAAE,KAAK,KAAK,EAAE,GAAG,KAAK;QAC3C,EAAE,OAAO,4BAA4B,OAAO,YAAY,SAAS,KAAK;;AAE9E;;;;;;AAOA,SAAS,sBAAsB,KAA2B;CACxD,IAAI,IAAI,SAAS,UAAU,CAAC,IAAI,SAAS,SAAA,+GAAuB,GAC9D,IAAI,SAAS,KAAK,cAAc;AAEpC;;;;;AAMA,SAAgB,oBACd,OACA,MACA,GACQ;CACR,sBAAsB,EAAE,GAAG;CAE3B,OAAO,GADI,kBAAkB,EAAE,KAAK,YAAY,aACrC,EAAE,GAAG,mBAAmB,EAAE,KAAK,KAAK,EAAE,GAAG,KAAK;AAC3D;;;;;;;;;;;;;AAcA,SAAgB,iBACd,OACA,MACA,GACQ;CACR,sBAAsB,EAAE,GAAG;CAC3B,MAAM,SAAS,kBAAkB,EAAE,KAAK,UAAU,UAAU;CAC5D,MAAM,KAAK,mBAAmB,EAAE,KAAK,KAAK;CAC1C,MAAM,IAAI,EAAE,KAAK,IAAI;CACrB,IAAI,OAAO,OAAO,EAAE,GAAG,OAAO,GAAG,GAAG,GAAG,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,OAAO,IAAI,EAAE,OAAO,GAAG,EAAE;CAGpF,IAAI,EAAE,SAAS,QAAQ,MAAM,EAAE,YAAY,EAAE,QAAQ;CACrD,OAAO;AACT"}
@@ -0,0 +1,12 @@
1
+ import { SchemaIR, ZodDelegateIR } from "../../types.js";
2
+ import { FastGen, SlowGen } from "../context.js";
3
+ //#region src/core/codegen/schemas/zod-delegate.d.ts
4
+ /** The retained schema is irrelevant on success; compile the equivalent inner. */
5
+ declare function fastZodDelegate(ir: ZodDelegateIR, g: FastGen): string | null;
6
+ /** Let the pristine Zod schema produce byte-exact issues on the cold path. */
7
+ declare function slowCompiledZodDelegate(ir: SchemaIR & {
8
+ type: "zodDelegate";
9
+ }, g: SlowGen): string;
10
+ //#endregion
11
+ export { fastZodDelegate, slowCompiledZodDelegate };
12
+ //# sourceMappingURL=zod-delegate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-delegate.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/zod-delegate.ts"],"mappings":";;;;iBAKgB,gBAAgB,IAAI,eAAe,GAAG;;iBAKtC,wBACd,IAAI;EAAa;GACjB,GAAG"}
@@ -0,0 +1,14 @@
1
+ import { slowZodDelegate } from "./fallback.js";
2
+ //#region src/core/codegen/schemas/zod-delegate.ts
3
+ /** The retained schema is irrelevant on success; compile the equivalent inner. */
4
+ function fastZodDelegate(ir, g) {
5
+ return g.visit(ir.inner);
6
+ }
7
+ /** Let the pristine Zod schema produce byte-exact issues on the cold path. */
8
+ function slowCompiledZodDelegate(ir, g) {
9
+ return slowZodDelegate(ir.refIndex, g);
10
+ }
11
+ //#endregion
12
+ export { fastZodDelegate, slowCompiledZodDelegate };
13
+
14
+ //# sourceMappingURL=zod-delegate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-delegate.js","names":[],"sources":["../../../../src/core/codegen/schemas/zod-delegate.ts"],"sourcesContent":["import type { SchemaIR, ZodDelegateIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport { slowZodDelegate } from \"./fallback.js\";\n\n/** The retained schema is irrelevant on success; compile the equivalent inner. */\nexport function fastZodDelegate(ir: ZodDelegateIR, g: FastGen): string | null {\n return g.visit(ir.inner);\n}\n\n/** Let the pristine Zod schema produce byte-exact issues on the cold path. */\nexport function slowCompiledZodDelegate(\n ir: SchemaIR & { type: \"zodDelegate\" },\n g: SlowGen,\n): string {\n return slowZodDelegate(ir.refIndex, g);\n}\n"],"mappings":";;;AAKA,SAAgB,gBAAgB,IAAmB,GAA2B;CAC5E,OAAO,EAAE,MAAM,GAAG,KAAK;AACzB;;AAGA,SAAgB,wBACd,IACA,GACQ;CACR,OAAO,gBAAgB,GAAG,UAAU,CAAC;AACvC"}
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"slow-path.d.ts","names":[],"sources":["../../../src/core/codegen/slow-path.ts"],"mappings":";;;iBAqGgB,cACd,mBACA,oBACA,kBACA,mBACA,KAAK,gBACL,sBACC;iBAyCa,aAAa,IAAI,UAAU,GAAG"}
@@ -36,6 +36,7 @@ import { slowUndefined } from "./schemas/undefined.js";
36
36
  import { slowUnion } from "./schemas/union.js";
37
37
  import { slowUnknown } from "./schemas/unknown.js";
38
38
  import { slowVoid } from "./schemas/void.js";
39
+ import { slowCompiledZodDelegate } from "./schemas/zod-delegate.js";
39
40
  //#region src/core/codegen/slow-path.ts
40
41
  const slowRegistry = {
41
42
  string: slowString,
@@ -70,6 +71,7 @@ const slowRegistry = {
70
71
  pipe: slowPipe,
71
72
  effect: slowEffect,
72
73
  custom: slowCustom,
74
+ zodDelegate: slowCompiledZodDelegate,
73
75
  templateLiteral: slowTemplateLiteral,
74
76
  catch: slowCatch,
75
77
  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 { 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"}
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\";\nimport { slowCompiledZodDelegate } from \"./schemas/zod-delegate.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 zodDelegate: slowCompiledZodDelegate,\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,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,aAAa;CACb,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"}
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostic.d.ts","names":[],"sources":["../../src/core/diagnostic.ts"],"mappings":";;KASY;UAEK;;EAEf;;EAEA;;EAEA,QAAQ;;EAER;;EAEA;;EAEA,UAAU;;UAGK;;EAEf,MAAM;;EAEN;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;IAAa;IAAgB;IAAc;;;;;;;iBAyL7B,eAAe,IAAI,WAAW"}
1
+ {"version":3,"file":"diagnostic.d.ts","names":[],"sources":["../../src/core/diagnostic.ts"],"mappings":";;KASY;UAEK;;EAEf;;EAEA;;EAEA,QAAQ;;EAER;;EAEA;;EAEA,UAAU;;UAGK;;EAEf,MAAM;;EAEN;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;IAAa;IAAgB;IAAc;;;;;;;iBA6L7B,eAAe,IAAI,WAAW"}
@@ -78,6 +78,9 @@ function* iterChildren(ir, parentPath) {
78
78
  case "recursionTarget":
79
79
  yield [parentPath, ir.inner];
80
80
  break;
81
+ case "zodDelegate":
82
+ yield [parentPath, ir.inner];
83
+ break;
81
84
  }
82
85
  }
83
86
  function walkIR(ir, currentPath) {
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostic.js","names":[],"sources":["../../src/core/diagnostic.ts"],"sourcesContent":["/**\n * Schema diagnostic utilities for the `check` command.\n * Walks SchemaIR in a single pass and collects per-node diagnostic info.\n */\n\nimport type { SchemaIR } from \"./types.js\";\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport type NodeStatus = \"compiled\" | \"fallback\";\n\nexport interface DiagnosticNode {\n /** SchemaIR type (e.g. \"string\", \"object\", \"fallback\") */\n type: string;\n /** Dot-separated path from root (e.g. \".name\", \".addresses[]\") */\n path: string;\n /** Whether this node is compiled or falls back to Zod */\n status: NodeStatus;\n /** Fallback reason when status is \"fallback\" */\n reason?: string;\n /** Actionable hint for fixing the fallback */\n hint?: string;\n /** Child diagnostic nodes */\n children: DiagnosticNode[];\n}\n\nexport interface DiagnosticResult {\n /** Root diagnostic tree */\n root: DiagnosticNode;\n /** Total leaf node count */\n total: number;\n /** Number of compiled leaf nodes */\n compilable: number;\n /** Coverage percentage (0-100) */\n coveragePct: number;\n /** Whether the schema is eligible for Fast Path */\n fastPathEligible: boolean;\n /** Reason Fast Path is ineligible (when fastPathEligible is false) */\n fastPathBlocker?: string;\n /** Flat list of fallback entries for summary display */\n fallbacks: { reason: string; path: string; hint: string }[];\n}\n\n// ─── Fast Path Eligibility ──────────────────────────────────────────────────\n\n/** Schema types that block Fast Path eligibility. */\nconst FAST_PATH_BLOCKERS = new Set<string>([\"fallback\", \"effect\", \"stringBool\"]);\n\n/** Check if a single node blocks Fast Path (without recursing). */\nfunction detectFastPathBlocker(ir: SchemaIR): string | null {\n if (FAST_PATH_BLOCKERS.has(ir.type)) {\n return ir.type === \"fallback\" ? `fallback (${(ir as { reason: string }).reason})` : ir.type;\n }\n if (\"coerce\" in ir && ir.coerce) {\n return `coerce (${ir.type})`;\n }\n // Value-mutating string checks make the schema slow-path only: overwrite\n // effects (.trim()/.toLowerCase()) rewrite the value and url checks trim it,\n // while the fast path returns input unchanged.\n if (ir.type === \"string\") {\n for (const check of ir.checks) {\n if (check.kind === \"overwrite_effect\") return \"overwrite (.trim()/.toLowerCase())\";\n if (check.kind === \"string_format\" && check.format === \"url\") return \"url (trims value)\";\n }\n }\n return null;\n}\n\n// ─── Hint Generation ────────────────────────────────────────────────────────\n\nconst FALLBACK_HINTS: Record<string, string> = {\n transform: \"Extract transform into a separate post-processing step\",\n refine: \"Replace .refine() with built-in checks (e.g. .min(), .max(), .regex())\",\n superRefine: \"Replace .superRefine() with built-in checks if possible\",\n custom: \"Replace z.custom() with a supported schema type\",\n lazy: \"Ensure the lazy getter resolves to a static schema (recursion compiles automatically)\",\n unsupported: \"This schema type is not yet supported by zod-compiler\",\n coalesced:\n \"Every property falls back to Zod, so the whole object is delegated once (faster than per-field delegation)\",\n};\n\nfunction getHint(reason: string): string {\n return FALLBACK_HINTS[reason] ?? \"Check zod-compiler documentation for supported schemas\";\n}\n\n// ─── Child Iteration (with path computation) ───────────────────────────────\n\n/** Yields [childPath, childIR] pairs for a given SchemaIR node. */\nfunction* iterChildren(ir: SchemaIR, parentPath: string): Generator<[string, SchemaIR]> {\n switch (ir.type) {\n case \"object\":\n for (const [key, child] of Object.entries(ir.properties)) {\n yield [`${parentPath}.${key}`, child];\n }\n break;\n case \"array\":\n yield [`${parentPath}[]`, ir.element];\n break;\n case \"tuple\":\n for (const [i, item] of ir.items.entries()) {\n yield [`${parentPath}[${i}]`, item];\n }\n if (ir.rest) yield [`${parentPath}[...rest]`, ir.rest];\n break;\n case \"record\":\n yield [`${parentPath}[key]`, ir.keyType];\n yield [`${parentPath}[value]`, ir.valueType];\n break;\n case \"set\":\n yield [`${parentPath}[element]`, ir.valueType];\n break;\n case \"map\":\n yield [`${parentPath}[key]`, ir.keyType];\n yield [`${parentPath}[value]`, ir.valueType];\n break;\n case \"union\":\n case \"discriminatedUnion\":\n for (const [i, opt] of ir.options.entries()) {\n yield [`${parentPath}[${i}]`, opt];\n }\n break;\n case \"intersection\":\n yield [`${parentPath}[left]`, ir.left];\n yield [`${parentPath}[right]`, ir.right];\n break;\n case \"optional\":\n case \"nullable\":\n case \"readonly\":\n yield [parentPath, ir.inner];\n break;\n case \"default\":\n case \"catch\":\n yield [parentPath, ir.inner];\n break;\n case \"pipe\":\n yield [`${parentPath}[in]`, ir.in];\n yield [`${parentPath}[out]`, ir.out];\n break;\n case \"effect\":\n yield [`${parentPath}[inner]`, ir.inner];\n break;\n case \"recursionTarget\":\n // Transparent wrapper around a non-root recursive sub-schema: descend so\n // coverage and any inner fallbacks are reported at the same path.\n yield [parentPath, ir.inner];\n break;\n }\n}\n\n// ─── Single-Pass Diagnostic Walker ──────────────────────────────────────────\n\ninterface WalkResult {\n node: DiagnosticNode;\n total: number;\n compilable: number;\n fallbacks: { reason: string; path: string; hint: string }[];\n /** First Fast Path blocker found in this subtree, or null if eligible. */\n fastPathBlocker: string | null;\n}\n\nfunction walkIR(ir: SchemaIR, currentPath: string): WalkResult {\n // Fallback leaf\n if (ir.type === \"fallback\") {\n const hint = getHint(ir.reason);\n const nodePath = currentPath || \"(root)\";\n return {\n node: {\n type: ir.type,\n path: nodePath,\n status: \"fallback\",\n reason: ir.reason,\n hint,\n children: [],\n },\n total: 1,\n compilable: 0,\n fallbacks: [{ reason: ir.reason, path: nodePath, hint }],\n fastPathBlocker: `fallback (${ir.reason})`,\n };\n }\n\n // Check if this node itself blocks Fast Path\n let fastPathBlocker = detectFastPathBlocker(ir);\n\n // Recurse into children\n const children: DiagnosticNode[] = [];\n let total = 0;\n let compilable = 0;\n const fallbacks: { reason: string; path: string; hint: string }[] = [];\n\n for (const [childPath, child] of iterChildren(ir, currentPath)) {\n const r = walkIR(child, childPath);\n children.push(r.node);\n total += r.total;\n compilable += r.compilable;\n fallbacks.push(...r.fallbacks);\n if (fastPathBlocker === null && r.fastPathBlocker !== null) {\n fastPathBlocker = r.fastPathBlocker;\n }\n }\n\n // Leaf node (no children produced by iterChildren)\n if (children.length === 0) {\n total = 1;\n compilable = 1;\n }\n\n return {\n node: {\n type: ir.type,\n path: currentPath || \"(root)\",\n status: \"compiled\",\n children,\n },\n total,\n compilable,\n fallbacks,\n fastPathBlocker,\n };\n}\n\n/**\n * Diagnose a SchemaIR in a single pass.\n * Returns a diagnostic tree with coverage stats, Fast Path eligibility, and actionable hints.\n */\nexport function diagnoseSchema(ir: SchemaIR): DiagnosticResult {\n const { node: root, total, compilable, fallbacks, fastPathBlocker } = walkIR(ir, \"\");\n const coveragePct = total > 0 ? Math.round((compilable / total) * 100) : 100;\n\n const result: DiagnosticResult = {\n root,\n total,\n compilable,\n coveragePct,\n fastPathEligible: fastPathBlocker === null,\n fallbacks,\n };\n if (fastPathBlocker !== null) {\n result.fastPathBlocker = fastPathBlocker;\n }\n return result;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"diagnostic.js","names":[],"sources":["../../src/core/diagnostic.ts"],"sourcesContent":["/**\n * Schema diagnostic utilities for the `check` command.\n * Walks SchemaIR in a single pass and collects per-node diagnostic info.\n */\n\nimport type { SchemaIR } from \"./types.js\";\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\nexport type NodeStatus = \"compiled\" | \"fallback\";\n\nexport interface DiagnosticNode {\n /** SchemaIR type (e.g. \"string\", \"object\", \"fallback\") */\n type: string;\n /** Dot-separated path from root (e.g. \".name\", \".addresses[]\") */\n path: string;\n /** Whether this node is compiled or falls back to Zod */\n status: NodeStatus;\n /** Fallback reason when status is \"fallback\" */\n reason?: string;\n /** Actionable hint for fixing the fallback */\n hint?: string;\n /** Child diagnostic nodes */\n children: DiagnosticNode[];\n}\n\nexport interface DiagnosticResult {\n /** Root diagnostic tree */\n root: DiagnosticNode;\n /** Total leaf node count */\n total: number;\n /** Number of compiled leaf nodes */\n compilable: number;\n /** Coverage percentage (0-100) */\n coveragePct: number;\n /** Whether the schema is eligible for Fast Path */\n fastPathEligible: boolean;\n /** Reason Fast Path is ineligible (when fastPathEligible is false) */\n fastPathBlocker?: string;\n /** Flat list of fallback entries for summary display */\n fallbacks: { reason: string; path: string; hint: string }[];\n}\n\n// ─── Fast Path Eligibility ──────────────────────────────────────────────────\n\n/** Schema types that block Fast Path eligibility. */\nconst FAST_PATH_BLOCKERS = new Set<string>([\"fallback\", \"effect\", \"stringBool\"]);\n\n/** Check if a single node blocks Fast Path (without recursing). */\nfunction detectFastPathBlocker(ir: SchemaIR): string | null {\n if (FAST_PATH_BLOCKERS.has(ir.type)) {\n return ir.type === \"fallback\" ? `fallback (${(ir as { reason: string }).reason})` : ir.type;\n }\n if (\"coerce\" in ir && ir.coerce) {\n return `coerce (${ir.type})`;\n }\n // Value-mutating string checks make the schema slow-path only: overwrite\n // effects (.trim()/.toLowerCase()) rewrite the value and url checks trim it,\n // while the fast path returns input unchanged.\n if (ir.type === \"string\") {\n for (const check of ir.checks) {\n if (check.kind === \"overwrite_effect\") return \"overwrite (.trim()/.toLowerCase())\";\n if (check.kind === \"string_format\" && check.format === \"url\") return \"url (trims value)\";\n }\n }\n return null;\n}\n\n// ─── Hint Generation ────────────────────────────────────────────────────────\n\nconst FALLBACK_HINTS: Record<string, string> = {\n transform: \"Extract transform into a separate post-processing step\",\n refine: \"Replace .refine() with built-in checks (e.g. .min(), .max(), .regex())\",\n superRefine: \"Replace .superRefine() with built-in checks if possible\",\n custom: \"Replace z.custom() with a supported schema type\",\n lazy: \"Ensure the lazy getter resolves to a static schema (recursion compiles automatically)\",\n unsupported: \"This schema type is not yet supported by zod-compiler\",\n coalesced:\n \"Every property falls back to Zod, so the whole object is delegated once (faster than per-field delegation)\",\n};\n\nfunction getHint(reason: string): string {\n return FALLBACK_HINTS[reason] ?? \"Check zod-compiler documentation for supported schemas\";\n}\n\n// ─── Child Iteration (with path computation) ───────────────────────────────\n\n/** Yields [childPath, childIR] pairs for a given SchemaIR node. */\nfunction* iterChildren(ir: SchemaIR, parentPath: string): Generator<[string, SchemaIR]> {\n switch (ir.type) {\n case \"object\":\n for (const [key, child] of Object.entries(ir.properties)) {\n yield [`${parentPath}.${key}`, child];\n }\n break;\n case \"array\":\n yield [`${parentPath}[]`, ir.element];\n break;\n case \"tuple\":\n for (const [i, item] of ir.items.entries()) {\n yield [`${parentPath}[${i}]`, item];\n }\n if (ir.rest) yield [`${parentPath}[...rest]`, ir.rest];\n break;\n case \"record\":\n yield [`${parentPath}[key]`, ir.keyType];\n yield [`${parentPath}[value]`, ir.valueType];\n break;\n case \"set\":\n yield [`${parentPath}[element]`, ir.valueType];\n break;\n case \"map\":\n yield [`${parentPath}[key]`, ir.keyType];\n yield [`${parentPath}[value]`, ir.valueType];\n break;\n case \"union\":\n case \"discriminatedUnion\":\n for (const [i, opt] of ir.options.entries()) {\n yield [`${parentPath}[${i}]`, opt];\n }\n break;\n case \"intersection\":\n yield [`${parentPath}[left]`, ir.left];\n yield [`${parentPath}[right]`, ir.right];\n break;\n case \"optional\":\n case \"nullable\":\n case \"readonly\":\n yield [parentPath, ir.inner];\n break;\n case \"default\":\n case \"catch\":\n yield [parentPath, ir.inner];\n break;\n case \"pipe\":\n yield [`${parentPath}[in]`, ir.in];\n yield [`${parentPath}[out]`, ir.out];\n break;\n case \"effect\":\n yield [`${parentPath}[inner]`, ir.inner];\n break;\n case \"recursionTarget\":\n // Transparent wrapper around a non-root recursive sub-schema: descend so\n // coverage and any inner fallbacks are reported at the same path.\n yield [parentPath, ir.inner];\n break;\n case \"zodDelegate\":\n // The success path is compiled; only issue materialization delegates.\n yield [parentPath, ir.inner];\n break;\n }\n}\n\n// ─── Single-Pass Diagnostic Walker ──────────────────────────────────────────\n\ninterface WalkResult {\n node: DiagnosticNode;\n total: number;\n compilable: number;\n fallbacks: { reason: string; path: string; hint: string }[];\n /** First Fast Path blocker found in this subtree, or null if eligible. */\n fastPathBlocker: string | null;\n}\n\nfunction walkIR(ir: SchemaIR, currentPath: string): WalkResult {\n // Fallback leaf\n if (ir.type === \"fallback\") {\n const hint = getHint(ir.reason);\n const nodePath = currentPath || \"(root)\";\n return {\n node: {\n type: ir.type,\n path: nodePath,\n status: \"fallback\",\n reason: ir.reason,\n hint,\n children: [],\n },\n total: 1,\n compilable: 0,\n fallbacks: [{ reason: ir.reason, path: nodePath, hint }],\n fastPathBlocker: `fallback (${ir.reason})`,\n };\n }\n\n // Check if this node itself blocks Fast Path\n let fastPathBlocker = detectFastPathBlocker(ir);\n\n // Recurse into children\n const children: DiagnosticNode[] = [];\n let total = 0;\n let compilable = 0;\n const fallbacks: { reason: string; path: string; hint: string }[] = [];\n\n for (const [childPath, child] of iterChildren(ir, currentPath)) {\n const r = walkIR(child, childPath);\n children.push(r.node);\n total += r.total;\n compilable += r.compilable;\n fallbacks.push(...r.fallbacks);\n if (fastPathBlocker === null && r.fastPathBlocker !== null) {\n fastPathBlocker = r.fastPathBlocker;\n }\n }\n\n // Leaf node (no children produced by iterChildren)\n if (children.length === 0) {\n total = 1;\n compilable = 1;\n }\n\n return {\n node: {\n type: ir.type,\n path: currentPath || \"(root)\",\n status: \"compiled\",\n children,\n },\n total,\n compilable,\n fallbacks,\n fastPathBlocker,\n };\n}\n\n/**\n * Diagnose a SchemaIR in a single pass.\n * Returns a diagnostic tree with coverage stats, Fast Path eligibility, and actionable hints.\n */\nexport function diagnoseSchema(ir: SchemaIR): DiagnosticResult {\n const { node: root, total, compilable, fallbacks, fastPathBlocker } = walkIR(ir, \"\");\n const coveragePct = total > 0 ? Math.round((compilable / total) * 100) : 100;\n\n const result: DiagnosticResult = {\n root,\n total,\n compilable,\n coveragePct,\n fastPathEligible: fastPathBlocker === null,\n fallbacks,\n };\n if (fastPathBlocker !== null) {\n result.fastPathBlocker = fastPathBlocker;\n }\n return result;\n}\n"],"mappings":"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"}
@@ -14,6 +14,12 @@
14
14
  * a promise where zod's synchronous parse raises $ZodAsyncError.
15
15
  */
16
16
  declare function isReferenceablePredicate(fn: unknown): boolean;
17
+ /**
18
+ * Can a callback be invoked with the value alone without hiding Zod's second
19
+ * parse-context argument? `fn.length` is insufficient here: default and rest
20
+ * parameters can observe that argument while still reporting length 0 or 1.
21
+ */
22
+ declare function isContextFreeUnaryCallback(fn: unknown): boolean;
17
23
  /**
18
24
  * A `superRefine`: a `custom` check whose callback takes zod's PAYLOAD rather
19
25
  * than the value, collecting issues instead of returning a verdict. Zod stores
@@ -44,5 +50,5 @@ declare function isPayloadCheck(check: {
44
50
  */
45
51
  declare function tryCompileEffect(fn: unknown): string | undefined;
46
52
  //#endregion
47
- export { isPayloadCheck, isReferenceablePredicate, tryCompileEffect };
53
+ export { isContextFreeUnaryCallback, isPayloadCheck, isReferenceablePredicate, tryCompileEffect };
48
54
  //# sourceMappingURL=effects.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"effects.d.ts","names":[],"sources":["../../../src/core/extract/effects.ts"],"mappings":";;;;;;;;;;;;;;;iBAwDgB,yBAAyB;;;;;;;;;;;;;;iBAsBzB,eAAe;EAC7B;IAAS;IAAiB;MAAQ;MAAc;;IAAkB,SAAS;;;;;;;;iBAe7D,iBAAiB"}
1
+ {"version":3,"file":"effects.d.ts","names":[],"sources":["../../../src/core/extract/effects.ts"],"mappings":";;;;;;;;;;;;;;;iBAwDgB,yBAAyB;;;;;;iBAczB,2BAA2B;;;;;;;;;;;;;;iBAgC3B,eAAe;EAC7B;IAAS;IAAiB;MAAQ;MAAc;;IAAkB,SAAS;;;;;;;;iBAe7D,iBAAiB"}
@@ -52,6 +52,29 @@ function isReferenceablePredicate(fn) {
52
52
  return kind !== "AsyncFunction" && kind !== "GeneratorFunction" && kind !== "AsyncGeneratorFunction";
53
53
  }
54
54
  /**
55
+ * Can a callback be invoked with the value alone without hiding Zod's second
56
+ * parse-context argument? `fn.length` is insufficient here: default and rest
57
+ * parameters can observe that argument while still reporting length 0 or 1.
58
+ */
59
+ function isContextFreeUnaryCallback(fn) {
60
+ if (!isReferenceablePredicate(fn)) return false;
61
+ let ast;
62
+ try {
63
+ ast = parseExpressionAt(fn.toString(), 0, {
64
+ ecmaVersion: "latest",
65
+ sourceType: "module"
66
+ });
67
+ } catch {
68
+ return false;
69
+ }
70
+ const fnNode = ast;
71
+ if ((fnNode.params?.length ?? 0) > 1) return false;
72
+ if (fnNode.params?.some((param) => param.type === "RestElement")) return false;
73
+ const refs = /* @__PURE__ */ new Set();
74
+ if (fnNode.body) collectIdentifierRefs(fnNode.body, refs);
75
+ return !refs.has("arguments");
76
+ }
77
+ /**
55
78
  * A `superRefine`: a `custom` check whose callback takes zod's PAYLOAD rather
56
79
  * than the value, collecting issues instead of returning a verdict. Zod stores
57
80
  * it on the check instance (`_zod.check`) rather than `def.fn`.
@@ -166,6 +189,6 @@ function collectIdentifierRefs(node, refs) {
166
189
  }
167
190
  }
168
191
  //#endregion
169
- export { isPayloadCheck, isReferenceablePredicate, tryCompileEffect };
192
+ export { isContextFreeUnaryCallback, isPayloadCheck, isReferenceablePredicate, tryCompileEffect };
170
193
 
171
194
  //# sourceMappingURL=effects.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"effects.js","names":[],"sources":["../../../src/core/extract/effects.ts"],"sourcesContent":["/**\n * Effect compilation utilities.\n * Uses fn.toString() to extract function source text from live Zod schema references,\n * then classifies whether the function can be safely inlined (zero external captures).\n */\n\nimport type { Node } from \"acorn\";\nimport { parseExpressionAt } from \"acorn\";\n\n// Well-known globals that are safe to reference in inlined functions\nconst SAFE_GLOBALS = new Set([\n \"undefined\",\n \"null\",\n \"NaN\",\n \"Infinity\",\n \"Math\",\n \"Number\",\n \"String\",\n \"Boolean\",\n \"Array\",\n \"Object\",\n \"JSON\",\n \"Date\",\n \"RegExp\",\n \"parseInt\",\n \"parseFloat\",\n \"isNaN\",\n \"isFinite\",\n \"encodeURI\",\n \"decodeURI\",\n \"encodeURIComponent\",\n \"decodeURIComponent\",\n \"BigInt\",\n \"Symbol\",\n \"Map\",\n \"Set\",\n \"Error\",\n \"TypeError\",\n \"RangeError\",\n \"SyntaxError\",\n \"ReferenceError\",\n \"Promise\",\n \"globalThis\",\n \"true\",\n \"false\",\n]);\n\n/**\n * Can this refine predicate be CALLED by reference from generated code?\n *\n * Weaker than {@link tryCompileEffect}: the callback keeps its own closure, so\n * captures are fine — only shapes whose semantics the generated call could not\n * reproduce are rejected. A second parameter means the zod `ctx` protocol\n * (superRefine-style issue collection), and an async/generator function returns\n * a promise where zod's synchronous parse raises $ZodAsyncError.\n */\nexport function isReferenceablePredicate(fn: unknown): boolean {\n if (typeof fn !== \"function\") return false;\n if (fn.length >= 2) return false;\n const kind = fn.constructor?.name;\n return (\n kind !== \"AsyncFunction\" && kind !== \"GeneratorFunction\" && kind !== \"AsyncGeneratorFunction\"\n );\n}\n\n/**\n * A `superRefine`: a `custom` check whose callback takes zod's PAYLOAD rather\n * than the value, collecting issues instead of returning a verdict. Zod stores\n * it on the check instance (`_zod.check`) rather than `def.fn`.\n *\n * Narrowed to zod's OWN wrapper, which is what `_superRefine` builds and the\n * only thing whose payload handling is modelled here: it installs `addIssue`,\n * so every issue the user adds has been through `util.issue`. The trait marks a\n * genuine `$ZodCheck` instance and so excludes a raw `.check(fn)` — shape-\n * identical, but there the callback IS the user's, holding the payload\n * unmediated. A `when` predicate is likewise refused: it makes zod run the\n * check conditionally, where generated code runs it always.\n */\nexport function isPayloadCheck(check: {\n _zod?: { check?: unknown; def?: { fn?: unknown; when?: unknown }; traits?: Set<string> };\n}): boolean {\n return (\n check._zod?.def?.fn === undefined &&\n check._zod?.def?.when === undefined &&\n typeof check._zod?.check === \"function\" &&\n check._zod?.traits?.has(\"$ZodCheck\") === true\n );\n}\n\n/**\n * Try to compile a function into an inlineable source string.\n * Returns the function source if it's a zero-capture function (safe to inline),\n * or undefined if it cannot be compiled (async, has captures, or parse failure).\n */\nexport function tryCompileEffect(fn: unknown): string | undefined {\n if (typeof fn !== \"function\") return undefined;\n\n const source = fn.toString();\n\n // Quick reject: native functions\n if (source.includes(\"[native code]\")) return undefined;\n\n let ast: Node;\n try {\n ast = parseExpressionAt(source, 0, {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n });\n } catch {\n // Parse failed — likely TypeScript annotations or unsupported syntax\n return undefined;\n }\n\n // Reject async and generator functions\n if (\"async\" in ast && (ast as { async?: boolean }).async) return undefined;\n if (\"generator\" in ast && (ast as { generator?: boolean }).generator) return undefined;\n\n // Collect parameter names\n const params = new Set<string>();\n const fnNode = ast as {\n type: string;\n params?: Node[];\n body?: Node;\n };\n\n // Reject functions with 2+ required parameters (uses Zod ctx argument).\n // transform(value, ctx) and superRefine(value, ctx) rely on ctx for\n // issue collection, which cannot be reproduced in compiled output.\n // fn.length counts parameters before the first default, so (v, base=10)\n // has length 1 (allowed) while (v, ctx) has length 2 (rejected).\n if ((fn as { length: number }).length >= 2) return undefined;\n\n if (fnNode.params) {\n for (const param of fnNode.params) {\n collectBindingNames(param, params);\n }\n }\n\n // Collect local variable declarations in the body\n const locals = new Set<string>();\n if (fnNode.body && fnNode.body.type === \"BlockStatement\") {\n collectLocals(fnNode.body, locals);\n }\n\n // Collect all identifier references in the body\n const refs = new Set<string>();\n if (fnNode.body) {\n collectIdentifierRefs(fnNode.body, refs);\n }\n\n // Check for external captures\n for (const ref of refs) {\n if (!params.has(ref) && !locals.has(ref) && !SAFE_GLOBALS.has(ref)) {\n return undefined; // Has external capture\n }\n }\n\n return source;\n}\n\n/** Collect binding names from a parameter pattern (handles destructuring). */\nfunction collectBindingNames(node: Node, names: Set<string>): void {\n switch (node.type) {\n case \"Identifier\":\n names.add((node as Node & { name: string }).name);\n break;\n case \"AssignmentPattern\":\n collectBindingNames((node as Node & { left: Node }).left, names);\n break;\n case \"ObjectPattern\":\n for (const prop of (node as Node & { properties: Node[] }).properties) {\n if (prop.type === \"RestElement\") {\n collectBindingNames((prop as Node & { argument: Node }).argument, names);\n } else {\n collectBindingNames((prop as Node & { value: Node }).value, names);\n }\n }\n break;\n case \"ArrayPattern\":\n for (const elem of (node as Node & { elements: (Node | null)[] }).elements) {\n if (elem) collectBindingNames(elem, names);\n }\n break;\n case \"RestElement\":\n collectBindingNames((node as Node & { argument: Node }).argument, names);\n break;\n }\n}\n\n/** Collect locally declared variable names from a block body. */\nfunction collectLocals(body: Node, locals: Set<string>): void {\n const block = body as Node & { body: Node[] };\n if (!block.body) return;\n for (const stmt of block.body) {\n if (stmt.type === \"VariableDeclaration\") {\n for (const decl of (stmt as Node & { declarations: Node[] }).declarations) {\n const id = (decl as Node & { id: Node }).id;\n collectBindingNames(id, locals);\n }\n }\n }\n}\n\n/** Collect all identifier references in an AST subtree. */\nfunction collectIdentifierRefs(node: Node, refs: Set<string>): void {\n if (node.type === \"Identifier\") {\n refs.add((node as Node & { name: string }).name);\n return;\n }\n\n // `this` in arrow functions captures from enclosing scope — treat as external capture\n if (node.type === \"ThisExpression\") {\n refs.add(\"this\");\n return;\n }\n\n // Skip property access identifiers (obj.prop — \"prop\" is not a reference)\n if (node.type === \"MemberExpression\") {\n const member = node as Node & { object: Node; property: Node; computed: boolean };\n collectIdentifierRefs(member.object, refs);\n if (member.computed) {\n collectIdentifierRefs(member.property, refs);\n }\n return;\n }\n\n // Object literal properties: skip non-computed keys, only walk values\n if (node.type === \"Property\") {\n const prop = node as Node & { key: Node; value: Node; computed: boolean };\n if (prop.computed) {\n collectIdentifierRefs(prop.key, refs);\n }\n collectIdentifierRefs(prop.value, refs);\n return;\n }\n\n // Skip binding positions in variable declarations, function params, etc.\n if (node.type === \"VariableDeclarator\") {\n const decl = node as Node & { init: Node | null };\n if (decl.init) collectIdentifierRefs(decl.init, refs);\n return;\n }\n\n // Recursively walk all child nodes\n for (const key of Object.keys(node)) {\n if (key === \"type\" || key === \"start\" || key === \"end\") continue;\n const child = (node as unknown as Record<string, unknown>)[key];\n if (child && typeof child === \"object\") {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n collectIdentifierRefs(item as Node, refs);\n }\n }\n } else if (\"type\" in child) {\n collectIdentifierRefs(child as Node, refs);\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"effects.js","names":[],"sources":["../../../src/core/extract/effects.ts"],"sourcesContent":["/**\n * Effect compilation utilities.\n * Uses fn.toString() to extract function source text from live Zod schema references,\n * then classifies whether the function can be safely inlined (zero external captures).\n */\n\nimport type { Node } from \"acorn\";\nimport { parseExpressionAt } from \"acorn\";\n\n// Well-known globals that are safe to reference in inlined functions\nconst SAFE_GLOBALS = new Set([\n \"undefined\",\n \"null\",\n \"NaN\",\n \"Infinity\",\n \"Math\",\n \"Number\",\n \"String\",\n \"Boolean\",\n \"Array\",\n \"Object\",\n \"JSON\",\n \"Date\",\n \"RegExp\",\n \"parseInt\",\n \"parseFloat\",\n \"isNaN\",\n \"isFinite\",\n \"encodeURI\",\n \"decodeURI\",\n \"encodeURIComponent\",\n \"decodeURIComponent\",\n \"BigInt\",\n \"Symbol\",\n \"Map\",\n \"Set\",\n \"Error\",\n \"TypeError\",\n \"RangeError\",\n \"SyntaxError\",\n \"ReferenceError\",\n \"Promise\",\n \"globalThis\",\n \"true\",\n \"false\",\n]);\n\n/**\n * Can this refine predicate be CALLED by reference from generated code?\n *\n * Weaker than {@link tryCompileEffect}: the callback keeps its own closure, so\n * captures are fine — only shapes whose semantics the generated call could not\n * reproduce are rejected. A second parameter means the zod `ctx` protocol\n * (superRefine-style issue collection), and an async/generator function returns\n * a promise where zod's synchronous parse raises $ZodAsyncError.\n */\nexport function isReferenceablePredicate(fn: unknown): boolean {\n if (typeof fn !== \"function\") return false;\n if (fn.length >= 2) return false;\n const kind = fn.constructor?.name;\n return (\n kind !== \"AsyncFunction\" && kind !== \"GeneratorFunction\" && kind !== \"AsyncGeneratorFunction\"\n );\n}\n\n/**\n * Can a callback be invoked with the value alone without hiding Zod's second\n * parse-context argument? `fn.length` is insufficient here: default and rest\n * parameters can observe that argument while still reporting length 0 or 1.\n */\nexport function isContextFreeUnaryCallback(fn: unknown): boolean {\n if (!isReferenceablePredicate(fn)) return false;\n let ast: Node;\n try {\n ast = parseExpressionAt((fn as Function).toString(), 0, {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n });\n } catch {\n return false;\n }\n const fnNode = ast as { params?: Node[]; body?: Node };\n if ((fnNode.params?.length ?? 0) > 1) return false;\n if (fnNode.params?.some((param) => param.type === \"RestElement\")) return false;\n const refs = new Set<string>();\n if (fnNode.body) collectIdentifierRefs(fnNode.body, refs);\n return !refs.has(\"arguments\");\n}\n\n/**\n * A `superRefine`: a `custom` check whose callback takes zod's PAYLOAD rather\n * than the value, collecting issues instead of returning a verdict. Zod stores\n * it on the check instance (`_zod.check`) rather than `def.fn`.\n *\n * Narrowed to zod's OWN wrapper, which is what `_superRefine` builds and the\n * only thing whose payload handling is modelled here: it installs `addIssue`,\n * so every issue the user adds has been through `util.issue`. The trait marks a\n * genuine `$ZodCheck` instance and so excludes a raw `.check(fn)` — shape-\n * identical, but there the callback IS the user's, holding the payload\n * unmediated. A `when` predicate is likewise refused: it makes zod run the\n * check conditionally, where generated code runs it always.\n */\nexport function isPayloadCheck(check: {\n _zod?: { check?: unknown; def?: { fn?: unknown; when?: unknown }; traits?: Set<string> };\n}): boolean {\n return (\n check._zod?.def?.fn === undefined &&\n check._zod?.def?.when === undefined &&\n typeof check._zod?.check === \"function\" &&\n check._zod?.traits?.has(\"$ZodCheck\") === true\n );\n}\n\n/**\n * Try to compile a function into an inlineable source string.\n * Returns the function source if it's a zero-capture function (safe to inline),\n * or undefined if it cannot be compiled (async, has captures, or parse failure).\n */\nexport function tryCompileEffect(fn: unknown): string | undefined {\n if (typeof fn !== \"function\") return undefined;\n\n const source = fn.toString();\n\n // Quick reject: native functions\n if (source.includes(\"[native code]\")) return undefined;\n\n let ast: Node;\n try {\n ast = parseExpressionAt(source, 0, {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n });\n } catch {\n // Parse failed — likely TypeScript annotations or unsupported syntax\n return undefined;\n }\n\n // Reject async and generator functions\n if (\"async\" in ast && (ast as { async?: boolean }).async) return undefined;\n if (\"generator\" in ast && (ast as { generator?: boolean }).generator) return undefined;\n\n // Collect parameter names\n const params = new Set<string>();\n const fnNode = ast as {\n type: string;\n params?: Node[];\n body?: Node;\n };\n\n // Reject functions with 2+ required parameters (uses Zod ctx argument).\n // transform(value, ctx) and superRefine(value, ctx) rely on ctx for\n // issue collection, which cannot be reproduced in compiled output.\n // fn.length counts parameters before the first default, so (v, base=10)\n // has length 1 (allowed) while (v, ctx) has length 2 (rejected).\n if ((fn as { length: number }).length >= 2) return undefined;\n\n if (fnNode.params) {\n for (const param of fnNode.params) {\n collectBindingNames(param, params);\n }\n }\n\n // Collect local variable declarations in the body\n const locals = new Set<string>();\n if (fnNode.body && fnNode.body.type === \"BlockStatement\") {\n collectLocals(fnNode.body, locals);\n }\n\n // Collect all identifier references in the body\n const refs = new Set<string>();\n if (fnNode.body) {\n collectIdentifierRefs(fnNode.body, refs);\n }\n\n // Check for external captures\n for (const ref of refs) {\n if (!params.has(ref) && !locals.has(ref) && !SAFE_GLOBALS.has(ref)) {\n return undefined; // Has external capture\n }\n }\n\n return source;\n}\n\n/** Collect binding names from a parameter pattern (handles destructuring). */\nfunction collectBindingNames(node: Node, names: Set<string>): void {\n switch (node.type) {\n case \"Identifier\":\n names.add((node as Node & { name: string }).name);\n break;\n case \"AssignmentPattern\":\n collectBindingNames((node as Node & { left: Node }).left, names);\n break;\n case \"ObjectPattern\":\n for (const prop of (node as Node & { properties: Node[] }).properties) {\n if (prop.type === \"RestElement\") {\n collectBindingNames((prop as Node & { argument: Node }).argument, names);\n } else {\n collectBindingNames((prop as Node & { value: Node }).value, names);\n }\n }\n break;\n case \"ArrayPattern\":\n for (const elem of (node as Node & { elements: (Node | null)[] }).elements) {\n if (elem) collectBindingNames(elem, names);\n }\n break;\n case \"RestElement\":\n collectBindingNames((node as Node & { argument: Node }).argument, names);\n break;\n }\n}\n\n/** Collect locally declared variable names from a block body. */\nfunction collectLocals(body: Node, locals: Set<string>): void {\n const block = body as Node & { body: Node[] };\n if (!block.body) return;\n for (const stmt of block.body) {\n if (stmt.type === \"VariableDeclaration\") {\n for (const decl of (stmt as Node & { declarations: Node[] }).declarations) {\n const id = (decl as Node & { id: Node }).id;\n collectBindingNames(id, locals);\n }\n }\n }\n}\n\n/** Collect all identifier references in an AST subtree. */\nfunction collectIdentifierRefs(node: Node, refs: Set<string>): void {\n if (node.type === \"Identifier\") {\n refs.add((node as Node & { name: string }).name);\n return;\n }\n\n // `this` in arrow functions captures from enclosing scope — treat as external capture\n if (node.type === \"ThisExpression\") {\n refs.add(\"this\");\n return;\n }\n\n // Skip property access identifiers (obj.prop — \"prop\" is not a reference)\n if (node.type === \"MemberExpression\") {\n const member = node as Node & { object: Node; property: Node; computed: boolean };\n collectIdentifierRefs(member.object, refs);\n if (member.computed) {\n collectIdentifierRefs(member.property, refs);\n }\n return;\n }\n\n // Object literal properties: skip non-computed keys, only walk values\n if (node.type === \"Property\") {\n const prop = node as Node & { key: Node; value: Node; computed: boolean };\n if (prop.computed) {\n collectIdentifierRefs(prop.key, refs);\n }\n collectIdentifierRefs(prop.value, refs);\n return;\n }\n\n // Skip binding positions in variable declarations, function params, etc.\n if (node.type === \"VariableDeclarator\") {\n const decl = node as Node & { init: Node | null };\n if (decl.init) collectIdentifierRefs(decl.init, refs);\n return;\n }\n\n // Recursively walk all child nodes\n for (const key of Object.keys(node)) {\n if (key === \"type\" || key === \"start\" || key === \"end\") continue;\n const child = (node as unknown as Record<string, unknown>)[key];\n if (child && typeof child === \"object\") {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n collectIdentifierRefs(item as Node, refs);\n }\n }\n } else if (\"type\" in child) {\n collectIdentifierRefs(child as Node, refs);\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"intersection.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/intersection.ts"],"mappings":";;cAGa,qBAAqB"}
1
+ {"version":3,"file":"intersection.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/intersection.ts"],"mappings":";;cAIa,qBAAqB"}
@@ -3,6 +3,19 @@ import { hasMutation } from "../../codegen/context.js";
3
3
  const extractIntersection = (def, ctx) => {
4
4
  const left = ctx.visit(def.left, "._zod.def.left");
5
5
  const right = ctx.visit(def.right, "._zod.def.right");
6
+ const merged = mergeDisjointStripObjects(left, right);
7
+ if (merged !== null && ctx.refs) {
8
+ const refIndex = ctx.refs.length;
9
+ ctx.refs.push({
10
+ schema: ctx.schema,
11
+ accessPath: ctx.path
12
+ });
13
+ return {
14
+ type: "zodDelegate",
15
+ inner: merged,
16
+ refIndex
17
+ };
18
+ }
6
19
  if (hasMutation(left) || hasMutation(right)) return ctx.fallback("unsupported");
7
20
  return {
8
21
  type: "intersection",
@@ -10,6 +23,25 @@ const extractIntersection = (def, ctx) => {
10
23
  right
11
24
  };
12
25
  };
26
+ /** Merge only the object-intersection shape whose equivalence is statically obvious. */
27
+ function mergeDisjointStripObjects(left, right) {
28
+ if (left.type !== "object" || right.type !== "object") return null;
29
+ if (left.stripUnknownKeys !== true || right.stripUnknownKeys !== true) return null;
30
+ if (left.strict === true || right.strict === true || left.catchall !== void 0 || right.catchall !== void 0 || left.checks !== void 0 || right.checks !== void 0 || left.suppressAbsentKeys !== void 0 || right.suppressAbsentKeys !== void 0) return null;
31
+ const leftKeys = Object.keys(left.properties);
32
+ if (leftKeys.some((key) => Object.hasOwn(right.properties, key))) return null;
33
+ const properties = Object.create(null);
34
+ for (const key of leftKeys) properties[key] = left.properties[key];
35
+ const rightKeys = Object.keys(right.properties);
36
+ for (const key of rightKeys) properties[key] = right.properties[key];
37
+ const expectedOrder = [...leftKeys, ...rightKeys];
38
+ if (!Object.keys(properties).every((key, index) => key === expectedOrder[index])) return null;
39
+ return {
40
+ type: "object",
41
+ properties,
42
+ stripUnknownKeys: true
43
+ };
44
+ }
13
45
  //#endregion
14
46
  export { extractIntersection };
15
47
 
@@ -1 +1 @@
1
- {"version":3,"file":"intersection.js","names":[],"sources":["../../../../src/core/extract/extractors/intersection.ts"],"sourcesContent":["import { hasMutation } from \"../../codegen/context.js\";\nimport type { Extractor } from \"../types.js\";\n\nexport const extractIntersection: Extractor = (def, ctx) => {\n const left = ctx.visit(def.left, \"._zod.def.left\");\n const right = ctx.visit(def.right, \"._zod.def.right\");\n // Zod validates both sides on the ORIGINAL input and MERGES the results,\n // throwing on unmergable conflicts. The compiled generator runs the sides\n // sequentially on the same value — equivalent only when neither side\n // rewrites it. Mutating sides (coerce, trim, defaults, ...) delegate to Zod.\n if (hasMutation(left) || hasMutation(right)) {\n return ctx.fallback(\"unsupported\");\n }\n return { type: \"intersection\", left, right };\n};\n"],"mappings":";;AAGA,MAAa,uBAAkC,KAAK,QAAQ;CAC1D,MAAM,OAAO,IAAI,MAAM,IAAI,MAAM,gBAAgB;CACjD,MAAM,QAAQ,IAAI,MAAM,IAAI,OAAO,iBAAiB;CAKpD,IAAI,YAAY,IAAI,KAAK,YAAY,KAAK,GACxC,OAAO,IAAI,SAAS,aAAa;CAEnC,OAAO;EAAE,MAAM;EAAgB;EAAM;CAAM;AAC7C"}
1
+ {"version":3,"file":"intersection.js","names":[],"sources":["../../../../src/core/extract/extractors/intersection.ts"],"sourcesContent":["import { hasMutation } from \"../../codegen/context.js\";\nimport type { ObjectIR, SchemaIR } from \"../../types.js\";\nimport type { Extractor } from \"../types.js\";\n\nexport const extractIntersection: Extractor = (def, ctx) => {\n const left = ctx.visit(def.left, \"._zod.def.left\");\n const right = ctx.visit(def.right, \"._zod.def.right\");\n\n // The everyday `.and()` case: two default z.object() shapes with disjoint\n // keys. Zod parses each against the original input, strips each side to its\n // own keys, then merges the two outputs. A single object over the union of\n // those keys has identical acceptance and output semantics. The pristine\n // intersection remains the source of truth for cold-path issues (notably, a\n // non-object input produces one issue per side).\n const merged = mergeDisjointStripObjects(left, right);\n if (merged !== null && ctx.refs) {\n const refIndex = ctx.refs.length;\n ctx.refs.push({ schema: ctx.schema, accessPath: ctx.path });\n return { type: \"zodDelegate\", inner: merged, refIndex };\n }\n\n // Zod validates both sides on the ORIGINAL input and MERGES the results,\n // throwing on unmergable conflicts. The compiled generator runs the sides\n // sequentially on the same value — equivalent only when neither side\n // rewrites it. Mutating sides (coerce, trim, defaults, ...) delegate to Zod.\n if (hasMutation(left) || hasMutation(right)) {\n return ctx.fallback(\"unsupported\");\n }\n return { type: \"intersection\", left, right };\n};\n\n/** Merge only the object-intersection shape whose equivalence is statically obvious. */\nfunction mergeDisjointStripObjects(left: SchemaIR, right: SchemaIR): ObjectIR | null {\n if (left.type !== \"object\" || right.type !== \"object\") return null;\n if (left.stripUnknownKeys !== true || right.stripUnknownKeys !== true) return null;\n if (\n left.strict === true ||\n right.strict === true ||\n left.catchall !== undefined ||\n right.catchall !== undefined ||\n left.checks !== undefined ||\n right.checks !== undefined ||\n left.suppressAbsentKeys !== undefined ||\n right.suppressAbsentKeys !== undefined\n ) {\n return null;\n }\n\n const leftKeys = Object.keys(left.properties);\n if (leftKeys.some((key) => Object.hasOwn(right.properties, key))) return null;\n\n const properties: Record<string, SchemaIR> = Object.create(null) as Record<string, SchemaIR>;\n for (const key of leftKeys) properties[key] = left.properties[key] as SchemaIR;\n const rightKeys = Object.keys(right.properties);\n for (const key of rightKeys) {\n properties[key] = right.properties[key] as SchemaIR;\n }\n // Integer-like keys are enumerated before other strings regardless of\n // insertion order. If that changes left-then-right traversal, a successful\n // parse could observe transform/refine callbacks in a different order.\n const expectedOrder = [...leftKeys, ...rightKeys];\n if (!Object.keys(properties).every((key, index) => key === expectedOrder[index])) return null;\n return { type: \"object\", properties, stripUnknownKeys: true };\n}\n"],"mappings":";;AAIA,MAAa,uBAAkC,KAAK,QAAQ;CAC1D,MAAM,OAAO,IAAI,MAAM,IAAI,MAAM,gBAAgB;CACjD,MAAM,QAAQ,IAAI,MAAM,IAAI,OAAO,iBAAiB;CAQpD,MAAM,SAAS,0BAA0B,MAAM,KAAK;CACpD,IAAI,WAAW,QAAQ,IAAI,MAAM;EAC/B,MAAM,WAAW,IAAI,KAAK;EAC1B,IAAI,KAAK,KAAK;GAAE,QAAQ,IAAI;GAAQ,YAAY,IAAI;EAAK,CAAC;EAC1D,OAAO;GAAE,MAAM;GAAe,OAAO;GAAQ;EAAS;CACxD;CAMA,IAAI,YAAY,IAAI,KAAK,YAAY,KAAK,GACxC,OAAO,IAAI,SAAS,aAAa;CAEnC,OAAO;EAAE,MAAM;EAAgB;EAAM;CAAM;AAC7C;;AAGA,SAAS,0BAA0B,MAAgB,OAAkC;CACnF,IAAI,KAAK,SAAS,YAAY,MAAM,SAAS,UAAU,OAAO;CAC9D,IAAI,KAAK,qBAAqB,QAAQ,MAAM,qBAAqB,MAAM,OAAO;CAC9E,IACE,KAAK,WAAW,QAChB,MAAM,WAAW,QACjB,KAAK,aAAa,KAAA,KAClB,MAAM,aAAa,KAAA,KACnB,KAAK,WAAW,KAAA,KAChB,MAAM,WAAW,KAAA,KACjB,KAAK,uBAAuB,KAAA,KAC5B,MAAM,uBAAuB,KAAA,GAE7B,OAAO;CAGT,MAAM,WAAW,OAAO,KAAK,KAAK,UAAU;CAC5C,IAAI,SAAS,MAAM,QAAQ,OAAO,OAAO,MAAM,YAAY,GAAG,CAAC,GAAG,OAAO;CAEzE,MAAM,aAAuC,OAAO,OAAO,IAAI;CAC/D,KAAK,MAAM,OAAO,UAAU,WAAW,OAAO,KAAK,WAAW;CAC9D,MAAM,YAAY,OAAO,KAAK,MAAM,UAAU;CAC9C,KAAK,MAAM,OAAO,WAChB,WAAW,OAAO,MAAM,WAAW;CAKrC,MAAM,gBAAgB,CAAC,GAAG,UAAU,GAAG,SAAS;CAChD,IAAI,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,OAAO,KAAK,UAAU,QAAQ,cAAc,MAAM,GAAG,OAAO;CACzF,OAAO;EAAE,MAAM;EAAU;EAAY,kBAAkB;CAAK;AAC9D"}