zod-compiler 1.27.0 → 2.0.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 (118) hide show
  1. package/README.md +95 -53
  2. package/dist/core/codegen/build-path.d.ts +12 -1
  3. package/dist/core/codegen/build-path.d.ts.map +1 -1
  4. package/dist/core/codegen/build-path.js +47 -9
  5. package/dist/core/codegen/build-path.js.map +1 -1
  6. package/dist/core/codegen/context.d.ts +31 -16
  7. package/dist/core/codegen/context.d.ts.map +1 -1
  8. package/dist/core/codegen/context.js +43 -18
  9. package/dist/core/codegen/context.js.map +1 -1
  10. package/dist/core/codegen/emit-issue.d.ts +17 -20
  11. package/dist/core/codegen/emit-issue.d.ts.map +1 -1
  12. package/dist/core/codegen/emit-issue.js +13 -13
  13. package/dist/core/codegen/emit-issue.js.map +1 -1
  14. package/dist/core/codegen/index.d.ts.map +1 -1
  15. package/dist/core/codegen/index.js +9 -7
  16. package/dist/core/codegen/index.js.map +1 -1
  17. package/dist/core/codegen/issue-decls.d.ts +41 -7
  18. package/dist/core/codegen/issue-decls.d.ts.map +1 -1
  19. package/dist/core/codegen/issue-decls.js +49 -9
  20. package/dist/core/codegen/issue-decls.js.map +1 -1
  21. package/dist/core/codegen/schemas/array.js +1 -1
  22. package/dist/core/codegen/schemas/array.js.map +1 -1
  23. package/dist/core/codegen/schemas/bigint.d.ts.map +1 -1
  24. package/dist/core/codegen/schemas/bigint.js +15 -4
  25. package/dist/core/codegen/schemas/bigint.js.map +1 -1
  26. package/dist/core/codegen/schemas/catch.d.ts.map +1 -1
  27. package/dist/core/codegen/schemas/catch.js +3 -2
  28. package/dist/core/codegen/schemas/catch.js.map +1 -1
  29. package/dist/core/codegen/schemas/discriminated-union.d.ts.map +1 -1
  30. package/dist/core/codegen/schemas/discriminated-union.js +2 -1
  31. package/dist/core/codegen/schemas/discriminated-union.js.map +1 -1
  32. package/dist/core/codegen/schemas/effect.d.ts.map +1 -1
  33. package/dist/core/codegen/schemas/effect.js +4 -2
  34. package/dist/core/codegen/schemas/effect.js.map +1 -1
  35. package/dist/core/codegen/schemas/file.js +2 -8
  36. package/dist/core/codegen/schemas/file.js.map +1 -1
  37. package/dist/core/codegen/schemas/number.js +4 -3
  38. package/dist/core/codegen/schemas/number.js.map +1 -1
  39. package/dist/core/codegen/schemas/object.d.ts +11 -1
  40. package/dist/core/codegen/schemas/object.d.ts.map +1 -1
  41. package/dist/core/codegen/schemas/object.js +58 -14
  42. package/dist/core/codegen/schemas/object.js.map +1 -1
  43. package/dist/core/codegen/schemas/readonly.d.ts.map +1 -1
  44. package/dist/core/codegen/schemas/readonly.js.map +1 -1
  45. package/dist/core/codegen/schemas/record.d.ts.map +1 -1
  46. package/dist/core/codegen/schemas/record.js +8 -3
  47. package/dist/core/codegen/schemas/record.js.map +1 -1
  48. package/dist/core/codegen/schemas/sizeable.d.ts +49 -3
  49. package/dist/core/codegen/schemas/sizeable.d.ts.map +1 -1
  50. package/dist/core/codegen/schemas/sizeable.js +101 -8
  51. package/dist/core/codegen/schemas/sizeable.js.map +1 -1
  52. package/dist/core/codegen/schemas/string.d.ts.map +1 -1
  53. package/dist/core/codegen/schemas/string.js +45 -16
  54. package/dist/core/codegen/schemas/string.js.map +1 -1
  55. package/dist/core/codegen/schemas/tuple.d.ts +14 -11
  56. package/dist/core/codegen/schemas/tuple.d.ts.map +1 -1
  57. package/dist/core/codegen/schemas/tuple.js +99 -49
  58. package/dist/core/codegen/schemas/tuple.js.map +1 -1
  59. package/dist/core/codegen/well-known-regex.d.ts +7 -0
  60. package/dist/core/codegen/well-known-regex.d.ts.map +1 -1
  61. package/dist/core/codegen/well-known-regex.js +10 -3
  62. package/dist/core/codegen/well-known-regex.js.map +1 -1
  63. package/dist/core/extract/checks.d.ts.map +1 -1
  64. package/dist/core/extract/checks.js +1 -0
  65. package/dist/core/extract/checks.js.map +1 -1
  66. package/dist/core/extract/extractors/intersection.js +21 -6
  67. package/dist/core/extract/extractors/intersection.js.map +1 -1
  68. package/dist/core/extract/extractors/object.d.ts.map +1 -1
  69. package/dist/core/extract/extractors/object.js +25 -9
  70. package/dist/core/extract/extractors/object.js.map +1 -1
  71. package/dist/core/extract/extractors/optional.d.ts.map +1 -1
  72. package/dist/core/extract/extractors/optional.js +1 -28
  73. package/dist/core/extract/extractors/optional.js.map +1 -1
  74. package/dist/core/extract/extractors/record.js +9 -3
  75. package/dist/core/extract/extractors/record.js.map +1 -1
  76. package/dist/core/extract/extractors/tuple.d.ts.map +1 -1
  77. package/dist/core/extract/extractors/tuple.js +16 -20
  78. package/dist/core/extract/extractors/tuple.js.map +1 -1
  79. package/dist/core/extract/types.d.ts +16 -8
  80. package/dist/core/extract/types.d.ts.map +1 -1
  81. package/dist/core/iife.d.ts +2 -1
  82. package/dist/core/iife.d.ts.map +1 -1
  83. package/dist/core/iife.js +2 -1
  84. package/dist/core/iife.js.map +1 -1
  85. package/dist/core/types.d.ts +70 -21
  86. package/dist/core/types.d.ts.map +1 -1
  87. package/dist/jit.d.ts.map +1 -1
  88. package/dist/jit.js +47 -12
  89. package/dist/jit.js.map +1 -1
  90. package/dist/runtime.d.ts +3 -1
  91. package/dist/runtime.js +12 -10
  92. package/dist/swc.d.ts +5 -1
  93. package/dist/swc.d.ts.map +1 -1
  94. package/dist/swc.js.map +1 -1
  95. package/dist/turbopack.d.ts +6 -1
  96. package/dist/turbopack.d.ts.map +1 -1
  97. package/dist/turbopack.js.map +1 -1
  98. package/dist/unplugin/disk-cache.d.ts.map +1 -1
  99. package/dist/unplugin/disk-cache.js +1 -0
  100. package/dist/unplugin/disk-cache.js.map +1 -1
  101. package/dist/unplugin/index.d.ts.map +1 -1
  102. package/dist/unplugin/index.js +56 -9
  103. package/dist/unplugin/index.js.map +1 -1
  104. package/dist/unplugin/pool-worker.d.ts +1 -0
  105. package/dist/unplugin/pool-worker.js +116 -0
  106. package/dist/unplugin/pool-worker.js.map +1 -0
  107. package/dist/unplugin/pool.d.ts +162 -0
  108. package/dist/unplugin/pool.d.ts.map +1 -0
  109. package/dist/unplugin/pool.js +445 -0
  110. package/dist/unplugin/pool.js.map +1 -0
  111. package/dist/unplugin/transform.d.ts +2 -1
  112. package/dist/unplugin/transform.d.ts.map +1 -1
  113. package/dist/unplugin/transform.js +1 -1
  114. package/dist/unplugin/transform.js.map +1 -1
  115. package/dist/unplugin/types.d.ts +36 -1
  116. package/dist/unplugin/types.d.ts.map +1 -1
  117. package/dist/unplugin/types.js.map +1 -1
  118. package/package.json +2 -2
package/dist/runtime.js CHANGED
@@ -8,7 +8,7 @@ export function __zcFinD(f,inp){return new __ZcFail(null,f,inp);}
8
8
  export function __zcFinZ(z,r,i){return new __ZcFailZ(z,r,i);}
9
9
  function __zcSrRun(f,p){var r=f(p);if(r&&typeof r.then==="function"){throw new __zcCore.$ZodAsyncError();}}
10
10
  export function __zcTS(m,o,i,inp,p,msg){var r={origin:o,code:"too_small",minimum:m,inclusive:i,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}
11
- export function __zcTSn(m,o,inp,p,msg){var r={code:"too_small",minimum:m,origin:o,input:inp,path:p,continue:false};if(msg!==undefined)r.message=msg;return r;}
11
+ export function __zcTSt(m,o,inp,p,msg){var r={code:"too_small",minimum:m,inclusive:true,origin:o,input:inp,path:p,continue:false};if(msg!==undefined)r.message=msg;return r;}
12
12
  export function __zcTSx(m,o,inp,p,msg){var r={origin:o,code:"too_small",minimum:m,inclusive:true,exact:true,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}
13
13
  export function __zcTB(m,o,i,inp,p,msg){var r={origin:o,code:"too_big",maximum:m,inclusive:i,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}
14
14
  export function __zcTBt(m,o,inp,p,msg){var r={code:"too_big",maximum:m,inclusive:true,origin:o,input:inp,path:p,continue:false};if(msg!==undefined)r.message=msg;return r;}
@@ -18,26 +18,28 @@ export function __zcITc(e,inp,p,msg){var r={code:"invalid_type",expected:e,input
18
18
  export function __zcIF(o,f,inp,p,extra,msg){var r=o===undefined?{code:"invalid_format",format:f}:{origin:o,code:"invalid_format",format:f};if(extra)Object.assign(r,extra);r.input=inp;r.path=p;if(msg!==undefined)r.message=msg;return r;}
19
19
  export function __zcIV(values,inp,p,extra,msg){var r={code:"invalid_value"};if(extra)Object.assign(r,extra);r.values=values;r.input=inp;r.path=p;if(msg!==undefined)r.message=msg;return r;}
20
20
  export function __zcUK(k,inp,p,msg){var r={code:"unrecognized_keys",keys:k,input:inp,path:p};if(msg!==undefined)r.message=msg;return r;}
21
- export function __zcAb(e,i){for(;i<e.length;i++){var s=e[i];if(s.continue===false)return true;var c=s.code;if(c==="invalid_type"||c==="invalid_value"||c==="invalid_union"||c==="unrecognized_keys"||c==="invalid_key"||c==="invalid_element")return true;}return false;}
22
- export function __zcFsr(v,s){var vd=((""+v).split(".")[1]||"").length;var ss=""+s;var sd=(ss.split(".")[1]||"").length;if(sd===0&&/\d?e-\d?/.test(ss)){var m=ss.match(/\d?e-(\d?)/);if(m&&m[1]){sd=parseInt(m[1],10);}}var d=vd>sd?vd:sd;var vi=parseInt(v.toFixed(d).replace(".",""),10);var si=parseInt(s.toFixed(d).replace(".",""),10);return (vi%si)/Math.pow(10,d);}
21
+ export function __zcAb(e,i){for(;i<e.length;i++){var s=e[i];if(s.continue===false)return true;var c=s.code;if(c==="invalid_type"||c==="invalid_value"||c==="invalid_union"||c==="invalid_key"||c==="invalid_element")return true;}return false;}
22
+ export function __zcFsr(v,s){var r=v/s;var q=Math.round(r);var t=4*Number.EPSILON*Math.max(Math.abs(r),1);return Math.abs(r-q)<t?0:r-q;}
23
23
  export function __zcFz(e){for(var i=0;i<e.length;i++){var s=e[i];if(s.message===undefined&&typeof __zcMsg==="function")s.message=__zcMsg(s);delete s.input;delete s.continue;}return e;}
24
24
  export const __zcHop=Object.prototype.hasOwnProperty;
25
25
  export function __zcLo(v){return Array.isArray(v)?"array":typeof v==="string"?"string":"unknown";}
26
26
  export function __zcSo(v){return v instanceof Set?"set":v instanceof Map?"map":(typeof File!=="undefined"&&v instanceof File)?"file":"unknown";}
27
+ export function __zcCpl(s){var n=s.length;if(!/[\uD800-\uDBFF]/.test(s))return n;var c=n;for(var i=0;i<n-1;i++){if((s.charCodeAt(i)&0xfc00)===0xd800&&(s.charCodeAt(i+1)&0xfc00)===0xdc00){c--;i++;}}return c;}
28
+ export function __zcPs(o){if(!Object.prototype.hasOwnProperty.call(o,"__proto__"))return o;var c={...o};delete c["__proto__"];return c;}
27
29
  export function __zcPlain(o){if(typeof o!=="object"||o===null||Array.isArray(o))return false;var c=o.constructor;if(c===undefined||typeof c!=="function")return true;var p=c.prototype;if(typeof p!=="object"||p===null||Array.isArray(p))return false;return Object.prototype.hasOwnProperty.call(p,"isPrototypeOf");}
28
30
  export function __zcPfx(d,s,b,k){for(var i=0;i<s.length;i++){var x=s[i];x.path=b.concat(k,x.path);d.push(x);}}
29
31
  export function __zcCu(f,v){var r=f(v);if(r&&typeof r.then==="function"){throw new __zcCore.$ZodAsyncError();}return !!r;}
30
- export function __zcSr(f,v,p,e){var q={value:v,issues:[]};__zcSrRun(f,q);for(var i=0;i<q.issues.length;i++){var s=q.issues[i],t={};for(var k in s){if(k!=="inst"&&k!=="continue")t[k]=s[k];}if(s.continue!==true)q.aborted=true;t.path=s.path&&s.path.length?p.concat(s.path):p;e.push(t);}return q;}
32
+ export function __zcSr(f,v,p,e,m){var q={value:v,issues:[]};__zcSrRun(f,q);for(var i=0;i<q.issues.length;i++){var s=q.issues[i],t={};for(var k in s){if(k!=="inst"&&k!=="continue")t[k]=s[k];}if(s.continue!==true)q.aborted=true;t.path=s.path&&s.path.length?p.concat(s.path):p;if(t.message===undefined&&m!==undefined)t.message=m;e.push(t);}return q;}
31
33
  export function __zcSrOk(f,v){var p={value:v,issues:[]};__zcSrRun(f,p);return p.issues.length===0&&p.value===v;}
32
34
  export const __zcReEmail=new RegExp("^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$");
33
35
  export const __zcReEmailSrc="/^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$/";
34
36
  export const __zcReUuid=new RegExp("^([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$");
35
37
  export const __zcReUuidSrc="/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/";
36
- export const __zcReCuid=new RegExp("^[cC][^\\s-][^\\s-][^\\s-][^\\s-][^\\s-][^\\s-][^\\s-][^\\s-][^\\s-]*$");
37
- export const __zcReCuidSrc="/^[cC][^\\s-]{8,}$/";
38
+ export const __zcReCuid=new RegExp("^[cC][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z][0-9a-z]*$");
39
+ export const __zcReCuidSrc="/^[cC][0-9a-z]{6,}$/";
38
40
  export const __zcReCuid2=new RegExp("^[0-9a-z]+$");
39
- export const __zcReUlid=new RegExp("^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]$");
40
- export const __zcReUlidSrc="/^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/";
41
+ export const __zcReUlid=new RegExp("^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]$");
42
+ export const __zcReUlidSrc="/^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$/";
41
43
  export const __zcReNanoid=new RegExp("^[a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-][a-zA-Z0-9_-]$");
42
44
  export const __zcReNanoidSrc="/^[a-zA-Z0-9_-]{21}$/";
43
45
  export const __zcReXid=new RegExp("^[0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V][0-9a-vA-V]$");
@@ -56,6 +58,6 @@ export const __zcReGuidSrc="/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9
56
58
  export const __zcReIsoDate=new RegExp("^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d\\d\\d\\d-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$");
57
59
  export const __zcReIsoDateSrc="/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$/";
58
60
  export const __zcReIsoTime=new RegExp("^(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?$");
59
- export const __zcReIsoDateTime=new RegExp("^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d\\d\\d\\d-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$");
60
- export const __zcReIsoDateTimeSrc="/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$/";
61
+ export const __zcReIsoDateTime=new RegExp("^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d\\d\\d\\d-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$");
62
+ export const __zcReIsoDateTimeSrc="/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$/";
61
63
  export const __zcReIsoDuration=new RegExp("^P(?:(\\d+W)|(?!.*W)(?=\\d|T\\d)(\\d+Y)?(\\d+M)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+([.,]\\d+)?S)?)?)$");
package/dist/swc.d.ts CHANGED
@@ -26,8 +26,12 @@ interface SwcCoreLike {
26
26
  * persistent cache, so hosts that need one must key transform results on
27
27
  * content themselves. `include`/`exclude` are honored: files they reject
28
28
  * pass through to SWC without the zod-compiler step.
29
+ *
30
+ * No `parallel` either: this is a per-file transformer, so whatever drives it
31
+ * decides how many files run at once. A pool owned by a single `transform()`
32
+ * call would compete with that instead of adding to it.
29
33
  */
30
- type ZodCompilerSwcOptions = Omit<ZodCompilerPluginOptions, "apply" | "cache" | "codegenMode"> & {
34
+ type ZodCompilerSwcOptions = Omit<ZodCompilerPluginOptions, "apply" | "cache" | "codegenMode" | "parallel"> & {
31
35
  /**
32
36
  * SWC is a transformer, not a bundler plugin host, so inline is the safe
33
37
  * default. Lean mode may emit virtual runtime imports that SWC cannot resolve
package/dist/swc.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"swc.d.ts","names":[],"sources":["../src/swc.ts"],"mappings":";;;;;;;;UAYiB;EACf;EACA;EACA;GACC;;UAGc;EACf;EACA;GACC;;UAGc;EACf,UAAU,cAAc,UAAU,aAAa,QAAQ;;;;;;;;;KAU7C,wBAAwB,KAClC;;;;;;EAQA;;UAGe;;EAEf,MAAM;;EAEN,cAAc;;UAGC,kCAAkC;;EAEjD;;UAGe;EACf,UAAU,cAAc,SAAS,4BAA4B,QAAQ;EACrE,cAAc,kBAAkB,UAAU,oBAAoB,QAAQ;;iBAoFlD,UACpB,cACA,SAAS,4BACR,QAAQ;iBAIW,cACpB,kBACA,UAAU,oBACT,QAAQ;iBAKK,kBAAkB,WAAW,oBAAoB;;;;;iBAgB3C,iBACpB,KAAK,aACL,cACA,SAAS,4BACR,QAAQ;iBAIa,YAAY,WAAW,oBAAoB"}
1
+ {"version":3,"file":"swc.d.ts","names":[],"sources":["../src/swc.ts"],"mappings":";;;;;;;;UAYiB;EACf;EACA;EACA;GACC;;UAGc;EACf;EACA;GACC;;UAGc;EACf,UAAU,cAAc,UAAU,aAAa,QAAQ;;;;;;;;;;;;;KAc7C,wBAAwB,KAClC;;;;;;EAQA;;UAGe;;EAEf,MAAM;;EAEN,cAAc;;UAGC,kCAAkC;;EAEjD;;UAGe;EACf,UAAU,cAAc,SAAS,4BAA4B,QAAQ;EACrE,cAAc,kBAAkB,UAAU,oBAAoB,QAAQ;;iBAoFlD,UACpB,cACA,SAAS,4BACR,QAAQ;iBAIW,cACpB,kBACA,UAAU,oBACT,QAAQ;iBAKK,kBAAkB,WAAW,oBAAoB;;;;;iBAgB3C,iBACpB,KAAK,aACL,cACA,SAAS,4BACR,QAAQ;iBAIa,YAAY,WAAW,oBAAoB"}
package/dist/swc.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"swc.js","names":[],"sources":["../src/swc.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { invalidateModuleCache } from \"./loader.js\";\nimport { shouldTransform, transformCodeWithMap } from \"./unplugin/transform.js\";\nimport type { TransformOptions, ZodCompilerPluginOptions } from \"./unplugin/types.js\";\n\n/**\n * Minimal @swc/core option shape. zod-compiler keeps @swc/core optional so\n * non-SWC users do not install a native dependency just by using the package.\n * `inputSourceMap` matches @swc/core: a JSON string or a boolean — swc does\n * not accept map objects.\n */\nexport interface SwcOptions {\n filename?: string | undefined;\n inputSourceMap?: boolean | string | undefined;\n sourceMaps?: boolean | \"inline\" | undefined;\n [key: string]: unknown;\n}\n\nexport interface SwcOutput {\n code: string;\n map?: string | undefined;\n [key: string]: unknown;\n}\n\nexport interface SwcCoreLike {\n transform(code: string, options?: SwcOptions): Promise<SwcOutput>;\n}\n\n/**\n * Plugin options that make sense for a transformer host. `apply` is Vite\n * lifecycle and `cache` is the bundler disk cache — the bridge keeps no\n * persistent cache, so hosts that need one must key transform results on\n * content themselves. `include`/`exclude` are honored: files they reject\n * pass through to SWC without the zod-compiler step.\n */\nexport type ZodCompilerSwcOptions = Omit<\n ZodCompilerPluginOptions,\n \"apply\" | \"cache\" | \"codegenMode\"\n> & {\n /**\n * SWC is a transformer, not a bundler plugin host, so inline is the safe\n * default. Lean mode may emit virtual runtime imports that SWC cannot resolve\n * unless another tool handles them after SWC.\n */\n codegenMode?: \"inline\" | \"lean\" | undefined;\n};\n\nexport interface SwcBridgeDefaults {\n /** Options passed through to @swc/core.transform. */\n swc?: SwcOptions | undefined;\n /** zod-compiler options. Defaults match the build plugins except codegenMode is \"inline\". */\n zodCompiler?: ZodCompilerSwcOptions | undefined;\n}\n\nexport interface SwcBridgeTransformOptions extends SwcBridgeDefaults {\n /** Absolute or project-relative filename for schema discovery and SWC config resolution. */\n filename: string;\n}\n\nexport interface SwcBridge {\n transform(code: string, options: SwcBridgeTransformOptions): Promise<SwcOutput>;\n transformFile(filename: string, options?: SwcBridgeDefaults): Promise<SwcOutput>;\n}\n\nfunction toTransformOptions(options?: ZodCompilerSwcOptions): TransformOptions {\n const output = options?.output ?? \"schema\";\n return {\n mode: options?.codegenMode ?? \"inline\",\n verbose: options?.verbose,\n zodCompat: output === \"schema\" || output === \"compact\",\n compact: output === \"compact\",\n autoDiscover: (options?.schemas ?? \"auto\") === \"auto\",\n hoist: options?.hoist,\n };\n}\n\n/**\n * Per-call options win key-by-key over factory defaults. The merge is\n * shallow: a per-call `swc.jsc` replaces the default `jsc` wholesale rather\n * than deep-merging parser/target settings.\n */\nfunction mergeOptions(\n defaults: SwcBridgeDefaults | undefined,\n options: SwcBridgeTransformOptions,\n): SwcBridgeTransformOptions {\n return {\n filename: options.filename,\n swc: { ...defaults?.swc, ...options.swc },\n zodCompiler: { ...defaults?.zodCompiler, ...options.zodCompiler },\n };\n}\n\nasync function loadSwc(): Promise<SwcCoreLike> {\n try {\n return (await import(\"@swc/core\")) as unknown as SwcCoreLike;\n } catch (error) {\n const cause = error instanceof Error ? `: ${error.message}` : \"\";\n throw new Error(\n `zod-compiler/swc requires @swc/core to be installed by the consuming project${cause}`,\n );\n }\n}\n\n/**\n * Last content seen per filename. Discovery executes schema files from DISK\n * through a module cache that outlives transform calls, so when a host (dev\n * server, watch-mode test runner) re-transforms a file with new content, the\n * stale executions must be dropped or the compiled validators keep\n * reflecting the old schema. Same content-diff scheme as the unplugin\n * transform hook; tracked for every file fed to the bridge — an excluded\n * file can still be a dependency a schema file executed.\n */\nconst lastSeenCode = new Map<string, string>();\n\nfunction invalidateOnContentChange(filename: string, code: string): void {\n const key = path.resolve(filename);\n const previous = lastSeenCode.get(key);\n if (previous !== undefined && previous !== code) {\n invalidateModuleCache();\n }\n lastSeenCode.set(key, code);\n}\n\nasync function transformWith(\n swc: SwcCoreLike,\n code: string,\n options: SwcBridgeTransformOptions,\n): Promise<SwcOutput> {\n invalidateOnContentChange(options.filename, code);\n\n const zodResult = shouldTransform(options.filename, options.zodCompiler)\n ? await transformCodeWithMap(code, options.filename, toTransformOptions(options.zodCompiler))\n : null;\n\n const swcOptions: SwcOptions = {\n ...options.swc,\n filename: options.swc?.filename ?? options.filename,\n };\n if (zodResult?.map && swcOptions.inputSourceMap === undefined) {\n swcOptions.inputSourceMap = JSON.stringify(zodResult.map);\n }\n\n return swc.transform(zodResult?.code ?? code, swcOptions);\n}\n\nexport async function transform(\n code: string,\n options: SwcBridgeTransformOptions,\n): Promise<SwcOutput> {\n return transformWith(await loadSwc(), code, options);\n}\n\nexport async function transformFile(\n filename: string,\n options?: SwcBridgeDefaults,\n): Promise<SwcOutput> {\n const code = await fs.readFile(filename, \"utf8\");\n return transform(code, { ...options, filename });\n}\n\nexport function createSwcCompiler(defaults?: SwcBridgeDefaults): SwcBridge {\n return {\n transform(code, options) {\n return transform(code, mergeOptions(defaults, options));\n },\n async transformFile(filename, options) {\n const code = await fs.readFile(filename, \"utf8\");\n return transform(code, mergeOptions(defaults, { ...options, filename }));\n },\n };\n}\n\n/**\n * Test seam for the SWC bridge. It is exported because it is also useful for\n * custom hosts that already own a @swc/core-compatible transform function.\n */\nexport async function transformWithSwc(\n swc: SwcCoreLike,\n code: string,\n options: SwcBridgeTransformOptions,\n): Promise<SwcOutput> {\n return transformWith(swc, code, options);\n}\n\nexport default function zodCompiler(defaults?: SwcBridgeDefaults): SwcBridge {\n return createSwcCompiler(defaults);\n}\n"],"mappings":";;;;;AAiEA,SAAS,mBAAmB,SAAmD;CAC7E,MAAM,SAAS,SAAS,UAAU;CAClC,OAAO;EACL,MAAM,SAAS,eAAe;EAC9B,SAAS,SAAS;EAClB,WAAW,WAAW,YAAY,WAAW;EAC7C,SAAS,WAAW;EACpB,eAAe,SAAS,WAAW,YAAY;EAC/C,OAAO,SAAS;CAClB;AACF;;;;;;AAOA,SAAS,aACP,UACA,SAC2B;CAC3B,OAAO;EACL,UAAU,QAAQ;EAClB,KAAK;GAAE,GAAG,UAAU;GAAK,GAAG,QAAQ;EAAI;EACxC,aAAa;GAAE,GAAG,UAAU;GAAa,GAAG,QAAQ;EAAY;CAClE;AACF;AAEA,eAAe,UAAgC;CAC7C,IAAI;EACF,OAAQ,MAAM,OAAO;CACvB,SAAS,OAAO;EACd,MAAM,QAAQ,iBAAiB,QAAQ,KAAK,MAAM,YAAY;EAC9D,MAAM,IAAI,MACR,+EAA+E,OACjF;CACF;AACF;;;;;;;;;;AAWA,MAAM,+BAAe,IAAI,IAAoB;AAE7C,SAAS,0BAA0B,UAAkB,MAAoB;CACvE,MAAM,MAAM,KAAK,QAAQ,QAAQ;CACjC,MAAM,WAAW,aAAa,IAAI,GAAG;CACrC,IAAI,aAAa,KAAA,KAAa,aAAa,MACzC,sBAAsB;CAExB,aAAa,IAAI,KAAK,IAAI;AAC5B;AAEA,eAAe,cACb,KACA,MACA,SACoB;CACpB,0BAA0B,QAAQ,UAAU,IAAI;CAEhD,MAAM,YAAY,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IACnE,MAAM,qBAAqB,MAAM,QAAQ,UAAU,mBAAmB,QAAQ,WAAW,CAAC,IAC1F;CAEJ,MAAM,aAAyB;EAC7B,GAAG,QAAQ;EACX,UAAU,QAAQ,KAAK,YAAY,QAAQ;CAC7C;CACA,IAAI,WAAW,OAAO,WAAW,mBAAmB,KAAA,GAClD,WAAW,iBAAiB,KAAK,UAAU,UAAU,GAAG;CAG1D,OAAO,IAAI,UAAU,WAAW,QAAQ,MAAM,UAAU;AAC1D;AAEA,eAAsB,UACpB,MACA,SACoB;CACpB,OAAO,cAAc,MAAM,QAAQ,GAAG,MAAM,OAAO;AACrD;AAEA,eAAsB,cACpB,UACA,SACoB;CAEpB,OAAO,UAAU,MADE,GAAG,SAAS,UAAU,MAAM,GACxB;EAAE,GAAG;EAAS;CAAS,CAAC;AACjD;AAEA,SAAgB,kBAAkB,UAAyC;CACzE,OAAO;EACL,UAAU,MAAM,SAAS;GACvB,OAAO,UAAU,MAAM,aAAa,UAAU,OAAO,CAAC;EACxD;EACA,MAAM,cAAc,UAAU,SAAS;GAErC,OAAO,UAAU,MADE,GAAG,SAAS,UAAU,MAAM,GACxB,aAAa,UAAU;IAAE,GAAG;IAAS;GAAS,CAAC,CAAC;EACzE;CACF;AACF;;;;;AAMA,eAAsB,iBACpB,KACA,MACA,SACoB;CACpB,OAAO,cAAc,KAAK,MAAM,OAAO;AACzC;AAEA,SAAwB,YAAY,UAAyC;CAC3E,OAAO,kBAAkB,QAAQ;AACnC"}
1
+ {"version":3,"file":"swc.js","names":[],"sources":["../src/swc.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { invalidateModuleCache } from \"./loader.js\";\nimport { shouldTransform, transformCodeWithMap } from \"./unplugin/transform.js\";\nimport type { TransformOptions, ZodCompilerPluginOptions } from \"./unplugin/types.js\";\n\n/**\n * Minimal @swc/core option shape. zod-compiler keeps @swc/core optional so\n * non-SWC users do not install a native dependency just by using the package.\n * `inputSourceMap` matches @swc/core: a JSON string or a boolean — swc does\n * not accept map objects.\n */\nexport interface SwcOptions {\n filename?: string | undefined;\n inputSourceMap?: boolean | string | undefined;\n sourceMaps?: boolean | \"inline\" | undefined;\n [key: string]: unknown;\n}\n\nexport interface SwcOutput {\n code: string;\n map?: string | undefined;\n [key: string]: unknown;\n}\n\nexport interface SwcCoreLike {\n transform(code: string, options?: SwcOptions): Promise<SwcOutput>;\n}\n\n/**\n * Plugin options that make sense for a transformer host. `apply` is Vite\n * lifecycle and `cache` is the bundler disk cache — the bridge keeps no\n * persistent cache, so hosts that need one must key transform results on\n * content themselves. `include`/`exclude` are honored: files they reject\n * pass through to SWC without the zod-compiler step.\n *\n * No `parallel` either: this is a per-file transformer, so whatever drives it\n * decides how many files run at once. A pool owned by a single `transform()`\n * call would compete with that instead of adding to it.\n */\nexport type ZodCompilerSwcOptions = Omit<\n ZodCompilerPluginOptions,\n \"apply\" | \"cache\" | \"codegenMode\" | \"parallel\"\n> & {\n /**\n * SWC is a transformer, not a bundler plugin host, so inline is the safe\n * default. Lean mode may emit virtual runtime imports that SWC cannot resolve\n * unless another tool handles them after SWC.\n */\n codegenMode?: \"inline\" | \"lean\" | undefined;\n};\n\nexport interface SwcBridgeDefaults {\n /** Options passed through to @swc/core.transform. */\n swc?: SwcOptions | undefined;\n /** zod-compiler options. Defaults match the build plugins except codegenMode is \"inline\". */\n zodCompiler?: ZodCompilerSwcOptions | undefined;\n}\n\nexport interface SwcBridgeTransformOptions extends SwcBridgeDefaults {\n /** Absolute or project-relative filename for schema discovery and SWC config resolution. */\n filename: string;\n}\n\nexport interface SwcBridge {\n transform(code: string, options: SwcBridgeTransformOptions): Promise<SwcOutput>;\n transformFile(filename: string, options?: SwcBridgeDefaults): Promise<SwcOutput>;\n}\n\nfunction toTransformOptions(options?: ZodCompilerSwcOptions): TransformOptions {\n const output = options?.output ?? \"schema\";\n return {\n mode: options?.codegenMode ?? \"inline\",\n verbose: options?.verbose,\n zodCompat: output === \"schema\" || output === \"compact\",\n compact: output === \"compact\",\n autoDiscover: (options?.schemas ?? \"auto\") === \"auto\",\n hoist: options?.hoist,\n };\n}\n\n/**\n * Per-call options win key-by-key over factory defaults. The merge is\n * shallow: a per-call `swc.jsc` replaces the default `jsc` wholesale rather\n * than deep-merging parser/target settings.\n */\nfunction mergeOptions(\n defaults: SwcBridgeDefaults | undefined,\n options: SwcBridgeTransformOptions,\n): SwcBridgeTransformOptions {\n return {\n filename: options.filename,\n swc: { ...defaults?.swc, ...options.swc },\n zodCompiler: { ...defaults?.zodCompiler, ...options.zodCompiler },\n };\n}\n\nasync function loadSwc(): Promise<SwcCoreLike> {\n try {\n return (await import(\"@swc/core\")) as unknown as SwcCoreLike;\n } catch (error) {\n const cause = error instanceof Error ? `: ${error.message}` : \"\";\n throw new Error(\n `zod-compiler/swc requires @swc/core to be installed by the consuming project${cause}`,\n );\n }\n}\n\n/**\n * Last content seen per filename. Discovery executes schema files from DISK\n * through a module cache that outlives transform calls, so when a host (dev\n * server, watch-mode test runner) re-transforms a file with new content, the\n * stale executions must be dropped or the compiled validators keep\n * reflecting the old schema. Same content-diff scheme as the unplugin\n * transform hook; tracked for every file fed to the bridge — an excluded\n * file can still be a dependency a schema file executed.\n */\nconst lastSeenCode = new Map<string, string>();\n\nfunction invalidateOnContentChange(filename: string, code: string): void {\n const key = path.resolve(filename);\n const previous = lastSeenCode.get(key);\n if (previous !== undefined && previous !== code) {\n invalidateModuleCache();\n }\n lastSeenCode.set(key, code);\n}\n\nasync function transformWith(\n swc: SwcCoreLike,\n code: string,\n options: SwcBridgeTransformOptions,\n): Promise<SwcOutput> {\n invalidateOnContentChange(options.filename, code);\n\n const zodResult = shouldTransform(options.filename, options.zodCompiler)\n ? await transformCodeWithMap(code, options.filename, toTransformOptions(options.zodCompiler))\n : null;\n\n const swcOptions: SwcOptions = {\n ...options.swc,\n filename: options.swc?.filename ?? options.filename,\n };\n if (zodResult?.map && swcOptions.inputSourceMap === undefined) {\n swcOptions.inputSourceMap = JSON.stringify(zodResult.map);\n }\n\n return swc.transform(zodResult?.code ?? code, swcOptions);\n}\n\nexport async function transform(\n code: string,\n options: SwcBridgeTransformOptions,\n): Promise<SwcOutput> {\n return transformWith(await loadSwc(), code, options);\n}\n\nexport async function transformFile(\n filename: string,\n options?: SwcBridgeDefaults,\n): Promise<SwcOutput> {\n const code = await fs.readFile(filename, \"utf8\");\n return transform(code, { ...options, filename });\n}\n\nexport function createSwcCompiler(defaults?: SwcBridgeDefaults): SwcBridge {\n return {\n transform(code, options) {\n return transform(code, mergeOptions(defaults, options));\n },\n async transformFile(filename, options) {\n const code = await fs.readFile(filename, \"utf8\");\n return transform(code, mergeOptions(defaults, { ...options, filename }));\n },\n };\n}\n\n/**\n * Test seam for the SWC bridge. It is exported because it is also useful for\n * custom hosts that already own a @swc/core-compatible transform function.\n */\nexport async function transformWithSwc(\n swc: SwcCoreLike,\n code: string,\n options: SwcBridgeTransformOptions,\n): Promise<SwcOutput> {\n return transformWith(swc, code, options);\n}\n\nexport default function zodCompiler(defaults?: SwcBridgeDefaults): SwcBridge {\n return createSwcCompiler(defaults);\n}\n"],"mappings":";;;;;AAqEA,SAAS,mBAAmB,SAAmD;CAC7E,MAAM,SAAS,SAAS,UAAU;CAClC,OAAO;EACL,MAAM,SAAS,eAAe;EAC9B,SAAS,SAAS;EAClB,WAAW,WAAW,YAAY,WAAW;EAC7C,SAAS,WAAW;EACpB,eAAe,SAAS,WAAW,YAAY;EAC/C,OAAO,SAAS;CAClB;AACF;;;;;;AAOA,SAAS,aACP,UACA,SAC2B;CAC3B,OAAO;EACL,UAAU,QAAQ;EAClB,KAAK;GAAE,GAAG,UAAU;GAAK,GAAG,QAAQ;EAAI;EACxC,aAAa;GAAE,GAAG,UAAU;GAAa,GAAG,QAAQ;EAAY;CAClE;AACF;AAEA,eAAe,UAAgC;CAC7C,IAAI;EACF,OAAQ,MAAM,OAAO;CACvB,SAAS,OAAO;EACd,MAAM,QAAQ,iBAAiB,QAAQ,KAAK,MAAM,YAAY;EAC9D,MAAM,IAAI,MACR,+EAA+E,OACjF;CACF;AACF;;;;;;;;;;AAWA,MAAM,+BAAe,IAAI,IAAoB;AAE7C,SAAS,0BAA0B,UAAkB,MAAoB;CACvE,MAAM,MAAM,KAAK,QAAQ,QAAQ;CACjC,MAAM,WAAW,aAAa,IAAI,GAAG;CACrC,IAAI,aAAa,KAAA,KAAa,aAAa,MACzC,sBAAsB;CAExB,aAAa,IAAI,KAAK,IAAI;AAC5B;AAEA,eAAe,cACb,KACA,MACA,SACoB;CACpB,0BAA0B,QAAQ,UAAU,IAAI;CAEhD,MAAM,YAAY,gBAAgB,QAAQ,UAAU,QAAQ,WAAW,IACnE,MAAM,qBAAqB,MAAM,QAAQ,UAAU,mBAAmB,QAAQ,WAAW,CAAC,IAC1F;CAEJ,MAAM,aAAyB;EAC7B,GAAG,QAAQ;EACX,UAAU,QAAQ,KAAK,YAAY,QAAQ;CAC7C;CACA,IAAI,WAAW,OAAO,WAAW,mBAAmB,KAAA,GAClD,WAAW,iBAAiB,KAAK,UAAU,UAAU,GAAG;CAG1D,OAAO,IAAI,UAAU,WAAW,QAAQ,MAAM,UAAU;AAC1D;AAEA,eAAsB,UACpB,MACA,SACoB;CACpB,OAAO,cAAc,MAAM,QAAQ,GAAG,MAAM,OAAO;AACrD;AAEA,eAAsB,cACpB,UACA,SACoB;CAEpB,OAAO,UAAU,MADE,GAAG,SAAS,UAAU,MAAM,GACxB;EAAE,GAAG;EAAS;CAAS,CAAC;AACjD;AAEA,SAAgB,kBAAkB,UAAyC;CACzE,OAAO;EACL,UAAU,MAAM,SAAS;GACvB,OAAO,UAAU,MAAM,aAAa,UAAU,OAAO,CAAC;EACxD;EACA,MAAM,cAAc,UAAU,SAAS;GAErC,OAAO,UAAU,MADE,GAAG,SAAS,UAAU,MAAM,GACxB,aAAa,UAAU;IAAE,GAAG;IAAS;GAAS,CAAC,CAAC;EACzE;CACF;AACF;;;;;AAMA,eAAsB,iBACpB,KACA,MACA,SACoB;CACpB,OAAO,cAAc,KAAK,MAAM,OAAO;AACzC;AAEA,SAAwB,YAAY,UAAyC;CAC3E,OAAO,kBAAkB,QAAQ;AACnC"}
@@ -12,8 +12,13 @@ import { TransformSourceMap } from "./unplugin/transform.js";
12
12
  * `buildEnd` flush that a loader has no equivalent for, and loader hosts keep
13
13
  * their own persistent result cache — Turbopack's is keyed on content plus the
14
14
  * dependencies declared below, which is what this would have re-implemented.
15
+ *
16
+ * No `parallel`: the concurrency is the host's to own. Turbopack already runs
17
+ * loaders across its own worker pool, so a pool per loader invocation would
18
+ * multiply threads against a machine that is already saturated — and the
19
+ * module-cache staleness stamps below assume one shared execution cache.
15
20
  */
16
- type ZodCompilerTurbopackOptions = Omit<ZodCompilerPluginOptions, "apply" | "cache" | "codegenMode" | "hoist"> & {
21
+ type ZodCompilerTurbopackOptions = Omit<ZodCompilerPluginOptions, "apply" | "cache" | "codegenMode" | "hoist" | "parallel"> & {
17
22
  hoist?: boolean | {
18
23
  schemaNamePattern?: string | null | undefined;
19
24
  } | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"turbopack.d.ts","names":[],"sources":["../src/turbopack.ts"],"mappings":";;;;;;;;;;;;;;;KA+DY,8BAA8B,KACxC;EAGA;IAAoB;;;;;;;;;;;;;;;;;;;;EAmBpB;;;;;;;UAQe;EACf;EACA,UAAU,OAAO,cAAc,eAAe,MAAM;EACpD,eAAe;EACf,eAAe;EACf,WAAW;EACX;;iBAwMsB,kBACtB,MAAM,0BACN,gBACA,WAAW"}
1
+ {"version":3,"file":"turbopack.d.ts","names":[],"sources":["../src/turbopack.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;KAoEY,8BAA8B,KACxC;EAGA;IAAoB;;;;;;;;;;;;;;;;;;;;EAmBpB;;;;;;;UAQe;EACf;EACA,UAAU,OAAO,cAAc,eAAe,MAAM;EACpD,eAAe;EACf,eAAe;EACf,WAAW;EACX;;iBAwMsB,kBACtB,MAAM,0BACN,gBACA,WAAW"}
@@ -1 +1 @@
1
- {"version":3,"file":"turbopack.js","names":[],"sources":["../src/turbopack.ts"],"sourcesContent":["/**\n * Webpack-loader entry point, for Turbopack (Next.js) and any other host that\n * runs webpack loaders but not webpack plugins.\n *\n * Turbopack deliberately supports no webpack plugins, so the unplugin build\n * plugins cannot reach it. Loaders it does run, through the real `loader-runner`\n * library, and that is enough: this module is the whole zod-compiler transform\n * behind `this.async()`.\n *\n * It emits TypeScript, not JavaScript. A `turbopack.rules` entry that sets no\n * `as`/`type` leaves the loader's output to be parsed as whatever the file\n * already was, so Turbopack's own SWC pass handles the syntax — there is no\n * second transpile here and no @swc/core dependency.\n *\n * `codegenMode: \"lean\"` is available here — it imports the shared helpers from\n * `zod-compiler/runtime`, a real package subpath, rather than the `virtual:` id\n * the build plugins answer from a resolve hook that a loader does not have. It\n * is opt-in; see the option's doc for why.\n *\n * // next.config.ts\n * export default {\n * turbopack: {\n * rules: {\n * \"*.{ts,tsx}\": {\n * condition: { all: [{ not: \"foreign\" }, { content: /[Zz]od/ }] },\n * loaders: [\"zod-compiler/turbopack\"],\n * },\n * },\n * },\n * };\n *\n * The `content` condition is the Turbopack equivalent of the plugins' own code\n * filter and must stay as loose as ZOD_MENTION: narrowing it to the literal\n * specifier `\"zod\"` would silently skip `zod/v4`, `zod/mini` and the\n * `zod-compiler` import that drives `schemas: \"explicit\"` — no error, those\n * schemas just quietly stay uncompiled.\n */\n\nimport fs from \"node:fs\";\nimport remapping from \"@jridgewell/remapping\";\nimport { getFirstPartyModulePaths, invalidateModuleCache } from \"./loader.js\";\nimport { resetDepGraphMemo, transformDependencies } from \"./unplugin/dep-graph.js\";\nimport {\n log,\n shouldTransform,\n type TransformSourceMap,\n transformCodeWithMap,\n} from \"./unplugin/transform.js\";\nimport type { ZodCompilerPluginOptions } from \"./unplugin/types.js\";\nimport { RUNTIME_PACKAGE_ID } from \"./unplugin/virtual.js\";\n\n/**\n * Plugin options minus the ones a loader host cannot express.\n *\n * Turbopack serializes loader options through `next.config`, so they have to be\n * plain JSON — hence no `apply` (a Vite lifecycle function) and a string-only\n * `schemaNamePattern` where the plugin also accepts a RegExp.\n *\n * No `cache` either: the disk cache's dependency bookkeeping leans on a\n * `buildEnd` flush that a loader has no equivalent for, and loader hosts keep\n * their own persistent result cache — Turbopack's is keyed on content plus the\n * dependencies declared below, which is what this would have re-implemented.\n */\nexport type ZodCompilerTurbopackOptions = Omit<\n ZodCompilerPluginOptions,\n \"apply\" | \"cache\" | \"codegenMode\" | \"hoist\"\n> & {\n hoist?: boolean | { schemaNamePattern?: string | null | undefined } | undefined;\n /**\n * `\"inline\"` (default) emits the shared helpers into every transformed file.\n *\n * `\"lean\"` imports them from `zod-compiler/runtime` instead, so a bundle\n * carries one copy however many files were transformed. That specifier is a\n * real package subpath, which is what makes it usable from a loader at all —\n * the `virtual:` id the build plugins emit needs a `resolveId` hook, and a\n * loader has none.\n *\n * Opt-in rather than the default because it only holds when the host BUNDLES\n * the import. Next.js does for client and App Router server code, but Pages\n * Router server code externalizes node_modules imports unless\n * `bundlePagesRouterDependencies` is set — and `zod-compiler` is normally a\n * devDependency, so a production install prunes it and the route throws\n * ERR_MODULE_NOT_FOUND on the first request. A bigger bundle is the better\n * default than a runtime failure that no build step reports.\n * @default \"inline\"\n */\n codegenMode?: \"lean\" | \"inline\" | undefined;\n};\n\n/**\n * The slice of webpack's loader context this uses. Structural rather than\n * imported from webpack: the package must not take a webpack dependency to\n * serve a host that is not webpack.\n */\nexport interface ZodCompilerLoaderContext {\n resourcePath: string;\n async(): (error: Error | null, code?: string, map?: TransformSourceMap) => void;\n getOptions?(): ZodCompilerTurbopackOptions | undefined;\n addDependency?(file: string): void;\n cacheable?(flag: boolean): void;\n query?: unknown;\n}\n\n/**\n * Disk stamps of every module the shared execution cache is currently holding.\n *\n * Discovery executes schema files from DISK through a module cache that outlives\n * loader calls. The bundler plugins evict it from `watchChange`; a loader has no\n * such hook, so staleness has to be detected here or compiled validators keep\n * reflecting whatever the files said when they were first executed.\n *\n * Diffing the entry's own content is NOT enough, and the gap is exactly the case\n * `addDependency` exists to handle: when an imported constant changes, the host\n * re-runs the loader for a schema file whose content is UNCHANGED.\n *\n * Tracked GLOBALLY rather than per file, because that is what it describes: one\n * process-wide module cache, stale the moment any file behind it changes. A\n * per-file dependency list cannot express that — a file being transformed for\n * the FIRST time has no list yet, but the cache it is about to read from is\n * already warm and may already be stale. Keying on the cache's own inventory\n * also means one edit costs one eviction rather than one per dependent file.\n *\n * `getFirstPartyModulePaths()` is that inventory, which is why it is sound here\n * while being unsound as a per-file dependency list (see transformDependencies):\n * the question is \"what is cached\", not \"what does this file need\". It covers\n * dependencies the host never feeds through this loader at all — a constants\n * file that never mentions zod, or one a rule's `exclude` skips — but only the\n * ones jiti holds, so the two known gaps are its gaps:\n *\n * - `.js`/`.mjs` deps go through native `import()` (see loader.ts), which has no\n * evictable cache. The build plugins are equally stale there; on Bun and Deno\n * that is every module, and this whole mechanism is inert.\n * - A file being CREATED changes nothing's stamp, so a new module that shadows\n * an existing resolution (`limits.ts` beside `limits/index.ts`) keeps serving\n * the old one until some stamped file also changes. `watchChange` gives the\n * plugins a signal for this that a loader host has no equivalent of.\n *\n * mtime+size like the disk cache's fast path, minus its content hashes: those\n * exist to survive checkouts across processes, and this map dies with the\n * process.\n */\nconst executedModuleStamps = new Map<string, string>();\n\nfunction stampOf(file: string): string {\n try {\n const stat = fs.statSync(file, { throwIfNoEntry: false });\n return stat === undefined ? \"\" : `${stat.mtimeMs}:${stat.size}`;\n } catch {\n // Non-ENOENT (EACCES, ELOOP, ENOTDIR): unreadable is indistinguishable from\n // changed, and must never fail the build.\n return \"\";\n }\n}\n\n/**\n * Drop cached module executions when any file behind them changed on disk.\n *\n * Eviction is global (see invalidateModuleCache), so it is deliberately paired\n * with the dep-graph memos the plugin also resets in `watchChange`: a resolution\n * memoized before a file existed would otherwise pin that importer to an\n * unanalyzable graph for the life of the process.\n *\n * Costs one stat per executed module, paid by every file that reaches the\n * transform — ~3 ms per file against the 1,900-module project dep-graph.ts\n * cites, the same order as the closure walk `transformDependencies` does a few\n * lines later, and far below the discovery it is protecting.\n */\nfunction invalidateStaleExecutions(): void {\n let stale = false;\n for (const [file, stamp] of executedModuleStamps) {\n if (stampOf(file) !== stamp) {\n stale = true;\n break;\n }\n }\n if (!stale) return;\n invalidateModuleCache();\n resetDepGraphMemo();\n executedModuleStamps.clear();\n}\n\n/**\n * Stamp whatever discovery just executed.\n *\n * Keeps the FIRST stamp for a file rather than refreshing: a file edited between\n * its execution and this call must keep its pre-edit stamp, or the next run\n * would see it as fresh and pin the stale execution permanently.\n *\n * `null` means no jiti instance exists — a runtime whose module cache cannot be\n * evicted at all (loader.ts), so there is nothing to track and nothing to fix.\n */\nfunction recordExecutedModules(): void {\n for (const file of getFirstPartyModulePaths() ?? []) {\n if (!executedModuleStamps.has(file)) executedModuleStamps.set(file, stampOf(file));\n }\n}\n\nfunction readOptions(context: ZodCompilerLoaderContext): ZodCompilerTurbopackOptions {\n const options = context.getOptions?.();\n if (options !== undefined) return options;\n // `getOptions` is standard but not universal; `query` is the older shape.\n return typeof context.query === \"object\" && context.query !== null\n ? (context.query as ZodCompilerTurbopackOptions)\n : {};\n}\n\n/**\n * Declare what a rebuild must watch. The host caches this file's output keyed on\n * its own content, but discovery executed the whole import graph — without these\n * an edit to an imported constant leaves a stale validator in the bundle.\n */\nfunction declareDependencies(\n context: ZodCompilerLoaderContext,\n id: string,\n verbose: boolean,\n): void {\n const { files, complete } = transformDependencies(id);\n for (const file of files) context.addDependency?.(file);\n if (complete) return;\n // The graph could not be analyzed (a non-literal dynamic import anywhere in\n // the closure), so the list above is just this file — not enough for the host\n // to know when to re-run us. Asking to be re-run every build is the only\n // answer left. Freshness itself does not depend on this: whether the loader\n // is re-invoked once or every time, invalidateStaleExecutions decides what\n // discovery may reuse.\n context.cacheable?.(false);\n if (verbose) {\n log(`Cannot analyze the import graph of ${id} — re-running its transform every build`);\n }\n}\n\ninterface LoaderResult {\n code: string;\n map?: TransformSourceMap | undefined;\n}\n\n/** The transform itself, split out so the loader shell stays callback-only. */\nasync function run(\n context: ZodCompilerLoaderContext,\n source: string,\n inputMap: TransformSourceMap | undefined,\n): Promise<LoaderResult> {\n const id = context.resourcePath;\n const options = readOptions(context);\n\n if (!shouldTransform(id, options)) return { code: source, map: inputMap };\n\n // Before discovery can read a stale execution. Note this covers an edit to\n // THIS file too: discovery executed it, so it carries a stamp of its own —\n // there is no separate host-content diff, which would only ever fire for\n // content that differs from the disk discovery actually reads.\n invalidateStaleExecutions();\n\n const output = options.output ?? \"schema\";\n let discoveryRan = false;\n const result = await transformCodeWithMap(source, id, {\n mode: options.codegenMode ?? \"inline\",\n runtimeId: RUNTIME_PACKAGE_ID,\n verbose: options.verbose,\n // \"compact\" keeps the Zod schema (its safeParse IS the cold error path), so\n // only \"bag\" drops Zod compatibility.\n zodCompat: output === \"schema\" || output === \"compact\",\n compact: output === \"compact\",\n autoDiscover: (options.schemas ?? \"auto\") === \"auto\",\n hoist: options.hoist,\n onDiscovery: () => {\n discoveryRan = true;\n },\n onUncacheableResult: () => {\n // Discovery recovered from a process.exit (an env guard in a secret-less\n // build): the result is a function of the ENVIRONMENT, not the file, so it\n // must not be cached against this content.\n context.cacheable?.(false);\n },\n });\n\n // Only discovery reads other files. A hoist-only or bailed-out transform is a\n // pure function of this file's content, which the host already keys on.\n if (discoveryRan) {\n recordExecutedModules();\n declareDependencies(context, id, options.verbose === true);\n }\n\n if (result === null) return { code: source, map: inputMap };\n return { code: result.code, map: composeMaps(result.map, inputMap) };\n}\n\n/** Chain this transform's map onto an earlier loader's, newest first. */\nfunction composeMaps(\n map: TransformSourceMap | null,\n inputMap: TransformSourceMap | undefined,\n): TransformSourceMap | undefined {\n if (map === null) return inputMap;\n if (inputMap === undefined) return map;\n return remapping(\n [map, inputMap] as Parameters<typeof remapping>[0],\n () => null,\n ) as unknown as TransformSourceMap;\n}\n\nexport default function zodCompilerLoader(\n this: ZodCompilerLoaderContext,\n source: string,\n inputMap?: TransformSourceMap,\n): void {\n const callback = this.async();\n const succeed = (result: LoaderResult): void => callback(null, result.code, result.map);\n const fail = (error: unknown): void =>\n callback(error instanceof Error ? error : new Error(String(error)));\n\n // Two `then` handlers rather than one plus a try/catch: loader-runner invokes\n // the host's continuation synchronously from `callback`, so a throw inside it\n // would re-enter a catch and call back a second time. As rejection handlers of\n // the SAME promise, exactly one of these can ever run.\n run(this, source, inputMap).then(succeed, fail);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6IA,MAAM,uCAAuB,IAAI,IAAoB;AAErD,SAAS,QAAQ,MAAsB;CACrC,IAAI;EACF,MAAM,OAAO,GAAG,SAAS,MAAM,EAAE,gBAAgB,MAAM,CAAC;EACxD,OAAO,SAAS,KAAA,IAAY,KAAK,GAAG,KAAK,QAAQ,GAAG,KAAK;CAC3D,QAAQ;EAGN,OAAO;CACT;AACF;;;;;;;;;;;;;;AAeA,SAAS,4BAAkC;CACzC,IAAI,QAAQ;CACZ,KAAK,MAAM,CAAC,MAAM,UAAU,sBAC1B,IAAI,QAAQ,IAAI,MAAM,OAAO;EAC3B,QAAQ;EACR;CACF;CAEF,IAAI,CAAC,OAAO;CACZ,sBAAsB;CACtB,kBAAkB;CAClB,qBAAqB,MAAM;AAC7B;;;;;;;;;;;AAYA,SAAS,wBAA8B;CACrC,KAAK,MAAM,QAAQ,yBAAyB,KAAK,CAAC,GAChD,IAAI,CAAC,qBAAqB,IAAI,IAAI,GAAG,qBAAqB,IAAI,MAAM,QAAQ,IAAI,CAAC;AAErF;AAEA,SAAS,YAAY,SAAgE;CACnF,MAAM,UAAU,QAAQ,aAAa;CACrC,IAAI,YAAY,KAAA,GAAW,OAAO;CAElC,OAAO,OAAO,QAAQ,UAAU,YAAY,QAAQ,UAAU,OACzD,QAAQ,QACT,CAAC;AACP;;;;;;AAOA,SAAS,oBACP,SACA,IACA,SACM;CACN,MAAM,EAAE,OAAO,aAAa,sBAAsB,EAAE;CACpD,KAAK,MAAM,QAAQ,OAAO,QAAQ,gBAAgB,IAAI;CACtD,IAAI,UAAU;CAOd,QAAQ,YAAY,KAAK;CACzB,IAAI,SACF,IAAI,sCAAsC,GAAG,wCAAwC;AAEzF;;AAQA,eAAe,IACb,SACA,QACA,UACuB;CACvB,MAAM,KAAK,QAAQ;CACnB,MAAM,UAAU,YAAY,OAAO;CAEnC,IAAI,CAAC,gBAAgB,IAAI,OAAO,GAAG,OAAO;EAAE,MAAM;EAAQ,KAAK;CAAS;CAMxE,0BAA0B;CAE1B,MAAM,SAAS,QAAQ,UAAU;CACjC,IAAI,eAAe;CACnB,MAAM,SAAS,MAAM,qBAAqB,QAAQ,IAAI;EACpD,MAAM,QAAQ,eAAe;EAC7B,WAAW;EACX,SAAS,QAAQ;EAGjB,WAAW,WAAW,YAAY,WAAW;EAC7C,SAAS,WAAW;EACpB,eAAe,QAAQ,WAAW,YAAY;EAC9C,OAAO,QAAQ;EACf,mBAAmB;GACjB,eAAe;EACjB;EACA,2BAA2B;GAIzB,QAAQ,YAAY,KAAK;EAC3B;CACF,CAAC;CAID,IAAI,cAAc;EAChB,sBAAsB;EACtB,oBAAoB,SAAS,IAAI,QAAQ,YAAY,IAAI;CAC3D;CAEA,IAAI,WAAW,MAAM,OAAO;EAAE,MAAM;EAAQ,KAAK;CAAS;CAC1D,OAAO;EAAE,MAAM,OAAO;EAAM,KAAK,YAAY,OAAO,KAAK,QAAQ;CAAE;AACrE;;AAGA,SAAS,YACP,KACA,UACgC;CAChC,IAAI,QAAQ,MAAM,OAAO;CACzB,IAAI,aAAa,KAAA,GAAW,OAAO;CACnC,OAAO,UACL,CAAC,KAAK,QAAQ,SACR,IACR;AACF;AAEA,SAAwB,kBAEtB,QACA,UACM;CACN,MAAM,WAAW,KAAK,MAAM;CAC5B,MAAM,WAAW,WAA+B,SAAS,MAAM,OAAO,MAAM,OAAO,GAAG;CACtF,MAAM,QAAQ,UACZ,SAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;CAMpE,IAAI,MAAM,QAAQ,QAAQ,CAAC,CAAC,KAAK,SAAS,IAAI;AAChD"}
1
+ {"version":3,"file":"turbopack.js","names":[],"sources":["../src/turbopack.ts"],"sourcesContent":["/**\n * Webpack-loader entry point, for Turbopack (Next.js) and any other host that\n * runs webpack loaders but not webpack plugins.\n *\n * Turbopack deliberately supports no webpack plugins, so the unplugin build\n * plugins cannot reach it. Loaders it does run, through the real `loader-runner`\n * library, and that is enough: this module is the whole zod-compiler transform\n * behind `this.async()`.\n *\n * It emits TypeScript, not JavaScript. A `turbopack.rules` entry that sets no\n * `as`/`type` leaves the loader's output to be parsed as whatever the file\n * already was, so Turbopack's own SWC pass handles the syntax — there is no\n * second transpile here and no @swc/core dependency.\n *\n * `codegenMode: \"lean\"` is available here — it imports the shared helpers from\n * `zod-compiler/runtime`, a real package subpath, rather than the `virtual:` id\n * the build plugins answer from a resolve hook that a loader does not have. It\n * is opt-in; see the option's doc for why.\n *\n * // next.config.ts\n * export default {\n * turbopack: {\n * rules: {\n * \"*.{ts,tsx}\": {\n * condition: { all: [{ not: \"foreign\" }, { content: /[Zz]od/ }] },\n * loaders: [\"zod-compiler/turbopack\"],\n * },\n * },\n * },\n * };\n *\n * The `content` condition is the Turbopack equivalent of the plugins' own code\n * filter and must stay as loose as ZOD_MENTION: narrowing it to the literal\n * specifier `\"zod\"` would silently skip `zod/v4`, `zod/mini` and the\n * `zod-compiler` import that drives `schemas: \"explicit\"` — no error, those\n * schemas just quietly stay uncompiled.\n */\n\nimport fs from \"node:fs\";\nimport remapping from \"@jridgewell/remapping\";\nimport { getFirstPartyModulePaths, invalidateModuleCache } from \"./loader.js\";\nimport { resetDepGraphMemo, transformDependencies } from \"./unplugin/dep-graph.js\";\nimport {\n log,\n shouldTransform,\n type TransformSourceMap,\n transformCodeWithMap,\n} from \"./unplugin/transform.js\";\nimport type { ZodCompilerPluginOptions } from \"./unplugin/types.js\";\nimport { RUNTIME_PACKAGE_ID } from \"./unplugin/virtual.js\";\n\n/**\n * Plugin options minus the ones a loader host cannot express.\n *\n * Turbopack serializes loader options through `next.config`, so they have to be\n * plain JSON — hence no `apply` (a Vite lifecycle function) and a string-only\n * `schemaNamePattern` where the plugin also accepts a RegExp.\n *\n * No `cache` either: the disk cache's dependency bookkeeping leans on a\n * `buildEnd` flush that a loader has no equivalent for, and loader hosts keep\n * their own persistent result cache — Turbopack's is keyed on content plus the\n * dependencies declared below, which is what this would have re-implemented.\n *\n * No `parallel`: the concurrency is the host's to own. Turbopack already runs\n * loaders across its own worker pool, so a pool per loader invocation would\n * multiply threads against a machine that is already saturated — and the\n * module-cache staleness stamps below assume one shared execution cache.\n */\nexport type ZodCompilerTurbopackOptions = Omit<\n ZodCompilerPluginOptions,\n \"apply\" | \"cache\" | \"codegenMode\" | \"hoist\" | \"parallel\"\n> & {\n hoist?: boolean | { schemaNamePattern?: string | null | undefined } | undefined;\n /**\n * `\"inline\"` (default) emits the shared helpers into every transformed file.\n *\n * `\"lean\"` imports them from `zod-compiler/runtime` instead, so a bundle\n * carries one copy however many files were transformed. That specifier is a\n * real package subpath, which is what makes it usable from a loader at all —\n * the `virtual:` id the build plugins emit needs a `resolveId` hook, and a\n * loader has none.\n *\n * Opt-in rather than the default because it only holds when the host BUNDLES\n * the import. Next.js does for client and App Router server code, but Pages\n * Router server code externalizes node_modules imports unless\n * `bundlePagesRouterDependencies` is set — and `zod-compiler` is normally a\n * devDependency, so a production install prunes it and the route throws\n * ERR_MODULE_NOT_FOUND on the first request. A bigger bundle is the better\n * default than a runtime failure that no build step reports.\n * @default \"inline\"\n */\n codegenMode?: \"lean\" | \"inline\" | undefined;\n};\n\n/**\n * The slice of webpack's loader context this uses. Structural rather than\n * imported from webpack: the package must not take a webpack dependency to\n * serve a host that is not webpack.\n */\nexport interface ZodCompilerLoaderContext {\n resourcePath: string;\n async(): (error: Error | null, code?: string, map?: TransformSourceMap) => void;\n getOptions?(): ZodCompilerTurbopackOptions | undefined;\n addDependency?(file: string): void;\n cacheable?(flag: boolean): void;\n query?: unknown;\n}\n\n/**\n * Disk stamps of every module the shared execution cache is currently holding.\n *\n * Discovery executes schema files from DISK through a module cache that outlives\n * loader calls. The bundler plugins evict it from `watchChange`; a loader has no\n * such hook, so staleness has to be detected here or compiled validators keep\n * reflecting whatever the files said when they were first executed.\n *\n * Diffing the entry's own content is NOT enough, and the gap is exactly the case\n * `addDependency` exists to handle: when an imported constant changes, the host\n * re-runs the loader for a schema file whose content is UNCHANGED.\n *\n * Tracked GLOBALLY rather than per file, because that is what it describes: one\n * process-wide module cache, stale the moment any file behind it changes. A\n * per-file dependency list cannot express that — a file being transformed for\n * the FIRST time has no list yet, but the cache it is about to read from is\n * already warm and may already be stale. Keying on the cache's own inventory\n * also means one edit costs one eviction rather than one per dependent file.\n *\n * `getFirstPartyModulePaths()` is that inventory, which is why it is sound here\n * while being unsound as a per-file dependency list (see transformDependencies):\n * the question is \"what is cached\", not \"what does this file need\". It covers\n * dependencies the host never feeds through this loader at all — a constants\n * file that never mentions zod, or one a rule's `exclude` skips — but only the\n * ones jiti holds, so the two known gaps are its gaps:\n *\n * - `.js`/`.mjs` deps go through native `import()` (see loader.ts), which has no\n * evictable cache. The build plugins are equally stale there; on Bun and Deno\n * that is every module, and this whole mechanism is inert.\n * - A file being CREATED changes nothing's stamp, so a new module that shadows\n * an existing resolution (`limits.ts` beside `limits/index.ts`) keeps serving\n * the old one until some stamped file also changes. `watchChange` gives the\n * plugins a signal for this that a loader host has no equivalent of.\n *\n * mtime+size like the disk cache's fast path, minus its content hashes: those\n * exist to survive checkouts across processes, and this map dies with the\n * process.\n */\nconst executedModuleStamps = new Map<string, string>();\n\nfunction stampOf(file: string): string {\n try {\n const stat = fs.statSync(file, { throwIfNoEntry: false });\n return stat === undefined ? \"\" : `${stat.mtimeMs}:${stat.size}`;\n } catch {\n // Non-ENOENT (EACCES, ELOOP, ENOTDIR): unreadable is indistinguishable from\n // changed, and must never fail the build.\n return \"\";\n }\n}\n\n/**\n * Drop cached module executions when any file behind them changed on disk.\n *\n * Eviction is global (see invalidateModuleCache), so it is deliberately paired\n * with the dep-graph memos the plugin also resets in `watchChange`: a resolution\n * memoized before a file existed would otherwise pin that importer to an\n * unanalyzable graph for the life of the process.\n *\n * Costs one stat per executed module, paid by every file that reaches the\n * transform — ~3 ms per file against the 1,900-module project dep-graph.ts\n * cites, the same order as the closure walk `transformDependencies` does a few\n * lines later, and far below the discovery it is protecting.\n */\nfunction invalidateStaleExecutions(): void {\n let stale = false;\n for (const [file, stamp] of executedModuleStamps) {\n if (stampOf(file) !== stamp) {\n stale = true;\n break;\n }\n }\n if (!stale) return;\n invalidateModuleCache();\n resetDepGraphMemo();\n executedModuleStamps.clear();\n}\n\n/**\n * Stamp whatever discovery just executed.\n *\n * Keeps the FIRST stamp for a file rather than refreshing: a file edited between\n * its execution and this call must keep its pre-edit stamp, or the next run\n * would see it as fresh and pin the stale execution permanently.\n *\n * `null` means no jiti instance exists — a runtime whose module cache cannot be\n * evicted at all (loader.ts), so there is nothing to track and nothing to fix.\n */\nfunction recordExecutedModules(): void {\n for (const file of getFirstPartyModulePaths() ?? []) {\n if (!executedModuleStamps.has(file)) executedModuleStamps.set(file, stampOf(file));\n }\n}\n\nfunction readOptions(context: ZodCompilerLoaderContext): ZodCompilerTurbopackOptions {\n const options = context.getOptions?.();\n if (options !== undefined) return options;\n // `getOptions` is standard but not universal; `query` is the older shape.\n return typeof context.query === \"object\" && context.query !== null\n ? (context.query as ZodCompilerTurbopackOptions)\n : {};\n}\n\n/**\n * Declare what a rebuild must watch. The host caches this file's output keyed on\n * its own content, but discovery executed the whole import graph — without these\n * an edit to an imported constant leaves a stale validator in the bundle.\n */\nfunction declareDependencies(\n context: ZodCompilerLoaderContext,\n id: string,\n verbose: boolean,\n): void {\n const { files, complete } = transformDependencies(id);\n for (const file of files) context.addDependency?.(file);\n if (complete) return;\n // The graph could not be analyzed (a non-literal dynamic import anywhere in\n // the closure), so the list above is just this file — not enough for the host\n // to know when to re-run us. Asking to be re-run every build is the only\n // answer left. Freshness itself does not depend on this: whether the loader\n // is re-invoked once or every time, invalidateStaleExecutions decides what\n // discovery may reuse.\n context.cacheable?.(false);\n if (verbose) {\n log(`Cannot analyze the import graph of ${id} — re-running its transform every build`);\n }\n}\n\ninterface LoaderResult {\n code: string;\n map?: TransformSourceMap | undefined;\n}\n\n/** The transform itself, split out so the loader shell stays callback-only. */\nasync function run(\n context: ZodCompilerLoaderContext,\n source: string,\n inputMap: TransformSourceMap | undefined,\n): Promise<LoaderResult> {\n const id = context.resourcePath;\n const options = readOptions(context);\n\n if (!shouldTransform(id, options)) return { code: source, map: inputMap };\n\n // Before discovery can read a stale execution. Note this covers an edit to\n // THIS file too: discovery executed it, so it carries a stamp of its own —\n // there is no separate host-content diff, which would only ever fire for\n // content that differs from the disk discovery actually reads.\n invalidateStaleExecutions();\n\n const output = options.output ?? \"schema\";\n let discoveryRan = false;\n const result = await transformCodeWithMap(source, id, {\n mode: options.codegenMode ?? \"inline\",\n runtimeId: RUNTIME_PACKAGE_ID,\n verbose: options.verbose,\n // \"compact\" keeps the Zod schema (its safeParse IS the cold error path), so\n // only \"bag\" drops Zod compatibility.\n zodCompat: output === \"schema\" || output === \"compact\",\n compact: output === \"compact\",\n autoDiscover: (options.schemas ?? \"auto\") === \"auto\",\n hoist: options.hoist,\n onDiscovery: () => {\n discoveryRan = true;\n },\n onUncacheableResult: () => {\n // Discovery recovered from a process.exit (an env guard in a secret-less\n // build): the result is a function of the ENVIRONMENT, not the file, so it\n // must not be cached against this content.\n context.cacheable?.(false);\n },\n });\n\n // Only discovery reads other files. A hoist-only or bailed-out transform is a\n // pure function of this file's content, which the host already keys on.\n if (discoveryRan) {\n recordExecutedModules();\n declareDependencies(context, id, options.verbose === true);\n }\n\n if (result === null) return { code: source, map: inputMap };\n return { code: result.code, map: composeMaps(result.map, inputMap) };\n}\n\n/** Chain this transform's map onto an earlier loader's, newest first. */\nfunction composeMaps(\n map: TransformSourceMap | null,\n inputMap: TransformSourceMap | undefined,\n): TransformSourceMap | undefined {\n if (map === null) return inputMap;\n if (inputMap === undefined) return map;\n return remapping(\n [map, inputMap] as Parameters<typeof remapping>[0],\n () => null,\n ) as unknown as TransformSourceMap;\n}\n\nexport default function zodCompilerLoader(\n this: ZodCompilerLoaderContext,\n source: string,\n inputMap?: TransformSourceMap,\n): void {\n const callback = this.async();\n const succeed = (result: LoaderResult): void => callback(null, result.code, result.map);\n const fail = (error: unknown): void =>\n callback(error instanceof Error ? error : new Error(String(error)));\n\n // Two `then` handlers rather than one plus a try/catch: loader-runner invokes\n // the host's continuation synchronously from `callback`, so a throw inside it\n // would re-enter a catch and call back a second time. As rejection handlers of\n // the SAME promise, exactly one of these can ever run.\n run(this, source, inputMap).then(succeed, fail);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkJA,MAAM,uCAAuB,IAAI,IAAoB;AAErD,SAAS,QAAQ,MAAsB;CACrC,IAAI;EACF,MAAM,OAAO,GAAG,SAAS,MAAM,EAAE,gBAAgB,MAAM,CAAC;EACxD,OAAO,SAAS,KAAA,IAAY,KAAK,GAAG,KAAK,QAAQ,GAAG,KAAK;CAC3D,QAAQ;EAGN,OAAO;CACT;AACF;;;;;;;;;;;;;;AAeA,SAAS,4BAAkC;CACzC,IAAI,QAAQ;CACZ,KAAK,MAAM,CAAC,MAAM,UAAU,sBAC1B,IAAI,QAAQ,IAAI,MAAM,OAAO;EAC3B,QAAQ;EACR;CACF;CAEF,IAAI,CAAC,OAAO;CACZ,sBAAsB;CACtB,kBAAkB;CAClB,qBAAqB,MAAM;AAC7B;;;;;;;;;;;AAYA,SAAS,wBAA8B;CACrC,KAAK,MAAM,QAAQ,yBAAyB,KAAK,CAAC,GAChD,IAAI,CAAC,qBAAqB,IAAI,IAAI,GAAG,qBAAqB,IAAI,MAAM,QAAQ,IAAI,CAAC;AAErF;AAEA,SAAS,YAAY,SAAgE;CACnF,MAAM,UAAU,QAAQ,aAAa;CACrC,IAAI,YAAY,KAAA,GAAW,OAAO;CAElC,OAAO,OAAO,QAAQ,UAAU,YAAY,QAAQ,UAAU,OACzD,QAAQ,QACT,CAAC;AACP;;;;;;AAOA,SAAS,oBACP,SACA,IACA,SACM;CACN,MAAM,EAAE,OAAO,aAAa,sBAAsB,EAAE;CACpD,KAAK,MAAM,QAAQ,OAAO,QAAQ,gBAAgB,IAAI;CACtD,IAAI,UAAU;CAOd,QAAQ,YAAY,KAAK;CACzB,IAAI,SACF,IAAI,sCAAsC,GAAG,wCAAwC;AAEzF;;AAQA,eAAe,IACb,SACA,QACA,UACuB;CACvB,MAAM,KAAK,QAAQ;CACnB,MAAM,UAAU,YAAY,OAAO;CAEnC,IAAI,CAAC,gBAAgB,IAAI,OAAO,GAAG,OAAO;EAAE,MAAM;EAAQ,KAAK;CAAS;CAMxE,0BAA0B;CAE1B,MAAM,SAAS,QAAQ,UAAU;CACjC,IAAI,eAAe;CACnB,MAAM,SAAS,MAAM,qBAAqB,QAAQ,IAAI;EACpD,MAAM,QAAQ,eAAe;EAC7B,WAAW;EACX,SAAS,QAAQ;EAGjB,WAAW,WAAW,YAAY,WAAW;EAC7C,SAAS,WAAW;EACpB,eAAe,QAAQ,WAAW,YAAY;EAC9C,OAAO,QAAQ;EACf,mBAAmB;GACjB,eAAe;EACjB;EACA,2BAA2B;GAIzB,QAAQ,YAAY,KAAK;EAC3B;CACF,CAAC;CAID,IAAI,cAAc;EAChB,sBAAsB;EACtB,oBAAoB,SAAS,IAAI,QAAQ,YAAY,IAAI;CAC3D;CAEA,IAAI,WAAW,MAAM,OAAO;EAAE,MAAM;EAAQ,KAAK;CAAS;CAC1D,OAAO;EAAE,MAAM,OAAO;EAAM,KAAK,YAAY,OAAO,KAAK,QAAQ;CAAE;AACrE;;AAGA,SAAS,YACP,KACA,UACgC;CAChC,IAAI,QAAQ,MAAM,OAAO;CACzB,IAAI,aAAa,KAAA,GAAW,OAAO;CACnC,OAAO,UACL,CAAC,KAAK,QAAQ,SACR,IACR;AACF;AAEA,SAAwB,kBAEtB,QACA,UACM;CACN,MAAM,WAAW,KAAK,MAAM;CAC5B,MAAM,WAAW,WAA+B,SAAS,MAAM,OAAO,MAAM,OAAO,GAAG;CACtF,MAAM,QAAQ,UACZ,SAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;CAMpE,IAAI,MAAM,QAAQ,QAAQ,CAAC,CAAC,KAAK,SAAS,IAAI;AAChD"}
@@ -1 +1 @@
1
- {"version":3,"file":"disk-cache.d.ts","names":[],"sources":["../../src/unplugin/disk-cache.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA4DiB;EACf;EACA;;UASe;;EAEf;;EAEA;;EAEA,QAAQ;;EAER,MAAM;;;UAIS;EACf;EACA;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiEc,wBAAwB;;iBAiGxB;cAoBH;mBACM;mBACA;;mBAEA;UACT;UACA;EAEI,YAAA,aAAa,oBAAoB;;;;;;SAWtC,WAAW;EAOlB,IAAI,YAAY;UAIR;UAIA;;;;;;;UAUA;;;;;;;;;;UAiCA;;EA0CR,KAAK,cAAc;;;;;;UAoBX;;;;;;;UAoCA;;UAeA;UAaA;;EA+BR,KACE,aACA,uBACA,6BACA,QAAQ,iBACR,MAAM;;;;;;;EAeR,aACE,aACA,uBACA,QAAQ,iBACR,MAAM;;;;;EAWR;;;;;;EAoBA"}
1
+ {"version":3,"file":"disk-cache.d.ts","names":[],"sources":["../../src/unplugin/disk-cache.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA4DiB;EACf;EACA;;UASe;;EAEf;;EAEA;;EAEA,QAAQ;;EAER,MAAM;;;UAIS;EACf;EACA;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiEc,wBAAwB;;iBAiGxB;cAoBH;mBACM;mBACA;;mBAEA;UACT;UACA;EAEI,YAAA,aAAa,oBAAoB;;;;;;SAWtC,WAAW;EAOlB,IAAI,YAAY;UAIR;UAIA;;;;;;;UAUA;;;;;;;;;;UAiCA;;EA0CR,KAAK,cAAc;;;;;;UAoBX;;;;;;;UAoCA;;UAeA;UAaA;;EA+BR,KACE,aACA,uBACA,6BACA,QAAQ,iBACR,MAAM;;;;;;;EAeR,aACE,aACA,uBACA,QAAQ,iBACR,MAAM;;;;;EAWR;;;;;;EA2BA"}
@@ -437,6 +437,7 @@ var DiskCache = class {
437
437
  this.pending = [];
438
438
  const snapshot = this.superset === null ? null : this.superset();
439
439
  if (snapshot === null) return;
440
+ if (snapshot.length === 0) return;
440
441
  this.ensureDir();
441
442
  const built = this.buildDepset(snapshot);
442
443
  if (built === null) return;
@@ -1 +1 @@
1
- {"version":3,"file":"disk-cache.js","names":["fs","path"],"sources":["../../src/unplugin/disk-cache.ts"],"sourcesContent":["/**\n * Persistent transform-result cache.\n *\n * The expensive part of the unplugin transform is discovery: executing the\n * schema file (and transitively its whole first-party import graph) through\n * jiti inside the bundler's single-threaded server process. The in-memory\n * caches die with the process, so every `vitest run` / build re-pays that\n * cost even when nothing changed — which is exactly the loop integration\n * tests live in.\n *\n * Entries are keyed by a hash of (plugin version, zod version, transform\n * options, file id, file content) and validated against a recorded snapshot\n * of first-party files. Validation uses an mtime+size fast path and falls\n * back to content hashing, so a `touch` without changes still hits. A\n * superset of true dependencies only over-invalidates, never serves stale\n * output.\n *\n * Layout (CACHE_FORMAT 2): dependency snapshots are CONTENT-ADDRESSED and\n * shared — `deps/<sha1>.json` holds the {path → hash/mtime/size} map, and\n * each entry stores only the dep-set id. The v1 format inlined the full dep\n * map into every entry; in a large-codebase field report 835 superset-\n * fallback entries each embedded a ~1,900-file point-in-time snapshot (813\n * DISTINCT snapshots — the executed-modules superset grows as the build\n * progresses, so per-entry copies cannot even dedupe), totalling 283 MB\n * that any commit invalidated wholesale. Sharing dep-sets also means each\n * unique set is parsed and validated once per process instead of once per\n * entry.\n *\n * Superset fallbacks (entries whose static dep crawl was incomplete) are\n * DEFERRED: queued in memory and flushed in buildEnd / process exit against\n * a single end-of-build superset snapshot, so every superset entry of a\n * build shares ONE dep-set file (recording the still-growing snapshot at\n * save time is what produced 813 distinct copies). Deferred entries are\n * dropped on watchChange — a dependency edited between queueing and flush\n * would pair post-change hashes with a pre-change result, which is the one\n * combination that can serve stale output. A killed process loses only its\n * pending superset entries (the static-complete majority persists\n * immediately); the next run re-pays discovery for those files alone.\n *\n * Writes are atomic (tmp file + rename) so concurrent bundler processes\n * (vitest workspace projects) can share one cache directory safely.\n */\n\nimport { createHash } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** Bump when the on-disk layout changes; mismatched directories are wiped. */\nconst CACHE_FORMAT = 2;\nconst META_FILE = \"_meta.json\";\nconst GC_MARKER = \"_gc\";\nconst DEPSET_DIR = \"deps\";\n/** GC at most once per day per cache directory. */\nconst GC_INTERVAL_MS = 24 * 60 * 60 * 1000;\n/** Entries unread for this long are presumed orphaned by key churn. */\nconst MAX_ENTRY_AGE_MS = 30 * 24 * 60 * 60 * 1000;\n/** Stale atomic-write temp files (crashed processes) older than this are removed. */\nconst MAX_TMP_AGE_MS = 60 * 60 * 1000;\n\nexport interface CacheEntryStats {\n schemas: number;\n optimized: number;\n}\n\ninterface DepRecord {\n hash: string;\n mtimeMs: number;\n size: number;\n}\n\nexport interface CacheEntry {\n /** Transformed code, or null when the transform produced no change. */\n result: string | null;\n /** Content-addressed id of the shared dep-set file (deps/<id>.json). */\n depset: string;\n /** Build stats to replay on cache hits (only present when schemas compiled). */\n stats?: CacheEntryStats;\n /** Composed sourcemap for `result` (original → transformed). */\n map?: CacheSourceMap | null;\n}\n\n/** JSON shape of the persisted sourcemap (mirrors TransformSourceMap). */\nexport interface CacheSourceMap {\n version: number;\n sources: (string | null)[];\n sourcesContent?: (string | null)[];\n names: string[];\n mappings: string;\n file?: string | null;\n}\n\ninterface DepsetFile {\n files: Record<string, DepRecord>;\n}\n\ninterface PendingEntry {\n key: string;\n result: string | null;\n stats?: CacheEntryStats | undefined;\n map?: CacheSourceMap | null | undefined;\n}\n\nfunction sha1(data: string | Buffer): string {\n return createHash(\"sha1\").update(data).digest(\"hex\");\n}\n\n/** Read own package version once for cache keying (src and dist both sit two levels below the package root). */\nfunction readPluginVersion(): string {\n try {\n const pkgPath = new URL(\"../../package.json\", import.meta.url);\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\")) as { version?: string };\n return pkg.version ?? \"0\";\n } catch {\n return \"0\";\n }\n}\n\n/** Resolve the installed zod version — generated code depends on zod internals. */\nfunction readZodVersion(): string {\n try {\n const pkgPath = path.join(process.cwd(), \"node_modules\", \"zod\", \"package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\")) as { version?: string };\n return pkg.version ?? \"0\";\n } catch {\n return \"0\";\n }\n}\n\n/**\n * Build fingerprint: a content hash of the package's own source trees.\n *\n * The published version string alone is not enough — file: installs, linked\n * monorepo packages, and canary builds rebuild the compiler without a version\n * bump, and serving codegen from an older compiler build would be silently\n * stale.\n *\n * CONTENT-addressed, for the same reason depset ids are: mtimes do not survive\n * an install. pnpm rewrites every mtime under its `copy` import method, and\n * copy is what you get whenever the store sits on a different filesystem than\n * the workspace — the default on CI runners that mount the store as its own\n * volume (`npm_config_package_import_method=copy`). An mtime fingerprint\n * therefore rotates on every `pnpm install --frozen-lockfile`, which changes\n * every cache key and makes a restored CI cache wholly unreachable: the archive\n * unpacks, and nothing in it is ever looked up. Hardlink and APFS-clone installs\n * DO preserve mtimes, so the bug hides locally and reproduces only on the\n * runners that need the cache most.\n *\n * A content hash is also strictly tighter than mtime for the stated purpose: a\n * rebuild that reproduces identical bytes no longer discards the whole cache.\n *\n * Hashing ~0.7 MB across ~250 files costs ~5ms — once per process, and only if\n * a key is actually built (see `keyPrefix`).\n */\nexport function computeBuildFingerprint(root: string): string {\n const found: string[] = [];\n const walk = (dir: string): void => {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const p = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walk(p);\n } else if (/\\.(?:js|ts|json)$/.test(entry.name)) {\n // Relative + POSIX-normalised: the absolute prefix differs per checkout\n // and the separator differs per platform, neither of which is a rebuild.\n found.push(path.relative(root, p).split(path.sep).join(\"/\"));\n }\n }\n };\n for (const sub of [\"dist\", \"src\"]) {\n walk(path.join(root, sub));\n }\n found.sort();\n const hash = createHash(\"sha1\");\n for (const rel of found) {\n hash.update(rel);\n hash.update(\"\\0\");\n try {\n hash.update(fs.readFileSync(path.join(root, rel)));\n } catch {\n // Unreadable file: fold in a marker rather than abort. Collapsing to a\n // constant on error would make genuinely different builds share a key.\n hash.update(\"<unreadable>\");\n }\n }\n return `${hash.digest(\"hex\")}:${found.length}`;\n}\n\nfunction readBuildFingerprint(): string {\n try {\n return computeBuildFingerprint(fileURLToPath(new URL(\"../..\", import.meta.url)));\n } catch {\n return \"0\";\n }\n}\n\n/**\n * Version + build identity shared by every key in the process. Computed on\n * first key, not at import: reading two package.json files and hashing dist is\n * pure waste for a build that never enables the cache.\n */\nlet keyPrefixMemo: string | null = null;\n\nfunction keyPrefix(): string {\n keyPrefixMemo ??= `${readPluginVersion()}\\0${readBuildFingerprint()}\\0${readZodVersion()}`;\n return keyPrefixMemo;\n}\n\n/**\n * Per-process memo: path → current stat (+ lazily computed content hash).\n * One fs.stat per file per process regardless of how many dep-sets\n * reference it; content is read at most once.\n */\nconst depStatMemo = new Map<string, { mtimeMs: number; size: number; hash?: string } | null>();\n\n/** Per-process memo: dep-set file path → validation verdict. */\nconst depsetVerdictMemo = new Map<string, boolean>();\n\nfunction statDep(depPath: string): { mtimeMs: number; size: number; hash?: string } | null {\n const memo = depStatMemo.get(depPath);\n if (memo !== undefined) return memo;\n let result: { mtimeMs: number; size: number; hash?: string } | null;\n try {\n const stat = fs.statSync(depPath);\n result = { mtimeMs: stat.mtimeMs, size: stat.size };\n } catch {\n result = null;\n }\n depStatMemo.set(depPath, result);\n return result;\n}\n\nfunction hashDep(\n depPath: string,\n stat: { mtimeMs: number; size: number; hash?: string },\n): string | null {\n if (stat.hash !== undefined) return stat.hash;\n try {\n stat.hash = sha1(fs.readFileSync(depPath));\n return stat.hash;\n } catch {\n return null;\n }\n}\n\n/** Reset the per-process dep validation memos (watch-mode file changes). */\nexport function resetDepValidationMemo(): void {\n depStatMemo.clear();\n depsetVerdictMemo.clear();\n}\n\n/** Instances with pending deferred entries, flushed on process exit. */\nconst flushOnExit = new Set<DiskCache>();\nlet exitHookInstalled = false;\n\nfunction registerExitFlush(cache: DiskCache): void {\n flushOnExit.add(cache);\n if (!exitHookInstalled) {\n exitHookInstalled = true;\n // 'exit' allows only synchronous work; every write below is sync.\n process.once(\"exit\", () => {\n for (const c of flushOnExit) c.flushDeferred();\n });\n }\n}\n\nexport class DiskCache {\n private readonly dir: string;\n private readonly optionsKey: string;\n /** Superset snapshot provider (loader's executed first-party modules). */\n private readonly superset: (() => string[] | null) | null;\n private pending: PendingEntry[] = [];\n private initialized = false;\n\n constructor(dir: string, optionsKey: string, superset?: () => string[] | null) {\n this.dir = dir;\n this.optionsKey = optionsKey;\n this.superset = superset ?? null;\n }\n\n /**\n * Resolve the cache directory: an explicit string wins; otherwise\n * node_modules/.cache/zod-compiler under cwd (falling back to a\n * project-local .zod-compiler-cache when node_modules doesn't exist).\n */\n static resolveDir(cacheOption: string | true): string {\n if (typeof cacheOption === \"string\") return path.resolve(cacheOption);\n const nm = path.join(process.cwd(), \"node_modules\");\n if (fs.existsSync(nm)) return path.join(nm, \".cache\", \"zod-compiler\");\n return path.join(process.cwd(), \".zod-compiler-cache\");\n }\n\n key(id: string, code: string): string {\n return sha1(`${keyPrefix()}\\0${this.optionsKey}\\0${id}\\0${code}`);\n }\n\n private entryPath(key: string): string {\n return path.join(this.dir, `${key}.json`);\n }\n\n private depsetPath(id: string): string {\n return path.join(this.dir, DEPSET_DIR, `${id}.json`);\n }\n\n /**\n * One-time directory init: wipe on format mismatch (v1 inline-deps caches\n * reached 283 MB in the field — disposable by definition), then a\n * throttled GC pass. Best-effort throughout; a concurrent wipe/GC from\n * another process can only cause cache misses, never stale hits.\n */\n private ensureDir(): void {\n if (this.initialized) return;\n this.initialized = true;\n try {\n const metaPath = path.join(this.dir, META_FILE);\n let format = 0;\n try {\n format = (JSON.parse(fs.readFileSync(metaPath, \"utf8\")) as { format?: number }).format ?? 0;\n } catch {\n // missing or unreadable marker — treat as foreign format\n }\n if (format !== CACHE_FORMAT) {\n fs.rmSync(this.dir, { recursive: true, force: true });\n fs.mkdirSync(path.join(this.dir, DEPSET_DIR), { recursive: true });\n fs.writeFileSync(metaPath, JSON.stringify({ format: CACHE_FORMAT }));\n } else {\n fs.mkdirSync(path.join(this.dir, DEPSET_DIR), { recursive: true });\n this.maybeGc();\n }\n } catch {\n // cache stays best-effort\n }\n }\n\n /**\n * Throttled sweep: entries older than MAX_ENTRY_AGE_MS (orphaned by key\n * churn — content/version/options keys never repeat once inputs change)\n * and dep-set files no surviving entry references. Runs at most once per\n * GC_INTERVAL_MS per directory; the marker is claimed BEFORE sweeping so\n * concurrent processes skip. Deleting a dep-set raced by a concurrent\n * entry write only costs that entry a future miss — save() re-creates\n * absent dep-set files.\n */\n private maybeGc(): void {\n const marker = path.join(this.dir, GC_MARKER);\n try {\n const stat = fs.statSync(marker, { throwIfNoEntry: false });\n if (stat !== undefined && Date.now() - stat.mtimeMs < GC_INTERVAL_MS) return;\n fs.writeFileSync(marker, \"\");\n\n const now = Date.now();\n const referenced = new Set<string>();\n for (const name of fs.readdirSync(this.dir)) {\n const p = path.join(this.dir, name);\n if (name.endsWith(\".tmp\")) {\n const st = fs.statSync(p, { throwIfNoEntry: false });\n if (st !== undefined && now - st.mtimeMs > MAX_TMP_AGE_MS) fs.rmSync(p, { force: true });\n continue;\n }\n if (!name.endsWith(\".json\") || name === META_FILE) continue;\n try {\n const st = fs.statSync(p);\n if (now - st.mtimeMs > MAX_ENTRY_AGE_MS) {\n fs.rmSync(p, { force: true });\n continue;\n }\n const entry = JSON.parse(fs.readFileSync(p, \"utf8\")) as { depset?: string };\n if (typeof entry.depset === \"string\") referenced.add(entry.depset);\n } catch {\n fs.rmSync(p, { force: true });\n }\n }\n for (const name of fs.readdirSync(path.join(this.dir, DEPSET_DIR))) {\n if (!name.endsWith(\".json\")) continue;\n const id = name.slice(0, -\".json\".length);\n if (!referenced.has(id)) {\n fs.rmSync(path.join(this.dir, DEPSET_DIR, name), { force: true });\n }\n }\n } catch {\n // best effort\n }\n }\n\n /** Load an entry and validate its dep-set. Any failure → null. */\n load(key: string): CacheEntry | null {\n this.ensureDir();\n let entry: CacheEntry;\n try {\n entry = JSON.parse(fs.readFileSync(this.entryPath(key), \"utf8\")) as CacheEntry;\n } catch {\n return null;\n }\n if (entry === null || typeof entry !== \"object\" || typeof entry.depset !== \"string\") {\n return null;\n }\n if (!this.validateDepset(entry.depset)) return null;\n return entry;\n }\n\n /**\n * Validate every dep in a dep-set, once per process per set: superset\n * entries all share one set, so the ~N-file validation (and the JSON\n * parse) happens once instead of once per entry.\n */\n private validateDepset(id: string): boolean {\n const depsetFile = this.depsetPath(id);\n const memo = depsetVerdictMemo.get(depsetFile);\n if (memo !== undefined) return memo;\n let verdict = true;\n try {\n const parsed = JSON.parse(fs.readFileSync(depsetFile, \"utf8\")) as DepsetFile;\n if (parsed === null || typeof parsed !== \"object\" || typeof parsed.files !== \"object\") {\n verdict = false;\n } else {\n for (const [depPath, recorded] of Object.entries(parsed.files)) {\n const current = statDep(depPath);\n if (current === null) {\n verdict = false;\n break;\n }\n if (current.mtimeMs === recorded.mtimeMs && current.size === recorded.size) continue;\n if (hashDep(depPath, current) !== recorded.hash) {\n verdict = false;\n break;\n }\n }\n }\n } catch {\n verdict = false;\n }\n depsetVerdictMemo.set(depsetFile, verdict);\n return verdict;\n }\n\n /**\n * Stat + hash every dep into a content-addressed record map. The id hashes\n * sorted (path, content-hash) pairs ONLY — mtimes are validation fast-path\n * hints and must not fork the file name across checkouts/touches. Returns\n * null when any dep cannot be read (an unvalidatable set must not persist).\n */\n private buildDepset(depPaths: readonly string[]): { id: string; content: DepsetFile } | null {\n const records: Record<string, DepRecord> = {};\n for (const depPath of depPaths) {\n if (records[depPath] !== undefined) continue;\n const stat = statDep(depPath);\n const hash = stat === null ? null : hashDep(depPath, stat);\n if (stat === null || hash === null) return null;\n records[depPath] = { hash, mtimeMs: stat.mtimeMs, size: stat.size };\n }\n const sorted = Object.keys(records).sort();\n const id = sha1(sorted.map((p) => `${p}\\0${(records[p] as DepRecord).hash}`).join(\"\\n\"));\n return { id, content: { files: records } };\n }\n\n /** Write a dep-set file if absent (content-addressed: same id ⟹ same bytes). */\n private writeDepset(id: string, content: DepsetFile): boolean {\n const file = this.depsetPath(id);\n try {\n if (fs.existsSync(file)) return true;\n const tmp = `${file}.${process.pid}.tmp`;\n fs.writeFileSync(tmp, JSON.stringify(content));\n fs.renameSync(tmp, file);\n return true;\n } catch {\n return false;\n }\n }\n\n private writeEntry(\n key: string,\n depsetId: string,\n result: string | null,\n stats?: CacheEntryStats,\n map?: CacheSourceMap | null,\n ): void {\n const entry: CacheEntry = { result, depset: depsetId };\n if (stats) entry.stats = stats;\n if (map !== undefined && map !== null) {\n // The remapping result is a class instance; persist its JSON fields.\n entry.map = {\n version: map.version,\n sources: map.sources,\n ...(map.sourcesContent !== undefined ? { sourcesContent: map.sourcesContent } : {}),\n names: map.names,\n mappings: map.mappings,\n ...(map.file !== undefined ? { file: map.file } : {}),\n };\n }\n try {\n const file = this.entryPath(key);\n const tmp = `${file}.${process.pid}.tmp`;\n fs.writeFileSync(tmp, JSON.stringify(entry));\n fs.renameSync(tmp, file);\n } catch {\n // Cache writes are best-effort; failures only cost a recompute.\n }\n }\n\n /** Persist an entry whose dependency set is fully known (static crawl complete). */\n save(\n key: string,\n result: string | null,\n depPaths: readonly string[],\n stats?: CacheEntryStats,\n map?: CacheSourceMap | null,\n ): void {\n this.ensureDir();\n const built = this.buildDepset(depPaths);\n if (built === null) return;\n if (!this.writeDepset(built.id, built.content)) return;\n this.writeEntry(key, built.id, result, stats, map);\n }\n\n /**\n * Queue an entry whose static dep crawl was incomplete. Persisted by\n * flushDeferred() against ONE end-of-build superset snapshot — recording\n * the snapshot at save time gave every entry a distinct point-in-time\n * copy (the executed-modules set grows as discovery progresses).\n */\n saveDeferred(\n key: string,\n result: string | null,\n stats?: CacheEntryStats,\n map?: CacheSourceMap | null,\n ): void {\n if (this.superset === null) return;\n this.pending.push({ key, result, stats, map });\n registerExitFlush(this);\n }\n\n /**\n * Flush queued superset entries against the current loader snapshot.\n * Wired to buildEnd and (as a fallback) process exit; idempotent.\n */\n flushDeferred(): void {\n if (this.pending.length === 0) return;\n const pending = this.pending;\n this.pending = [];\n const snapshot = this.superset === null ? null : this.superset();\n if (snapshot === null) return;\n this.ensureDir();\n const built = this.buildDepset(snapshot);\n if (built === null) return;\n if (!this.writeDepset(built.id, built.content)) return;\n for (const p of pending) {\n this.writeEntry(p.key, built.id, p.result, p.stats, p.map);\n }\n }\n\n /**\n * Discard queued superset entries (watch-mode file change): their results\n * predate the change, but a flush would record post-change dep hashes —\n * the one pairing that could validate a stale result.\n */\n dropDeferred(): void {\n this.pending = [];\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"disk-cache.js","names":["fs","path"],"sources":["../../src/unplugin/disk-cache.ts"],"sourcesContent":["/**\n * Persistent transform-result cache.\n *\n * The expensive part of the unplugin transform is discovery: executing the\n * schema file (and transitively its whole first-party import graph) through\n * jiti inside the bundler's single-threaded server process. The in-memory\n * caches die with the process, so every `vitest run` / build re-pays that\n * cost even when nothing changed — which is exactly the loop integration\n * tests live in.\n *\n * Entries are keyed by a hash of (plugin version, zod version, transform\n * options, file id, file content) and validated against a recorded snapshot\n * of first-party files. Validation uses an mtime+size fast path and falls\n * back to content hashing, so a `touch` without changes still hits. A\n * superset of true dependencies only over-invalidates, never serves stale\n * output.\n *\n * Layout (CACHE_FORMAT 2): dependency snapshots are CONTENT-ADDRESSED and\n * shared — `deps/<sha1>.json` holds the {path → hash/mtime/size} map, and\n * each entry stores only the dep-set id. The v1 format inlined the full dep\n * map into every entry; in a large-codebase field report 835 superset-\n * fallback entries each embedded a ~1,900-file point-in-time snapshot (813\n * DISTINCT snapshots — the executed-modules superset grows as the build\n * progresses, so per-entry copies cannot even dedupe), totalling 283 MB\n * that any commit invalidated wholesale. Sharing dep-sets also means each\n * unique set is parsed and validated once per process instead of once per\n * entry.\n *\n * Superset fallbacks (entries whose static dep crawl was incomplete) are\n * DEFERRED: queued in memory and flushed in buildEnd / process exit against\n * a single end-of-build superset snapshot, so every superset entry of a\n * build shares ONE dep-set file (recording the still-growing snapshot at\n * save time is what produced 813 distinct copies). Deferred entries are\n * dropped on watchChange — a dependency edited between queueing and flush\n * would pair post-change hashes with a pre-change result, which is the one\n * combination that can serve stale output. A killed process loses only its\n * pending superset entries (the static-complete majority persists\n * immediately); the next run re-pays discovery for those files alone.\n *\n * Writes are atomic (tmp file + rename) so concurrent bundler processes\n * (vitest workspace projects) can share one cache directory safely.\n */\n\nimport { createHash } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** Bump when the on-disk layout changes; mismatched directories are wiped. */\nconst CACHE_FORMAT = 2;\nconst META_FILE = \"_meta.json\";\nconst GC_MARKER = \"_gc\";\nconst DEPSET_DIR = \"deps\";\n/** GC at most once per day per cache directory. */\nconst GC_INTERVAL_MS = 24 * 60 * 60 * 1000;\n/** Entries unread for this long are presumed orphaned by key churn. */\nconst MAX_ENTRY_AGE_MS = 30 * 24 * 60 * 60 * 1000;\n/** Stale atomic-write temp files (crashed processes) older than this are removed. */\nconst MAX_TMP_AGE_MS = 60 * 60 * 1000;\n\nexport interface CacheEntryStats {\n schemas: number;\n optimized: number;\n}\n\ninterface DepRecord {\n hash: string;\n mtimeMs: number;\n size: number;\n}\n\nexport interface CacheEntry {\n /** Transformed code, or null when the transform produced no change. */\n result: string | null;\n /** Content-addressed id of the shared dep-set file (deps/<id>.json). */\n depset: string;\n /** Build stats to replay on cache hits (only present when schemas compiled). */\n stats?: CacheEntryStats;\n /** Composed sourcemap for `result` (original → transformed). */\n map?: CacheSourceMap | null;\n}\n\n/** JSON shape of the persisted sourcemap (mirrors TransformSourceMap). */\nexport interface CacheSourceMap {\n version: number;\n sources: (string | null)[];\n sourcesContent?: (string | null)[];\n names: string[];\n mappings: string;\n file?: string | null;\n}\n\ninterface DepsetFile {\n files: Record<string, DepRecord>;\n}\n\ninterface PendingEntry {\n key: string;\n result: string | null;\n stats?: CacheEntryStats | undefined;\n map?: CacheSourceMap | null | undefined;\n}\n\nfunction sha1(data: string | Buffer): string {\n return createHash(\"sha1\").update(data).digest(\"hex\");\n}\n\n/** Read own package version once for cache keying (src and dist both sit two levels below the package root). */\nfunction readPluginVersion(): string {\n try {\n const pkgPath = new URL(\"../../package.json\", import.meta.url);\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\")) as { version?: string };\n return pkg.version ?? \"0\";\n } catch {\n return \"0\";\n }\n}\n\n/** Resolve the installed zod version — generated code depends on zod internals. */\nfunction readZodVersion(): string {\n try {\n const pkgPath = path.join(process.cwd(), \"node_modules\", \"zod\", \"package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\")) as { version?: string };\n return pkg.version ?? \"0\";\n } catch {\n return \"0\";\n }\n}\n\n/**\n * Build fingerprint: a content hash of the package's own source trees.\n *\n * The published version string alone is not enough — file: installs, linked\n * monorepo packages, and canary builds rebuild the compiler without a version\n * bump, and serving codegen from an older compiler build would be silently\n * stale.\n *\n * CONTENT-addressed, for the same reason depset ids are: mtimes do not survive\n * an install. pnpm rewrites every mtime under its `copy` import method, and\n * copy is what you get whenever the store sits on a different filesystem than\n * the workspace — the default on CI runners that mount the store as its own\n * volume (`npm_config_package_import_method=copy`). An mtime fingerprint\n * therefore rotates on every `pnpm install --frozen-lockfile`, which changes\n * every cache key and makes a restored CI cache wholly unreachable: the archive\n * unpacks, and nothing in it is ever looked up. Hardlink and APFS-clone installs\n * DO preserve mtimes, so the bug hides locally and reproduces only on the\n * runners that need the cache most.\n *\n * A content hash is also strictly tighter than mtime for the stated purpose: a\n * rebuild that reproduces identical bytes no longer discards the whole cache.\n *\n * Hashing ~0.7 MB across ~250 files costs ~5ms — once per process, and only if\n * a key is actually built (see `keyPrefix`).\n */\nexport function computeBuildFingerprint(root: string): string {\n const found: string[] = [];\n const walk = (dir: string): void => {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const p = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walk(p);\n } else if (/\\.(?:js|ts|json)$/.test(entry.name)) {\n // Relative + POSIX-normalised: the absolute prefix differs per checkout\n // and the separator differs per platform, neither of which is a rebuild.\n found.push(path.relative(root, p).split(path.sep).join(\"/\"));\n }\n }\n };\n for (const sub of [\"dist\", \"src\"]) {\n walk(path.join(root, sub));\n }\n found.sort();\n const hash = createHash(\"sha1\");\n for (const rel of found) {\n hash.update(rel);\n hash.update(\"\\0\");\n try {\n hash.update(fs.readFileSync(path.join(root, rel)));\n } catch {\n // Unreadable file: fold in a marker rather than abort. Collapsing to a\n // constant on error would make genuinely different builds share a key.\n hash.update(\"<unreadable>\");\n }\n }\n return `${hash.digest(\"hex\")}:${found.length}`;\n}\n\nfunction readBuildFingerprint(): string {\n try {\n return computeBuildFingerprint(fileURLToPath(new URL(\"../..\", import.meta.url)));\n } catch {\n return \"0\";\n }\n}\n\n/**\n * Version + build identity shared by every key in the process. Computed on\n * first key, not at import: reading two package.json files and hashing dist is\n * pure waste for a build that never enables the cache.\n */\nlet keyPrefixMemo: string | null = null;\n\nfunction keyPrefix(): string {\n keyPrefixMemo ??= `${readPluginVersion()}\\0${readBuildFingerprint()}\\0${readZodVersion()}`;\n return keyPrefixMemo;\n}\n\n/**\n * Per-process memo: path → current stat (+ lazily computed content hash).\n * One fs.stat per file per process regardless of how many dep-sets\n * reference it; content is read at most once.\n */\nconst depStatMemo = new Map<string, { mtimeMs: number; size: number; hash?: string } | null>();\n\n/** Per-process memo: dep-set file path → validation verdict. */\nconst depsetVerdictMemo = new Map<string, boolean>();\n\nfunction statDep(depPath: string): { mtimeMs: number; size: number; hash?: string } | null {\n const memo = depStatMemo.get(depPath);\n if (memo !== undefined) return memo;\n let result: { mtimeMs: number; size: number; hash?: string } | null;\n try {\n const stat = fs.statSync(depPath);\n result = { mtimeMs: stat.mtimeMs, size: stat.size };\n } catch {\n result = null;\n }\n depStatMemo.set(depPath, result);\n return result;\n}\n\nfunction hashDep(\n depPath: string,\n stat: { mtimeMs: number; size: number; hash?: string },\n): string | null {\n if (stat.hash !== undefined) return stat.hash;\n try {\n stat.hash = sha1(fs.readFileSync(depPath));\n return stat.hash;\n } catch {\n return null;\n }\n}\n\n/** Reset the per-process dep validation memos (watch-mode file changes). */\nexport function resetDepValidationMemo(): void {\n depStatMemo.clear();\n depsetVerdictMemo.clear();\n}\n\n/** Instances with pending deferred entries, flushed on process exit. */\nconst flushOnExit = new Set<DiskCache>();\nlet exitHookInstalled = false;\n\nfunction registerExitFlush(cache: DiskCache): void {\n flushOnExit.add(cache);\n if (!exitHookInstalled) {\n exitHookInstalled = true;\n // 'exit' allows only synchronous work; every write below is sync.\n process.once(\"exit\", () => {\n for (const c of flushOnExit) c.flushDeferred();\n });\n }\n}\n\nexport class DiskCache {\n private readonly dir: string;\n private readonly optionsKey: string;\n /** Superset snapshot provider (loader's executed first-party modules). */\n private readonly superset: (() => string[] | null) | null;\n private pending: PendingEntry[] = [];\n private initialized = false;\n\n constructor(dir: string, optionsKey: string, superset?: () => string[] | null) {\n this.dir = dir;\n this.optionsKey = optionsKey;\n this.superset = superset ?? null;\n }\n\n /**\n * Resolve the cache directory: an explicit string wins; otherwise\n * node_modules/.cache/zod-compiler under cwd (falling back to a\n * project-local .zod-compiler-cache when node_modules doesn't exist).\n */\n static resolveDir(cacheOption: string | true): string {\n if (typeof cacheOption === \"string\") return path.resolve(cacheOption);\n const nm = path.join(process.cwd(), \"node_modules\");\n if (fs.existsSync(nm)) return path.join(nm, \".cache\", \"zod-compiler\");\n return path.join(process.cwd(), \".zod-compiler-cache\");\n }\n\n key(id: string, code: string): string {\n return sha1(`${keyPrefix()}\\0${this.optionsKey}\\0${id}\\0${code}`);\n }\n\n private entryPath(key: string): string {\n return path.join(this.dir, `${key}.json`);\n }\n\n private depsetPath(id: string): string {\n return path.join(this.dir, DEPSET_DIR, `${id}.json`);\n }\n\n /**\n * One-time directory init: wipe on format mismatch (v1 inline-deps caches\n * reached 283 MB in the field — disposable by definition), then a\n * throttled GC pass. Best-effort throughout; a concurrent wipe/GC from\n * another process can only cause cache misses, never stale hits.\n */\n private ensureDir(): void {\n if (this.initialized) return;\n this.initialized = true;\n try {\n const metaPath = path.join(this.dir, META_FILE);\n let format = 0;\n try {\n format = (JSON.parse(fs.readFileSync(metaPath, \"utf8\")) as { format?: number }).format ?? 0;\n } catch {\n // missing or unreadable marker — treat as foreign format\n }\n if (format !== CACHE_FORMAT) {\n fs.rmSync(this.dir, { recursive: true, force: true });\n fs.mkdirSync(path.join(this.dir, DEPSET_DIR), { recursive: true });\n fs.writeFileSync(metaPath, JSON.stringify({ format: CACHE_FORMAT }));\n } else {\n fs.mkdirSync(path.join(this.dir, DEPSET_DIR), { recursive: true });\n this.maybeGc();\n }\n } catch {\n // cache stays best-effort\n }\n }\n\n /**\n * Throttled sweep: entries older than MAX_ENTRY_AGE_MS (orphaned by key\n * churn — content/version/options keys never repeat once inputs change)\n * and dep-set files no surviving entry references. Runs at most once per\n * GC_INTERVAL_MS per directory; the marker is claimed BEFORE sweeping so\n * concurrent processes skip. Deleting a dep-set raced by a concurrent\n * entry write only costs that entry a future miss — save() re-creates\n * absent dep-set files.\n */\n private maybeGc(): void {\n const marker = path.join(this.dir, GC_MARKER);\n try {\n const stat = fs.statSync(marker, { throwIfNoEntry: false });\n if (stat !== undefined && Date.now() - stat.mtimeMs < GC_INTERVAL_MS) return;\n fs.writeFileSync(marker, \"\");\n\n const now = Date.now();\n const referenced = new Set<string>();\n for (const name of fs.readdirSync(this.dir)) {\n const p = path.join(this.dir, name);\n if (name.endsWith(\".tmp\")) {\n const st = fs.statSync(p, { throwIfNoEntry: false });\n if (st !== undefined && now - st.mtimeMs > MAX_TMP_AGE_MS) fs.rmSync(p, { force: true });\n continue;\n }\n if (!name.endsWith(\".json\") || name === META_FILE) continue;\n try {\n const st = fs.statSync(p);\n if (now - st.mtimeMs > MAX_ENTRY_AGE_MS) {\n fs.rmSync(p, { force: true });\n continue;\n }\n const entry = JSON.parse(fs.readFileSync(p, \"utf8\")) as { depset?: string };\n if (typeof entry.depset === \"string\") referenced.add(entry.depset);\n } catch {\n fs.rmSync(p, { force: true });\n }\n }\n for (const name of fs.readdirSync(path.join(this.dir, DEPSET_DIR))) {\n if (!name.endsWith(\".json\")) continue;\n const id = name.slice(0, -\".json\".length);\n if (!referenced.has(id)) {\n fs.rmSync(path.join(this.dir, DEPSET_DIR, name), { force: true });\n }\n }\n } catch {\n // best effort\n }\n }\n\n /** Load an entry and validate its dep-set. Any failure → null. */\n load(key: string): CacheEntry | null {\n this.ensureDir();\n let entry: CacheEntry;\n try {\n entry = JSON.parse(fs.readFileSync(this.entryPath(key), \"utf8\")) as CacheEntry;\n } catch {\n return null;\n }\n if (entry === null || typeof entry !== \"object\" || typeof entry.depset !== \"string\") {\n return null;\n }\n if (!this.validateDepset(entry.depset)) return null;\n return entry;\n }\n\n /**\n * Validate every dep in a dep-set, once per process per set: superset\n * entries all share one set, so the ~N-file validation (and the JSON\n * parse) happens once instead of once per entry.\n */\n private validateDepset(id: string): boolean {\n const depsetFile = this.depsetPath(id);\n const memo = depsetVerdictMemo.get(depsetFile);\n if (memo !== undefined) return memo;\n let verdict = true;\n try {\n const parsed = JSON.parse(fs.readFileSync(depsetFile, \"utf8\")) as DepsetFile;\n if (parsed === null || typeof parsed !== \"object\" || typeof parsed.files !== \"object\") {\n verdict = false;\n } else {\n for (const [depPath, recorded] of Object.entries(parsed.files)) {\n const current = statDep(depPath);\n if (current === null) {\n verdict = false;\n break;\n }\n if (current.mtimeMs === recorded.mtimeMs && current.size === recorded.size) continue;\n if (hashDep(depPath, current) !== recorded.hash) {\n verdict = false;\n break;\n }\n }\n }\n } catch {\n verdict = false;\n }\n depsetVerdictMemo.set(depsetFile, verdict);\n return verdict;\n }\n\n /**\n * Stat + hash every dep into a content-addressed record map. The id hashes\n * sorted (path, content-hash) pairs ONLY — mtimes are validation fast-path\n * hints and must not fork the file name across checkouts/touches. Returns\n * null when any dep cannot be read (an unvalidatable set must not persist).\n */\n private buildDepset(depPaths: readonly string[]): { id: string; content: DepsetFile } | null {\n const records: Record<string, DepRecord> = {};\n for (const depPath of depPaths) {\n if (records[depPath] !== undefined) continue;\n const stat = statDep(depPath);\n const hash = stat === null ? null : hashDep(depPath, stat);\n if (stat === null || hash === null) return null;\n records[depPath] = { hash, mtimeMs: stat.mtimeMs, size: stat.size };\n }\n const sorted = Object.keys(records).sort();\n const id = sha1(sorted.map((p) => `${p}\\0${(records[p] as DepRecord).hash}`).join(\"\\n\"));\n return { id, content: { files: records } };\n }\n\n /** Write a dep-set file if absent (content-addressed: same id ⟹ same bytes). */\n private writeDepset(id: string, content: DepsetFile): boolean {\n const file = this.depsetPath(id);\n try {\n if (fs.existsSync(file)) return true;\n const tmp = `${file}.${process.pid}.tmp`;\n fs.writeFileSync(tmp, JSON.stringify(content));\n fs.renameSync(tmp, file);\n return true;\n } catch {\n return false;\n }\n }\n\n private writeEntry(\n key: string,\n depsetId: string,\n result: string | null,\n stats?: CacheEntryStats,\n map?: CacheSourceMap | null,\n ): void {\n const entry: CacheEntry = { result, depset: depsetId };\n if (stats) entry.stats = stats;\n if (map !== undefined && map !== null) {\n // The remapping result is a class instance; persist its JSON fields.\n entry.map = {\n version: map.version,\n sources: map.sources,\n ...(map.sourcesContent !== undefined ? { sourcesContent: map.sourcesContent } : {}),\n names: map.names,\n mappings: map.mappings,\n ...(map.file !== undefined ? { file: map.file } : {}),\n };\n }\n try {\n const file = this.entryPath(key);\n const tmp = `${file}.${process.pid}.tmp`;\n fs.writeFileSync(tmp, JSON.stringify(entry));\n fs.renameSync(tmp, file);\n } catch {\n // Cache writes are best-effort; failures only cost a recompute.\n }\n }\n\n /** Persist an entry whose dependency set is fully known (static crawl complete). */\n save(\n key: string,\n result: string | null,\n depPaths: readonly string[],\n stats?: CacheEntryStats,\n map?: CacheSourceMap | null,\n ): void {\n this.ensureDir();\n const built = this.buildDepset(depPaths);\n if (built === null) return;\n if (!this.writeDepset(built.id, built.content)) return;\n this.writeEntry(key, built.id, result, stats, map);\n }\n\n /**\n * Queue an entry whose static dep crawl was incomplete. Persisted by\n * flushDeferred() against ONE end-of-build superset snapshot — recording\n * the snapshot at save time gave every entry a distinct point-in-time\n * copy (the executed-modules set grows as discovery progresses).\n */\n saveDeferred(\n key: string,\n result: string | null,\n stats?: CacheEntryStats,\n map?: CacheSourceMap | null,\n ): void {\n if (this.superset === null) return;\n this.pending.push({ key, result, stats, map });\n registerExitFlush(this);\n }\n\n /**\n * Flush queued superset entries against the current loader snapshot.\n * Wired to buildEnd and (as a fallback) process exit; idempotent.\n */\n flushDeferred(): void {\n if (this.pending.length === 0) return;\n const pending = this.pending;\n this.pending = [];\n const snapshot = this.superset === null ? null : this.superset();\n if (snapshot === null) return;\n // Every pending entry got here by RUNNING discovery, so the set of modules\n // that discovery executed cannot be empty. An empty snapshot means the\n // provider lost track of them — the loader was invalidated between save\n // and flush, or transforms ran somewhere this process cannot observe — and\n // an entry with no deps validates as fresh forever. Declining costs one\n // recompute; persisting would serve that stale result indefinitely.\n if (snapshot.length === 0) return;\n this.ensureDir();\n const built = this.buildDepset(snapshot);\n if (built === null) return;\n if (!this.writeDepset(built.id, built.content)) return;\n for (const p of pending) {\n this.writeEntry(p.key, built.id, p.result, p.stats, p.map);\n }\n }\n\n /**\n * Discard queued superset entries (watch-mode file change): their results\n * predate the change, but a flush would record post-change dep hashes —\n * the one pairing that could validate a stale result.\n */\n dropDeferred(): void {\n this.pending = [];\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/unplugin/index.ts"],"mappings":";;cA4Da,6BAAQ,iBAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/unplugin/index.ts"],"mappings":";;cAoEa,6BAAQ,iBAAA"}
@@ -1,8 +1,9 @@
1
1
  import { getFirstPartyModulePaths, invalidateModuleCache } from "../loader.js";
2
2
  import { RESOLVED_RUNTIME_ID_FILTER, RUNTIME_SPECIFIER_FILTER, VIRTUAL_RUNTIME_ID, WP_RUNTIME_ID, loadVirtual, resolveVirtualId } from "./virtual.js";
3
- import { TRANSFORM_ID_FILTER, log, shouldTransform, transformCodeFilter, transformCodeWithMap } from "./transform.js";
3
+ import { TRANSFORM_ID_FILTER, log, shouldTransform, transformCodeFilter, transformCodeWithMap, warn } from "./transform.js";
4
4
  import { collectStaticDeps, resetDepGraphMemo } from "./dep-graph.js";
5
5
  import { DiskCache, resetDepValidationMemo } from "./disk-cache.js";
6
+ import { PoolUnavailableError, TransformPool, poolTransformOptions, resolvePoolSize } from "./pool.js";
6
7
  import { BuildStatsAccumulator } from "./types.js";
7
8
  import process from "node:process";
8
9
  import { createUnplugin } from "unplugin";
@@ -50,13 +51,38 @@ const unplugin = createUnplugin((options, meta) => {
50
51
  const mode = options?.codegenMode ?? (VIRTUAL_MODULE_FRAMEWORKS.has(meta.framework) ? "lean" : "inline");
51
52
  const runtimeId = WP_FRAMEWORKS.has(meta.framework) ? WP_RUNTIME_ID : VIRTUAL_RUNTIME_ID;
52
53
  const cacheOption = options?.cache ?? true;
54
+ const poolSize = resolvePoolSize(options?.parallel);
55
+ const pool = TransformPool.create(poolSize);
56
+ /** Dedupes the in-process-fallback warning to one per build. */
57
+ let warnedPoolFallback = false;
58
+ if (poolSize > 0 && pool === null) warn("parallel was requested but the transform worker entry could not be located (is zod-compiler bundled into your build?). Falling back to in-process transforms.");
59
+ /**
60
+ * Executed-module superset for the disk cache's deferred entries.
61
+ *
62
+ * With a pool, discovery happens in the workers, so this process's own
63
+ * loader has executed nothing and `getFirstPartyModulePaths()` alone would
64
+ * report an empty (or absent) set — silently dropping every deferred
65
+ * entry, which on large graphs is most of them. Both sides are unioned:
66
+ * the workers' reports, plus anything the in-process fallback executed
67
+ * here. A worker that cannot track modules at all (Bun/Deno native import)
68
+ * reports null, and null wins — the cache declines to persist rather than
69
+ * record a set it knows is incomplete.
70
+ */
71
+ const executedModuleSuperset = () => {
72
+ const local = getFirstPartyModulePaths();
73
+ if (pool === null) return local;
74
+ const remote = pool.firstPartyModulePaths();
75
+ if (remote === null) return null;
76
+ if (local === null) return remote;
77
+ return [.../* @__PURE__ */ new Set([...local, ...remote])];
78
+ };
53
79
  const diskCache = cacheOption === false ? null : new DiskCache(DiskCache.resolveDir(cacheOption), JSON.stringify({
54
80
  mode,
55
81
  runtimeId,
56
82
  output: outputMode,
57
83
  schemas: schemasMode,
58
84
  hoist: typeof options?.hoist === "object" ? String(options.hoist.schemaNamePattern ?? "default") : options?.hoist ?? true
59
- }), getFirstPartyModulePaths);
85
+ }), executedModuleSuperset);
60
86
  const viteApply = options?.apply === "all" ? void 0 : options?.apply ?? ((_config, env) => env.command === "build" || env.mode === "test" || process.env["VITEST"] !== void 0);
61
87
  const codeFilter = transformCodeFilter(options);
62
88
  return {
@@ -111,18 +137,21 @@ const unplugin = createUnplugin((options, meta) => {
111
137
  };
112
138
  }
113
139
  }
114
- let discoveryRan = false;
115
- let substantialWork = false;
116
- let uncacheable = false;
117
- let fileStats = null;
118
- const output = await transformCodeWithMap(code, id, {
140
+ const transformOptions = {
119
141
  mode,
120
142
  runtimeId,
121
143
  verbose,
122
144
  zodCompat,
123
145
  compact,
124
146
  autoDiscover,
125
- hoist: options?.hoist,
147
+ hoist: options?.hoist
148
+ };
149
+ let discoveryRan = false;
150
+ let substantialWork = false;
151
+ let uncacheable = false;
152
+ let fileStats = null;
153
+ const inProcess = async () => transformCodeWithMap(code, id, {
154
+ ...transformOptions,
126
155
  onDiscovery() {
127
156
  discoveryRan = true;
128
157
  },
@@ -133,10 +162,27 @@ const unplugin = createUnplugin((options, meta) => {
133
162
  uncacheable = true;
134
163
  },
135
164
  onBuildStats(s) {
136
- stats.add(s);
137
165
  fileStats = s;
138
166
  }
139
167
  });
168
+ let output;
169
+ if (pool === null) output = await inProcess();
170
+ else try {
171
+ const r = await pool.run(code, id, poolTransformOptions(transformOptions));
172
+ output = r.output;
173
+ discoveryRan = r.discoveryRan;
174
+ substantialWork = r.substantialWork;
175
+ uncacheable = r.uncacheable;
176
+ fileStats = r.stats;
177
+ } catch (error) {
178
+ if (!(error instanceof PoolUnavailableError)) throw error;
179
+ if (!warnedPoolFallback) {
180
+ warnedPoolFallback = true;
181
+ warn(`${error.message} — compiling ${id} in-process instead. Later files take the same path without repeating this warning.`);
182
+ }
183
+ output = await inProcess();
184
+ }
185
+ if (fileStats !== null) stats.add(fileStats);
140
186
  const result = output === null ? null : output.code;
141
187
  const map = output === null ? null : output.map;
142
188
  cache.set(id, {
@@ -168,6 +214,7 @@ const unplugin = createUnplugin((options, meta) => {
168
214
  if (!SOURCE_LIKE.test(id)) return;
169
215
  if (id.includes("node_modules")) return;
170
216
  invalidateModuleCache();
217
+ pool?.invalidate();
171
218
  resetDepValidationMemo();
172
219
  resetDepGraphMemo();
173
220
  diskCache?.dropDeferred();