zod-compiler 1.25.0 → 1.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core/iife.js CHANGED
@@ -100,10 +100,11 @@ const FIN_DEFERRED_DECL = "function __zcFinD(f,inp){return new __ZcFail(null,f,i
100
100
  * path is produced by the retained Zod schema itself (`zod` is the source of
101
101
  * truth, so the issues are byte-identical — no second validation engine).
102
102
  *
103
- * `_z` is the schema's PRISTINE bound safeParse (`__rf[N].safeParse.bind(...)`,
104
- * captured pre-`__zcMkv` by emitRfDelegate — see context.ts — so it is zod's
105
- * own implementation, never the compiled delegate, avoiding infinite
106
- * recursion). The zod parse is deferred until `.error` is read and cached, so
103
+ * `_z` is the schema's PRISTINE safeParse method and `_r` is its receiver. Both
104
+ * are captured pre-`__zcMkv` by emitRfMethod — see context.ts — so the method is
105
+ * zod's own implementation, never the compiled delegate, avoiding infinite
106
+ * recursion without allocating a bound function. The zod parse is deferred
107
+ * until `.error` is read and cached, so
107
108
  * the common `safeParse(x).success`/`.is(x)` checks on invalid input cost only
108
109
  * the fast check (zod never runs) — the same deferral boundary `__zcFinD`
109
110
  * establishes for the compiled slow walk. Sound because compact mode is gated
@@ -114,9 +115,9 @@ const FIN_DEFERRED_DECL = "function __zcFinD(f,inp){return new __ZcFail(null,f,i
114
115
  * re-wrap — zod already finalized it), so a delegated failure is exactly what
115
116
  * the unaltered schema would have produced.
116
117
  */
117
- const FAILZ_CLASS_DECL = "function __ZcFailZ(z,i){this.success=false;this._z=z;this._i=i;this._c=undefined;}Object.defineProperty(__ZcFailZ.prototype,\"error\",{configurable:true,get:function(){return this._c||(this._c=this._z(this._i).error);}});";
118
- /** Compact-mode finalizer: wrap a pristine bound safeParse + input into a lazy delegated failure. */
119
- const FINZ_DECL = "function __zcFinZ(z,i){return new __ZcFailZ(z,i);}";
118
+ const FAILZ_CLASS_DECL = "function __ZcFailZ(z,r,i){this.success=false;this._z=z;this._r=r;this._i=i;this._c=undefined;}Object.defineProperty(__ZcFailZ.prototype,\"error\",{configurable:true,get:function(){return this._c||(this._c=this._z.call(this._r,this._i).error);}});";
119
+ /** Compact-mode finalizer: retain a pristine safeParse, its receiver, and input lazily. */
120
+ const FINZ_DECL = "function __zcFinZ(z,r,i){return new __ZcFailZ(z,r,i);}";
120
121
  /**
121
122
  * Validator factory. Inline mode (CLI emitter) declares it once per compiled
122
123
  * file; lean mode (all unplugin bundlers) exports it once per bundle from the
@@ -206,12 +207,14 @@ function extractFunctionName(functionDef) {
206
207
  function generateIIFE(schemaExpr, schema, options) {
207
208
  const { codegenResult, refEntries } = schema;
208
209
  const fnName = extractFunctionName(codegenResult.functionDef);
209
- const schemaArg = options?.zodCompat !== false ? schemaExpr : "null";
210
+ const zodCompat = options?.zodCompat !== false;
211
+ const retainedSchema = refEntries.length > 0 ? "__zs" : schemaExpr;
212
+ const schemaArg = zodCompat ? retainedSchema : "null";
210
213
  const fcArg = codegenResult.fastFnName ?? "null";
211
214
  const isArg = codegenResult.isFnName ?? (codegenResult.fastTotal ? fcArg : "null");
212
215
  return [
213
216
  "/* @__PURE__ */ (() => {",
214
- ...refEntries.length > 0 ? [`var __rf=[${refEntries.map((fb) => `${schemaExpr}${fb.accessPath}`).join(",")}];`] : [],
217
+ ...refEntries.length > 0 ? [`var __zs=${schemaExpr};`, `var __rf=[${refEntries.map((fb) => `${retainedSchema}${fb.accessPath}`).join(",")}];`] : [],
215
218
  ...codegenResult.code.split("\n").filter((l) => l.trim() !== "" && l.trim() !== "/* zod-compiler */"),
216
219
  codegenResult.functionDef,
217
220
  `return __zcMkv(${fnName},${schemaArg},${fcArg},${isArg});`,
@@ -1 +1 @@
1
- {"version":3,"file":"iife.js","names":[],"sources":["../../src/core/iife.ts"],"sourcesContent":["/**\n * Shared CompiledSchema<T> IIFE generation.\n * Used by both CLI emitter and unplugin transform.\n */\n\nimport type { CompiledSchemaInfo } from \"./pipeline.js\";\n\n/**\n * Import statement required by generateIIFE output (references\n * __zodCompilerConfig). `core` is bound for $ZodAsyncError: superRefine and\n * custom callbacks may only reveal that they are async through the promise\n * they return, at which point zod's own synchronous parse raises.\n */\nexport const ZOD_CONFIG_IMPORT =\n 'import { config as __zodCompilerConfig, core as __zcCore, ZodRealError as __zcZodError } from \"zod\";';\n\n/**\n * File-level `__zcMsg` declaration (must appear once after ZOD_CONFIG_IMPORT):\n * the message an issue gets when nothing was baked into it at build time.\n *\n * Resolves zod's tail of `finalizeIssue` — `config.customError` then\n * `config.localeError` then \"Invalid input\" — and does it PER CALL, because the\n * config is mutable: `z.config({ localeError })` in an entry point runs after\n * the schema modules it imports, so a value snapshotted at module init misses\n * it. Reading a captured `localeError` alone also dropped `customError`\n * outright, silently ignoring the global map most i18n setups install.\n *\n * The head of zod's chain — the schema's own `error` option — is baked into the\n * issue at build time and short-circuits this. The one link that cannot be\n * reproduced is a per-CALL `ctx.error`, which would have to travel through\n * `safeParse`; that entry point sits at V8's inlining budget, where even an\n * unused extra parameter measured ~12% on every parse.\n *\n * Only ever called while building an error, never on a successful parse.\n */\nexport const ZOD_MSG_DECLARATION =\n 'function __zcUw(m){return typeof m===\"string\"?m:(m===undefined||m===null?undefined:m.message);}' +\n \"var __zcMsg=function(iss){var c=__zodCompilerConfig(),m;\" +\n \"if(c.customError){m=__zcUw(c.customError(iss));if(m!==undefined&&m!==null)return m;}\" +\n \"if(c.localeError){m=__zcUw(c.localeError(iss));if(m!==undefined&&m!==null)return m;}\" +\n 'return \"Invalid input\";};';\n\n/**\n * Shared failure-result for __zcFin / __zcFinD. Inline mode (CLI emitter)\n * declares it once per compiled file; lean mode (all unplugin bundlers) declares\n * it once per bundle in the plugin-materialized runtime module (module-local —\n * generated code only ever references __zcFin/__zcFinD, never __ZcFail).\n *\n * Why a prototype getter and not `{success:false, get error(){...}}`: an object\n * literal with an inline accessor forces V8 down its slow accessor-defining\n * allocation path — ~110ns per failure, measured — which dominates the entire\n * invalid-input cost whenever callers never read `.error`. Hosting `error` on\n * the prototype turns each failure into a plain field-only instantiation (~2ns,\n * ~13x), with the lazy-cache semantics intact. (Trade-off: `error` is a\n * prototype accessor, so it no longer shows up in `Object.keys(result)` / spread\n * / JSON.stringify of the result wrapper — `.success`/`.error`/`.data` access,\n * destructuring, and `in` are unaffected.)\n *\n * One class serves both finalizers, so the instances share one hidden class:\n * __zcFin passes pre-collected issues in `_e` (with `_f===null`); __zcFinD\n * passes the hosted slow-walk in `_f` plus the input in `_i`, and the getter\n * runs the walk on first `.error` read. The whole finalization — locale fill\n * (__zcMsg applied ONLY when an issue carries no message, never overwriting a\n * baked-in custom/fallback message), input strip, and ZodError construction\n * (zod v4 JSON.stringifies every issue into `message` and captures a stack\n * trace) — stays deferred inside the cached accessor exactly as before, since\n * the issues array is observable solely through `.error`.\n *\n * `input` is `delete`d, not assigned `undefined`. Key PRESENCE is observable —\n * `\"input\" in issue`, `Object.keys(issue)`, object spread, `toStrictEqual`\n * against a zod issue — and zod's `util.finalizeIssue` does `delete full.input`\n * whenever `reportInput` is off, so assignment left every compiled issue one\n * enumerable key wider than zod's. The delete's dictionary-mode transition is\n * affordable precisely BECAUSE of the deferral above: it runs only after a\n * caller asks for `.error` on a failed parse, and is memoised in `_c`. Neither\n * a successful parse nor a `.success`/`.is()` check on a rejected one reaches\n * it — measured, the two are unchanged, while the `.error` read itself goes\n * ~3.95us -> ~4.19us per failure, i.e. ~6% of a path whose cost is already\n * dominated by the ZodError construction on the next line (stack capture plus a\n * JSON.stringify of every issue).\n */\nexport const FAIL_CLASS_DECL =\n \"function __ZcFail(e,f,i){this.success=false;this._e=e;this._f=f;this._i=i;this._c=undefined;}\" +\n 'Object.defineProperty(__ZcFail.prototype,\"error\",{configurable:true,get:function(){' +\n \"if(this._c)return this._c;\" +\n \"var e=this._f!==null?this._f(this._i):this._e;\" +\n 'for(var i=0;i<e.length;i++){if(e[i].message===undefined&&typeof __zcMsg===\"function\")e[i].message=__zcMsg(e[i]);delete e[i].input;delete e[i].continue;}' +\n \"return this._c=new __zcZodError(e);}});\";\n\n/** Eager finalizer (mutation / partial-fast-path schemas): issues already\n * collected in `e`; success short-circuits to a plain result literal. */\nexport const FIN_DECL =\n \"function __zcFin(e,d){if(!e.length)return{success:true,data:d};return new __ZcFail(e,null,null);}\";\n\n/**\n * Deferred-collection finalizer for Fast-Path-eligible schemas. When the\n * fast check fails, the ENTIRE slow path (the issue-collecting re-walk) is\n * pushed into the cached `.error` accessor instead of running eagerly:\n * fast-eligible schemas never mutate, so the walk's only output is the\n * issues array, which is observable solely through `.error` — one step\n * further along the same lazy boundary `__zcFin` already established (locale\n * fill, input strip, ZodError construction). A failed safeParse whose\n * `.error` is never read costs the fast check alone.\n *\n * Takes the schema's HOSTED slow-walk function plus the input — NOT a\n * per-call closure: `__zcFinD(__sw_N, input)` allocates only the result\n * object, where `__zcFinD(function(){...})` paid a closure environment and\n * function object per failure. Hosting the walk also shrinks safeParse to\n * two statements, within V8's inlining budget (the success-path result\n * literal becomes escape-analyzable at monomorphic call sites).\n *\n * The walk re-reads `input` at `.error`-read time; a caller that mutates\n * the input between safeParse and reading `.error` sees issues for the\n * mutated value (zod materializes at parse time). Same caveat class as the\n * documented __zcFin deferral.\n */\nexport const FIN_DEFERRED_DECL = \"function __zcFinD(f,inp){return new __ZcFail(null,f,inp);}\";\n\n/**\n * Compact-mode failure class — a lazy failure that delegates error reporting to\n * the ORIGINAL Zod schema's `safeParse`. Used by `output: \"compact\"`, where the\n * compiled slow walk is dropped entirely: a mutation-free schema's fast check\n * is the only generated validation, and on a fast-check failure the cold error\n * path is produced by the retained Zod schema itself (`zod` is the source of\n * truth, so the issues are byte-identical — no second validation engine).\n *\n * `_z` is the schema's PRISTINE bound safeParse (`__rf[N].safeParse.bind(...)`,\n * captured pre-`__zcMkv` by emitRfDelegate — see context.ts — so it is zod's\n * own implementation, never the compiled delegate, avoiding infinite\n * recursion). The zod parse is deferred until `.error` is read and cached, so\n * the common `safeParse(x).success`/`.is(x)` checks on invalid input cost only\n * the fast check (zod never runs) — the same deferral boundary `__zcFinD`\n * establishes for the compiled slow walk. Sound because compact mode is gated\n * on a TOTAL fast path: `fc(input) === false` ⟹ zod rejects, so `success:false`\n * holds without consulting zod.\n *\n * The getter returns zod's OWN ZodError verbatim (no locale fill / input strip /\n * re-wrap — zod already finalized it), so a delegated failure is exactly what\n * the unaltered schema would have produced.\n */\nexport const FAILZ_CLASS_DECL =\n \"function __ZcFailZ(z,i){this.success=false;this._z=z;this._i=i;this._c=undefined;}\" +\n 'Object.defineProperty(__ZcFailZ.prototype,\"error\",{configurable:true,get:function(){' +\n \"return this._c||(this._c=this._z(this._i).error);}});\";\n\n/** Compact-mode finalizer: wrap a pristine bound safeParse + input into a lazy delegated failure. */\nexport const FINZ_DECL = \"function __zcFinZ(z,i){return new __ZcFailZ(z,i);}\";\n\n/**\n * Validator factory. Inline mode (CLI emitter) declares it once per compiled\n * file; lean mode (all unplugin bundlers) exports it once per bundle from the\n * plugin-materialized runtime module — generated code never imports it from\n * the zod-compiler package itself, so zod-compiler stays a devDependency and\n * the helper set is always version-locked to the codegen that calls it.\n * Wraps a safeParse function into the CompiledSchema interface.\n *\n * IDENTITY-PRESERVING: with zodCompat (schema != null) the compiled\n * parse/safeParse/parseAsync/safeParseAsync are installed as OWN properties\n * on the original schema object, which is returned as-is. zod v4 keys\n * several APIs on object identity — toJSONSchema's ctx.seen registers the\n * object it is handed while each processor closure captures the original\n * inst (a wrapper crashes `optionalProcessor` with \"Cannot set properties\n * of undefined (setting 'ref')\" the moment a compiled schema is composed\n * into another schema), and globalRegistry/.meta() is a WeakMap keyed by\n * the schema instance (a wrapper silently loses OpenAPI titles/ids). An\n * Object.create wrapper breaks both; mutating the original breaks neither\n * (zod's internal parsing flows through _zod.run, never the public\n * methods, and derived schemas — .optional(), .extend() — are fresh\n * instances that fall back to plain zod). schema=null (zodCompat: false)\n * still produces a plain method-bag object.\n *\n * fc is the schema's hosted fast-check boolean function (null when no Fast\n * Path exists). parse()/parseAsync() try it first and return the input\n * directly on success: fc is small enough for V8 to inline, so the hot parse\n * path runs with zero allocations — calling fn would allocate an intermediate\n * SafeParseResult that escape analysis cannot remove (fn never inlines).\n * Fast-path-eligible schemas never mutate, so fc(input) ⟹ data === input.\n *\n * is is the TOTAL fast-check predicate (fc when the fast path is total, else\n * null). Installed as `.is()` — a zero-allocation boolean type guard. When\n * null (partial fast path or none) `.is()` derives from fn(input).success: a\n * partial fc can pass-through valid input but its `false` does not imply\n * rejection (a default/catch may still succeed), so it would be unsound as a\n * standalone guard.\n *\n * parseAsync/safeParseAsync wrap the SYNC validator, which is right for every\n * schema the compiler can reproduce — none of them are async. It is wrong for\n * the ones it cannot: an `async` refinement or a `z.promise()` extracts to a\n * Zod delegate, and delegating means calling Zod's SYNCHRONOUS safeParse, which\n * raises `$ZodAsyncError` by design. Wrapping that gave the compiled schema an\n * async pair that rejected with `$ZodAsyncError` for EVERY input — including\n * valid ones — where Zod resolves normally, so `await UserSchema.parseAsync(x)`\n * stopped working the moment any part of the schema went async.\n *\n * So both are guarded: a synchronous throw hands off to the schema's ORIGINAL\n * async method, captured before these are installed — the same escape hatch\n * `~standard` already uses for its throw path, and for the same reason (the\n * compiled validator has no async mode to offer, and Zod's is exact). It also\n * fixes the smaller wart that a throwing `fn` made `parseAsync` throw\n * SYNCHRONOUSLY rather than return a rejected promise. With `zodCompat: false`\n * there is no schema to delegate to and the throw propagates as before.\n *\n * `~standard` is REPLACED, not merely preserved. Zod builds it lazily as\n * `validate: (v) => safeParse(inst, v)` — the core FUNCTION, which goes straight\n * to `inst._zod.run`. It never reads the schema's own `safeParse` property, so\n * installing the compiled one leaves this route entirely uncompiled: measured\n * 271.7 ns against the compiled 26.6 ns on the same schema, i.e. Standard Schema\n * consumers (tRPC, Hono, TanStack) were getting plain Zod.\n *\n * Zod's own validate is captured first and kept as the throw path: it catches a\n * synchronous throw and retries through `safeParseAsync`, which is how an async\n * refinement resolves and how a throwing check surfaces as a rejected promise\n * rather than a synchronous throw. The compiled validator cannot reproduce that\n * (async schemas delegate to Zod anyway), so deferring to the original is both\n * simpler and exact.\n *\n * Installed with defineProperty rather than assignment: Zod's lazy setter\n * redefines the slot as non-writable, so a second `__zcMkv` on the same schema\n * object — two exports aliasing one schema — would throw under ESM strict mode.\n */\nexport const MK_VALIDATOR_DECL =\n \"function __zcMkv(fn,schema,fc,is){var w=schema||{};var zpa=w.parseAsync,zspa=w.safeParseAsync;w.parse=fc?function(input){if(fc(input))return input;var r=fn(input);if(r.success)return r.data;throw r.error;}:function(input){var r=fn(input);if(r.success)return r.data;throw r.error;};w.safeParse=fn;w.safeParseAsync=function(input){try{return Promise.resolve(fn(input));}catch(e){if(zspa)return zspa(input);throw e;}};w.parseAsync=fc?function(input){try{if(fc(input))return Promise.resolve(input);var r=fn(input);if(r.success)return Promise.resolve(r.data);return Promise.reject(r.error);}catch(e){if(zpa)return zpa(input);throw e;}}:function(input){try{var r=fn(input);if(r.success)return Promise.resolve(r.data);return Promise.reject(r.error);}catch(e){if(zpa)return zpa(input);throw e;}};w.is=is||function(input){return fn(input).success;};\" +\n 'var s=w[\"~standard\"],zv=s&&s.validate;' +\n 'Object.defineProperty(w,\"~standard\",{configurable:true,value:{version:1,vendor:(s&&s.vendor)||\"zod\",validate:function(input){var r;try{if(fc&&fc(input))return{value:input};r=fn(input);}catch(e){if(zv)return zv(input);throw e;}return r.success?{value:r.data}:{issues:r.error.issues};}}});' +\n \"return w;}\";\n\nfunction extractFunctionName(functionDef: string): string {\n const match = /^function\\s+(\\w+)\\s*\\(/.exec(functionDef);\n if (!match?.[1]) {\n throw new Error(\"Cannot extract function name from generated code\");\n }\n return match[1];\n}\n\n/**\n * Generate a `/* @__PURE__ * /` IIFE wrapping a compiled validator.\n *\n * @param schemaExpr - Expression resolving to the original Zod schema\n * (e.g. `\"UserSchema\"` in unplugin, `\"(__src_X as any).schema\"` in CLI)\n * @param schema\n * @param options\n */\nexport function generateIIFE(\n schemaExpr: string,\n schema: CompiledSchemaInfo,\n options?: { zodCompat?: boolean | undefined },\n): string {\n const { codegenResult, refEntries } = schema;\n const fnName = extractFunctionName(codegenResult.functionDef);\n const zodCompat = options?.zodCompat !== false;\n const schemaArg = zodCompat ? schemaExpr : \"null\";\n const fcArg = codegenResult.fastFnName ?? \"null\";\n // `.is()` gets the fast-check directly only when it is a total predicate;\n // partial/none falls back to safeParse().success inside __zcMkv. A rebuilding\n // schema has no by-reference `fc` but still names its predicate separately.\n const isArg = codegenResult.isFnName ?? (codegenResult.fastTotal ? fcArg : \"null\");\n\n return [\n \"/* @__PURE__ */ (() => {\",\n ...(refEntries.length > 0\n ? [`var __rf=[${refEntries.map((fb) => `${schemaExpr}${fb.accessPath}`).join(\",\")}];`]\n : []),\n ...codegenResult.code\n .split(\"\\n\")\n .filter((l) => l.trim() !== \"\" && l.trim() !== \"/* zod-compiler */\"),\n codegenResult.functionDef,\n `return __zcMkv(${fnName},${schemaArg},${fcArg},${isArg});`,\n \"})()\",\n ].join(\"\\n\");\n}\n"],"mappings":";;;;;;;AAaA,MAAa,oBACX;;;;;;;;;;;;;;;;;;;;AAqBF,MAAa,sBACX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CF,MAAa,kBACX;;;AASF,MAAa,WACX;;;;;;;;;;;;;;;;;;;;;;;AAwBF,MAAa,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;AAwBjC,MAAa,mBACX;;AAKF,MAAa,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0EzB,MAAa,oBACX;AAKF,SAAS,oBAAoB,aAA6B;CACxD,MAAM,QAAQ,yBAAyB,KAAK,WAAW;CACvD,IAAI,CAAC,QAAQ,IACX,MAAM,IAAI,MAAM,kDAAkD;CAEpE,OAAO,MAAM;AACf;;;;;;;;;AAUA,SAAgB,aACd,YACA,QACA,SACQ;CACR,MAAM,EAAE,eAAe,eAAe;CACtC,MAAM,SAAS,oBAAoB,cAAc,WAAW;CAE5D,MAAM,YADY,SAAS,cAAc,QACX,aAAa;CAC3C,MAAM,QAAQ,cAAc,cAAc;CAI1C,MAAM,QAAQ,cAAc,aAAa,cAAc,YAAY,QAAQ;CAE3E,OAAO;EACL;EACA,GAAI,WAAW,SAAS,IACpB,CAAC,aAAa,WAAW,KAAK,OAAO,GAAG,aAAa,GAAG,YAAY,CAAC,CAAC,KAAK,GAAG,EAAE,GAAG,IACnF,CAAC;EACL,GAAG,cAAc,KACd,MAAM,IAAI,CAAC,CACX,QAAQ,MAAM,EAAE,KAAK,MAAM,MAAM,EAAE,KAAK,MAAM,oBAAoB;EACrE,cAAc;EACd,kBAAkB,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM;EACxD;CACF,CAAC,CAAC,KAAK,IAAI;AACb"}
1
+ {"version":3,"file":"iife.js","names":[],"sources":["../../src/core/iife.ts"],"sourcesContent":["/**\n * Shared CompiledSchema<T> IIFE generation.\n * Used by both CLI emitter and unplugin transform.\n */\n\nimport type { CompiledSchemaInfo } from \"./pipeline.js\";\n\n/**\n * Import statement required by generateIIFE output (references\n * __zodCompilerConfig). `core` is bound for $ZodAsyncError: superRefine and\n * custom callbacks may only reveal that they are async through the promise\n * they return, at which point zod's own synchronous parse raises.\n */\nexport const ZOD_CONFIG_IMPORT =\n 'import { config as __zodCompilerConfig, core as __zcCore, ZodRealError as __zcZodError } from \"zod\";';\n\n/**\n * File-level `__zcMsg` declaration (must appear once after ZOD_CONFIG_IMPORT):\n * the message an issue gets when nothing was baked into it at build time.\n *\n * Resolves zod's tail of `finalizeIssue` — `config.customError` then\n * `config.localeError` then \"Invalid input\" — and does it PER CALL, because the\n * config is mutable: `z.config({ localeError })` in an entry point runs after\n * the schema modules it imports, so a value snapshotted at module init misses\n * it. Reading a captured `localeError` alone also dropped `customError`\n * outright, silently ignoring the global map most i18n setups install.\n *\n * The head of zod's chain — the schema's own `error` option — is baked into the\n * issue at build time and short-circuits this. The one link that cannot be\n * reproduced is a per-CALL `ctx.error`, which would have to travel through\n * `safeParse`; that entry point sits at V8's inlining budget, where even an\n * unused extra parameter measured ~12% on every parse.\n *\n * Only ever called while building an error, never on a successful parse.\n */\nexport const ZOD_MSG_DECLARATION =\n 'function __zcUw(m){return typeof m===\"string\"?m:(m===undefined||m===null?undefined:m.message);}' +\n \"var __zcMsg=function(iss){var c=__zodCompilerConfig(),m;\" +\n \"if(c.customError){m=__zcUw(c.customError(iss));if(m!==undefined&&m!==null)return m;}\" +\n \"if(c.localeError){m=__zcUw(c.localeError(iss));if(m!==undefined&&m!==null)return m;}\" +\n 'return \"Invalid input\";};';\n\n/**\n * Shared failure-result for __zcFin / __zcFinD. Inline mode (CLI emitter)\n * declares it once per compiled file; lean mode (all unplugin bundlers) declares\n * it once per bundle in the plugin-materialized runtime module (module-local —\n * generated code only ever references __zcFin/__zcFinD, never __ZcFail).\n *\n * Why a prototype getter and not `{success:false, get error(){...}}`: an object\n * literal with an inline accessor forces V8 down its slow accessor-defining\n * allocation path — ~110ns per failure, measured — which dominates the entire\n * invalid-input cost whenever callers never read `.error`. Hosting `error` on\n * the prototype turns each failure into a plain field-only instantiation (~2ns,\n * ~13x), with the lazy-cache semantics intact. (Trade-off: `error` is a\n * prototype accessor, so it no longer shows up in `Object.keys(result)` / spread\n * / JSON.stringify of the result wrapper — `.success`/`.error`/`.data` access,\n * destructuring, and `in` are unaffected.)\n *\n * One class serves both finalizers, so the instances share one hidden class:\n * __zcFin passes pre-collected issues in `_e` (with `_f===null`); __zcFinD\n * passes the hosted slow-walk in `_f` plus the input in `_i`, and the getter\n * runs the walk on first `.error` read. The whole finalization — locale fill\n * (__zcMsg applied ONLY when an issue carries no message, never overwriting a\n * baked-in custom/fallback message), input strip, and ZodError construction\n * (zod v4 JSON.stringifies every issue into `message` and captures a stack\n * trace) — stays deferred inside the cached accessor exactly as before, since\n * the issues array is observable solely through `.error`.\n *\n * `input` is `delete`d, not assigned `undefined`. Key PRESENCE is observable —\n * `\"input\" in issue`, `Object.keys(issue)`, object spread, `toStrictEqual`\n * against a zod issue — and zod's `util.finalizeIssue` does `delete full.input`\n * whenever `reportInput` is off, so assignment left every compiled issue one\n * enumerable key wider than zod's. The delete's dictionary-mode transition is\n * affordable precisely BECAUSE of the deferral above: it runs only after a\n * caller asks for `.error` on a failed parse, and is memoised in `_c`. Neither\n * a successful parse nor a `.success`/`.is()` check on a rejected one reaches\n * it — measured, the two are unchanged, while the `.error` read itself goes\n * ~3.95us -> ~4.19us per failure, i.e. ~6% of a path whose cost is already\n * dominated by the ZodError construction on the next line (stack capture plus a\n * JSON.stringify of every issue).\n */\nexport const FAIL_CLASS_DECL =\n \"function __ZcFail(e,f,i){this.success=false;this._e=e;this._f=f;this._i=i;this._c=undefined;}\" +\n 'Object.defineProperty(__ZcFail.prototype,\"error\",{configurable:true,get:function(){' +\n \"if(this._c)return this._c;\" +\n \"var e=this._f!==null?this._f(this._i):this._e;\" +\n 'for(var i=0;i<e.length;i++){if(e[i].message===undefined&&typeof __zcMsg===\"function\")e[i].message=__zcMsg(e[i]);delete e[i].input;delete e[i].continue;}' +\n \"return this._c=new __zcZodError(e);}});\";\n\n/** Eager finalizer (mutation / partial-fast-path schemas): issues already\n * collected in `e`; success short-circuits to a plain result literal. */\nexport const FIN_DECL =\n \"function __zcFin(e,d){if(!e.length)return{success:true,data:d};return new __ZcFail(e,null,null);}\";\n\n/**\n * Deferred-collection finalizer for Fast-Path-eligible schemas. When the\n * fast check fails, the ENTIRE slow path (the issue-collecting re-walk) is\n * pushed into the cached `.error` accessor instead of running eagerly:\n * fast-eligible schemas never mutate, so the walk's only output is the\n * issues array, which is observable solely through `.error` — one step\n * further along the same lazy boundary `__zcFin` already established (locale\n * fill, input strip, ZodError construction). A failed safeParse whose\n * `.error` is never read costs the fast check alone.\n *\n * Takes the schema's HOSTED slow-walk function plus the input — NOT a\n * per-call closure: `__zcFinD(__sw_N, input)` allocates only the result\n * object, where `__zcFinD(function(){...})` paid a closure environment and\n * function object per failure. Hosting the walk also shrinks safeParse to\n * two statements, within V8's inlining budget (the success-path result\n * literal becomes escape-analyzable at monomorphic call sites).\n *\n * The walk re-reads `input` at `.error`-read time; a caller that mutates\n * the input between safeParse and reading `.error` sees issues for the\n * mutated value (zod materializes at parse time). Same caveat class as the\n * documented __zcFin deferral.\n */\nexport const FIN_DEFERRED_DECL = \"function __zcFinD(f,inp){return new __ZcFail(null,f,inp);}\";\n\n/**\n * Compact-mode failure class — a lazy failure that delegates error reporting to\n * the ORIGINAL Zod schema's `safeParse`. Used by `output: \"compact\"`, where the\n * compiled slow walk is dropped entirely: a mutation-free schema's fast check\n * is the only generated validation, and on a fast-check failure the cold error\n * path is produced by the retained Zod schema itself (`zod` is the source of\n * truth, so the issues are byte-identical — no second validation engine).\n *\n * `_z` is the schema's PRISTINE safeParse method and `_r` is its receiver. Both\n * are captured pre-`__zcMkv` by emitRfMethod — see context.ts — so the method is\n * zod's own implementation, never the compiled delegate, avoiding infinite\n * recursion without allocating a bound function. The zod parse is deferred\n * until `.error` is read and cached, so\n * the common `safeParse(x).success`/`.is(x)` checks on invalid input cost only\n * the fast check (zod never runs) — the same deferral boundary `__zcFinD`\n * establishes for the compiled slow walk. Sound because compact mode is gated\n * on a TOTAL fast path: `fc(input) === false` ⟹ zod rejects, so `success:false`\n * holds without consulting zod.\n *\n * The getter returns zod's OWN ZodError verbatim (no locale fill / input strip /\n * re-wrap — zod already finalized it), so a delegated failure is exactly what\n * the unaltered schema would have produced.\n */\nexport const FAILZ_CLASS_DECL =\n \"function __ZcFailZ(z,r,i){this.success=false;this._z=z;this._r=r;this._i=i;this._c=undefined;}\" +\n 'Object.defineProperty(__ZcFailZ.prototype,\"error\",{configurable:true,get:function(){' +\n \"return this._c||(this._c=this._z.call(this._r,this._i).error);}});\";\n\n/** Compact-mode finalizer: retain a pristine safeParse, its receiver, and input lazily. */\nexport const FINZ_DECL = \"function __zcFinZ(z,r,i){return new __ZcFailZ(z,r,i);}\";\n\n/**\n * Validator factory. Inline mode (CLI emitter) declares it once per compiled\n * file; lean mode (all unplugin bundlers) exports it once per bundle from the\n * plugin-materialized runtime module — generated code never imports it from\n * the zod-compiler package itself, so zod-compiler stays a devDependency and\n * the helper set is always version-locked to the codegen that calls it.\n * Wraps a safeParse function into the CompiledSchema interface.\n *\n * IDENTITY-PRESERVING: with zodCompat (schema != null) the compiled\n * parse/safeParse/parseAsync/safeParseAsync are installed as OWN properties\n * on the original schema object, which is returned as-is. zod v4 keys\n * several APIs on object identity — toJSONSchema's ctx.seen registers the\n * object it is handed while each processor closure captures the original\n * inst (a wrapper crashes `optionalProcessor` with \"Cannot set properties\n * of undefined (setting 'ref')\" the moment a compiled schema is composed\n * into another schema), and globalRegistry/.meta() is a WeakMap keyed by\n * the schema instance (a wrapper silently loses OpenAPI titles/ids). An\n * Object.create wrapper breaks both; mutating the original breaks neither\n * (zod's internal parsing flows through _zod.run, never the public\n * methods, and derived schemas — .optional(), .extend() — are fresh\n * instances that fall back to plain zod). schema=null (zodCompat: false)\n * still produces a plain method-bag object.\n *\n * fc is the schema's hosted fast-check boolean function (null when no Fast\n * Path exists). parse()/parseAsync() try it first and return the input\n * directly on success: fc is small enough for V8 to inline, so the hot parse\n * path runs with zero allocations — calling fn would allocate an intermediate\n * SafeParseResult that escape analysis cannot remove (fn never inlines).\n * Fast-path-eligible schemas never mutate, so fc(input) ⟹ data === input.\n *\n * is is the TOTAL fast-check predicate (fc when the fast path is total, else\n * null). Installed as `.is()` — a zero-allocation boolean type guard. When\n * null (partial fast path or none) `.is()` derives from fn(input).success: a\n * partial fc can pass-through valid input but its `false` does not imply\n * rejection (a default/catch may still succeed), so it would be unsound as a\n * standalone guard.\n *\n * parseAsync/safeParseAsync wrap the SYNC validator, which is right for every\n * schema the compiler can reproduce — none of them are async. It is wrong for\n * the ones it cannot: an `async` refinement or a `z.promise()` extracts to a\n * Zod delegate, and delegating means calling Zod's SYNCHRONOUS safeParse, which\n * raises `$ZodAsyncError` by design. Wrapping that gave the compiled schema an\n * async pair that rejected with `$ZodAsyncError` for EVERY input — including\n * valid ones — where Zod resolves normally, so `await UserSchema.parseAsync(x)`\n * stopped working the moment any part of the schema went async.\n *\n * So both are guarded: a synchronous throw hands off to the schema's ORIGINAL\n * async method, captured before these are installed — the same escape hatch\n * `~standard` already uses for its throw path, and for the same reason (the\n * compiled validator has no async mode to offer, and Zod's is exact). It also\n * fixes the smaller wart that a throwing `fn` made `parseAsync` throw\n * SYNCHRONOUSLY rather than return a rejected promise. With `zodCompat: false`\n * there is no schema to delegate to and the throw propagates as before.\n *\n * `~standard` is REPLACED, not merely preserved. Zod builds it lazily as\n * `validate: (v) => safeParse(inst, v)` — the core FUNCTION, which goes straight\n * to `inst._zod.run`. It never reads the schema's own `safeParse` property, so\n * installing the compiled one leaves this route entirely uncompiled: measured\n * 271.7 ns against the compiled 26.6 ns on the same schema, i.e. Standard Schema\n * consumers (tRPC, Hono, TanStack) were getting plain Zod.\n *\n * Zod's own validate is captured first and kept as the throw path: it catches a\n * synchronous throw and retries through `safeParseAsync`, which is how an async\n * refinement resolves and how a throwing check surfaces as a rejected promise\n * rather than a synchronous throw. The compiled validator cannot reproduce that\n * (async schemas delegate to Zod anyway), so deferring to the original is both\n * simpler and exact.\n *\n * Installed with defineProperty rather than assignment: Zod's lazy setter\n * redefines the slot as non-writable, so a second `__zcMkv` on the same schema\n * object — two exports aliasing one schema — would throw under ESM strict mode.\n */\nexport const MK_VALIDATOR_DECL =\n \"function __zcMkv(fn,schema,fc,is){var w=schema||{};var zpa=w.parseAsync,zspa=w.safeParseAsync;w.parse=fc?function(input){if(fc(input))return input;var r=fn(input);if(r.success)return r.data;throw r.error;}:function(input){var r=fn(input);if(r.success)return r.data;throw r.error;};w.safeParse=fn;w.safeParseAsync=function(input){try{return Promise.resolve(fn(input));}catch(e){if(zspa)return zspa(input);throw e;}};w.parseAsync=fc?function(input){try{if(fc(input))return Promise.resolve(input);var r=fn(input);if(r.success)return Promise.resolve(r.data);return Promise.reject(r.error);}catch(e){if(zpa)return zpa(input);throw e;}}:function(input){try{var r=fn(input);if(r.success)return Promise.resolve(r.data);return Promise.reject(r.error);}catch(e){if(zpa)return zpa(input);throw e;}};w.is=is||function(input){return fn(input).success;};\" +\n 'var s=w[\"~standard\"],zv=s&&s.validate;' +\n 'Object.defineProperty(w,\"~standard\",{configurable:true,value:{version:1,vendor:(s&&s.vendor)||\"zod\",validate:function(input){var r;try{if(fc&&fc(input))return{value:input};r=fn(input);}catch(e){if(zv)return zv(input);throw e;}return r.success?{value:r.data}:{issues:r.error.issues};}}});' +\n \"return w;}\";\n\nfunction extractFunctionName(functionDef: string): string {\n const match = /^function\\s+(\\w+)\\s*\\(/.exec(functionDef);\n if (!match?.[1]) {\n throw new Error(\"Cannot extract function name from generated code\");\n }\n return match[1];\n}\n\n/**\n * Generate a `/* @__PURE__ * /` IIFE wrapping a compiled validator.\n *\n * @param schemaExpr - Expression resolving to the original Zod schema\n * (e.g. `\"UserSchema\"` in unplugin, `\"(__src_X as any).schema\"` in CLI)\n * @param schema\n * @param options\n */\nexport function generateIIFE(\n schemaExpr: string,\n schema: CompiledSchemaInfo,\n options?: { zodCompat?: boolean | undefined },\n): string {\n const { codegenResult, refEntries } = schema;\n const fnName = extractFunctionName(codegenResult.functionDef);\n const zodCompat = options?.zodCompat !== false;\n // Every fallback access starts from the same source schema. Capture it once\n // whenever refs exist so an inline initializer is not reconstructed for each\n // path and again for the identity-preserving __zcMkv target.\n const retainedSchema = refEntries.length > 0 ? \"__zs\" : schemaExpr;\n const schemaArg = zodCompat ? retainedSchema : \"null\";\n const fcArg = codegenResult.fastFnName ?? \"null\";\n // `.is()` gets the fast-check directly only when it is a total predicate;\n // partial/none falls back to safeParse().success inside __zcMkv. A rebuilding\n // schema has no by-reference `fc` but still names its predicate separately.\n const isArg = codegenResult.isFnName ?? (codegenResult.fastTotal ? fcArg : \"null\");\n\n return [\n \"/* @__PURE__ */ (() => {\",\n ...(refEntries.length > 0\n ? [\n `var __zs=${schemaExpr};`,\n `var __rf=[${refEntries.map((fb) => `${retainedSchema}${fb.accessPath}`).join(\",\")}];`,\n ]\n : []),\n ...codegenResult.code\n .split(\"\\n\")\n .filter((l) => l.trim() !== \"\" && l.trim() !== \"/* zod-compiler */\"),\n codegenResult.functionDef,\n `return __zcMkv(${fnName},${schemaArg},${fcArg},${isArg});`,\n \"})()\",\n ].join(\"\\n\");\n}\n"],"mappings":";;;;;;;AAaA,MAAa,oBACX;;;;;;;;;;;;;;;;;;;;AAqBF,MAAa,sBACX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CF,MAAa,kBACX;;;AASF,MAAa,WACX;;;;;;;;;;;;;;;;;;;;;;;AAwBF,MAAa,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;AAyBjC,MAAa,mBACX;;AAKF,MAAa,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0EzB,MAAa,oBACX;AAKF,SAAS,oBAAoB,aAA6B;CACxD,MAAM,QAAQ,yBAAyB,KAAK,WAAW;CACvD,IAAI,CAAC,QAAQ,IACX,MAAM,IAAI,MAAM,kDAAkD;CAEpE,OAAO,MAAM;AACf;;;;;;;;;AAUA,SAAgB,aACd,YACA,QACA,SACQ;CACR,MAAM,EAAE,eAAe,eAAe;CACtC,MAAM,SAAS,oBAAoB,cAAc,WAAW;CAC5D,MAAM,YAAY,SAAS,cAAc;CAIzC,MAAM,iBAAiB,WAAW,SAAS,IAAI,SAAS;CACxD,MAAM,YAAY,YAAY,iBAAiB;CAC/C,MAAM,QAAQ,cAAc,cAAc;CAI1C,MAAM,QAAQ,cAAc,aAAa,cAAc,YAAY,QAAQ;CAE3E,OAAO;EACL;EACA,GAAI,WAAW,SAAS,IACpB,CACE,YAAY,WAAW,IACvB,aAAa,WAAW,KAAK,OAAO,GAAG,iBAAiB,GAAG,YAAY,CAAC,CAAC,KAAK,GAAG,EAAE,GACrF,IACA,CAAC;EACL,GAAG,cAAc,KACd,MAAM,IAAI,CAAC,CACX,QAAQ,MAAM,EAAE,KAAK,MAAM,MAAM,EAAE,KAAK,MAAM,oBAAoB;EACrE,cAAc;EACd,kBAAkB,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM;EACxD;CACF,CAAC,CAAC,KAAK,IAAI;AACb"}
@@ -8,9 +8,9 @@ interface CompiledSchemaInfo {
8
8
  codegenResult: CodeGenResult;
9
9
  refEntries: RefEntry[];
10
10
  }
11
- /** Module-scope shared-validator declarations produced by file-level dedup. */
11
+ /** Module-scope declarations produced by file-level schema and constant dedup. */
12
12
  interface SharedSchemaBlock {
13
- /** Shared `__zcSw_N` functions + their preamble. Empty string when nothing repeated. */
13
+ /** Shared constants and `__zcSw_N` functions. Empty string when nothing repeats. */
14
14
  code: string;
15
15
  /** Runtime helper names referenced by the shared block (lean mode imports). */
16
16
  usedHelpers: Set<string>;
@@ -38,12 +38,11 @@ interface CompileSchemasOptions {
38
38
  * Run the extract → generate pipeline for each discovered schema.
39
39
  * Shared by CLI generate and unplugin transform.
40
40
  *
41
- * Two passes so structurally repeated schemas can be deduplicated: pass 1
42
- * extracts every schema's IR, then a file-level plan hoists the slow walk of
43
- * any shape that recurs across schemas into a shared `__zcSw_N` function; pass
44
- * 2 generates each validator, calling the shared walk instead of re-inlining
45
- * it. Files with no repetition take the same path and produce identical output
46
- * to single-pass codegen (the plan is empty), paying only one linear analysis.
41
+ * Pass 1 extracts every schema's IR and plans repeated slow walks. Pass 2
42
+ * generates each validator, calling shared `__zcSw_N` functions instead of
43
+ * re-inlining them. Exact Set initializers reported during generation are then
44
+ * pooled when at least two validators use them. Files with no repetition keep
45
+ * their original local declarations.
47
46
  */
48
47
  declare function compileSchemas(schemas: DiscoveredSchema[], options: CompileSchemasOptions): CompileSchemasResult;
49
48
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.ts","names":[],"sources":["../../src/core/pipeline.ts"],"mappings":";;;;;UAQiB;EACf;EACA,eAAe;EACf,YAAY;;;UAIG;;EAEf;;EAEA,aAAa;;;UAIE;EACf,SAAS;EACT,QAAQ;;UAGO;;EAEf,MAAM;;;;;;;;EAQN;;EAEA,WAAW,oBAAoB,OAAO;;;;;;;;;;;;;iBAcxB,eACd,SAAS,oBACT,SAAS,wBACR;;;;;iBAqEa,qBAAqB,SAAS,uBAAuB"}
1
+ {"version":3,"file":"pipeline.d.ts","names":[],"sources":["../../src/core/pipeline.ts"],"mappings":";;;;;UAQiB;EACf;EACA,eAAe;EACf,YAAY;;;UAIG;;EAEf;;EAEA,aAAa;;;UAIE;EACf,SAAS;EACT,QAAQ;;UAGO;;EAEf,MAAM;;;;;;;;EAQN;;EAEA,WAAW,oBAAoB,OAAO;;;;;;;;;;;;iBA2CxB,eACd,SAAS,oBACT,SAAS,wBACR;;;;;iBA+Ha,qBAAqB,SAAS,uBAAuB"}
@@ -1,17 +1,32 @@
1
- import { createSharedSchemaPlan } from "./codegen/dedupe.js";
1
+ import { SHARED_BLOCK_MARKER, createSharedSchemaPlan } from "./codegen/dedupe.js";
2
2
  import { generateValidator } from "./codegen/index.js";
3
3
  import { extractSchema } from "./extract/index.js";
4
4
  //#region src/core/pipeline.ts
5
+ /** Plan exact Set initializers used by at least two validators in this file. */
6
+ function planSharedSetConstants(results, constantsByResult) {
7
+ const byInitializer = /* @__PURE__ */ new Map();
8
+ for (const { codegenResult } of results) for (const constant of constantsByResult.get(codegenResult) ?? []) {
9
+ const declaration = `var ${constant.name}=${constant.initializer};`;
10
+ if (!codegenResult.code.includes(declaration)) continue;
11
+ const users = byInitializer.get(constant.initializer);
12
+ if (users === void 0) byInitializer.set(constant.initializer, /* @__PURE__ */ new Set([codegenResult]));
13
+ else users.add(codegenResult);
14
+ }
15
+ const repeated = [...byInitializer.entries()].filter(([, users]) => users.size >= 2).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
16
+ return new Map(repeated.map(([initializer], index) => [initializer, `__zcSet_${index}`]));
17
+ }
18
+ function emitSharedSetConstants(sharedSetNames) {
19
+ return [...sharedSetNames].map(([initializer, name]) => `var ${name}=/* @__PURE__ */${initializer};`).join("\n");
20
+ }
5
21
  /**
6
22
  * Run the extract → generate pipeline for each discovered schema.
7
23
  * Shared by CLI generate and unplugin transform.
8
24
  *
9
- * Two passes so structurally repeated schemas can be deduplicated: pass 1
10
- * extracts every schema's IR, then a file-level plan hoists the slow walk of
11
- * any shape that recurs across schemas into a shared `__zcSw_N` function; pass
12
- * 2 generates each validator, calling the shared walk instead of re-inlining
13
- * it. Files with no repetition take the same path and produce identical output
14
- * to single-pass codegen (the plan is empty), paying only one linear analysis.
25
+ * Pass 1 extracts every schema's IR and plans repeated slow walks. Pass 2
26
+ * generates each validator, calling shared `__zcSw_N` functions instead of
27
+ * re-inlining them. Exact Set initializers reported during generation are then
28
+ * pooled when at least two validators use them. Files with no repetition keep
29
+ * their original local declarations.
15
30
  */
16
31
  function compileSchemas(schemas, options) {
17
32
  const handle = (exportName, err) => {
@@ -32,30 +47,59 @@ function compileSchemas(schemas, options) {
32
47
  handle(s.exportName, err);
33
48
  }
34
49
  const plan = options.compact ? void 0 : createSharedSchemaPlan(extracted.map((e) => e.ir), options.mode);
35
- const results = [];
50
+ const generated = [];
51
+ const constantsByResult = /* @__PURE__ */ new Map();
36
52
  for (const e of extracted) try {
53
+ const setConstants = /* @__PURE__ */ new Map();
37
54
  const codegenResult = generateValidator(e.ir, e.exportName, {
38
55
  refCount: e.refEntries.length,
39
56
  mode: options.mode,
40
57
  sharedSchemas: plan,
41
- compact: options.compact
58
+ compact: options.compact,
59
+ onSetConstant(constant) {
60
+ setConstants.set(constant.name, constant);
61
+ }
42
62
  });
43
- if (codegenResult.rootDelegateRefIndex !== void 0) e.refEntries.push({
44
- schema: e.schema,
45
- accessPath: ""
46
- });
47
- results.push({
63
+ constantsByResult.set(codegenResult, [...setConstants.values()]);
64
+ generated.push({
48
65
  exportName: e.exportName,
49
- codegenResult,
50
- refEntries: e.refEntries
66
+ schema: e.schema,
67
+ ir: e.ir,
68
+ refEntries: e.refEntries,
69
+ codegenResult
51
70
  });
52
71
  } catch (err) {
53
72
  handle(e.exportName, err);
54
73
  }
74
+ const sharedSetNames = planSharedSetConstants(generated.map(({ exportName, codegenResult, refEntries }) => ({
75
+ exportName,
76
+ codegenResult,
77
+ refEntries
78
+ })), constantsByResult);
79
+ if (sharedSetNames.size > 0) for (const entry of generated) entry.codegenResult = generateValidator(entry.ir, entry.exportName, {
80
+ refCount: entry.refEntries.length,
81
+ mode: options.mode,
82
+ sharedSchemas: plan,
83
+ compact: options.compact,
84
+ sharedSetNames
85
+ });
86
+ const results = generated.map((entry) => {
87
+ if (entry.codegenResult.rootDelegateRefIndex !== void 0) entry.refEntries.push({
88
+ schema: entry.schema,
89
+ accessPath: ""
90
+ });
91
+ return {
92
+ exportName: entry.exportName,
93
+ codegenResult: entry.codegenResult,
94
+ refEntries: entry.refEntries
95
+ };
96
+ });
97
+ const sharedSetCode = emitSharedSetConstants(sharedSetNames);
98
+ const slowWalkCode = plan?.code ?? "";
55
99
  return {
56
100
  schemas: results,
57
101
  shared: {
58
- code: plan?.code ?? "",
102
+ code: sharedSetCode === "" ? slowWalkCode : slowWalkCode === "" ? `${SHARED_BLOCK_MARKER}\n${sharedSetCode}` : `${slowWalkCode}\n${sharedSetCode}`,
59
103
  usedHelpers: plan?.usedHelpers ?? /* @__PURE__ */ new Set()
60
104
  }
61
105
  };
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.js","names":[],"sources":["../../src/core/pipeline.ts"],"sourcesContent":["import type { CodeGenResult, CodegenMode } from \"./codegen/context.js\";\nimport { createSharedSchemaPlan } from \"./codegen/dedupe.js\";\nimport { generateValidator } from \"./codegen/index.js\";\nimport type { RefEntry } from \"./extract/index.js\";\nimport { extractSchema } from \"./extract/index.js\";\nimport type { DiscoveredSchema, SchemaIR } from \"./types.js\";\n\n/** Result of compiling a single discovered schema through extract → generate pipeline. */\nexport interface CompiledSchemaInfo {\n exportName: string;\n codegenResult: CodeGenResult;\n refEntries: RefEntry[];\n}\n\n/** Module-scope shared-validator declarations produced by file-level dedup. */\nexport interface SharedSchemaBlock {\n /** Shared `__zcSw_N` functions + their preamble. Empty string when nothing repeated. */\n code: string;\n /** Runtime helper names referenced by the shared block (lean mode imports). */\n usedHelpers: Set<string>;\n}\n\n/** Output of {@link compileSchemas}: per-schema validators plus the file's shared block. */\nexport interface CompileSchemasResult {\n schemas: CompiledSchemaInfo[];\n shared: SharedSchemaBlock;\n}\n\nexport interface CompileSchemasOptions {\n /** \"inline\" for CLI .compiled.ts; \"lean\" for unplugin (imports from virtual:zod-compiler/runtime). */\n mode: CodegenMode;\n /**\n * Compact output (`output: \"compact\"`). Drop the compiled slow walk for\n * mutation-free, total-fast-path schemas and delegate their cold error path\n * to the retained Zod schema. Disables slow-walk sharing (delegated schemas\n * never emit a walk to share) and appends a root self-RefEntry per delegated\n * schema so `__rf[N]` resolves to the original Zod schema.\n */\n compact?: boolean | undefined;\n /** When provided, per-schema failures call this and continue. Otherwise the first error throws. */\n onError?: (exportName: string, error: Error) => void;\n}\n\n/**\n * Run the extract → generate pipeline for each discovered schema.\n * Shared by CLI generate and unplugin transform.\n *\n * Two passes so structurally repeated schemas can be deduplicated: pass 1\n * extracts every schema's IR, then a file-level plan hoists the slow walk of\n * any shape that recurs across schemas into a shared `__zcSw_N` function; pass\n * 2 generates each validator, calling the shared walk instead of re-inlining\n * it. Files with no repetition take the same path and produce identical output\n * to single-pass codegen (the plan is empty), paying only one linear analysis.\n */\nexport function compileSchemas(\n schemas: DiscoveredSchema[],\n options: CompileSchemasOptions,\n): CompileSchemasResult {\n const handle = (exportName: string, err: unknown): void => {\n if (options.onError) {\n options.onError(exportName, err instanceof Error ? err : new Error(String(err)));\n } else {\n throw err;\n }\n };\n\n // Pass 1: extract IR (and fallback refs) for every schema.\n const extracted: Array<{\n exportName: string;\n schema: unknown;\n ir: SchemaIR;\n refEntries: RefEntry[];\n }> = [];\n for (const s of schemas) {\n try {\n const refEntries: RefEntry[] = [];\n const ir = extractSchema(s.schema, refEntries);\n extracted.push({ exportName: s.exportName, schema: s.schema, ir, refEntries });\n } catch (err) {\n handle(s.exportName, err);\n }\n }\n\n // Compact mode delegates the cold error path of total-fast-path schemas to\n // zod, so they emit no slow walk to share — skip the plan (and the dead\n // shared functions it would generate for shapes that now only delegate).\n const plan = options.compact\n ? undefined\n : createSharedSchemaPlan(\n extracted.map((e) => e.ir),\n options.mode,\n );\n\n // Pass 2: generate each validator, sharing repeated slow walks via the plan.\n const results: CompiledSchemaInfo[] = [];\n for (const e of extracted) {\n try {\n const codegenResult = generateValidator(e.ir, e.exportName, {\n refCount: e.refEntries.length,\n mode: options.mode,\n sharedSchemas: plan,\n compact: options.compact,\n });\n // Compact delegation appends the schema itself as a fresh root RefEntry\n // (accessPath \"\" → __rf[N] = the original Zod schema, whose pristine\n // safeParse the validator delegates to). The index was reserved as\n // e.refEntries.length above, so this push lands exactly at it.\n if (codegenResult.rootDelegateRefIndex !== undefined) {\n e.refEntries.push({ schema: e.schema, accessPath: \"\" });\n }\n results.push({ exportName: e.exportName, codegenResult, refEntries: e.refEntries });\n } catch (err) {\n handle(e.exportName, err);\n }\n }\n\n return {\n schemas: results,\n shared: { code: plan?.code ?? \"\", usedHelpers: plan?.usedHelpers ?? new Set() },\n };\n}\n\n/**\n * Aggregate `usedHelpers` across multiple compiled schemas (typically all schemas in one file).\n * Used by the unplugin transform to construct a single import statement per file.\n */\nexport function aggregateUsedHelpers(schemas: CompiledSchemaInfo[]): Set<string> {\n const all = new Set<string>();\n for (const s of schemas) {\n for (const h of s.codegenResult.usedHelpers) all.add(h);\n }\n return all;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAsDA,SAAgB,eACd,SACA,SACsB;CACtB,MAAM,UAAU,YAAoB,QAAuB;EACzD,IAAI,QAAQ,SACV,QAAQ,QAAQ,YAAY,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;OAE/E,MAAM;CAEV;CAGA,MAAM,YAKD,CAAC;CACN,KAAK,MAAM,KAAK,SACd,IAAI;EACF,MAAM,aAAyB,CAAC;EAChC,MAAM,KAAK,cAAc,EAAE,QAAQ,UAAU;EAC7C,UAAU,KAAK;GAAE,YAAY,EAAE;GAAY,QAAQ,EAAE;GAAQ;GAAI;EAAW,CAAC;CAC/E,SAAS,KAAK;EACZ,OAAO,EAAE,YAAY,GAAG;CAC1B;CAMF,MAAM,OAAO,QAAQ,UACjB,KAAA,IACA,uBACE,UAAU,KAAK,MAAM,EAAE,EAAE,GACzB,QAAQ,IACV;CAGJ,MAAM,UAAgC,CAAC;CACvC,KAAK,MAAM,KAAK,WACd,IAAI;EACF,MAAM,gBAAgB,kBAAkB,EAAE,IAAI,EAAE,YAAY;GAC1D,UAAU,EAAE,WAAW;GACvB,MAAM,QAAQ;GACd,eAAe;GACf,SAAS,QAAQ;EACnB,CAAC;EAKD,IAAI,cAAc,yBAAyB,KAAA,GACzC,EAAE,WAAW,KAAK;GAAE,QAAQ,EAAE;GAAQ,YAAY;EAAG,CAAC;EAExD,QAAQ,KAAK;GAAE,YAAY,EAAE;GAAY;GAAe,YAAY,EAAE;EAAW,CAAC;CACpF,SAAS,KAAK;EACZ,OAAO,EAAE,YAAY,GAAG;CAC1B;CAGF,OAAO;EACL,SAAS;EACT,QAAQ;GAAE,MAAM,MAAM,QAAQ;GAAI,aAAa,MAAM,+BAAe,IAAI,IAAI;EAAE;CAChF;AACF;;;;;AAMA,SAAgB,qBAAqB,SAA4C;CAC/E,MAAM,sBAAM,IAAI,IAAY;CAC5B,KAAK,MAAM,KAAK,SACd,KAAK,MAAM,KAAK,EAAE,cAAc,aAAa,IAAI,IAAI,CAAC;CAExD,OAAO;AACT"}
1
+ {"version":3,"file":"pipeline.js","names":[],"sources":["../../src/core/pipeline.ts"],"sourcesContent":["import type { CodeGenResult, CodegenMode, GeneratedSetConstant } from \"./codegen/context.js\";\nimport { createSharedSchemaPlan, SHARED_BLOCK_MARKER } from \"./codegen/dedupe.js\";\nimport { generateValidator } from \"./codegen/index.js\";\nimport type { RefEntry } from \"./extract/index.js\";\nimport { extractSchema } from \"./extract/index.js\";\nimport type { DiscoveredSchema, SchemaIR } from \"./types.js\";\n\n/** Result of compiling a single discovered schema through extract → generate pipeline. */\nexport interface CompiledSchemaInfo {\n exportName: string;\n codegenResult: CodeGenResult;\n refEntries: RefEntry[];\n}\n\n/** Module-scope declarations produced by file-level schema and constant dedup. */\nexport interface SharedSchemaBlock {\n /** Shared constants and `__zcSw_N` functions. Empty string when nothing repeats. */\n code: string;\n /** Runtime helper names referenced by the shared block (lean mode imports). */\n usedHelpers: Set<string>;\n}\n\n/** Output of {@link compileSchemas}: per-schema validators plus the file's shared block. */\nexport interface CompileSchemasResult {\n schemas: CompiledSchemaInfo[];\n shared: SharedSchemaBlock;\n}\n\nexport interface CompileSchemasOptions {\n /** \"inline\" for CLI .compiled.ts; \"lean\" for unplugin (imports from virtual:zod-compiler/runtime). */\n mode: CodegenMode;\n /**\n * Compact output (`output: \"compact\"`). Drop the compiled slow walk for\n * mutation-free, total-fast-path schemas and delegate their cold error path\n * to the retained Zod schema. Disables slow-walk sharing (delegated schemas\n * never emit a walk to share) and appends a root self-RefEntry per delegated\n * schema so `__rf[N]` resolves to the original Zod schema.\n */\n compact?: boolean | undefined;\n /** When provided, per-schema failures call this and continue. Otherwise the first error throws. */\n onError?: (exportName: string, error: Error) => void;\n}\n\n/** Plan exact Set initializers used by at least two validators in this file. */\nfunction planSharedSetConstants(\n results: readonly CompiledSchemaInfo[],\n constantsByResult: ReadonlyMap<CodeGenResult, readonly GeneratedSetConstant[]>,\n): ReadonlyMap<string, string> {\n const byInitializer = new Map<string, Set<CodeGenResult>>();\n for (const { codegenResult } of results) {\n for (const constant of constantsByResult.get(codegenResult) ?? []) {\n const declaration = `var ${constant.name}=${constant.initializer};`;\n // Fast-path generation can emit a Set before a later node aborts. Its\n // preamble is rolled back, so only collect declarations that survived.\n if (!codegenResult.code.includes(declaration)) continue;\n const users = byInitializer.get(constant.initializer);\n if (users === undefined) byInitializer.set(constant.initializer, new Set([codegenResult]));\n else users.add(codegenResult);\n }\n }\n\n const repeated = [...byInitializer.entries()]\n .filter(([, users]) => users.size >= 2)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n return new Map(repeated.map(([initializer], index) => [initializer, `__zcSet_${index}`]));\n}\n\nfunction emitSharedSetConstants(sharedSetNames: ReadonlyMap<string, string>): string {\n return [...sharedSetNames]\n .map(([initializer, name]) => `var ${name}=/* @__PURE__ */${initializer};`)\n .join(\"\\n\");\n}\n\n/**\n * Run the extract → generate pipeline for each discovered schema.\n * Shared by CLI generate and unplugin transform.\n *\n * Pass 1 extracts every schema's IR and plans repeated slow walks. Pass 2\n * generates each validator, calling shared `__zcSw_N` functions instead of\n * re-inlining them. Exact Set initializers reported during generation are then\n * pooled when at least two validators use them. Files with no repetition keep\n * their original local declarations.\n */\nexport function compileSchemas(\n schemas: DiscoveredSchema[],\n options: CompileSchemasOptions,\n): CompileSchemasResult {\n const handle = (exportName: string, err: unknown): void => {\n if (options.onError) {\n options.onError(exportName, err instanceof Error ? err : new Error(String(err)));\n } else {\n throw err;\n }\n };\n\n // Pass 1: extract IR (and fallback refs) for every schema.\n const extracted: Array<{\n exportName: string;\n schema: unknown;\n ir: SchemaIR;\n refEntries: RefEntry[];\n }> = [];\n for (const s of schemas) {\n try {\n const refEntries: RefEntry[] = [];\n const ir = extractSchema(s.schema, refEntries);\n extracted.push({ exportName: s.exportName, schema: s.schema, ir, refEntries });\n } catch (err) {\n handle(s.exportName, err);\n }\n }\n\n // Compact mode delegates the cold error path of total-fast-path schemas to\n // zod, so they emit no slow walk to share — skip the plan (and the dead\n // shared functions it would generate for shapes that now only delegate).\n const plan = options.compact\n ? undefined\n : createSharedSchemaPlan(\n extracted.map((e) => e.ir),\n options.mode,\n );\n\n // Pass 2: generate each validator, sharing repeated slow walks via the plan\n // and observing which Set declarations survive codegen rollback.\n const generated: Array<{\n exportName: string;\n schema: unknown;\n ir: SchemaIR;\n refEntries: RefEntry[];\n codegenResult: CodeGenResult;\n }> = [];\n const constantsByResult = new Map<CodeGenResult, GeneratedSetConstant[]>();\n for (const e of extracted) {\n try {\n const setConstants = new Map<string, GeneratedSetConstant>();\n const codegenResult = generateValidator(e.ir, e.exportName, {\n refCount: e.refEntries.length,\n mode: options.mode,\n sharedSchemas: plan,\n compact: options.compact,\n onSetConstant(constant) {\n setConstants.set(constant.name, constant);\n },\n });\n constantsByResult.set(codegenResult, [...setConstants.values()]);\n generated.push({\n exportName: e.exportName,\n schema: e.schema,\n ir: e.ir,\n refEntries: e.refEntries,\n codegenResult,\n });\n } catch (err) {\n handle(e.exportName, err);\n }\n }\n\n const initialResults = generated.map(\n ({ exportName, codegenResult, refEntries }): CompiledSchemaInfo => ({\n exportName,\n codegenResult,\n refEntries,\n }),\n );\n const sharedSetNames = planSharedSetConstants(initialResults, constantsByResult);\n\n // Regenerate only when sharing is profitable. Selecting shared names before\n // emission avoids textual rewriting of generated JavaScript, where a user\n // enum string can legally contain text resembling a generated identifier.\n if (sharedSetNames.size > 0) {\n for (const entry of generated) {\n entry.codegenResult = generateValidator(entry.ir, entry.exportName, {\n refCount: entry.refEntries.length,\n mode: options.mode,\n sharedSchemas: plan,\n compact: options.compact,\n sharedSetNames,\n });\n }\n }\n\n const results: CompiledSchemaInfo[] = generated.map((entry) => {\n // Compact delegation appends the schema itself as a fresh root RefEntry\n // after the optional regeneration pass, keeping the reserved index stable.\n if (entry.codegenResult.rootDelegateRefIndex !== undefined) {\n entry.refEntries.push({ schema: entry.schema, accessPath: \"\" });\n }\n return {\n exportName: entry.exportName,\n codegenResult: entry.codegenResult,\n refEntries: entry.refEntries,\n };\n });\n\n const sharedSetCode = emitSharedSetConstants(sharedSetNames);\n const slowWalkCode = plan?.code ?? \"\";\n const sharedCode =\n sharedSetCode === \"\"\n ? slowWalkCode\n : slowWalkCode === \"\"\n ? `${SHARED_BLOCK_MARKER}\\n${sharedSetCode}`\n : `${slowWalkCode}\\n${sharedSetCode}`;\n\n return {\n schemas: results,\n shared: { code: sharedCode, usedHelpers: plan?.usedHelpers ?? new Set() },\n };\n}\n\n/**\n * Aggregate `usedHelpers` across multiple compiled schemas (typically all schemas in one file).\n * Used by the unplugin transform to construct a single import statement per file.\n */\nexport function aggregateUsedHelpers(schemas: CompiledSchemaInfo[]): Set<string> {\n const all = new Set<string>();\n for (const s of schemas) {\n for (const h of s.codegenResult.usedHelpers) all.add(h);\n }\n return all;\n}\n"],"mappings":"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"}
@@ -288,9 +288,9 @@ interface ObjectIR {
288
288
  strict?: boolean;
289
289
  /**
290
290
  * Strip unknown keys from the output (zod's DEFAULT `z.object()` behavior),
291
- * set only when the `stripUnknownKeys` build option is enabled AND this is a
292
- * genuine `z.object()` (no catchall) — never on `z.looseObject()` (keep) or
293
- * `z.strictObject()` (reject). Compiled by rebuilding a fresh object from
291
+ * set whenever this is a genuine `z.object()` (no catchall) — never on
292
+ * `z.looseObject()` (keep) or `z.strictObject()` (reject). Compiled by
293
+ * rebuilding a fresh object from
294
294
  * only the declared own keys, mirroring zod's strip exactly. Because the
295
295
  * output is a fresh value, a strip object counts as a {@link hasMutation}
296
296
  * node: it never takes the zero-allocation by-reference fast path, and parent
@@ -406,6 +406,20 @@ interface NullableIR {
406
406
  interface ReadonlyIR {
407
407
  type: "readonly";
408
408
  inner: SchemaIR;
409
+ /**
410
+ * Emit `Object.freeze` on the produced value.
411
+ *
412
+ * Set only when the freeze is OBSERVABLE and the value being frozen is one
413
+ * the compiler allocated (a stripping object, which both the build pass and
414
+ * the eager walk rebuild). Omitted when the inner yields a primitive, where
415
+ * freezing is a no-op and the wrapper compiles to nothing at all.
416
+ *
417
+ * A readonly carrying this flag counts as rebuilding (see rebuildSet), which
418
+ * is what withholds every by-reference shortcut — `fastResultIsInput` turns
419
+ * false, so neither safeParse's `data:input` return nor the `fc` behind
420
+ * `parse()` / `~standard` can hand back an unfrozen input.
421
+ */
422
+ freeze?: boolean;
409
423
  }
410
424
  interface DefaultIR {
411
425
  type: "default";
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/core/types.ts"],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/core/types.ts"],"mappings":"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"}
package/dist/runtime.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { config as __zodCompilerConfig, core as __zcCore, ZodRealError as __zcZodError } from "zod";
2
2
  function __zcUw(m){return typeof m==="string"?m:(m===undefined||m===null?undefined:m.message);}var __zcMsg=function(iss){var c=__zodCompilerConfig(),m;if(c.customError){m=__zcUw(c.customError(iss));if(m!==undefined&&m!==null)return m;}if(c.localeError){m=__zcUw(c.localeError(iss));if(m!==undefined&&m!==null)return m;}return "Invalid input";};
3
3
  function __ZcFail(e,f,i){this.success=false;this._e=e;this._f=f;this._i=i;this._c=undefined;}Object.defineProperty(__ZcFail.prototype,"error",{configurable:true,get:function(){if(this._c)return this._c;var e=this._f!==null?this._f(this._i):this._e;for(var i=0;i<e.length;i++){if(e[i].message===undefined&&typeof __zcMsg==="function")e[i].message=__zcMsg(e[i]);delete e[i].input;delete e[i].continue;}return this._c=new __zcZodError(e);}});
4
- function __ZcFailZ(z,i){this.success=false;this._z=z;this._i=i;this._c=undefined;}Object.defineProperty(__ZcFailZ.prototype,"error",{configurable:true,get:function(){return this._c||(this._c=this._z(this._i).error);}});
4
+ function __ZcFailZ(z,r,i){this.success=false;this._z=z;this._r=r;this._i=i;this._c=undefined;}Object.defineProperty(__ZcFailZ.prototype,"error",{configurable:true,get:function(){return this._c||(this._c=this._z.call(this._r,this._i).error);}});
5
5
  export function __zcMkv(fn,schema,fc,is){var w=schema||{};var zpa=w.parseAsync,zspa=w.safeParseAsync;w.parse=fc?function(input){if(fc(input))return input;var r=fn(input);if(r.success)return r.data;throw r.error;}:function(input){var r=fn(input);if(r.success)return r.data;throw r.error;};w.safeParse=fn;w.safeParseAsync=function(input){try{return Promise.resolve(fn(input));}catch(e){if(zspa)return zspa(input);throw e;}};w.parseAsync=fc?function(input){try{if(fc(input))return Promise.resolve(input);var r=fn(input);if(r.success)return Promise.resolve(r.data);return Promise.reject(r.error);}catch(e){if(zpa)return zpa(input);throw e;}}:function(input){try{var r=fn(input);if(r.success)return Promise.resolve(r.data);return Promise.reject(r.error);}catch(e){if(zpa)return zpa(input);throw e;}};w.is=is||function(input){return fn(input).success;};var s=w["~standard"],zv=s&&s.validate;Object.defineProperty(w,"~standard",{configurable:true,value:{version:1,vendor:(s&&s.vendor)||"zod",validate:function(input){var r;try{if(fc&&fc(input))return{value:input};r=fn(input);}catch(e){if(zv)return zv(input);throw e;}return r.success?{value:r.data}:{issues:r.error.issues};}}});return w;}
6
6
  export function __zcFin(e,d){if(!e.length)return{success:true,data:d};return new __ZcFail(e,null,null);}
7
7
  export function __zcFinD(f,inp){return new __ZcFail(null,f,inp);}
8
- export function __zcFinZ(z,i){return new __ZcFailZ(z,i);}
8
+ export function __zcFinZ(z,r,i){return new __ZcFailZ(z,r,i);}
9
9
  function __zcSrRun(f,p){var r=f(p);if(r&&typeof r.then==="function"){throw new __zcCore.$ZodAsyncError();}}
10
10
  export function __zcTS(m,o,i,inp,p,msg){var r={origin:o,code:"too_small",minimum:m,inclusive:i,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}
11
11
  export function __zcTSn(m,o,inp,p,msg){var r={code:"too_small",minimum:m,origin:o,input:inp,path:p,continue:false};if(msg!==undefined)r.message=msg;return r;}