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
package/README.md CHANGED
@@ -2,13 +2,17 @@
2
2
 
3
3
  **Compile Zod schemas into zero-overhead validation functions at build time.**
4
4
 
5
- Keep your existing Zod schemas. Get **up to 43x faster** validation, and up to **48x** on rejected
5
+ Keep your existing Zod schemas. Get **up to 43x faster** validation, and up to **140x** on rejected
6
6
  input. No code changes required.
7
7
 
8
8
  Requires **Zod ≥ 4.5**; compiled output reproduces 4.5's semantics exactly, so it does not match
9
9
  earlier 4.x releases. Use zod-compiler 1.x for Zod 4.0–4.4. An older Zod is refused with an explicit
10
10
  error; the build plugin and `jit()` warn once and leave schemas as plain Zod.
11
11
 
12
+ Classic `zod` and `zod/mini` schemas compile alike, and `compile()` and `jit()` accept both. A compiled
13
+ schema fails the way its flavor does: classic schemas with `ZodError`, Mini schemas with Zod core's
14
+ `$ZodError`.
15
+
12
16
  - [What Gets Compiled](#what-gets-compiled)
13
17
  - [Schema Hoisting](#schema-hoisting)
14
18
  - [z.compile vs zod-compiler](#zcompile-vs-zod-compiler)
@@ -26,7 +30,7 @@ loads pre-generated validators.
26
30
  | | zod-compiler (build plugins / CLI) | Zod `z.compile()` |
27
31
  | -------------------------------------- | -------------------------------------------- | --------------------------------------------- |
28
32
  | Compilation | Build time (true AOT) | Runtime (`z.compile()` or the first parse) |
29
- | Reported validation speedup | Up to 43x; up to 48x on rejected input | ~9x in Zod's headline example |
33
+ | Reported validation speedup | Up to 43x; up to 140x on rejected input | ~9x in Zod's headline example |
30
34
  | Uses `new Function()` at runtime | No | Yes |
31
35
  | Cold start | Fast; the validator is already generated | Pays for code generation at startup/first use |
32
36
  | Strict CSP without `'unsafe-eval'` | Supported | Compilation is unavailable |
@@ -548,7 +552,7 @@ A schema delegates to Zod when it reaches JavaScript the generated code cannot r
548
552
  | ---------------------------------------------------- | -------------------------------------------------------------------------- |
549
553
  | `.check(fn)`, `superRefine` + later checks | The callback holds Zod's payload unmediated, or `fatal` aborts Zod's chain |
550
554
  | `ctx`-taking or `async` callbacks | Needs Zod's parse context / the async pipeline |
551
- | `z.url()`, `z.jwt()` | Algorithmic formats (`new URL()`, signature parsing) |
555
+ | `z.jwt()` | Algorithmic format (signature parsing) |
552
556
  | Overlapping or policy-sensitive object intersections | Zod's independent parse-and-merge semantics cannot be safely collapsed |
553
557
  | `.readonly()` over a pass-through container | Zod freezes the output it rebuilt; these are the caller's own input |
554
558
  | Dynamic error maps, unresolvable `z.lazy()` | Not knowable at build time |
@@ -580,55 +584,55 @@ Schema-level `error` and `z.config()` maps are unaffected; for a per-call map us
580
584
 
581
585
  | Scenario | Zod v3 | Zod v4 | **zod-compiler** | Typia | AJV | vs Zod v4 |
582
586
  | ----------------------------------------------- | ------ | ------ | ---------------- | ----- | ----- | --------- |
583
- | simple string | 12.7M | 15.1M | **16.5M** | 17.1M | 17.7M | 1.1x |
584
- | string (min/max) | 12.5M | 7.3M | **16.7M** | 17.0M | 15.0M | 2.3x |
585
- | number (int+positive) | 12.0M | 9.5M | **16.8M** | 17.0M | 18.0M | 1.8x |
586
- | enum | 11.6M | 15.1M | **16.7M** | 17.3M | 17.5M | 1.1x |
587
- | bigint (min/max) | 11.5M | 8.4M | **16.6M** | — | — | 2.0x |
588
- | tuple [string, int, bool] | 5.7M | 7.6M | **17.5M** | 17.3M | 16.2M | 2.3x |
589
- | record\<string, number\> | 3.2M | 2.6M | **12.6M** | 11.8M | 15.1M | 4.9x |
590
- | set\<string\> (5 items) | 3.6M | 2.3M | **15.7M** | — | — | 7.0x |
591
- | set\<string\> (20 items) | 1.3M | 679K | **12.3M** | — | — | **18x** |
592
- | map\<string, number\> (5 entries) | 2.0M | 1.3M | **13.4M** | — | — | **10x** |
593
- | map\<string, number\> (20 entries) | 658K | 348K | **8.7M** | — | — | **25x** |
594
- | pipe (non-transform) | 8.9M | 4.7M | **17.6M** | — | — | 3.7x |
595
- | discriminatedUnion (3 variants) | 3.3M | 5.2M | **16.3M** | 15.5M | 7.6M | 3.1x |
596
- | discriminatedUnion (8 variants, rotating) | 2.6M | 4.6M | **10.1M** | — | — | 2.2x |
597
- | plain union of 8 tagged objects (auto-discrim.) | 350K | 1.3M | **10.4M** | — | — | 7.8x |
598
- | strict object (DB row) | 1.8M | 3.0M | **11.5M** | — | — | 3.9x |
599
- | medium object (valid) | 2.0M | 2.3M | **9.8M** | 11.4M | 7.6M | 4.2x |
600
- | medium object (extra keys stripped) | 1.8M | 2.1M | **9.6M** | — | — | 4.6x |
601
- | medium object (invalid) | 464K | 370K | **16.0M** | 2.9M | 7.6M | **43x** |
602
- | large object (10 items) | 119K | 169K | **5.2M** | 5.9M | 1.2M | **31x** |
603
- | large object (100 items) | 13K | 18K | **763K** | 1.3M | 127K | **43x** |
604
- | readonly field (wrapper compiles away) | 3.1M | 6.6M | **17.1M** | — | — | 2.6x |
605
- | readonly root object (rebuild + freeze) | 2.8M | 5.6M | **13.0M** | — | — | 2.3x |
606
- | readonly array (delegates to Zod) | 3.9M | 4.3M | **4.2M** | — | — | 1.0x |
607
- | recursive tree (7 nodes) | 576K | 1.0M | **7.8M** | 11.9M | 4.8M | 7.8x |
608
- | recursive tree (121 nodes) | 32K | 57K | **774K** | 1.9M | 371K | **13x** |
609
- | nested recursion (7 nodes) | 388K | 677K | **7.7M** | 10.4M | 3.0M | **11x** |
610
- | nested recursion (121 nodes) | 24K | 41K | **804K** | 1.6M | 210K | **20x** |
611
- | deeply nested object (243 leaves) | 11K | 27K | **847K** | 1.0M | 122K | **31x** |
612
- | event log (combined) | 370K | 786K | **7.7M** | — | — | 9.8x |
613
- | catalog product with image URLs (valid) | 426K | 451K | **1.4M** | — | — | 3.1x |
614
- | catalog product with image URLs (invalid) | 115K | 104K | **237K** | — | — | 2.3x |
615
- | catalog page (20 products) | 21K | 23K | **74K** | — | — | 3.2x |
616
- | object with transform (zero-capture) | 1.1M | 1.9M | **7.3M** | — | — | 3.9x |
617
- | array 10 × transform (zero-capture) | 120K | 209K | **4.3M** | — | — | **21x** |
618
- | array 50 × transform (zero-capture) | 25K | 43K | **1.0M** | — | — | **24x** |
619
- | object with captured transform | 1.2M | 7.8M | **15.3M** | — | — | 2.0x |
620
- | object with captured refine (cross-field) | 1.5M | 2.2M | **11.2M** | — | — | 5.1x |
621
- | object with superRefine (cross-field) | 1.4M | 2.1M | **9.1M** | — | — | 4.3x |
622
- | coerced query object (valid) | 1.8M | 2.9M | **5.4M** | — | — | 1.8x |
623
- | coerced query object (invalid) | 1.0M | 843K | **10.2M** | — | — | **12x** |
624
- | preprocessed query object (valid) | 430K | 1.7M | **5.3M** | — | — | 3.1x |
625
- | preprocessed query object (invalid) | 382K | 780K | **12.3M** | — | — | **16x** |
626
- | stringbool config object (valid) | — | 2.8M | **6.4M** | — | — | 2.2x |
627
- | stringbool config object (invalid) | — | 688K | **12.6M** | — | — | **18x** |
628
- | custom/instanceof request (valid) | 972K | 3.1M | **10.3M** | — | — | 3.3x |
629
- | custom/instanceof request (invalid) | 776K | 945K | **13.5M** | — | — | **14x** |
630
- | disjoint object intersection (valid) | 1.4M | 1.6M | **10.0M** | — | — | 6.1x |
631
- | disjoint object intersection (invalid) | 482K | 333K | **16.0M** | — | — | **48x** |
587
+ | simple string | 13.1M | 15.9M | **17.3M** | 18.0M | 17.6M | 1.1x |
588
+ | string (min/max) | 12.3M | 7.3M | **17.3M** | 17.2M | 15.6M | 2.4x |
589
+ | number (int+positive) | 12.4M | 9.7M | **17.5M** | 17.7M | 17.0M | 1.8x |
590
+ | enum | 11.6M | 15.5M | **17.8M** | 18.1M | 17.5M | 1.1x |
591
+ | bigint (min/max) | 11.9M | 8.6M | **17.3M** | — | — | 2.0x |
592
+ | tuple [string, int, bool] | 5.5M | 7.4M | **16.7M** | 16.0M | 15.8M | 2.3x |
593
+ | record\<string, number\> | 3.2M | 2.5M | **12.9M** | 12.0M | 15.2M | 5.1x |
594
+ | set\<string\> (5 items) | 3.6M | 2.3M | **15.3M** | — | — | 6.8x |
595
+ | set\<string\> (20 items) | 1.3M | 679K | **12.2M** | — | — | **18x** |
596
+ | map\<string, number\> (5 entries) | 2.0M | 1.3M | **13.5M** | — | — | **10x** |
597
+ | map\<string, number\> (20 entries) | 628K | 346K | **8.5M** | — | — | **25x** |
598
+ | pipe (non-transform) | 8.8M | 4.7M | **16.9M** | — | — | 3.6x |
599
+ | discriminatedUnion (3 variants) | 3.4M | 5.2M | **17.1M** | 16.5M | 7.9M | 3.3x |
600
+ | discriminatedUnion (8 variants, rotating) | 2.6M | 4.6M | **10.5M** | — | — | 2.3x |
601
+ | plain union of 8 tagged objects (auto-discrim.) | 357K | 1.3M | **10.5M** | — | — | 7.9x |
602
+ | strict object (DB row) | 1.8M | 2.9M | **11.4M** | — | — | 3.8x |
603
+ | medium object (valid) | 1.9M | 2.3M | **9.8M** | 11.1M | 7.6M | 4.2x |
604
+ | medium object (extra keys stripped) | 1.8M | 2.1M | **9.7M** | — | — | 4.6x |
605
+ | medium object (invalid) | 517K | 364K | **15.9M** | 2.8M | 7.5M | **44x** |
606
+ | large object (10 items) | 120K | 175K | **5.3M** | 6.0M | 1.2M | **30x** |
607
+ | large object (100 items) | 13K | 18K | **764K** | 1.3M | 124K | **43x** |
608
+ | readonly field (wrapper compiles away) | 3.1M | 6.6M | **17.8M** | — | — | 2.7x |
609
+ | readonly root object (rebuild + freeze) | 2.9M | 5.6M | **13.5M** | — | — | 2.4x |
610
+ | readonly array (delegates to Zod) | 4.0M | 4.4M | **4.4M** | — | — | 1.0x |
611
+ | recursive tree (7 nodes) | 574K | 997K | **9.0M** | 11.9M | 4.8M | 9.0x |
612
+ | recursive tree (121 nodes) | 32K | 58K | **986K** | 1.9M | 375K | **17x** |
613
+ | nested recursion (7 nodes) | 390K | 696K | **7.8M** | 10.5M | 2.9M | **11x** |
614
+ | nested recursion (121 nodes) | 24K | 42K | **816K** | 1.6M | 205K | **20x** |
615
+ | deeply nested object (243 leaves) | 11K | 28K | **845K** | 1.0M | 125K | **30x** |
616
+ | event log (combined) | 375K | 808K | **7.6M** | — | — | 9.4x |
617
+ | catalog product with image URLs (valid) | 432K | 467K | **2.1M** | — | — | 4.6x |
618
+ | catalog product with image URLs (invalid) | 114K | 106K | **14.8M** | — | — | **140x** |
619
+ | catalog page (20 products) | 21K | 23K | **122K** | — | — | 5.2x |
620
+ | object with transform (zero-capture) | 1.1M | 1.9M | **7.6M** | — | — | 4.0x |
621
+ | array 10 × transform (zero-capture) | 123K | 209K | **4.3M** | — | — | **21x** |
622
+ | array 50 × transform (zero-capture) | 26K | 41K | **1.0M** | — | — | **25x** |
623
+ | object with captured transform | 1.2M | 8.3M | **16.2M** | — | — | 2.0x |
624
+ | object with captured refine (cross-field) | 1.5M | 2.2M | **11.7M** | — | — | 5.3x |
625
+ | object with superRefine (cross-field) | 1.5M | 2.2M | **9.6M** | — | — | 4.3x |
626
+ | coerced query object (valid) | 1.8M | 3.0M | **5.4M** | — | — | 1.8x |
627
+ | coerced query object (invalid) | 1.0M | 837K | **10.6M** | — | — | **13x** |
628
+ | preprocessed query object (valid) | 432K | 1.8M | **5.4M** | — | — | 3.0x |
629
+ | preprocessed query object (invalid) | 393K | 796K | **12.7M** | — | — | **16x** |
630
+ | stringbool config object (valid) | — | 2.8M | **6.3M** | — | — | 2.2x |
631
+ | stringbool config object (invalid) | — | 698K | **13.5M** | — | — | **19x** |
632
+ | custom/instanceof request (valid) | 979K | 3.2M | **10.4M** | — | — | 3.3x |
633
+ | custom/instanceof request (invalid) | 795K | 955K | **13.6M** | — | — | **14x** |
634
+ | disjoint object intersection (valid) | 1.4M | 1.6M | **9.7M** | — | — | 6.0x |
635
+ | disjoint object intersection (invalid) | 494K | 335K | **15.7M** | — | — | **47x** |
632
636
 
633
637
  _ops/s, higher is better. `vp test bench` on an Apple M4 Max (zod 4.5.2, zod v3 3.23.8, typia 12, ajv 8),
634
638
  best of three runs. The harness costs ~60 ns per iteration, so the fastest rows sit at that floor and gaps
@@ -646,9 +650,9 @@ vp run benchmark # run locally
646
650
 
647
651
  A schema compiles to a **fast path** — one zero-allocation `&&` chain, shared by `.is()` and
648
652
  `parse()` — plus a **slow path** that collects errors only on failure, deferred until `.error` is read.
649
- Schemas that reshape their input (stripping objects, coercions, `stringbool`, defaults, transforms,
650
- context-free preprocessors, disjoint-key intersections) instead validate and rebuild in a single pass
651
- that bails on the first failure.
653
+ Schemas that reshape their input (stripping objects, coercions, `stringbool`, `z.url()`, defaults,
654
+ transforms, context-free preprocessors, disjoint-key intersections) instead validate and rebuild in a
655
+ single pass that bails on the first failure.
652
656
 
653
657
  Other optimizations:
654
658
 
@@ -1 +1 @@
1
- {"version":3,"file":"emitter.d.ts","names":[],"sources":["../../src/cli/emitter.ts"],"mappings":";;;;;UAkBiB;;;;;;EAMf;;;;;EAKA;;iBAGc,4BACd,SAAS,sBACT,uBACA,UAAU;;;;iBAoEI,kBAAkB,mBAAmB;;;;iBAmB/B,kBAAkB,oBAAoB,kBAAkB"}
1
+ {"version":3,"file":"emitter.d.ts","names":[],"sources":["../../src/cli/emitter.ts"],"mappings":";;;;;UAsBiB;;;;;;EAMf;;;;;EAKA;;iBAGc,4BACd,SAAS,sBACT,uBACA,UAAU;;;;iBA+EI,kBAAkB,mBAAmB;;;;iBAmB/B,kBAAkB,oBAAoB,kBAAkB"}
@@ -1,4 +1,4 @@
1
- import { FAILZ_CLASS_DECL, FAIL_CLASS_DECL, FINZ_DECL, FIN_DECL, FIN_DEFERRED_DECL, MK_VALIDATOR_DECL, ZOD_CONFIG_IMPORT, ZOD_MSG_DECLARATION, generateIIFE } from "../core/iife.js";
1
+ 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_CONFIG_IMPORT, ZOD_MSG_DECLARATION, generateIIFE, usesCoreErrors } from "../core/iife.js";
2
2
  import path from "node:path";
3
3
  import fs from "node:fs";
4
4
  //#region src/cli/emitter.ts
@@ -7,6 +7,8 @@ function generateCompiledFileContent(schemas, sourceRelPath, options) {
7
7
  if (!importPath.startsWith(".")) importPath = `./${importPath}`;
8
8
  const zodCompat = options?.zodCompat !== false;
9
9
  const usesFinZ = schemas.some((s) => s.codegenResult.usedHelpers.has("__zcFinZ"));
10
+ const usesCore = schemas.some((s) => usesCoreErrors(s.codegenResult.usedHelpers));
11
+ const classicFinalizers = schemas.some((s) => s.codegenResult.usedHelpers.has("__zcFin") || s.codegenResult.usedHelpers.has("__zcFinD")) || !usesCore;
10
12
  const needsSource = (s) => zodCompat || s.refEntries.length > 0 || s.codegenResult.usesRetainedSchema === true;
11
13
  const schemasNeedingImport = schemas.filter(needsSource);
12
14
  const exports = schemas.map((schema) => {
@@ -22,10 +24,14 @@ function generateCompiledFileContent(schemas, sourceRelPath, options) {
22
24
  "",
23
25
  ZOD_CONFIG_IMPORT,
24
26
  ZOD_MSG_DECLARATION,
25
- FAIL_CLASS_DECL,
27
+ ...classicFinalizers ? [FAIL_CLASS_DECL] : [],
26
28
  MK_VALIDATOR_DECL,
27
- FIN_DECL,
28
- FIN_DEFERRED_DECL,
29
+ ...classicFinalizers ? [FIN_DECL, FIN_DEFERRED_DECL] : [],
30
+ ...usesCore ? [
31
+ FAIL_CORE_CLASS_DECL,
32
+ FIN_CORE_DECL,
33
+ FIN_DEFERRED_CORE_DECL
34
+ ] : [],
29
35
  ...usesFinZ ? [FAILZ_CLASS_DECL, FINZ_DECL] : [],
30
36
  ...options?.sharedCode ? ["", options.sharedCode] : [],
31
37
  "",
@@ -1 +1 @@
1
- {"version":3,"file":"emitter.js","names":[],"sources":["../../src/cli/emitter.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\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_CONFIG_IMPORT,\n ZOD_MSG_DECLARATION,\n} from \"../core/iife.js\";\nimport type { CompiledSchemaInfo } from \"../core/pipeline.js\";\n\n/**\n * Generate the content of a .compiled.ts file from multiple schemas.\n */\nexport interface EmitterOptions {\n /**\n * When true (default), installs the compiled methods on the original schema\n * object for Zod-compatible output.\n * When false, produces a minimal plain object (smaller bundle).\n */\n zodCompat?: boolean | undefined;\n /**\n * File-level shared validator and constant declarations. Emitted once at\n * module scope so each compiled export can reference them.\n */\n sharedCode?: string | undefined;\n}\n\nexport function generateCompiledFileContent(\n schemas: CompiledSchemaInfo[],\n sourceRelPath: string,\n options?: EmitterOptions,\n): string {\n let importPath = sourceRelPath.replace(/\\.[cm]?[jt]sx?$/, \"\");\n if (!importPath.startsWith(\".\")) {\n importPath = `./${importPath}`;\n }\n\n const zodCompat = options?.zodCompat !== false;\n\n // Compact-mode schemas delegate cold errors to zod via __zcFinZ (its own\n // __ZcFailZ class), emitted only when some export uses it.\n const usesFinZ = schemas.some((s) => s.codegenResult.usedHelpers.has(\"__zcFinZ\"));\n\n // A schema needs the source import when:\n // - zodCompat: true (__zcMkv installs compiled methods on the schema)\n // - has refEntries (fallback schemas referenced via __rf[])\n // - delegates its cold path to the retained schema (reads the __zs binding)\n //\n // The last clause must stay in lockstep with generateIIFE's own test for\n // emitting `var __zs=<expr>;`: hand it an empty expression for a schema it\n // decides to bind and the file is a syntax error (`var __zs=;`).\n const needsSource = (s: CompiledSchemaInfo): boolean =>\n zodCompat || s.refEntries.length > 0 || s.codegenResult.usesRetainedSchema === true;\n const schemasNeedingImport = schemas.filter(needsSource);\n\n const exports = schemas.map((schema) => {\n const needsImport = needsSource(schema);\n // `compile()` returns the schema augmented with a non-enumerable `schema`\n // self-reference; auto-discovery (the default) instead finds plain exported\n // Zod schemas, which have no such property. Both shapes must resolve, so\n // fall through to the export itself — reading `.schema` alone yields\n // undefined there, which silently cost __zcMkv the real schema to augment\n // and hard-crashed every `__rf[]` base at module load.\n const src = `__src_${schema.exportName}`;\n const schemaExpr = needsImport ? `((${src} as any).schema ?? ${src})` : \"\";\n return `export const ${schema.exportName} = ${generateIIFE(schemaExpr, schema, { zodCompat })};`;\n });\n\n const importLine =\n schemasNeedingImport.length > 0\n ? [\n `import { ${schemasNeedingImport.map((s) => `${s.exportName} as __src_${s.exportName}`).join(\", \")} } from \"${importPath}\";`,\n \"\",\n ]\n : [];\n\n return [\n \"// AUTO-GENERATED by zod-compiler — DO NOT EDIT\",\n `// Source: ${sourceRelPath}`,\n \"\",\n ZOD_CONFIG_IMPORT,\n ZOD_MSG_DECLARATION,\n FAIL_CLASS_DECL,\n MK_VALIDATOR_DECL,\n FIN_DECL,\n FIN_DEFERRED_DECL,\n ...(usesFinZ ? [FAILZ_CLASS_DECL, FINZ_DECL] : []),\n ...(options?.sharedCode ? [\"\", options.sharedCode] : []),\n \"\",\n ...importLine,\n ...exports,\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * Resolve the output file path for a given input file.\n */\nexport function resolveOutputPath(inputPath: string, outputFlag: string | undefined): string {\n if (outputFlag) {\n // If output flag ends with / or is a directory, put the compiled file inside it\n if (outputFlag.endsWith(\"/\") || outputFlag.endsWith(path.sep)) {\n const baseName = path.basename(inputPath, path.extname(inputPath));\n return path.join(outputFlag, `${baseName}.compiled.ts`);\n }\n return outputFlag;\n }\n\n // Default: replace .ts with .compiled.ts\n const dir = path.dirname(inputPath);\n const baseName = path.basename(inputPath, path.extname(inputPath));\n return path.join(dir, `${baseName}.compiled.ts`);\n}\n\n/**\n * Write the compiled file content to disk.\n */\nexport async function writeCompiledFile(outputPath: string, content: string): Promise<void> {\n const dir = path.dirname(outputPath);\n await fs.promises.mkdir(dir, { recursive: true });\n await fs.promises.writeFile(outputPath, content, \"utf-8\");\n}\n"],"mappings":";;;;AAgCA,SAAgB,4BACd,SACA,eACA,SACQ;CACR,IAAI,aAAa,cAAc,QAAQ,mBAAmB,EAAE;CAC5D,IAAI,CAAC,WAAW,WAAW,GAAG,GAC5B,aAAa,KAAK;CAGpB,MAAM,YAAY,SAAS,cAAc;CAIzC,MAAM,WAAW,QAAQ,MAAM,MAAM,EAAE,cAAc,YAAY,IAAI,UAAU,CAAC;CAUhF,MAAM,eAAe,MACnB,aAAa,EAAE,WAAW,SAAS,KAAK,EAAE,cAAc,uBAAuB;CACjF,MAAM,uBAAuB,QAAQ,OAAO,WAAW;CAEvD,MAAM,UAAU,QAAQ,KAAK,WAAW;EACtC,MAAM,cAAc,YAAY,MAAM;EAOtC,MAAM,MAAM,SAAS,OAAO;EAC5B,MAAM,aAAa,cAAc,KAAK,IAAI,qBAAqB,IAAI,KAAK;EACxE,OAAO,gBAAgB,OAAO,WAAW,KAAK,aAAa,YAAY,QAAQ,EAAE,UAAU,CAAC,EAAE;CAChG,CAAC;CAED,MAAM,aACJ,qBAAqB,SAAS,IAC1B,CACE,YAAY,qBAAqB,KAAK,MAAM,GAAG,EAAE,WAAW,YAAY,EAAE,YAAY,CAAC,CAAC,KAAK,IAAI,EAAE,WAAW,WAAW,KACzH,EACF,IACA,CAAC;CAEP,OAAO;EACL;EACA,cAAc;EACd;EACA;EACA;EACA;EACA;EACA;EACA;EACA,GAAI,WAAW,CAAC,kBAAkB,SAAS,IAAI,CAAC;EAChD,GAAI,SAAS,aAAa,CAAC,IAAI,QAAQ,UAAU,IAAI,CAAC;EACtD;EACA,GAAG;EACH,GAAG;EACH;CACF,CAAC,CAAC,KAAK,IAAI;AACb;;;;AAKA,SAAgB,kBAAkB,WAAmB,YAAwC;CAC3F,IAAI,YAAY;EAEd,IAAI,WAAW,SAAS,GAAG,KAAK,WAAW,SAAS,KAAK,GAAG,GAAG;GAC7D,MAAM,WAAW,KAAK,SAAS,WAAW,KAAK,QAAQ,SAAS,CAAC;GACjE,OAAO,KAAK,KAAK,YAAY,GAAG,SAAS,aAAa;EACxD;EACA,OAAO;CACT;CAGA,MAAM,MAAM,KAAK,QAAQ,SAAS;CAClC,MAAM,WAAW,KAAK,SAAS,WAAW,KAAK,QAAQ,SAAS,CAAC;CACjE,OAAO,KAAK,KAAK,KAAK,GAAG,SAAS,aAAa;AACjD;;;;AAKA,eAAsB,kBAAkB,YAAoB,SAAgC;CAC1F,MAAM,MAAM,KAAK,QAAQ,UAAU;CACnC,MAAM,GAAG,SAAS,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;CAChD,MAAM,GAAG,SAAS,UAAU,YAAY,SAAS,OAAO;AAC1D"}
1
+ {"version":3,"file":"emitter.js","names":[],"sources":["../../src/cli/emitter.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\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_CONFIG_IMPORT,\n ZOD_MSG_DECLARATION,\n} from \"../core/iife.js\";\nimport type { CompiledSchemaInfo } from \"../core/pipeline.js\";\n\n/**\n * Generate the content of a .compiled.ts file from multiple schemas.\n */\nexport interface EmitterOptions {\n /**\n * When true (default), installs the compiled methods on the original schema\n * object for Zod-compatible output.\n * When false, produces a minimal plain object (smaller bundle).\n */\n zodCompat?: boolean | undefined;\n /**\n * File-level shared validator and constant declarations. Emitted once at\n * module scope so each compiled export can reference them.\n */\n sharedCode?: string | undefined;\n}\n\nexport function generateCompiledFileContent(\n schemas: CompiledSchemaInfo[],\n sourceRelPath: string,\n options?: EmitterOptions,\n): string {\n let importPath = sourceRelPath.replace(/\\.[cm]?[jt]sx?$/, \"\");\n if (!importPath.startsWith(\".\")) {\n importPath = `./${importPath}`;\n }\n\n const zodCompat = options?.zodCompat !== false;\n\n // Compact-mode schemas delegate cold errors to zod via __zcFinZ (its own\n // __ZcFailZ class), emitted only when some export uses it.\n const usesFinZ = schemas.some((s) => s.codegenResult.usedHelpers.has(\"__zcFinZ\"));\n\n // Exports of zod/mini schemas fail with core's `$ZodError`, through their own\n // finalizer pair (see FAIL_CORE_CLASS_DECL). The classic pair is emitted as it\n // always has been, whether called or not, except in a file whose validators\n // all build core errors: there nothing calls it.\n const usesCore = schemas.some((s) => usesCoreErrors(s.codegenResult.usedHelpers));\n const usesClassic = schemas.some(\n (s) =>\n s.codegenResult.usedHelpers.has(\"__zcFin\") || s.codegenResult.usedHelpers.has(\"__zcFinD\"),\n );\n const classicFinalizers = usesClassic || !usesCore;\n\n // A schema needs the source import when:\n // - zodCompat: true (__zcMkv installs compiled methods on the schema)\n // - has refEntries (fallback schemas referenced via __rf[])\n // - delegates its cold path to the retained schema (reads the __zs binding)\n //\n // The last clause must stay in lockstep with generateIIFE's own test for\n // emitting `var __zs=<expr>;`: hand it an empty expression for a schema it\n // decides to bind and the file is a syntax error (`var __zs=;`).\n const needsSource = (s: CompiledSchemaInfo): boolean =>\n zodCompat || s.refEntries.length > 0 || s.codegenResult.usesRetainedSchema === true;\n const schemasNeedingImport = schemas.filter(needsSource);\n\n const exports = schemas.map((schema) => {\n const needsImport = needsSource(schema);\n // `compile()` returns the schema augmented with a non-enumerable `schema`\n // self-reference; auto-discovery (the default) instead finds plain exported\n // Zod schemas, which have no such property. Both shapes must resolve, so\n // fall through to the export itself — reading `.schema` alone yields\n // undefined there, which silently cost __zcMkv the real schema to augment\n // and hard-crashed every `__rf[]` base at module load.\n const src = `__src_${schema.exportName}`;\n const schemaExpr = needsImport ? `((${src} as any).schema ?? ${src})` : \"\";\n return `export const ${schema.exportName} = ${generateIIFE(schemaExpr, schema, { zodCompat })};`;\n });\n\n const importLine =\n schemasNeedingImport.length > 0\n ? [\n `import { ${schemasNeedingImport.map((s) => `${s.exportName} as __src_${s.exportName}`).join(\", \")} } from \"${importPath}\";`,\n \"\",\n ]\n : [];\n\n return [\n \"// AUTO-GENERATED by zod-compiler — DO NOT EDIT\",\n `// Source: ${sourceRelPath}`,\n \"\",\n ZOD_CONFIG_IMPORT,\n ZOD_MSG_DECLARATION,\n ...(classicFinalizers ? [FAIL_CLASS_DECL] : []),\n MK_VALIDATOR_DECL,\n ...(classicFinalizers ? [FIN_DECL, FIN_DEFERRED_DECL] : []),\n ...(usesCore ? [FAIL_CORE_CLASS_DECL, FIN_CORE_DECL, FIN_DEFERRED_CORE_DECL] : []),\n ...(usesFinZ ? [FAILZ_CLASS_DECL, FINZ_DECL] : []),\n ...(options?.sharedCode ? [\"\", options.sharedCode] : []),\n \"\",\n ...importLine,\n ...exports,\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * Resolve the output file path for a given input file.\n */\nexport function resolveOutputPath(inputPath: string, outputFlag: string | undefined): string {\n if (outputFlag) {\n // If output flag ends with / or is a directory, put the compiled file inside it\n if (outputFlag.endsWith(\"/\") || outputFlag.endsWith(path.sep)) {\n const baseName = path.basename(inputPath, path.extname(inputPath));\n return path.join(outputFlag, `${baseName}.compiled.ts`);\n }\n return outputFlag;\n }\n\n // Default: replace .ts with .compiled.ts\n const dir = path.dirname(inputPath);\n const baseName = path.basename(inputPath, path.extname(inputPath));\n return path.join(dir, `${baseName}.compiled.ts`);\n}\n\n/**\n * Write the compiled file content to disk.\n */\nexport async function writeCompiledFile(outputPath: string, content: string): Promise<void> {\n const dir = path.dirname(outputPath);\n await fs.promises.mkdir(dir, { recursive: true });\n await fs.promises.writeFile(outputPath, content, \"utf-8\");\n}\n"],"mappings":";;;;AAoCA,SAAgB,4BACd,SACA,eACA,SACQ;CACR,IAAI,aAAa,cAAc,QAAQ,mBAAmB,EAAE;CAC5D,IAAI,CAAC,WAAW,WAAW,GAAG,GAC5B,aAAa,KAAK;CAGpB,MAAM,YAAY,SAAS,cAAc;CAIzC,MAAM,WAAW,QAAQ,MAAM,MAAM,EAAE,cAAc,YAAY,IAAI,UAAU,CAAC;CAMhF,MAAM,WAAW,QAAQ,MAAM,MAAM,eAAe,EAAE,cAAc,WAAW,CAAC;CAKhF,MAAM,oBAJc,QAAQ,MACzB,MACC,EAAE,cAAc,YAAY,IAAI,SAAS,KAAK,EAAE,cAAc,YAAY,IAAI,UAAU,CAExD,KAAK,CAAC;CAU1C,MAAM,eAAe,MACnB,aAAa,EAAE,WAAW,SAAS,KAAK,EAAE,cAAc,uBAAuB;CACjF,MAAM,uBAAuB,QAAQ,OAAO,WAAW;CAEvD,MAAM,UAAU,QAAQ,KAAK,WAAW;EACtC,MAAM,cAAc,YAAY,MAAM;EAOtC,MAAM,MAAM,SAAS,OAAO;EAC5B,MAAM,aAAa,cAAc,KAAK,IAAI,qBAAqB,IAAI,KAAK;EACxE,OAAO,gBAAgB,OAAO,WAAW,KAAK,aAAa,YAAY,QAAQ,EAAE,UAAU,CAAC,EAAE;CAChG,CAAC;CAED,MAAM,aACJ,qBAAqB,SAAS,IAC1B,CACE,YAAY,qBAAqB,KAAK,MAAM,GAAG,EAAE,WAAW,YAAY,EAAE,YAAY,CAAC,CAAC,KAAK,IAAI,EAAE,WAAW,WAAW,KACzH,EACF,IACA,CAAC;CAEP,OAAO;EACL;EACA,cAAc;EACd;EACA;EACA;EACA,GAAI,oBAAoB,CAAC,eAAe,IAAI,CAAC;EAC7C;EACA,GAAI,oBAAoB,CAAC,UAAU,iBAAiB,IAAI,CAAC;EACzD,GAAI,WAAW;GAAC;GAAsB;GAAe;EAAsB,IAAI,CAAC;EAChF,GAAI,WAAW,CAAC,kBAAkB,SAAS,IAAI,CAAC;EAChD,GAAI,SAAS,aAAa,CAAC,IAAI,QAAQ,UAAU,IAAI,CAAC;EACtD;EACA,GAAG;EACH,GAAG;EACH;CACF,CAAC,CAAC,KAAK,IAAI;AACb;;;;AAKA,SAAgB,kBAAkB,WAAmB,YAAwC;CAC3F,IAAI,YAAY;EAEd,IAAI,WAAW,SAAS,GAAG,KAAK,WAAW,SAAS,KAAK,GAAG,GAAG;GAC7D,MAAM,WAAW,KAAK,SAAS,WAAW,KAAK,QAAQ,SAAS,CAAC;GACjE,OAAO,KAAK,KAAK,YAAY,GAAG,SAAS,aAAa;EACxD;EACA,OAAO;CACT;CAGA,MAAM,MAAM,KAAK,QAAQ,SAAS;CAClC,MAAM,WAAW,KAAK,SAAS,WAAW,KAAK,QAAQ,SAAS,CAAC;CACjE,OAAO,KAAK,KAAK,KAAK,GAAG,SAAS,aAAa;AACjD;;;;AAKA,eAAsB,kBAAkB,YAAoB,SAAgC;CAC1F,MAAM,MAAM,KAAK,QAAQ,UAAU;CACnC,MAAM,GAAG,SAAS,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;CAChD,MAAM,GAAG,SAAS,UAAU,YAAY,SAAS,OAAO;AAC1D"}
@@ -1 +1 @@
1
- {"version":3,"file":"build-path.d.ts","names":[],"sources":["../../../src/core/codegen/build-path.ts"],"mappings":";;;;iBAuMgB,eAAe,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiCnB,kBAAkB,IAAI;;;;;;;;;;;iBAmFtB,sBAAsB,IAAI;;;;;;iBAmD1B,cAAc,IAAI,UAAU,KAAK"}
1
+ {"version":3,"file":"build-path.d.ts","names":[],"sources":["../../../src/core/codegen/build-path.ts"],"mappings":";;;;iBAwMgB,eAAe,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiCnB,kBAAkB,IAAI;;;;;;;;;;;iBA+EtB,sBAAsB,IAAI;;;;;;iBAmD1B,cAAc,IAAI,UAAU,KAAK"}
@@ -5,7 +5,7 @@ import { detectUnionDiscriminator } from "./schemas/discriminated-union.js";
5
5
  import { estimateFastCost, orderByRuntimeCost, predictedInlineSize } from "./fast-size.js";
6
6
  import { parsedProperties } from "./schemas/object.js";
7
7
  import { innerAppliesDefaultOnUndefined } from "./schemas/optional.js";
8
- import { fastStringCheck } from "./schemas/string.js";
8
+ import { buildUrlCheck, fastStringCheck, isUrlRewrite } from "./schemas/string.js";
9
9
  import { emitStringBoolMap, stringBoolInlineHit, stringBoolUsesInline } from "./schemas/string-bool.js";
10
10
  import { createFastGen, generateFast } from "./fast-path.js";
11
11
  //#region src/core/codegen/build-path.ts
@@ -40,7 +40,7 @@ function rebuildSet(root, includeProtoScrub = true) {
40
40
  for (const node of nodes) {
41
41
  if (rebuilds.has(node)) continue;
42
42
  const target = node.type === "recursiveRef" ? targets.get(node.refId ?? 0) : void 0;
43
- if (node.type === "object" && node.stripUnknownKeys === true || node.type === "readonly" && node.freeze === true || node.type === "default" || node.type === "tuple" && tupleRewritesShortInput(node) || includeProtoScrub && needsProtoScrub(node) || node.type === "stringBool" || node.type === "string" && (node.coerce === true || node.checks.some((c) => c.kind === "overwrite_effect")) || (node.type === "number" || node.type === "boolean" || node.type === "bigint" || node.type === "date") && node.coerce === true || node.type === "effect" || target !== void 0 && rebuilds.has(target) || children(node).some((child) => rebuilds.has(child))) {
43
+ if (node.type === "object" && node.stripUnknownKeys === true || node.type === "readonly" && node.freeze === true || node.type === "default" || node.type === "tuple" && tupleRewritesShortInput(node) || includeProtoScrub && needsProtoScrub(node) || node.type === "stringBool" || node.type === "string" && (node.coerce === true || node.checks.some((c) => c.kind === "overwrite_effect" || isUrlRewrite(c))) || (node.type === "number" || node.type === "boolean" || node.type === "bigint" || node.type === "date") && node.coerce === true || node.type === "effect" || target !== void 0 && rebuilds.has(target) || children(node).some((child) => rebuilds.has(child))) {
44
44
  rebuilds.add(node);
45
45
  changed = true;
46
46
  }
@@ -95,7 +95,7 @@ function fastResultIsInput(ir) {
95
95
  }
96
96
  /**
97
97
  * True when the subtree mutates for any reason the build pass cannot reproduce —
98
- * `.catch()`, `z.url()`, `superRefine`. Those rewrite values in ways this pass
98
+ * `.catch()`, `superRefine`. Those rewrite values in ways this pass
99
99
  * (which validates, coerces, decodes string booleans, substitutes declared
100
100
  * defaults, applies ordered string rewrites and copies) does not model, so the
101
101
  * schema keeps the eager walk.
@@ -110,7 +110,7 @@ function mutatesBeyondStrip(ir) {
110
110
  */
111
111
  function mutatesHere(ir) {
112
112
  switch (ir.type) {
113
- case "string": return superRefines(ir.checks) || ir.checks.some((c) => c.kind === "string_format" && c.format === "url");
113
+ case "string": return superRefines(ir.checks);
114
114
  case "number": return superRefines(ir.checks);
115
115
  case "boolean":
116
116
  case "bigint":
@@ -181,6 +181,9 @@ function generateBuild(ir, ctx) {
181
181
  temps: [],
182
182
  used: 0
183
183
  };
184
+ const name = emitTemp(ctx, "vb");
185
+ const recNames = ctx.buildRecNames ??= /* @__PURE__ */ new Map();
186
+ recNames.set(0, name);
184
187
  const built = build(ir, "input", {
185
188
  ctx,
186
189
  extractable: false,
@@ -188,8 +191,10 @@ function generateBuild(ir, ctx) {
188
191
  rebuilds,
189
192
  scope
190
193
  });
191
- if (built === null) return null;
192
- const name = emitTemp(ctx, "vb");
194
+ if (built === null) {
195
+ recNames.delete(0);
196
+ return null;
197
+ }
193
198
  ctx.preamble.push(`function ${name}(input){${declareFastTemps(scope)}${built.code}return ${built.value};}`);
194
199
  return name;
195
200
  }
@@ -731,6 +736,10 @@ function buildString(ir, input, g) {
731
736
  break;
732
737
  case "super_refine_effect": return null;
733
738
  default: {
739
+ if (check.kind === "string_format" && isUrlRewrite(check)) {
740
+ code += buildUrlCheck(check, value, g.fail, (prefix) => local(g, prefix), g.ctx);
741
+ break;
742
+ }
734
743
  const expr = fastStringCheck(check, value, g.ctx);
735
744
  if (expr === null) return null;
736
745
  code += `if(!(${expr}))return ${g.fail};`;