zod-compiler 2.0.6 → 2.0.8

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 (66) hide show
  1. package/README.md +59 -55
  2. package/dist/cli/emitter.d.ts.map +1 -1
  3. package/dist/cli/emitter.js +10 -4
  4. package/dist/cli/emitter.js.map +1 -1
  5. package/dist/core/codegen/build-path.d.ts.map +1 -1
  6. package/dist/core/codegen/build-path.js +15 -6
  7. package/dist/core/codegen/build-path.js.map +1 -1
  8. package/dist/core/codegen/context.d.ts +21 -3
  9. package/dist/core/codegen/context.d.ts.map +1 -1
  10. package/dist/core/codegen/context.js +17 -1
  11. package/dist/core/codegen/context.js.map +1 -1
  12. package/dist/core/codegen/index.d.ts +8 -0
  13. package/dist/core/codegen/index.d.ts.map +1 -1
  14. package/dist/core/codegen/index.js +13 -11
  15. package/dist/core/codegen/index.js.map +1 -1
  16. package/dist/core/codegen/issue-decls.d.ts +11 -1
  17. package/dist/core/codegen/issue-decls.d.ts.map +1 -1
  18. package/dist/core/codegen/issue-decls.js +12 -1
  19. package/dist/core/codegen/issue-decls.js.map +1 -1
  20. package/dist/core/codegen/schemas/string.d.ts +15 -2
  21. package/dist/core/codegen/schemas/string.d.ts.map +1 -1
  22. package/dist/core/codegen/schemas/string.js +97 -35
  23. package/dist/core/codegen/schemas/string.js.map +1 -1
  24. package/dist/core/compile.d.ts +4 -3
  25. package/dist/core/compile.d.ts.map +1 -1
  26. package/dist/core/compile.js +1 -0
  27. package/dist/core/compile.js.map +1 -1
  28. package/dist/core/extract/error-class.d.ts +32 -0
  29. package/dist/core/extract/error-class.d.ts.map +1 -0
  30. package/dist/core/extract/error-class.js +13 -0
  31. package/dist/core/extract/error-class.js.map +1 -0
  32. package/dist/core/iife.d.ts +52 -18
  33. package/dist/core/iife.d.ts.map +1 -1
  34. package/dist/core/iife.js +82 -20
  35. package/dist/core/iife.js.map +1 -1
  36. package/dist/core/pipeline.d.ts +1 -0
  37. package/dist/core/pipeline.d.ts.map +1 -1
  38. package/dist/core/pipeline.js +6 -1
  39. package/dist/core/pipeline.js.map +1 -1
  40. package/dist/core/types.d.ts +16 -5
  41. package/dist/core/types.d.ts.map +1 -1
  42. package/dist/is-zod-schema.d.ts +5 -3
  43. package/dist/is-zod-schema.d.ts.map +1 -1
  44. package/dist/is-zod-schema.js +3 -1
  45. package/dist/is-zod-schema.js.map +1 -1
  46. package/dist/jit.d.ts +5 -4
  47. package/dist/jit.d.ts.map +1 -1
  48. package/dist/jit.js +17 -3
  49. package/dist/jit.js.map +1 -1
  50. package/dist/register/index.js.map +1 -1
  51. package/dist/runtime.d.ts +3 -0
  52. package/dist/runtime.js +7 -2
  53. package/dist/static-filter.d.ts +19 -1
  54. package/dist/static-filter.d.ts.map +1 -1
  55. package/dist/static-filter.js +27 -1
  56. package/dist/static-filter.js.map +1 -1
  57. package/dist/unplugin/hoist-compile.d.ts.map +1 -1
  58. package/dist/unplugin/hoist-compile.js +5 -1
  59. package/dist/unplugin/hoist-compile.js.map +1 -1
  60. package/dist/unplugin/transform.d.ts.map +1 -1
  61. package/dist/unplugin/transform.js +10 -4
  62. package/dist/unplugin/transform.js.map +1 -1
  63. package/dist/unplugin/virtual.d.ts.map +1 -1
  64. package/dist/unplugin/virtual.js +6 -1
  65. package/dist/unplugin/virtual.js.map +1 -1
  66. package/package.json +1 -1
@@ -2,7 +2,9 @@
2
2
  /**
3
3
  * Is this value a Zod schema? Zod schemas carry `_zod.def`, so that is the
4
4
  * probe — the same one auto-discovery uses at build time and `jitAll` uses at
5
- * runtime.
5
+ * runtime. It holds for every flavor (classic, `zod/mini`, raw `zod/v4/core`),
6
+ * so the narrowing is to the `$ZodType` they all extend, not to classic's
7
+ * `ZodType`.
6
8
  *
7
9
  * The probe is applied to values the caller does not control: every own value
8
10
  * of a module namespace under `jitAll`, every export of a candidate file under
@@ -1 +1 @@
1
- {"version":3,"file":"is-zod-schema.js","names":[],"sources":["../src/is-zod-schema.ts"],"sourcesContent":["import type { ZodType } from \"zod\";\n\n/**\n * Is this value a Zod schema? Zod schemas carry `_zod.def`, so that is the\n * probe — the same one auto-discovery uses at build time and `jitAll` uses at\n * runtime.\n *\n * The probe is applied to values the caller does not control: every own value\n * of a module namespace under `jitAll`, every export of a candidate file under\n * discovery. Some of those are not plain objects. A Proxy can trap `has` and\n * `get` and throw — an ORM model, a strict test double, an i18n catch-all — and\n * `\"_zod\" in value` fires the first trap while reading `_zod` fires the second.\n * Whatever such a value is, it is not a Zod schema, and asking must not take\n * down the caller: an uncaught throw here aborts the importing app at boot, or\n * the build, over an object that was never a candidate.\n *\n * Kept as a leaf module with no runtime imports: `jit`/`jitAll` reach it on the\n * startup path, where pulling in the build-time graph would be pure cost.\n */\nexport function isZodSchema(value: unknown): value is ZodType {\n if (typeof value !== \"object\" || value === null) return false;\n try {\n if (!(\"_zod\" in value)) return false;\n const internal = (value as Record<string, unknown>)[\"_zod\"];\n return typeof internal === \"object\" && internal !== null && \"def\" in internal;\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAmBA,SAAgB,YAAY,OAAkC;CAC5D,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;CACxD,IAAI;EACF,IAAI,EAAE,UAAU,QAAQ,OAAO;EAC/B,MAAM,WAAY,MAAkC;EACpD,OAAO,OAAO,aAAa,YAAY,aAAa,QAAQ,SAAS;CACvE,QAAQ;EACN,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"is-zod-schema.js","names":[],"sources":["../src/is-zod-schema.ts"],"sourcesContent":["import type { core } from \"zod\";\n\n/**\n * Is this value a Zod schema? Zod schemas carry `_zod.def`, so that is the\n * probe — the same one auto-discovery uses at build time and `jitAll` uses at\n * runtime. It holds for every flavor (classic, `zod/mini`, raw `zod/v4/core`),\n * so the narrowing is to the `$ZodType` they all extend, not to classic's\n * `ZodType`.\n *\n * The probe is applied to values the caller does not control: every own value\n * of a module namespace under `jitAll`, every export of a candidate file under\n * discovery. Some of those are not plain objects. A Proxy can trap `has` and\n * `get` and throw — an ORM model, a strict test double, an i18n catch-all — and\n * `\"_zod\" in value` fires the first trap while reading `_zod` fires the second.\n * Whatever such a value is, it is not a Zod schema, and asking must not take\n * down the caller: an uncaught throw here aborts the importing app at boot, or\n * the build, over an object that was never a candidate.\n *\n * Kept as a leaf module with no runtime imports: `jit`/`jitAll` reach it on the\n * startup path, where pulling in the build-time graph would be pure cost.\n */\nexport function isZodSchema(value: unknown): value is core.$ZodType {\n if (typeof value !== \"object\" || value === null) return false;\n try {\n if (!(\"_zod\" in value)) return false;\n const internal = (value as Record<string, unknown>)[\"_zod\"];\n return typeof internal === \"object\" && internal !== null && \"def\" in internal;\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,YAAY,OAAwC;CAClE,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;CACxD,IAAI;EACF,IAAI,EAAE,UAAU,QAAQ,OAAO;EAC/B,MAAM,WAAY,MAAkC;EACpD,OAAO,OAAO,aAAa,YAAY,aAAa,QAAQ,SAAS;CACvE,QAAQ;EACN,OAAO;CACT;AACF"}
package/dist/jit.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { CompiledSchema } from "./core/types.js";
2
- import { ZodType, output } from "zod";
1
+ import { CompilableSchema, CompiledSchema } from "./core/types.js";
2
+ import { output } from "zod";
3
3
  //#region src/jit.d.ts
4
4
  interface JitOptions {
5
5
  /**
@@ -31,9 +31,10 @@ interface JitOptions {
31
31
  *
32
32
  * Schemas the compiler cannot reproduce fall back to Zod per sub-schema, the
33
33
  * same way they do at build time; a schema that cannot be compiled at all is
34
- * left as plain Zod.
34
+ * left as plain Zod. `zod/mini` schemas compile the same way, and keep failing
35
+ * with Mini's own error class (see {@link CompilableSchema}).
35
36
  */
36
- declare function jit<T extends ZodType>(schema: T, options?: JitOptions): T & CompiledSchema<output<T>>;
37
+ declare function jit<T extends CompilableSchema>(schema: T, options?: JitOptions): T & CompiledSchema<output<T>>;
37
38
  /**
38
39
  * Compile every Zod schema found among an object's own values — typically a
39
40
  * module namespace, so a whole schema file opts in with one call:
package/dist/jit.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"jit.d.ts","names":[],"sources":["../src/jit.ts"],"mappings":";;;UA2FiB;;;;;;;EAOf;;EAEA;;;;;;;;;;;;;;;;;;;;;;;iBAwBc,IAAI,UAAU,SAC5B,QAAQ,GACR,UAAU,aACT,IAAI,eAAe,OAAO;;;;;;;;;;;;;;iBAiOb,OAAO,iBAAiB,UAAU"}
1
+ {"version":3,"file":"jit.d.ts","names":[],"sources":["../src/jit.ts"],"mappings":";;;UA4GiB;;;;;;;EAOf;;EAEA;;;;;;;;;;;;;;;;;;;;;;;;iBAyBc,IAAI,UAAU,kBAC5B,QAAQ,GACR,UAAU,aACT,IAAI,eAAe,OAAO;;;;;;;;;;;;;;iBAiOb,OAAO,iBAAiB,UAAU"}
package/dist/jit.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { isZodSchema } from "./is-zod-schema.js";
2
- import { FAILZ_CLASS_DECL, FAIL_CLASS_DECL, FINZ_DECL, FIN_DECL, FIN_DEFERRED_DECL, MK_VALIDATOR_DECL, ZOD_MSG_DECLARATION, generateIIFE } from "./core/iife.js";
2
+ import { FAILZ_CLASS_DECL, FAIL_CLASS_DECL, FAIL_CORE_CLASS_DECL, FINZ_DECL, FIN_CORE_DECL, FIN_DECL, FIN_DEFERRED_CORE_DECL, FIN_DEFERRED_DECL, MK_VALIDATOR_DECL, ZOD_MSG_DECLARATION, generateIIFE, usesCoreErrors } from "./core/iife.js";
3
3
  import { isSupportedZodVersion, isUnsupportedZodVersionError, unsupportedZodVersionMessage, warnUnsupportedZodOnce, zodVersionOf } from "./core/extract/zod-version.js";
4
4
  import { compileSchemas } from "./core/pipeline.js";
5
5
  import { ZodRealError, config, core } from "zod";
@@ -47,6 +47,10 @@ import { ZodRealError, config, core } from "zod";
47
47
  * evaluated code has no module scope to resolve. Byte-for-byte the same helper
48
48
  * source the CLI emitter writes into a `.compiled.ts`, so a JIT validator and
49
49
  * an AOT one share their entire runtime layer.
50
+ *
51
+ * Two of them, by the error class the schema fails with: the classic finalizers,
52
+ * or the `$ZodError` twins a `zod/mini` schema's validator calls. Each schema is
53
+ * evaluated on its own, so it gets exactly the pair its code names.
50
54
  */
51
55
  const RUNTIME_PRELUDE = [
52
56
  ZOD_MSG_DECLARATION,
@@ -57,6 +61,15 @@ const RUNTIME_PRELUDE = [
57
61
  FAILZ_CLASS_DECL,
58
62
  FINZ_DECL
59
63
  ].join("\n");
64
+ const RUNTIME_PRELUDE_CORE = [
65
+ ZOD_MSG_DECLARATION,
66
+ FAIL_CORE_CLASS_DECL,
67
+ MK_VALIDATOR_DECL,
68
+ FIN_CORE_DECL,
69
+ FIN_DEFERRED_CORE_DECL,
70
+ FAILZ_CLASS_DECL,
71
+ FINZ_DECL
72
+ ].join("\n");
60
73
  /**
61
74
  * Methods `__zcMkv` installs. Each is fronted by a compile-on-read accessor
62
75
  * until the schema materializes.
@@ -95,7 +108,8 @@ const seen = /* @__PURE__ */ new WeakSet();
95
108
  *
96
109
  * Schemas the compiler cannot reproduce fall back to Zod per sub-schema, the
97
110
  * same way they do at build time; a schema that cannot be compiled at all is
98
- * left as plain Zod.
111
+ * left as plain Zod. `zod/mini` schemas compile the same way, and keep failing
112
+ * with Mini's own error class (see {@link CompilableSchema}).
99
113
  */
100
114
  function jit(schema, options) {
101
115
  const target = schema;
@@ -285,7 +299,7 @@ function buildValidator(schema, options) {
285
299
  const compiled = schemas[0];
286
300
  if (compiled === void 0) throw new Error("zod-compiler: schema produced no validator");
287
301
  const body = [
288
- RUNTIME_PRELUDE,
302
+ usesCoreErrors(compiled.codegenResult.usedHelpers) ? RUNTIME_PRELUDE_CORE : RUNTIME_PRELUDE,
289
303
  shared.code,
290
304
  `return ${generateIIFE("__schema", compiled)};`
291
305
  ].join("\n");
package/dist/jit.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"jit.js","names":["zodCore","zodConfig"],"sources":["../src/jit.ts"],"sourcesContent":["/**\n * Runtime compilation — the same extract → codegen pipeline the build plugin\n * runs, executed in-process and evaluated through `new Function`.\n *\n * The AOT paths (unplugin, CLI) need a build step to fire. Plenty of everyday\n * code has none: `tsx server.ts`, `node --experimental-strip-types`, a Jest\n * suite, a serverless handler bundled by someone else's toolchain, a library\n * that ships schemas to consumers. There `compile()` is a no-op and every parse\n * runs plain Zod. `jit()` closes that gap — one call, no build integration,\n * measured 3-25x on everyday schemas at ~0.1-0.2 ms of one-time compilation.\n *\n * Nothing here re-implements validation: {@link compileSchemas} and\n * {@link generateIIFE} are the exact modules the plugin and CLI use, so the\n * generated validator, its Zod parity and its performance are identical to what\n * a build would have emitted. The only difference is *when* the code is\n * produced.\n *\n * Compilation is LAZY by default: `jit()` installs accessors that compile on\n * the first read of a parse method and replace themselves with the compiled\n * ones. Importing a module of 500 schemas therefore costs nothing, and a\n * serverless invocation touching three of them pays for three.\n *\n * Zod itself keeps the parse methods on the PROTOTYPE (since 4.4): each is a\n * getter that binds the method on first read and installs the bound copy as an\n * own data property, so a fresh schema carries no own `parse` / `safeParse` at\n * all, while one that has been read already does. `jit()` meets both shapes —\n * it snapshots whatever the slots hold, fronts them with its own accessors, and\n * hands back exactly what it found whenever it has to step aside.\n *\n * Runtime code generation is not always permitted — a strict CSP without\n * `unsafe-eval`, some edge runtimes. Zod v4 has the same constraint (its object\n * fast-pass is itself a `new Function`) and already exposes the two switches\n * for it: the `core.util.allowsEval` probe and `z.config({ jitless: true })`.\n * `jit()` honours both and degrades to plain Zod, so one setting governs both\n * compilers. Those targets are where the build plugin belongs anyway — it emits\n * the same validator with no runtime evaluation at all.\n */\n\nimport { config as zodConfig, core as zodCore, ZodRealError, type output, type ZodType } from \"zod\";\nimport {\n FAIL_CLASS_DECL,\n FAILZ_CLASS_DECL,\n FIN_DECL,\n FIN_DEFERRED_DECL,\n FINZ_DECL,\n generateIIFE,\n MK_VALIDATOR_DECL,\n ZOD_MSG_DECLARATION,\n} from \"./core/iife.js\";\nimport {\n isSupportedZodVersion,\n isUnsupportedZodVersionError,\n unsupportedZodVersionMessage,\n warnUnsupportedZodOnce,\n zodVersionOf,\n} from \"./core/extract/zod-version.js\";\nimport { compileSchemas } from \"./core/pipeline.js\";\nimport type { CompiledSchema } from \"./core/types.js\";\nimport { isZodSchema } from \"./is-zod-schema.js\";\n\n/**\n * The declarations `ZOD_CONFIG_IMPORT` supplies to an emitted module, minus the\n * import itself — `zod`'s three bindings arrive as parameters instead, so the\n * evaluated code has no module scope to resolve. Byte-for-byte the same helper\n * source the CLI emitter writes into a `.compiled.ts`, so a JIT validator and\n * an AOT one share their entire runtime layer.\n */\nconst RUNTIME_PRELUDE = [\n ZOD_MSG_DECLARATION,\n FAIL_CLASS_DECL,\n MK_VALIDATOR_DECL,\n FIN_DECL,\n FIN_DEFERRED_DECL,\n FAILZ_CLASS_DECL,\n FINZ_DECL,\n].join(\"\\n\");\n\n/**\n * Methods `__zcMkv` installs. Each is fronted by a compile-on-read accessor\n * until the schema materializes.\n *\n * `~standard` earns its place: Zod builds it as a closure over `_zod.run`, not\n * over the schema's `safeParse` property, so a Standard Schema consumer (tRPC,\n * Hono, TanStack Form) that never touches `safeParse` would otherwise keep\n * running plain Zod forever behind a \"compiled\" schema.\n */\nconst SLOTS = [\"parse\", \"safeParse\", \"parseAsync\", \"safeParseAsync\", \"is\", \"~standard\"] as const;\n\n/** Schemas already handed to `jit()`, so a second call is a no-op rather than a recompile. */\nconst seen = new WeakSet<object>();\n\nexport interface JitOptions {\n /**\n * Compile immediately instead of on first use. Costs ~0.1-0.2 ms per schema\n * at import time; useful for a long-lived server that would rather pay during\n * startup than on the first request, or to surface a compilation failure\n * eagerly. Default `false`.\n */\n eager?: boolean | undefined;\n /** Use compact codegen and delegate cold error production to Zod. @default \"schema\" */\n output?: \"schema\" | \"compact\" | undefined;\n}\n\n/**\n * Compile `schema` in-process and install the compiled `parse` / `safeParse` /\n * `parseAsync` / `safeParseAsync` / `is` / `~standard` on it.\n *\n * Returns the SAME object — identity-preserving exactly as the build plugin is,\n * so `.shape`, `_zod`, `instanceof`, `z.toJSONSchema()`, `.meta()` and\n * composition into a larger schema all keep working, and every existing\n * reference to the schema picks the compiled methods up.\n *\n * ```ts\n * import { z } from \"zod\";\n * import { jit } from \"zod-compiler/jit\";\n *\n * export const UserSchema = jit(z.object({ name: z.string(), email: z.email() }));\n * UserSchema.safeParse(input); // compiled on this first call\n * ```\n *\n * Schemas the compiler cannot reproduce fall back to Zod per sub-schema, the\n * same way they do at build time; a schema that cannot be compiled at all is\n * left as plain Zod.\n */\nexport function jit<T extends ZodType>(\n schema: T,\n options?: JitOptions,\n): T & CompiledSchema<output<T>> {\n const target = schema as unknown as Record<string, unknown>;\n if (seen.has(target)) return schema as T & CompiledSchema<output<T>>;\n\n // A schema built by a zod this release does not reproduce is left as plain\n // Zod — correct, just not faster — and said so once (see zod-version.ts).\n // Not thrown: `jit()` runs at module scope, where a throw takes the importing\n // app down at boot over what is a dependency-range problem. Checked before\n // anything is installed, so neither the eager path nor a later\n // compile-on-read accessor can reach the pipeline for it. A value that is not\n // a zod schema at all is not a version problem and takes the usual route.\n const version = zodVersionOf(schema);\n if (isZodSchema(schema) && !isSupportedZodVersion(version)) {\n warnUnsupportedZodOnce(unsupportedZodVersionMessage(version));\n return schema as T & CompiledSchema<output<T>>;\n }\n seen.add(target);\n\n if (options?.eager === true) {\n materialize(schema, options);\n return schema as T & CompiledSchema<output<T>>;\n }\n\n // Snapshot what each slot holds before anything is installed: materialize()\n // restores it before handing the object to `__zcMkv`, so the generated code\n // sees a pristine schema — it captures the original `parseAsync` /\n // `safeParseAsync` as its throw paths, and capturing a stub there would loop\n // back into itself. An untouched schema holds NOTHING here: Zod keeps the\n // methods on the prototype and only installs a bound own copy on first read,\n // so `undefined` is the common entry and means \"back to the prototype\". A\n // schema that was read before `jit()` — or that the build plugin's `__zcMkv`\n // already materialized — carries the copies, and those go back verbatim.\n const original = new Map<string, PropertyDescriptor | undefined>();\n try {\n for (const slot of SLOTS) {\n original.set(slot, Object.getOwnPropertyDescriptor(target, slot));\n }\n } catch {\n // The target answers a descriptor query with a throw — an exotic wrapper,\n // not anything Zod built. Without a snapshot there is nothing to roll back\n // to, so install nothing and hand back the schema exactly as it came.\n return schema as T & CompiledSchema<output<T>>;\n }\n\n // Installing the accessors is the step most likely to throw rather than\n // degrade: a slot locked non-configurable (a future Zod, another wrapper) makes\n // defineProperty raise, and `jit()` is called at module scope — so an\n // unhandled throw here takes down the importing app at boot. Roll back to\n // whatever Zod had and leave the schema alone instead.\n let pending = true;\n try {\n installAccessors(\n target,\n original,\n () => {\n if (!pending) return;\n pending = false;\n restore(target, original);\n materialize(schema, options);\n },\n () => {\n if (!pending) return;\n pending = false;\n // Restore EVERY slot, not just the one being written. A left-behind\n // accessor whose trigger has been cancelled would read `target[slot]`\n // and re-enter itself — unbounded recursion, which is what a later read\n // of an untouched slot (`~standard`, from a Standard Schema consumer)\n // would otherwise hit.\n //\n // Reached only when something WRITES a slot before anything reads one: a\n // test double, another wrapper, or an AOT `safeParse` assigned directly.\n // The build plugin's own `__zcMkv` does not land here — its first\n // statement READS `parseAsync`/`safeParseAsync` to capture their\n // originals, so it triggers materialization and then overwrites the\n // compiled-by-jit methods with the compiled-by-plugin ones.\n restore(target, original);\n },\n );\n } catch {\n pending = false;\n restore(target, original);\n }\n\n return schema as T & CompiledSchema<output<T>>;\n}\n\n/**\n * Front every installed method with a compile-on-read accessor. `trigger`\n * materializes the schema, which normally replaces these accessors with the\n * compiled methods (or restores what Zod had). When that replacement does not\n * take, the getter serves Zod's own method rather than re-reading the slot —\n * see the re-entrancy note in the body.\n */\nfunction installAccessors(\n target: Record<string, unknown>,\n original: ReadonlyMap<string, PropertyDescriptor | undefined>,\n trigger: () => void,\n cancel: () => void,\n): void {\n for (const slot of SLOTS) {\n // Re-entrancy is settled structurally rather than by inspection. Reading the\n // slot again is how this getter normally hands over — to the compiled method\n // materialize() installed, or to whatever restore() put back — but the\n // handover can fail to take: a target frozen after `jit()` refuses both, and\n // a second copy of this module in the graph leaves ITS accessor on the slot,\n // so the two bounce reads between them. `trigger()` is spent by then, so\n // either way the read recurses until the stack blows. While a read is already\n // in flight, or once the accessor is known to be stranded, serve Zod's own\n // method instead: a schema that cannot be compiled still parses.\n let reading = false;\n // A stranded accessor is permanent, so what it serves is resolved once.\n let stranded: { value: unknown } | undefined;\n const serveZod = (): unknown => {\n stranded ??= { value: fromZod(target, original, slot) };\n return stranded.value;\n };\n const read = function (): unknown {\n if (reading) return serveZod();\n reading = true;\n try {\n trigger();\n if (stillFrontedBy(target, slot, read)) return serveZod();\n return target[slot];\n } finally {\n reading = false;\n }\n };\n Object.defineProperty(target, slot, {\n configurable: true,\n // Keep the schema's own-key set as it was. A slot Zod already materialized\n // is an enumerable own copy, so the accessor fronting it is enumerable too;\n // an untouched slot has no own key at all, and `is` never exists on a Zod\n // schema, so those stay out of `Object.keys` — the non-enumerable\n // convention `compile()` already uses.\n enumerable: original.get(slot)?.enumerable ?? false,\n get: read,\n set(value: unknown) {\n // Someone overwrote a method before first use (a test double, another\n // wrapper). Their value wins, and compilation is cancelled outright —\n // materializing later would restore the snapshot over it. With the\n // accessor gone the write does what it would have without `jit()`:\n // overwrite the own copy, or run Zod's prototype setter, which installs\n // one. Only a target that refused the rollback still routes here, and a\n // direct define is the one way left to honour the write.\n cancel();\n if (!stillFrontedBy(target, slot, read)) {\n target[slot] = value;\n return;\n }\n Object.defineProperty(target, slot, {\n configurable: true,\n enumerable: original.get(slot)?.enumerable ?? false,\n value,\n writable: true,\n });\n },\n });\n }\n}\n\n/**\n * Is `slot` still fronted by this very accessor — i.e. did the replacement that\n * `trigger()` was supposed to perform not take? A target that will not answer\n * the question is assumed to still hold it, since reading the slot to find out\n * is the recursion being avoided.\n */\nfunction stillFrontedBy(\n target: Record<string, unknown>,\n slot: string,\n getter: () => unknown,\n): boolean {\n try {\n return Object.getOwnPropertyDescriptor(target, slot)?.get === getter;\n } catch {\n return true;\n }\n}\n\n/**\n * Zod's own value for `slot`, bypassing the accessor that fronts it.\n *\n * A slot the snapshot holds is answered from the snapshot. One it does not hold\n * lives on the prototype, where Zod's getter binds the method to its receiver\n * and installs the bound copy as an own property — on a target that accepts\n * the install, that also replaces the stranded accessor with the copy, which is\n * the handover materialize() could not make. A target that refuses the install\n * (frozen, or a wrapper that vetoes `defineProperty`) makes Zod's getter throw\n * instead; there the read goes through a stand-in that inherits from the target,\n * so the copy lands on the stand-in and the method reaches `_zod` through the\n * chain. `undefined` for `is`, which no plain Zod schema carries — the same\n * thing every other degradation path leaves there.\n */\nfunction fromZod(\n target: Record<string, unknown>,\n original: ReadonlyMap<string, PropertyDescriptor | undefined>,\n slot: string,\n): unknown {\n const descriptor = original.get(slot);\n if (descriptor !== undefined) {\n // A snapshot can hold another accessor (a second copy of this module\n // installed first), so invoke it rather than reading a `value` it lacks.\n return descriptor.get === undefined ? descriptor.value : descriptor.get.call(target);\n }\n const proto = Object.getPrototypeOf(target) as object | null;\n if (proto === null) return undefined;\n try {\n return Reflect.get(proto, slot, target);\n } catch {\n return Reflect.get(proto, slot, Object.create(target));\n }\n}\n\n/**\n * Compile every Zod schema found among an object's own values — typically a\n * module namespace, so a whole schema file opts in with one call:\n *\n * ```ts\n * import * as schemas from \"./schemas.js\";\n * jitAll(schemas);\n * ```\n *\n * The namespace object itself is never written to (a module namespace is\n * read-only); `jit()` mutates the schema objects it holds, which is what every\n * importer of that module already references.\n */\nexport function jitAll(schemas: object, options?: JitOptions): void {\n for (const value of Object.values(schemas)) {\n if (isZodSchema(value)) jit(value, options);\n }\n}\n\n/**\n * Put back what the snapshot holds, dropping the compile-on-read accessors: the\n * own copy Zod (or the build plugin) had installed, or nothing at all, so the\n * slot reads through to Zod's prototype getter again.\n */\nfunction restore(\n target: Record<string, unknown>,\n original: ReadonlyMap<string, PropertyDescriptor | undefined>,\n): void {\n for (const slot of SLOTS) {\n // Per slot, because this also runs as the rollback for a failed install: a\n // target that refuses one slot must not cost the others their restoration.\n // A slot left fronted by its accessor still reads correctly — the getter\n // serves Zod's own method — but it keeps a redundant indirection, so\n // restoring what can be restored is worth the try/catch.\n try {\n const descriptor = original.get(slot);\n if (descriptor === undefined) delete target[slot];\n else Object.defineProperty(target, slot, descriptor);\n } catch {\n // Nothing further to try for this slot.\n }\n }\n}\n\n/**\n * Whether runtime code generation is permitted here. Read per call, never\n * snapshotted: `z.config({ jitless: true })` runs in an entry point, after the\n * schema modules it imports have already been evaluated.\n */\nfunction codegenAllowed(): boolean {\n return zodCore.globalConfig.jitless !== true && zodCore.util.allowsEval.value;\n}\n\n/**\n * Run the pipeline and let the generated IIFE install its methods on `schema`.\n * Swallows failure: a schema that cannot be compiled keeps Zod's own methods,\n * which the caller already has, so there is nothing to report and nothing to\n * break.\n */\nfunction materialize(schema: unknown, options?: JitOptions): void {\n if (!codegenAllowed()) return;\n try {\n buildValidator(schema, options);\n } catch (error) {\n // Left as plain Zod. Deliberately silent: `jit()` is an optimization, and a\n // schema using a construct the compiler declines is a supported outcome,\n // not an error. The one exception is the zod version guard — a mismatched\n // zod is a configuration problem the user has to hear about. `jit()` itself\n // refuses such a schema before installing anything; this covers a caller\n // that reached the pipeline another way.\n if (isUnsupportedZodVersionError(error)) warnUnsupportedZodOnce(error.message);\n }\n}\n\n/**\n * Generate the validator and evaluate it, reproducing the module a\n * `.compiled.ts` would have been: helper preamble, the file-level shared block,\n * then the `__zcMkv` IIFE whose `__rf[]` bases and install target are the live\n * schema object passed in as `__schema`.\n */\nfunction buildValidator(schema: unknown, options?: JitOptions): void {\n const { schemas, shared } = compileSchemas([{ exportName: \"jit\", schema }], {\n compact: options?.output === \"compact\",\n mode: \"inline\",\n });\n const compiled = schemas[0];\n if (compiled === undefined) throw new Error(\"zod-compiler: schema produced no validator\");\n\n const body = [RUNTIME_PRELUDE, shared.code, `return ${generateIIFE(\"__schema\", compiled)};`].join(\n \"\\n\",\n );\n\n // The three bindings ZOD_CONFIG_IMPORT would have imported, passed in so the\n // evaluated code needs no module resolution of its own.\n // oxlint-disable-next-line no-new-func -- generating the validator IS the feature\n const factory = new Function(\n \"__zodCompilerConfig\",\n \"__zcCore\",\n \"__zcZodError\",\n \"__schema\",\n body,\n ) as (\n zodConfigFn: typeof zodConfig,\n zodCoreNs: typeof zodCore,\n zodErrorCtor: typeof ZodRealError,\n target: unknown,\n ) => unknown;\n\n factory(zodConfig, zodCore, ZodRealError, schema);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"jit.js","names":["zodCore","zodConfig"],"sources":["../src/jit.ts"],"sourcesContent":["/**\n * Runtime compilation — the same extract → codegen pipeline the build plugin\n * runs, executed in-process and evaluated through `new Function`.\n *\n * The AOT paths (unplugin, CLI) need a build step to fire. Plenty of everyday\n * code has none: `tsx server.ts`, `node --experimental-strip-types`, a Jest\n * suite, a serverless handler bundled by someone else's toolchain, a library\n * that ships schemas to consumers. There `compile()` is a no-op and every parse\n * runs plain Zod. `jit()` closes that gap — one call, no build integration,\n * measured 3-25x on everyday schemas at ~0.1-0.2 ms of one-time compilation.\n *\n * Nothing here re-implements validation: {@link compileSchemas} and\n * {@link generateIIFE} are the exact modules the plugin and CLI use, so the\n * generated validator, its Zod parity and its performance are identical to what\n * a build would have emitted. The only difference is *when* the code is\n * produced.\n *\n * Compilation is LAZY by default: `jit()` installs accessors that compile on\n * the first read of a parse method and replace themselves with the compiled\n * ones. Importing a module of 500 schemas therefore costs nothing, and a\n * serverless invocation touching three of them pays for three.\n *\n * Zod itself keeps the parse methods on the PROTOTYPE (since 4.4): each is a\n * getter that binds the method on first read and installs the bound copy as an\n * own data property, so a fresh schema carries no own `parse` / `safeParse` at\n * all, while one that has been read already does. `jit()` meets both shapes —\n * it snapshots whatever the slots hold, fronts them with its own accessors, and\n * hands back exactly what it found whenever it has to step aside.\n *\n * Runtime code generation is not always permitted — a strict CSP without\n * `unsafe-eval`, some edge runtimes. Zod v4 has the same constraint (its object\n * fast-pass is itself a `new Function`) and already exposes the two switches\n * for it: the `core.util.allowsEval` probe and `z.config({ jitless: true })`.\n * `jit()` honours both and degrades to plain Zod, so one setting governs both\n * compilers. Those targets are where the build plugin belongs anyway — it emits\n * the same validator with no runtime evaluation at all.\n */\n\nimport { config as zodConfig, core as zodCore, ZodRealError, type output } from \"zod\";\nimport {\n FAIL_CLASS_DECL,\n FAIL_CORE_CLASS_DECL,\n FAILZ_CLASS_DECL,\n FIN_CORE_DECL,\n FIN_DECL,\n FIN_DEFERRED_CORE_DECL,\n FIN_DEFERRED_DECL,\n FINZ_DECL,\n generateIIFE,\n MK_VALIDATOR_DECL,\n usesCoreErrors,\n ZOD_MSG_DECLARATION,\n} from \"./core/iife.js\";\nimport {\n isSupportedZodVersion,\n isUnsupportedZodVersionError,\n unsupportedZodVersionMessage,\n warnUnsupportedZodOnce,\n zodVersionOf,\n} from \"./core/extract/zod-version.js\";\nimport { compileSchemas } from \"./core/pipeline.js\";\nimport type { CompilableSchema, CompiledSchema } from \"./core/types.js\";\nimport { isZodSchema } from \"./is-zod-schema.js\";\n\n/**\n * The declarations `ZOD_CONFIG_IMPORT` supplies to an emitted module, minus the\n * import itself — `zod`'s three bindings arrive as parameters instead, so the\n * evaluated code has no module scope to resolve. Byte-for-byte the same helper\n * source the CLI emitter writes into a `.compiled.ts`, so a JIT validator and\n * an AOT one share their entire runtime layer.\n *\n * Two of them, by the error class the schema fails with: the classic finalizers,\n * or the `$ZodError` twins a `zod/mini` schema's validator calls. Each schema is\n * evaluated on its own, so it gets exactly the pair its code names.\n */\nconst RUNTIME_PRELUDE = [\n ZOD_MSG_DECLARATION,\n FAIL_CLASS_DECL,\n MK_VALIDATOR_DECL,\n FIN_DECL,\n FIN_DEFERRED_DECL,\n FAILZ_CLASS_DECL,\n FINZ_DECL,\n].join(\"\\n\");\nconst RUNTIME_PRELUDE_CORE = [\n ZOD_MSG_DECLARATION,\n FAIL_CORE_CLASS_DECL,\n MK_VALIDATOR_DECL,\n FIN_CORE_DECL,\n FIN_DEFERRED_CORE_DECL,\n FAILZ_CLASS_DECL,\n FINZ_DECL,\n].join(\"\\n\");\n\n/**\n * Methods `__zcMkv` installs. Each is fronted by a compile-on-read accessor\n * until the schema materializes.\n *\n * `~standard` earns its place: Zod builds it as a closure over `_zod.run`, not\n * over the schema's `safeParse` property, so a Standard Schema consumer (tRPC,\n * Hono, TanStack Form) that never touches `safeParse` would otherwise keep\n * running plain Zod forever behind a \"compiled\" schema.\n */\nconst SLOTS = [\"parse\", \"safeParse\", \"parseAsync\", \"safeParseAsync\", \"is\", \"~standard\"] as const;\n\n/** Schemas already handed to `jit()`, so a second call is a no-op rather than a recompile. */\nconst seen = new WeakSet<object>();\n\nexport interface JitOptions {\n /**\n * Compile immediately instead of on first use. Costs ~0.1-0.2 ms per schema\n * at import time; useful for a long-lived server that would rather pay during\n * startup than on the first request, or to surface a compilation failure\n * eagerly. Default `false`.\n */\n eager?: boolean | undefined;\n /** Use compact codegen and delegate cold error production to Zod. @default \"schema\" */\n output?: \"schema\" | \"compact\" | undefined;\n}\n\n/**\n * Compile `schema` in-process and install the compiled `parse` / `safeParse` /\n * `parseAsync` / `safeParseAsync` / `is` / `~standard` on it.\n *\n * Returns the SAME object — identity-preserving exactly as the build plugin is,\n * so `.shape`, `_zod`, `instanceof`, `z.toJSONSchema()`, `.meta()` and\n * composition into a larger schema all keep working, and every existing\n * reference to the schema picks the compiled methods up.\n *\n * ```ts\n * import { z } from \"zod\";\n * import { jit } from \"zod-compiler/jit\";\n *\n * export const UserSchema = jit(z.object({ name: z.string(), email: z.email() }));\n * UserSchema.safeParse(input); // compiled on this first call\n * ```\n *\n * Schemas the compiler cannot reproduce fall back to Zod per sub-schema, the\n * same way they do at build time; a schema that cannot be compiled at all is\n * left as plain Zod. `zod/mini` schemas compile the same way, and keep failing\n * with Mini's own error class (see {@link CompilableSchema}).\n */\nexport function jit<T extends CompilableSchema>(\n schema: T,\n options?: JitOptions,\n): T & CompiledSchema<output<T>> {\n const target = schema as unknown as Record<string, unknown>;\n if (seen.has(target)) return schema as T & CompiledSchema<output<T>>;\n\n // A schema built by a zod this release does not reproduce is left as plain\n // Zod — correct, just not faster — and said so once (see zod-version.ts).\n // Not thrown: `jit()` runs at module scope, where a throw takes the importing\n // app down at boot over what is a dependency-range problem. Checked before\n // anything is installed, so neither the eager path nor a later\n // compile-on-read accessor can reach the pipeline for it. A value that is not\n // a zod schema at all is not a version problem and takes the usual route.\n const version = zodVersionOf(schema);\n if (isZodSchema(schema) && !isSupportedZodVersion(version)) {\n warnUnsupportedZodOnce(unsupportedZodVersionMessage(version));\n return schema as T & CompiledSchema<output<T>>;\n }\n seen.add(target);\n\n if (options?.eager === true) {\n materialize(schema, options);\n return schema as T & CompiledSchema<output<T>>;\n }\n\n // Snapshot what each slot holds before anything is installed: materialize()\n // restores it before handing the object to `__zcMkv`, so the generated code\n // sees a pristine schema — it captures the original `parseAsync` /\n // `safeParseAsync` as its throw paths, and capturing a stub there would loop\n // back into itself. An untouched schema holds NOTHING here: Zod keeps the\n // methods on the prototype and only installs a bound own copy on first read,\n // so `undefined` is the common entry and means \"back to the prototype\". A\n // schema that was read before `jit()` — or that the build plugin's `__zcMkv`\n // already materialized — carries the copies, and those go back verbatim.\n const original = new Map<string, PropertyDescriptor | undefined>();\n try {\n for (const slot of SLOTS) {\n original.set(slot, Object.getOwnPropertyDescriptor(target, slot));\n }\n } catch {\n // The target answers a descriptor query with a throw — an exotic wrapper,\n // not anything Zod built. Without a snapshot there is nothing to roll back\n // to, so install nothing and hand back the schema exactly as it came.\n return schema as T & CompiledSchema<output<T>>;\n }\n\n // Installing the accessors is the step most likely to throw rather than\n // degrade: a slot locked non-configurable (a future Zod, another wrapper) makes\n // defineProperty raise, and `jit()` is called at module scope — so an\n // unhandled throw here takes down the importing app at boot. Roll back to\n // whatever Zod had and leave the schema alone instead.\n let pending = true;\n try {\n installAccessors(\n target,\n original,\n () => {\n if (!pending) return;\n pending = false;\n restore(target, original);\n materialize(schema, options);\n },\n () => {\n if (!pending) return;\n pending = false;\n // Restore EVERY slot, not just the one being written. A left-behind\n // accessor whose trigger has been cancelled would read `target[slot]`\n // and re-enter itself — unbounded recursion, which is what a later read\n // of an untouched slot (`~standard`, from a Standard Schema consumer)\n // would otherwise hit.\n //\n // Reached only when something WRITES a slot before anything reads one: a\n // test double, another wrapper, or an AOT `safeParse` assigned directly.\n // The build plugin's own `__zcMkv` does not land here — its first\n // statement READS `parseAsync`/`safeParseAsync` to capture their\n // originals, so it triggers materialization and then overwrites the\n // compiled-by-jit methods with the compiled-by-plugin ones.\n restore(target, original);\n },\n );\n } catch {\n pending = false;\n restore(target, original);\n }\n\n return schema as T & CompiledSchema<output<T>>;\n}\n\n/**\n * Front every installed method with a compile-on-read accessor. `trigger`\n * materializes the schema, which normally replaces these accessors with the\n * compiled methods (or restores what Zod had). When that replacement does not\n * take, the getter serves Zod's own method rather than re-reading the slot —\n * see the re-entrancy note in the body.\n */\nfunction installAccessors(\n target: Record<string, unknown>,\n original: ReadonlyMap<string, PropertyDescriptor | undefined>,\n trigger: () => void,\n cancel: () => void,\n): void {\n for (const slot of SLOTS) {\n // Re-entrancy is settled structurally rather than by inspection. Reading the\n // slot again is how this getter normally hands over — to the compiled method\n // materialize() installed, or to whatever restore() put back — but the\n // handover can fail to take: a target frozen after `jit()` refuses both, and\n // a second copy of this module in the graph leaves ITS accessor on the slot,\n // so the two bounce reads between them. `trigger()` is spent by then, so\n // either way the read recurses until the stack blows. While a read is already\n // in flight, or once the accessor is known to be stranded, serve Zod's own\n // method instead: a schema that cannot be compiled still parses.\n let reading = false;\n // A stranded accessor is permanent, so what it serves is resolved once.\n let stranded: { value: unknown } | undefined;\n const serveZod = (): unknown => {\n stranded ??= { value: fromZod(target, original, slot) };\n return stranded.value;\n };\n const read = function (): unknown {\n if (reading) return serveZod();\n reading = true;\n try {\n trigger();\n if (stillFrontedBy(target, slot, read)) return serveZod();\n return target[slot];\n } finally {\n reading = false;\n }\n };\n Object.defineProperty(target, slot, {\n configurable: true,\n // Keep the schema's own-key set as it was. A slot Zod already materialized\n // is an enumerable own copy, so the accessor fronting it is enumerable too;\n // an untouched slot has no own key at all, and `is` never exists on a Zod\n // schema, so those stay out of `Object.keys` — the non-enumerable\n // convention `compile()` already uses.\n enumerable: original.get(slot)?.enumerable ?? false,\n get: read,\n set(value: unknown) {\n // Someone overwrote a method before first use (a test double, another\n // wrapper). Their value wins, and compilation is cancelled outright —\n // materializing later would restore the snapshot over it. With the\n // accessor gone the write does what it would have without `jit()`:\n // overwrite the own copy, or run Zod's prototype setter, which installs\n // one. Only a target that refused the rollback still routes here, and a\n // direct define is the one way left to honour the write.\n cancel();\n if (!stillFrontedBy(target, slot, read)) {\n target[slot] = value;\n return;\n }\n Object.defineProperty(target, slot, {\n configurable: true,\n enumerable: original.get(slot)?.enumerable ?? false,\n value,\n writable: true,\n });\n },\n });\n }\n}\n\n/**\n * Is `slot` still fronted by this very accessor — i.e. did the replacement that\n * `trigger()` was supposed to perform not take? A target that will not answer\n * the question is assumed to still hold it, since reading the slot to find out\n * is the recursion being avoided.\n */\nfunction stillFrontedBy(\n target: Record<string, unknown>,\n slot: string,\n getter: () => unknown,\n): boolean {\n try {\n return Object.getOwnPropertyDescriptor(target, slot)?.get === getter;\n } catch {\n return true;\n }\n}\n\n/**\n * Zod's own value for `slot`, bypassing the accessor that fronts it.\n *\n * A slot the snapshot holds is answered from the snapshot. One it does not hold\n * lives on the prototype, where Zod's getter binds the method to its receiver\n * and installs the bound copy as an own property — on a target that accepts\n * the install, that also replaces the stranded accessor with the copy, which is\n * the handover materialize() could not make. A target that refuses the install\n * (frozen, or a wrapper that vetoes `defineProperty`) makes Zod's getter throw\n * instead; there the read goes through a stand-in that inherits from the target,\n * so the copy lands on the stand-in and the method reaches `_zod` through the\n * chain. `undefined` for `is`, which no plain Zod schema carries — the same\n * thing every other degradation path leaves there.\n */\nfunction fromZod(\n target: Record<string, unknown>,\n original: ReadonlyMap<string, PropertyDescriptor | undefined>,\n slot: string,\n): unknown {\n const descriptor = original.get(slot);\n if (descriptor !== undefined) {\n // A snapshot can hold another accessor (a second copy of this module\n // installed first), so invoke it rather than reading a `value` it lacks.\n return descriptor.get === undefined ? descriptor.value : descriptor.get.call(target);\n }\n const proto = Object.getPrototypeOf(target) as object | null;\n if (proto === null) return undefined;\n try {\n return Reflect.get(proto, slot, target);\n } catch {\n return Reflect.get(proto, slot, Object.create(target));\n }\n}\n\n/**\n * Compile every Zod schema found among an object's own values — typically a\n * module namespace, so a whole schema file opts in with one call:\n *\n * ```ts\n * import * as schemas from \"./schemas.js\";\n * jitAll(schemas);\n * ```\n *\n * The namespace object itself is never written to (a module namespace is\n * read-only); `jit()` mutates the schema objects it holds, which is what every\n * importer of that module already references.\n */\nexport function jitAll(schemas: object, options?: JitOptions): void {\n for (const value of Object.values(schemas)) {\n // Whatever zod built, raw `zod/v4/core` schemas included. jit()'s\n // narrower constraint keeps the TYPE it returns honest; this call\n // discards the return, and the runtime path takes any zod schema.\n if (isZodSchema(value)) jit(value as CompilableSchema, options);\n }\n}\n\n/**\n * Put back what the snapshot holds, dropping the compile-on-read accessors: the\n * own copy Zod (or the build plugin) had installed, or nothing at all, so the\n * slot reads through to Zod's prototype getter again.\n */\nfunction restore(\n target: Record<string, unknown>,\n original: ReadonlyMap<string, PropertyDescriptor | undefined>,\n): void {\n for (const slot of SLOTS) {\n // Per slot, because this also runs as the rollback for a failed install: a\n // target that refuses one slot must not cost the others their restoration.\n // A slot left fronted by its accessor still reads correctly — the getter\n // serves Zod's own method — but it keeps a redundant indirection, so\n // restoring what can be restored is worth the try/catch.\n try {\n const descriptor = original.get(slot);\n if (descriptor === undefined) delete target[slot];\n else Object.defineProperty(target, slot, descriptor);\n } catch {\n // Nothing further to try for this slot.\n }\n }\n}\n\n/**\n * Whether runtime code generation is permitted here. Read per call, never\n * snapshotted: `z.config({ jitless: true })` runs in an entry point, after the\n * schema modules it imports have already been evaluated.\n */\nfunction codegenAllowed(): boolean {\n return zodCore.globalConfig.jitless !== true && zodCore.util.allowsEval.value;\n}\n\n/**\n * Run the pipeline and let the generated IIFE install its methods on `schema`.\n * Swallows failure: a schema that cannot be compiled keeps Zod's own methods,\n * which the caller already has, so there is nothing to report and nothing to\n * break.\n */\nfunction materialize(schema: unknown, options?: JitOptions): void {\n if (!codegenAllowed()) return;\n try {\n buildValidator(schema, options);\n } catch (error) {\n // Left as plain Zod. Deliberately silent: `jit()` is an optimization, and a\n // schema using a construct the compiler declines is a supported outcome,\n // not an error. The one exception is the zod version guard — a mismatched\n // zod is a configuration problem the user has to hear about. `jit()` itself\n // refuses such a schema before installing anything; this covers a caller\n // that reached the pipeline another way.\n if (isUnsupportedZodVersionError(error)) warnUnsupportedZodOnce(error.message);\n }\n}\n\n/**\n * Generate the validator and evaluate it, reproducing the module a\n * `.compiled.ts` would have been: helper preamble, the file-level shared block,\n * then the `__zcMkv` IIFE whose `__rf[]` bases and install target are the live\n * schema object passed in as `__schema`.\n */\nfunction buildValidator(schema: unknown, options?: JitOptions): void {\n const { schemas, shared } = compileSchemas([{ exportName: \"jit\", schema }], {\n compact: options?.output === \"compact\",\n mode: \"inline\",\n });\n const compiled = schemas[0];\n if (compiled === undefined) throw new Error(\"zod-compiler: schema produced no validator\");\n\n const prelude = usesCoreErrors(compiled.codegenResult.usedHelpers)\n ? RUNTIME_PRELUDE_CORE\n : RUNTIME_PRELUDE;\n const body = [prelude, shared.code, `return ${generateIIFE(\"__schema\", compiled)};`].join(\"\\n\");\n\n // The three bindings ZOD_CONFIG_IMPORT would have imported, passed in so the\n // evaluated code needs no module resolution of its own.\n // oxlint-disable-next-line no-new-func -- generating the validator IS the feature\n const factory = new Function(\n \"__zodCompilerConfig\",\n \"__zcCore\",\n \"__zcZodError\",\n \"__schema\",\n body,\n ) as (\n zodConfigFn: typeof zodConfig,\n zodCoreNs: typeof zodCore,\n zodErrorCtor: typeof ZodRealError,\n target: unknown,\n ) => unknown;\n\n factory(zodConfig, zodCore, ZodRealError, schema);\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/register/index.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport { registerHooks } from \"node:module\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { init } from \"es-module-lexer\";\nimport type { ZodType } from \"zod\";\nimport { isCompiledSchema } from \"../core/compile.js\";\nimport { jit, jitAll, type JitOptions } from \"../jit.js\";\nimport type { ZodCompilerRegisterConfig } from \"./config.js\";\nimport { decodeModuleSource, instrumentModule, isRegisterFormat } from \"./transform.js\";\n\nconst CONFIG_FILE = \"zod-compiler.json\";\nconst REGISTER_SYMBOL = Symbol.for(\"zod-compiler:register\");\n\nawait init;\n\nconst config = loadConfig();\nconst jitOptions: JitOptions = {\n eager: config.eager,\n output: config.output,\n};\n\nObject.defineProperty(globalThis, REGISTER_SYMBOL, {\n configurable: true,\n value(value: unknown, flatten = false): unknown {\n registerValue(value);\n if (flatten && typeof value === \"object\" && value !== null) {\n for (const exported of Object.values(value)) registerValue(exported);\n }\n return value;\n },\n});\n\nregisterHooks({\n load(url, context, nextLoad) {\n const loaded = nextLoad(url, context);\n if (!url.startsWith(\"file:\") || !isRegisterFormat(loaded.format) || loaded.source == null) {\n return loaded;\n }\n\n // Posix separators, because the include/exclude globs are matched with\n // picomatch and the plugin paths it was written against are bundler ids,\n // which are already normalized. `fileURLToPath` is the only caller that\n // yields native separators, so on Windows `include: [\"src/**\"]` matched\n // nothing and the whole feature went silently inert.\n const filename = fileURLToPath(url).replaceAll(\"\\\\\", \"/\");\n const source = decodeModuleSource(loaded.source);\n // This hook runs for EVERY module the process loads, and what it adds is\n // only an optimization. So nothing it does may fail a load: a config value\n // of the wrong shape, a lexer that chokes on a dialect it half-supports, a\n // bad `hoist.schemaNamePattern` — each would otherwise surface as a crash\n // at startup, in someone else's file, naming zod-compiler internals. Ship\n // the module unchanged instead and leave its schemas uncompiled.\n let transformed: string | null;\n try {\n transformed = instrumentModule(source, filename, loaded.format, config);\n } catch {\n return loaded;\n }\n return transformed === null ? loaded : { ...loaded, source: transformed };\n },\n});\n\nfunction registerValue(value: unknown): void {\n if (config.schemas === \"explicit\") {\n if (isCompiledSchema(value)) jit(value as unknown as ZodType, jitOptions);\n } else {\n jitAll({ value }, jitOptions);\n }\n}\n\nfunction loadConfig(): ZodCompilerRegisterConfig {\n const filename = path.resolve(CONFIG_FILE);\n let source: string;\n try {\n source = fs.readFileSync(filename, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return {};\n throw error;\n }\n\n let value: unknown;\n try {\n value = JSON.parse(source);\n } catch (error) {\n throw new SyntaxError(`Cannot parse ${filename}: ${(error as Error).message}`, {\n cause: error,\n });\n }\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new TypeError(`${filename} must contain a JSON object`);\n }\n return value as ZodCompilerRegisterConfig;\n}\n"],"mappings":";;;;;;;;;AAWA,MAAM,cAAc;AACpB,MAAM,kBAAkB,OAAO,IAAI,uBAAuB;AAE1D,MAAM;AAEN,MAAM,SAAS,WAAW;AAC1B,MAAM,aAAyB;CAC7B,OAAO,OAAO;CACd,QAAQ,OAAO;AACjB;AAEA,OAAO,eAAe,YAAY,iBAAiB;CACjD,cAAc;CACd,MAAM,OAAgB,UAAU,OAAgB;EAC9C,cAAc,KAAK;EACnB,IAAI,WAAW,OAAO,UAAU,YAAY,UAAU,MACpD,KAAK,MAAM,YAAY,OAAO,OAAO,KAAK,GAAG,cAAc,QAAQ;EAErE,OAAO;CACT;AACF,CAAC;AAED,cAAc,EACZ,KAAK,KAAK,SAAS,UAAU;CAC3B,MAAM,SAAS,SAAS,KAAK,OAAO;CACpC,IAAI,CAAC,IAAI,WAAW,OAAO,KAAK,CAAC,iBAAiB,OAAO,MAAM,KAAK,OAAO,UAAU,MACnF,OAAO;CAQT,MAAM,WAAW,cAAc,GAAG,CAAC,CAAC,WAAW,MAAM,GAAG;CACxD,MAAM,SAAS,mBAAmB,OAAO,MAAM;CAO/C,IAAI;CACJ,IAAI;EACF,cAAc,iBAAiB,QAAQ,UAAU,OAAO,QAAQ,MAAM;CACxE,QAAQ;EACN,OAAO;CACT;CACA,OAAO,gBAAgB,OAAO,SAAS;EAAE,GAAG;EAAQ,QAAQ;CAAY;AAC1E,EACF,CAAC;AAED,SAAS,cAAc,OAAsB;CAC3C,IAAI,OAAO,YAAY,YACjB;MAAA,iBAAiB,KAAK,GAAG,IAAI,OAA6B,UAAU;CAAA,OAExE,OAAO,EAAE,MAAM,GAAG,UAAU;AAEhC;AAEA,SAAS,aAAwC;CAC/C,MAAM,WAAW,KAAK,QAAQ,WAAW;CACzC,IAAI;CACJ,IAAI;EACF,SAAS,GAAG,aAAa,UAAU,MAAM;CAC3C,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU,OAAO,CAAC;EAChE,MAAM;CACR;CAEA,IAAI;CACJ,IAAI;EACF,QAAQ,KAAK,MAAM,MAAM;CAC3B,SAAS,OAAO;EACd,MAAM,IAAI,YAAY,gBAAgB,SAAS,IAAK,MAAgB,WAAW,EAC7E,OAAO,MACT,CAAC;CACH;CACA,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GACpE,MAAM,IAAI,UAAU,GAAG,SAAS,4BAA4B;CAE9D,OAAO;AACT"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/register/index.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport { registerHooks } from \"node:module\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { init } from \"es-module-lexer\";\nimport { isCompiledSchema } from \"../core/compile.js\";\nimport type { CompilableSchema } from \"../core/types.js\";\nimport { jit, jitAll, type JitOptions } from \"../jit.js\";\nimport type { ZodCompilerRegisterConfig } from \"./config.js\";\nimport { decodeModuleSource, instrumentModule, isRegisterFormat } from \"./transform.js\";\n\nconst CONFIG_FILE = \"zod-compiler.json\";\nconst REGISTER_SYMBOL = Symbol.for(\"zod-compiler:register\");\n\nawait init;\n\nconst config = loadConfig();\nconst jitOptions: JitOptions = {\n eager: config.eager,\n output: config.output,\n};\n\nObject.defineProperty(globalThis, REGISTER_SYMBOL, {\n configurable: true,\n value(value: unknown, flatten = false): unknown {\n registerValue(value);\n if (flatten && typeof value === \"object\" && value !== null) {\n for (const exported of Object.values(value)) registerValue(exported);\n }\n return value;\n },\n});\n\nregisterHooks({\n load(url, context, nextLoad) {\n const loaded = nextLoad(url, context);\n if (!url.startsWith(\"file:\") || !isRegisterFormat(loaded.format) || loaded.source == null) {\n return loaded;\n }\n\n // Posix separators, because the include/exclude globs are matched with\n // picomatch and the plugin paths it was written against are bundler ids,\n // which are already normalized. `fileURLToPath` is the only caller that\n // yields native separators, so on Windows `include: [\"src/**\"]` matched\n // nothing and the whole feature went silently inert.\n const filename = fileURLToPath(url).replaceAll(\"\\\\\", \"/\");\n const source = decodeModuleSource(loaded.source);\n // This hook runs for EVERY module the process loads, and what it adds is\n // only an optimization. So nothing it does may fail a load: a config value\n // of the wrong shape, a lexer that chokes on a dialect it half-supports, a\n // bad `hoist.schemaNamePattern` — each would otherwise surface as a crash\n // at startup, in someone else's file, naming zod-compiler internals. Ship\n // the module unchanged instead and leave its schemas uncompiled.\n let transformed: string | null;\n try {\n transformed = instrumentModule(source, filename, loaded.format, config);\n } catch {\n return loaded;\n }\n return transformed === null ? loaded : { ...loaded, source: transformed };\n },\n});\n\nfunction registerValue(value: unknown): void {\n if (config.schemas === \"explicit\") {\n if (isCompiledSchema(value)) jit(value as unknown as CompilableSchema, jitOptions);\n } else {\n jitAll({ value }, jitOptions);\n }\n}\n\nfunction loadConfig(): ZodCompilerRegisterConfig {\n const filename = path.resolve(CONFIG_FILE);\n let source: string;\n try {\n source = fs.readFileSync(filename, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return {};\n throw error;\n }\n\n let value: unknown;\n try {\n value = JSON.parse(source);\n } catch (error) {\n throw new SyntaxError(`Cannot parse ${filename}: ${(error as Error).message}`, {\n cause: error,\n });\n }\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new TypeError(`${filename} must contain a JSON object`);\n }\n return value as ZodCompilerRegisterConfig;\n}\n"],"mappings":";;;;;;;;;AAWA,MAAM,cAAc;AACpB,MAAM,kBAAkB,OAAO,IAAI,uBAAuB;AAE1D,MAAM;AAEN,MAAM,SAAS,WAAW;AAC1B,MAAM,aAAyB;CAC7B,OAAO,OAAO;CACd,QAAQ,OAAO;AACjB;AAEA,OAAO,eAAe,YAAY,iBAAiB;CACjD,cAAc;CACd,MAAM,OAAgB,UAAU,OAAgB;EAC9C,cAAc,KAAK;EACnB,IAAI,WAAW,OAAO,UAAU,YAAY,UAAU,MACpD,KAAK,MAAM,YAAY,OAAO,OAAO,KAAK,GAAG,cAAc,QAAQ;EAErE,OAAO;CACT;AACF,CAAC;AAED,cAAc,EACZ,KAAK,KAAK,SAAS,UAAU;CAC3B,MAAM,SAAS,SAAS,KAAK,OAAO;CACpC,IAAI,CAAC,IAAI,WAAW,OAAO,KAAK,CAAC,iBAAiB,OAAO,MAAM,KAAK,OAAO,UAAU,MACnF,OAAO;CAQT,MAAM,WAAW,cAAc,GAAG,CAAC,CAAC,WAAW,MAAM,GAAG;CACxD,MAAM,SAAS,mBAAmB,OAAO,MAAM;CAO/C,IAAI;CACJ,IAAI;EACF,cAAc,iBAAiB,QAAQ,UAAU,OAAO,QAAQ,MAAM;CACxE,QAAQ;EACN,OAAO;CACT;CACA,OAAO,gBAAgB,OAAO,SAAS;EAAE,GAAG;EAAQ,QAAQ;CAAY;AAC1E,EACF,CAAC;AAED,SAAS,cAAc,OAAsB;CAC3C,IAAI,OAAO,YAAY,YACjB;MAAA,iBAAiB,KAAK,GAAG,IAAI,OAAsC,UAAU;CAAA,OAEjF,OAAO,EAAE,MAAM,GAAG,UAAU;AAEhC;AAEA,SAAS,aAAwC;CAC/C,MAAM,WAAW,KAAK,QAAQ,WAAW;CACzC,IAAI;CACJ,IAAI;EACF,SAAS,GAAG,aAAa,UAAU,MAAM;CAC3C,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU,OAAO,CAAC;EAChE,MAAM;CACR;CAEA,IAAI;CACJ,IAAI;EACF,QAAQ,KAAK,MAAM,MAAM;CAC3B,SAAS,OAAO;EACd,MAAM,IAAI,YAAY,gBAAgB,SAAS,IAAK,MAAgB,WAAW,EAC7E,OAAO,MACT,CAAC;CACH;CACA,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GACpE,MAAM,IAAI,UAAU,GAAG,SAAS,4BAA4B;CAE9D,OAAO;AACT"}
package/dist/runtime.d.ts CHANGED
@@ -4,6 +4,8 @@
4
4
  export declare const __zcMkv: any;
5
5
  export declare const __zcFin: any;
6
6
  export declare const __zcFinD: any;
7
+ export declare const __zcFinC: any;
8
+ export declare const __zcFinDC: any;
7
9
  export declare const __zcFinZ: any;
8
10
  export declare const __zcTS: any;
9
11
  export declare const __zcTSt: any;
@@ -24,6 +26,7 @@ export declare const __zcLo: any;
24
26
  export declare const __zcSo: any;
25
27
  export declare const __zcCpl: any;
26
28
  export declare const __zcEmail: any;
29
+ export declare const __zcUrl: any;
27
30
  export declare const __zcPs: any;
28
31
  export declare const __zcPlain: any;
29
32
  export declare const __zcPfx: any;
package/dist/runtime.js CHANGED
@@ -1,10 +1,14 @@
1
- import { config as __zodCompilerConfig, core as __zcCore, ZodRealError as __zcZodError } from "zod";
1
+ import { config as __zodCompilerConfig, ZodRealError as __zcZodError } from "zod";
2
+ import * as __zcCore from "zod/v4/core";
2
3
  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
- 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
+ var __ZcFail=/* @__PURE__ */(function(){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++){var s=e[i],o={},k;if(s.message===undefined&&typeof __zcMsg==="function")s.message=__zcMsg(s);for(k in s){if(k!=="input"&&k!=="continue"&&Object.prototype.hasOwnProperty.call(s,k))o[k]=s[k];}e[i]=o;}return this._c=new __zcZodError(e);}});return __ZcFail;})();
5
+ var __ZcFailC=/* @__PURE__ */(function(){function __ZcFailC(e,f,i){this.success=false;this._e=e;this._f=f;this._i=i;this._c=undefined;}Object.defineProperty(__ZcFailC.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++){var s=e[i],o={},k;if(s.message===undefined&&typeof __zcMsg==="function")s.message=__zcMsg(s);for(k in s){if(k!=="input"&&k!=="continue"&&Object.prototype.hasOwnProperty.call(s,k))o[k]=s[k];}e[i]=o;}return this._c=new __zcCore.$ZodRealError(e);}});return __ZcFailC;})();
4
6
  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
7
  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;};Object.defineProperty(w,"~standard",{configurable:true,value:{version:1,vendor:"zod",validate:function(input){var r;try{if(fc&&fc(input))return{value:input};r=fn(input);}catch(e){if(zspa)return zspa(input).then(function(q){return q.success?{value:q.data}:{issues:q.error.issues};});throw e;}return r.success?{value:r.data}:{issues:r.error.issues};}}});return w;}
6
8
  export function __zcFin(e,d){if(!e.length)return{success:true,data:d};return new __ZcFail(e,null,null);}
7
9
  export function __zcFinD(f,inp){return new __ZcFail(null,f,inp);}
10
+ export function __zcFinC(e,d){if(!e.length)return{success:true,data:d};return new __ZcFailC(e,null,null);}
11
+ export function __zcFinDC(f,inp){return new __ZcFailC(null,f,inp);}
8
12
  export function __zcFinZ(z,r,i){return new __ZcFailZ(z,r,i);}
9
13
  function __zcSrRun(f,p){var r=f(p);if(r&&typeof r.then==="function"){throw new __zcCore.$ZodAsyncError();}}
10
14
  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;}
@@ -26,6 +30,7 @@ export function __zcLo(v){return Array.isArray(v)?"array":typeof v==="string"?"s
26
30
  export function __zcSo(v){return v instanceof Set?"set":v instanceof Map?"map":(typeof File!=="undefined"&&v instanceof File)?"file":"unknown";}
27
31
  export function __zcCpl(s){var n=s.length;if(!/[\uD800-\uDBFF]/.test(s))return n;var c=n;for(var i=0;i<n-1;i++){if((s.charCodeAt(i)&0xfc00)===0xd800&&(s.charCodeAt(i+1)&0xfc00)===0xdc00){c--;i++;}}return c;}
28
32
  export function __zcEmail(s){var n=s.length,i=0,c,p=46;for(;;){if(i===n)return false;c=s.charCodeAt(i);if(c===64)break;if(c===46){if(p===46)return false;}else if(!((c>=97&&c<=122)||(c>=65&&c<=90)||(c>=48&&c<=57)||c===95||c===39||c===43||c===45))return false;p=c;i++;}if(p===46||p===39)return false;var l=++i,d=0,t=true;for(;i<n;i++){c=s.charCodeAt(i);if((c>=97&&c<=122)||(c>=65&&c<=90))continue;if(c===46){if(i===l)return false;d++;l=i+1;t=true;continue;}if((c>=48&&c<=57)||c===45){if(c===45&&i===l)return false;t=false;continue;}return false;}return d>0&&t&&n-l>=2;}
33
+ export function __zcUrl(s){if(typeof URL==="function"&&typeof URL.canParse==="function")return URL.canParse(s);try{new URL(s);return true;}catch(_){return false;}}
29
34
  export function __zcPs(o){if(!Object.prototype.hasOwnProperty.call(o,"__proto__"))return o;var c={...o};delete c["__proto__"];return c;}
30
35
  export function __zcPlain(o){if(typeof o!=="object"||o===null||Array.isArray(o))return false;var c=o.constructor;if(c===Object||c===undefined||typeof c!=="function")return true;var p=c.prototype;if(typeof p!=="object"||p===null||Array.isArray(p))return false;return Object.prototype.hasOwnProperty.call(p,"isPrototypeOf");}
31
36
  export function __zcPfx(d,s,b,k){for(var i=0;i<s.length;i++){var x=s[i];x.path=b.concat(k,x.path);d.push(x);}}
@@ -1,4 +1,22 @@
1
1
  //#region src/static-filter.d.ts
2
+ /**
3
+ * Does the source export a value the analysis would call a candidate on sight?
4
+ *
5
+ * Such an initializer is a call, member or `new` expression, and
6
+ * `classifyValue` reports every one of those as a candidate, so for a file that
7
+ * has one the transpile and parse below cannot change the answer. A schema
8
+ * file almost always does (`z.object(…)`, `compile(…)`), and skipping jiti's
9
+ * Babel transform for it took a quarter off a cold build of a 181-file corpus.
10
+ * The prefixes the analysis calls SAFE stay out of the pattern — `typeof`,
11
+ * `void` and `delete` (unary), `async`, `function` and `class` — and `await`
12
+ * is left to it as well.
13
+ *
14
+ * A line it misreads — inside a block comment or template literal, an `export`
15
+ * nested in a namespace, a `<` comparison — errs toward candidate, the
16
+ * filter's documented safe direction: one extra execution, never a missed
17
+ * schema.
18
+ */
19
+ declare function hasObviousCandidateExport(code: string): boolean;
2
20
  /**
3
21
  * Check whether a file's exports could include Zod schemas (or compile()
4
22
  * results) without executing the file. `filename` selects the TS/JSX
@@ -6,5 +24,5 @@
6
24
  */
7
25
  declare function mayExportSchemas(code: string, filename: string): Promise<boolean>;
8
26
  //#endregion
9
- export { mayExportSchemas };
27
+ export { hasObviousCandidateExport, mayExportSchemas };
10
28
  //# sourceMappingURL=static-filter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"static-filter.d.ts","names":[],"sources":["../src/static-filter.ts"],"mappings":";;;;;;iBAgDsB,iBAAiB,cAAc,mBAAmB"}
1
+ {"version":3,"file":"static-filter.d.ts","names":[],"sources":["../src/static-filter.ts"],"mappings":";;;;;;;;;;;;;;;;;;iBAoEgB,0BAA0B;;;;;;iBASpB,iBAAiB,cAAc,mBAAmB"}
@@ -9,11 +9,37 @@ function getTransformer() {
9
9
  return transformerPromise;
10
10
  }
11
11
  /**
12
+ * `export const X = ident(…)`, `ident.member`, `ident<T>(…)` or `new Ident(…)`,
13
+ * or `export default` of the same, starting a line.
14
+ */
15
+ const OBVIOUS_CANDIDATE_EXPORT = /^[ \t]*export[ \t]+(?:(?:const|let|var)[ \t]+[A-Za-z_$][\w$]*[ \t]*(?::[^=;]*)?=|default[ \t]+)\s*(?:new\s+)?(?!(?:async|function|class|typeof|void|delete|await)\b)[A-Za-z_$][\w$]*\s*[.(<]/m;
16
+ /**
17
+ * Does the source export a value the analysis would call a candidate on sight?
18
+ *
19
+ * Such an initializer is a call, member or `new` expression, and
20
+ * `classifyValue` reports every one of those as a candidate, so for a file that
21
+ * has one the transpile and parse below cannot change the answer. A schema
22
+ * file almost always does (`z.object(…)`, `compile(…)`), and skipping jiti's
23
+ * Babel transform for it took a quarter off a cold build of a 181-file corpus.
24
+ * The prefixes the analysis calls SAFE stay out of the pattern — `typeof`,
25
+ * `void` and `delete` (unary), `async`, `function` and `class` — and `await`
26
+ * is left to it as well.
27
+ *
28
+ * A line it misreads — inside a block comment or template literal, an `export`
29
+ * nested in a namespace, a `<` comparison — errs toward candidate, the
30
+ * filter's documented safe direction: one extra execution, never a missed
31
+ * schema.
32
+ */
33
+ function hasObviousCandidateExport(code) {
34
+ return OBVIOUS_CANDIDATE_EXPORT.test(code);
35
+ }
36
+ /**
12
37
  * Check whether a file's exports could include Zod schemas (or compile()
13
38
  * results) without executing the file. `filename` selects the TS/JSX
14
39
  * transforms; the file does not need to exist on disk.
15
40
  */
16
41
  async function mayExportSchemas(code, filename) {
42
+ if (hasObviousCandidateExport(code)) return true;
17
43
  let transpiled;
18
44
  try {
19
45
  transpiled = (await getTransformer()).transform({
@@ -264,6 +290,6 @@ function mentionsExports(root) {
264
290
  return false;
265
291
  }
266
292
  //#endregion
267
- export { mayExportSchemas };
293
+ export { hasObviousCandidateExport, mayExportSchemas };
268
294
 
269
295
  //# sourceMappingURL=static-filter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"static-filter.js","names":[],"sources":["../src/static-filter.ts"],"sourcesContent":["import path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type {\n AnyNode,\n AssignmentExpression,\n Expression,\n Program,\n Statement,\n VariableDeclaration,\n} from \"acorn\";\nimport { parse } from \"acorn\";\nimport type { Jiti } from \"jiti\";\n\n/**\n * Static pre-filter: decide whether a source file could export Zod schemas\n * WITHOUT executing it.\n *\n * Discovery executes candidate files (and, transitively, their whole import\n * graph) to scan exports. In autoDiscover mode most files that import zod\n * only export functions, components, or constants — executing them is pure\n * waste. This filter transpiles the single file (no dependencies) with\n * jiti's transform and inspects the top-level exports of the resulting\n * CommonJS with acorn.\n *\n * Conservative by construction: it returns false (\"skip\") only when every\n * export is statically provable as a non-schema (function, class, arrow,\n * literal, ...). Anything unrecognized — call expressions, re-exports,\n * `export *`, dynamic patterns, transpile/parse failures — keeps the file a\n * candidate, so a filter miss costs one execution, never a schema.\n */\n\ntype Classification = \"safe\" | \"candidate\";\n\n/** Lazily created jiti instance used only for single-file transpilation. */\nlet transformerPromise: Promise<Jiti> | undefined;\n\nfunction getTransformer(): Promise<Jiti> {\n transformerPromise ??= import(\"jiti\").then(({ createJiti }) =>\n createJiti(pathToFileURL(path.resolve(\"__zod-compiler-static-filter__.mjs\")).href),\n );\n return transformerPromise;\n}\n\n/**\n * Check whether a file's exports could include Zod schemas (or compile()\n * results) without executing the file. `filename` selects the TS/JSX\n * transforms; the file does not need to exist on disk.\n */\nexport async function mayExportSchemas(code: string, filename: string): Promise<boolean> {\n let transpiled: string;\n try {\n const jiti = await getTransformer();\n transpiled = jiti.transform({\n source: code,\n filename,\n ts: /\\.[cm]?tsx?$/.test(filename),\n jsx: /\\.[cm]?[jt]sx$/.test(filename),\n // async mode keeps top-level await and emits `await jitiImport(...)`\n // instead of failing — statements stay top-level either way.\n async: true,\n });\n } catch {\n return true;\n }\n // jiti reports parse failures by embedding an error marker in the output\n // instead of throwing. We can't analyze such files — keep them candidates\n // so the real loader surfaces (or tolerates) the failure exactly as before.\n if (transpiled.includes(\"__JITI_ERROR__\")) return true;\n\n let program: Program;\n try {\n // Module mode: the CJS output has no import/export statements, but may\n // contain top-level await (async transform).\n program = parse(transpiled, { ecmaVersion: \"latest\", sourceType: \"module\" });\n } catch {\n return true;\n }\n\n return analyzeProgram(program);\n}\n\n/** Where an assignment target sends its value, in discovery terms. */\ntype ExportTarget =\n | { kind: \"named\"; name: string }\n | { kind: \"default\" }\n | { kind: \"local\"; name: string }\n | { kind: \"ignored\" }\n | { kind: \"unknown\" };\n\nfunction analyzeProgram(program: Program): boolean {\n // Pass 1: top-level bindings for identifier resolution, plus the set of\n // identifier nodes that are \"expected\" (declaration ids and exports-chain\n // participants). Pass 2 counts every OTHER reference: a name referenced\n // outside its declaration/export may be mutated after creation\n // (defineProperty, member writes, helper calls), so object/array values\n // it names cannot be proven schema-free.\n const safeDecls = new Set<string>();\n const varInits = new Map<string, Expression | null>();\n const expectedNodes = new Set<object>();\n\n function recordChainNodes(expr: Expression): void {\n let node = expr;\n let hasExportTarget = false;\n while (node.type === \"AssignmentExpression\" && node.operator === \"=\") {\n if (node.left.type === \"Identifier\") {\n // Assignment LHS identifiers rebind — they never read the value.\n expectedNodes.add(node.left);\n } else if (node.left.type === \"MemberExpression\" && mentionsExports(node.left)) {\n hasExportTarget = true;\n }\n node = node.right;\n }\n // The bare-identifier value of an export chain (`exports.f = f`) is the\n // export itself; in a plain local chain (`alias = obj`) it is a real\n // aliasing reference and must count toward taint.\n if (hasExportTarget && node.type === \"Identifier\") expectedNodes.add(node);\n }\n\n for (const stmt of program.body) {\n if (stmt.type === \"FunctionDeclaration\" || stmt.type === \"ClassDeclaration\") {\n safeDecls.add(stmt.id.name);\n expectedNodes.add(stmt.id);\n } else if (stmt.type === \"VariableDeclaration\") {\n for (const decl of stmt.declarations) {\n if (decl.id.type === \"Identifier\") {\n expectedNodes.add(decl.id);\n // The init may be an `exports.X = value` chain — resolution should\n // see the final value expression.\n varInits.set(decl.id.name, decl.init ? unwrapAssignmentChain(decl.init).value : null);\n }\n if (decl.init) recordChainNodes(decl.init);\n }\n } else if (stmt.type === \"ExpressionStatement\") {\n recordChainNodes(stmt.expression);\n }\n }\n\n const referencedNames = collectReferencedNames(program, expectedNodes);\n\n function resolveIdentifier(name: string, defaultish: boolean, seen: Set<string>): Classification {\n if (seen.has(name)) return \"candidate\";\n seen.add(name);\n if (safeDecls.has(name)) return \"safe\";\n const init = varInits.get(name);\n if (init === undefined || init === null) return \"candidate\";\n return classifyValue(init, defaultish, seen, [name]);\n }\n\n /**\n * Could this expression evaluate to a Zod schema (an object carrying\n * `_zod.def` or the compile() marker)? `bindingNames` are the local\n * aliases of the value — a mutable container (object/array) is only safe\n * when none of its aliases are referenced elsewhere in the file.\n */\n function classifyValue(\n node: Expression,\n defaultish: boolean,\n seen: Set<string>,\n bindingNames: readonly string[],\n ): Classification {\n switch (node.type) {\n case \"Literal\":\n case \"TemplateLiteral\":\n case \"ArrowFunctionExpression\":\n case \"FunctionExpression\":\n case \"ClassExpression\":\n case \"UnaryExpression\":\n case \"UpdateExpression\":\n case \"BinaryExpression\":\n return \"safe\";\n case \"ObjectExpression\":\n case \"ArrayExpression\":\n // Discovery unwraps the properties of a default export object (and\n // arrays are objects), so containers are candidates there. Named\n // export containers are never unwrapped — but a container whose\n // alias is referenced elsewhere may be mutated into a schema shape.\n if (defaultish) return \"candidate\";\n return bindingNames.some((n) => referencedNames.has(n)) ? \"candidate\" : \"safe\";\n case \"Identifier\":\n return resolveIdentifier(node.name, defaultish, seen);\n case \"ConditionalExpression\":\n return classifyValue(node.consequent, defaultish, seen, bindingNames) === \"safe\" &&\n classifyValue(node.alternate, defaultish, seen, bindingNames) === \"safe\"\n ? \"safe\"\n : \"candidate\";\n case \"LogicalExpression\":\n return classifyValue(node.left, defaultish, seen, bindingNames) === \"safe\" &&\n classifyValue(node.right, defaultish, seen, bindingNames) === \"safe\"\n ? \"safe\"\n : \"candidate\";\n case \"SequenceExpression\": {\n const last = node.expressions[node.expressions.length - 1];\n return last ? classifyValue(last, defaultish, seen, bindingNames) : \"candidate\";\n }\n case \"AssignmentExpression\":\n return classifyValue(unwrapAssignmentChain(node).value, defaultish, seen, bindingNames);\n default:\n // CallExpression, NewExpression, MemberExpression, AwaitExpression, ...\n return \"candidate\";\n }\n }\n\n /** Process one assignment chain: classify the value for each export target. */\n function processAssignment(expr: Expression, declaredName?: string): Classification {\n const { targets, value } = unwrapAssignmentChain(expr);\n const bindingNames: string[] = declaredName ? [declaredName] : [];\n const exportTargets: ExportTarget[] = [];\n for (const target of targets) {\n const exportTarget = exportTargetOf(target);\n if (exportTarget.kind === \"unknown\") return \"candidate\";\n if (exportTarget.kind === \"local\") bindingNames.push(exportTarget.name);\n exportTargets.push(exportTarget);\n }\n for (const exportTarget of exportTargets) {\n if (exportTarget.kind === \"named\") {\n if (classifyValue(value, false, new Set(), bindingNames) === \"candidate\")\n return \"candidate\";\n } else if (exportTarget.kind === \"default\") {\n if (classifyValue(value, true, new Set(), bindingNames) === \"candidate\") return \"candidate\";\n }\n }\n // Catch-all: the value itself may reference exports in a shape we did\n // not positively classify (e.g. `exports.a = fn(exports.b)`).\n return mentionsExports(value) ? \"candidate\" : \"safe\";\n }\n\n function processVariableDeclaration(stmt: VariableDeclaration): Classification {\n for (const decl of stmt.declarations) {\n if (!decl.init) continue;\n if (decl.id.type === \"Identifier\") {\n if (processAssignment(decl.init, decl.id.name) === \"candidate\") return \"candidate\";\n } else if (mentionsExports(decl.init)) {\n // Destructuring declarators don't export by themselves (babel emits\n // separate `exports.x = x` statements), but be paranoid about any\n // exports reference hiding inside.\n return \"candidate\";\n }\n }\n return \"safe\";\n }\n\n function processStatement(stmt: Statement): Classification {\n switch (stmt.type) {\n case \"FunctionDeclaration\":\n case \"ClassDeclaration\":\n // Bodies that touch exports (hand-written CJS patterns) are opaque.\n return mentionsExports(stmt) ? \"candidate\" : \"safe\";\n case \"VariableDeclaration\":\n return processVariableDeclaration(stmt);\n case \"ExpressionStatement\":\n return processExpression(stmt.expression);\n case \"EmptyStatement\":\n return \"safe\";\n default:\n // if/try/for/... — babel never emits exports there for ESM input,\n // but hand-written CJS might (`if (x) module.exports = ...`).\n return mentionsExports(stmt) ? \"candidate\" : \"safe\";\n }\n }\n\n function processExpression(expr: Expression): Classification {\n switch (expr.type) {\n case \"Literal\":\n // Directive prologue (\"use strict\")\n return \"safe\";\n case \"AssignmentExpression\":\n return processAssignment(expr);\n case \"SequenceExpression\": {\n for (const sub of expr.expressions) {\n if (processExpression(sub) === \"candidate\") return \"candidate\";\n }\n return \"safe\";\n }\n case \"CallExpression\":\n if (isEsModuleMarker(expr)) return \"safe\";\n // Other exports-touching calls: re-export getters\n // (Object.defineProperty), `export *` loops, helpers.\n return mentionsExports(expr) ? \"candidate\" : \"safe\";\n default:\n return mentionsExports(expr) ? \"candidate\" : \"safe\";\n }\n }\n\n for (const stmt of program.body) {\n // The CJS output of jiti's transform contains no module declarations,\n // but if one ever appears, treat the file as a candidate.\n if (!isStatement(stmt)) return true;\n if (processStatement(stmt) === \"candidate\") return true;\n }\n return false;\n}\n\nfunction isStatement(node: AnyNode): node is Statement {\n return !node.type.startsWith(\"Import\") && !node.type.startsWith(\"Export\");\n}\n\n/**\n * Unwrap `a = b = c = value` into its assignment targets and final value.\n * Non-assignment expressions return zero targets and themselves as value.\n */\nfunction unwrapAssignmentChain(expr: Expression): {\n targets: AssignmentExpression[\"left\"][];\n value: Expression;\n} {\n const targets: AssignmentExpression[\"left\"][] = [];\n let value = expr;\n while (value.type === \"AssignmentExpression\" && value.operator === \"=\") {\n targets.push(value.left);\n value = value.right;\n }\n return { targets, value };\n}\n\n/** Classify an assignment target by what discovery would see. */\nfunction exportTargetOf(target: AssignmentExpression[\"left\"]): ExportTarget {\n if (target.type === \"Identifier\") {\n if (target.name === \"exports\" || target.name === \"module\") return { kind: \"unknown\" };\n return { kind: \"local\", name: target.name };\n }\n if (target.type !== \"MemberExpression\") {\n return mentionsExports(target) ? { kind: \"unknown\" } : { kind: \"ignored\" };\n }\n const propName =\n !target.computed && target.property.type === \"Identifier\"\n ? target.property.name\n : target.computed && target.property.type === \"Literal\"\n ? String(target.property.value)\n : null;\n\n // exports.X = ...\n if (target.object.type === \"Identifier\" && target.object.name === \"exports\") {\n if (propName === null) return { kind: \"unknown\" };\n return propName === \"default\" ? { kind: \"default\" } : { kind: \"named\", name: propName };\n }\n // module.exports = ... behaves like a default export after interop:\n // discovery unwraps its properties.\n if (target.object.type === \"Identifier\" && target.object.name === \"module\") {\n return propName === \"exports\" ? { kind: \"default\" } : { kind: \"unknown\" };\n }\n // module.exports.X = ...\n if (\n target.object.type === \"MemberExpression\" &&\n target.object.object.type === \"Identifier\" &&\n target.object.object.name === \"module\" &&\n !target.object.computed &&\n target.object.property.type === \"Identifier\" &&\n target.object.property.name === \"exports\"\n ) {\n if (propName === null) return { kind: \"unknown\" };\n return propName === \"default\" ? { kind: \"default\" } : { kind: \"named\", name: propName };\n }\n return mentionsExports(target) ? { kind: \"unknown\" } : { kind: \"ignored\" };\n}\n\n/** Object.defineProperty(exports, \"__esModule\", { value: true }) */\nfunction isEsModuleMarker(expr: Expression): boolean {\n return (\n expr.type === \"CallExpression\" &&\n expr.callee.type === \"MemberExpression\" &&\n expr.callee.object.type === \"Identifier\" &&\n expr.callee.object.name === \"Object\" &&\n !expr.callee.computed &&\n expr.callee.property.type === \"Identifier\" &&\n expr.callee.property.name === \"defineProperty\" &&\n expr.arguments[0]?.type === \"Identifier\" &&\n expr.arguments[0].name === \"exports\" &&\n expr.arguments[1]?.type === \"Literal\" &&\n expr.arguments[1].value === \"__esModule\"\n );\n}\n\n/**\n * Collect every identifier name used in a reference position, excluding the\n * `expected` nodes (declaration ids and exports-chain participants).\n * Non-reference positions — non-computed member properties, non-computed\n * object/class keys, labels — are skipped. Over-counting (e.g. shadowed\n * names in nested scopes, function params) only makes the filter more\n * conservative, never less safe.\n */\nfunction collectReferencedNames(program: Program, expected: Set<object>): Set<string> {\n const names = new Set<string>();\n const stack: unknown[] = [program];\n while (stack.length > 0) {\n const node = stack.pop();\n if (Array.isArray(node)) {\n for (const item of node) stack.push(item);\n continue;\n }\n if (typeof node !== \"object\" || node === null) continue;\n const record = node as Record<string, unknown>;\n const type = record[\"type\"];\n if (typeof type !== \"string\") continue;\n\n if (type === \"Identifier\") {\n if (!expected.has(node)) names.add(record[\"name\"] as string);\n continue;\n }\n\n for (const [key, value] of Object.entries(record)) {\n if (typeof value !== \"object\" || value === null) continue;\n // Skip non-reference identifier positions.\n if (key === \"property\" && type === \"MemberExpression\" && record[\"computed\"] !== true)\n continue;\n if (\n key === \"key\" &&\n (type === \"Property\" || type === \"PropertyDefinition\" || type === \"MethodDefinition\") &&\n record[\"computed\"] !== true\n )\n continue;\n if (key === \"label\") continue;\n stack.push(value);\n }\n }\n return names;\n}\n\n/** Deep-walk an acorn node for any reference to `exports` or `module`. */\nfunction mentionsExports(root: object): boolean {\n const stack: unknown[] = [root];\n while (stack.length > 0) {\n const node = stack.pop();\n if (Array.isArray(node)) {\n for (const item of node) stack.push(item);\n continue;\n }\n if (typeof node !== \"object\" || node === null) continue;\n const record = node as Record<string, unknown>;\n if (typeof record[\"type\"] === \"string\") {\n if (\n record[\"type\"] === \"Identifier\" &&\n (record[\"name\"] === \"exports\" || record[\"name\"] === \"module\")\n ) {\n return true;\n }\n }\n for (const value of Object.values(record)) {\n if (typeof value === \"object\" && value !== null) stack.push(value);\n }\n }\n return false;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"static-filter.js","names":[],"sources":["../src/static-filter.ts"],"sourcesContent":["import path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type {\n AnyNode,\n AssignmentExpression,\n Expression,\n Program,\n Statement,\n VariableDeclaration,\n} from \"acorn\";\nimport { parse } from \"acorn\";\nimport type { Jiti } from \"jiti\";\n\n/**\n * Static pre-filter: decide whether a source file could export Zod schemas\n * WITHOUT executing it.\n *\n * Discovery executes candidate files (and, transitively, their whole import\n * graph) to scan exports. In autoDiscover mode most files that import zod\n * only export functions, components, or constants — executing them is pure\n * waste. This filter transpiles the single file (no dependencies) with\n * jiti's transform and inspects the top-level exports of the resulting\n * CommonJS with acorn — unless a line of the source already settles the\n * answer (see hasObviousCandidateExport).\n *\n * Conservative by construction: it returns false (\"skip\") only when every\n * export is statically provable as a non-schema (function, class, arrow,\n * literal, ...). Anything unrecognized — call expressions, re-exports,\n * `export *`, dynamic patterns, transpile/parse failures — keeps the file a\n * candidate, so a filter miss costs one execution, never a schema.\n */\n\ntype Classification = \"safe\" | \"candidate\";\n\n/** Lazily created jiti instance used only for single-file transpilation. */\nlet transformerPromise: Promise<Jiti> | undefined;\n\nfunction getTransformer(): Promise<Jiti> {\n transformerPromise ??= import(\"jiti\").then(({ createJiti }) =>\n createJiti(pathToFileURL(path.resolve(\"__zod-compiler-static-filter__.mjs\")).href),\n );\n return transformerPromise;\n}\n\n/**\n * `export const X = ident(…)`, `ident.member`, `ident<T>(…)` or `new Ident(…)`,\n * or `export default` of the same, starting a line.\n */\nconst OBVIOUS_CANDIDATE_EXPORT =\n /^[ \\t]*export[ \\t]+(?:(?:const|let|var)[ \\t]+[A-Za-z_$][\\w$]*[ \\t]*(?::[^=;]*)?=|default[ \\t]+)\\s*(?:new\\s+)?(?!(?:async|function|class|typeof|void|delete|await)\\b)[A-Za-z_$][\\w$]*\\s*[.(<]/m;\n\n/**\n * Does the source export a value the analysis would call a candidate on sight?\n *\n * Such an initializer is a call, member or `new` expression, and\n * `classifyValue` reports every one of those as a candidate, so for a file that\n * has one the transpile and parse below cannot change the answer. A schema\n * file almost always does (`z.object(…)`, `compile(…)`), and skipping jiti's\n * Babel transform for it took a quarter off a cold build of a 181-file corpus.\n * The prefixes the analysis calls SAFE stay out of the pattern — `typeof`,\n * `void` and `delete` (unary), `async`, `function` and `class` — and `await`\n * is left to it as well.\n *\n * A line it misreads — inside a block comment or template literal, an `export`\n * nested in a namespace, a `<` comparison — errs toward candidate, the\n * filter's documented safe direction: one extra execution, never a missed\n * schema.\n */\nexport function hasObviousCandidateExport(code: string): boolean {\n return OBVIOUS_CANDIDATE_EXPORT.test(code);\n}\n\n/**\n * Check whether a file's exports could include Zod schemas (or compile()\n * results) without executing the file. `filename` selects the TS/JSX\n * transforms; the file does not need to exist on disk.\n */\nexport async function mayExportSchemas(code: string, filename: string): Promise<boolean> {\n if (hasObviousCandidateExport(code)) return true;\n let transpiled: string;\n try {\n const jiti = await getTransformer();\n transpiled = jiti.transform({\n source: code,\n filename,\n ts: /\\.[cm]?tsx?$/.test(filename),\n jsx: /\\.[cm]?[jt]sx$/.test(filename),\n // async mode keeps top-level await and emits `await jitiImport(...)`\n // instead of failing — statements stay top-level either way.\n async: true,\n });\n } catch {\n return true;\n }\n // jiti reports parse failures by embedding an error marker in the output\n // instead of throwing. We can't analyze such files — keep them candidates\n // so the real loader surfaces (or tolerates) the failure exactly as before.\n if (transpiled.includes(\"__JITI_ERROR__\")) return true;\n\n let program: Program;\n try {\n // Module mode: the CJS output has no import/export statements, but may\n // contain top-level await (async transform).\n program = parse(transpiled, { ecmaVersion: \"latest\", sourceType: \"module\" });\n } catch {\n return true;\n }\n\n return analyzeProgram(program);\n}\n\n/** Where an assignment target sends its value, in discovery terms. */\ntype ExportTarget =\n | { kind: \"named\"; name: string }\n | { kind: \"default\" }\n | { kind: \"local\"; name: string }\n | { kind: \"ignored\" }\n | { kind: \"unknown\" };\n\nfunction analyzeProgram(program: Program): boolean {\n // Pass 1: top-level bindings for identifier resolution, plus the set of\n // identifier nodes that are \"expected\" (declaration ids and exports-chain\n // participants). Pass 2 counts every OTHER reference: a name referenced\n // outside its declaration/export may be mutated after creation\n // (defineProperty, member writes, helper calls), so object/array values\n // it names cannot be proven schema-free.\n const safeDecls = new Set<string>();\n const varInits = new Map<string, Expression | null>();\n const expectedNodes = new Set<object>();\n\n function recordChainNodes(expr: Expression): void {\n let node = expr;\n let hasExportTarget = false;\n while (node.type === \"AssignmentExpression\" && node.operator === \"=\") {\n if (node.left.type === \"Identifier\") {\n // Assignment LHS identifiers rebind — they never read the value.\n expectedNodes.add(node.left);\n } else if (node.left.type === \"MemberExpression\" && mentionsExports(node.left)) {\n hasExportTarget = true;\n }\n node = node.right;\n }\n // The bare-identifier value of an export chain (`exports.f = f`) is the\n // export itself; in a plain local chain (`alias = obj`) it is a real\n // aliasing reference and must count toward taint.\n if (hasExportTarget && node.type === \"Identifier\") expectedNodes.add(node);\n }\n\n for (const stmt of program.body) {\n if (stmt.type === \"FunctionDeclaration\" || stmt.type === \"ClassDeclaration\") {\n safeDecls.add(stmt.id.name);\n expectedNodes.add(stmt.id);\n } else if (stmt.type === \"VariableDeclaration\") {\n for (const decl of stmt.declarations) {\n if (decl.id.type === \"Identifier\") {\n expectedNodes.add(decl.id);\n // The init may be an `exports.X = value` chain — resolution should\n // see the final value expression.\n varInits.set(decl.id.name, decl.init ? unwrapAssignmentChain(decl.init).value : null);\n }\n if (decl.init) recordChainNodes(decl.init);\n }\n } else if (stmt.type === \"ExpressionStatement\") {\n recordChainNodes(stmt.expression);\n }\n }\n\n const referencedNames = collectReferencedNames(program, expectedNodes);\n\n function resolveIdentifier(name: string, defaultish: boolean, seen: Set<string>): Classification {\n if (seen.has(name)) return \"candidate\";\n seen.add(name);\n if (safeDecls.has(name)) return \"safe\";\n const init = varInits.get(name);\n if (init === undefined || init === null) return \"candidate\";\n return classifyValue(init, defaultish, seen, [name]);\n }\n\n /**\n * Could this expression evaluate to a Zod schema (an object carrying\n * `_zod.def` or the compile() marker)? `bindingNames` are the local\n * aliases of the value — a mutable container (object/array) is only safe\n * when none of its aliases are referenced elsewhere in the file.\n */\n function classifyValue(\n node: Expression,\n defaultish: boolean,\n seen: Set<string>,\n bindingNames: readonly string[],\n ): Classification {\n switch (node.type) {\n case \"Literal\":\n case \"TemplateLiteral\":\n case \"ArrowFunctionExpression\":\n case \"FunctionExpression\":\n case \"ClassExpression\":\n case \"UnaryExpression\":\n case \"UpdateExpression\":\n case \"BinaryExpression\":\n return \"safe\";\n case \"ObjectExpression\":\n case \"ArrayExpression\":\n // Discovery unwraps the properties of a default export object (and\n // arrays are objects), so containers are candidates there. Named\n // export containers are never unwrapped — but a container whose\n // alias is referenced elsewhere may be mutated into a schema shape.\n if (defaultish) return \"candidate\";\n return bindingNames.some((n) => referencedNames.has(n)) ? \"candidate\" : \"safe\";\n case \"Identifier\":\n return resolveIdentifier(node.name, defaultish, seen);\n case \"ConditionalExpression\":\n return classifyValue(node.consequent, defaultish, seen, bindingNames) === \"safe\" &&\n classifyValue(node.alternate, defaultish, seen, bindingNames) === \"safe\"\n ? \"safe\"\n : \"candidate\";\n case \"LogicalExpression\":\n return classifyValue(node.left, defaultish, seen, bindingNames) === \"safe\" &&\n classifyValue(node.right, defaultish, seen, bindingNames) === \"safe\"\n ? \"safe\"\n : \"candidate\";\n case \"SequenceExpression\": {\n const last = node.expressions[node.expressions.length - 1];\n return last ? classifyValue(last, defaultish, seen, bindingNames) : \"candidate\";\n }\n case \"AssignmentExpression\":\n return classifyValue(unwrapAssignmentChain(node).value, defaultish, seen, bindingNames);\n default:\n // CallExpression, NewExpression, MemberExpression, AwaitExpression, ...\n return \"candidate\";\n }\n }\n\n /** Process one assignment chain: classify the value for each export target. */\n function processAssignment(expr: Expression, declaredName?: string): Classification {\n const { targets, value } = unwrapAssignmentChain(expr);\n const bindingNames: string[] = declaredName ? [declaredName] : [];\n const exportTargets: ExportTarget[] = [];\n for (const target of targets) {\n const exportTarget = exportTargetOf(target);\n if (exportTarget.kind === \"unknown\") return \"candidate\";\n if (exportTarget.kind === \"local\") bindingNames.push(exportTarget.name);\n exportTargets.push(exportTarget);\n }\n for (const exportTarget of exportTargets) {\n if (exportTarget.kind === \"named\") {\n if (classifyValue(value, false, new Set(), bindingNames) === \"candidate\")\n return \"candidate\";\n } else if (exportTarget.kind === \"default\") {\n if (classifyValue(value, true, new Set(), bindingNames) === \"candidate\") return \"candidate\";\n }\n }\n // Catch-all: the value itself may reference exports in a shape we did\n // not positively classify (e.g. `exports.a = fn(exports.b)`).\n return mentionsExports(value) ? \"candidate\" : \"safe\";\n }\n\n function processVariableDeclaration(stmt: VariableDeclaration): Classification {\n for (const decl of stmt.declarations) {\n if (!decl.init) continue;\n if (decl.id.type === \"Identifier\") {\n if (processAssignment(decl.init, decl.id.name) === \"candidate\") return \"candidate\";\n } else if (mentionsExports(decl.init)) {\n // Destructuring declarators don't export by themselves (babel emits\n // separate `exports.x = x` statements), but be paranoid about any\n // exports reference hiding inside.\n return \"candidate\";\n }\n }\n return \"safe\";\n }\n\n function processStatement(stmt: Statement): Classification {\n switch (stmt.type) {\n case \"FunctionDeclaration\":\n case \"ClassDeclaration\":\n // Bodies that touch exports (hand-written CJS patterns) are opaque.\n return mentionsExports(stmt) ? \"candidate\" : \"safe\";\n case \"VariableDeclaration\":\n return processVariableDeclaration(stmt);\n case \"ExpressionStatement\":\n return processExpression(stmt.expression);\n case \"EmptyStatement\":\n return \"safe\";\n default:\n // if/try/for/... — babel never emits exports there for ESM input,\n // but hand-written CJS might (`if (x) module.exports = ...`).\n return mentionsExports(stmt) ? \"candidate\" : \"safe\";\n }\n }\n\n function processExpression(expr: Expression): Classification {\n switch (expr.type) {\n case \"Literal\":\n // Directive prologue (\"use strict\")\n return \"safe\";\n case \"AssignmentExpression\":\n return processAssignment(expr);\n case \"SequenceExpression\": {\n for (const sub of expr.expressions) {\n if (processExpression(sub) === \"candidate\") return \"candidate\";\n }\n return \"safe\";\n }\n case \"CallExpression\":\n if (isEsModuleMarker(expr)) return \"safe\";\n // Other exports-touching calls: re-export getters\n // (Object.defineProperty), `export *` loops, helpers.\n return mentionsExports(expr) ? \"candidate\" : \"safe\";\n default:\n return mentionsExports(expr) ? \"candidate\" : \"safe\";\n }\n }\n\n for (const stmt of program.body) {\n // The CJS output of jiti's transform contains no module declarations,\n // but if one ever appears, treat the file as a candidate.\n if (!isStatement(stmt)) return true;\n if (processStatement(stmt) === \"candidate\") return true;\n }\n return false;\n}\n\nfunction isStatement(node: AnyNode): node is Statement {\n return !node.type.startsWith(\"Import\") && !node.type.startsWith(\"Export\");\n}\n\n/**\n * Unwrap `a = b = c = value` into its assignment targets and final value.\n * Non-assignment expressions return zero targets and themselves as value.\n */\nfunction unwrapAssignmentChain(expr: Expression): {\n targets: AssignmentExpression[\"left\"][];\n value: Expression;\n} {\n const targets: AssignmentExpression[\"left\"][] = [];\n let value = expr;\n while (value.type === \"AssignmentExpression\" && value.operator === \"=\") {\n targets.push(value.left);\n value = value.right;\n }\n return { targets, value };\n}\n\n/** Classify an assignment target by what discovery would see. */\nfunction exportTargetOf(target: AssignmentExpression[\"left\"]): ExportTarget {\n if (target.type === \"Identifier\") {\n if (target.name === \"exports\" || target.name === \"module\") return { kind: \"unknown\" };\n return { kind: \"local\", name: target.name };\n }\n if (target.type !== \"MemberExpression\") {\n return mentionsExports(target) ? { kind: \"unknown\" } : { kind: \"ignored\" };\n }\n const propName =\n !target.computed && target.property.type === \"Identifier\"\n ? target.property.name\n : target.computed && target.property.type === \"Literal\"\n ? String(target.property.value)\n : null;\n\n // exports.X = ...\n if (target.object.type === \"Identifier\" && target.object.name === \"exports\") {\n if (propName === null) return { kind: \"unknown\" };\n return propName === \"default\" ? { kind: \"default\" } : { kind: \"named\", name: propName };\n }\n // module.exports = ... behaves like a default export after interop:\n // discovery unwraps its properties.\n if (target.object.type === \"Identifier\" && target.object.name === \"module\") {\n return propName === \"exports\" ? { kind: \"default\" } : { kind: \"unknown\" };\n }\n // module.exports.X = ...\n if (\n target.object.type === \"MemberExpression\" &&\n target.object.object.type === \"Identifier\" &&\n target.object.object.name === \"module\" &&\n !target.object.computed &&\n target.object.property.type === \"Identifier\" &&\n target.object.property.name === \"exports\"\n ) {\n if (propName === null) return { kind: \"unknown\" };\n return propName === \"default\" ? { kind: \"default\" } : { kind: \"named\", name: propName };\n }\n return mentionsExports(target) ? { kind: \"unknown\" } : { kind: \"ignored\" };\n}\n\n/** Object.defineProperty(exports, \"__esModule\", { value: true }) */\nfunction isEsModuleMarker(expr: Expression): boolean {\n return (\n expr.type === \"CallExpression\" &&\n expr.callee.type === \"MemberExpression\" &&\n expr.callee.object.type === \"Identifier\" &&\n expr.callee.object.name === \"Object\" &&\n !expr.callee.computed &&\n expr.callee.property.type === \"Identifier\" &&\n expr.callee.property.name === \"defineProperty\" &&\n expr.arguments[0]?.type === \"Identifier\" &&\n expr.arguments[0].name === \"exports\" &&\n expr.arguments[1]?.type === \"Literal\" &&\n expr.arguments[1].value === \"__esModule\"\n );\n}\n\n/**\n * Collect every identifier name used in a reference position, excluding the\n * `expected` nodes (declaration ids and exports-chain participants).\n * Non-reference positions — non-computed member properties, non-computed\n * object/class keys, labels — are skipped. Over-counting (e.g. shadowed\n * names in nested scopes, function params) only makes the filter more\n * conservative, never less safe.\n */\nfunction collectReferencedNames(program: Program, expected: Set<object>): Set<string> {\n const names = new Set<string>();\n const stack: unknown[] = [program];\n while (stack.length > 0) {\n const node = stack.pop();\n if (Array.isArray(node)) {\n for (const item of node) stack.push(item);\n continue;\n }\n if (typeof node !== \"object\" || node === null) continue;\n const record = node as Record<string, unknown>;\n const type = record[\"type\"];\n if (typeof type !== \"string\") continue;\n\n if (type === \"Identifier\") {\n if (!expected.has(node)) names.add(record[\"name\"] as string);\n continue;\n }\n\n for (const [key, value] of Object.entries(record)) {\n if (typeof value !== \"object\" || value === null) continue;\n // Skip non-reference identifier positions.\n if (key === \"property\" && type === \"MemberExpression\" && record[\"computed\"] !== true)\n continue;\n if (\n key === \"key\" &&\n (type === \"Property\" || type === \"PropertyDefinition\" || type === \"MethodDefinition\") &&\n record[\"computed\"] !== true\n )\n continue;\n if (key === \"label\") continue;\n stack.push(value);\n }\n }\n return names;\n}\n\n/** Deep-walk an acorn node for any reference to `exports` or `module`. */\nfunction mentionsExports(root: object): boolean {\n const stack: unknown[] = [root];\n while (stack.length > 0) {\n const node = stack.pop();\n if (Array.isArray(node)) {\n for (const item of node) stack.push(item);\n continue;\n }\n if (typeof node !== \"object\" || node === null) continue;\n const record = node as Record<string, unknown>;\n if (typeof record[\"type\"] === \"string\") {\n if (\n record[\"type\"] === \"Identifier\" &&\n (record[\"name\"] === \"exports\" || record[\"name\"] === \"module\")\n ) {\n return true;\n }\n }\n for (const value of Object.values(record)) {\n if (typeof value === \"object\" && value !== null) stack.push(value);\n }\n }\n return false;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"hoist-compile.d.ts","names":[],"sources":["../../src/unplugin/hoist-compile.ts"],"mappings":";;;;;UAkDiB;;EAEf;;EAEA;;EAEA,MAAM;;;;;;iBA6Fc,sBACpB,kBAAkB,iBAClB,cACA,YACA,MAAM,cACL,QAAQ"}
1
+ {"version":3,"file":"hoist-compile.d.ts","names":[],"sources":["../../src/unplugin/hoist-compile.ts"],"mappings":";;;;;UAmDiB;;EAEf;;EAEA;;EAEA,MAAM;;;;;;iBAgGc,sBACpB,kBAAkB,iBAClB,cACA,YACA,MAAM,cACL,QAAQ"}
@@ -2,6 +2,7 @@ import { isZodSchema } from "../is-zod-schema.js";
2
2
  import { loadModule } from "../loader.js";
3
3
  import { isUnsupportedZodVersionError, warnUnsupportedZodOnce } from "../core/extract/zod-version.js";
4
4
  import { generateValidator } from "../core/codegen/index.js";
5
+ import { zodErrorClassOf } from "../core/extract/error-class.js";
5
6
  import { extractSchema } from "../core/extract/index.js";
6
7
  import { analyzeHoistedExpression, collectImportBindings } from "./hoist.js";
7
8
  //#region src/unplugin/hoist-compile.ts
@@ -41,7 +42,10 @@ async function compileOne(schema, importDetails, id, mode, moduleCache) {
41
42
  const refEntries = [];
42
43
  const ir = extractSchema(value, refEntries);
43
44
  if (ir.type === "fallback") return null;
44
- const codegenResult = generateValidator(ir, schema.name, { mode });
45
+ const codegenResult = generateValidator(ir, schema.name, {
46
+ mode,
47
+ errorClass: zodErrorClassOf(value)
48
+ });
45
49
  return {
46
50
  name: schema.name,
47
51
  text: schema.text,
@@ -1 +1 @@
1
- {"version":3,"file":"hoist-compile.js","names":[],"sources":["../../src/unplugin/hoist-compile.ts"],"sourcesContent":["/**\n * Build-time compilation of hoisted schemas.\n *\n * After hoistZodSchemasMeta() lifts `z.object({...})` constructions to\n * module-scope `_zh_*` declarations, this step evaluates each hoisted\n * expression with the project's real zod module, runs the regular\n * extract → codegen pipeline on the resulting schema object, and hands the\n * transform a compiled IIFE to splice in as the declaration initializer:\n *\n * const _zh_x = z.object({ id: z.number() });\n * ⇣\n * const _zh_x = /* @__PURE__ *\\/ (() => { ... return __zcMkv(...); })();\n *\n * Eligibility is STRICTER than hoist eligibility. Hoisting only moves an\n * expression; compiling it bakes build-time evaluation results into\n * generated checks, so the construction must be deterministic:\n *\n * - Every EAGER free identifier must be a zod-package binding. Anything\n * else (other imports: `getLimit()`, globals: `new Date()`,\n * `Math.random()`) could evaluate differently at build time vs module\n * load — those schemas stay plainly hoisted.\n * - DEFERRED references (inside refine/transform/default callbacks) are\n * unrestricted: callbacks reach generated code via fn.toString() or stay\n * on the runtime-constructed schema (`__rf` delegation), never via their\n * build-time closure values. If extraction itself needs a deferred value\n * it cannot have (z.lazy(() => ImportedChild)), evaluation throws and the\n * schema falls back to a plain hoist.\n *\n * Every failure path is graceful: the declaration keeps its original zod\n * expression and runtime behavior is unchanged.\n */\n\nimport type { CodegenMode } from \"../core/codegen/context.js\";\nimport { generateValidator } from \"../core/codegen/index.js\";\nimport { extractSchema, type RefEntry } from \"../core/extract/index.js\";\nimport {\n isUnsupportedZodVersionError,\n warnUnsupportedZodOnce,\n} from \"../core/extract/zod-version.js\";\nimport type { CompiledSchemaInfo } from \"../core/pipeline.js\";\nimport { isZodSchema } from \"../is-zod-schema.js\";\nimport { loadModule } from \"../loader.js\";\nimport {\n analyzeHoistedExpression,\n collectImportBindings,\n type HoistedSchema,\n type ImportDetail,\n} from \"./hoist.js\";\n\n/** A hoisted declaration whose initializer can be replaced with a compiled IIFE. */\nexport interface CompiledHoistedSchema {\n /** The `_zh_*` declaration name. */\n name: string;\n /** Original construction expression text (becomes the IIFE's schema expression). */\n text: string;\n /** Compiled validator for the schema. */\n info: CompiledSchemaInfo;\n}\n\n/**\n * Evaluate a hoisted expression with its zod bindings and compile it.\n * Returns null when the schema is ineligible or anything fails.\n */\nasync function compileOne(\n schema: HoistedSchema,\n importDetails: Map<string, ImportDetail>,\n id: string,\n mode: CodegenMode,\n moduleCache: Map<string, Promise<Record<string, unknown>>>,\n): Promise<CompiledHoistedSchema | null> {\n const analysis = analyzeHoistedExpression(schema.text);\n if (analysis === null) return null;\n\n // Determinism gate: eager evaluation may only touch zod bindings.\n const free = new Set([...analysis.eagerFree, ...analysis.deferredFree]);\n const bindings: Array<{ name: string; detail: ImportDetail }> = [];\n for (const name of analysis.eagerFree) {\n const detail = importDetails.get(name);\n if (!detail || !isZodSpecifier(detail.specifier)) return null;\n }\n // Inject every free import binding we can resolve (eager ones are all zod\n // by the gate above; deferred ones are best-effort — extraction only\n // dereferences them for build-time-invoked callbacks like z.lazy getters).\n for (const name of free) {\n const detail = importDetails.get(name);\n if (detail && isZodSpecifier(detail.specifier)) {\n bindings.push({ name, detail });\n } else if (analysis.eagerFree.has(name)) {\n return null;\n }\n // deferred non-zod names stay unbound: the evaluated closure would throw\n // if invoked at build time, which the try/catch below converts to a skip.\n }\n\n try {\n const values = await Promise.all(\n bindings.map(async ({ detail }) => {\n let loading = moduleCache.get(detail.specifier);\n if (!loading) {\n loading = loadModule(detail.specifier, id);\n moduleCache.set(detail.specifier, loading);\n }\n const mod = await loading;\n return detail.imported === \"*\" ? mod : mod[detail.imported];\n }),\n );\n\n const evaluate = new Function(\n ...bindings.map((b) => b.name),\n `\"use strict\"; return (${schema.text});`,\n );\n const value: unknown = evaluate(...values);\n if (!isZodSchema(value)) return null;\n\n const refEntries: RefEntry[] = [];\n const ir = extractSchema(value, refEntries);\n // A root fallback compiles to a pure delegation wrapper — strictly worse\n // than leaving the plain hoisted construction in place.\n if (ir.type === \"fallback\") return null;\n\n const codegenResult = generateValidator(ir, schema.name, { mode });\n return {\n name: schema.name,\n text: schema.text,\n info: { exportName: schema.name, codegenResult, refEntries },\n };\n } catch (error) {\n // Anything the hoisted expression cannot do at build time is a skip, not a\n // failure — except the zod version guard, a dependency-range problem the\n // build has to report (once: every schema trips it the same way).\n if (isUnsupportedZodVersionError(error)) warnUnsupportedZodOnce(error.message);\n return null;\n }\n}\n\nfunction isZodSpecifier(specifier: string): boolean {\n return (\n specifier === \"zod\" ||\n specifier === \"zod/v4\" ||\n specifier === \"zod/mini\" ||\n specifier === \"zod/v4/mini\" ||\n specifier === \"zod/v4-mini\"\n );\n}\n\n/**\n * Compile every eligible hoisted schema. Failures are silent per schema —\n * the caller leaves ineligible declarations as plain hoists.\n */\nexport async function compileHoistedSchemas(\n schemas: readonly HoistedSchema[],\n code: string,\n id: string,\n mode: CodegenMode,\n): Promise<CompiledHoistedSchema[]> {\n const { details } = collectImportBindings(code);\n const moduleCache = new Map<string, Promise<Record<string, unknown>>>();\n const compiled: CompiledHoistedSchema[] = [];\n for (const schema of schemas) {\n const result = await compileOne(schema, details, id, mode, moduleCache);\n if (result !== null) compiled.push(result);\n }\n return compiled;\n}\n"],"mappings":";;;;;;;;;;;AA+DA,eAAe,WACb,QACA,eACA,IACA,MACA,aACuC;CACvC,MAAM,WAAW,yBAAyB,OAAO,IAAI;CACrD,IAAI,aAAa,MAAM,OAAO;CAG9B,MAAM,uBAAO,IAAI,IAAI,CAAC,GAAG,SAAS,WAAW,GAAG,SAAS,YAAY,CAAC;CACtE,MAAM,WAA0D,CAAC;CACjE,KAAK,MAAM,QAAQ,SAAS,WAAW;EACrC,MAAM,SAAS,cAAc,IAAI,IAAI;EACrC,IAAI,CAAC,UAAU,CAAC,eAAe,OAAO,SAAS,GAAG,OAAO;CAC3D;CAIA,KAAK,MAAM,QAAQ,MAAM;EACvB,MAAM,SAAS,cAAc,IAAI,IAAI;EACrC,IAAI,UAAU,eAAe,OAAO,SAAS,GAC3C,SAAS,KAAK;GAAE;GAAM;EAAO,CAAC;OACzB,IAAI,SAAS,UAAU,IAAI,IAAI,GACpC,OAAO;CAIX;CAEA,IAAI;EACF,MAAM,SAAS,MAAM,QAAQ,IAC3B,SAAS,IAAI,OAAO,EAAE,aAAa;GACjC,IAAI,UAAU,YAAY,IAAI,OAAO,SAAS;GAC9C,IAAI,CAAC,SAAS;IACZ,UAAU,WAAW,OAAO,WAAW,EAAE;IACzC,YAAY,IAAI,OAAO,WAAW,OAAO;GAC3C;GACA,MAAM,MAAM,MAAM;GAClB,OAAO,OAAO,aAAa,MAAM,MAAM,IAAI,OAAO;EACpD,CAAC,CACH;EAMA,MAAM,QAAiB,IAJF,SACnB,GAAG,SAAS,KAAK,MAAM,EAAE,IAAI,GAC7B,yBAAyB,OAAO,KAAK,GAET,CAAC,CAAC,GAAG,MAAM;EACzC,IAAI,CAAC,YAAY,KAAK,GAAG,OAAO;EAEhC,MAAM,aAAyB,CAAC;EAChC,MAAM,KAAK,cAAc,OAAO,UAAU;EAG1C,IAAI,GAAG,SAAS,YAAY,OAAO;EAEnC,MAAM,gBAAgB,kBAAkB,IAAI,OAAO,MAAM,EAAE,KAAK,CAAC;EACjE,OAAO;GACL,MAAM,OAAO;GACb,MAAM,OAAO;GACb,MAAM;IAAE,YAAY,OAAO;IAAM;IAAe;GAAW;EAC7D;CACF,SAAS,OAAO;EAId,IAAI,6BAA6B,KAAK,GAAG,uBAAuB,MAAM,OAAO;EAC7E,OAAO;CACT;AACF;AAEA,SAAS,eAAe,WAA4B;CAClD,OACE,cAAc,SACd,cAAc,YACd,cAAc,cACd,cAAc,iBACd,cAAc;AAElB;;;;;AAMA,eAAsB,sBACpB,SACA,MACA,IACA,MACkC;CAClC,MAAM,EAAE,YAAY,sBAAsB,IAAI;CAC9C,MAAM,8BAAc,IAAI,IAA8C;CACtE,MAAM,WAAoC,CAAC;CAC3C,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,SAAS,MAAM,WAAW,QAAQ,SAAS,IAAI,MAAM,WAAW;EACtE,IAAI,WAAW,MAAM,SAAS,KAAK,MAAM;CAC3C;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"hoist-compile.js","names":[],"sources":["../../src/unplugin/hoist-compile.ts"],"sourcesContent":["/**\n * Build-time compilation of hoisted schemas.\n *\n * After hoistZodSchemasMeta() lifts `z.object({...})` constructions to\n * module-scope `_zh_*` declarations, this step evaluates each hoisted\n * expression with the project's real zod module, runs the regular\n * extract → codegen pipeline on the resulting schema object, and hands the\n * transform a compiled IIFE to splice in as the declaration initializer:\n *\n * const _zh_x = z.object({ id: z.number() });\n * ⇣\n * const _zh_x = /* @__PURE__ *\\/ (() => { ... return __zcMkv(...); })();\n *\n * Eligibility is STRICTER than hoist eligibility. Hoisting only moves an\n * expression; compiling it bakes build-time evaluation results into\n * generated checks, so the construction must be deterministic:\n *\n * - Every EAGER free identifier must be a zod-package binding. Anything\n * else (other imports: `getLimit()`, globals: `new Date()`,\n * `Math.random()`) could evaluate differently at build time vs module\n * load — those schemas stay plainly hoisted.\n * - DEFERRED references (inside refine/transform/default callbacks) are\n * unrestricted: callbacks reach generated code via fn.toString() or stay\n * on the runtime-constructed schema (`__rf` delegation), never via their\n * build-time closure values. If extraction itself needs a deferred value\n * it cannot have (z.lazy(() => ImportedChild)), evaluation throws and the\n * schema falls back to a plain hoist.\n *\n * Every failure path is graceful: the declaration keeps its original zod\n * expression and runtime behavior is unchanged.\n */\n\nimport type { CodegenMode } from \"../core/codegen/context.js\";\nimport { generateValidator } from \"../core/codegen/index.js\";\nimport { zodErrorClassOf } from \"../core/extract/error-class.js\";\nimport { extractSchema, type RefEntry } from \"../core/extract/index.js\";\nimport {\n isUnsupportedZodVersionError,\n warnUnsupportedZodOnce,\n} from \"../core/extract/zod-version.js\";\nimport type { CompiledSchemaInfo } from \"../core/pipeline.js\";\nimport { isZodSchema } from \"../is-zod-schema.js\";\nimport { loadModule } from \"../loader.js\";\nimport {\n analyzeHoistedExpression,\n collectImportBindings,\n type HoistedSchema,\n type ImportDetail,\n} from \"./hoist.js\";\n\n/** A hoisted declaration whose initializer can be replaced with a compiled IIFE. */\nexport interface CompiledHoistedSchema {\n /** The `_zh_*` declaration name. */\n name: string;\n /** Original construction expression text (becomes the IIFE's schema expression). */\n text: string;\n /** Compiled validator for the schema. */\n info: CompiledSchemaInfo;\n}\n\n/**\n * Evaluate a hoisted expression with its zod bindings and compile it.\n * Returns null when the schema is ineligible or anything fails.\n */\nasync function compileOne(\n schema: HoistedSchema,\n importDetails: Map<string, ImportDetail>,\n id: string,\n mode: CodegenMode,\n moduleCache: Map<string, Promise<Record<string, unknown>>>,\n): Promise<CompiledHoistedSchema | null> {\n const analysis = analyzeHoistedExpression(schema.text);\n if (analysis === null) return null;\n\n // Determinism gate: eager evaluation may only touch zod bindings.\n const free = new Set([...analysis.eagerFree, ...analysis.deferredFree]);\n const bindings: Array<{ name: string; detail: ImportDetail }> = [];\n for (const name of analysis.eagerFree) {\n const detail = importDetails.get(name);\n if (!detail || !isZodSpecifier(detail.specifier)) return null;\n }\n // Inject every free import binding we can resolve (eager ones are all zod\n // by the gate above; deferred ones are best-effort — extraction only\n // dereferences them for build-time-invoked callbacks like z.lazy getters).\n for (const name of free) {\n const detail = importDetails.get(name);\n if (detail && isZodSpecifier(detail.specifier)) {\n bindings.push({ name, detail });\n } else if (analysis.eagerFree.has(name)) {\n return null;\n }\n // deferred non-zod names stay unbound: the evaluated closure would throw\n // if invoked at build time, which the try/catch below converts to a skip.\n }\n\n try {\n const values = await Promise.all(\n bindings.map(async ({ detail }) => {\n let loading = moduleCache.get(detail.specifier);\n if (!loading) {\n loading = loadModule(detail.specifier, id);\n moduleCache.set(detail.specifier, loading);\n }\n const mod = await loading;\n return detail.imported === \"*\" ? mod : mod[detail.imported];\n }),\n );\n\n const evaluate = new Function(\n ...bindings.map((b) => b.name),\n `\"use strict\"; return (${schema.text});`,\n );\n const value: unknown = evaluate(...values);\n if (!isZodSchema(value)) return null;\n\n const refEntries: RefEntry[] = [];\n const ir = extractSchema(value, refEntries);\n // A root fallback compiles to a pure delegation wrapper — strictly worse\n // than leaving the plain hoisted construction in place.\n if (ir.type === \"fallback\") return null;\n\n const codegenResult = generateValidator(ir, schema.name, {\n mode,\n errorClass: zodErrorClassOf(value),\n });\n return {\n name: schema.name,\n text: schema.text,\n info: { exportName: schema.name, codegenResult, refEntries },\n };\n } catch (error) {\n // Anything the hoisted expression cannot do at build time is a skip, not a\n // failure — except the zod version guard, a dependency-range problem the\n // build has to report (once: every schema trips it the same way).\n if (isUnsupportedZodVersionError(error)) warnUnsupportedZodOnce(error.message);\n return null;\n }\n}\n\nfunction isZodSpecifier(specifier: string): boolean {\n return (\n specifier === \"zod\" ||\n specifier === \"zod/v4\" ||\n specifier === \"zod/mini\" ||\n specifier === \"zod/v4/mini\" ||\n specifier === \"zod/v4-mini\"\n );\n}\n\n/**\n * Compile every eligible hoisted schema. Failures are silent per schema —\n * the caller leaves ineligible declarations as plain hoists.\n */\nexport async function compileHoistedSchemas(\n schemas: readonly HoistedSchema[],\n code: string,\n id: string,\n mode: CodegenMode,\n): Promise<CompiledHoistedSchema[]> {\n const { details } = collectImportBindings(code);\n const moduleCache = new Map<string, Promise<Record<string, unknown>>>();\n const compiled: CompiledHoistedSchema[] = [];\n for (const schema of schemas) {\n const result = await compileOne(schema, details, id, mode, moduleCache);\n if (result !== null) compiled.push(result);\n }\n return compiled;\n}\n"],"mappings":";;;;;;;;;;;;AAgEA,eAAe,WACb,QACA,eACA,IACA,MACA,aACuC;CACvC,MAAM,WAAW,yBAAyB,OAAO,IAAI;CACrD,IAAI,aAAa,MAAM,OAAO;CAG9B,MAAM,uBAAO,IAAI,IAAI,CAAC,GAAG,SAAS,WAAW,GAAG,SAAS,YAAY,CAAC;CACtE,MAAM,WAA0D,CAAC;CACjE,KAAK,MAAM,QAAQ,SAAS,WAAW;EACrC,MAAM,SAAS,cAAc,IAAI,IAAI;EACrC,IAAI,CAAC,UAAU,CAAC,eAAe,OAAO,SAAS,GAAG,OAAO;CAC3D;CAIA,KAAK,MAAM,QAAQ,MAAM;EACvB,MAAM,SAAS,cAAc,IAAI,IAAI;EACrC,IAAI,UAAU,eAAe,OAAO,SAAS,GAC3C,SAAS,KAAK;GAAE;GAAM;EAAO,CAAC;OACzB,IAAI,SAAS,UAAU,IAAI,IAAI,GACpC,OAAO;CAIX;CAEA,IAAI;EACF,MAAM,SAAS,MAAM,QAAQ,IAC3B,SAAS,IAAI,OAAO,EAAE,aAAa;GACjC,IAAI,UAAU,YAAY,IAAI,OAAO,SAAS;GAC9C,IAAI,CAAC,SAAS;IACZ,UAAU,WAAW,OAAO,WAAW,EAAE;IACzC,YAAY,IAAI,OAAO,WAAW,OAAO;GAC3C;GACA,MAAM,MAAM,MAAM;GAClB,OAAO,OAAO,aAAa,MAAM,MAAM,IAAI,OAAO;EACpD,CAAC,CACH;EAMA,MAAM,QAAiB,IAJF,SACnB,GAAG,SAAS,KAAK,MAAM,EAAE,IAAI,GAC7B,yBAAyB,OAAO,KAAK,GAET,CAAC,CAAC,GAAG,MAAM;EACzC,IAAI,CAAC,YAAY,KAAK,GAAG,OAAO;EAEhC,MAAM,aAAyB,CAAC;EAChC,MAAM,KAAK,cAAc,OAAO,UAAU;EAG1C,IAAI,GAAG,SAAS,YAAY,OAAO;EAEnC,MAAM,gBAAgB,kBAAkB,IAAI,OAAO,MAAM;GACvD;GACA,YAAY,gBAAgB,KAAK;EACnC,CAAC;EACD,OAAO;GACL,MAAM,OAAO;GACb,MAAM,OAAO;GACb,MAAM;IAAE,YAAY,OAAO;IAAM;IAAe;GAAW;EAC7D;CACF,SAAS,OAAO;EAId,IAAI,6BAA6B,KAAK,GAAG,uBAAuB,MAAM,OAAO;EAC7E,OAAO;CACT;AACF;AAEA,SAAS,eAAe,WAA4B;CAClD,OACE,cAAc,SACd,cAAc,YACd,cAAc,cACd,cAAc,iBACd,cAAc;AAElB;;;;;AAMA,eAAsB,sBACpB,SACA,MACA,IACA,MACkC;CAClC,MAAM,EAAE,YAAY,sBAAsB,IAAI;CAC9C,MAAM,8BAAc,IAAI,IAA8C;CACtE,MAAM,WAAoC,CAAC;CAC3C,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,SAAS,MAAM,WAAW,QAAQ,SAAS,IAAI,MAAM,WAAW;EACtE,IAAI,WAAW,MAAM,SAAS,KAAK,MAAM;CAC3C;CACA,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"transform.d.ts","names":[],"sources":["../../src/unplugin/transform.ts"],"mappings":";;;;UAkCiB;EACf;EACA;EACA;EACA;EACA;EACA;;;;;;;;;;cAsHW,wBAAsB;;;;iBAgDnB,gBAAgB,YAAY,UAAU;;;;;;;;;;;;;cA6BzC;EAAuB,SAAS;EAAU,SAAS;;;;;;;;iBAgChD,oBAAoB,UAAU,2BAA2B;iBAOzD,IAAI;iBAKJ,KAAK;UAKJ;EACf;EACA,KAAK;;;;;;;iBAQe,cACpB,cACA,YACA,SAAS,mBACR;;;;;;iBAUmB,qBACpB,cACA,YACA,SAAS,mBACR,QAAQ;;;;iBA2XK,cACd,cACA,SAAS,sBACT;EAAY;;;;;;iBAiDE,kBAAkB,cAAc;;;;;iBAgBhC,0BACd,cACA,SAAS,sBACT;EAAY;;;;;;iBAgGE,oBAAoB"}
1
+ {"version":3,"file":"transform.d.ts","names":[],"sources":["../../src/unplugin/transform.ts"],"mappings":";;;;UAsCiB;EACf;EACA;EACA;EACA;EACA;EACA;;;;;;;;;;cAsHW,wBAAsB;;;;iBAgDnB,gBAAgB,YAAY,UAAU;;;;;;;;;;;;;cA6BzC;EAAuB,SAAS;EAAU,SAAS;;;;;;;;iBAgChD,oBAAoB,UAAU,2BAA2B;iBAOzD,IAAI;iBAKJ,KAAK;UAKJ;EACf;EACA,KAAK;;;;;;;iBAQe,cACpB,cACA,YACA,SAAS,mBACR;;;;;;iBAUmB,qBACpB,cACA,YACA,SAAS,mBACR,QAAQ;;;;iBA8YK,cACd,cACA,SAAS,sBACT;EAAY;;;;;;iBAiDE,kBAAkB,cAAc;;;;;iBAgBhC,0BACd,cACA,SAAS,sBACT;EAAY;;;;;;iBAgGE,oBAAoB"}