zod-compiler 1.26.3 → 1.27.0

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 (38) hide show
  1. package/README.md +41 -1
  2. package/dist/core/compile.d.ts +5 -0
  3. package/dist/core/compile.d.ts.map +1 -1
  4. package/dist/core/compile.js +11 -1
  5. package/dist/core/compile.js.map +1 -1
  6. package/dist/core/extract/effects.d.ts +17 -1
  7. package/dist/core/extract/effects.d.ts.map +1 -1
  8. package/dist/core/extract/effects.js +131 -9
  9. package/dist/core/extract/effects.js.map +1 -1
  10. package/dist/core/extract/extractors/pipe.d.ts.map +1 -1
  11. package/dist/core/extract/extractors/pipe.js +18 -16
  12. package/dist/core/extract/extractors/pipe.js.map +1 -1
  13. package/dist/discovery.d.ts.map +1 -1
  14. package/dist/discovery.js +14 -18
  15. package/dist/discovery.js.map +1 -1
  16. package/dist/is-zod-schema.d.ts +23 -0
  17. package/dist/is-zod-schema.d.ts.map +1 -0
  18. package/dist/is-zod-schema.js +32 -0
  19. package/dist/is-zod-schema.js.map +1 -0
  20. package/dist/jit.d.ts +2 -0
  21. package/dist/jit.d.ts.map +1 -1
  22. package/dist/jit.js +72 -34
  23. package/dist/jit.js.map +1 -1
  24. package/dist/register/config.d.ts +20 -0
  25. package/dist/register/config.d.ts.map +1 -0
  26. package/dist/register/config.js +1 -0
  27. package/dist/register/index.d.ts +1 -0
  28. package/dist/register/index.js +68 -0
  29. package/dist/register/index.js.map +1 -0
  30. package/dist/register/transform.d.ts +16 -0
  31. package/dist/register/transform.d.ts.map +1 -0
  32. package/dist/register/transform.js +148 -0
  33. package/dist/register/transform.js.map +1 -0
  34. package/dist/unplugin/hoist-compile.d.ts.map +1 -1
  35. package/dist/unplugin/hoist-compile.js +1 -4
  36. package/dist/unplugin/hoist-compile.js.map +1 -1
  37. package/package.json +12 -1
  38. package/schema.json +80 -0
package/README.md CHANGED
@@ -14,7 +14,7 @@ input. No code changes required.
14
14
 
15
15
  ## Usage
16
16
 
17
- Four ways to use zod-compiler — pick one:
17
+ Five ways to use zod-compiler — pick one:
18
18
 
19
19
  ### 1. Automatic Mode (Default)
20
20
 
@@ -114,6 +114,46 @@ Libraries should ship plain Zod and let the app decide.
114
114
  Needs `new Function`, as Zod's own object fast-path does. `z.config({ jitless: true })` and a CSP
115
115
  that blocks eval both leave a working plain-Zod schema.
116
116
 
117
+ ### 5. Node.js Register Hook
118
+
119
+ Node.js 22.15+ can automatically insert the equivalent of `jit()` for exported schemas as modules
120
+ load, with no source changes and no bundler:
121
+
122
+ ```bash
123
+ node --import zod-compiler/register src/server.js
124
+ ```
125
+
126
+ The same preload handles ESM imports, CommonJS `require()`, and Node's native TypeScript formats. It
127
+ also chains with TypeScript runners:
128
+
129
+ ```bash
130
+ node --import zod-compiler/register --import tsx src/server.ts
131
+ ```
132
+
133
+ This is runtime JIT instrumentation, not the AOT source rewriting performed by the Vite, Rsbuild, and
134
+ other build plugins. The hook identifies exported schema bindings and registers their live Zod objects;
135
+ validators are generated in-process, lazily on first use. It does not execute modules twice and adds no
136
+ transform cache beyond Node's module cache. Use a build plugin or the CLI when generated validator code
137
+ must exist before Node starts or runtime `new Function` is unavailable.
138
+
139
+ Optional settings come from `zod-compiler.json` in the working directory:
140
+
141
+ ```json
142
+ {
143
+ "$schema": "./node_modules/zod-compiler/schema.json",
144
+ "include": ["src/**"],
145
+ "exclude": ["**/*.test.ts"],
146
+ "schemas": "auto",
147
+ "eager": false,
148
+ "output": "schema",
149
+ "hoist": true
150
+ }
151
+ ```
152
+
153
+ `output: "compact"` preserves the Zod schema and compiled valid-input fast path while delegating cold
154
+ error production to Zod. Full `"schema"` output remains the default. `"bag"` is unavailable because a
155
+ load hook cannot replace already-linked ESM export bindings safely.
156
+
117
157
  ## Build Plugin
118
158
 
119
159
  ### Supported Build Tools
@@ -16,6 +16,11 @@ declare function compile<T extends ZodType>(zodSchema: T): T & CompiledSchema<ou
16
16
  /**
17
17
  * Check if a value is a CompiledSchema created by compile().
18
18
  * Used by the CLI to discover schemas in source files.
19
+ *
20
+ * Like {@link isZodSchema}, this probes values the caller does not control —
21
+ * every export of every candidate file — and `COMPILED_MARKER in value` fires a
22
+ * Proxy's `has` trap. A trap that throws (an ORM model, a strict test double)
23
+ * must not fail the build over an export that was never a candidate.
19
24
  */
20
25
  declare function isCompiledSchema(value: unknown): value is CompiledSchema<unknown>;
21
26
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"compile.d.ts","names":[],"sources":["../../src/core/compile.ts"],"mappings":";;;;;;;;;;;;;;iBA+CgB,QAAQ,UAAU,SAAS,WAAW,IAAI,IAAI,eAAe,OAAO;;;;;iBAYpE,iBAAiB,iBAAiB,SAAS"}
1
+ {"version":3,"file":"compile.d.ts","names":[],"sources":["../../src/core/compile.ts"],"mappings":";;;;;;;;;;;;;;iBA+CgB,QAAQ,UAAU,SAAS,WAAW,IAAI,IAAI,eAAe,OAAO;;;;;;;;;;iBAiBpE,iBAAiB,iBAAiB,SAAS"}
@@ -47,9 +47,19 @@ function compile(zodSchema) {
47
47
  /**
48
48
  * Check if a value is a CompiledSchema created by compile().
49
49
  * Used by the CLI to discover schemas in source files.
50
+ *
51
+ * Like {@link isZodSchema}, this probes values the caller does not control —
52
+ * every export of every candidate file — and `COMPILED_MARKER in value` fires a
53
+ * Proxy's `has` trap. A trap that throws (an ORM model, a strict test double)
54
+ * must not fail the build over an export that was never a candidate.
50
55
  */
51
56
  function isCompiledSchema(value) {
52
- return typeof value === "object" && value !== null && COMPILED_MARKER in value;
57
+ if (typeof value !== "object" || value === null) return false;
58
+ try {
59
+ return COMPILED_MARKER in value;
60
+ } catch {
61
+ return false;
62
+ }
53
63
  }
54
64
  //#endregion
55
65
  export { compile, isCompiledSchema };
@@ -1 +1 @@
1
- {"version":3,"file":"compile.js","names":[],"sources":["../../src/core/compile.ts"],"sourcesContent":["import type { output, ZodType } from \"zod\";\nimport type { CompiledSchema } from \"./types.js\";\n\nconst COMPILED_MARKER = Symbol.for(\"zod-compiler:compiled\");\n\n/**\n * Dev-time fallback: returns the ORIGINAL schema object, augmented with a\n * non-enumerable `schema` self-reference (CLI emitter discovery reads\n * `(__src_X as any).schema`) and the compiled marker.\n *\n * Identity preservation is load-bearing: zod v4 keys toJSONSchema's\n * processing context and globalRegistry/.meta() on schema object identity —\n * an Object.create facade crashes toJSONSchema the moment the compiled\n * schema is composed into another schema, and silently loses .meta()\n * metadata. parse/safeParse/... remain the schema's own zod methods.\n */\nfunction createFallback<T>(zodSchema: unknown): CompiledSchema<T> {\n const facade = zodSchema as CompiledSchema<T>;\n if (!(\"schema\" in (facade as object))) {\n Object.defineProperty(facade, \"schema\", { value: zodSchema, enumerable: false });\n }\n // Dev-time `.is`: delegates to zod's own safeParse so the boolean guard works\n // before `zod-compiler generate` runs. The build replaces this with the\n // compiled fast-check (zero-allocation) via __zcMkv. Non-enumerable to keep\n // the augmentation invisible on the identity-preserved schema object.\n if (!(\"is\" in (facade as object))) {\n Object.defineProperty(facade, \"is\", {\n value: (input: unknown): boolean => facade.safeParse(input).success,\n enumerable: false,\n configurable: true,\n writable: true,\n });\n }\n return facade;\n}\n\n/**\n * Compile a Zod schema into an optimized validator.\n *\n * At dev-time, falls back to Zod's runtime validation: the schema itself is\n * returned (identity-preserving for toJSONSchema/.meta()), tagged with the\n * compiled marker so build-time discovery can find it.\n * After `npx zod-compiler generate`, import from the `.compiled.ts` file instead.\n *\n * The return type is `T & CompiledSchema<output<T>>`, preserving the original\n * Zod schema type for compatibility with libraries like `@hono/zod-validator`.\n */\nexport function compile<T extends ZodType>(zodSchema: T): T & CompiledSchema<output<T>> {\n const result = createFallback<output<T>>(zodSchema);\n if (!(COMPILED_MARKER in (result as object))) {\n Object.defineProperty(result, COMPILED_MARKER, { value: true, enumerable: false });\n }\n return result as T & CompiledSchema<output<T>>;\n}\n\n/**\n * Check if a value is a CompiledSchema created by compile().\n * Used by the CLI to discover schemas in source files.\n */\nexport function isCompiledSchema(value: unknown): value is CompiledSchema<unknown> {\n return typeof value === \"object\" && value !== null && COMPILED_MARKER in value;\n}\n"],"mappings":";AAGA,MAAM,kBAAkB,OAAO,IAAI,uBAAuB;;;;;;;;;;;;AAa1D,SAAS,eAAkB,WAAuC;CAChE,MAAM,SAAS;CACf,IAAI,EAAE,YAAa,SACjB,OAAO,eAAe,QAAQ,UAAU;EAAE,OAAO;EAAW,YAAY;CAAM,CAAC;CAMjF,IAAI,EAAE,QAAS,SACb,OAAO,eAAe,QAAQ,MAAM;EAClC,QAAQ,UAA4B,OAAO,UAAU,KAAK,CAAC,CAAC;EAC5D,YAAY;EACZ,cAAc;EACd,UAAU;CACZ,CAAC;CAEH,OAAO;AACT;;;;;;;;;;;;AAaA,SAAgB,QAA2B,WAA6C;CACtF,MAAM,SAAS,eAA0B,SAAS;CAClD,IAAI,EAAE,mBAAoB,SACxB,OAAO,eAAe,QAAQ,iBAAiB;EAAE,OAAO;EAAM,YAAY;CAAM,CAAC;CAEnF,OAAO;AACT;;;;;AAMA,SAAgB,iBAAiB,OAAkD;CACjF,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,mBAAmB;AAC3E"}
1
+ {"version":3,"file":"compile.js","names":[],"sources":["../../src/core/compile.ts"],"sourcesContent":["import type { output, ZodType } from \"zod\";\nimport type { CompiledSchema } from \"./types.js\";\n\nconst COMPILED_MARKER = Symbol.for(\"zod-compiler:compiled\");\n\n/**\n * Dev-time fallback: returns the ORIGINAL schema object, augmented with a\n * non-enumerable `schema` self-reference (CLI emitter discovery reads\n * `(__src_X as any).schema`) and the compiled marker.\n *\n * Identity preservation is load-bearing: zod v4 keys toJSONSchema's\n * processing context and globalRegistry/.meta() on schema object identity —\n * an Object.create facade crashes toJSONSchema the moment the compiled\n * schema is composed into another schema, and silently loses .meta()\n * metadata. parse/safeParse/... remain the schema's own zod methods.\n */\nfunction createFallback<T>(zodSchema: unknown): CompiledSchema<T> {\n const facade = zodSchema as CompiledSchema<T>;\n if (!(\"schema\" in (facade as object))) {\n Object.defineProperty(facade, \"schema\", { value: zodSchema, enumerable: false });\n }\n // Dev-time `.is`: delegates to zod's own safeParse so the boolean guard works\n // before `zod-compiler generate` runs. The build replaces this with the\n // compiled fast-check (zero-allocation) via __zcMkv. Non-enumerable to keep\n // the augmentation invisible on the identity-preserved schema object.\n if (!(\"is\" in (facade as object))) {\n Object.defineProperty(facade, \"is\", {\n value: (input: unknown): boolean => facade.safeParse(input).success,\n enumerable: false,\n configurable: true,\n writable: true,\n });\n }\n return facade;\n}\n\n/**\n * Compile a Zod schema into an optimized validator.\n *\n * At dev-time, falls back to Zod's runtime validation: the schema itself is\n * returned (identity-preserving for toJSONSchema/.meta()), tagged with the\n * compiled marker so build-time discovery can find it.\n * After `npx zod-compiler generate`, import from the `.compiled.ts` file instead.\n *\n * The return type is `T & CompiledSchema<output<T>>`, preserving the original\n * Zod schema type for compatibility with libraries like `@hono/zod-validator`.\n */\nexport function compile<T extends ZodType>(zodSchema: T): T & CompiledSchema<output<T>> {\n const result = createFallback<output<T>>(zodSchema);\n if (!(COMPILED_MARKER in (result as object))) {\n Object.defineProperty(result, COMPILED_MARKER, { value: true, enumerable: false });\n }\n return result as T & CompiledSchema<output<T>>;\n}\n\n/**\n * Check if a value is a CompiledSchema created by compile().\n * Used by the CLI to discover schemas in source files.\n *\n * Like {@link isZodSchema}, this probes values the caller does not control —\n * every export of every candidate file — and `COMPILED_MARKER in value` fires a\n * Proxy's `has` trap. A trap that throws (an ORM model, a strict test double)\n * must not fail the build over an export that was never a candidate.\n */\nexport function isCompiledSchema(value: unknown): value is CompiledSchema<unknown> {\n if (typeof value !== \"object\" || value === null) return false;\n try {\n return COMPILED_MARKER in value;\n } catch {\n return false;\n }\n}\n"],"mappings":";AAGA,MAAM,kBAAkB,OAAO,IAAI,uBAAuB;;;;;;;;;;;;AAa1D,SAAS,eAAkB,WAAuC;CAChE,MAAM,SAAS;CACf,IAAI,EAAE,YAAa,SACjB,OAAO,eAAe,QAAQ,UAAU;EAAE,OAAO;EAAW,YAAY;CAAM,CAAC;CAMjF,IAAI,EAAE,QAAS,SACb,OAAO,eAAe,QAAQ,MAAM;EAClC,QAAQ,UAA4B,OAAO,UAAU,KAAK,CAAC,CAAC;EAC5D,YAAY;EACZ,cAAc;EACd,UAAU;CACZ,CAAC;CAEH,OAAO;AACT;;;;;;;;;;;;AAaA,SAAgB,QAA2B,WAA6C;CACtF,MAAM,SAAS,eAA0B,SAAS;CAClD,IAAI,EAAE,mBAAoB,SACxB,OAAO,eAAe,QAAQ,iBAAiB;EAAE,OAAO;EAAM,YAAY;CAAM,CAAC;CAEnF,OAAO;AACT;;;;;;;;;;AAWA,SAAgB,iBAAiB,OAAkD;CACjF,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;CACxD,IAAI;EACF,OAAO,mBAAmB;CAC5B,QAAQ;EACN,OAAO;CACT;AACF"}
@@ -14,6 +14,22 @@
14
14
  * a promise where zod's synchronous parse raises $ZodAsyncError.
15
15
  */
16
16
  declare function isReferenceablePredicate(fn: unknown): boolean;
17
+ /**
18
+ * Does this callback DEMONSTRABLY observe zod's second (parse-context) argument?
19
+ *
20
+ * Only a positive answer is trustworthy. Zod calls `transform(value, payload)`
21
+ * while every compiled route passes the value alone, so a callback that reads
22
+ * the second argument must be left to zod — but a callback whose signature
23
+ * cannot be read (a native like `Number`, a bound function) is NOT thereby
24
+ * suspect, and refusing those costs a very common idiom its compiled path for
25
+ * no correctness gain: `.transform(Number)` delegating measured SLOWER than not
26
+ * compiling at all. So this reports only what the parsed parameter list proves.
27
+ *
28
+ * `fn.length` cannot answer this at all: it stops counting at the first default
29
+ * and ignores a rest element, so `(...args) => args.length` reports 0 and
30
+ * `(v, ctx = null) => …` reports 1.
31
+ */
32
+ declare function observesSecondArgument(fn: unknown): boolean;
17
33
  /**
18
34
  * Can a callback be invoked with the value alone without hiding Zod's second
19
35
  * parse-context argument? `fn.length` is insufficient here: default and rest
@@ -50,5 +66,5 @@ declare function isPayloadCheck(check: {
50
66
  */
51
67
  declare function tryCompileEffect(fn: unknown): string | undefined;
52
68
  //#endregion
53
- export { isContextFreeUnaryCallback, isPayloadCheck, isReferenceablePredicate, tryCompileEffect };
69
+ export { isContextFreeUnaryCallback, isPayloadCheck, isReferenceablePredicate, observesSecondArgument, tryCompileEffect };
54
70
  //# sourceMappingURL=effects.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"effects.d.ts","names":[],"sources":["../../../src/core/extract/effects.ts"],"mappings":";;;;;;;;;;;;;;;iBAwDgB,yBAAyB;;;;;;iBAczB,2BAA2B;;;;;;;;;;;;;;iBAgC3B,eAAe;EAC7B;IAAS;IAAiB;MAAQ;MAAc;;IAAkB,SAAS;;;;;;;;iBAe7D,iBAAiB"}
1
+ {"version":3,"file":"effects.d.ts","names":[],"sources":["../../../src/core/extract/effects.ts"],"mappings":";;;;;;;;;;;;;;;iBAwDgB,yBAAyB;;;;;;;;;;;;;;;;iBA8IzB,uBAAuB;;;;;;iBAWvB,2BAA2B;;;;;;;;;;;;;;iBA8B3B,eAAe;EAC7B;IAAS;IAAiB;MAAQ;MAAc;;IAAkB,SAAS;;;;;;;;iBAe7D,iBAAiB"}
@@ -52,27 +52,148 @@ function isReferenceablePredicate(fn) {
52
52
  return kind !== "AsyncFunction" && kind !== "GeneratorFunction" && kind !== "AsyncGeneratorFunction";
53
53
  }
54
54
  /**
55
+ * Not every callable stringifies to an expression, and acorn parses what it can
56
+ * rather than refusing. A method shorthand lifted off an object literal —
57
+ * `normalize(v) { … }` — parses as the CALL `normalize(v)` and stops at the
58
+ * brace; a getter's source yields a bare Identifier; a class yields a
59
+ * ClassExpression. None of those carry `params`, so every arity guard below
60
+ * passes vacuously and `tryCompileEffect` hands back the whole source including
61
+ * the trailing block, which lands in generated code as
62
+ * `var __ef_2=(normalize(v) { … });` — a SyntaxError that makes the emitted
63
+ * module unparseable while the CLI still reports success.
64
+ *
65
+ * Only an arrow or a function expression is safe to treat as an inlineable
66
+ * callback. Rejecting is the conservative answer rather than merely the safe
67
+ * one: with no parsed params there is nothing to prove the callback ignores
68
+ * zod's second (parse-context) argument, so a refine/transform degrades to a
69
+ * call through `__rf[N]` and a preprocess degrades further, to full zod
70
+ * delegation. Every shape above keeps zod's own behaviour either way — a class
71
+ * still throws on a call-without-new exactly as it does under zod.
72
+ *
73
+ * The parse must also have consumed the whole source. Nothing here can prove
74
+ * `toString()` returned real source text, and a partial parse would re-open
75
+ * this same bug class by re-emitting the unconsumed tail.
76
+ */
77
+ function asFunctionNode(ast, source) {
78
+ if (!(ast.type === "ArrowFunctionExpression" || ast.type === "FunctionExpression")) return null;
79
+ return source.slice(ast.end).trim() === "" ? ast : null;
80
+ }
81
+ /**
82
+ * Recover a callback's parameter list from its source, or null when the source
83
+ * does not reveal one. An arrow or function expression parses directly; a method
84
+ * shorthand lifted off an object literal (`normalize(v) { … }`) is not an
85
+ * expression on its own but becomes one wrapped back in the braces it came from.
86
+ * A native or bound function reveals nothing — `[native code]` has no parameter
87
+ * list to read, and skipping it before either parse keeps two thrown acorn
88
+ * exceptions off the hot path for `.transform(Number)` and friends.
89
+ */
90
+ function recoverSignature(source) {
91
+ if (source.includes("[native code]")) return null;
92
+ try {
93
+ const fnNode = asFunctionNode(parseExpressionAt(source, 0, {
94
+ ecmaVersion: "latest",
95
+ sourceType: "module"
96
+ }), source);
97
+ if (fnNode !== null) return fnNode;
98
+ } catch {}
99
+ try {
100
+ const wrapped = `({${source}})`;
101
+ const ast = parseExpressionAt(wrapped, 0, {
102
+ ecmaVersion: "latest",
103
+ sourceType: "module"
104
+ });
105
+ if (wrapped.slice(ast.end).trim() !== ")") return null;
106
+ const properties = ast.properties;
107
+ if (properties?.length !== 1) return null;
108
+ const value = properties[0]?.value;
109
+ return value !== void 0 && value.type === "FunctionExpression" ? value : null;
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+ /**
115
+ * Does a body reference ITS OWN `arguments`?
116
+ *
117
+ * A nested non-arrow function gets a fresh `arguments` binding, so its use of
118
+ * the name says nothing about the callback being classified — descending into
119
+ * one would fail a perfectly safe callback and cost it its compiled path. An
120
+ * arrow has no `arguments` of its own and so is walked.
121
+ */
122
+ function referencesOwnArguments(node) {
123
+ if (node.type === "Identifier") return node.name === "arguments";
124
+ if (node.type === "FunctionExpression" || node.type === "FunctionDeclaration") return false;
125
+ if (node.type === "MemberExpression") {
126
+ const member = node;
127
+ if (referencesOwnArguments(member.object)) return true;
128
+ return member.computed && referencesOwnArguments(member.property);
129
+ }
130
+ if (node.type === "Property") {
131
+ const property = node;
132
+ if (property.computed && referencesOwnArguments(property.key)) return true;
133
+ return referencesOwnArguments(property.value);
134
+ }
135
+ for (const key of Object.keys(node)) {
136
+ if (key === "type" || key === "start" || key === "end") continue;
137
+ const child = node[key];
138
+ if (!child || typeof child !== "object") continue;
139
+ const children = Array.isArray(child) ? child : [child];
140
+ for (const item of children) if (item && typeof item === "object" && "type" in item) {
141
+ if (referencesOwnArguments(item)) return true;
142
+ }
143
+ }
144
+ return false;
145
+ }
146
+ /**
147
+ * The three ways a parsed signature can betray zod's second argument. Shared so
148
+ * that {@link observesSecondArgument} and {@link isContextFreeUnaryCallback},
149
+ * which are exact inverses of it, cannot drift apart.
150
+ */
151
+ function signatureObservesSecondArgument(fnNode) {
152
+ const params = fnNode.params ?? [];
153
+ if (params.length > 1) return true;
154
+ if (params.some((param) => param.type === "RestElement")) return true;
155
+ return fnNode.body !== void 0 && referencesOwnArguments(fnNode.body);
156
+ }
157
+ /**
158
+ * Does this callback DEMONSTRABLY observe zod's second (parse-context) argument?
159
+ *
160
+ * Only a positive answer is trustworthy. Zod calls `transform(value, payload)`
161
+ * while every compiled route passes the value alone, so a callback that reads
162
+ * the second argument must be left to zod — but a callback whose signature
163
+ * cannot be read (a native like `Number`, a bound function) is NOT thereby
164
+ * suspect, and refusing those costs a very common idiom its compiled path for
165
+ * no correctness gain: `.transform(Number)` delegating measured SLOWER than not
166
+ * compiling at all. So this reports only what the parsed parameter list proves.
167
+ *
168
+ * `fn.length` cannot answer this at all: it stops counting at the first default
169
+ * and ignores a rest element, so `(...args) => args.length` reports 0 and
170
+ * `(v, ctx = null) => …` reports 1.
171
+ */
172
+ function observesSecondArgument(fn) {
173
+ if (typeof fn !== "function") return false;
174
+ const fnNode = recoverSignature(fn.toString());
175
+ return fnNode !== null && signatureObservesSecondArgument(fnNode);
176
+ }
177
+ /**
55
178
  * Can a callback be invoked with the value alone without hiding Zod's second
56
179
  * parse-context argument? `fn.length` is insufficient here: default and rest
57
180
  * parameters can observe that argument while still reporting length 0 or 1.
58
181
  */
59
182
  function isContextFreeUnaryCallback(fn) {
60
183
  if (!isReferenceablePredicate(fn)) return false;
184
+ const source = fn.toString();
61
185
  let ast;
62
186
  try {
63
- ast = parseExpressionAt(fn.toString(), 0, {
187
+ ast = parseExpressionAt(source, 0, {
64
188
  ecmaVersion: "latest",
65
189
  sourceType: "module"
66
190
  });
67
191
  } catch {
68
192
  return false;
69
193
  }
70
- const fnNode = ast;
71
- if ((fnNode.params?.length ?? 0) > 1) return false;
72
- if (fnNode.params?.some((param) => param.type === "RestElement")) return false;
73
- const refs = /* @__PURE__ */ new Set();
74
- if (fnNode.body) collectIdentifierRefs(fnNode.body, refs);
75
- return !refs.has("arguments");
194
+ const fnNode = asFunctionNode(ast, source);
195
+ if (fnNode === null) return false;
196
+ return !signatureObservesSecondArgument(fnNode);
76
197
  }
77
198
  /**
78
199
  * A `superRefine`: a `custom` check whose callback takes zod's PAYLOAD rather
@@ -108,10 +229,11 @@ function tryCompileEffect(fn) {
108
229
  } catch {
109
230
  return;
110
231
  }
232
+ const fnNode = asFunctionNode(ast, source);
233
+ if (fnNode === null) return void 0;
111
234
  if ("async" in ast && ast.async) return void 0;
112
235
  if ("generator" in ast && ast.generator) return void 0;
113
236
  const params = /* @__PURE__ */ new Set();
114
- const fnNode = ast;
115
237
  if (fn.length >= 2) return void 0;
116
238
  if (fnNode.params) for (const param of fnNode.params) collectBindingNames(param, params);
117
239
  const locals = /* @__PURE__ */ new Set();
@@ -189,6 +311,6 @@ function collectIdentifierRefs(node, refs) {
189
311
  }
190
312
  }
191
313
  //#endregion
192
- export { isContextFreeUnaryCallback, isPayloadCheck, isReferenceablePredicate, tryCompileEffect };
314
+ export { isContextFreeUnaryCallback, isPayloadCheck, isReferenceablePredicate, observesSecondArgument, tryCompileEffect };
193
315
 
194
316
  //# sourceMappingURL=effects.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"effects.js","names":[],"sources":["../../../src/core/extract/effects.ts"],"sourcesContent":["/**\n * Effect compilation utilities.\n * Uses fn.toString() to extract function source text from live Zod schema references,\n * then classifies whether the function can be safely inlined (zero external captures).\n */\n\nimport type { Node } from \"acorn\";\nimport { parseExpressionAt } from \"acorn\";\n\n// Well-known globals that are safe to reference in inlined functions\nconst SAFE_GLOBALS = new Set([\n \"undefined\",\n \"null\",\n \"NaN\",\n \"Infinity\",\n \"Math\",\n \"Number\",\n \"String\",\n \"Boolean\",\n \"Array\",\n \"Object\",\n \"JSON\",\n \"Date\",\n \"RegExp\",\n \"parseInt\",\n \"parseFloat\",\n \"isNaN\",\n \"isFinite\",\n \"encodeURI\",\n \"decodeURI\",\n \"encodeURIComponent\",\n \"decodeURIComponent\",\n \"BigInt\",\n \"Symbol\",\n \"Map\",\n \"Set\",\n \"Error\",\n \"TypeError\",\n \"RangeError\",\n \"SyntaxError\",\n \"ReferenceError\",\n \"Promise\",\n \"globalThis\",\n \"true\",\n \"false\",\n]);\n\n/**\n * Can this refine predicate be CALLED by reference from generated code?\n *\n * Weaker than {@link tryCompileEffect}: the callback keeps its own closure, so\n * captures are fine — only shapes whose semantics the generated call could not\n * reproduce are rejected. A second parameter means the zod `ctx` protocol\n * (superRefine-style issue collection), and an async/generator function returns\n * a promise where zod's synchronous parse raises $ZodAsyncError.\n */\nexport function isReferenceablePredicate(fn: unknown): boolean {\n if (typeof fn !== \"function\") return false;\n if (fn.length >= 2) return false;\n const kind = fn.constructor?.name;\n return (\n kind !== \"AsyncFunction\" && kind !== \"GeneratorFunction\" && kind !== \"AsyncGeneratorFunction\"\n );\n}\n\n/**\n * Can a callback be invoked with the value alone without hiding Zod's second\n * parse-context argument? `fn.length` is insufficient here: default and rest\n * parameters can observe that argument while still reporting length 0 or 1.\n */\nexport function isContextFreeUnaryCallback(fn: unknown): boolean {\n if (!isReferenceablePredicate(fn)) return false;\n let ast: Node;\n try {\n ast = parseExpressionAt((fn as Function).toString(), 0, {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n });\n } catch {\n return false;\n }\n const fnNode = ast as { params?: Node[]; body?: Node };\n if ((fnNode.params?.length ?? 0) > 1) return false;\n if (fnNode.params?.some((param) => param.type === \"RestElement\")) return false;\n const refs = new Set<string>();\n if (fnNode.body) collectIdentifierRefs(fnNode.body, refs);\n return !refs.has(\"arguments\");\n}\n\n/**\n * A `superRefine`: a `custom` check whose callback takes zod's PAYLOAD rather\n * than the value, collecting issues instead of returning a verdict. Zod stores\n * it on the check instance (`_zod.check`) rather than `def.fn`.\n *\n * Narrowed to zod's OWN wrapper, which is what `_superRefine` builds and the\n * only thing whose payload handling is modelled here: it installs `addIssue`,\n * so every issue the user adds has been through `util.issue`. The trait marks a\n * genuine `$ZodCheck` instance and so excludes a raw `.check(fn)` — shape-\n * identical, but there the callback IS the user's, holding the payload\n * unmediated. A `when` predicate is likewise refused: it makes zod run the\n * check conditionally, where generated code runs it always.\n */\nexport function isPayloadCheck(check: {\n _zod?: { check?: unknown; def?: { fn?: unknown; when?: unknown }; traits?: Set<string> };\n}): boolean {\n return (\n check._zod?.def?.fn === undefined &&\n check._zod?.def?.when === undefined &&\n typeof check._zod?.check === \"function\" &&\n check._zod?.traits?.has(\"$ZodCheck\") === true\n );\n}\n\n/**\n * Try to compile a function into an inlineable source string.\n * Returns the function source if it's a zero-capture function (safe to inline),\n * or undefined if it cannot be compiled (async, has captures, or parse failure).\n */\nexport function tryCompileEffect(fn: unknown): string | undefined {\n if (typeof fn !== \"function\") return undefined;\n\n const source = fn.toString();\n\n // Quick reject: native functions\n if (source.includes(\"[native code]\")) return undefined;\n\n let ast: Node;\n try {\n ast = parseExpressionAt(source, 0, {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n });\n } catch {\n // Parse failed — likely TypeScript annotations or unsupported syntax\n return undefined;\n }\n\n // Reject async and generator functions\n if (\"async\" in ast && (ast as { async?: boolean }).async) return undefined;\n if (\"generator\" in ast && (ast as { generator?: boolean }).generator) return undefined;\n\n // Collect parameter names\n const params = new Set<string>();\n const fnNode = ast as {\n type: string;\n params?: Node[];\n body?: Node;\n };\n\n // Reject functions with 2+ required parameters (uses Zod ctx argument).\n // transform(value, ctx) and superRefine(value, ctx) rely on ctx for\n // issue collection, which cannot be reproduced in compiled output.\n // fn.length counts parameters before the first default, so (v, base=10)\n // has length 1 (allowed) while (v, ctx) has length 2 (rejected).\n if ((fn as { length: number }).length >= 2) return undefined;\n\n if (fnNode.params) {\n for (const param of fnNode.params) {\n collectBindingNames(param, params);\n }\n }\n\n // Collect local variable declarations in the body\n const locals = new Set<string>();\n if (fnNode.body && fnNode.body.type === \"BlockStatement\") {\n collectLocals(fnNode.body, locals);\n }\n\n // Collect all identifier references in the body\n const refs = new Set<string>();\n if (fnNode.body) {\n collectIdentifierRefs(fnNode.body, refs);\n }\n\n // Check for external captures\n for (const ref of refs) {\n if (!params.has(ref) && !locals.has(ref) && !SAFE_GLOBALS.has(ref)) {\n return undefined; // Has external capture\n }\n }\n\n return source;\n}\n\n/** Collect binding names from a parameter pattern (handles destructuring). */\nfunction collectBindingNames(node: Node, names: Set<string>): void {\n switch (node.type) {\n case \"Identifier\":\n names.add((node as Node & { name: string }).name);\n break;\n case \"AssignmentPattern\":\n collectBindingNames((node as Node & { left: Node }).left, names);\n break;\n case \"ObjectPattern\":\n for (const prop of (node as Node & { properties: Node[] }).properties) {\n if (prop.type === \"RestElement\") {\n collectBindingNames((prop as Node & { argument: Node }).argument, names);\n } else {\n collectBindingNames((prop as Node & { value: Node }).value, names);\n }\n }\n break;\n case \"ArrayPattern\":\n for (const elem of (node as Node & { elements: (Node | null)[] }).elements) {\n if (elem) collectBindingNames(elem, names);\n }\n break;\n case \"RestElement\":\n collectBindingNames((node as Node & { argument: Node }).argument, names);\n break;\n }\n}\n\n/** Collect locally declared variable names from a block body. */\nfunction collectLocals(body: Node, locals: Set<string>): void {\n const block = body as Node & { body: Node[] };\n if (!block.body) return;\n for (const stmt of block.body) {\n if (stmt.type === \"VariableDeclaration\") {\n for (const decl of (stmt as Node & { declarations: Node[] }).declarations) {\n const id = (decl as Node & { id: Node }).id;\n collectBindingNames(id, locals);\n }\n }\n }\n}\n\n/** Collect all identifier references in an AST subtree. */\nfunction collectIdentifierRefs(node: Node, refs: Set<string>): void {\n if (node.type === \"Identifier\") {\n refs.add((node as Node & { name: string }).name);\n return;\n }\n\n // `this` in arrow functions captures from enclosing scope — treat as external capture\n if (node.type === \"ThisExpression\") {\n refs.add(\"this\");\n return;\n }\n\n // Skip property access identifiers (obj.prop — \"prop\" is not a reference)\n if (node.type === \"MemberExpression\") {\n const member = node as Node & { object: Node; property: Node; computed: boolean };\n collectIdentifierRefs(member.object, refs);\n if (member.computed) {\n collectIdentifierRefs(member.property, refs);\n }\n return;\n }\n\n // Object literal properties: skip non-computed keys, only walk values\n if (node.type === \"Property\") {\n const prop = node as Node & { key: Node; value: Node; computed: boolean };\n if (prop.computed) {\n collectIdentifierRefs(prop.key, refs);\n }\n collectIdentifierRefs(prop.value, refs);\n return;\n }\n\n // Skip binding positions in variable declarations, function params, etc.\n if (node.type === \"VariableDeclarator\") {\n const decl = node as Node & { init: Node | null };\n if (decl.init) collectIdentifierRefs(decl.init, refs);\n return;\n }\n\n // Recursively walk all child nodes\n for (const key of Object.keys(node)) {\n if (key === \"type\" || key === \"start\" || key === \"end\") continue;\n const child = (node as unknown as Record<string, unknown>)[key];\n if (child && typeof child === \"object\") {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n collectIdentifierRefs(item as Node, refs);\n }\n }\n } else if (\"type\" in child) {\n collectIdentifierRefs(child as Node, refs);\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"effects.js","names":[],"sources":["../../../src/core/extract/effects.ts"],"sourcesContent":["/**\n * Effect compilation utilities.\n * Uses fn.toString() to extract function source text from live Zod schema references,\n * then classifies whether the function can be safely inlined (zero external captures).\n */\n\nimport type { Node } from \"acorn\";\nimport { parseExpressionAt } from \"acorn\";\n\n// Well-known globals that are safe to reference in inlined functions\nconst SAFE_GLOBALS = new Set([\n \"undefined\",\n \"null\",\n \"NaN\",\n \"Infinity\",\n \"Math\",\n \"Number\",\n \"String\",\n \"Boolean\",\n \"Array\",\n \"Object\",\n \"JSON\",\n \"Date\",\n \"RegExp\",\n \"parseInt\",\n \"parseFloat\",\n \"isNaN\",\n \"isFinite\",\n \"encodeURI\",\n \"decodeURI\",\n \"encodeURIComponent\",\n \"decodeURIComponent\",\n \"BigInt\",\n \"Symbol\",\n \"Map\",\n \"Set\",\n \"Error\",\n \"TypeError\",\n \"RangeError\",\n \"SyntaxError\",\n \"ReferenceError\",\n \"Promise\",\n \"globalThis\",\n \"true\",\n \"false\",\n]);\n\n/**\n * Can this refine predicate be CALLED by reference from generated code?\n *\n * Weaker than {@link tryCompileEffect}: the callback keeps its own closure, so\n * captures are fine — only shapes whose semantics the generated call could not\n * reproduce are rejected. A second parameter means the zod `ctx` protocol\n * (superRefine-style issue collection), and an async/generator function returns\n * a promise where zod's synchronous parse raises $ZodAsyncError.\n */\nexport function isReferenceablePredicate(fn: unknown): boolean {\n if (typeof fn !== \"function\") return false;\n if (fn.length >= 2) return false;\n const kind = fn.constructor?.name;\n return (\n kind !== \"AsyncFunction\" && kind !== \"GeneratorFunction\" && kind !== \"AsyncGeneratorFunction\"\n );\n}\n\n/** A parsed callback: the node types whose SOURCE TEXT is a usable expression. */\ntype FunctionNode = Node & { params?: Node[]; body?: Node };\n\n/**\n * Not every callable stringifies to an expression, and acorn parses what it can\n * rather than refusing. A method shorthand lifted off an object literal —\n * `normalize(v) { … }` — parses as the CALL `normalize(v)` and stops at the\n * brace; a getter's source yields a bare Identifier; a class yields a\n * ClassExpression. None of those carry `params`, so every arity guard below\n * passes vacuously and `tryCompileEffect` hands back the whole source including\n * the trailing block, which lands in generated code as\n * `var __ef_2=(normalize(v) { … });` — a SyntaxError that makes the emitted\n * module unparseable while the CLI still reports success.\n *\n * Only an arrow or a function expression is safe to treat as an inlineable\n * callback. Rejecting is the conservative answer rather than merely the safe\n * one: with no parsed params there is nothing to prove the callback ignores\n * zod's second (parse-context) argument, so a refine/transform degrades to a\n * call through `__rf[N]` and a preprocess degrades further, to full zod\n * delegation. Every shape above keeps zod's own behaviour either way — a class\n * still throws on a call-without-new exactly as it does under zod.\n *\n * The parse must also have consumed the whole source. Nothing here can prove\n * `toString()` returned real source text, and a partial parse would re-open\n * this same bug class by re-emitting the unconsumed tail.\n */\nfunction asFunctionNode(ast: Node, source: string): FunctionNode | null {\n const isFunction = ast.type === \"ArrowFunctionExpression\" || ast.type === \"FunctionExpression\";\n if (!isFunction) return null;\n return source.slice(ast.end).trim() === \"\" ? (ast as FunctionNode) : null;\n}\n\n/**\n * Recover a callback's parameter list from its source, or null when the source\n * does not reveal one. An arrow or function expression parses directly; a method\n * shorthand lifted off an object literal (`normalize(v) { … }`) is not an\n * expression on its own but becomes one wrapped back in the braces it came from.\n * A native or bound function reveals nothing — `[native code]` has no parameter\n * list to read, and skipping it before either parse keeps two thrown acorn\n * exceptions off the hot path for `.transform(Number)` and friends.\n */\nfunction recoverSignature(source: string): FunctionNode | null {\n if (source.includes(\"[native code]\")) return null;\n try {\n const ast = parseExpressionAt(source, 0, { ecmaVersion: \"latest\", sourceType: \"module\" });\n const fnNode = asFunctionNode(ast, source);\n if (fnNode !== null) return fnNode;\n } catch {\n // Not an expression on its own; try the object-literal form below.\n }\n try {\n const wrapped = `({${source}})`;\n const ast = parseExpressionAt(wrapped, 0, { ecmaVersion: \"latest\", sourceType: \"module\" });\n // Same full-consumption rule asFunctionNode applies: a source that closes\n // the wrapper early and reopens it would otherwise yield the WRONG method's\n // parameter list. Acorn returns the inner object expression, so the only\n // text that may remain is the wrapper's own closing paren.\n if (wrapped.slice(ast.end).trim() !== \")\") return null;\n const properties = (ast as Node & { properties?: (Node & { value?: Node })[] }).properties;\n if (properties?.length !== 1) return null;\n const value = properties[0]?.value;\n return value !== undefined && value.type === \"FunctionExpression\"\n ? (value as FunctionNode)\n : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Does a body reference ITS OWN `arguments`?\n *\n * A nested non-arrow function gets a fresh `arguments` binding, so its use of\n * the name says nothing about the callback being classified — descending into\n * one would fail a perfectly safe callback and cost it its compiled path. An\n * arrow has no `arguments` of its own and so is walked.\n */\nfunction referencesOwnArguments(node: Node): boolean {\n if (node.type === \"Identifier\") return (node as Node & { name: string }).name === \"arguments\";\n if (node.type === \"FunctionExpression\" || node.type === \"FunctionDeclaration\") return false;\n // `obj.arguments` and `{ arguments: v }` name a property, not the binding —\n // the same positions collectIdentifierRefs skips.\n if (node.type === \"MemberExpression\") {\n const member = node as Node & { object: Node; property: Node; computed: boolean };\n if (referencesOwnArguments(member.object)) return true;\n return member.computed && referencesOwnArguments(member.property);\n }\n if (node.type === \"Property\") {\n const property = node as Node & { key: Node; value: Node; computed: boolean };\n if (property.computed && referencesOwnArguments(property.key)) return true;\n return referencesOwnArguments(property.value);\n }\n for (const key of Object.keys(node)) {\n if (key === \"type\" || key === \"start\" || key === \"end\") continue;\n const child = (node as unknown as Record<string, unknown>)[key];\n if (!child || typeof child !== \"object\") continue;\n const children = Array.isArray(child) ? child : [child];\n for (const item of children) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n if (referencesOwnArguments(item as Node)) return true;\n }\n }\n }\n return false;\n}\n\n/**\n * The three ways a parsed signature can betray zod's second argument. Shared so\n * that {@link observesSecondArgument} and {@link isContextFreeUnaryCallback},\n * which are exact inverses of it, cannot drift apart.\n */\nfunction signatureObservesSecondArgument(fnNode: FunctionNode): boolean {\n const params = fnNode.params ?? [];\n if (params.length > 1) return true;\n if (params.some((param) => param.type === \"RestElement\")) return true;\n return fnNode.body !== undefined && referencesOwnArguments(fnNode.body);\n}\n\n/**\n * Does this callback DEMONSTRABLY observe zod's second (parse-context) argument?\n *\n * Only a positive answer is trustworthy. Zod calls `transform(value, payload)`\n * while every compiled route passes the value alone, so a callback that reads\n * the second argument must be left to zod — but a callback whose signature\n * cannot be read (a native like `Number`, a bound function) is NOT thereby\n * suspect, and refusing those costs a very common idiom its compiled path for\n * no correctness gain: `.transform(Number)` delegating measured SLOWER than not\n * compiling at all. So this reports only what the parsed parameter list proves.\n *\n * `fn.length` cannot answer this at all: it stops counting at the first default\n * and ignores a rest element, so `(...args) => args.length` reports 0 and\n * `(v, ctx = null) => …` reports 1.\n */\nexport function observesSecondArgument(fn: unknown): boolean {\n if (typeof fn !== \"function\") return false;\n const fnNode = recoverSignature(fn.toString());\n return fnNode !== null && signatureObservesSecondArgument(fnNode);\n}\n\n/**\n * Can a callback be invoked with the value alone without hiding Zod's second\n * parse-context argument? `fn.length` is insufficient here: default and rest\n * parameters can observe that argument while still reporting length 0 or 1.\n */\nexport function isContextFreeUnaryCallback(fn: unknown): boolean {\n if (!isReferenceablePredicate(fn)) return false;\n const source = (fn as Function).toString();\n let ast: Node;\n try {\n ast = parseExpressionAt(source, 0, {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n });\n } catch {\n return false;\n }\n const fnNode = asFunctionNode(ast, source);\n if (fnNode === null) return false;\n return !signatureObservesSecondArgument(fnNode);\n}\n\n/**\n * A `superRefine`: a `custom` check whose callback takes zod's PAYLOAD rather\n * than the value, collecting issues instead of returning a verdict. Zod stores\n * it on the check instance (`_zod.check`) rather than `def.fn`.\n *\n * Narrowed to zod's OWN wrapper, which is what `_superRefine` builds and the\n * only thing whose payload handling is modelled here: it installs `addIssue`,\n * so every issue the user adds has been through `util.issue`. The trait marks a\n * genuine `$ZodCheck` instance and so excludes a raw `.check(fn)` — shape-\n * identical, but there the callback IS the user's, holding the payload\n * unmediated. A `when` predicate is likewise refused: it makes zod run the\n * check conditionally, where generated code runs it always.\n */\nexport function isPayloadCheck(check: {\n _zod?: { check?: unknown; def?: { fn?: unknown; when?: unknown }; traits?: Set<string> };\n}): boolean {\n return (\n check._zod?.def?.fn === undefined &&\n check._zod?.def?.when === undefined &&\n typeof check._zod?.check === \"function\" &&\n check._zod?.traits?.has(\"$ZodCheck\") === true\n );\n}\n\n/**\n * Try to compile a function into an inlineable source string.\n * Returns the function source if it's a zero-capture function (safe to inline),\n * or undefined if it cannot be compiled (async, has captures, or parse failure).\n */\nexport function tryCompileEffect(fn: unknown): string | undefined {\n if (typeof fn !== \"function\") return undefined;\n\n const source = fn.toString();\n\n // Quick reject: native functions\n if (source.includes(\"[native code]\")) return undefined;\n\n let ast: Node;\n try {\n ast = parseExpressionAt(source, 0, {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n });\n } catch {\n // Parse failed — likely TypeScript annotations or unsupported syntax\n return undefined;\n }\n\n // Only a fully-consumed arrow or function expression can be re-emitted as\n // source; see asFunctionNode for the shapes acorn otherwise accepts.\n const fnNode = asFunctionNode(ast, source);\n if (fnNode === null) return undefined;\n\n // Reject async and generator functions\n if (\"async\" in ast && (ast as { async?: boolean }).async) return undefined;\n if (\"generator\" in ast && (ast as { generator?: boolean }).generator) return undefined;\n\n // Collect parameter names\n const params = new Set<string>();\n\n // Reject functions with 2+ required parameters (uses Zod ctx argument).\n // transform(value, ctx) and superRefine(value, ctx) rely on ctx for\n // issue collection, which cannot be reproduced in compiled output.\n // fn.length counts parameters before the first default, so (v, base=10)\n // has length 1 (allowed) while (v, ctx) has length 2 (rejected).\n if ((fn as { length: number }).length >= 2) return undefined;\n\n if (fnNode.params) {\n for (const param of fnNode.params) {\n collectBindingNames(param, params);\n }\n }\n\n // Collect local variable declarations in the body\n const locals = new Set<string>();\n if (fnNode.body && fnNode.body.type === \"BlockStatement\") {\n collectLocals(fnNode.body, locals);\n }\n\n // Collect all identifier references in the body\n const refs = new Set<string>();\n if (fnNode.body) {\n collectIdentifierRefs(fnNode.body, refs);\n }\n\n // Check for external captures\n for (const ref of refs) {\n if (!params.has(ref) && !locals.has(ref) && !SAFE_GLOBALS.has(ref)) {\n return undefined; // Has external capture\n }\n }\n\n return source;\n}\n\n/** Collect binding names from a parameter pattern (handles destructuring). */\nfunction collectBindingNames(node: Node, names: Set<string>): void {\n switch (node.type) {\n case \"Identifier\":\n names.add((node as Node & { name: string }).name);\n break;\n case \"AssignmentPattern\":\n collectBindingNames((node as Node & { left: Node }).left, names);\n break;\n case \"ObjectPattern\":\n for (const prop of (node as Node & { properties: Node[] }).properties) {\n if (prop.type === \"RestElement\") {\n collectBindingNames((prop as Node & { argument: Node }).argument, names);\n } else {\n collectBindingNames((prop as Node & { value: Node }).value, names);\n }\n }\n break;\n case \"ArrayPattern\":\n for (const elem of (node as Node & { elements: (Node | null)[] }).elements) {\n if (elem) collectBindingNames(elem, names);\n }\n break;\n case \"RestElement\":\n collectBindingNames((node as Node & { argument: Node }).argument, names);\n break;\n }\n}\n\n/** Collect locally declared variable names from a block body. */\nfunction collectLocals(body: Node, locals: Set<string>): void {\n const block = body as Node & { body: Node[] };\n if (!block.body) return;\n for (const stmt of block.body) {\n if (stmt.type === \"VariableDeclaration\") {\n for (const decl of (stmt as Node & { declarations: Node[] }).declarations) {\n const id = (decl as Node & { id: Node }).id;\n collectBindingNames(id, locals);\n }\n }\n }\n}\n\n/** Collect all identifier references in an AST subtree. */\nfunction collectIdentifierRefs(node: Node, refs: Set<string>): void {\n if (node.type === \"Identifier\") {\n refs.add((node as Node & { name: string }).name);\n return;\n }\n\n // `this` in arrow functions captures from enclosing scope — treat as external capture\n if (node.type === \"ThisExpression\") {\n refs.add(\"this\");\n return;\n }\n\n // Skip property access identifiers (obj.prop — \"prop\" is not a reference)\n if (node.type === \"MemberExpression\") {\n const member = node as Node & { object: Node; property: Node; computed: boolean };\n collectIdentifierRefs(member.object, refs);\n if (member.computed) {\n collectIdentifierRefs(member.property, refs);\n }\n return;\n }\n\n // Object literal properties: skip non-computed keys, only walk values\n if (node.type === \"Property\") {\n const prop = node as Node & { key: Node; value: Node; computed: boolean };\n if (prop.computed) {\n collectIdentifierRefs(prop.key, refs);\n }\n collectIdentifierRefs(prop.value, refs);\n return;\n }\n\n // Skip binding positions in variable declarations, function params, etc.\n if (node.type === \"VariableDeclarator\") {\n const decl = node as Node & { init: Node | null };\n if (decl.init) collectIdentifierRefs(decl.init, refs);\n return;\n }\n\n // Recursively walk all child nodes\n for (const key of Object.keys(node)) {\n if (key === \"type\" || key === \"start\" || key === \"end\") continue;\n const child = (node as unknown as Record<string, unknown>)[key];\n if (child && typeof child === \"object\") {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n collectIdentifierRefs(item as Node, refs);\n }\n }\n } else if (\"type\" in child) {\n collectIdentifierRefs(child as Node, refs);\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"pipe.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/pipe.ts"],"mappings":";;;iBASgB,YAAY,KAAK,QAAQ,KAAK,mBAAmB"}
1
+ {"version":3,"file":"pipe.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/pipe.ts"],"mappings":";;;iBAUgB,YAAY,KAAK,QAAQ,KAAK,mBAAmB"}
@@ -1,4 +1,4 @@
1
- import { isContextFreeUnaryCallback, isReferenceablePredicate, tryCompileEffect } from "../effects.js";
1
+ import { isContextFreeUnaryCallback, isReferenceablePredicate, observesSecondArgument, tryCompileEffect } from "../effects.js";
2
2
  import { extractStringBool, isStringBoolCodec } from "./string-bool.js";
3
3
  //#region src/core/extract/extractors/pipe.ts
4
4
  function extractPipe(def, ctx) {
@@ -33,25 +33,27 @@ function extractPipe(def, ctx) {
33
33
  }
34
34
  const outDef = def.out?._zod?.def;
35
35
  if (outDef && outDef.type === "transform") {
36
- const source = tryCompileEffect(outDef.transform);
37
- if (source) return {
38
- type: "effect",
39
- effectKind: "transform",
40
- source,
41
- inner: ctx.visit(def.in, "._zod.def.in")
42
- };
43
- if (ctx.refs && isReferenceablePredicate(outDef.transform)) {
44
- const refIndex = ctx.refs.length;
45
- ctx.refs.push({
46
- schema: outDef.transform,
47
- accessPath: `${ctx.path}._zod.def.out._zod.def.transform`
48
- });
49
- return {
36
+ if (!observesSecondArgument(outDef.transform)) {
37
+ const source = tryCompileEffect(outDef.transform);
38
+ if (source) return {
50
39
  type: "effect",
51
40
  effectKind: "transform",
52
- refIndex,
41
+ source,
53
42
  inner: ctx.visit(def.in, "._zod.def.in")
54
43
  };
44
+ if (ctx.refs && isReferenceablePredicate(outDef.transform)) {
45
+ const refIndex = ctx.refs.length;
46
+ ctx.refs.push({
47
+ schema: outDef.transform,
48
+ accessPath: `${ctx.path}._zod.def.out._zod.def.transform`
49
+ });
50
+ return {
51
+ type: "effect",
52
+ effectKind: "transform",
53
+ refIndex,
54
+ inner: ctx.visit(def.in, "._zod.def.in")
55
+ };
56
+ }
55
57
  }
56
58
  return ctx.fallback("transform");
57
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"pipe.js","names":[],"sources":["../../../../src/core/extract/extractors/pipe.ts"],"sourcesContent":["import type { SchemaIR } from \"../../types.js\";\nimport {\n isContextFreeUnaryCallback,\n isReferenceablePredicate,\n tryCompileEffect,\n} from \"../effects.js\";\nimport type { ExtractorContext, ZodDef } from \"../types.js\";\nimport { extractStringBool, isStringBoolCodec } from \"./string-bool.js\";\n\nexport function extractPipe(def: ZodDef, ctx: ExtractorContext): SchemaIR {\n // Detect stringbool Codec (string→boolean with transform + reverseTransform)\n if (isStringBoolCodec(def)) {\n const ir = extractStringBool(def, ctx);\n if (ir) return ir;\n // Probing failed — fall back to Zod\n return ctx.fallback(\"transform\");\n }\n\n // Other Codecs (z.codec): the decode transform lives on the pipe def itself.\n // Compiling in/out as a plain pipe would validate the untransformed value\n // against the output schema — delegate to Zod.\n if (def.transform !== undefined) {\n return ctx.fallback(\"transform\");\n }\n\n // z.preprocess(fn, schema) is represented as pipe(transform(fn), schema):\n // run the callback before validating the output schema. A synchronous\n // single-argument callback needs no Zod parse context, so it can use the\n // same inline-or-reference machinery as a regular transform.\n const inDef = def.in?._zod?.def;\n if (inDef && inDef.type === \"transform\" && isContextFreeUnaryCallback(inDef.transform)) {\n const source = tryCompileEffect(inDef.transform);\n if (source) {\n return {\n type: \"effect\",\n effectKind: \"preprocess\",\n source,\n inner: ctx.visit(def.out, \"._zod.def.out\"),\n };\n }\n if (ctx.refs) {\n const refIndex = ctx.refs.length;\n ctx.refs.push({\n schema: inDef.transform,\n accessPath: `${ctx.path}._zod.def.in._zod.def.transform`,\n });\n return {\n type: \"effect\",\n effectKind: \"preprocess\",\n refIndex,\n inner: ctx.visit(def.out, \"._zod.def.out\"),\n };\n }\n }\n\n const outDef = def.out?._zod?.def;\n if (outDef && outDef.type === \"transform\") {\n const source = tryCompileEffect(outDef.transform);\n if (source) {\n const inIR = ctx.visit(def.in, \"._zod.def.in\");\n return { type: \"effect\", effectKind: \"transform\", source, inner: inIR };\n }\n // Not inlineable (the callback captures). Call the user's own function\n // through a schema reference rather than delegating: falling back costs the\n // schema its compiled path AND adds the delegate wrapper on top, which\n // measured SLOWER than plain zod. A two-argument transform is zod's `ctx`\n // protocol and an async one returns a promise, so both still delegate.\n if (ctx.refs && isReferenceablePredicate(outDef.transform)) {\n const refIndex = ctx.refs.length;\n ctx.refs.push({\n schema: outDef.transform,\n accessPath: `${ctx.path}._zod.def.out._zod.def.transform`,\n });\n const inIR = ctx.visit(def.in, \"._zod.def.in\");\n return { type: \"effect\", effectKind: \"transform\", refIndex, inner: inIR };\n }\n return ctx.fallback(\"transform\");\n }\n const inIR = ctx.visit(def.in, \"._zod.def.in\");\n const outIR = ctx.visit(def.out, \"._zod.def.out\");\n return { type: \"pipe\", in: inIR, out: outIR };\n}\n"],"mappings":";;;AASA,SAAgB,YAAY,KAAa,KAAiC;CAExE,IAAI,kBAAkB,GAAG,GAAG;EAC1B,MAAM,KAAK,kBAAkB,KAAK,GAAG;EACrC,IAAI,IAAI,OAAO;EAEf,OAAO,IAAI,SAAS,WAAW;CACjC;CAKA,IAAI,IAAI,cAAc,KAAA,GACpB,OAAO,IAAI,SAAS,WAAW;CAOjC,MAAM,QAAQ,IAAI,IAAI,MAAM;CAC5B,IAAI,SAAS,MAAM,SAAS,eAAe,2BAA2B,MAAM,SAAS,GAAG;EACtF,MAAM,SAAS,iBAAiB,MAAM,SAAS;EAC/C,IAAI,QACF,OAAO;GACL,MAAM;GACN,YAAY;GACZ;GACA,OAAO,IAAI,MAAM,IAAI,KAAK,eAAe;EAC3C;EAEF,IAAI,IAAI,MAAM;GACZ,MAAM,WAAW,IAAI,KAAK;GAC1B,IAAI,KAAK,KAAK;IACZ,QAAQ,MAAM;IACd,YAAY,GAAG,IAAI,KAAK;GAC1B,CAAC;GACD,OAAO;IACL,MAAM;IACN,YAAY;IACZ;IACA,OAAO,IAAI,MAAM,IAAI,KAAK,eAAe;GAC3C;EACF;CACF;CAEA,MAAM,SAAS,IAAI,KAAK,MAAM;CAC9B,IAAI,UAAU,OAAO,SAAS,aAAa;EACzC,MAAM,SAAS,iBAAiB,OAAO,SAAS;EAChD,IAAI,QAEF,OAAO;GAAE,MAAM;GAAU,YAAY;GAAa;GAAQ,OAD7C,IAAI,MAAM,IAAI,IAAI,cACqC;EAAE;EAOxE,IAAI,IAAI,QAAQ,yBAAyB,OAAO,SAAS,GAAG;GAC1D,MAAM,WAAW,IAAI,KAAK;GAC1B,IAAI,KAAK,KAAK;IACZ,QAAQ,OAAO;IACf,YAAY,GAAG,IAAI,KAAK;GAC1B,CAAC;GAED,OAAO;IAAE,MAAM;IAAU,YAAY;IAAa;IAAU,OAD/C,IAAI,MAAM,IAAI,IAAI,cACuC;GAAE;EAC1E;EACA,OAAO,IAAI,SAAS,WAAW;CACjC;CAGA,OAAO;EAAE,MAAM;EAAQ,IAFV,IAAI,MAAM,IAAI,IAAI,cAED;EAAG,KADnB,IAAI,MAAM,IAAI,KAAK,eACS;CAAE;AAC9C"}
1
+ {"version":3,"file":"pipe.js","names":[],"sources":["../../../../src/core/extract/extractors/pipe.ts"],"sourcesContent":["import type { SchemaIR } from \"../../types.js\";\nimport {\n isContextFreeUnaryCallback,\n isReferenceablePredicate,\n observesSecondArgument,\n tryCompileEffect,\n} from \"../effects.js\";\nimport type { ExtractorContext, ZodDef } from \"../types.js\";\nimport { extractStringBool, isStringBoolCodec } from \"./string-bool.js\";\n\nexport function extractPipe(def: ZodDef, ctx: ExtractorContext): SchemaIR {\n // Detect stringbool Codec (string→boolean with transform + reverseTransform)\n if (isStringBoolCodec(def)) {\n const ir = extractStringBool(def, ctx);\n if (ir) return ir;\n // Probing failed — fall back to Zod\n return ctx.fallback(\"transform\");\n }\n\n // Other Codecs (z.codec): the decode transform lives on the pipe def itself.\n // Compiling in/out as a plain pipe would validate the untransformed value\n // against the output schema — delegate to Zod.\n if (def.transform !== undefined) {\n return ctx.fallback(\"transform\");\n }\n\n // z.preprocess(fn, schema) is represented as pipe(transform(fn), schema):\n // run the callback before validating the output schema. A synchronous\n // single-argument callback needs no Zod parse context, so it can use the\n // same inline-or-reference machinery as a regular transform.\n const inDef = def.in?._zod?.def;\n if (inDef && inDef.type === \"transform\" && isContextFreeUnaryCallback(inDef.transform)) {\n const source = tryCompileEffect(inDef.transform);\n if (source) {\n return {\n type: \"effect\",\n effectKind: \"preprocess\",\n source,\n inner: ctx.visit(def.out, \"._zod.def.out\"),\n };\n }\n if (ctx.refs) {\n const refIndex = ctx.refs.length;\n ctx.refs.push({\n schema: inDef.transform,\n accessPath: `${ctx.path}._zod.def.in._zod.def.transform`,\n });\n return {\n type: \"effect\",\n effectKind: \"preprocess\",\n refIndex,\n inner: ctx.visit(def.out, \"._zod.def.out\"),\n };\n }\n }\n\n const outDef = def.out?._zod?.def;\n if (outDef && outDef.type === \"transform\") {\n // Zod calls `transform(payload.value, payload)`; both compiled routes below\n // pass the value alone. A callback whose parameter list PROVES it reads that\n // second argument has to stay with zod — `fn.length` cannot see it, since it\n // stops at the first default and ignores a rest element, so\n // `(...args) => args.length` and `(v, ctx = null) => …` both slipped through\n // and silently computed a different result compiled than under zod.\n //\n // A signature that cannot be READ (a native, a bound function) is not\n // thereby suspect and keeps the reference route. That knowingly retains a\n // narrow pre-existing hole — a variadic native like `String.fromCharCode`,\n // or `((...args) => …).bind(null)`, reports length 0 and still sees only one\n // argument compiled — because closing it would make the far more common\n // `.transform(Number)` delegate, which measures slower than not compiling.\n if (!observesSecondArgument(outDef.transform)) {\n const source = tryCompileEffect(outDef.transform);\n if (source) {\n const inIR = ctx.visit(def.in, \"._zod.def.in\");\n return { type: \"effect\", effectKind: \"transform\", source, inner: inIR };\n }\n // Not inlineable (the callback captures, or its source is not an\n // expression). Call the user's own function through a schema reference\n // rather than delegating: falling back costs the schema its compiled path\n // AND adds the delegate wrapper on top, which measured SLOWER than plain\n // zod.\n if (ctx.refs && isReferenceablePredicate(outDef.transform)) {\n const refIndex = ctx.refs.length;\n ctx.refs.push({\n schema: outDef.transform,\n accessPath: `${ctx.path}._zod.def.out._zod.def.transform`,\n });\n const inIR = ctx.visit(def.in, \"._zod.def.in\");\n return { type: \"effect\", effectKind: \"transform\", refIndex, inner: inIR };\n }\n }\n // A ctx-taking transform is zod's issue-collection protocol and an async one\n // returns a promise, so both delegate.\n return ctx.fallback(\"transform\");\n }\n const inIR = ctx.visit(def.in, \"._zod.def.in\");\n const outIR = ctx.visit(def.out, \"._zod.def.out\");\n return { type: \"pipe\", in: inIR, out: outIR };\n}\n"],"mappings":";;;AAUA,SAAgB,YAAY,KAAa,KAAiC;CAExE,IAAI,kBAAkB,GAAG,GAAG;EAC1B,MAAM,KAAK,kBAAkB,KAAK,GAAG;EACrC,IAAI,IAAI,OAAO;EAEf,OAAO,IAAI,SAAS,WAAW;CACjC;CAKA,IAAI,IAAI,cAAc,KAAA,GACpB,OAAO,IAAI,SAAS,WAAW;CAOjC,MAAM,QAAQ,IAAI,IAAI,MAAM;CAC5B,IAAI,SAAS,MAAM,SAAS,eAAe,2BAA2B,MAAM,SAAS,GAAG;EACtF,MAAM,SAAS,iBAAiB,MAAM,SAAS;EAC/C,IAAI,QACF,OAAO;GACL,MAAM;GACN,YAAY;GACZ;GACA,OAAO,IAAI,MAAM,IAAI,KAAK,eAAe;EAC3C;EAEF,IAAI,IAAI,MAAM;GACZ,MAAM,WAAW,IAAI,KAAK;GAC1B,IAAI,KAAK,KAAK;IACZ,QAAQ,MAAM;IACd,YAAY,GAAG,IAAI,KAAK;GAC1B,CAAC;GACD,OAAO;IACL,MAAM;IACN,YAAY;IACZ;IACA,OAAO,IAAI,MAAM,IAAI,KAAK,eAAe;GAC3C;EACF;CACF;CAEA,MAAM,SAAS,IAAI,KAAK,MAAM;CAC9B,IAAI,UAAU,OAAO,SAAS,aAAa;EAczC,IAAI,CAAC,uBAAuB,OAAO,SAAS,GAAG;GAC7C,MAAM,SAAS,iBAAiB,OAAO,SAAS;GAChD,IAAI,QAEF,OAAO;IAAE,MAAM;IAAU,YAAY;IAAa;IAAQ,OAD7C,IAAI,MAAM,IAAI,IAAI,cACqC;GAAE;GAOxE,IAAI,IAAI,QAAQ,yBAAyB,OAAO,SAAS,GAAG;IAC1D,MAAM,WAAW,IAAI,KAAK;IAC1B,IAAI,KAAK,KAAK;KACZ,QAAQ,OAAO;KACf,YAAY,GAAG,IAAI,KAAK;IAC1B,CAAC;IAED,OAAO;KAAE,MAAM;KAAU,YAAY;KAAa;KAAU,OAD/C,IAAI,MAAM,IAAI,IAAI,cACuC;IAAE;GAC1E;EACF;EAGA,OAAO,IAAI,SAAS,WAAW;CACjC;CAGA,OAAO;EAAE,MAAM;EAAQ,IAFV,IAAI,MAAM,IAAI,IAAI,cAED;EAAG,KADnB,IAAI,MAAM,IAAI,KAAK,eACS;CAAE;AAC9C"}
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.d.ts","names":[],"sources":["../src/discovery.ts"],"mappings":";;UAIiB;;EAEf;;;;;;;;;iBAoBoB,gBACpB,kBACA,UAAU,kBACT,QAAQ"}
1
+ {"version":3,"file":"discovery.d.ts","names":[],"sources":["../src/discovery.ts"],"mappings":";;UAKiB;;EAEf;;;;;;;;;iBAUoB,gBACpB,kBACA,UAAU,kBACT,QAAQ"}
package/dist/discovery.js CHANGED
@@ -1,16 +1,8 @@
1
1
  import { isCompiledSchema } from "./core/compile.js";
2
+ import { isZodSchema } from "./is-zod-schema.js";
2
3
  import { loadSourceFile } from "./loader.js";
3
4
  //#region src/discovery.ts
4
5
  /**
5
- * Check if a value is a Zod schema by detecting the _zod.def structure.
6
- * Used by autoDiscover to find plain Zod exports.
7
- */
8
- function isZodSchema(value) {
9
- if (typeof value !== "object" || value === null || !("_zod" in value)) return false;
10
- const zod = value["_zod"];
11
- return typeof zod === "object" && zod !== null && "def" in zod;
12
- }
13
- /**
14
6
  * Discover schemas in a source file by importing it and scanning exports.
15
7
  *
16
8
  * - Default mode: finds compile() calls via CompiledSchema marker.
@@ -22,15 +14,19 @@ async function discoverSchemas(filePath, options) {
22
14
  const schemas = [];
23
15
  const targets = { ...mod };
24
16
  const defaultExport = mod["default"];
25
- if (defaultExport != null && typeof defaultExport === "object" && !isCompiledSchema(defaultExport)) Object.assign(targets, defaultExport);
26
- for (const [exportName, value] of Object.entries(targets)) if (isCompiledSchema(value)) schemas.push({
27
- exportName,
28
- schema: value["schema"]
29
- });
30
- else if (options?.autoDiscover && isZodSchema(value)) schemas.push({
31
- exportName,
32
- schema: value
33
- });
17
+ if (defaultExport != null && typeof defaultExport === "object" && !isCompiledSchema(defaultExport)) try {
18
+ Object.assign(targets, defaultExport);
19
+ } catch {}
20
+ for (const [exportName, value] of Object.entries(targets)) try {
21
+ if (isCompiledSchema(value)) schemas.push({
22
+ exportName,
23
+ schema: value["schema"]
24
+ });
25
+ else if (options?.autoDiscover && isZodSchema(value)) schemas.push({
26
+ exportName,
27
+ schema: value
28
+ });
29
+ } catch {}
34
30
  return schemas;
35
31
  }
36
32
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.js","names":[],"sources":["../src/discovery.ts"],"sourcesContent":["import { isCompiledSchema } from \"./core/compile.js\";\nimport type { DiscoveredSchema } from \"./core/types.js\";\nimport { loadSourceFile } from \"./loader.js\";\n\nexport interface DiscoverOptions {\n /** Auto-detect all exported Zod schemas without requiring compile() wrappers. */\n autoDiscover?: boolean | undefined;\n}\n\n/**\n * Check if a value is a Zod schema by detecting the _zod.def structure.\n * Used by autoDiscover to find plain Zod exports.\n */\nfunction isZodSchema(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null || !(\"_zod\" in value)) return false;\n const zod = (value as Record<string, unknown>)[\"_zod\"];\n return typeof zod === \"object\" && zod !== null && \"def\" in zod;\n}\n\n/**\n * Discover schemas in a source file by importing it and scanning exports.\n *\n * - Default mode: finds compile() calls via CompiledSchema marker.\n * - autoDiscover mode: also finds plain Zod schema exports via _zod.def detection.\n * compile() schemas take priority (isCompiledSchema checked first).\n */\nexport async function discoverSchemas(\n filePath: string,\n options?: DiscoverOptions,\n): Promise<DiscoveredSchema[]> {\n const mod = await loadSourceFile(filePath);\n const schemas: DiscoveredSchema[] = [];\n const targets: Record<string, unknown> = { ...mod };\n const defaultExport = mod[\"default\"];\n if (\n defaultExport != null &&\n typeof defaultExport === \"object\" &&\n !isCompiledSchema(defaultExport)\n ) {\n Object.assign(targets, defaultExport as Record<string, unknown>);\n }\n\n for (const [exportName, value] of Object.entries(targets)) {\n if (isCompiledSchema(value)) {\n schemas.push({ exportName, schema: (value as unknown as Record<string, unknown>)[\"schema\"] });\n } else if (options?.autoDiscover && isZodSchema(value)) {\n schemas.push({ exportName, schema: value });\n }\n }\n\n return schemas;\n}\n"],"mappings":";;;;;;;AAaA,SAAS,YAAY,OAAyB;CAC5C,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,EAAE,UAAU,QAAQ,OAAO;CAC9E,MAAM,MAAO,MAAkC;CAC/C,OAAO,OAAO,QAAQ,YAAY,QAAQ,QAAQ,SAAS;AAC7D;;;;;;;;AASA,eAAsB,gBACpB,UACA,SAC6B;CAC7B,MAAM,MAAM,MAAM,eAAe,QAAQ;CACzC,MAAM,UAA8B,CAAC;CACrC,MAAM,UAAmC,EAAE,GAAG,IAAI;CAClD,MAAM,gBAAgB,IAAI;CAC1B,IACE,iBAAiB,QACjB,OAAO,kBAAkB,YACzB,CAAC,iBAAiB,aAAa,GAE/B,OAAO,OAAO,SAAS,aAAwC;CAGjE,KAAK,MAAM,CAAC,YAAY,UAAU,OAAO,QAAQ,OAAO,GACtD,IAAI,iBAAiB,KAAK,GACxB,QAAQ,KAAK;EAAE;EAAY,QAAS,MAA6C;CAAU,CAAC;MACvF,IAAI,SAAS,gBAAgB,YAAY,KAAK,GACnD,QAAQ,KAAK;EAAE;EAAY,QAAQ;CAAM,CAAC;CAI9C,OAAO;AACT"}
1
+ {"version":3,"file":"discovery.js","names":[],"sources":["../src/discovery.ts"],"sourcesContent":["import { isCompiledSchema } from \"./core/compile.js\";\nimport type { DiscoveredSchema } from \"./core/types.js\";\nimport { isZodSchema } from \"./is-zod-schema.js\";\nimport { loadSourceFile } from \"./loader.js\";\n\nexport interface DiscoverOptions {\n /** Auto-detect all exported Zod schemas without requiring compile() wrappers. */\n autoDiscover?: boolean | undefined;\n}\n\n/**\n * Discover schemas in a source file by importing it and scanning exports.\n *\n * - Default mode: finds compile() calls via CompiledSchema marker.\n * - autoDiscover mode: also finds plain Zod schema exports via _zod.def detection.\n * compile() schemas take priority (isCompiledSchema checked first).\n */\nexport async function discoverSchemas(\n filePath: string,\n options?: DiscoverOptions,\n): Promise<DiscoveredSchema[]> {\n const mod = await loadSourceFile(filePath);\n const schemas: DiscoveredSchema[] = [];\n const targets: Record<string, unknown> = { ...mod };\n const defaultExport = mod[\"default\"];\n if (\n defaultExport != null &&\n typeof defaultExport === \"object\" &&\n !isCompiledSchema(defaultExport)\n ) {\n try {\n Object.assign(targets, defaultExport as Record<string, unknown>);\n } catch {\n // A default export that resists enumeration — a Proxy trapping `ownKeys`,\n // a getter that throws — is not a bag of schemas. The named exports of\n // this file still stand on their own.\n }\n }\n\n for (const [exportName, value] of Object.entries(targets)) {\n // Probing touches values the user never offered as candidates, and a Proxy\n // can answer `has` with true and then throw from `get` (an ORM model, an\n // i18n catch-all). Skipping that one export keeps the rest of the file's\n // schemas compiling, where letting it escape fails the whole build.\n try {\n if (isCompiledSchema(value)) {\n schemas.push({\n exportName,\n schema: (value as unknown as Record<string, unknown>)[\"schema\"],\n });\n } else if (options?.autoDiscover && isZodSchema(value)) {\n schemas.push({ exportName, schema: value });\n }\n } catch {\n // Not a schema; leave it alone.\n }\n }\n\n return schemas;\n}\n"],"mappings":";;;;;;;;;;;AAiBA,eAAsB,gBACpB,UACA,SAC6B;CAC7B,MAAM,MAAM,MAAM,eAAe,QAAQ;CACzC,MAAM,UAA8B,CAAC;CACrC,MAAM,UAAmC,EAAE,GAAG,IAAI;CAClD,MAAM,gBAAgB,IAAI;CAC1B,IACE,iBAAiB,QACjB,OAAO,kBAAkB,YACzB,CAAC,iBAAiB,aAAa,GAE/B,IAAI;EACF,OAAO,OAAO,SAAS,aAAwC;CACjE,QAAQ,CAIR;CAGF,KAAK,MAAM,CAAC,YAAY,UAAU,OAAO,QAAQ,OAAO,GAKtD,IAAI;EACF,IAAI,iBAAiB,KAAK,GACxB,QAAQ,KAAK;GACX;GACA,QAAS,MAA6C;EACxD,CAAC;OACI,IAAI,SAAS,gBAAgB,YAAY,KAAK,GACnD,QAAQ,KAAK;GAAE;GAAY,QAAQ;EAAM,CAAC;CAE9C,QAAQ,CAER;CAGF,OAAO;AACT"}
@@ -0,0 +1,23 @@
1
+ import { ZodType } from "zod";
2
+ //#region src/is-zod-schema.d.ts
3
+ /**
4
+ * Is this value a Zod schema? Zod schemas carry `_zod.def`, so that is the
5
+ * probe — the same one auto-discovery uses at build time and `jitAll` uses at
6
+ * runtime.
7
+ *
8
+ * The probe is applied to values the caller does not control: every own value
9
+ * of a module namespace under `jitAll`, every export of a candidate file under
10
+ * discovery. Some of those are not plain objects. A Proxy can trap `has` and
11
+ * `get` and throw — an ORM model, a strict test double, an i18n catch-all — and
12
+ * `"_zod" in value` fires the first trap while reading `_zod` fires the second.
13
+ * Whatever such a value is, it is not a Zod schema, and asking must not take
14
+ * down the caller: an uncaught throw here aborts the importing app at boot, or
15
+ * the build, over an object that was never a candidate.
16
+ *
17
+ * Kept as a leaf module with no runtime imports: `jit`/`jitAll` reach it on the
18
+ * startup path, where pulling in the build-time graph would be pure cost.
19
+ */
20
+ declare function isZodSchema(value: unknown): value is ZodType;
21
+ //#endregion
22
+ export { isZodSchema };
23
+ //# sourceMappingURL=is-zod-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-zod-schema.d.ts","names":[],"sources":["../src/is-zod-schema.ts"],"mappings":";;;;;;;;;;;;;;;;;;;iBAmBgB,YAAY,iBAAiB,SAAS"}
@@ -0,0 +1,32 @@
1
+ //#region src/is-zod-schema.ts
2
+ /**
3
+ * Is this value a Zod schema? Zod schemas carry `_zod.def`, so that is the
4
+ * probe — the same one auto-discovery uses at build time and `jitAll` uses at
5
+ * runtime.
6
+ *
7
+ * The probe is applied to values the caller does not control: every own value
8
+ * of a module namespace under `jitAll`, every export of a candidate file under
9
+ * discovery. Some of those are not plain objects. A Proxy can trap `has` and
10
+ * `get` and throw — an ORM model, a strict test double, an i18n catch-all — and
11
+ * `"_zod" in value` fires the first trap while reading `_zod` fires the second.
12
+ * Whatever such a value is, it is not a Zod schema, and asking must not take
13
+ * down the caller: an uncaught throw here aborts the importing app at boot, or
14
+ * the build, over an object that was never a candidate.
15
+ *
16
+ * Kept as a leaf module with no runtime imports: `jit`/`jitAll` reach it on the
17
+ * startup path, where pulling in the build-time graph would be pure cost.
18
+ */
19
+ function isZodSchema(value) {
20
+ if (typeof value !== "object" || value === null) return false;
21
+ try {
22
+ if (!("_zod" in value)) return false;
23
+ const internal = value["_zod"];
24
+ return typeof internal === "object" && internal !== null && "def" in internal;
25
+ } catch {
26
+ return false;
27
+ }
28
+ }
29
+ //#endregion
30
+ export { isZodSchema };
31
+
32
+ //# sourceMappingURL=is-zod-schema.js.map