void 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (365) hide show
  1. package/AGENTS_TEMPLATE.md +1 -3
  2. package/README.md +19 -9
  3. package/dist/{account-cmd-CDry3B5V.mjs → account-cmd-l-FM7Ioq.mjs} +8 -7
  4. package/dist/{application-analysis-DC2yPvzY.mjs → application-analysis-DKy02UY0.mjs} +21 -32
  5. package/dist/{application-routing-C9khgLiS.mjs → application-routing-DQyUh_5S.mjs} +1 -1
  6. package/dist/{auth-D52yG7hl.mjs → auth-Dn2vw-SG.mjs} +9 -9
  7. package/dist/{auth-B60QDbjg.mjs → auth-LMJcNESe.mjs} +1 -1
  8. package/dist/{auth-link-DjeOptYi.mjs → auth-link-vz9Z2kmG.mjs} +5 -5
  9. package/dist/{auth-migrations-9vif1uj8.mjs → auth-migrations-BwHgeEsD.mjs} +8 -18
  10. package/dist/{auth-router-BRxIuqDi.mjs → auth-router-DvsCUVw4.mjs} +6 -19
  11. package/dist/{better-auth-shared-BVkjZ3vM.d.mts → better-auth-shared-CPvWD3nG.d.mts} +0 -4
  12. package/dist/{better-auth-shared-hy6RPh9W.mjs → better-auth-shared-DPpj8zxc.mjs} +94 -16
  13. package/dist/{build-cmd-Ce3C8huo.mjs → build-cmd-D6we0VjO.mjs} +8 -7
  14. package/dist/{cache-CS8Yxr5a.mjs → cache-CZRdzKx1.mjs} +8 -8
  15. package/dist/{cancel-deploy-mZsD88Xa.mjs → cancel-deploy-CQ3NnMvf.mjs} +6 -5
  16. package/dist/{cf-access-YTlqvAsk.mjs → cf-access-BcIBSKqG.mjs} +11 -12
  17. package/dist/{cf-build-output-U8o1Gl6X.mjs → cf-build-output-DjvB_872.mjs} +62 -85
  18. package/dist/cf-build-output-H9a97vME.mjs +2 -0
  19. package/dist/cf-user-agent-lKblH3zU.mjs +17 -0
  20. package/dist/cli/cli.mjs +48 -83
  21. package/dist/cli/cloudflare-operation-process.mjs +78 -78
  22. package/dist/cli/env-schema-probe.mjs +5 -6
  23. package/dist/{client-mC-dFGDD.mjs → client-DlDJuY0s.mjs} +30 -8
  24. package/dist/client-tso2OfcG.mjs +2 -0
  25. package/dist/{cloudflare-auth-Qdc7tw8F.mjs → cloudflare-auth-CuxfF6OA.mjs} +9 -17
  26. package/dist/{cloudflare-cmd-bhwCxAN6.mjs → cloudflare-cmd-lHqNlJGW.mjs} +6 -5
  27. package/dist/{cloudflare-config-Bktvwtpf.mjs → cloudflare-config-BmhBv3HV.mjs} +1 -1
  28. package/dist/{cloudflare-connect-CuhRgk3k.mjs → cloudflare-connect-2G3rL2rP.mjs} +4 -4
  29. package/dist/{cloudflare-operations-DoQrVHAt.mjs → cloudflare-operations-BlYLlowZ.mjs} +1 -1
  30. package/dist/{cloudflare-operations-DYa3coIk.mjs → cloudflare-operations-CwI1bChl.mjs} +41 -70
  31. package/dist/{cloudflare-process-B-wekeR6.mjs → cloudflare-process-2-TfIEzB.mjs} +20 -22
  32. package/dist/{config-CyQ-wVd7.mjs → config-5r-YpMC-.mjs} +1 -1
  33. package/dist/{config-C_XRIPx2.mjs → config-B_ggY-IO.mjs} +3 -3
  34. package/dist/config-BtSFvXZx.mjs +2 -0
  35. package/dist/config-CLYAosnQ.mjs +78 -0
  36. package/dist/{config-BMHb8RCj.d.mts → config-CSa15FvD.d.mts} +1 -1
  37. package/dist/{config-Br_JZD6u.mjs → config-DiowPT80.mjs} +24 -30
  38. package/dist/config-entry.d.mts +1 -1
  39. package/dist/{config-write-B1f88wJA.mjs → config-write-AHGesphx.mjs} +8 -7
  40. package/dist/{connect-H6EvP62X.mjs → connect-BfPBHHm-.mjs} +12 -11
  41. package/dist/{create-project-B2kk9Q0o.mjs → create-project-BdXEFKal.mjs} +7 -7
  42. package/dist/{create-project-Dbb4uiN-.mjs → create-project-qFDC45zg.mjs} +1 -1
  43. package/dist/{db-DpCKZto0.mjs → db-Xs80hcaA.mjs} +101 -133
  44. package/dist/{delete-CxlSvsvW.mjs → delete-d2vuou3j.mjs} +7 -7
  45. package/dist/{deploy-CeEbnv4b.mjs → deploy-Bqe1PYBw.mjs} +2351 -2497
  46. package/dist/deploy-VsWsVQkw.mjs +2 -0
  47. package/dist/{dev-inbox-D64ZKUod.mjs → dev-inbox-CK-UuPob.mjs} +7 -16
  48. package/dist/{discover-BcPPiF1o.mjs → discover-CkCYv9T_.mjs} +1 -2
  49. package/dist/discover-EDUf6JJw.mjs +2 -0
  50. package/dist/{prompts-ZAuJlyCj.mjs → dist-Bx1z6Eva.mjs} +1 -1
  51. package/dist/{dist-AoCzRTJE.mjs → dist-Cegxq8gs.mjs} +8 -8
  52. package/dist/{prompts-6OSVA7DJ.mjs → dist-D3m88sjf.mjs} +10 -53
  53. package/dist/{domain-B_9VgOm6.mjs → domain-BsDUBAvt.mjs} +29 -30
  54. package/dist/{dotenv-VQxupEUv.mjs → dotenv-BfU6eqFt.mjs} +20 -32
  55. package/dist/{drizzle-Beb2Am5S.mjs → drizzle-CF84FOVM.mjs} +16 -13
  56. package/dist/{email-BokHgjKj.mjs → email-_lGAHOhp.mjs} +24 -19
  57. package/dist/{email--tEtMoj_.mjs → email-d_Aux1vM.mjs} +1 -1
  58. package/dist/{entry-CF5wvH4w.mjs → entry-CMAkqBmU.mjs} +6 -6
  59. package/dist/{env-_v349JBd.mjs → env-BxocywBB.mjs} +25 -6
  60. package/dist/{env-BdTc-K2L.mjs → env-C74QepDE.mjs} +10 -10
  61. package/dist/{env-D5liuQYR.mjs → env-DMA3cXZt.mjs} +1 -1
  62. package/dist/{env-raw-Cx8ElDdj.mjs → env-raw-jZpP5n47.mjs} +1 -2
  63. package/dist/{env-types-D5kds_OZ.mjs → env-types-CwXgeMgL.mjs} +1 -1
  64. package/dist/{env-validation-cZBvGWaP.mjs → env-validation-CrVNjsPm.mjs} +1 -1
  65. package/dist/{env-validation-vBSQ8gw5.mjs → env-validation-Dxdx_5wo.mjs} +17 -21
  66. package/dist/{fetch-_SeGZao9.d.mts → fetch-BPCYVi3H.d.mts} +20 -1
  67. package/dist/{fetch-BIZJh7vR.mjs → fetch-sAcwjzLO.mjs} +1 -1
  68. package/dist/{fetch-stream-Nm3hkZbT.mjs → fetch-stream-PGgz4uGz.mjs} +18 -23
  69. package/dist/{fetch-stream-Bjf0hoZb.d.mts → fetch-stream-obvZ7fvO.d.mts} +1 -16
  70. package/dist/{gen-BhDlTpwW.mjs → gen-DtkQhvQg.mjs} +40 -57
  71. package/dist/gen-dA9hW7AV.mjs +2 -0
  72. package/dist/{github-cmd-hhkTzlGS.mjs → github-cmd-DaDdBbSS.mjs} +65 -164
  73. package/dist/{handler-DghKr6dU.mjs → handler-BHAn2fZJ.mjs} +1 -0
  74. package/dist/{handler-BeK3weD4.d.mts → handler-CNAwjRcW.d.mts} +4 -11
  75. package/dist/{head-Do8P4puT.d.mts → head-C8K_iGe6.d.mts} +1 -1
  76. package/dist/{head-D_QRR5Yd.mjs → head-D2VF4ApL.mjs} +29 -51
  77. package/dist/{headers-2rKda9Y6.mjs → headers-DlfR78Nu.mjs} +3 -3
  78. package/dist/{help-Bv44e2tm.mjs → help-0bTe_lID.mjs} +13 -32
  79. package/dist/help-DfQYVfGY.mjs +2 -0
  80. package/dist/{inbound-fRkqcXaC.mjs → inbound-PfQDLXc5.mjs} +23 -24
  81. package/dist/index.d.mts +19 -2
  82. package/dist/index.mjs +619 -532
  83. package/dist/{info-DhtTKh65.mjs → info-PYWwkWyt.mjs} +4 -4
  84. package/dist/{init-BMf4T03q.mjs → init-DaBGmaXC.mjs} +161 -198
  85. package/dist/{link-BqYsSS_I.mjs → link-C6fjafep.mjs} +10 -10
  86. package/dist/{list-_kBToO-l.mjs → list-BTFBlqKl.mjs} +9 -8
  87. package/dist/{live-CB1y5IuC.mjs → live-CgQuTpqD.mjs} +15 -16
  88. package/dist/{live-iAuEH1hd.d.mts → live-Du8jXwOW.d.mts} +4 -2
  89. package/dist/{local-d1-fGxYrVD6.mjs → local-d1-ga5O_idV.mjs} +2 -2
  90. package/dist/{login-DFuo-RWO.mjs → login-GKcNUmtI.mjs} +7 -7
  91. package/dist/login-mx_ydhmp.mjs +2 -0
  92. package/dist/{logs-DYgnh6dH.mjs → logs-BiMSULmc.mjs} +28 -11
  93. package/dist/migrate-BHp07yJE.mjs +2 -0
  94. package/dist/{migrate-CV1on-yJ.mjs → migrate-NLsjBbuM.mjs} +34 -20
  95. package/dist/{mime-D5Nmdzf7.mjs → mime-DxCznlJq.mjs} +45 -95
  96. package/dist/{node-C6Q6e1Es.mjs → node-DJicPvPj.mjs} +5 -5
  97. package/dist/{operator-args-DdY54Bgb.mjs → operator-args-DOBhIaIg.mjs} +10 -1
  98. package/dist/{operator-auth-2dsGlTT5.mjs → operator-auth-BEZ_u0Cz.mjs} +2 -2
  99. package/dist/{operator-client-BFqpp6z0.mjs → operator-client-DbDv4vtE.mjs} +2 -2
  100. package/dist/{operator-cmd-CUGHXrQf.mjs → operator-cmd-BT4hgavL.mjs} +24 -11
  101. package/dist/operator-cmd-CwBB-1yb.mjs +2 -0
  102. package/dist/{output-BvdaehtE.mjs → output-C50ag2r3.mjs} +20 -2
  103. package/dist/pages/client.d.mts +19 -51
  104. package/dist/pages/client.mjs +204 -225
  105. package/dist/pages/head-client.d.mts +1 -1
  106. package/dist/pages/head.d.mts +1 -1
  107. package/dist/pages/head.mjs +1 -1
  108. package/dist/pages/index.d.mts +2 -2
  109. package/dist/pages/index.mjs +6 -12
  110. package/dist/pages/islands-plugin.d.mts +1 -1
  111. package/dist/pages/islands-plugin.mjs +21 -25
  112. package/dist/pages/protocol.d.mts +2 -2
  113. package/dist/pages/protocol.mjs +2 -2
  114. package/dist/{plan-CJGEeTEI.mjs → plan-BhLO2p4w.mjs} +2 -1
  115. package/dist/plan-CH2G51je.mjs +2 -0
  116. package/dist/platform-args-D4E1PjVw.mjs +2 -0
  117. package/dist/{platform-args-DSV2p4nY.mjs → platform-args-DUeXY9Om.mjs} +1 -1
  118. package/dist/{platform-auth-config-Bosx7U1h.mjs → platform-auth-config-CkSx3usH.mjs} +40 -36
  119. package/dist/{platform-auth-protection-DcbL_LW_.mjs → platform-auth-protection-BJimB_p9.mjs} +9 -8
  120. package/dist/{platform-auth-recovery-WfTZ8K6o.mjs → platform-auth-recovery-BU6izFAU.mjs} +8 -7
  121. package/dist/{platform-cmd-DbKtQb_4.mjs → platform-cmd-BbY1dWiM.mjs} +14 -13
  122. package/dist/{platform-cmd-dyFUof0t.mjs → platform-cmd-BbmJiq5O.mjs} +1 -1
  123. package/dist/{platform-discovery-BC0HCKYt.mjs → platform-discovery-DIYz8V63.mjs} +1 -1
  124. package/dist/{platform-domain-Bsq43AJp.mjs → platform-domain-CHVS6cV0.mjs} +6 -6
  125. package/dist/{platform-lifecycle--5gWmDoI.mjs → platform-lifecycle-ByAlpkhV.mjs} +1 -1
  126. package/dist/{platform-lifecycle-Cfi0o6xM.mjs → platform-lifecycle-DzCwqhFq.mjs} +76 -387
  127. package/dist/{platform-management-Bbwf6-Dq.mjs → platform-management-CBOT4BSM.mjs} +1 -1
  128. package/dist/{platform-management-BrF9STZp.mjs → platform-management-CuuVDhyt.mjs} +7 -7
  129. package/dist/{platform-plans-config-DU-8nC2Y.mjs → platform-plans-config-CN-heS4q.mjs} +5 -4
  130. package/dist/{platform-recovery-DQAqAQYu.mjs → platform-recovery-CskSJgLh.mjs} +3 -3
  131. package/dist/{platform-registry-P387v42A.mjs → platform-registry-Cjv2Qf0r.mjs} +5 -1
  132. package/dist/platform-setup-BBxRxmFC.mjs +299 -0
  133. package/dist/{plugin-inference-CXWnn79A.mjs → plugin-inference-BlQbOL0P.mjs} +43 -42
  134. package/dist/prepare-DRUV-g3X.mjs +2 -0
  135. package/dist/{prepare-WoUubFKL.mjs → prepare-Do96654b.mjs} +8 -8
  136. package/dist/{prepare-BMrRWLwP.mjs → prepare-Dq9hTIbo.mjs} +4 -4
  137. package/dist/{prerender-render-D9qbLs2f.mjs → prerender-render-Ct20jftc.mjs} +17 -3
  138. package/dist/prerender-render.mjs +1 -1
  139. package/dist/{project-DQstYTxO.mjs → project-BAm2aB6Q.mjs} +1 -1
  140. package/dist/{project-BiaPQ7ur.mjs → project-DedqCp59.mjs} +1 -1
  141. package/dist/{project-cmd-BbH2LptN.mjs → project-cmd-DaoRrn-8.mjs} +19 -19
  142. package/dist/{project-team-Yiww3-go.mjs → project-team-Q_lCA7Io.mjs} +15 -18
  143. package/dist/{project-token-BrtT2NkY.mjs → project-token-BArgK9o1.mjs} +14 -17
  144. package/dist/{project-tsconfig-B8GeM0RM.mjs → project-tsconfig-CVJhNMxs.mjs} +2 -1
  145. package/dist/{project-zero-trust-nYJ2V2nc.mjs → project-zero-trust-KO9Hu2Pq.mjs} +12 -9
  146. package/dist/{protocol-BX8t2jiw.d.mts → protocol-BPZaNqBo.d.mts} +4 -2
  147. package/dist/{protocol-U7bfjHmA.mjs → protocol-CfaUwNs8.mjs} +46 -49
  148. package/dist/{providers-CJlNS3kT.mjs → providers-BXuhmuub.mjs} +1 -12
  149. package/dist/{provision-VeqAC77A.mjs → provision-BfaLiWbj.mjs} +38 -41
  150. package/dist/{provision-DMTPG8Rh.mjs → provision-Wu9ZBk9L.mjs} +2 -3
  151. package/dist/{requests-TIz_jtSu.mjs → requests-C7Oeoh5u.mjs} +5 -5
  152. package/dist/resolve-project-BIeYs9Cw.mjs +2 -0
  153. package/dist/{resolve-project-BG5ORGID.mjs → resolve-project-CP8rZs_p.mjs} +6 -1
  154. package/dist/{rollback-dBqICPzj.mjs → rollback-DVAtrLAl.mjs} +30 -37
  155. package/dist/{route-types-Clm4WD0B.mjs → route-types-CbtsIeFn.mjs} +1 -1
  156. package/dist/route-url-BgPja1GS.d.mts +5 -0
  157. package/dist/route-url-BjGv_KKY.mjs +28 -0
  158. package/dist/runner-9wjsSZx-.mjs +2 -0
  159. package/dist/{runner--2KF6Kua.mjs → runner-DMBKSrRk.mjs} +2 -2
  160. package/dist/{runner-mysql-DmWchSWJ.mjs → runner-mysql-CEzKtmFb.mjs} +8 -8
  161. package/dist/{runner-mysql-CEG5JozK.mjs → runner-mysql-CkFqZ6CE.mjs} +1 -1
  162. package/dist/{runner-pg-Bh4AgZdj.mjs → runner-pg-BGYRFdRp.mjs} +1 -1
  163. package/dist/runtime/ai.d.mts +1 -1
  164. package/dist/runtime/ai.mjs +34 -95
  165. package/dist/runtime/better-auth-mysql.d.mts +1 -1
  166. package/dist/runtime/better-auth-mysql.mjs +4 -9
  167. package/dist/runtime/better-auth-pg.d.mts +1 -1
  168. package/dist/runtime/better-auth-pg.mjs +5 -10
  169. package/dist/runtime/better-auth.d.mts +1 -1
  170. package/dist/runtime/better-auth.mjs +3 -3
  171. package/dist/runtime/client-react.d.mts +2 -3
  172. package/dist/runtime/client-react.mjs +2 -2
  173. package/dist/runtime/client-solid.d.mts +2 -3
  174. package/dist/runtime/client-solid.mjs +2 -2
  175. package/dist/runtime/client-svelte.d.mts +2 -3
  176. package/dist/runtime/client-svelte.mjs +2 -2
  177. package/dist/runtime/client-vue.d.mts +2 -3
  178. package/dist/runtime/client-vue.mjs +2 -2
  179. package/dist/runtime/client.d.mts +2 -3
  180. package/dist/runtime/client.mjs +2 -2
  181. package/dist/runtime/db.mjs +1 -1
  182. package/dist/runtime/durable.mjs +3 -10
  183. package/dist/runtime/email/testing.mjs +5 -5
  184. package/dist/runtime/email.d.mts +2 -1
  185. package/dist/runtime/email.mjs +11 -27
  186. package/dist/runtime/env-public.mjs +15 -20
  187. package/dist/runtime/env.mjs +1 -1
  188. package/dist/runtime/fetch-stream.d.mts +2 -2
  189. package/dist/runtime/fetch-stream.mjs +1 -1
  190. package/dist/runtime/fetch.d.mts +2 -3
  191. package/dist/runtime/fetch.mjs +1 -1
  192. package/dist/runtime/handler.d.mts +1 -1
  193. package/dist/runtime/handler.mjs +2 -2
  194. package/dist/runtime/isr.mjs +1 -1
  195. package/dist/runtime/kv.mjs +10 -11
  196. package/dist/runtime/live-client.d.mts +1 -1
  197. package/dist/runtime/live-client.mjs +12 -22
  198. package/dist/runtime/live-server.mjs +27 -35
  199. package/dist/runtime/live.d.mts +2 -2
  200. package/dist/runtime/live.mjs +2 -2
  201. package/dist/runtime/migration-handler.mjs +4 -81
  202. package/dist/runtime/queues.mjs +1 -1
  203. package/dist/runtime/remote/binding-handler.d.mts +2 -2
  204. package/dist/runtime/remote/binding-handler.mjs +8 -9
  205. package/dist/runtime/remote/index.mjs +27 -77
  206. package/dist/runtime/route-url.d.mts +2 -4
  207. package/dist/runtime/route-url.mjs +1 -1
  208. package/dist/runtime/routing.d.mts +1 -1
  209. package/dist/runtime/routing.mjs +15 -26
  210. package/dist/runtime/sandbox.mjs +2 -2
  211. package/dist/runtime/sse.mjs +1 -1
  212. package/dist/runtime/storage.mjs +1 -1
  213. package/dist/runtime/validator.d.mts +1 -1
  214. package/dist/runtime/ws-server.d.mts +2 -2
  215. package/dist/runtime/ws-server.mjs +30 -61
  216. package/dist/runtime/ws.d.mts +1 -1
  217. package/dist/runtime/ws.mjs +1 -1
  218. package/dist/{scan-C8dA7iM4.mjs → scan-C3uDzz6J.mjs} +12 -13
  219. package/dist/{secret-AKnNXNP5.mjs → secret-BvtB7IjY.mjs} +15 -15
  220. package/dist/{skills-BPQUGClu.mjs → skills-BgVMvQdy.mjs} +2 -2
  221. package/dist/{sse-CQNaDFFV.mjs → sse-CMvCgjNk.mjs} +1 -1
  222. package/dist/{subcommand-prompt-B6WS9Aoz.mjs → subcommand-prompt-BSlnD1fI.mjs} +2 -2
  223. package/dist/sveltekit.d.mts +0 -2
  224. package/dist/terminal-log-text-D9ACPOuG.mjs +32 -0
  225. package/dist/validate-BJr9LfXy.mjs +2 -0
  226. package/dist/{validate-ymhap6M_.mjs → validate-C9qJArv7.mjs} +9 -28
  227. package/dist/{defer-YsYDUoii.mjs → validation-error-Dg9Recn1.mjs} +11 -11
  228. package/dist/{wrangler-DhUerFkW.mjs → wrangler-D9KTuqnv.mjs} +89 -194
  229. package/dist/{ws-C79j4aED.d.mts → ws-CChrrAU-.d.mts} +1 -1
  230. package/dist/{yarn-pnp-BV4AzNul.mjs → yarn-pnp-CUX_iTPA.mjs} +19 -27
  231. package/{skills/void/docs → docs}/guide/ai.md +5 -3
  232. package/{skills/void/docs → docs}/guide/auth.md +37 -1
  233. package/{skills/void/docs → docs}/guide/database/postgresql.md +2 -2
  234. package/{skills/void/docs → docs}/guide/deployment.md +2 -2
  235. package/{skills/void/docs → docs}/guide/edge/revalidation.md +1 -1
  236. package/{skills/void/docs → docs}/guide/edge/rewrites.md +2 -4
  237. package/{skills/void/docs → docs}/guide/email/receiving.md +2 -2
  238. package/{skills/void/docs → docs}/guide/email/sending.md +0 -2
  239. package/{skills/void/docs → docs}/guide/env-vars.md +4 -0
  240. package/{skills/void/docs → docs}/guide/jobs.md +2 -2
  241. package/{skills/void/docs → docs}/guide/live.md +10 -5
  242. package/{skills/void/docs → docs}/guide/pages-routing/actions-and-forms.md +20 -8
  243. package/{skills/void/docs → docs}/guide/pages-routing/loaders.md +7 -3
  244. package/{skills/void/docs → docs}/guide/pages-routing/markdown.md +15 -23
  245. package/{skills/void/docs → docs}/guide/pages-routing/overview.md +6 -4
  246. package/{skills/void/docs → docs}/guide/platform/administration/access.md +3 -11
  247. package/{skills/void/docs → docs}/guide/platform/administration/operations.md +3 -12
  248. package/{skills/void/docs → docs}/guide/platform/administration/projects.md +1 -1
  249. package/{skills/void/docs → docs}/guide/platform/development/local.md +4 -2
  250. package/{skills/void/docs → docs}/guide/platform/development/runtime.md +1 -1
  251. package/{skills/void/docs → docs}/guide/platform/development/schema-ci.md +1 -1
  252. package/{skills/void/docs → docs}/guide/platform/installation/domains.md +3 -1
  253. package/{skills/void/docs → docs}/guide/platform/installation/first-deployment.md +4 -2
  254. package/{skills/void/docs → docs}/guide/platform/installation/maintenance.md +4 -0
  255. package/{skills/void/docs → docs}/guide/project-collaboration.md +7 -3
  256. package/{skills/void/docs → docs}/guide/queues.md +0 -2
  257. package/{skills/void/docs → docs}/guide/remote-dev.md +5 -5
  258. package/{skills/void/docs → docs}/guide/sandboxes.md +2 -2
  259. package/{skills/void/docs → docs}/guide/sse.md +0 -2
  260. package/{skills/void/docs → docs}/guide/storage.md +7 -2
  261. package/{skills/void/docs → docs}/guide/websockets.md +4 -0
  262. package/docs/integrations/agents.md +37 -0
  263. package/{skills/void/docs → docs}/integrations/frameworks/astro.md +3 -3
  264. package/{skills/void/docs → docs}/integrations/frameworks/nuxt.md +6 -2
  265. package/{skills/void/docs → docs}/integrations/hono.md +15 -7
  266. package/{skills/void/docs → docs}/integrations/nodejs-bun-deno.md +1 -0
  267. package/{skills/void/docs → docs}/reference/api/auth.md +1 -1
  268. package/{skills/void/docs → docs}/reference/api/handlers.md +4 -2
  269. package/{skills/void/docs → docs}/reference/api/pages.md +4 -0
  270. package/{skills/void/docs → docs}/reference/cli/auth.md +1 -6
  271. package/{skills/void/docs → docs}/reference/cli/database.md +3 -4
  272. package/{skills/void/docs → docs}/reference/cli/deploy.md +1 -1
  273. package/{skills/void/docs → docs}/reference/cli/generate.md +1 -1
  274. package/{skills/void/docs → docs}/reference/cli/github.md +3 -12
  275. package/{skills/void/docs → docs}/reference/cli/platform-config.md +1 -1
  276. package/{skills/void/docs → docs}/reference/cli/platform-email.md +2 -3
  277. package/{skills/void/docs → docs}/reference/cli/platform-installation.md +1 -1
  278. package/{skills/void/docs → docs}/reference/cli/platform-operations.md +3 -0
  279. package/{skills/void/docs → docs}/reference/cli/platform-users.md +2 -2
  280. package/{skills/void/docs → docs}/reference/cli/project.md +5 -4
  281. package/{skills/void/docs → docs}/reference/cli/secrets.md +3 -3
  282. package/{skills/void/docs → docs}/reference/cli/setup.md +9 -11
  283. package/{skills/void/docs → docs}/reference/cli.md +1 -1
  284. package/{skills/void/docs → docs}/reference/config.md +13 -13
  285. package/{skills/void/docs → docs}/reference/resource-inference.md +1 -16
  286. package/package.json +34 -30
  287. package/schema.json +1 -1
  288. package/skills/migrate-vite-cloudflare-to-void/SKILL.md +4 -1
  289. package/skills/void/SKILL.md +9 -5
  290. package/dist/cf-build-output-ChY6-XWP.mjs +0 -2
  291. package/dist/client-BxVixJBI.mjs +0 -2
  292. package/dist/config-0acf1oys.mjs +0 -2
  293. package/dist/config-D8MOgiQV.mjs +0 -32
  294. package/dist/deploy-qmiLE1G1.mjs +0 -2
  295. package/dist/discover-AMCBXivQ.mjs +0 -2
  296. package/dist/fetch-error-Sp1R4mZv.d.mts +0 -20
  297. package/dist/gen-qCBwH3fZ.mjs +0 -2
  298. package/dist/help-Dw5f9Xrg.mjs +0 -2
  299. package/dist/login-D7meQg_3.mjs +0 -2
  300. package/dist/migrate-CEu2X5DB.mjs +0 -2
  301. package/dist/operator-cmd-DRPvhpjM.mjs +0 -2
  302. package/dist/plan-COtUvdMA.mjs +0 -2
  303. package/dist/platform-args-Dcy_5b_Y.mjs +0 -2
  304. package/dist/prepare-BBknoZI1.mjs +0 -2
  305. package/dist/resolve-project-DtbidV4y.mjs +0 -2
  306. package/dist/route-url-CG7U-cRN.mjs +0 -15
  307. package/dist/runner-voIltZrY.mjs +0 -2
  308. package/dist/validate-pna7R3Pv.mjs +0 -2
  309. package/skills/void/docs/integrations/agents.md +0 -26
  310. package/dist/{runner-pg-D4OPlil-.mjs → runner-pg-DV8p_zjA.mjs} +4 -4
  311. package/{skills/void/docs → docs}/guide/app-types.md +0 -0
  312. package/{skills/void/docs → docs}/guide/database/d1.md +2 -2
  313. package/{skills/void/docs → docs}/guide/database/mysql.md +0 -0
  314. package/{skills/void/docs → docs}/guide/database.md +8 -8
  315. package/{skills/void/docs → docs}/guide/durable-state.md +0 -0
  316. package/{skills/void/docs → docs}/guide/edge/headers.md +0 -0
  317. package/{skills/void/docs → docs}/guide/edge/prerendering.md +0 -0
  318. package/{skills/void/docs → docs}/guide/edge/redirects.md +0 -0
  319. package/{skills/void/docs → docs}/guide/edge/static-assets.md +0 -0
  320. package/{skills/void/docs → docs}/guide/email/domains.md +0 -0
  321. package/{skills/void/docs → docs}/guide/email.md +0 -0
  322. package/{skills/void/docs → docs}/guide/index.md +0 -0
  323. package/{skills/void/docs → docs}/guide/kv.md +0 -0
  324. package/{skills/void/docs → docs}/guide/pages-routing/head.md +0 -0
  325. package/{skills/void/docs → docs}/guide/pages-routing/islands.md +2 -2
  326. package/{skills/void/docs → docs}/guide/pages-routing/layouts.md +0 -0
  327. package/{skills/void/docs → docs}/guide/pages-routing/view-transitions.md +0 -0
  328. package/{skills/void/docs → docs}/guide/platform/administration/email.md +0 -0
  329. package/{skills/void/docs → docs}/guide/platform/administration/plans.md +0 -0
  330. package/{skills/void/docs → docs}/guide/platform/administration/zero-trust.md +0 -0
  331. package/{skills/void/docs → docs}/guide/platform/installation/ci.md +0 -0
  332. package/{skills/void/docs → docs}/guide/platform/installation/credentials.md +0 -0
  333. package/{skills/void/docs → docs}/guide/platform/installation/prerequisites.md +0 -0
  334. package/{skills/void/docs → docs}/guide/platform/installation/setup.md +0 -0
  335. package/{skills/void/docs → docs}/guide/platform/installation/uninstall.md +0 -0
  336. package/{skills/void/docs → docs}/guide/platform-administration.md +0 -0
  337. package/{skills/void/docs → docs}/guide/platform-development.md +0 -0
  338. package/{skills/void/docs → docs}/guide/quickstart.md +0 -0
  339. package/{skills/void/docs → docs}/guide/self-hosted-platform.md +0 -0
  340. package/{skills/void/docs → docs}/guide/server-routing.md +3 -3
  341. package/{skills/void/docs → docs}/guide/ssg.md +0 -0
  342. package/{skills/void/docs → docs}/guide/ssr.md +0 -0
  343. package/{skills/void/docs → docs}/guide/type-safety.md +0 -0
  344. package/{skills/void/docs → docs}/guide/typed-fetch.md +0 -0
  345. package/{skills/void/docs → docs}/index.md +0 -0
  346. package/{skills/void/docs → docs}/integrations/cloudflare.md +0 -0
  347. package/{skills/void/docs → docs}/integrations/frameworks/analog.md +3 -3
  348. package/{skills/void/docs → docs}/integrations/frameworks/overview.md +0 -0
  349. package/{skills/void/docs → docs}/integrations/frameworks/react-router.md +3 -3
  350. package/{skills/void/docs → docs}/integrations/frameworks/sveltekit.md +1 -1
  351. package/{skills/void/docs → docs}/integrations/frameworks/tanstack-start.md +2 -2
  352. package/{skills/void/docs → docs}/reference/api/client.md +0 -0
  353. package/{skills/void/docs → docs}/reference/api/database.md +2 -2
  354. /package/{skills/void/docs → docs}/reference/api/durable.md +0 -0
  355. /package/{skills/void/docs → docs}/reference/api/env.md +0 -0
  356. /package/{skills/void/docs → docs}/reference/api/plugin.md +0 -0
  357. /package/{skills/void/docs → docs}/reference/api/resources.md +0 -0
  358. /package/{skills/void/docs → docs}/reference/api/rewrites.md +0 -0
  359. /package/{skills/void/docs → docs}/reference/api/types.md +0 -0
  360. /package/{skills/void/docs → docs}/reference/api.md +0 -0
  361. /package/{skills/void/docs → docs}/reference/cli/domains.md +0 -0
  362. /package/{skills/void/docs → docs}/reference/cli/email.md +0 -0
  363. /package/{skills/void/docs → docs}/reference/cli/platform-zero-trust.md +0 -0
  364. /package/{skills/void/docs → docs}/reference/cli/platform.md +0 -0
  365. /package/{skills/void/docs → docs}/reference/structure.md +0 -0
package/dist/index.mjs CHANGED
@@ -1,50 +1,51 @@
1
1
  import { n as __toESM } from "./rolldown-runtime-DXIUcv95.mjs";
2
2
  import { a as join, l as relative, n as dirname, u as resolve } from "./dist-BVqXeR_h.mjs";
3
3
  import { t as require_picocolors } from "./picocolors-BTps1_gs.mjs";
4
- import { d as walk, n as FRAMEWORK_SCAN_DIRS, p as applyBindingOverrides, t as DEFAULT_SCAN_DIRS, u as inferBindingsFromSource } from "./plugin-inference-CXWnn79A.mjs";
4
+ import { d as staticModuleReferences, f as walk, m as applyBindingOverrides, n as FRAMEWORK_SCAN_DIRS, t as DEFAULT_SCAN_DIRS, u as inferBindingsFromSource } from "./plugin-inference-BlQbOL0P.mjs";
5
5
  import { n as readProjectPaths, t as assertNoRootSourceConventionConflicts } from "./project-paths-GgcMozYz.mjs";
6
6
  import { n as loadVoidAuthConfig } from "./config-DPByhVck.mjs";
7
- import { d as isNodeTarget, m as readConfig, u as getDatabaseDialect } from "./config-Br_JZD6u.mjs";
7
+ import { d as isNodeTarget, m as readConfig, u as getDatabaseDialect } from "./config-DiowPT80.mjs";
8
8
  import { i as isMemberExpression, n as isIdentifier, r as isLiteral } from "./ast-CPGPjb0V.mjs";
9
9
  import { t as isRouteEnabledForEnvironment } from "./parse-filename-CUbj-1MP.mjs";
10
- import { r as scanQueuesSync, s as scanJobsSync, t as analyzeApplicationSync } from "./application-analysis-DC2yPvzY.mjs";
11
- import { a as validateSsrEntries, n as SSR_SERVER_ENTRY_RELATIVE_PATHS, o as validateSsrEntry, t as SSR_CLIENT_ENTRY_RELATIVE_PATHS } from "./entry-CF5wvH4w.mjs";
12
- import { r as hasEmailHandlers } from "./email--tEtMoj_.mjs";
13
- import { i as resolveLayoutIds } from "./scan-C8dA7iM4.mjs";
14
- import { a as hasSandboxSqliteMigration, c as resolveSandboxConfig, i as assertSandboxNamespaceConfig, l as sandboxContainerConfig, n as DEFAULT_SANDBOX_CLASS_NAME, r as SANDBOX_MIGRATION_TAG } from "./config-C_XRIPx2.mjs";
15
- import { Ct as hasDocumentRoutes, H as selectNativeVersionMetadataBinding, J as collectPages, St as hasApiRoutes, Tt as resolveAssetRoutingPolicy, V as CLOUDFLARE_PRESERVE_BINDINGS_BUILD_ENV, X as hasRevalidate, Y as collectRevalidate, at as resolveDistDir, lt as resolveWorkerDirName, o as prerenderPages, s as prerenderPagesNode, u as SEND_EMAIL_BINDING, xt as generateQueueResolverSource } from "./deploy-CeEbnv4b.mjs";
10
+ import { r as scanQueuesSync, s as scanJobsSync, t as analyzeApplicationSync } from "./application-analysis-DKy02UY0.mjs";
11
+ import { a as validateSsrEntries, n as SSR_SERVER_ENTRY_RELATIVE_PATHS, o as validateSsrEntry, t as SSR_CLIENT_ENTRY_RELATIVE_PATHS } from "./entry-CMAkqBmU.mjs";
12
+ import { r as hasEmailHandlers } from "./email-d_Aux1vM.mjs";
13
+ import { i as resolveLayoutIds } from "./scan-C3uDzz6J.mjs";
14
+ import { a as hasSandboxSqliteMigration, c as resolveSandboxConfig, i as assertSandboxNamespaceConfig, l as sandboxContainerConfig, n as DEFAULT_SANDBOX_CLASS_NAME, r as SANDBOX_MIGRATION_TAG } from "./config-B_ggY-IO.mjs";
15
+ import { G as hasRevalidate, R as CLOUDFLARE_PRESERVE_BINDINGS_BUILD_ENV, U as collectPages, W as collectRevalidate, _t as hasApiRoutes, et as resolveDistDir, gt as generateQueueResolverSource, it as resolveWorkerDirName, o as prerenderPages, s as prerenderPagesNode, u as SEND_EMAIL_BINDING, vt as hasDocumentRoutes, yt as resolveAssetRoutingPolicy, z as selectNativeVersionMetadataBinding } from "./deploy-Bqe1PYBw.mjs";
16
16
  import { i as voidWarn, n as voidError, r as voidLog, t as setShowTimestamp } from "./log-vqY3pqwI.mjs";
17
- import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-2rKda9Y6.mjs";
18
- import { n as readDeploymentPlatform, r as readProjectConfig } from "./project-DQstYTxO.mjs";
19
- import { a as restoreCloudflarePreviewPointer, r as materializeCloudflareConfig, s as stashCloudflareDeployPointer } from "./cloudflare-config-Bktvwtpf.mjs";
20
- import { D as syncWranglerDurableObjects, E as syncWranglerBindings, O as syncWranglerWebSockets, T as resolveLiveBindingConfig, _ as readExistingLiveBindingConfig, a as LIVE_MIGRATION_TAG, b as readWranglerCompat, d as ensureWranglerCompatibilityDate, f as ensureWranglerNodejsAlsFlag, g as normalizeWranglerCompatibilityFlags, h as nextDurableObjectMigrationTag, m as hasDurableObjectSqliteMigration, p as findBindingNameOwner, u as ensureWranglerCompat, w as readWranglerWebSocketTombstones } from "./wrangler-DhUerFkW.mjs";
21
- import { n as writeLocalD1PersistState, t as resolveLocalD1Target } from "./local-d1-fGxYrVD6.mjs";
17
+ import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-DlfR78Nu.mjs";
18
+ import { n as readDeploymentPlatform, r as readProjectConfig } from "./project-BAm2aB6Q.mjs";
19
+ import { a as restoreCloudflarePreviewPointer, r as materializeCloudflareConfig, s as stashCloudflareDeployPointer } from "./cloudflare-config-BmhBv3HV.mjs";
20
+ import { D as syncWranglerBindings, E as resolveLiveBindingConfig, O as syncWranglerDurableObjects, T as readWranglerWebSocketTombstones, a as LIVE_MIGRATION_TAG, d as ensureWranglerCompatibilityDate, f as ensureWranglerNodejsAlsFlag, g as normalizeWranglerCompatibilityFlags, h as nextDurableObjectMigrationTag, k as syncWranglerWebSockets, m as hasDurableObjectSqliteMigration, p as findBindingNameOwner, u as ensureWranglerCompat, v as readExistingLiveBindingConfig, x as readWranglerCompat } from "./wrangler-D9KTuqnv.mjs";
21
+ import { n as writeLocalD1PersistState, t as resolveLocalD1Target } from "./local-d1-ga5O_idV.mjs";
22
22
  import { n as scopedAccessCredentials } from "./cf-access-TWNzXltp.mjs";
23
- import { r as ensurePlatformProxyAccess } from "./cf-access-YTlqvAsk.mjs";
24
- import { a as getApiUrl, c as getToken, o as getProxyUrl } from "./auth-D52yG7hl.mjs";
25
- import { n as parseDotenvFile } from "./dotenv-VQxupEUv.mjs";
26
- import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-B8GeM0RM.mjs";
27
- import { i as generateDbDts, n as generateQueueTypes, r as DRIZZLE_OPERATORS, t as generateRouteTypes } from "./route-types-Clm4WD0B.mjs";
28
- import { t as discoverSchema } from "./discover-BcPPiF1o.mjs";
29
- import { _ as parseCapturedMessage, a as DEV_INBOX_ENV_VAR, c as DEV_INBOX_TOKEN_ENV_VAR, f as clearDevBuffer, g as listDevMessages, l as DEV_INBOX_TOKEN_HEADER, m as findDevMessage, n as DEV_INBOX_ACK_VALUE, o as DEV_INBOX_MAX_CAPTURE_BYTES, s as DEV_INBOX_PREFIX, t as DEV_INBOX_ACK_HEADER, u as DEV_INBOX_TOKEN_QUERY, v as pushToDevBuffer, y as registerEmailSink } from "./dev-inbox-D64ZKUod.mjs";
30
- import { t as MagicString } from "./dist-AoCzRTJE.mjs";
31
- import { n as findEnvFileForPaths, r as generateEnvTypes, t as findEnvFile } from "./env-types-D5kds_OZ.mjs";
32
- import { y as loadUserEnvFile } from "./env-validation-vBSQ8gw5.mjs";
33
- import { a as loadLocalEnv, o as resolveRemoteMode, r as findLegacyEnvFiles, s as stripInternalEnvKeys, t as CLIENT_ENV_PREFIX$2 } from "./env-_v349JBd.mjs";
34
- import { n as INTERNAL_BINDING_PREFIXES, t as INTERNAL_BINDING_EXACT_NAMES } from "./env-raw-Cx8ElDdj.mjs";
35
- import { n as completeApplicationAnalysis } from "./application-routing-C9khgLiS.mjs";
36
- import { n as generateVoidAuthDrizzleSchema, t as generateVoidAuthDatabaseModule } from "./drizzle-Beb2Am5S.mjs";
37
- import { n as defer } from "./defer-YsYDUoii.mjs";
38
- import { a as defineMiddleware, c as defineScheduled, i as defineHead, n as basicAuth, o as defineQueue, r as defineHandler, s as defineRender, t as VoidAssetRewriteError } from "./handler-DghKr6dU.mjs";
39
- import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync, writeSync } from "node:fs";
23
+ import { r as ensurePlatformProxyAccess } from "./cf-access-BcIBSKqG.mjs";
24
+ import { a as getApiUrl, c as getToken, o as getProxyUrl } from "./auth-Dn2vw-SG.mjs";
25
+ import { d as printSuccess } from "./output-C50ag2r3.mjs";
26
+ import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-CVJhNMxs.mjs";
27
+ import { i as generateDbDts, n as generateQueueTypes, r as DRIZZLE_OPERATORS, t as generateRouteTypes } from "./route-types-CbtsIeFn.mjs";
28
+ import { t as discoverSchema } from "./discover-CkCYv9T_.mjs";
29
+ import { _ as listDevMessages, a as DEV_INBOX_ENV_VAR, b as registerEmailSink, c as DEV_INBOX_TOKEN_ENV_VAR, h as findDevMessage, l as DEV_INBOX_TOKEN_HEADER, n as DEV_INBOX_ACK_VALUE, o as DEV_INBOX_MAX_CAPTURE_BYTES, p as clearDevBuffer, s as DEV_INBOX_PREFIX, t as DEV_INBOX_ACK_HEADER, u as DEV_INBOX_TOKEN_QUERY, v as parseCapturedMessage, y as pushToDevBuffer } from "./dev-inbox-CK-UuPob.mjs";
30
+ import { t as MagicString } from "./dist-Cegxq8gs.mjs";
31
+ import { n as scanWranglerDotenvKeys } from "./dotenv-BfU6eqFt.mjs";
32
+ import { a as isClientEnvKey, c as resolveRemoteMode, i as findReservedEnvKeys, n as LOCAL_ENV_FILE, o as loadLocalEnv, r as findLegacyEnvFiles, s as reservedEnvKeysMessage } from "./env-BxocywBB.mjs";
33
+ import { n as findEnvFileForPaths, r as generateEnvTypes, t as findEnvFile } from "./env-types-CwXgeMgL.mjs";
34
+ import { y as loadUserEnvFile } from "./env-validation-Dxdx_5wo.mjs";
35
+ import { n as INTERNAL_BINDING_PREFIXES, t as INTERNAL_BINDING_EXACT_NAMES } from "./env-raw-jZpP5n47.mjs";
36
+ import { n as completeApplicationAnalysis } from "./application-routing-DQyUh_5S.mjs";
37
+ import { r as defer } from "./validation-error-Dg9Recn1.mjs";
38
+ import { i as safeJsonStringify } from "./protocol-CfaUwNs8.mjs";
39
+ import { n as generateVoidAuthDrizzleSchema, t as generateVoidAuthDatabaseModule } from "./drizzle-CF84FOVM.mjs";
40
+ import { a as defineMiddleware, c as defineScheduled, i as defineHead, n as basicAuth, o as defineQueue, r as defineHandler, s as defineRender, t as VoidAssetRewriteError } from "./handler-BHAn2fZJ.mjs";
41
+ import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync, writeSync } from "node:fs";
40
42
  import { cloudflare } from "@cloudflare/vite-plugin";
41
43
  import { fileURLToPath, pathToFileURL } from "node:url";
42
- import { normalizePath, parseSync } from "vite";
44
+ import { loadEnv, normalizePath, parseSync, resolveEnvPrefix } from "vite";
43
45
  import { appendSqliteClassMigration } from "@void/deploy-core";
44
- import { timingSafeEqual } from "node:crypto";
46
+ import { randomBytes, timingSafeEqual } from "node:crypto";
45
47
  import { homedir } from "node:os";
46
- import { isDeepStrictEqual } from "node:util";
47
- import { init, parse } from "es-module-lexer";
48
+ import { isDeepStrictEqual, parseEnv } from "node:util";
48
49
  import { compilePattern } from "@void/edge";
49
50
  //#region src/pages/dev-css-assets.ts
50
51
  var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
@@ -75,7 +76,7 @@ function normalizeDevCssUrl(id, root) {
75
76
  const { pathname, suffix } = splitQueryHash(normalized);
76
77
  const normalizedRoot = normalizePath(root);
77
78
  if (pathname === normalizedRoot || pathname.startsWith(normalizedRoot + "/")) return "/" + normalizePath(pathname.slice(normalizedRoot.length + 1)) + suffix;
78
- if (pathname.startsWith("/@fs/") || pathname.startsWith("/@vite/") || pathname === "/@vite/client" || pathname.startsWith("/@react-refresh") || pathname.startsWith("/node_modules/.vite/")) return normalized;
79
+ if (pathname.startsWith("/@fs/") || pathname.startsWith("/@vite/") || pathname.startsWith("/@react-refresh") || pathname.startsWith("/node_modules/.vite/")) return normalized;
79
80
  if (pathname.startsWith("/")) {
80
81
  const rootFirstSegment = normalizedRoot.split("/").filter(Boolean)[0];
81
82
  if (rootFirstSegment && !pathname.startsWith(`/${rootFirstSegment}/`)) return normalized;
@@ -86,9 +87,6 @@ function normalizeDevCssUrl(id, root) {
86
87
  function normalizeRequestUrl(url) {
87
88
  return normalizePath(url).replace(/^\/@id\/__x00__/, "\0");
88
89
  }
89
- function toTransformRequestUrl(url) {
90
- return normalizeRequestUrl(url);
91
- }
92
90
  function isCssRequest(url) {
93
91
  return !CSS_BYPASS_RE.test(url) && (CSS_REQUEST_RE.test(url) || STYLE_QUERY_RE.test(url));
94
92
  }
@@ -194,7 +192,7 @@ var DevCssProvider = class {
194
192
  const addByUrl = async (rawUrl) => {
195
193
  const url = normalizeDevCssUrl(rawUrl, this.root);
196
194
  if (isOptimizedDepUrl(url)) return;
197
- const requestUrl = toTransformRequestUrl(url);
195
+ const requestUrl = normalizeRequestUrl(url);
198
196
  let result;
199
197
  try {
200
198
  result = await environment.transformRequest(requestUrl);
@@ -243,16 +241,9 @@ var DevCssProvider = class {
243
241
  });
244
242
  continue;
245
243
  }
246
- const inlineUrl = appendInlineQuery(url);
247
- let result;
248
- try {
249
- result = await environment.transformRequest(inlineUrl);
250
- } catch (error) {
251
- this.server.config.logger.warnOnce(`[void] Skipping dev CSS asset for ${url}: ${error instanceof Error ? error.message : String(error)}`);
252
- continue;
253
- }
254
- if (!result) continue;
255
244
  try {
245
+ const result = await environment.transformRequest(appendInlineQuery(url));
246
+ if (!result) continue;
256
247
  assets.push({
257
248
  url,
258
249
  id: cssViteDevIds.get(url) ?? url,
@@ -396,7 +387,7 @@ export async function getVoidPagesDevCss(request, env, payload) {
396
387
  res.end("Method Not Allowed");
397
388
  return;
398
389
  }
399
- if (req.headers["X-Void-Dev-Asset-Request".toLowerCase()] !== "pages-css") {
390
+ if (req.headers[DEV_CSS_ASSET_GUARD_HEADER.toLowerCase()] !== DEV_CSS_ASSET_GUARD_VALUE) {
400
391
  res.statusCode = 403;
401
392
  res.end("Forbidden");
402
393
  return;
@@ -428,17 +419,16 @@ export async function getVoidPagesDevCss(request, env, payload) {
428
419
  async transform(code, id) {
429
420
  if (this.environment?.name !== "client") return;
430
421
  if (id.includes("/node_modules/.vite/")) return;
431
- await init;
432
- let imports;
422
+ let parsed;
433
423
  try {
434
- [imports] = parse(code);
424
+ parsed = parseSync(id, code, { lang: "js" });
425
+ if (parsed.errors.length > 0) return;
435
426
  } catch {
436
427
  return;
437
428
  }
438
429
  const edges = [];
439
- for (const item of imports) {
440
- if (item.type !== "static" && item.type !== "reexport-star" || !item.specifier) continue;
441
- const resolved = await this.resolve(item.specifier, id, { skipSelf: true });
430
+ for (const { source } of staticModuleReferences(parsed, code)) {
431
+ const resolved = await this.resolve(source, id, { skipSelf: true });
442
432
  if (resolved?.id) edges.push(normalizeDevCssUrl(resolved.id, root));
443
433
  }
444
434
  edgeStore.set(id, edges);
@@ -501,10 +491,10 @@ function readVoidDevCredentials(root) {
501
491
  * block the server. On completion, writes project.json and restarts the server.
502
492
  */
503
493
  async function runAiSetupPrompt(root, token, apiUrl, server) {
504
- const { select, text, isCancel, log } = await import("./prompts-ZAuJlyCj.mjs");
505
- const { PlatformClient } = await import("./client-BxVixJBI.mjs");
506
- const { attachSelectedPlatform, writeProjectConfig } = await import("./project-BiaPQ7ur.mjs");
507
- const { inferDefaultSlug } = await import("./resolve-project-DtbidV4y.mjs");
494
+ const { select, text, isCancel, log } = await import("./dist-Bx1z6Eva.mjs");
495
+ const { PlatformClient } = await import("./client-tso2OfcG.mjs");
496
+ const { attachSelectedPlatform, writeProjectConfig } = await import("./project-DedqCp59.mjs");
497
+ const { inferDefaultSlug } = await import("./resolve-project-BIeYs9Cw.mjs");
508
498
  const client = new PlatformClient(token, apiUrl);
509
499
  let projects;
510
500
  try {
@@ -531,7 +521,7 @@ async function runAiSetupPrompt(root, token, apiUrl, server) {
531
521
  let slug;
532
522
  if (result === "__create__") {
533
523
  const { validateProjectSlug } = await import("./project-slug-DofjTFd-.mjs");
534
- const { createProjectWithSlugRetry } = await import("./create-project-Dbb4uiN-.mjs");
524
+ const { createProjectWithSlugRetry } = await import("./create-project-qFDC45zg.mjs");
535
525
  let created;
536
526
  try {
537
527
  created = await createProjectWithSlugRetry(client, inferDefaultSlug(root), async (defaultSlug) => {
@@ -560,8 +550,8 @@ async function runAiSetupPrompt(root, token, apiUrl, server) {
560
550
  projectId,
561
551
  slug
562
552
  }, client.apiUrl));
563
- log.success(`Linked to ${import_picocolors.default.blue(slug)}`);
564
553
  await server.restart();
554
+ printSuccess(`Linked to ${slug}.`);
565
555
  }
566
556
  //#endregion
567
557
  //#region src/plugin-binding-runtime.ts
@@ -691,6 +681,25 @@ function createClientStubsPlugin() {
691
681
  }
692
682
  //#endregion
693
683
  //#region src/plugin-dev-config.ts
684
+ /** Run `fn` shortly after Vite prints "ready" so dev messages stay visible. */
685
+ function afterServerReady(server, fn) {
686
+ if (server.httpServer) server.httpServer.once("listening", () => setTimeout(fn, 100));
687
+ else fn();
688
+ }
689
+ /**
690
+ * Best-effort detection of `vite build` (and `vite preview`) so we can skip
691
+ * dev-only side effects — writing the dev-trigger token and the dispatch
692
+ * file — when no dev server will run. Vite's CLI passes the subcommand as
693
+ * `process.argv[2]`; programmatic API users (and `vite` with no subcommand)
694
+ * default to `dev`, which we detect as the absence of a build-shaped first
695
+ * positional. This is intentionally permissive: when in doubt, we behave as
696
+ * if we're in dev so first-request correctness is preserved.
697
+ */
698
+ function isViteBuildOrPreview() {
699
+ const subcommand = process.argv[2];
700
+ return subcommand === "build" || subcommand === "preview";
701
+ }
702
+ const DEV_TRIGGER_TOKEN_FILE_MODE = 384;
694
703
  /**
695
704
  * Persist the dev-trigger token at `<root>/.void/dev-trigger-token` so
696
705
  * every voidPlugin() invocation against the same project reads the same
@@ -701,12 +710,8 @@ function createClientStubsPlugin() {
701
710
  * hint plugin would print the other, and curls would land on
702
711
  * `unauthorized`.
703
712
  *
704
- * The file persists across dev sessions by design — it lives under `.void/`
705
- * (gitignored), and `cleanupWrapper` in `void deploy` explicitly preserves
706
- * it across its own wipe so saved curl scripts keep working between deploys.
707
- * The token is local to the project and only honored by a worker bound to
708
- * localhost; treat it like the rest of `.void/` — disposable scratch state,
709
- * safe to remove manually if desired.
713
+ * The file persists across dev sessions and deploys so saved curl scripts
714
+ * keep working. It lives under `.void/` (gitignored) and is safe to remove.
710
715
  *
711
716
  * The file is owner-only (0600). It is the one credential the dev-inbox
712
717
  * routes accept, and loopback is reachable by every account on the host,
@@ -715,20 +720,6 @@ function createClientStubsPlugin() {
715
720
  * release is tightened in place, never rotated. Windows has no group/other
716
721
  * bits — `mode` and `chmod` only toggle write there — so it is left alone.
717
722
  */
718
- /**
719
- * Best-effort detection of `vite build` (and `vite preview`) so we can skip
720
- * dev-only side effects — writing the dev-trigger token and the dispatch
721
- * file — when no dev server will run. Vite's CLI passes the subcommand as
722
- * `process.argv[2]`; programmatic API users (and `vite` with no subcommand)
723
- * default to `dev`, which we detect as the absence of a build-shaped first
724
- * positional. This is intentionally permissive: when in doubt, we behave as
725
- * if we're in dev so first-request correctness is preserved.
726
- */
727
- function isViteBuildOrPreview() {
728
- const subcommand = process.argv[2];
729
- return subcommand === "build" || subcommand === "preview";
730
- }
731
- const DEV_TRIGGER_TOKEN_FILE_MODE = 384;
732
723
  function getOrCreateDevTriggerToken(root) {
733
724
  const tokenPath = join(root, ".void", "dev-trigger-token");
734
725
  const readExisting = () => {
@@ -767,6 +758,48 @@ function getOrCreateDevTriggerToken(root) {
767
758
  return fresh;
768
759
  }
769
760
  }
761
+ const DEV_TRIGGER_SECRET_PATH_RE = /(?:^|\/)\.void[. ]*\/(?:.*\/)?(?:dev-trigger|drizzle\.config\.json)|(?:^|\/)\.(?:nuxt|nitro)[. ]*\/(?:.*\/)?dev\/|(?:^|\/)\.astro[. ]*\/(?:.*\/)?dev\.log/;
762
+ function isDevTriggerSecretRequest(url) {
763
+ if (!url) return false;
764
+ let path = url.split(/[?#]/, 1)[0] ?? "";
765
+ for (let i = 0; i < 3; i++) {
766
+ let decoded;
767
+ try {
768
+ decoded = decodeURIComponent(path);
769
+ } catch {
770
+ break;
771
+ }
772
+ if (decoded === path) break;
773
+ path = decoded;
774
+ }
775
+ return DEV_TRIGGER_SECRET_PATH_RE.test(path.replaceAll("\\", "/").toLowerCase());
776
+ }
777
+ /**
778
+ * Vite serves every root file that `server.fs.deny` does not list, and its default
779
+ * CORS lets any localhost origin read the reply, so the files above would hand their
780
+ * secrets to `--host` peers, other local accounts and other local dev servers.
781
+ * `server.fs.deny` is not used: a plugin's list replaces Vite's defaults, and the
782
+ * Cloudflare worker environment applies the same check when it imports
783
+ * `.void/dev-triggers.ts`. The middleware is added in configureServer, so it runs
784
+ * before Vite's transform, /@fs/ and static middlewares.
785
+ */
786
+ function devTriggerSecretGuardPlugin() {
787
+ return {
788
+ name: "void:dev-trigger-secret-guard",
789
+ apply: "serve",
790
+ enforce: "pre",
791
+ configureServer(server) {
792
+ server.middlewares.use((req, res, next) => {
793
+ if (!isDevTriggerSecretRequest(req.url)) {
794
+ next();
795
+ return;
796
+ }
797
+ res.statusCode = 404;
798
+ res.end("Not Found");
799
+ });
800
+ }
801
+ };
802
+ }
770
803
  /**
771
804
  * Warn when the project's `package.json` does not declare `"type": "module"`.
772
805
  *
@@ -809,11 +842,7 @@ function devTriggersHintPlugin(root, options) {
809
842
  const jobs = scanJobsSync(root, paths.cronsDir);
810
843
  const queues = scanQueuesSync(root, paths.queuesDir);
811
844
  if (jobs.length === 0 && queues.length === 0) return;
812
- const afterReady = (fn) => {
813
- if (server.httpServer) server.httpServer.once("listening", () => setTimeout(fn, 100));
814
- else fn();
815
- };
816
- afterReady(() => {
845
+ afterServerReady(server, () => {
817
846
  const address = server.httpServer?.address();
818
847
  const port = typeof address === "object" && address ? address.port : null;
819
848
  if (port == null) return;
@@ -823,8 +852,7 @@ function devTriggersHintPlugin(root, options) {
823
852
  if (queues.length > 0) summary.push(`${queues.length} queue${queues.length === 1 ? "" : "s"}`);
824
853
  console.log();
825
854
  voidLog(`Detected ${summary.join(" and ")}. Trigger locally:`);
826
- const configuredProxyToken = parseDotenvFile(join(root, ".env"), { expand: false }).__VOID_PROXY_TOKEN;
827
- const authHeader = configuredProxyToken ? `x-void-internal: ${configuredProxyToken}` : `x-void-dev-trigger: ${devTriggerToken}`;
855
+ const authHeader = `x-void-dev-trigger: ${devTriggerToken}`;
828
856
  if (jobs.length > 0) {
829
857
  const sample = jobs[0].crons[0];
830
858
  console.log(` \x1b[2mcurl -X POST ${baseUrl}/__void/scheduled \\\n -H "${authHeader}" \\\n -d '{"cron":"${sample}","scheduledTime":'"$(date +%s000)"'}'\x1b[22m`);
@@ -1021,7 +1049,7 @@ function createPrimaryDb(d1) {
1021
1049
  const migrationsDir = paths.migrationsDir;
1022
1050
  if (!(existsSync(migrationsDir) && readdirSync(migrationsDir).some((f) => f.endsWith(".sql")))) {
1023
1051
  if (dialect === "sqlite") {
1024
- const { getApplicationTables, getMiniflareDatabasePath } = await import("./runner-voIltZrY.mjs");
1052
+ const { getApplicationTables, getMiniflareDatabasePath } = await import("./runner-9wjsSZx-.mjs");
1025
1053
  if ((await getApplicationTables(getMiniflareDatabasePath(options.localD1PersistRoot ?? join(root, ".void"), options.localD1DatabaseId ?? "local")).catch(() => [])).length > 0) return;
1026
1054
  }
1027
1055
  voidWarn(`Schema found but no migrations in ${paths.sourceRelative("db/migrations")}/.\n \`void db push\` syncs the schema to your local DB without creating migration files.
@@ -1039,7 +1067,6 @@ function createPrimaryDb(d1) {
1039
1067
  }
1040
1068
  //#endregion
1041
1069
  //#region src/plugin-email-dev-inbox.ts
1042
- const INBOX_PREFIX = DEV_INBOX_PREFIX;
1043
1070
  const SECURITY_HEADERS = {
1044
1071
  "Cache-Control": "no-store",
1045
1072
  "X-Content-Type-Options": "nosniff",
@@ -1049,18 +1076,14 @@ const SECURITY_HEADERS = {
1049
1076
  };
1050
1077
  function emailDevInboxPlugin(options) {
1051
1078
  const { devTriggerToken } = options;
1052
- let devMode = false;
1053
1079
  let unregister;
1054
1080
  /** `href` for an inbox path that keeps the browser session authorized. */
1055
- const inboxHref = (path) => `${INBOX_PREFIX}${path}?${DEV_INBOX_TOKEN_QUERY}=${encodeURIComponent(devTriggerToken)}`;
1081
+ const inboxHref = (path) => `${DEV_INBOX_PREFIX}${path}?${DEV_INBOX_TOKEN_QUERY}=${encodeURIComponent(devTriggerToken)}`;
1056
1082
  return {
1057
1083
  name: "void:email-dev-inbox",
1058
1084
  apply: "serve",
1059
- config() {
1060
- devMode = true;
1061
- },
1062
1085
  buildStart() {
1063
- if (!devMode || unregister) return;
1086
+ if (unregister) return;
1064
1087
  unregister = registerEmailSink((m) => pushToDevBuffer(m));
1065
1088
  },
1066
1089
  buildEnd() {
@@ -1070,7 +1093,6 @@ function emailDevInboxPlugin(options) {
1070
1093
  }
1071
1094
  },
1072
1095
  configureServer(server) {
1073
- if (server.config.command !== "serve") return;
1074
1096
  const printInboxUrl = () => {
1075
1097
  const address = server.httpServer?.address();
1076
1098
  const port = typeof address === "object" && address ? address.port : null;
@@ -1078,12 +1100,7 @@ function emailDevInboxPlugin(options) {
1078
1100
  voidLog(`Email inbox: http://localhost:${port}${inboxHref("")}`);
1079
1101
  };
1080
1102
  if (server.httpServer) server.httpServer.once("listening", () => setTimeout(printInboxUrl, 100));
1081
- server.middlewares.use(INBOX_PREFIX, (req, res) => {
1082
- if (server.config.command !== "serve") {
1083
- res.writeHead(404, SECURITY_HEADERS);
1084
- res.end();
1085
- return;
1086
- }
1103
+ server.middlewares.use(DEV_INBOX_PREFIX, (req, res) => {
1087
1104
  const url = new URL(req.url ?? "/", "http://localhost");
1088
1105
  const path = url.pathname;
1089
1106
  if (!isAuthorized(req.headers["x-void-dev-trigger"], url, devTriggerToken)) {
@@ -1296,168 +1313,6 @@ function renderDetailHtml(m, href) {
1296
1313
  </body></html>`;
1297
1314
  }
1298
1315
  //#endregion
1299
- //#region src/plugin-env-client-fold.ts
1300
- let cachedModule$1 = null;
1301
- async function loadEnvPublic$1() {
1302
- if (cachedModule$1) return cachedModule$1;
1303
- const mod = await import("./runtime/env-public.mjs");
1304
- cachedModule$1 = mod;
1305
- return mod;
1306
- }
1307
- const SKIP_EXT$1 = /\.(?:css|s[ac]ss|less|styl|json|svg|png|jpe?g|gif|webp|avif|ico|woff2?|ttf|otf|eot|wasm|mp[34]|webm|md|mdx)(?:\?|$)/;
1308
- const SCRIPT_LANG_RE$1 = /[?&]lang\.([a-z0-9]+)/i;
1309
- function mentionsVoidEnv$1(code) {
1310
- return code.includes("void/env");
1311
- }
1312
- function extractEnvBindings$1(program) {
1313
- const direct = /* @__PURE__ */ new Set();
1314
- const namespace = /* @__PURE__ */ new Set();
1315
- let found = false;
1316
- for (const node of program.body ?? []) {
1317
- if (node.type !== "ImportDeclaration") continue;
1318
- if (node.source.value !== "void/env") continue;
1319
- found = true;
1320
- for (const spec of node.specifiers ?? []) if (spec.type === "ImportSpecifier") {
1321
- const importedName = spec.imported;
1322
- if (!isIdentifier(importedName)) continue;
1323
- if (importedName.name === "env") direct.add(spec.local.name);
1324
- } else if (spec.type === "ImportNamespaceSpecifier") namespace.add(spec.local.name);
1325
- }
1326
- if (!found || direct.size === 0 && namespace.size === 0) return null;
1327
- return {
1328
- directNames: direct,
1329
- namespaceNames: namespace
1330
- };
1331
- }
1332
- /**
1333
- * Walk the AST and collect every static `env.FOO` / `env["FOO"]` (or
1334
- * `<ns>.env.FOO`) read. Dynamic keys (`env[variable]`) are intentionally
1335
- * ignored — the Proxy still serves them at runtime, and we can't prove
1336
- * they'd be folded the same way Vite folds `import.meta.env.FOO`.
1337
- */
1338
- function findFoldHits(program, bindings) {
1339
- const hits = [];
1340
- walk(program, { enter(node) {
1341
- const current = node;
1342
- if (!isMemberExpression(current)) return;
1343
- if (isIdentifier(current.object) && bindings.directNames.has(current.object.name)) {
1344
- pushHit(current, hits);
1345
- return;
1346
- }
1347
- const obj = current.object;
1348
- if (isMemberExpression(obj) && !obj.computed && isIdentifier(obj.object) && bindings.namespaceNames.has(obj.object.name) && isIdentifier(obj.property) && obj.property.name === "env") pushHit(current, hits);
1349
- } });
1350
- return hits;
1351
- }
1352
- function pushHit(node, hits) {
1353
- if (!node.computed && isIdentifier(node.property)) {
1354
- hits.push({
1355
- start: node.start,
1356
- end: node.end,
1357
- property: node.property.name
1358
- });
1359
- return;
1360
- }
1361
- if (node.computed && isLiteral(node.property) && typeof node.property.value === "string") {
1362
- hits.push({
1363
- start: node.start,
1364
- end: node.end,
1365
- property: node.property.value
1366
- });
1367
- return;
1368
- }
1369
- }
1370
- const CLIENT_ENV_PREFIX$1 = "VITE_";
1371
- /**
1372
- * Build-time constant folding for client-only `env.FOO` reads.
1373
- *
1374
- * Purpose: match what Vite already does for `import.meta.env.FOO` so that
1375
- * branches like `if (env.MODE === "production") { ... }` get dead-code
1376
- * eliminated in production client bundles. The runtime `env` Proxy on the
1377
- * client already resolves keys via `import.meta.env`, so *semantics* don't
1378
- * change — we just move the lookup from runtime to build time, which
1379
- * unlocks DCE and bundler minification of guarded blocks.
1380
- *
1381
- * Safety properties:
1382
- * • Runs only when `this.environment.name === 'client'`. Server / SSR /
1383
- * worker builds keep the full runtime Proxy (secrets, runtime-injected
1384
- * vars, HMR remain live).
1385
- * • Only folds keys that match Void's fixed `VITE_` client prefix. A
1386
- * server-only key is never folded (and the env-client-guard plugin
1387
- * would have errored out earlier anyway).
1388
- * • Only folds static member access: `env.FOO` and `env["FOO"]`.
1389
- * Dynamic access, destructuring, and reassignment fall through to the
1390
- * runtime Proxy.
1391
- * • Only folds keys with a known build-time value (build shell or schema
1392
- * `.default(...)`). Unknown keys are left as-is
1393
- * and the Proxy handles them at runtime — no "undefined" shoved in.
1394
- * • Build-only (`apply: 'build'`). Dev runs through the Proxy so local
1395
- * `.env` edits take effect without a full rebuild.
1396
- */
1397
- function envClientFoldPlugin() {
1398
- let loadedEnv = {};
1399
- const schemaDefaults = {};
1400
- /** The merged view the transform consults for each key. */
1401
- function resolveValue(key) {
1402
- if (key in loadedEnv) return loadedEnv[key];
1403
- if (key in schemaDefaults) return schemaDefaults[key];
1404
- }
1405
- function isExposedToClient(key) {
1406
- return key.startsWith(CLIENT_ENV_PREFIX$1);
1407
- }
1408
- return {
1409
- name: "void:env-client-fold",
1410
- apply: "build",
1411
- enforce: "post",
1412
- async configResolved(config) {
1413
- loadedEnv = config.env ?? {};
1414
- try {
1415
- const mod = await loadEnvPublic$1();
1416
- if (mod._hasRegisteredSchema()) {
1417
- const defaults = mod._getSchemaDefaults();
1418
- for (const [key, value] of Object.entries(defaults)) if (isExposedToClient(key) && !(key in loadedEnv)) schemaDefaults[key] = value;
1419
- }
1420
- } catch {}
1421
- },
1422
- async transform(code, id) {
1423
- if (this.environment?.name !== "client") return;
1424
- if (id.includes("/node_modules/")) return;
1425
- if (SKIP_EXT$1.test(id)) return;
1426
- const langMatch = SCRIPT_LANG_RE$1.exec(id);
1427
- const isScriptQuery = langMatch !== null && /^(?:m?[jt]sx?|cjs)$/i.test(langMatch[1]);
1428
- const isPlainScript = /\.(?:[mc]?[jt]sx?)(?:\?|$)/.test(id);
1429
- const isSfc = /\.(?:svelte|vue|astro)(?:\?|$)/.test(id);
1430
- if (!isScriptQuery && !isPlainScript && !isSfc) return;
1431
- if (!mentionsVoidEnv$1(code)) return;
1432
- if (Object.keys(loadedEnv).length === 0 && Object.keys(schemaDefaults).length === 0) return;
1433
- let parsed;
1434
- try {
1435
- parsed = parseSync(id, code);
1436
- } catch {
1437
- return;
1438
- }
1439
- const program = parsed.program;
1440
- const bindings = extractEnvBindings$1(program);
1441
- if (!bindings) return;
1442
- const hits = findFoldHits(program, bindings);
1443
- if (hits.length === 0) return;
1444
- let ms = null;
1445
- for (const hit of hits) {
1446
- if (!isExposedToClient(hit.property)) continue;
1447
- const value = resolveValue(hit.property);
1448
- if (value === void 0) continue;
1449
- if (!ms) ms = new MagicString(code);
1450
- ms.overwrite(hit.start, hit.end, JSON.stringify(value));
1451
- }
1452
- if (!ms) return;
1453
- return {
1454
- code: ms.toString(),
1455
- map: ms.generateMap({ hires: "boundary" })
1456
- };
1457
- }
1458
- };
1459
- }
1460
- //#endregion
1461
1316
  //#region src/plugin-env-client-guard.ts
1462
1317
  let cachedModule = null;
1463
1318
  async function loadEnvPublic() {
@@ -1469,12 +1324,22 @@ async function loadEnvPublic() {
1469
1324
  const SKIP_EXT = /\.(?:css|s[ac]ss|less|styl|json|svg|png|jpe?g|gif|webp|avif|ico|woff2?|ttf|otf|eot|wasm|mp[34]|webm|md|mdx)(?:\?|$)/;
1470
1325
  const SCRIPT_LANG_RE = /[?&]lang\.([a-z0-9]+)/i;
1471
1326
  /**
1472
- * Quick check: does the source mention `void/env` at all? If not, skip the
1473
- * full AST walk. Catches static imports, dynamic imports, and edge cases
1474
- * like comments — false positives here just mean we parse a file we didn't
1475
- * have to, never that we miss a real reference.
1327
+ * Cheap prefilter shared with the fold plugin: is this a client script
1328
+ * module that mentions `void/env`? Anything we'd want to parse is JS/TS or
1329
+ * has a script sub-resource (Vue/Svelte SFC virtual chunks include a
1330
+ * `lang.tsx` / `lang.ts` / `lang.js` query). SFC files themselves
1331
+ * (`.svelte`, `.vue`, `.astro`) are accepted because both plugins run with
1332
+ * `enforce: 'post'`, after the framework compiler has turned them into
1333
+ * plain JS. The `void/env` substring check lets most files skip the AST
1334
+ * walk; a false positive only means an unneeded parse.
1476
1335
  */
1477
- function mentionsVoidEnv(code) {
1336
+ function isVoidEnvScriptCandidate(id, code) {
1337
+ if (id.includes("/node_modules/") || SKIP_EXT.test(id)) return false;
1338
+ const langMatch = SCRIPT_LANG_RE.exec(id);
1339
+ const isScriptQuery = langMatch !== null && /^(?:m?[jt]sx?|cjs)$/i.test(langMatch[1]);
1340
+ const isPlainScript = /\.(?:[mc]?[jt]sx?)(?:\?|$)/.test(id);
1341
+ const isSfc = /\.(?:svelte|vue|astro)(?:\?|$)/.test(id);
1342
+ if (!isScriptQuery && !isPlainScript && !isSfc) return false;
1478
1343
  return code.includes("void/env");
1479
1344
  }
1480
1345
  function extractEnvBindings(program) {
@@ -1490,7 +1355,6 @@ function extractEnvBindings(program) {
1490
1355
  if (!isIdentifier(importedName)) continue;
1491
1356
  if (importedName.name === "env") direct.add(spec.local.name);
1492
1357
  } else if (spec.type === "ImportNamespaceSpecifier") namespace.add(spec.local.name);
1493
- else if (spec.type === "ImportDefaultSpecifier") {}
1494
1358
  }
1495
1359
  if (!found || direct.size === 0 && namespace.size === 0) return null;
1496
1360
  return {
@@ -1518,22 +1382,28 @@ function offsetToLineCol(index, offset) {
1518
1382
  };
1519
1383
  }
1520
1384
  /**
1521
- * Walk the AST collecting every `env.X` (or `<ns>.env.X`) read where `env`
1522
- * is bound to the `void/env` proxy. Computed access produces a warning hit
1523
- * with `property === null`; everything else carries the property name.
1385
+ * Visit every `env.X` (or `<ns>.env.X`) member expression where `env` is
1386
+ * bound to the `void/env` proxy.
1524
1387
  */
1525
- function findHits(program, bindings) {
1526
- const hits = [];
1388
+ function walkEnvReads(program, bindings, visit) {
1527
1389
  walk(program, { enter(node) {
1528
1390
  const current = node;
1529
1391
  if (!isMemberExpression(current)) return;
1530
1392
  if (isIdentifier(current.object) && bindings.directNames.has(current.object.name)) {
1531
- recordHit(current, hits);
1393
+ visit(current);
1532
1394
  return;
1533
1395
  }
1534
1396
  const obj = current.object;
1535
- if (isMemberExpression(obj) && !obj.computed && isIdentifier(obj.object) && bindings.namespaceNames.has(obj.object.name) && isIdentifier(obj.property) && obj.property.name === "env") recordHit(current, hits);
1397
+ if (isMemberExpression(obj) && !obj.computed && isIdentifier(obj.object) && bindings.namespaceNames.has(obj.object.name) && isIdentifier(obj.property) && obj.property.name === "env") visit(current);
1536
1398
  } });
1399
+ }
1400
+ /**
1401
+ * Collect every `void/env` read. Computed access produces a warning hit
1402
+ * with `property === null`; everything else carries the property name.
1403
+ */
1404
+ function findHits(program, bindings) {
1405
+ const hits = [];
1406
+ walkEnvReads(program, bindings, (node) => recordHit(node, hits));
1537
1407
  return hits;
1538
1408
  }
1539
1409
  function recordHit(node, hits) {
@@ -1564,21 +1434,13 @@ function recordHit(node, hits) {
1564
1434
  * No-op when no `env.ts` is registered. Server / SSR / worker environments
1565
1435
  * are skipped — only the `client` environment is checked.
1566
1436
  */
1567
- const CLIENT_ENV_PREFIX = "VITE_";
1568
1437
  function envClientGuardPlugin(root) {
1569
1438
  return {
1570
1439
  name: "void:env-client-guard",
1571
1440
  enforce: "post",
1572
1441
  async transform(code, id) {
1573
1442
  if (this.environment?.name !== "client") return;
1574
- if (id.includes("/node_modules/")) return;
1575
- if (SKIP_EXT.test(id)) return;
1576
- const langMatch = SCRIPT_LANG_RE.exec(id);
1577
- const isScriptQuery = langMatch !== null && /^(?:m?[jt]sx?|cjs)$/i.test(langMatch[1]);
1578
- const isPlainScript = /\.(?:[mc]?[jt]sx?)(?:\?|$)/.test(id);
1579
- const isSfc = /\.(?:svelte|vue|astro)(?:\?|$)/.test(id);
1580
- if (!isScriptQuery && !isPlainScript && !isSfc) return;
1581
- if (!mentionsVoidEnv(code)) return;
1443
+ if (!isVoidEnvScriptCandidate(id, code)) return;
1582
1444
  const { _hasRegisteredSchema, _getRegisteredSchema } = await loadEnvPublic();
1583
1445
  if (!_hasRegisteredSchema()) return;
1584
1446
  const schema = _getRegisteredSchema();
@@ -1611,7 +1473,7 @@ function envClientGuardPlugin(root) {
1611
1473
  }
1612
1474
  const key = hit.property;
1613
1475
  if (!(key in schema)) continue;
1614
- if (key.startsWith(CLIENT_ENV_PREFIX)) continue;
1476
+ if (isClientEnvKey(key)) continue;
1615
1477
  this.error({
1616
1478
  message: `Server-only env "${key}" referenced from client code at ${relPath}:${line}:${column}.\nIf this should be available in the browser, rename it with the VITE_* prefix in env.ts.\nIf it must stay server-only, remove this reference and read the value via a server route or loader.`,
1617
1479
  loc: {
@@ -1625,14 +1487,176 @@ function envClientGuardPlugin(root) {
1625
1487
  };
1626
1488
  }
1627
1489
  //#endregion
1490
+ //#region src/plugin-env-client-fold.ts
1491
+ /**
1492
+ * Walk the AST and collect every static `env.FOO` / `env["FOO"]` (or
1493
+ * `<ns>.env.FOO`) read. Dynamic keys (`env[variable]`) are intentionally
1494
+ * ignored — the Proxy still serves them at runtime, and we can't prove
1495
+ * they'd be folded the same way Vite folds `import.meta.env.FOO`.
1496
+ */
1497
+ function findFoldHits(program, bindings) {
1498
+ const hits = [];
1499
+ walkEnvReads(program, bindings, (node) => pushHit(node, hits));
1500
+ return hits;
1501
+ }
1502
+ function pushHit(node, hits) {
1503
+ if (!node.computed && isIdentifier(node.property)) {
1504
+ hits.push({
1505
+ start: node.start,
1506
+ end: node.end,
1507
+ property: node.property.name
1508
+ });
1509
+ return;
1510
+ }
1511
+ if (node.computed && isLiteral(node.property) && typeof node.property.value === "string") {
1512
+ hits.push({
1513
+ start: node.start,
1514
+ end: node.end,
1515
+ property: node.property.value
1516
+ });
1517
+ return;
1518
+ }
1519
+ }
1520
+ /**
1521
+ * Build-time constant folding for client-only `env.FOO` reads.
1522
+ *
1523
+ * Purpose: match what Vite already does for `import.meta.env.FOO` so that
1524
+ * branches like `if (env.VITE_FLAG === "on") { ... }` get dead-code
1525
+ * eliminated in production client bundles. The runtime `env` Proxy on the
1526
+ * client already resolves keys via `import.meta.env`, so *semantics* don't
1527
+ * change — we just move the lookup from runtime to build time, which
1528
+ * unlocks DCE and bundler minification of guarded blocks.
1529
+ *
1530
+ * Safety properties:
1531
+ * • Runs only when `this.environment.name === 'client'`. Server / SSR /
1532
+ * worker builds keep the full runtime Proxy (secrets, runtime-injected
1533
+ * vars, HMR remain live).
1534
+ * • Only folds keys that match Void's fixed `VITE_` client prefix. A
1535
+ * server-only key is never folded (and the env-client-guard plugin
1536
+ * would have errored out earlier anyway).
1537
+ * • Only folds static member access: `env.FOO` and `env["FOO"]`.
1538
+ * Dynamic access, destructuring, and reassignment fall through to the
1539
+ * runtime Proxy.
1540
+ * • Only folds keys with a known build-time value (build shell or schema
1541
+ * `.default(...)`). Unknown keys are left as-is
1542
+ * and the Proxy handles them at runtime — no "undefined" shoved in.
1543
+ * • Build-only (`apply: 'build'`). Dev runs through the Proxy so local
1544
+ * `.env` edits take effect without a full rebuild.
1545
+ */
1546
+ function envClientFoldPlugin() {
1547
+ let loadedEnv = {};
1548
+ const schemaDefaults = {};
1549
+ /** The merged view the transform consults for each key. */
1550
+ function resolveValue(key) {
1551
+ if (key in loadedEnv) return loadedEnv[key];
1552
+ if (key in schemaDefaults) return schemaDefaults[key];
1553
+ }
1554
+ return {
1555
+ name: "void:env-client-fold",
1556
+ apply: "build",
1557
+ enforce: "post",
1558
+ async configResolved(config) {
1559
+ loadedEnv = config.env ?? {};
1560
+ try {
1561
+ const mod = await loadEnvPublic();
1562
+ if (mod._hasRegisteredSchema()) {
1563
+ const defaults = mod._getSchemaDefaults();
1564
+ for (const [key, value] of Object.entries(defaults)) if (isClientEnvKey(key) && !(key in loadedEnv)) schemaDefaults[key] = value;
1565
+ }
1566
+ } catch {}
1567
+ },
1568
+ async transform(code, id) {
1569
+ if (this.environment?.name !== "client") return;
1570
+ if (!isVoidEnvScriptCandidate(id, code)) return;
1571
+ if (Object.keys(loadedEnv).length === 0 && Object.keys(schemaDefaults).length === 0) return;
1572
+ let parsed;
1573
+ try {
1574
+ parsed = parseSync(id, code);
1575
+ } catch {
1576
+ return;
1577
+ }
1578
+ const program = parsed.program;
1579
+ const bindings = extractEnvBindings(program);
1580
+ if (!bindings) return;
1581
+ const hits = findFoldHits(program, bindings);
1582
+ if (hits.length === 0) return;
1583
+ let ms = null;
1584
+ for (const hit of hits) {
1585
+ if (!isClientEnvKey(hit.property)) continue;
1586
+ const value = resolveValue(hit.property);
1587
+ if (value === void 0) continue;
1588
+ if (!ms) ms = new MagicString(code);
1589
+ ms.overwrite(hit.start, hit.end, JSON.stringify(value));
1590
+ }
1591
+ if (!ms) return;
1592
+ return {
1593
+ code: ms.toString(),
1594
+ map: ms.generateMap({ hires: "boundary" })
1595
+ };
1596
+ }
1597
+ };
1598
+ }
1599
+ //#endregion
1628
1600
  //#region src/plugin-env-schema.ts
1629
- let cachedSchemaModule = null;
1630
- async function loadSchemaModule() {
1631
- if (cachedSchemaModule) return cachedSchemaModule;
1632
- const mod = await import("./runtime/env-public.mjs");
1633
- cachedSchemaModule = mod;
1634
- return mod;
1601
+ /**
1602
+ * Variables Vite's `loadEnv` copies from env files into `process.env` when
1603
+ * they are unset, as `[file key, process.env key]`.
1604
+ */
1605
+ const VITE_PROCESS_ENV_COPIES = [
1606
+ ["NODE_ENV", "VITE_USER_NODE_ENV"],
1607
+ ["BROWSER", "BROWSER"],
1608
+ ["BROWSER_ARGS", "BROWSER_ARGS"]
1609
+ ];
1610
+ /** The `process.env` values Vite may overwrite, read before it loads env files. */
1611
+ function readProcessEnvCopies() {
1612
+ return Object.fromEntries(VITE_PROCESS_ENV_COPIES.map(([, target]) => [target, process.env[target]]));
1613
+ }
1614
+ /**
1615
+ * Delete the client values Vite took from `<root>/.env` during a build.
1616
+ * Keys come from Vite's own parser (`node:util` `parseEnv`), so every key
1617
+ * Vite read is covered. A key stays when the shell or the `vp run` task
1618
+ * runner supplies it: `loadEnv` with `envDir: false` returns exactly those
1619
+ * values. Nothing is stripped when Vite loads env files from another
1620
+ * `envDir` (or none), since `<root>/.env` was not the source.
1621
+ *
1622
+ * Vite also copies a few `.env` values into `process.env` (see
1623
+ * {@link VITE_PROCESS_ENV_COPIES}), where they would pass as shell values.
1624
+ * `before` holds those variables as they were before Vite loaded the file;
1625
+ * a copy Vite made is removed from `process.env` again, so this check and
1626
+ * the next config resolution in the same build (the app builder resolves
1627
+ * once per environment) see only the shell.
1628
+ */
1629
+ function stripLocalClientEnv(root, config, before) {
1630
+ if (config.envDir === false || realDir(config.envDir) !== realDir(root)) return;
1631
+ const file = join(root, LOCAL_ENV_FILE);
1632
+ if (!existsSync(file)) return;
1633
+ const parsed = parseEnv(readFileSync(file, "utf8"));
1634
+ const keys = new Set(Object.keys(parsed));
1635
+ for (const [source, target] of VITE_PROCESS_ENV_COPIES) {
1636
+ const value = parsed[source];
1637
+ if (value === void 0) continue;
1638
+ keys.add(target);
1639
+ if (before && before[target] === void 0 && process.env[target] === value) delete process.env[target];
1640
+ }
1641
+ const prefixes = resolveEnvPrefix({ envPrefix: config.envPrefix });
1642
+ const shell = loadEnv(config.mode, false, prefixes);
1643
+ for (const key of keys) {
1644
+ if (!prefixes.some((prefix) => key.startsWith(prefix))) continue;
1645
+ const value = shell[key];
1646
+ if (value === void 0) delete config.env[key];
1647
+ else config.env[key] = value;
1648
+ }
1635
1649
  }
1650
+ /** Match Vite's canonical paths, including Windows short names and filesystem casing. */
1651
+ function realDir(dir) {
1652
+ try {
1653
+ return realpathSync.native(dir);
1654
+ } catch {
1655
+ return resolve(dir);
1656
+ }
1657
+ }
1658
+ /** Patterns Vite's `define` typically uses for env-shaped constants. */
1659
+ const DEFINE_ENV_PATTERN = /^(?:globalThis\.)?(?:process\.env|import\.meta\.env)\.([A-Z_][A-Z0-9_]*)$/;
1636
1660
  /**
1637
1661
  * Vite plugin that auto-discovers `env.ts` at the project root and:
1638
1662
  * - imports it (registering the user's schema via `defineEnv`)
@@ -1641,24 +1665,22 @@ async function loadSchemaModule() {
1641
1665
  * on missing/invalid required keys
1642
1666
  * - regenerates types + re-validates when env.ts changes (HMR)
1643
1667
  *
1644
- * Hard errors are reserved for `void deploy`; dev keeps moving with warnings
1668
+ * Hard errors are reserved for builds and deploys; dev keeps moving with warnings
1645
1669
  * so unrelated work isn't blocked by a stale local value.
1646
1670
  */
1647
- /** Patterns Vite's `define` typically uses for env-shaped constants. */
1648
- const DEFINE_ENV_PATTERN = /^(?:globalThis\.)?(?:process\.env|import\.meta\.env)\.([A-Z_][A-Z0-9_]*)$/;
1649
- const CLIENT_ENV_PREFIXES = [CLIENT_ENV_PREFIX$2];
1650
1671
  function envSchemaPlugin(root, options = {}) {
1651
1672
  const outDir = join(root, ".void");
1652
1673
  let envFile = null;
1653
1674
  let command = "serve";
1675
+ let processEnvCopiesBefore;
1654
1676
  const appliedClientDefaults = /* @__PURE__ */ new Map();
1655
- async function emitSchemaFacts(prefixes) {
1677
+ async function emitSchemaFacts() {
1656
1678
  if (!envFile || !options.onSchema) return;
1657
- const { _hasRegisteredSchema, _getRegisteredSchema } = await loadSchemaModule();
1679
+ const { _hasRegisteredSchema, _getRegisteredSchema } = await loadEnvPublic();
1658
1680
  if (!_hasRegisteredSchema()) return;
1659
1681
  const schema = _getRegisteredSchema() ?? {};
1660
1682
  const keys = Object.keys(schema);
1661
- const clientKeys = keys.filter((key) => prefixes.some((prefix) => key.startsWith(prefix)));
1683
+ const clientKeys = keys.filter(isClientEnvKey);
1662
1684
  const clientKeySet = new Set(clientKeys);
1663
1685
  const serverKeys = keys.filter((key) => !clientKeySet.has(key));
1664
1686
  const requiredServerKeys = [];
@@ -1673,6 +1695,12 @@ function envSchemaPlugin(root, options = {}) {
1673
1695
  requiredServerKeys
1674
1696
  });
1675
1697
  }
1698
+ async function assertNoReservedSchemaKeys() {
1699
+ const { _hasRegisteredSchema, _getRegisteredSchema } = await loadEnvPublic();
1700
+ if (!_hasRegisteredSchema()) return;
1701
+ const reserved = findReservedEnvKeys(Object.keys(_getRegisteredSchema() ?? {}));
1702
+ if (reserved.length > 0) throw new Error(reservedEnvKeysMessage("env.ts", reserved));
1703
+ }
1676
1704
  function regenerateTypes() {
1677
1705
  if (!envFile) return;
1678
1706
  mkdirSync(outDir, { recursive: true });
@@ -1689,7 +1717,7 @@ function envSchemaPlugin(root, options = {}) {
1689
1717
  }
1690
1718
  }
1691
1719
  async function warnOnDefineOverlap(define) {
1692
- const { _hasRegisteredSchema, _getRegisteredSchema } = await loadSchemaModule();
1720
+ const { _hasRegisteredSchema, _getRegisteredSchema } = await loadEnvPublic();
1693
1721
  if (!_hasRegisteredSchema()) return;
1694
1722
  const schema = _getRegisteredSchema();
1695
1723
  if (!schema) return;
@@ -1710,7 +1738,7 @@ function envSchemaPlugin(root, options = {}) {
1710
1738
  }
1711
1739
  async function validateAndWarn() {
1712
1740
  if (!envFile) return;
1713
- const { _hasRegisteredSchema, validateAllEnv } = await loadSchemaModule();
1741
+ const { _hasRegisteredSchema, validateAllEnv } = await loadEnvPublic();
1714
1742
  if (!_hasRegisteredSchema()) return;
1715
1743
  const report = await validateAllEnv({
1716
1744
  ...loadLocalEnv(root, { onWarn: (message) => voidWarn(message) }),
@@ -1727,7 +1755,7 @@ function envSchemaPlugin(root, options = {}) {
1727
1755
  const configEnv = config.env ?? {};
1728
1756
  for (const [key, value] of appliedClientDefaults) if (configEnv[key] === value) delete configEnv[key];
1729
1757
  appliedClientDefaults.clear();
1730
- const { _hasRegisteredSchema, _getRegisteredSchema, _getSchemaDefaults } = await loadSchemaModule();
1758
+ const { _hasRegisteredSchema, _getRegisteredSchema, _getSchemaDefaults } = await loadEnvPublic();
1731
1759
  if (!_hasRegisteredSchema()) return;
1732
1760
  const schema = _getRegisteredSchema() ?? {};
1733
1761
  const readable = {
@@ -1735,13 +1763,12 @@ function envSchemaPlugin(root, options = {}) {
1735
1763
  ...process.env
1736
1764
  };
1737
1765
  for (const key of Object.keys(schema)) {
1738
- if (!key.startsWith("VITE_") || configEnv[key] !== void 0) continue;
1766
+ if (!isClientEnvKey(key) || configEnv[key] !== void 0) continue;
1739
1767
  const value = readable[key];
1740
1768
  if (value !== void 0) configEnv[key] = value;
1741
1769
  }
1742
1770
  for (const [key, value] of Object.entries(_getSchemaDefaults())) {
1743
- if (!key.startsWith("VITE_")) continue;
1744
- if (configEnv[key] !== void 0) continue;
1771
+ if (!isClientEnvKey(key) || configEnv[key] !== void 0) continue;
1745
1772
  configEnv[key] = value;
1746
1773
  appliedClientDefaults.set(key, value);
1747
1774
  }
@@ -1750,31 +1777,31 @@ function envSchemaPlugin(root, options = {}) {
1750
1777
  name: "void:env-schema",
1751
1778
  enforce: "pre",
1752
1779
  async config(_config, environment) {
1780
+ processEnvCopiesBefore = environment.command === "build" ? readProcessEnvCopies() : void 0;
1753
1781
  const legacyFiles = findLegacyEnvFiles(root);
1754
1782
  if (legacyFiles.length > 0) throw new Error(`void/env supports one local file: .env. Remove ${legacyFiles.join(", ")} and follow the env migration guide.`);
1783
+ const reservedInDotenv = findReservedEnvKeys(scanWranglerDotenvKeys(join(root, LOCAL_ENV_FILE)));
1784
+ if (reservedInDotenv.length > 0) throw new Error(reservedEnvKeysMessage(".env", reservedInDotenv));
1755
1785
  envFile = findEnvFile(root);
1756
1786
  if (!envFile) return;
1757
1787
  regenerateTypes();
1758
1788
  await importUserEnv(environment.command === "build");
1759
- await emitSchemaFacts(CLIENT_ENV_PREFIXES);
1789
+ await emitSchemaFacts();
1790
+ await assertNoReservedSchemaKeys();
1760
1791
  },
1761
1792
  async configResolved(config) {
1762
1793
  command = config.command;
1763
- await emitSchemaFacts(CLIENT_ENV_PREFIXES);
1764
- if (command === "build") {
1765
- const local = loadLocalEnv(root, { onWarn: (message) => voidWarn(message) });
1766
- for (const [key, value] of Object.entries(local)) if (process.env[key] === void 0 && config.env[key] === value) delete config.env[key];
1767
- }
1794
+ await emitSchemaFacts();
1795
+ if (command === "build") stripLocalClientEnv(root, config, processEnvCopiesBefore);
1768
1796
  await applyClientValuesToViteEnv(config);
1769
1797
  if (command === "build" && envFile) {
1770
- const { validateAllEnv } = await loadSchemaModule();
1798
+ const { validateAllEnv } = await loadEnvPublic();
1771
1799
  const report = await validateAllEnv({
1772
1800
  ...config.env,
1773
1801
  ...process.env
1774
1802
  });
1775
- const clientKey = (key) => key.startsWith(CLIENT_ENV_PREFIX$2);
1776
- const missing = report.missing.filter(clientKey);
1777
- const invalid = report.invalid.filter(({ key }) => clientKey(key));
1803
+ const missing = report.missing.filter(isClientEnvKey);
1804
+ const invalid = report.invalid.filter(({ key }) => isClientEnvKey(key));
1778
1805
  if (missing.length > 0 || invalid.length > 0) {
1779
1806
  const lines = ["Client env validation failed:"];
1780
1807
  for (const key of missing) lines.push(` • ${key} is required — set it in the build shell or add a schema default`);
@@ -1787,18 +1814,16 @@ function envSchemaPlugin(root, options = {}) {
1787
1814
  async configureServer(server) {
1788
1815
  if (!envFile) return;
1789
1816
  await importUserEnv();
1790
- const run = () => {
1817
+ afterServerReady(server, () => {
1791
1818
  validateAndWarn();
1792
- };
1793
- if (server.httpServer) server.httpServer.once("listening", () => setTimeout(run, 100));
1794
- else run();
1819
+ });
1795
1820
  server.watcher.add(envFile);
1796
1821
  server.watcher.on("change", async (file) => {
1797
1822
  if (file === envFile) {
1798
1823
  voidLog("env.ts changed — regenerating types");
1799
1824
  regenerateTypes();
1800
1825
  await importUserEnv();
1801
- await emitSchemaFacts(CLIENT_ENV_PREFIXES);
1826
+ await emitSchemaFacts();
1802
1827
  await applyClientValuesToViteEnv(server.config);
1803
1828
  await validateAndWarn();
1804
1829
  server.ws.send({ type: "full-reload" });
@@ -1899,12 +1924,9 @@ function __json(body, status = 200) {
1899
1924
  });
1900
1925
  }
1901
1926
 
1902
- function __authorize(request, env) {
1903
- const internal = request.headers.get("x-void-internal");
1904
- const dev = request.headers.get("x-void-dev-trigger");
1905
- const expected = env?.__VOID_PROXY_TOKEN;
1906
- if (expected) return internal === expected;
1907
- return dev === __DEV_TRIGGER_TOKEN;
1927
+ // Dev only: production framework Workers dispatch through cli/wrapper.ts.
1928
+ function __authorize(request) {
1929
+ return request.headers.get("x-void-dev-trigger") === __DEV_TRIGGER_TOKEN;
1908
1930
  }
1909
1931
 
1910
1932
  function __wrapMessage(m, decisions) {
@@ -1994,7 +2016,7 @@ export async function handleDevTrigger(request, env, ctx) {
1994
2016
  if (url.pathname !== "/__void/scheduled" && url.pathname !== "/__void/queue") {
1995
2017
  return null;
1996
2018
  }
1997
- if (!__authorize(request, env)) {
2019
+ if (!__authorize(request)) {
1998
2020
  return __json({ error: "unauthorized" }, 401);
1999
2021
  }
2000
2022
  if (url.pathname === "/__void/scheduled") {
@@ -2004,6 +2026,17 @@ export async function handleDevTrigger(request, env, ctx) {
2004
2026
  }
2005
2027
  `;
2006
2028
  }
2029
+ /** Append any missing `lines` to the project's `.gitignore`. */
2030
+ function ensureGitignoreLines(root, lines) {
2031
+ const gitignorePath = join(root, ".gitignore");
2032
+ let contents = "";
2033
+ if (existsSync(gitignorePath)) contents = readFileSync(gitignorePath, "utf-8");
2034
+ const existing = new Set(contents.split("\n").map((line) => line.trim()));
2035
+ const missing = lines.filter((line) => !existing.has(line));
2036
+ if (missing.length === 0) return;
2037
+ const sep = contents.length === 0 || contents.endsWith("\n") ? "" : "\n";
2038
+ writeFileSync(gitignorePath, `${contents}${sep}${missing.join("\n")}\n`);
2039
+ }
2007
2040
  const NITRO_MIDDLEWARE_FILENAME = "__void-dev-trigger.ts";
2008
2041
  function nitroMiddlewareSource(dispatchPath) {
2009
2042
  return `// @ts-nocheck — Auto-generated by Void in dev. Do NOT edit. Removed when the dev server stops.
@@ -2040,33 +2073,22 @@ export default async function voidDevTrigger(event) {
2040
2073
  }
2041
2074
  /**
2042
2075
  * Writes a tiny Nitro server middleware to the user's project so
2043
- * \`/__void/scheduled\` and \`/__void/queue\` are intercepted inside Nitro's
2044
- * request pipeline (where \`event.context.cloudflare?.env\` is populated).
2076
+ * `/__void/scheduled` and `/__void/queue` are intercepted inside Nitro's
2077
+ * request pipeline (where `event.context.cloudflare?.env` is populated).
2045
2078
  *
2046
- * Nitro discovers middleware from \`<serverDir>/middleware/*.ts\`. Nuxt scans
2047
- * \`server/\`, Analog scans \`src/server/\`, so the caller specifies which.
2048
- * To keep the generated file out of git we append a \`.gitignore\` entry,
2079
+ * Nitro discovers middleware from `<serverDir>/middleware/*.ts`. Nuxt scans
2080
+ * `server/`, Analog scans `src/server/`, so the caller specifies which.
2081
+ * To keep the generated file out of git we append a `.gitignore` entry,
2049
2082
  * and the file is removed when the dev server stops.
2050
2083
  */
2051
2084
  function nitroDevTriggerInjectPlugin(root, options) {
2052
2085
  const middlewareRelative = `${options.serverDir}/middleware/${NITRO_MIDDLEWARE_FILENAME}`;
2053
2086
  const middlewarePath = join(root, middlewareRelative);
2054
- const gitignoreLine = middlewareRelative;
2055
2087
  const dispatchPath = frameworkTriggersDispatchPath(root);
2056
2088
  const ensureFile = () => {
2057
2089
  mkdirSync(dirname(middlewarePath), { recursive: true });
2058
2090
  writeFileSync(middlewarePath, nitroMiddlewareSource(dispatchPath));
2059
2091
  };
2060
- const ensureGitignore = () => {
2061
- const gitignorePath = join(root, ".gitignore");
2062
- let contents = "";
2063
- if (existsSync(gitignorePath)) {
2064
- contents = readFileSync(gitignorePath, "utf-8");
2065
- if (contents.split("\n").some((line) => line.trim() === gitignoreLine)) return;
2066
- }
2067
- const sep = contents.length === 0 || contents.endsWith("\n") ? "" : "\n";
2068
- writeFileSync(gitignorePath, `${contents}${sep}${gitignoreLine}\n`);
2069
- };
2070
2092
  const cleanup = () => {
2071
2093
  rmSync(middlewarePath, { force: true });
2072
2094
  };
@@ -2075,10 +2097,9 @@ function nitroDevTriggerInjectPlugin(root, options) {
2075
2097
  apply: "serve",
2076
2098
  configureServer(server) {
2077
2099
  ensureFile();
2078
- ensureGitignore();
2100
+ ensureGitignoreLines(root, [middlewareRelative]);
2079
2101
  server.httpServer?.once("close", cleanup);
2080
- const onExit = () => cleanup();
2081
- process.once("exit", onExit);
2102
+ process.once("exit", cleanup);
2082
2103
  }
2083
2104
  };
2084
2105
  }
@@ -2130,9 +2151,9 @@ export default defineEventHandler(async (event) => {
2130
2151
  * some routes out of Nitro), so Nitro plugins like nitro-cloudflare-dev never
2131
2152
  * run for it and `event.context.cloudflare` is empty there.
2132
2153
  *
2133
- * Workaround: put the actual handler at \`src/server/routes/api/__void/[trigger].ts\`
2134
- * — Nitro \*does\* run plugins for routes — and proxy \`/__void/{scheduled,queue}\`
2135
- * to \`/api/__void/{scheduled,queue}\` from a small Vite middleware so the
2154
+ * Workaround: put the actual handlers at `src/server/routes/api/__void/{cron,queue}-trigger.ts`
2155
+ * — Nitro *does* run plugins for routes — and proxy `/__void/{scheduled,queue}`
2156
+ * to `/api/__void/{cron,queue}-trigger` from a small Vite middleware so the
2136
2157
  * user-facing URL stays consistent with the other frameworks.
2137
2158
  */
2138
2159
  function analogDevTriggerInjectPlugin(root) {
@@ -2147,16 +2168,6 @@ function analogDevTriggerInjectPlugin(root) {
2147
2168
  mkdirSync(routeDir, { recursive: true });
2148
2169
  for (const { user, path } of routePaths) writeFileSync(path, analogRouteSource(dispatchPath, user));
2149
2170
  };
2150
- const ensureGitignore = () => {
2151
- const gitignorePath = join(root, ".gitignore");
2152
- let contents = "";
2153
- if (existsSync(gitignorePath)) contents = readFileSync(gitignorePath, "utf-8");
2154
- const existing = new Set(contents.split("\n").map((l) => l.trim()));
2155
- const missing = gitignoreLines.filter((l) => !existing.has(l));
2156
- if (missing.length === 0) return;
2157
- const sep = contents.length === 0 || contents.endsWith("\n") ? "" : "\n";
2158
- writeFileSync(gitignorePath, `${contents}${sep}${missing.join("\n")}\n`);
2159
- };
2160
2171
  const cleanup = () => {
2161
2172
  for (const { path } of routePaths) rmSync(path, { force: true });
2162
2173
  };
@@ -2165,7 +2176,7 @@ function analogDevTriggerInjectPlugin(root) {
2165
2176
  apply: "serve",
2166
2177
  configureServer(server) {
2167
2178
  ensureFiles();
2168
- ensureGitignore();
2179
+ ensureGitignoreLines(root, gitignoreLines);
2169
2180
  const proxy = async (req, res, next) => {
2170
2181
  if (req.method !== "POST") {
2171
2182
  next();
@@ -2177,6 +2188,12 @@ function analogDevTriggerInjectPlugin(root) {
2177
2188
  next();
2178
2189
  return;
2179
2190
  }
2191
+ const address = server.httpServer?.address();
2192
+ if (!address || typeof address !== "object") {
2193
+ next();
2194
+ return;
2195
+ }
2196
+ const hostname = address.address === "0.0.0.0" ? "127.0.0.1" : address.address === "::" ? "localhost" : address.address.includes(":") ? `[${address.address}]` : address.address;
2180
2197
  try {
2181
2198
  const chunks = [];
2182
2199
  for await (const chunk of req) chunks.push(chunk);
@@ -2184,8 +2201,7 @@ function analogDevTriggerInjectPlugin(root) {
2184
2201
  const headers = new Headers();
2185
2202
  for (const [key, value] of Object.entries(req.headers)) if (typeof value === "string") headers.set(key, value);
2186
2203
  else if (Array.isArray(value)) headers.set(key, value.join(", "));
2187
- const host = req.headers.host ?? "localhost";
2188
- const upstream = await fetch(`http://${host}/api/__void/${match.file}`, {
2204
+ const upstream = await fetch(`http://${hostname}:${address.port}/api/__void/${match.file}`, {
2189
2205
  method: "POST",
2190
2206
  headers,
2191
2207
  body
@@ -2199,8 +2215,7 @@ function analogDevTriggerInjectPlugin(root) {
2199
2215
  };
2200
2216
  server.middlewares.use(proxy);
2201
2217
  server.httpServer?.once("close", cleanup);
2202
- const onExit = () => cleanup();
2203
- process.once("exit", onExit);
2218
+ process.once("exit", cleanup);
2204
2219
  }
2205
2220
  };
2206
2221
  }
@@ -2221,15 +2236,13 @@ export const onRequest = defineMiddleware(async (context, next) => {
2221
2236
  });
2222
2237
  `;
2223
2238
  }
2224
- const ASTRO_GITIGNORE_LINE = "src/middleware.ts";
2225
2239
  /**
2226
2240
  * Writes a tiny Astro middleware to the user's project so
2227
- * \`/__void/scheduled\` and \`/__void/queue\` are intercepted inside Astro's
2228
- * request pipeline (where \`context.locals.runtime.env\` is populated by
2229
- * \`@astrojs/cloudflare\`).
2241
+ * `/__void/scheduled` and `/__void/queue` are intercepted inside Astro's
2242
+ * request pipeline, with bindings from `cloudflare:workers`.
2230
2243
  *
2231
2244
  * Astro middleware lives at a single discoverable entry, so if the user
2232
- * already has \`src/middleware.ts\` we don't overwrite it — instead we log a
2245
+ * already has `src/middleware.ts` we don't overwrite it — instead we log a
2233
2246
  * one-shot snippet they can paste into their existing middleware.
2234
2247
  *
2235
2248
  * If we wrote the file we remove it on dev-server close so the user's
@@ -2248,17 +2261,6 @@ function astroDevTriggerInjectPlugin(root) {
2248
2261
  writeFileSync(middlewarePath, astroMiddlewareSource(frameworkTriggersDispatchPath(root)));
2249
2262
  wroteFile = true;
2250
2263
  };
2251
- const ensureGitignore = () => {
2252
- if (!wroteFile) return;
2253
- const gitignorePath = join(root, ".gitignore");
2254
- let contents = "";
2255
- if (existsSync(gitignorePath)) {
2256
- contents = readFileSync(gitignorePath, "utf-8");
2257
- if (contents.split("\n").some((line) => line.trim() === ASTRO_GITIGNORE_LINE)) return;
2258
- }
2259
- const sep = contents.length === 0 || contents.endsWith("\n") ? "" : "\n";
2260
- writeFileSync(gitignorePath, `${contents}${sep}${ASTRO_GITIGNORE_LINE}\n`);
2261
- };
2262
2264
  const cleanup = () => {
2263
2265
  if (wroteFile) rmSync(middlewarePath, { force: true });
2264
2266
  };
@@ -2267,10 +2269,9 @@ function astroDevTriggerInjectPlugin(root) {
2267
2269
  apply: "serve",
2268
2270
  configureServer(server) {
2269
2271
  ensureFile();
2270
- ensureGitignore();
2272
+ if (wroteFile) ensureGitignoreLines(root, [ASTRO_MIDDLEWARE_RELATIVE]);
2271
2273
  server.httpServer?.once("close", cleanup);
2272
- const onExit = () => cleanup();
2273
- process.once("exit", onExit);
2274
+ process.once("exit", cleanup);
2274
2275
  }
2275
2276
  };
2276
2277
  }
@@ -2382,7 +2383,7 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
2382
2383
  lines.push("app.use(__routingRuntime.requestMiddleware);");
2383
2384
  lines.push("");
2384
2385
  const emitEdgeRules = !managedDispatch;
2385
- if (emitEdgeRules && options.redirectRules?.length) compileRoutingRulesMiddleware(lines, options.redirectRules, options.dev === true, options.nodeTarget === true);
2386
+ if (emitEdgeRules && options.redirectRules?.length) compileRoutingRulesMiddleware(lines, options.redirectRules, options.dev === true);
2386
2387
  if (emitEdgeRules && options.headerRules?.length) compileHeaderMiddleware(lines, options.headerRules);
2387
2388
  if (auth) {
2388
2389
  lines.push("app.use(async (c, next) => {");
@@ -2475,20 +2476,26 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
2475
2476
  }
2476
2477
  lines.push("");
2477
2478
  lines.push("export async function scheduled(controller, env, ctx) {");
2479
+ lines.push(" const run = async (job, loadJob) => {");
2480
+ lines.push(" try {");
2481
+ lines.push(" const mod = await loadJob();");
2482
+ lines.push(" if (typeof mod.default === 'function') await mod.default(controller, env, ctx);");
2483
+ lines.push(" } catch (error) {");
2484
+ lines.push(" console.error(`[void] Scheduled job ${job} failed for cron \"${controller.cron}\":`, error);");
2485
+ lines.push(" return { job, error };");
2486
+ lines.push(" }");
2487
+ lines.push(" };");
2478
2488
  lines.push(" const tasks = [];");
2479
2489
  for (let i = 0; i < scan.jobs.length; i++) {
2480
2490
  const job = scan.jobs[i];
2481
2491
  const filePath = JSON.stringify(`${jobsDir}/${job.filePath}`);
2482
- lines.push(` if (job${i}Crons.includes(controller.cron)) {`);
2483
- lines.push(" tasks.push((async () => {");
2484
- lines.push(` const mod = await import(${filePath});`);
2485
- lines.push(" if (typeof mod.default === 'function') await mod.default(controller, env, ctx);");
2486
- lines.push(" })());");
2487
- lines.push(" }");
2492
+ const label = JSON.stringify(relative(options.projectRoot, `${jobsDir}/${job.filePath}`));
2493
+ lines.push(` if (job${i}Crons.includes(controller.cron)) tasks.push(run(${label}, () => import(${filePath})));`);
2488
2494
  }
2489
- lines.push(" const results = await Promise.allSettled(tasks);");
2490
- lines.push(" const errors = results.filter(result => result.status === \"rejected\").map(result => result.reason);");
2491
- lines.push(" if (errors.length) throw new AggregateError(errors, \"Scheduled jobs failed\");");
2495
+ lines.push(" const failures = (await Promise.all(tasks)).filter(Boolean);");
2496
+ lines.push(" if (failures.length) {");
2497
+ lines.push(" throw new AggregateError(failures.map((failure) => failure.error), `Scheduled jobs failed: ${failures.map((failure) => failure.job).join(\", \")}`);");
2498
+ lines.push(" }");
2492
2499
  lines.push("}");
2493
2500
  lines.push("");
2494
2501
  } else {
@@ -2500,7 +2507,7 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
2500
2507
  lines.push(" const __token = c.req.header(\"x-void-internal\");");
2501
2508
  lines.push(" const __expected = __getRawEnv()?.__VOID_PROXY_TOKEN;");
2502
2509
  lines.push(" const __devToken = c.req.header(\"x-void-dev-trigger\");");
2503
- lines.push(" if (__expected ? __token !== __expected : import.meta.env.DEV ? __devToken !== __VOID_DEV_TRIGGER_TOKEN : __token !== __VOID_BUILD_KEY) {");
2510
+ lines.push(" if (__VOID_SERVE ? __devToken !== __VOID_DEV_TRIGGER_TOKEN : __expected ? __token !== __expected : __token !== __VOID_BUILD_KEY) {");
2504
2511
  lines.push(" return c.json({ error: \"unauthorized\" }, 401);");
2505
2512
  lines.push(" }");
2506
2513
  lines.push(" const { cron, scheduledTime } = await c.req.json();");
@@ -2556,7 +2563,7 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
2556
2563
  lines.push(" const __token = c.req.header(\"x-void-internal\");");
2557
2564
  lines.push(" const __expected = __getRawEnv()?.__VOID_PROXY_TOKEN;");
2558
2565
  lines.push(" const __devToken = c.req.header(\"x-void-dev-trigger\");");
2559
- lines.push(" if (__expected ? __token !== __expected : import.meta.env.DEV ? __devToken !== __VOID_DEV_TRIGGER_TOKEN : __token !== __VOID_BUILD_KEY) {");
2566
+ lines.push(" if (__VOID_SERVE ? __devToken !== __VOID_DEV_TRIGGER_TOKEN : __expected ? __token !== __expected : __token !== __VOID_BUILD_KEY) {");
2560
2567
  lines.push(" return c.json({ error: \"unauthorized\" }, 401);");
2561
2568
  lines.push(" }");
2562
2569
  lines.push(" const payload = await c.req.json();");
@@ -2740,6 +2747,7 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
2740
2747
  lines.push("function __getRawEnv() { return __rawEnvStore.getStore(); }");
2741
2748
  lines.push(`export const __VOID_BUILD_KEY = ${JSON.stringify(crypto.randomUUID())};`);
2742
2749
  lines.push(`export const __VOID_DEV_TRIGGER_TOKEN = ${JSON.stringify(options.devTriggerToken ?? crypto.randomUUID())};`);
2750
+ lines.push(`const __VOID_SERVE = ${JSON.stringify(options.dev === true)};`);
2743
2751
  lines.push(`export const __VOID_ISR_REVALIDATE = ${JSON.stringify(mergedRevalidate)};`);
2744
2752
  lines.push(`export const __VOID_ISR_PAGES = ${JSON.stringify(isrPages)};`);
2745
2753
  lines.push(`export const __VOID_ISR_PATH_REVALIDATE = ${JSON.stringify(isrPathRevalidate)};`);
@@ -2888,7 +2896,10 @@ function compilePageRoutes(lines, scan, pages, options) {
2888
2896
  }
2889
2897
  const renderer = page.renderMode === "island" ? "renderIslandPage" : "renderPage";
2890
2898
  const assetTags = page.renderMode === "island" && pages.islandClientEntryId ? "getIslandsAssetTags" : "getPagesAssetTags";
2891
- const routeOptions = `{ ${serverImportPath ? "load: _pload_" + safeId + ", " : ""}${mdHeadRef ? "head: " + mdHeadRef + ", " : ""}componentId: ${JSON.stringify(page.componentId)}, render: ${renderer}, assets: ${assetTags}, headConfig: __voidHeadConfig, renderOptions: ${JSON.stringify({ renderMode: page.renderMode })} }`;
2899
+ const routeOptions = `{ ${serverImportPath ? "load: _pload_" + safeId + ", " : ""}${mdHeadRef ? "head: " + mdHeadRef + ", " : ""}componentId: ${JSON.stringify(page.componentId)}, render: ${renderer}, assets: ${assetTags}, headConfig: __voidHeadConfig, renderOptions: ${JSON.stringify({
2900
+ renderMode: page.renderMode,
2901
+ ...options.dev === true ? { dev: true } : {}
2902
+ })} }`;
2892
2903
  lines.push(`app.get(${JSON.stringify(page.pattern)}, __createPageGetHandler(${routeOptions}));`);
2893
2904
  if (page.methods.includes("action") || page.methods.includes("actions")) lines.push(`app.on(["POST", "PUT", "PATCH", "DELETE"], ${JSON.stringify(page.pattern)}, __createPageMutationHandler({ ...${routeOptions}, namedActions: ${JSON.stringify(page.methods.includes("actions"))} }));`);
2894
2905
  }
@@ -2919,26 +2930,25 @@ const serveWithAssets = __createAssetFallback(${JSON.stringify({
2919
2930
  })});
2920
2931
  `;
2921
2932
  }
2933
+ function deriveEmailRuntimePath(runtimeEnvPath) {
2934
+ return runtimeEnvPath.replace(/_?env(\.[^/]+)?$/, (_m, ext) => "email" + (ext ?? ""));
2935
+ }
2922
2936
  /**
2923
2937
  * Generate the Worker entry module code.
2924
2938
  * Re-exports the Hono app from the virtual module with an asset-serving
2925
2939
  * fallthrough: when the app returns 404, the entry tries env.ASSETS.fetch()
2926
2940
  * and adds proper Cache-Control headers (immutable for Vite-hashed /assets/*).
2927
2941
  *
2928
- * When `hasQueues` is true, also exports a `queue()` handler that
2929
- * serializes the native MessageBatch and dispatches it to the
2930
- * `/__void/queue` Hono route. In local dev Miniflare delivers batches
2931
- * natively to this export; in production the relay worker does it.
2942
+ * When `hasQueues` is true, also exports a `queue()` method that passes the
2943
+ * native MessageBatch to `__processQueue` and applies its ack/retry
2944
+ * decisions. Managed delivery uses the `/__void/queue` route instead.
2932
2945
  *
2933
- * When `remotePath` is provided, wraps the fetch handler to detect
2946
+ * When `remote` is provided, wraps the fetch handler to detect
2934
2947
  * remote mode (`__VOID_REMOTE`) and replace local bindings with
2935
2948
  * proxy-backed versions.
2936
2949
  */
2937
- function deriveEmailRuntimePath(runtimeEnvPath) {
2938
- return runtimeEnvPath.replace(/_?env(\.[^/]+)?$/, (_m, ext) => "email" + (ext ?? ""));
2939
- }
2940
2950
  function compileWorkerEntry(options = {}) {
2941
- const { hasQueues = false, versionMetadataBinding, remote, assetsDir = "assets", webSocketClasses = [], runtimeEnvPath = "void/_env", runtimeEmailPath, envSchemaModule, sandbox, serveSpaFallback = false, hasEmail = false, live, isr, notFound = "none", durableObjects = [] } = options;
2951
+ const { hasQueues = false, versionMetadataBinding, remote, assetsDir = "assets", webSocketClasses = [], runtimeEnvPath = "void/_env", runtimeEmailPath, envSchemaModule, sandbox, serveSpaFallback = false, hasEmail = false, live, dev = false, isr, notFound = "none", durableObjects = [] } = options;
2942
2952
  const remotePath = remote?.remotePath;
2943
2953
  const remoteBindingNames = JSON.stringify(remote?.bindingNames);
2944
2954
  const createRemoteEnvCall = `createRemoteEnv(env${remote?.bindingNames ? `, ${remoteBindingNames}` : ""})`;
@@ -2953,8 +2963,10 @@ function compileWorkerEntry(options = {}) {
2953
2963
  const liveBindingName = typeof live === "object" ? live.bindingName ?? "VOID_LIVE" : "VOID_LIVE";
2954
2964
  const liveClassName = typeof live === "object" ? live.className ?? "VoidLiveStreamDurableObject" : "VoidLiveStreamDurableObject";
2955
2965
  const liveRouteImport = hasLive ? ", VoidLiveStreamDurableObject" : "";
2956
- const liveRuntimeImport = hasLive ? `import { __setLiveBindingName } from "void/live";\n` : "";
2957
- const liveExport = hasLive ? liveClassName === "VoidLiveStreamDurableObject" ? `export { VoidLiveStreamDurableObject };\n__setLiveBindingName(${JSON.stringify(liveBindingName)});\n` : `export { VoidLiveStreamDurableObject as ${liveClassName} };\n__setLiveBindingName(${JSON.stringify(liveBindingName)});\n` : "";
2966
+ const liveDev = hasLive && dev === true;
2967
+ const liveRuntimeImport = hasLive ? `import { __setLiveBindingName${liveDev ? ", __setLiveDev" : ""} } from "void/live";\n` : "";
2968
+ const liveDevCall = liveDev ? "__setLiveDev(true);\n" : "";
2969
+ const liveExport = hasLive ? liveClassName === "VoidLiveStreamDurableObject" ? `export { VoidLiveStreamDurableObject };\n__setLiveBindingName(${JSON.stringify(liveBindingName)});\n${liveDevCall}` : `export { VoidLiveStreamDurableObject as ${liveClassName} };\n__setLiveBindingName(${JSON.stringify(liveBindingName)});\n${liveDevCall}` : "";
2958
2970
  const durableObjectImports = durableObjects.map(({ filePath }, index) => `import __voidDurableObject${index} from ${JSON.stringify(filePath)};`).join("\n");
2959
2971
  const durableObjectExports = durableObjects.map(({ bindingName, className, name }, index) => `__voidDurableObject${index}.__bind(${JSON.stringify(bindingName)}${name !== void 0 ? `, ${JSON.stringify(name)}` : ""});\nexport const ${className} = __voidDurableObject${index}.class;`).join("\n");
2960
2972
  const runtimeEnvImport = `import { withRuntimeEnv } from ${JSON.stringify(runtimeEnvPath)};\n`;
@@ -3097,7 +3109,7 @@ ${entryClass}`;
3097
3109
  function compileNodeApp(target, honoPath, envSchemaModule) {
3098
3110
  const staticImport = target === "bun" ? `import { serveStatic } from "hono/bun";` : target === "deno" ? `import { serveStatic } from "hono/deno";` : `import { serveStatic } from "@hono/node-server/serve-static";`;
3099
3111
  const envSchemaImport = envSchemaModule ? `import ${JSON.stringify(envSchemaModule.importPath)};\nimport { _validateEnvOnce } from ${JSON.stringify(envSchemaModule.helperPath)};\n` : "";
3100
- const envMiddleware = envSchemaModule ? `app.use("*", async (c, next) => { if (c.req.method !== "POST" || c.req.path !== "/__void/prerender-paths") await _validateEnvOnce(${target === "deno" ? "Deno.env.toObject()" : "process.env"}); return next(); });\n` : "";
3112
+ const envMiddleware = envSchemaModule ? `app.use("*", async (c, next) => { if (!(c.req.method === "POST" && c.req.path === "/__void/prerender-paths" && c.env?.__VOID_PRERENDER === "1")) await _validateEnvOnce(${target === "deno" ? "Deno.env.toObject()" : "process.env"}); return next(); });\n` : "";
3101
3113
  return `${staticImport}
3102
3114
  import { fileURLToPath } from "node:url";
3103
3115
  import { Hono } from ${JSON.stringify(honoPath)};
@@ -3110,11 +3122,7 @@ assetApp.get("*", serveStatic({ root: clientRoot }));
3110
3122
  const assets = { fetch: (request) => assetApp.fetch(request) };
3111
3123
  const app = new Hono();
3112
3124
  ${envMiddleware}app.post("/__void/prerender-paths", async (c) => {
3113
- const __expected = c.env?.__VOID_PROXY_TOKEN;
3114
- if (__expected) {
3115
- const __token = c.req.header("x-void-internal");
3116
- if (!__token || __token !== __expected) return c.json({ error: "unauthorized" }, 401);
3117
- }
3125
+ if (c.env?.__VOID_PRERENDER !== "1") return c.notFound();
3118
3126
  const paths = await __voidCollectPrerenderPaths(c.req.raw);
3119
3127
  return c.json({ paths });
3120
3128
  });
@@ -3144,16 +3152,17 @@ serve({ fetch: app.fetch, port }, (info) => {
3144
3152
  console.log("Listening on http://localhost:" + info.port);
3145
3153
  });`;
3146
3154
  }
3147
- /**
3148
- * Generate the server-side fetch client module for non-CF targets.
3149
- * Same as compileWorkerClient but without cloudflare:workers dependency.
3150
- * Calls app.fetch() directly (no HTTP round-trip) with ALS context forwarding.
3151
- */
3155
+ /** Generate the browser client module: re-exports the HTTP fetch, stream, and auth clients. */
3152
3156
  function compileBrowserClient(authClientPath, fetchPath, fetchStreamPath) {
3153
3157
  return `export { fetch, FetchError } from ${JSON.stringify(fetchPath)};
3154
3158
  export { fetchStream, StreamError } from ${JSON.stringify(fetchStreamPath)};
3155
3159
  export { auth, createAuthClient } from ${JSON.stringify(authClientPath)};`;
3156
3160
  }
3161
+ /**
3162
+ * Generate the server-side fetch client module for non-CF targets.
3163
+ * Same as compileWorkerClient but without cloudflare:workers dependency.
3164
+ * Calls app.fetch() directly (no HTTP round-trip) with ALS context forwarding.
3165
+ */
3157
3166
  function compileNodeClient(authClientPath) {
3158
3167
  return `import app, { requestContext } from "virtual:void-routes";
3159
3168
  import { interpolateRouteParams } from ${JSON.stringify(ROUTE_URL_PATH)};
@@ -3220,7 +3229,7 @@ export async function fetch(path, options) {
3220
3229
  function stripOriginForProd(rules) {
3221
3230
  return rules.map(({ origin: _origin, ...rest }) => rest);
3222
3231
  }
3223
- function compileRoutingRulesMiddleware(lines, rules, dev, _nodeTarget) {
3232
+ function compileRoutingRulesMiddleware(lines, rules, dev) {
3224
3233
  lines.push("// Routing rules: redirects + rewrites (applied before route handlers)");
3225
3234
  lines.push(`const __routingRules = ${JSON.stringify(dev ? rules : stripOriginForProd(rules))};`);
3226
3235
  lines.push("app.use(\"*\", __routingRuntime.routingMiddleware(__routingRules));");
@@ -3304,10 +3313,8 @@ export async function fetch(path, options) {
3304
3313
  * Resolve the server entry module for a Class A framework. We always wrap
3305
3314
  * the framework's own entry with a tiny shim that intercepts
3306
3315
  * `/__void/{scheduled,queue}` in dev so cron and queue handlers run with
3307
- * the worker's full CF bindings. The interception block is gated on
3308
- * `import.meta.env.DEV` and the dispatch module is loaded via dynamic
3309
- * `import()` so the entire dev path (including user cron imports) is
3310
- * dead-code-eliminated in `vite build`. Production deploys go through
3316
+ * the worker's full CF bindings. `vite build` gets a dispatch-free shim
3317
+ * (see `generateClassATriggerWrapper`). Production deploys go through
3311
3318
  * `cli/wrapper.ts` instead.
3312
3319
  */
3313
3320
  function getFrameworkEntry(framework, root, devTriggerToken, notFound) {
@@ -3447,18 +3454,16 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
3447
3454
  if (hasSqlMigrations && dialect !== "sqlite") {
3448
3455
  const url = readDatabaseUrl(root);
3449
3456
  if (!url) {
3450
- const warn = () => {
3457
+ afterServerReady(server, () => {
3451
3458
  console.log();
3452
3459
  voidError(`DATABASE_URL not found in .env — skipping ${dialect === "mysql" ? "MySQL" : "PostgreSQL"} migrations.`);
3453
- };
3454
- if (server.httpServer) server.httpServer.once("listening", () => setTimeout(warn, 100));
3455
- else warn();
3460
+ });
3456
3461
  return;
3457
3462
  }
3458
3463
  const { collectMigrations } = await import("./collect-DLyFDiKW.mjs");
3459
- const { validateMigrations } = await import("./validate-pna7R3Pv.mjs");
3464
+ const { validateMigrations } = await import("./validate-BJr9LfXy.mjs");
3460
3465
  const allMigrations = collectMigrations(migrationsDir);
3461
- const appliedNames = dialect === "mysql" ? await import("./runner-mysql-CEG5JozK.mjs").then((runner) => runner.getMySqlAppliedMigrations(url)) : await import("./runner-pg-Bh4AgZdj.mjs").then((runner) => runner.getPgAppliedMigrations(url));
3466
+ const appliedNames = dialect === "mysql" ? await import("./runner-mysql-CkFqZ6CE.mjs").then((runner) => runner.getMySqlAppliedMigrations(url)) : await import("./runner-pg-BGYRFdRp.mjs").then((runner) => runner.getPgAppliedMigrations(url));
3462
3467
  const appliedSet = new Set(appliedNames);
3463
3468
  const pending = allMigrations.filter((migration) => !appliedSet.has(migration.name));
3464
3469
  if (pending.length > 0) try {
@@ -3466,12 +3471,12 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
3466
3471
  } catch (err) {
3467
3472
  voidWarn(err instanceof Error ? err.message : String(err));
3468
3473
  }
3469
- const applied = dialect === "mysql" ? await import("./runner-mysql-CEG5JozK.mjs").then((runner) => runner.runMySqlMigrations(migrationsDir, url)) : await import("./runner-pg-Bh4AgZdj.mjs").then((runner) => runner.runPgMigrations(migrationsDir, url));
3474
+ const applied = dialect === "mysql" ? await import("./runner-mysql-CkFqZ6CE.mjs").then((runner) => runner.runMySqlMigrations(migrationsDir, url)) : await import("./runner-pg-BGYRFdRp.mjs").then((runner) => runner.runPgMigrations(migrationsDir, url));
3470
3475
  if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
3471
3476
  } else if (hasSqlMigrations) {
3472
- const { runMigrations, getMiniflareDatabasePath, getAppliedMigrations } = await import("./runner-voIltZrY.mjs");
3477
+ const { runMigrations, getMiniflareDatabasePath, getAppliedMigrations } = await import("./runner-9wjsSZx-.mjs");
3473
3478
  const { collectMigrations } = await import("./collect-DLyFDiKW.mjs");
3474
- const { validateMigrations } = await import("./validate-pna7R3Pv.mjs");
3479
+ const { validateMigrations } = await import("./validate-BJr9LfXy.mjs");
3475
3480
  const dbPath = getMiniflareDatabasePath(persistRoot, databaseId);
3476
3481
  const allMigrations = collectMigrations(migrationsDir);
3477
3482
  const appliedRows = await getAppliedMigrations(dbPath);
@@ -3486,7 +3491,7 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
3486
3491
  if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
3487
3492
  }
3488
3493
  if (auth?.enabled) {
3489
- const { runLocalVoidAuthMigrations } = await import("./node-C6Q6e1Es.mjs");
3494
+ const { runLocalVoidAuthMigrations } = await import("./node-DJicPvPj.mjs");
3490
3495
  await runLocalVoidAuthMigrations(root, dialect, persistRoot, databaseId, auth.configPath);
3491
3496
  voidLog("Applied Better Auth migrations");
3492
3497
  }
@@ -3686,6 +3691,7 @@ function createFrameworkPlugins(input) {
3686
3691
  }
3687
3692
  };
3688
3693
  return [
3694
+ devTriggerSecretGuardPlugin(),
3689
3695
  createCloudflareBuiltinsExternalPlugin(),
3690
3696
  wranglerCompatPlugin,
3691
3697
  wranglerSyncPlugin,
@@ -3886,8 +3892,7 @@ function prerenderPlugin(root, config) {
3886
3892
  workerDir,
3887
3893
  clientDir,
3888
3894
  persistDir,
3889
- remoteMode,
3890
- projectId: process.env.VOID_DEPLOY_PROJECT_ID || void 0
3895
+ remoteMode
3891
3896
  });
3892
3897
  }
3893
3898
  if (result.paths.length === 0) {
@@ -3901,7 +3906,7 @@ function prerenderPlugin(root, config) {
3901
3906
  //#endregion
3902
3907
  //#region src/plugin-restart.ts
3903
3908
  const BINDING_HINT_RESOLVED = "\0virtual:void/binding-hint";
3904
- const RESTARTING_HTML = `<!DOCTYPE html>
3909
+ const RESTARTING_HTML_HEAD = `<!DOCTYPE html>
3905
3910
  <html lang="en">
3906
3911
  <head>
3907
3912
  <meta charset="UTF-8">
@@ -3919,24 +3924,87 @@ const RESTARTING_HTML = `<!DOCTYPE html>
3919
3924
  <body>
3920
3925
  <div><span class="tag">[void]</span> Restarting dev server<span class="dots"><span>.</span><span>.</span><span>.</span></span></div>
3921
3926
  <script>
3922
- fetch("/__void/restart",{method:"POST"}).catch(function(){});
3927
+ var target=`;
3928
+ const RESTARTING_HTML_MIDDLE = `;
3929
+ var token=`;
3930
+ const RESTARTING_HTML_TAIL = `;
3931
+ fetch("/__void/restart",{method:"POST",headers:{"x-void-restart":token}}).catch(function(){});
3923
3932
  setTimeout(function p(){
3924
3933
  fetch("/__void/ping").then(function(r){
3925
- if(r.ok)location.href=new URLSearchParams(location.search).get("return")||"/";
3934
+ if(r.ok)location.href=target;
3926
3935
  else setTimeout(p,150);
3927
3936
  }).catch(function(){setTimeout(p,150)});
3928
3937
  },300);
3929
3938
  <\/script>
3930
3939
  </body>
3931
3940
  </html>`;
3932
- const CLIENT_MODULE = `if (import.meta.hot) {
3941
+ const CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
3942
+ /**
3943
+ * Returns `value` when it is a path on the dev server's own origin, else `/`.
3944
+ * The restarting page navigates to it, so a scheme (`javascript:`) or a
3945
+ * protocol-relative form (`//host`, `/\host`) must never get through.
3946
+ */
3947
+ function sanitizeReturnPath(value) {
3948
+ if (!value || value[0] !== "/" || value[1] === "/" || value[1] === "\\") return "/";
3949
+ if (CONTROL_CHARS.test(value)) return "/";
3950
+ const base = "http://void.invalid";
3951
+ try {
3952
+ if (new URL(value, base).origin !== base) return "/";
3953
+ } catch {
3954
+ return "/";
3955
+ }
3956
+ return value;
3957
+ }
3958
+ /**
3959
+ * `token` is the `t` query value the toast navigated with. The page sends it
3960
+ * back with its POST; `/__void/restart` checks it, not this page.
3961
+ */
3962
+ function renderRestartingPage(returnPath, token) {
3963
+ return RESTARTING_HTML_HEAD + safeJsonStringify(sanitizeReturnPath(returnPath)) + RESTARTING_HTML_MIDDLE + safeJsonStringify(token ?? "") + RESTARTING_HTML_TAIL;
3964
+ }
3965
+ /** Whether `presented` is this server's restart token, compared in constant time. */
3966
+ function isRestartTokenValid(presented, expected) {
3967
+ if (typeof presented !== "string" || expected.length === 0) return false;
3968
+ const a = Buffer.from(presented);
3969
+ const b = Buffer.from(expected);
3970
+ return a.byteLength === b.byteLength && timingSafeEqual(a, b);
3971
+ }
3972
+ /**
3973
+ * Whether a browser sent `req` from the dev server's own origin. Any page the
3974
+ * developer visits can POST to localhost, so cross-site callers are refused.
3975
+ * `Sec-Fetch-Site` is set by the browser and cannot be forged by a page;
3976
+ * `Origin` is the fallback for browsers that do not send it. Clients that send
3977
+ * neither (curl, scripts) are not browsers acting for another site.
3978
+ */
3979
+ function isSameOriginRequest(req) {
3980
+ const site = req.headers["sec-fetch-site"];
3981
+ if (site !== void 0) return site === "same-origin";
3982
+ const origin = req.headers.origin;
3983
+ if (origin === void 0) return true;
3984
+ const host = req.headers.host;
3985
+ if (!host) return false;
3986
+ return origin === `${req.socket?.encrypted ? "https" : "http"}://${host}`;
3987
+ }
3988
+ /** Whether a browser navigated here from the dev origin itself or from the address bar. */
3989
+ function isSameOriginNavigation(req) {
3990
+ const site = req.headers["sec-fetch-site"];
3991
+ return site === void 0 || site === "same-origin" || site === "none";
3992
+ }
3993
+ /**
3994
+ * The toast is the only in-app way to reach `/__void/restarting`, so it is
3995
+ * the only place that knows the restart token. Vite's default `server.cors`
3996
+ * lets only localhost origins read this module, the same exposure as the
3997
+ * HMR token in `/@vite/client`.
3998
+ */
3999
+ const clientModule = (token) => `if (import.meta.hot) {
4000
+ const token = ${JSON.stringify(token)};
3933
4001
  import.meta.hot.on("void:new-binding", (d) => {
3934
4002
  if (document.getElementById("void-binding-hint")) return;
3935
4003
  const e = document.createElement("div");
3936
4004
  e.id = "void-binding-hint";
3937
4005
  e.style.cssText = "position:fixed;top:16px;right:16px;z-index:99999;padding:12px 16px;background:rgba(26,26,46,0.95);color:#e0def4;font:13px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;border-radius:8px;border:1px solid rgba(255,255,255,0.08);box-shadow:0 8px 32px rgba(0,0,0,0.4);backdrop-filter:blur(8px);cursor:pointer;max-width:300px;transform:translateX(calc(100% + 16px));transition:transform .3s ease";
3938
4006
  e.innerHTML = "<strong style=\\"color:#c4a7e7\\">[void]</strong> " + d.message + " Click to restart.";
3939
- e.onclick = () => { location.href = "/__void/restarting?return=" + encodeURIComponent(location.pathname + location.search); };
4007
+ e.onclick = () => { location.href = "/__void/restarting?return=" + encodeURIComponent(location.pathname + location.search) + "&t=" + encodeURIComponent(token); };
3940
4008
  document.body.appendChild(e);
3941
4009
  requestAnimationFrame(() => requestAnimationFrame(() => {
3942
4010
  e.style.transform = "translateX(0)";
@@ -3947,8 +4015,10 @@ const CLIENT_MODULE = `if (import.meta.hot) {
3947
4015
  * Vite plugin that provides controlled dev-server restart via the browser.
3948
4016
  *
3949
4017
  * Serves three endpoints:
3950
- * - `/__void/restarting` — standalone loading page (no Vite HMR client)
3951
- * - `/__void/restart` — POST triggers `server.restart()` after a short delay
4018
+ * - `/__void/restarting` — standalone loading page (no Vite HMR client);
4019
+ * same-origin navigation only
4020
+ * - `/__void/restart` — same-origin POST carrying this server's restart
4021
+ * token triggers `server.restart()` after a short delay
3952
4022
  * - `/__void/ping` — health check; returns 503 on the old server instance
3953
4023
  * (after restart is triggered) and 200 on the new one
3954
4024
  *
@@ -3956,6 +4026,7 @@ const CLIENT_MODULE = `if (import.meta.hot) {
3956
4026
  * `void:new-binding` HMR events and shows a toast with a click-to-restart action.
3957
4027
  */
3958
4028
  function restartPlugin() {
4029
+ let restartToken = "";
3959
4030
  return {
3960
4031
  name: "void:restart",
3961
4032
  apply: "serve",
@@ -3968,11 +4039,13 @@ function restartPlugin() {
3968
4039
  load: {
3969
4040
  filter: { id: /^\0virtual:void\/binding-hint$/ },
3970
4041
  handler() {
3971
- return CLIENT_MODULE;
4042
+ return clientModule(restartToken);
3972
4043
  }
3973
4044
  },
3974
4045
  configureServer(server) {
3975
4046
  let restartPending = false;
4047
+ const token = randomBytes(16).toString("base64url");
4048
+ restartToken = token;
3976
4049
  server.middlewares.use("/__void/ping", (_req, res) => {
3977
4050
  res.writeHead(restartPending ? 503 : 200);
3978
4051
  res.end();
@@ -3983,14 +4056,35 @@ function restartPlugin() {
3983
4056
  res.end();
3984
4057
  return;
3985
4058
  }
4059
+ if (!isSameOriginRequest(req) || !isRestartTokenValid(req.headers["x-void-restart"], token)) {
4060
+ res.writeHead(403);
4061
+ res.end();
4062
+ return;
4063
+ }
3986
4064
  restartPending = true;
3987
4065
  res.writeHead(200);
3988
4066
  res.end();
3989
4067
  setTimeout(() => server.restart(), 50);
3990
4068
  });
3991
- server.middlewares.use("/__void/restarting", (_req, res) => {
3992
- res.writeHead(200, { "Content-Type": "text/html" });
3993
- res.end(RESTARTING_HTML);
4069
+ server.middlewares.use("/__void/restarting", (req, res) => {
4070
+ if (!isSameOriginNavigation(req)) {
4071
+ res.writeHead(403, { "Content-Type": "text/plain" });
4072
+ res.end("Restart the dev server from its own page.");
4073
+ return;
4074
+ }
4075
+ let returnPath = null;
4076
+ let presented = null;
4077
+ try {
4078
+ const params = new URL(req.url ?? "/", "http://void.invalid").searchParams;
4079
+ returnPath = params.get("return");
4080
+ presented = params.get("t");
4081
+ } catch {}
4082
+ res.writeHead(200, {
4083
+ "Content-Type": "text/html",
4084
+ "Cache-Control": "no-store",
4085
+ "Referrer-Policy": "no-referrer"
4086
+ });
4087
+ res.end(renderRestartingPage(returnPath, presented));
3994
4088
  });
3995
4089
  }
3996
4090
  };
@@ -4026,9 +4120,16 @@ const PAGE_VIRTUAL_MODULE_IDS = [
4026
4120
  ];
4027
4121
  const RESOLVE_ID_FILTER = /^(virtual:void-(routes|client-worker(?::(react|vue|svelte|solid))?|client-browser(?::(react|vue|svelte|solid))?)|void(\/client(\/(react|vue|svelte|solid))?|\/queues)?)$/;
4028
4122
  const LOAD_ID_FILTER = /^virtual:void-(routes|client-worker(?::(react|vue|svelte|solid))?|client-browser(?::(react|vue|svelte|solid))?)$/;
4123
+ /**
4124
+ * Vite plugin that provides file-based routing.
4125
+ * - Scans routes/ and middleware/ directories
4126
+ * - Generates virtual modules for the route table and worker entry
4127
+ * - Watches for file changes and triggers HMR
4128
+ */
4029
4129
  function routingPlugin(root, options) {
4030
4130
  let scan = null;
4031
4131
  let routeEnvironment = "prod";
4132
+ let previewing = false;
4032
4133
  let ssr = null;
4033
4134
  let authConfigPath = null;
4034
4135
  const paths = readProjectPaths(root);
@@ -4264,24 +4365,48 @@ function routingPlugin(root, options) {
4264
4365
  notFound: options.routing?.notFound
4265
4366
  });
4266
4367
  }
4368
+ const sandboxEntry = options.hasSandbox ? {
4369
+ bindingName: options.sandboxBindingName,
4370
+ className: options.sandboxClassName,
4371
+ instanceType: options.sandboxInstanceType,
4372
+ runtimePath: sandboxRuntimePath
4373
+ } : false;
4374
+ function writeNodeEntries(target) {
4375
+ writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(target, compileOptions.honoPath, getEnvSchemaModule()));
4376
+ writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(target));
4377
+ }
4378
+ /** Write the Cloudflare worker entry from the completed scan. */
4379
+ function writeWorkerEntry(isr) {
4380
+ writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
4381
+ versionMetadataBinding: options.getVersionMetadataBinding?.(),
4382
+ hasQueues: scan.queues.length > 0,
4383
+ remote,
4384
+ assetsDir: resolvedConfig?.build.assetsDir,
4385
+ webSocketClasses: [...(scan.websockets ?? []).map((route) => route.className), ...options.websocketTombstones ?? []],
4386
+ runtimeEnvPath: compileOptions.runtimeEnvPath,
4387
+ runtimeEmailPath: compileOptions.runtimeEmailPath,
4388
+ envSchemaModule: getEnvSchemaModule(),
4389
+ sandbox: sandboxEntry,
4390
+ hasEmail: hasEmailHandlers(),
4391
+ serveSpaFallback: shouldServeSpaFallback(),
4392
+ live: options.live,
4393
+ dev: compileOptions.dev,
4394
+ isr,
4395
+ notFound: currentNotFoundHandling(),
4396
+ durableObjects: options.durableObjects
4397
+ }));
4398
+ }
4267
4399
  const outDir = join(root, ".void");
4268
4400
  mkdirSync(outDir, { recursive: true });
4269
- if (options.target) {
4270
- writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(options.target, compileOptions.honoPath, getEnvSchemaModule()));
4271
- writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
4272
- } else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
4401
+ if (options.target) writeNodeEntries(options.target);
4402
+ else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
4273
4403
  versionMetadataBinding: options.getVersionMetadataBinding?.(),
4274
4404
  remote,
4275
4405
  webSocketClasses: options.websocketTombstones,
4276
4406
  runtimeEnvPath: compileOptions.runtimeEnvPath,
4277
4407
  runtimeEmailPath: compileOptions.runtimeEmailPath,
4278
4408
  envSchemaModule: getEnvSchemaModule(),
4279
- sandbox: options.hasSandbox ? {
4280
- bindingName: options.sandboxBindingName,
4281
- className: options.sandboxClassName,
4282
- instanceType: options.sandboxInstanceType,
4283
- runtimePath: sandboxRuntimePath
4284
- } : false,
4409
+ sandbox: sandboxEntry,
4285
4410
  live: options.live,
4286
4411
  durableObjects: options.durableObjects
4287
4412
  }));
@@ -4291,7 +4416,7 @@ function routingPlugin(root, options) {
4291
4416
  configResolved(config) {
4292
4417
  resolvedConfig = config;
4293
4418
  authClientPath = resolveAuthClientPath(config);
4294
- compileOptions.dev = config.command === "serve";
4419
+ compileOptions.dev = config.command === "serve" && !previewing;
4295
4420
  compileOptions.assetsDir = config.build.assetsDir;
4296
4421
  compileOptions.reactRefresh = config.plugins.some((plugin) => plugin.name === "vite:react-babel" || plugin.name === "vite:react-refresh" || plugin.name === "vite:react-swc");
4297
4422
  const resolvedPublicDir = config.publicDir && config.publicDir.length > 0 ? config.publicDir : null;
@@ -4301,6 +4426,7 @@ function routingPlugin(root, options) {
4301
4426
  }
4302
4427
  },
4303
4428
  config(_, env) {
4429
+ previewing = env.isPreview === true;
4304
4430
  routeEnvironment = env.command === "serve" && !env.isPreview ? "dev" : "prod";
4305
4431
  compileOptions.hasLive = options.live !== void 0;
4306
4432
  compileOptions.managedDispatch = !options.target && env.command === "build" && Boolean(process.env.VOID_DEPLOY_PROJECT_ID);
@@ -4345,7 +4471,7 @@ function routingPlugin(root, options) {
4345
4471
  const migrationsDir = paths.migrationsDir;
4346
4472
  if (existsSync(migrationsDir) || compileOptions.auth) {
4347
4473
  const { collectMigrations } = await import("./collect-DLyFDiKW.mjs");
4348
- const { buildMigrationPlanBundle } = await import("./plan-COtUvdMA.mjs");
4474
+ const { buildMigrationPlanBundle } = await import("./plan-CH2G51je.mjs");
4349
4475
  const migrations = existsSync(migrationsDir) ? collectMigrations(migrationsDir) : [];
4350
4476
  const dialect = getDatabaseDialect(readConfig(root));
4351
4477
  compileOptions.migrationHandlerPath = resolveFile(dialect === "postgresql" ? "#self/runtime/migration-handler-pg" : dialect === "mysql" ? "#self/runtime/migration-handler-mysql" : "#self/runtime/migration-handler");
@@ -4354,43 +4480,19 @@ function routingPlugin(root, options) {
4354
4480
  compileOptions.migrationDialect = dialect;
4355
4481
  }
4356
4482
  }
4357
- if (options.target) {
4358
- writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(options.target, compileOptions.honoPath, getEnvSchemaModule()));
4359
- writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
4360
- } else {
4483
+ if (options.target) writeNodeEntries(options.target);
4484
+ else {
4361
4485
  const buildConfig = readConfig(root);
4362
4486
  const merged = collectRevalidate(buildConfig, scan.pages);
4363
4487
  const configuredRevalidate = buildConfig.routing?.revalidate;
4364
- const emitIsr = !options.target && resolvedConfig?.command === "build" && !process.env.VOID_DEPLOY_PROJECT_ID && hasRevalidate(merged);
4488
+ const emitIsr = resolvedConfig?.command === "build" && !process.env.VOID_DEPLOY_PROJECT_ID && hasRevalidate(merged);
4365
4489
  isrRevalidate = emitIsr ? merged : void 0;
4366
4490
  isrPages = emitIsr ? collectPages(scan.pages, configuredRevalidate) : void 0;
4367
4491
  isrPathRevalidate = emitIsr && configuredRevalidate && typeof configuredRevalidate === "object" ? configuredRevalidate : void 0;
4368
- const isr = emitIsr ? {
4492
+ writeWorkerEntry(emitIsr ? {
4369
4493
  runtimePath: resolveFile("@void/isr/runtime"),
4370
4494
  queryAllowlist: buildConfig.routing?.revalidateQueryAllowlist
4371
- } : void 0;
4372
- writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
4373
- versionMetadataBinding: options.getVersionMetadataBinding?.(),
4374
- hasQueues: scan.queues.length > 0,
4375
- remote,
4376
- assetsDir: resolvedConfig?.build.assetsDir,
4377
- webSocketClasses: [...(scan.websockets ?? []).map((route) => route.className), ...options.websocketTombstones ?? []],
4378
- runtimeEnvPath: compileOptions.runtimeEnvPath,
4379
- runtimeEmailPath: compileOptions.runtimeEmailPath,
4380
- envSchemaModule: getEnvSchemaModule(),
4381
- sandbox: options.hasSandbox ? {
4382
- bindingName: options.sandboxBindingName,
4383
- className: options.sandboxClassName,
4384
- instanceType: options.sandboxInstanceType,
4385
- runtimePath: sandboxRuntimePath
4386
- } : false,
4387
- hasEmail: hasEmailHandlers(),
4388
- serveSpaFallback: shouldServeSpaFallback(),
4389
- live: options.live,
4390
- isr,
4391
- notFound: currentNotFoundHandling(),
4392
- durableObjects: options.durableObjects
4393
- }));
4495
+ } : void 0);
4394
4496
  }
4395
4497
  },
4396
4498
  resolveId: {
@@ -4414,6 +4516,7 @@ function routingPlugin(root, options) {
4414
4516
  return compileWorkerClient(getClientAuthPath(id));
4415
4517
  }
4416
4518
  if (id !== VIRTUAL_ROUTES_ID) return;
4519
+ if (this?.environment?.name === "client") throw new Error(`${VIRTUAL_ROUTES_ID} is server-only and cannot be loaded by client code.`);
4417
4520
  if (!scan) await rescan();
4418
4521
  return compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, ssr, compileOptions, getPagesCompileOptions(), isrRevalidate, isrPages, isrPathRevalidate);
4419
4522
  }
@@ -4423,7 +4526,7 @@ function routingPlugin(root, options) {
4423
4526
  const handleFileChange = async (path, event) => {
4424
4527
  const normalizedPath = normalizePath(path);
4425
4528
  const sourceConventionChanged = isInDirectory(normalizedPath, normalizedRoutesDir) || isInDirectory(normalizedPath, normalizedMiddlewareDir) || isInDirectory(normalizedPath, normalizedJobsDir) || isInDirectory(normalizedPath, normalizedQueuesDir) || isInDirectory(normalizedPath, normalizedEmailDir) || isInDirectory(normalizedPath, normalizedPagesDir) || isPotentialAuthConfigPath(normalizedPath) || isPotentialEnvFilePath(normalizedPath);
4426
- if (!sourceConventionChanged && !isInDirectory(normalizedPath, normalizedSsrDir) && !isPotentialEnvFilePath(normalizedPath)) return;
4529
+ if (!sourceConventionChanged && !isInDirectory(normalizedPath, normalizedSsrDir)) return;
4427
4530
  if (!sourceConventionChanged && isInDirectory(normalizedPath, normalizedSsrDir) && !ssrEntryAbsPaths.has(normalizedPath)) return;
4428
4531
  await rescan(true);
4429
4532
  resolveAuthCompileOptions();
@@ -4434,31 +4537,8 @@ function routingPlugin(root, options) {
4434
4537
  await writeAuthDatabaseFiles();
4435
4538
  writeRouteTypes();
4436
4539
  writeQueueTypes();
4437
- if (options.target) {
4438
- writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(options.target, compileOptions.honoPath, getEnvSchemaModule()));
4439
- writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
4440
- } else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
4441
- versionMetadataBinding: options.getVersionMetadataBinding?.(),
4442
- hasQueues: scan.queues.length > 0,
4443
- remote,
4444
- assetsDir: resolvedConfig?.build.assetsDir,
4445
- webSocketClasses: [...(scan.websockets ?? []).map((route) => route.className), ...options.websocketTombstones ?? []],
4446
- runtimeEnvPath: compileOptions.runtimeEnvPath,
4447
- runtimeEmailPath: compileOptions.runtimeEmailPath,
4448
- envSchemaModule: getEnvSchemaModule(),
4449
- sandbox: options.hasSandbox ? {
4450
- bindingName: options.sandboxBindingName,
4451
- className: options.sandboxClassName,
4452
- instanceType: options.sandboxInstanceType,
4453
- runtimePath: sandboxRuntimePath
4454
- } : false,
4455
- hasEmail: hasEmailHandlers(),
4456
- serveSpaFallback: shouldServeSpaFallback(),
4457
- live: options.live,
4458
- isr: void 0,
4459
- notFound: currentNotFoundHandling(),
4460
- durableObjects: options.durableObjects
4461
- }));
4540
+ if (options.target) writeNodeEntries(options.target);
4541
+ else writeWorkerEntry(void 0);
4462
4542
  const structural = event !== "change";
4463
4543
  const pageManifestChanged = isInDirectory(normalizedPath, normalizedPagesDir) && (structural || normalizedPath.endsWith(".md"));
4464
4544
  for (const env of Object.values(devServer.environments)) {
@@ -4585,6 +4665,15 @@ function shouldInjectEmailDevInboxVar(routeEnvironment, needsEmail, assetsBindin
4585
4665
  }
4586
4666
  //#endregion
4587
4667
  //#region src/plugin-worker-bindings.ts
4668
+ /** Durable Object bindings already in the patch, else in the resolved config. */
4669
+ function currentDurableObjectBindings(result, resolved) {
4670
+ return (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
4671
+ }
4672
+ /** Migrations already in the patch, else in the resolved config. */
4673
+ function currentMigrations(result, resolved) {
4674
+ const current = result.migrations ?? resolved.migrations;
4675
+ return Array.isArray(current) ? current : [];
4676
+ }
4588
4677
  function mergeWorkerBindings(resolved, input) {
4589
4678
  const { websocketRoutes, routeEnvironment, workerConfig, envSchemaFacts, authEnabled, viteCommand, bindingNames, dialect, application, bindings, directCloudflareDestination, config, devTriggerToken, queues, durableObjects, sandboxConfig, shouldEmitLive, liveConfig, root, remoteMode } = input;
4590
4679
  const environmentWebsocketRoutes = websocketRoutes.filter((route) => isRouteEnabledForEnvironment(route, routeEnvironment));
@@ -4691,12 +4780,12 @@ function mergeWorkerBindings(resolved, input) {
4691
4780
  class_name: route.className
4692
4781
  }));
4693
4782
  if (missingDurableObjectBindings.length > 0) result.durable_objects = { bindings: [...existingDurableObjectBindings, ...missingDurableObjectBindings] };
4694
- const currentMigrations = resolved.migrations;
4695
- const planned = appendSqliteClassMigration(Array.isArray(currentMigrations) ? currentMigrations : [], environmentWebsocketRoutes.map((route) => route.className), "void-ws-v1", existingDurableObjectBindings.map((binding) => binding.class_name));
4783
+ const resolvedMigrations = resolved.migrations;
4784
+ const planned = appendSqliteClassMigration(Array.isArray(resolvedMigrations) ? resolvedMigrations : [], environmentWebsocketRoutes.map((route) => route.className), "void-ws-v1", existingDurableObjectBindings.map((binding) => binding.class_name));
4696
4785
  if (planned.added) result.migrations = planned.migrations;
4697
4786
  }
4698
4787
  if (durableObjects.length > 0) {
4699
- const existingBindings = (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
4788
+ const existingBindings = currentDurableObjectBindings(result, resolved);
4700
4789
  for (const module of durableObjects) {
4701
4790
  const conflict = existingBindings.find((binding) => binding.name === module.bindingName && binding.class_name !== module.className);
4702
4791
  if (conflict) throw new Error(`durable: binding '${module.bindingName}' is already configured for class '${conflict.class_name}', but durable-objects/ requires '${module.className}'.`);
@@ -4706,8 +4795,7 @@ function mergeWorkerBindings(resolved, input) {
4706
4795
  class_name: module.className
4707
4796
  }));
4708
4797
  if (additions.length > 0) result.durable_objects = { bindings: [...existingBindings, ...additions] };
4709
- const currentMigrations = result.migrations ?? resolved.migrations;
4710
- const existingMigrations = Array.isArray(currentMigrations) ? currentMigrations : [];
4798
+ const existingMigrations = currentMigrations(result, resolved);
4711
4799
  for (const module of durableObjects) if (existingMigrations.find((migration) => migration.tag === module.migrationTag && !migration.new_sqlite_classes?.includes(module.className))) throw new Error(`durable: migration tag '${module.migrationTag}' is already used by another Durable Object migration.`);
4712
4800
  const migrationAdditions = durableObjects.filter((module) => !existingMigrations.some((migration) => migration.tag === module.migrationTag || migration.new_sqlite_classes?.includes(module.className))).map((module) => ({
4713
4801
  tag: module.migrationTag,
@@ -4720,28 +4808,26 @@ function mergeWorkerBindings(resolved, input) {
4720
4808
  ...resolved,
4721
4809
  ...result
4722
4810
  }, sandboxConfig);
4723
- const existingDurableObjectBindings = (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
4811
+ const existingDurableObjectBindings = currentDurableObjectBindings(result, resolved);
4724
4812
  if (!existingDurableObjectBindings.some((binding) => binding.name === sandboxConfig.binding)) result.durable_objects = { bindings: [...existingDurableObjectBindings, {
4725
4813
  name: sandboxConfig.binding,
4726
4814
  class_name: sandboxConfig.className
4727
4815
  }] };
4728
4816
  result.containers = [...(result.containers ?? resolved.containers ?? []).filter((container) => container.class_name !== sandboxConfig.className), sandboxContainerConfig(sandboxConfig)];
4729
4817
  resolved.containers = [];
4730
- const currentMigrations = result.migrations ?? resolved.migrations;
4731
- const existingMigrations = Array.isArray(currentMigrations) ? currentMigrations : [];
4818
+ const existingMigrations = currentMigrations(result, resolved);
4732
4819
  if (!hasSandboxSqliteMigration(existingMigrations, sandboxConfig.className)) result.migrations = [...existingMigrations, {
4733
4820
  tag: SANDBOX_MIGRATION_TAG,
4734
4821
  new_sqlite_classes: [sandboxConfig.className]
4735
4822
  }];
4736
4823
  }
4737
4824
  if (shouldEmitLive() && liveConfig) {
4738
- const existingDurableObjectBindings = (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
4825
+ const existingDurableObjectBindings = currentDurableObjectBindings(result, resolved);
4739
4826
  if (!existingDurableObjectBindings.some((binding) => binding.name === liveConfig.bindingName)) result.durable_objects = { bindings: [...existingDurableObjectBindings, {
4740
4827
  name: liveConfig.bindingName,
4741
4828
  class_name: liveConfig.className
4742
4829
  }] };
4743
- const currentMigrations = result.migrations ?? resolved.migrations;
4744
- const existingMigrations = Array.isArray(currentMigrations) ? currentMigrations : [];
4830
+ const existingMigrations = currentMigrations(result, resolved);
4745
4831
  if (!hasDurableObjectSqliteMigration(existingMigrations, liveConfig.className)) result.migrations = [...existingMigrations, {
4746
4832
  tag: nextDurableObjectMigrationTag(existingMigrations, LIVE_MIGRATION_TAG),
4747
4833
  new_sqlite_classes: [liveConfig.className]
@@ -4801,6 +4887,8 @@ function mergeWorkerBindings(resolved, input) {
4801
4887
  }
4802
4888
  //#endregion
4803
4889
  //#region src/index.ts
4890
+ const scheduledPointerCleanups = /* @__PURE__ */ new Set();
4891
+ const scheduledPreviewPointerCleanups = /* @__PURE__ */ new Set();
4804
4892
  /**
4805
4893
  * Void plugin — the main entry point.
4806
4894
  *
@@ -4818,8 +4906,6 @@ function mergeWorkerBindings(resolved, input) {
4818
4906
  * that are missing. This allows `vite build && wrangler deploy` to work
4819
4907
  * for deploying directly to Cloudflare without the Void platform.
4820
4908
  */
4821
- const scheduledPointerCleanups = /* @__PURE__ */ new Set();
4822
- const scheduledPreviewPointerCleanups = /* @__PURE__ */ new Set();
4823
4909
  function voidPlugin(options) {
4824
4910
  const root = process.cwd();
4825
4911
  const directCloudflareDestination = readDeploymentPlatform(root) === "cloudflare";
@@ -5008,10 +5094,8 @@ function voidPlugin(options) {
5008
5094
  const ignored = ["**/.void/**"];
5009
5095
  if (command === "serve") {
5010
5096
  const local = loadLocalEnv(root, { onWarn: (message) => voidWarn(message) });
5011
- const devEnv = { ...local };
5012
- for (const key of /* @__PURE__ */ new Set([...Object.keys(local), ...envSchemaFacts?.keys ?? []])) if (process.env[key] !== void 0) devEnv[key] = process.env[key];
5013
- const { sanitized: env, stripped } = stripInternalEnvKeys(devEnv);
5014
- if (stripped.length > 0) voidWarn(`Ignoring internal env keys from .env: ${stripped.join(", ")}. These are reserved for Void runtime use.`);
5097
+ const env = { ...local };
5098
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(local), ...envSchemaFacts?.keys ?? []])) if (process.env[key] !== void 0) env[key] = process.env[key];
5015
5099
  workerConfig.vars = {
5016
5100
  ...workerConfig.vars,
5017
5101
  ...env
@@ -5167,6 +5251,7 @@ function voidPlugin(options) {
5167
5251
  createCloudflareBuiltinsExternalPlugin(),
5168
5252
  createWorkerEnvironmentSanitizerPlugin([viteEnvironment.name, ...viteEnvironment.childEnvironments ?? []]),
5169
5253
  createWranglerCompatPersistPlugin(root, config),
5254
+ devTriggerSecretGuardPlugin(),
5170
5255
  restartPlugin(),
5171
5256
  bindingWatchPlugin,
5172
5257
  ...bindings.needsEmail ? [emailDevInboxPlugin({ devTriggerToken })] : [],
@@ -5215,6 +5300,7 @@ function voidPlugin(options) {
5215
5300
  if (hasInboundEmail) throw new Error(`email: inbound email handlers (email/) require target: "cloudflare". They are not available with target: '${config.target}'.`);
5216
5301
  if (config.routing?.revalidate) voidWarn("\"routing.revalidate\" in void.config.ts is ignored with target: \"" + config.target + "\" (edge caching is CF-only)");
5217
5302
  return [
5303
+ devTriggerSecretGuardPlugin(),
5218
5304
  restartPlugin(),
5219
5305
  envPlugin,
5220
5306
  serverRuntimeOptimizerPlugin,
@@ -5291,6 +5377,7 @@ function voidPlugin(options) {
5291
5377
  createDefaultWorkerEnvironmentPlugin(),
5292
5378
  createWorkerEnvironmentSanitizerPlugin([DEFAULT_WORKER_ENVIRONMENT_NAME]),
5293
5379
  createWranglerCompatPersistPlugin(root, config),
5380
+ devTriggerSecretGuardPlugin(),
5294
5381
  restartPlugin(),
5295
5382
  bindingWatchPlugin,
5296
5383
  ...bindings.needsEmail ? [emailDevInboxPlugin({ devTriggerToken })] : [],