void 0.10.13 → 0.20.1

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 (432) hide show
  1. package/AGENTS_TEMPLATE.md +6 -0
  2. package/{skills/void/docs/node_modules/void/node_modules/pglite-server/LICENSE.md → LICENSE} +1 -1
  3. package/README.md +34 -8
  4. package/dist/{auth-qgMlYp7Z.d.mts → auth-DkcFflXV.d.mts} +10 -11
  5. package/dist/auth-W9WII-mN.mjs +630 -0
  6. package/dist/{auth-cmd-BqsdZJp5.mjs → auth-cmd-CAH62yDU.mjs} +4 -3
  7. package/dist/{auth-migrations-BTZ-ATvQ.mjs → auth-migrations-9vif1uj8.mjs} +53 -12
  8. package/dist/{better-auth-shared-BQooDxbw.mjs → better-auth-shared-CYw1T3k4.mjs} +7 -10
  9. package/dist/{better-auth-shared-DealXecJ.d.mts → better-auth-shared-DSCeohOK.d.mts} +10 -5
  10. package/dist/{build-cmd-Bujrv5q-.mjs → build-cmd-CJvZvPQO.mjs} +6 -4
  11. package/dist/{cache-C11V8Fxq.mjs → cache-BlNeQjuP.mjs} +9 -5
  12. package/dist/{cancel-deploy-fwFYF04b.mjs → cancel-deploy-CmlAZ9P6.mjs} +3 -2
  13. package/dist/cf-access-AJ1ehiFR.mjs +42 -0
  14. package/dist/cf-access-DsSsZUPr.mjs +67 -0
  15. package/dist/cli/cli.d.mts +1 -1
  16. package/dist/cli/cli.mjs +1735 -165
  17. package/dist/cli/env-schema-probe.d.mts +48 -61
  18. package/dist/cli/env-schema-probe.mjs +10 -58
  19. package/dist/{client-Gb71-XkG.mjs → client-Clirrol3.mjs} +120 -463
  20. package/dist/cloudflare-auth-B1QtTO1b.mjs +217 -0
  21. package/dist/cloudflare-cmd-B6_OZx2V.mjs +62 -0
  22. package/dist/cloudflare-connect-j5D4hhrG.mjs +56 -0
  23. package/dist/cloudflare-operations-CPTpRW6d.mjs +566 -0
  24. package/dist/{config-qHGgPuWT.mjs → config-BQFq7QvD.mjs} +75 -41
  25. package/dist/{config-CutEMNGJ.mjs → config-NOG_U1aK.mjs} +10 -15
  26. package/dist/connect-C04Wdy_h.mjs +79 -0
  27. package/dist/{create-project-DsYvl3TB.mjs → create-project-ChGZ1DFd.mjs} +26 -16
  28. package/dist/database-provider.d.mts +21 -0
  29. package/dist/database-provider.mjs +6 -0
  30. package/dist/{db-C0i0sYMS.mjs → db-D2d_mUsB.mjs} +612 -117
  31. package/dist/{delete-mh6p-zkQ.mjs → delete-D8GigDk8.mjs} +9 -6
  32. package/dist/{deploy-jBJT1fUT.mjs → deploy-iXZ3F0N6.mjs} +3252 -1995
  33. package/dist/dev-inbox-DkgRWLkW.mjs +307 -0
  34. package/dist/{discover-CJHyvYfR.mjs → discover-xvfrgJeo.mjs} +3 -3
  35. package/dist/{magic-string.es-ZQjdJFFn.mjs → dist-BR1quN_w.mjs} +570 -216
  36. package/dist/{dist-DaKKDf8D.mjs → dist-BrsS7cai.mjs} +17 -2
  37. package/dist/{dist-BuiRJkTd.mjs → dist-m40_XgNh.mjs} +48 -33
  38. package/dist/{domain-DiaNQbrl.mjs → domain-B1VmoSr0.mjs} +41 -4
  39. package/dist/dotenv-VQxupEUv.mjs +181 -0
  40. package/dist/edge.d.mts +2 -0
  41. package/dist/edge.mjs +2 -0
  42. package/dist/email-Ce6SQq-i.mjs +182 -0
  43. package/dist/email-uKyQYUVY.mjs +1016 -0
  44. package/dist/{entry-D7yy4xVH.mjs → entry-DU3oDoQ3.mjs} +2 -2
  45. package/dist/env-BcQzYgoG.mjs +73 -0
  46. package/dist/env-D4Emu-M_.mjs +95 -0
  47. package/dist/env-helpers-CyKtOBpj.d.mts +22 -0
  48. package/dist/env-public-D_6u46fX.d.mts +135 -0
  49. package/dist/{env-types-QBj-ndax.mjs → env-types-BNPhro-M.mjs} +2 -2
  50. package/dist/{env-validation-Dea3v3ej.mjs → env-validation-ENpMy6Ez.mjs} +68 -122
  51. package/dist/{gen-BzXf3Jh2.mjs → gen-DI2YwdBM.mjs} +70 -15
  52. package/dist/generate-RTK8_kK1.mjs +47 -0
  53. package/dist/{github-cmd-DKcGUNsj.mjs → github-cmd-xItS5Zwf.mjs} +39 -31
  54. package/dist/{handler-Cjh8uM3Y.d.mts → handler-D1hLsObx.d.mts} +124 -122
  55. package/dist/{head-nmvOgFjd.d.mts → head-Do8P4puT.d.mts} +9 -8
  56. package/dist/{headers-ChAADPQu.mjs → headers-D8QfRX9Y.mjs} +12 -10
  57. package/dist/inbound-2d0zi2yS.mjs +706 -0
  58. package/dist/inbound-afAcWeQ9.d.mts +363 -0
  59. package/dist/index.d.mts +61 -34
  60. package/dist/index.mjs +1012 -293
  61. package/dist/{init-7JCAcKNQ.mjs → init-BD-9THgn.mjs} +329 -271
  62. package/dist/link-RMdgjF1v.mjs +52 -0
  63. package/dist/{list-CPwFDZ_c.mjs → list-3F52R_yO.mjs} +37 -5
  64. package/dist/{runner-kapo9aPs.mjs → local-d1-D2I6Ox5F.mjs} +116 -6
  65. package/dist/{login-BT3H8PN3.mjs → login-pV69H-ZO.mjs} +17 -15
  66. package/dist/{logs-Bt313ax7.mjs → logs-DFHHD6wE.mjs} +16 -2
  67. package/dist/mime-BJD7d_qL.mjs +1216 -0
  68. package/dist/neon-DHwd2zvC.mjs +54 -0
  69. package/dist/{node-pRg81HqV.mjs → node-Dk3H2jmU.mjs} +29 -8
  70. package/dist/operator-cmd-DYWRbWUA.mjs +348 -0
  71. package/dist/{agents-CtgBYqld.mjs → output-tFQLLj26.mjs} +676 -211
  72. package/dist/{package-json-Cx1osYo6.mjs → package-json-CPoWX79C.mjs} +1 -1
  73. package/dist/pages/client.d.mts +43 -41
  74. package/dist/pages/client.mjs +40 -33
  75. package/dist/pages/head-client.d.mts +10 -12
  76. package/dist/pages/head.d.mts +1 -1
  77. package/dist/pages/index.d.mts +37 -14
  78. package/dist/pages/index.mjs +5 -5
  79. package/dist/pages/islands-plugin.d.mts +25 -27
  80. package/dist/pages/islands-plugin.mjs +6 -4
  81. package/dist/pages/prefetch.d.mts +6 -7
  82. package/dist/pages/protocol.d.mts +2 -2
  83. package/dist/pages/protocol.mjs +2 -1
  84. package/dist/pages/serialize.d.mts +7 -8
  85. package/dist/platform-cmd-DxJ2FRwR.mjs +123 -0
  86. package/dist/platform-domain-ChvbJkdy.mjs +228 -0
  87. package/dist/platform-lifecycle-DN4MzJF_.mjs +4450 -0
  88. package/dist/platform-management-Db2PXw0B.mjs +698 -0
  89. package/dist/platform-recovery-C_YO-tIs.mjs +99 -0
  90. package/dist/{plugin-inference-DMeavIJ6.mjs → plugin-inference-BDRfZngg.mjs} +35 -19
  91. package/dist/prepare-BfJvFUtJ.mjs +14 -0
  92. package/dist/{prepare-BvvgAz-3.mjs → prepare-CBetXvsN.mjs} +15 -24
  93. package/dist/{preset-BjyR3lzz.mjs → preset-lAy0B0BQ.mjs} +25 -212
  94. package/dist/{project-cmd-D_w-4w5B.mjs → project-cmd-Mo0V9yKS.mjs} +25 -12
  95. package/dist/{project-paths-BQd7OmIo.mjs → project-paths-SK8nMHPp.mjs} +3 -1
  96. package/dist/{project-tsconfig-B-QtXjLQ.mjs → project-tsconfig-Ql2XsSQp.mjs} +2 -2
  97. package/dist/{protocol-Bnb0LFp3.d.mts → protocol-C-pqYJjE.d.mts} +3 -4
  98. package/dist/{provision-CPx2ZxsH.mjs → provision-Blnstcm2.mjs} +78 -45
  99. package/dist/r2-conditions-D1Wk8i7b.mjs +30 -0
  100. package/dist/{requests-B8sZxaFM.mjs → requests-BcKOVpRg.mjs} +5 -3
  101. package/dist/{resolve-project-BBMtLLV9.mjs → resolve-project--Vxawf7z.mjs} +2 -2
  102. package/dist/rollback-Bx85-0xh.mjs +166 -0
  103. package/dist/{rolldown-runtime-DJK8HYOj.mjs → rolldown-runtime-rQ84J-ij.mjs} +1 -1
  104. package/dist/{route-types-z1jtHEi_.mjs → route-types-Da-DpyUp.mjs} +77 -25
  105. package/dist/routes-stub.d.mts +22 -23
  106. package/dist/runner-mysql-7BPUNGmL.mjs +61 -0
  107. package/dist/{runner-pg-CHM76xuC.mjs → runner-pg-BkEza-dX.mjs} +18 -6
  108. package/dist/runtime/ai.d.mts +21 -14
  109. package/dist/runtime/ai.mjs +5 -4
  110. package/dist/runtime/auth-client-react.d.mts +3 -5
  111. package/dist/runtime/auth-client-solid.d.mts +3 -5
  112. package/dist/runtime/auth-client-svelte.d.mts +3 -5
  113. package/dist/runtime/auth-client-vue.d.mts +3 -5
  114. package/dist/runtime/auth-client.d.mts +3 -5
  115. package/dist/runtime/auth.d.mts +1 -1
  116. package/dist/runtime/better-auth-mysql.d.mts +10 -0
  117. package/dist/runtime/better-auth-mysql.mjs +49 -0
  118. package/dist/runtime/better-auth-pg.d.mts +8 -9
  119. package/dist/runtime/better-auth-pg.mjs +2 -2
  120. package/dist/runtime/better-auth.d.mts +8 -9
  121. package/dist/runtime/better-auth.mjs +2 -2
  122. package/dist/runtime/client-react.d.mts +1 -1
  123. package/dist/runtime/client-solid.d.mts +1 -1
  124. package/dist/runtime/client-svelte.d.mts +1 -1
  125. package/dist/runtime/client-vue.d.mts +1 -1
  126. package/dist/runtime/client.d.mts +1 -1
  127. package/dist/runtime/db-mysql.d.mts +2 -0
  128. package/dist/runtime/db-mysql.mjs +1 -0
  129. package/dist/runtime/db.d.mts +10 -11
  130. package/dist/runtime/durable.d.mts +47 -0
  131. package/dist/runtime/durable.mjs +146 -0
  132. package/dist/runtime/email/testing.d.mts +112 -0
  133. package/dist/runtime/email/testing.mjs +283 -0
  134. package/dist/runtime/email.d.mts +30 -0
  135. package/dist/runtime/email.mjs +573 -0
  136. package/dist/runtime/env-helpers.d.mts +2 -2
  137. package/dist/runtime/env-helpers.mjs +5 -42
  138. package/dist/runtime/env-public-client.d.mts +10 -11
  139. package/dist/runtime/env-public-client.mjs +1 -1
  140. package/dist/runtime/env-public.d.mts +2 -2
  141. package/dist/runtime/env-public.mjs +104 -49
  142. package/dist/runtime/env.d.mts +19 -18
  143. package/dist/runtime/env.mjs +15 -2
  144. package/dist/runtime/fetch-stream.d.mts +20 -21
  145. package/dist/runtime/fetch.d.mts +15 -16
  146. package/dist/runtime/handler.d.mts +1 -1
  147. package/dist/runtime/isr.d.mts +21 -22
  148. package/dist/runtime/isr.mjs +26 -8
  149. package/dist/runtime/kv.d.mts +9 -10
  150. package/dist/runtime/live-client.d.mts +5 -7
  151. package/dist/runtime/live-client.mjs +9 -7
  152. package/dist/runtime/live-server.d.mts +4 -5
  153. package/dist/runtime/live.d.mts +22 -24
  154. package/dist/runtime/live.mjs +1 -1
  155. package/dist/runtime/log.d.mts +16 -17
  156. package/dist/runtime/migration-handler-mysql.d.mts +4 -0
  157. package/dist/runtime/migration-handler-mysql.mjs +81 -0
  158. package/dist/runtime/migration-handler-pg.d.mts +2 -4
  159. package/dist/runtime/migration-handler.d.mts +5 -6
  160. package/dist/runtime/migration-handler.mjs +4 -3
  161. package/dist/runtime/queues.d.mts +3 -4
  162. package/dist/runtime/queues.mjs +2 -1
  163. package/dist/runtime/remote/binding-handler.d.mts +10 -12
  164. package/dist/runtime/remote/binding-handler.mjs +24 -3
  165. package/dist/runtime/remote/index.d.mts +5 -6
  166. package/dist/runtime/remote/index.mjs +21 -18
  167. package/dist/runtime/response.d.mts +10 -11
  168. package/dist/runtime/sandbox.d.mts +56 -55
  169. package/dist/runtime/sandbox.mjs +57 -49
  170. package/dist/runtime/schema-mysql.d.mts +1 -0
  171. package/dist/runtime/schema-mysql.mjs +2 -0
  172. package/dist/runtime/seed.d.mts +14 -9
  173. package/dist/runtime/sse-client.d.mts +6 -7
  174. package/dist/runtime/sse.d.mts +11 -12
  175. package/dist/runtime/storage.d.mts +3 -4
  176. package/dist/runtime/validator.d.mts +1 -1
  177. package/dist/runtime/ws-server.d.mts +12 -12
  178. package/dist/runtime/ws-server.mjs +32 -4
  179. package/dist/runtime/ws.d.mts +19 -21
  180. package/dist/{scan-ChWt4pX1.mjs → scan-BMH4rzlv.mjs} +42 -19
  181. package/dist/{scan-NU4xKGci.mjs → scan-CpK-57ug.mjs} +5 -4
  182. package/dist/{secret-Dt32J6RI.mjs → secret-ByhJ9AMl.mjs} +62 -5
  183. package/dist/{skills-CLjN0uUO.mjs → skills-Q46GZMO-.mjs} +6 -4
  184. package/dist/sqlite-validation-BzKMWnO4.mjs +25 -0
  185. package/dist/{standard-schema-DJ0HW7QP.d.mts → standard-schema-Fo_vCAZh.d.mts} +6 -6
  186. package/dist/{subcommand-prompt-BzV8iQZo.mjs → subcommand-prompt-WfySCQ7S.mjs} +67 -48
  187. package/dist/sveltekit.d.mts +12 -11
  188. package/dist/sveltekit.mjs +1 -1
  189. package/dist/types-BAp5AEBU.d.mts +79 -0
  190. package/dist/types-CKWnYgfy.d.mts +1 -0
  191. package/dist/{validate-Cw_RLeTj.mjs → validate-tBBN_dXH.mjs} +4 -3
  192. package/dist/wrangler--imS8n0d.mjs +1796 -0
  193. package/dist/{yarn-pnp-DJn3SAHF.mjs → yarn-pnp-DxSInkzL.mjs} +1 -1
  194. package/package.json +79 -65
  195. package/schema.json +22 -3
  196. package/skills/void/SKILL.md +59 -2
  197. package/skills/void/docs/guide/ai.md +32 -14
  198. package/skills/void/docs/guide/app-types.md +6 -6
  199. package/skills/void/docs/guide/auth.md +14 -16
  200. package/skills/void/docs/guide/database/d1.md +6 -0
  201. package/skills/void/docs/guide/database/mysql.md +60 -0
  202. package/skills/void/docs/guide/database/postgresql.md +14 -9
  203. package/skills/void/docs/guide/database.md +39 -26
  204. package/skills/void/docs/guide/deployment.md +99 -25
  205. package/skills/void/docs/guide/durable-state.md +140 -0
  206. package/skills/void/docs/guide/edge/headers.md +5 -5
  207. package/skills/void/docs/guide/edge/prerendering.md +2 -0
  208. package/skills/void/docs/guide/edge/revalidation.md +21 -6
  209. package/skills/void/docs/guide/edge/rewrites.md +18 -14
  210. package/skills/void/docs/guide/edge/static-assets.md +12 -10
  211. package/skills/void/docs/guide/email.md +640 -0
  212. package/skills/void/docs/guide/env-migration.md +109 -0
  213. package/skills/void/docs/guide/env-vars.md +70 -248
  214. package/skills/void/docs/guide/index.md +15 -17
  215. package/skills/void/docs/guide/jobs.md +8 -5
  216. package/skills/void/docs/guide/live.md +7 -15
  217. package/skills/void/docs/guide/pages-routing/actions-and-forms.md +8 -4
  218. package/skills/void/docs/guide/pages-routing/islands.md +3 -3
  219. package/skills/void/docs/guide/pages-routing/loaders.md +6 -4
  220. package/skills/void/docs/guide/pages-routing/overview.md +7 -7
  221. package/skills/void/docs/guide/platform-administration.md +212 -0
  222. package/skills/void/docs/guide/platform-development.md +264 -0
  223. package/skills/void/docs/guide/queues.md +10 -10
  224. package/skills/void/docs/guide/quickstart.md +46 -67
  225. package/skills/void/docs/guide/remote-dev.md +8 -6
  226. package/skills/void/docs/guide/sandboxes.md +33 -16
  227. package/skills/void/docs/guide/self-hosted-platform.md +553 -0
  228. package/skills/void/docs/guide/server-routing.md +5 -5
  229. package/skills/void/docs/guide/sse.md +4 -4
  230. package/skills/void/docs/guide/ssg.md +5 -3
  231. package/skills/void/docs/guide/storage.md +2 -2
  232. package/skills/void/docs/guide/websockets.md +15 -7
  233. package/skills/void/docs/index.md +3 -3
  234. package/skills/void/docs/integrations/agents.md +6 -64
  235. package/skills/void/docs/integrations/cloudflare.md +165 -146
  236. package/skills/void/docs/integrations/frameworks/analog.md +4 -4
  237. package/skills/void/docs/integrations/frameworks/astro.md +5 -5
  238. package/skills/void/docs/integrations/frameworks/nuxt.md +5 -5
  239. package/skills/void/docs/integrations/frameworks/overview.md +3 -3
  240. package/skills/void/docs/integrations/frameworks/react-router.md +3 -3
  241. package/skills/void/docs/integrations/frameworks/sveltekit.md +3 -3
  242. package/skills/void/docs/integrations/frameworks/tanstack-start.md +2 -2
  243. package/skills/void/docs/integrations/nodejs-bun-deno.md +11 -4
  244. package/skills/void/docs/reference/api.md +42 -6
  245. package/skills/void/docs/reference/cli.md +665 -160
  246. package/skills/void/docs/reference/config.md +67 -27
  247. package/skills/void/docs/reference/resource-inference.md +13 -9
  248. package/skills/void/docs/reference/structure.md +9 -11
  249. package/AGENT_PROMPT.md +0 -19
  250. package/dist/cf-access-Bqw81xAf.mjs +0 -22
  251. package/dist/env-CZy5MorI.mjs +0 -299
  252. package/dist/env-helpers-z4stu8uc.d.mts +0 -52
  253. package/dist/env-mask-Dd47NbR6.mjs +0 -90
  254. package/dist/env-public-BfiLcMBk.d.mts +0 -140
  255. package/dist/link-CdGHSIy-.mjs +0 -45
  256. package/dist/mcp-DoM3_nhd.mjs +0 -377
  257. package/dist/project-paths-GpziKeQQ.d.mts +0 -25
  258. package/dist/providers-BNKRacMr.d.mts +0 -7
  259. package/dist/proxy-D-3_D-Gl.mjs +0 -5
  260. package/dist/rollback-CkvTFXx5.mjs +0 -90
  261. package/dist/runtime/isr-cache.d.mts +0 -207
  262. package/dist/runtime/isr-cache.mjs +0 -523
  263. package/dist/types-lLjNE9Qp.d.mts +0 -51
  264. package/getting-started-prompt.txt +0 -28
  265. package/skills/void/command/void.md +0 -7
  266. package/skills/void/docs/integrations/auth-providers.md +0 -0
  267. package/skills/void/docs/integrations/payment-processors.md +0 -0
  268. package/skills/void/docs/node_modules/@iconify/vue/README.md +0 -408
  269. package/skills/void/docs/node_modules/@iconify/vue/offline/readme.md +0 -5
  270. package/skills/void/docs/node_modules/@voidzero-dev/vitepress-theme/README.md +0 -103
  271. package/skills/void/docs/node_modules/oxc-minify/README.md +0 -78
  272. package/skills/void/docs/node_modules/reka-ui/README.md +0 -80
  273. package/skills/void/docs/node_modules/vitepress/README.md +0 -28
  274. package/skills/void/docs/node_modules/vitepress/template/api-examples.md +0 -49
  275. package/skills/void/docs/node_modules/vitepress/template/index.md +0 -28
  276. package/skills/void/docs/node_modules/vitepress/template/markdown-examples.md +0 -85
  277. package/skills/void/docs/node_modules/vitepress-plugin-group-icons/README.md +0 -101
  278. package/skills/void/docs/node_modules/void/AGENT_PROMPT.md +0 -19
  279. package/skills/void/docs/node_modules/void/CLAUDE.md +0 -221
  280. package/skills/void/docs/node_modules/void/README.md +0 -90
  281. package/skills/void/docs/node_modules/void/node_modules/@clack/prompts/CHANGELOG.md +0 -685
  282. package/skills/void/docs/node_modules/void/node_modules/@clack/prompts/README.md +0 -396
  283. package/skills/void/docs/node_modules/void/node_modules/@cloudflare/sandbox/README.md +0 -219
  284. package/skills/void/docs/node_modules/void/node_modules/@cloudflare/vite-plugin/README.md +0 -37
  285. package/skills/void/docs/node_modules/void/node_modules/@cloudflare/workers-types/README.md +0 -135
  286. package/skills/void/docs/node_modules/void/node_modules/@electric-sql/pglite/README.md +0 -189
  287. package/skills/void/docs/node_modules/void/node_modules/@hono/oauth-providers/CHANGELOG.md +0 -143
  288. package/skills/void/docs/node_modules/void/node_modules/@hono/oauth-providers/README.md +0 -1272
  289. package/skills/void/docs/node_modules/void/node_modules/@napi-rs/keyring/README.md +0 -19
  290. package/skills/void/docs/node_modules/void/node_modules/@types/better-sqlite3/README.md +0 -15
  291. package/skills/void/docs/node_modules/void/node_modules/@types/node/README.md +0 -15
  292. package/skills/void/docs/node_modules/void/node_modules/@types/pg/README.md +0 -15
  293. package/skills/void/docs/node_modules/void/node_modules/@types/proper-lockfile/README.md +0 -51
  294. package/skills/void/docs/node_modules/void/node_modules/@typescript/native-preview/README.md +0 -22
  295. package/skills/void/docs/node_modules/void/node_modules/@typescript/native-preview/vendor/vscode-jsonrpc/README.md +0 -69
  296. package/skills/void/docs/node_modules/void/node_modules/@void/md/README.md +0 -153
  297. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/@shikijs/engine-javascript/README.md +0 -9
  298. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/@shikijs/transformers/README.md +0 -9
  299. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/@types/node/README.md +0 -15
  300. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/gray-matter/CHANGELOG.md +0 -24
  301. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/gray-matter/README.md +0 -565
  302. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-exit/README.md +0 -127
  303. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-anchor/README.md +0 -600
  304. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-attrs/README.md +0 -386
  305. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-container/README.md +0 -95
  306. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-emoji/README.md +0 -101
  307. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-footnote/README.md +0 -135
  308. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/pathslash/README.md +0 -64
  309. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/shiki/README.md +0 -15
  310. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/tinyglobby/README.md +0 -25
  311. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/AGENTS.md +0 -16
  312. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/README.md +0 -220
  313. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/build.md +0 -21
  314. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/check.md +0 -35
  315. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/create.md +0 -70
  316. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/fmt.md +0 -20
  317. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/index.md +0 -35
  318. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/lint.md +0 -26
  319. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/pack.md +0 -17
  320. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/run.md +0 -364
  321. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/staged.md +0 -15
  322. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/test.md +0 -18
  323. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/automatic-data-tracking.md +0 -145
  324. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/build.md +0 -40
  325. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/cache.md +0 -107
  326. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/check.md +0 -60
  327. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/ci.md +0 -62
  328. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/commit-hooks.md +0 -60
  329. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/create.md +0 -341
  330. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/dev.md +0 -24
  331. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/docker.md +0 -175
  332. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/env.md +0 -167
  333. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/fmt.md +0 -41
  334. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/github-actions-cache.md +0 -165
  335. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/ide-integration.md +0 -101
  336. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/implode.md +0 -23
  337. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/index.md +0 -134
  338. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/install.md +0 -199
  339. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/lint.md +0 -50
  340. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/migrate-rules.md +0 -347
  341. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/migrate.md +0 -197
  342. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/monorepo.md +0 -176
  343. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/pack.md +0 -69
  344. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/run.md +0 -356
  345. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/test.md +0 -35
  346. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/troubleshooting.md +0 -108
  347. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/upgrade.md +0 -101
  348. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/vpx.md +0 -66
  349. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/why.md +0 -39
  350. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/index.md +0 -12
  351. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/team.md +0 -35
  352. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/templates/generator/README.md +0 -35
  353. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/templates/monorepo/README.md +0 -29
  354. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vue/README.md +0 -58
  355. package/skills/void/docs/node_modules/void/node_modules/arktype/README.md +0 -165
  356. package/skills/void/docs/node_modules/void/node_modules/better-auth/LICENSE.md +0 -20
  357. package/skills/void/docs/node_modules/void/node_modules/better-auth/README.md +0 -32
  358. package/skills/void/docs/node_modules/void/node_modules/better-sqlite3/README.md +0 -99
  359. package/skills/void/docs/node_modules/void/node_modules/blake3-jit/README.md +0 -108
  360. package/skills/void/docs/node_modules/void/node_modules/drizzle-arktype/README.md +0 -51
  361. package/skills/void/docs/node_modules/void/node_modules/drizzle-kit/README.md +0 -79
  362. package/skills/void/docs/node_modules/void/node_modules/drizzle-orm/README.md +0 -44
  363. package/skills/void/docs/node_modules/void/node_modules/drizzle-valibot/README.md +0 -51
  364. package/skills/void/docs/node_modules/void/node_modules/drizzle-zod/README.md +0 -65
  365. package/skills/void/docs/node_modules/void/node_modules/es-module-lexer/README.md +0 -403
  366. package/skills/void/docs/node_modules/void/node_modules/estree-walker/README.md +0 -48
  367. package/skills/void/docs/node_modules/void/node_modules/hono/README.md +0 -85
  368. package/skills/void/docs/node_modules/void/node_modules/ignore/README.md +0 -452
  369. package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/CHANGELOG.md +0 -76
  370. package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/LICENSE.md +0 -21
  371. package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/README.md +0 -364
  372. package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/SECURITY.md +0 -41
  373. package/skills/void/docs/node_modules/void/node_modules/magic-string/README.md +0 -325
  374. package/skills/void/docs/node_modules/void/node_modules/ofetch/README.md +0 -398
  375. package/skills/void/docs/node_modules/void/node_modules/pathslash/README.md +0 -64
  376. package/skills/void/docs/node_modules/void/node_modules/pg/README.md +0 -96
  377. package/skills/void/docs/node_modules/void/node_modules/pglite-server/README.md +0 -135
  378. package/skills/void/docs/node_modules/void/node_modules/picocolors/README.md +0 -21
  379. package/skills/void/docs/node_modules/void/node_modules/proper-lockfile/CHANGELOG.md +0 -108
  380. package/skills/void/docs/node_modules/void/node_modules/proper-lockfile/README.md +0 -183
  381. package/skills/void/docs/node_modules/void/node_modules/tinyglobby/README.md +0 -25
  382. package/skills/void/docs/node_modules/void/node_modules/valibot/LICENSE.md +0 -9
  383. package/skills/void/docs/node_modules/void/node_modules/valibot/README.md +0 -94
  384. package/skills/void/docs/node_modules/void/node_modules/vite-plus/AGENTS.md +0 -16
  385. package/skills/void/docs/node_modules/void/node_modules/vite-plus/README.md +0 -220
  386. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/build.md +0 -21
  387. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/check.md +0 -35
  388. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/create.md +0 -70
  389. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/fmt.md +0 -20
  390. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/index.md +0 -35
  391. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/lint.md +0 -26
  392. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/pack.md +0 -17
  393. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/run.md +0 -364
  394. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/staged.md +0 -15
  395. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/test.md +0 -18
  396. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/automatic-data-tracking.md +0 -145
  397. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/build.md +0 -40
  398. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/cache.md +0 -107
  399. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/check.md +0 -60
  400. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/ci.md +0 -62
  401. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/commit-hooks.md +0 -60
  402. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/create.md +0 -341
  403. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/dev.md +0 -24
  404. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/docker.md +0 -175
  405. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/env.md +0 -167
  406. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/fmt.md +0 -41
  407. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/github-actions-cache.md +0 -165
  408. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/ide-integration.md +0 -101
  409. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/implode.md +0 -23
  410. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/index.md +0 -134
  411. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/install.md +0 -199
  412. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/lint.md +0 -50
  413. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/migrate-rules.md +0 -347
  414. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/migrate.md +0 -197
  415. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/monorepo.md +0 -176
  416. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/pack.md +0 -69
  417. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/run.md +0 -356
  418. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/test.md +0 -35
  419. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/troubleshooting.md +0 -108
  420. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/upgrade.md +0 -101
  421. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/vpx.md +0 -66
  422. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/why.md +0 -39
  423. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/index.md +0 -12
  424. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/team.md +0 -35
  425. package/skills/void/docs/node_modules/void/node_modules/vite-plus/templates/generator/README.md +0 -35
  426. package/skills/void/docs/node_modules/void/node_modules/vite-plus/templates/monorepo/README.md +0 -29
  427. package/skills/void/docs/node_modules/void/node_modules/wrangler/README.md +0 -63
  428. package/skills/void/docs/node_modules/void/node_modules/zod/README.md +0 -191
  429. package/skills/void/docs/node_modules/void/skills/migrate-vite-cloudflare-to-void/SKILL.md +0 -175
  430. package/skills/void/docs/node_modules/void/skills/void/SKILL.md +0 -76
  431. package/skills/void/docs/node_modules/void/skills/void/command/void.md +0 -7
  432. package/skills/void/docs/node_modules/void/test/e2e/README.md +0 -85
package/dist/index.mjs CHANGED
@@ -1,32 +1,38 @@
1
- import { r as __toESM } from "./rolldown-runtime-DJK8HYOj.mjs";
2
- import { a as join, n as dirname, s as relative } from "./dist-DaKKDf8D.mjs";
3
- import { M as require_picocolors } from "./agents-CtgBYqld.mjs";
4
- import { A as resolveAssetRoutingPolicy, C as syncWranglerBindings, D as resolveSandboxConfig, E as isSandboxEnabled, O as hasApiRoutes, S as resolveLiveBindingConfig, T as SANDBOX_MIGRATION_TAG, _ as nextDurableObjectMigrationTag, a as prerenderPagesNode, c as filterLoadedEnv, d as LIVE_MIGRATION_TAG, g as hasDurableObjectSqliteMigration, h as ensureWranglerNodejsAlsFlag, i as prerenderPages, k as hasDocumentRoutes, l as resolveRemoteMode, m as ensureWranglerCompatibilityDate, n as resolveDistDir, o as collectRevalidate, p as ensureWranglerCompat, r as resolveWorkerDirName, s as hasRevalidate, u as stripInternalEnvKeys, v as normalizeWranglerCompatibilityFlags, x as readWranglerCompat, y as readExistingLiveBindingConfig } from "./deploy-jBJT1fUT.mjs";
5
- import { i as resolveProjectPaths, n as readProjectPaths, t as assertNoRootSourceConventionConflicts } from "./project-paths-BQd7OmIo.mjs";
6
- import { a as scanQueuesSync, c as loadVoidAuthConfig, i as scanWebSocketRoutesSync, n as scanRoutePatternsSync, o as scanJobsSync, r as scanRoutes, s as findVoidAuthConfig, t as scanMiddlewareSync } from "./scan-ChWt4pX1.mjs";
7
- import { b as readProjectConfig, f as getToken, m as isStagingMode } from "./client-Gb71-XkG.mjs";
8
- import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-B-QtXjLQ.mjs";
9
- import { c as getDatabaseDialect, f as readConfig, l as isNodeTarget, p as resolveBindingNames } from "./config-qHGgPuWT.mjs";
10
- import { a as inferBindingsFromSource, i as inferProjectBindings, n as FRAMEWORK_SCAN_DIRS, o as walk, r as detectFramework, t as DEFAULT_SCAN_DIRS } from "./plugin-inference-DMeavIJ6.mjs";
11
- import { a as validateSsrEntries, c as isIdentifier, l as isLiteral, n as SSR_SERVER_ENTRY_RELATIVE_PATHS, o as validateSsrEntry, t as SSR_CLIENT_ENTRY_RELATIVE_PATHS, u as isMemberExpression } from "./entry-D7yy4xVH.mjs";
1
+ import { r as __toESM } from "./rolldown-runtime-rQ84J-ij.mjs";
2
+ import { c as relative, l as resolve, n as dirname, o as join } from "./dist-BrsS7cai.mjs";
3
+ import { st as require_picocolors } from "./output-tFQLLj26.mjs";
4
+ import { D as resolveAssetRoutingPolicy, E as hasDocumentRoutes, T as hasApiRoutes, _ as scanDurableObjectModules, c as resolveDistDir, d as generateQueueResolverSource, f as prerenderPages, g as hasRevalidate, h as collectRevalidate, l as resolveWorkerDirName, m as collectPages, p as prerenderPagesNode, v as SEND_EMAIL_BINDING } from "./deploy-iXZ3F0N6.mjs";
5
+ import { i as resolveProjectPaths, n as readProjectPaths, t as assertNoRootSourceConventionConflicts } from "./project-paths-SK8nMHPp.mjs";
6
+ import { a as scanQueuesSync, c as loadVoidAuthConfig, i as scanWebSocketRoutesSync, n as scanRoutePatternsSync, o as scanJobsSync, r as scanRoutes, s as findVoidAuthConfig, t as scanMiddlewareSync } from "./scan-BMH4rzlv.mjs";
7
+ import { a as getApiUrl, b as readDeploymentPlatform, c as getToken, o as getProxyUrl, u as isStagingMode, x as readProjectConfig } from "./auth-W9WII-mN.mjs";
8
+ import { C as readWranglerWebSocketTombstones, D as syncWranglerWebSockets, E as syncWranglerDurableObjects, M as isSandboxEnabled, P as resolveSandboxConfig, T as syncWranglerBindings, a as LIVE_MIGRATION_TAG, d as ensureWranglerNodejsAlsFlag, g as readExistingLiveBindingConfig, h as normalizeWranglerCompatibilityFlags, j as SANDBOX_MIGRATION_TAG, l as ensureWranglerCompat, m as nextDurableObjectMigrationTag, p as hasDurableObjectSqliteMigration, u as ensureWranglerCompatibilityDate, w as resolveLiveBindingConfig, y as readWranglerCompat } from "./wrangler--imS8n0d.mjs";
9
+ import { c as getDatabaseDialect, f as readConfig, l as isNodeTarget, p as resolveBindingNames } from "./config-BQFq7QvD.mjs";
12
10
  import { i as voidWarn, n as voidError, r as voidLog, t as setShowTimestamp } from "./log-ChfPKsVd.mjs";
13
- import { l as parseDotenvFile } from "./preset-BjyR3lzz.mjs";
14
- import { i as generateVoidAuthDrizzleSchema, n as generateQueueTypes, o as DRIZZLE_OPERATORS, r as generateVoidAuthDatabaseModule, s as generateDbDts, t as generateRouteTypes } from "./route-types-z1jtHEi_.mjs";
15
- import { t as discoverSchema } from "./discover-CJHyvYfR.mjs";
16
- import { t as MagicString } from "./magic-string.es-ZQjdJFFn.mjs";
17
- import { n as findEnvFileForPaths, r as generateEnvTypes, t as findEnvFile } from "./env-types-QBj-ndax.mjs";
18
- import { h as loadUserEnvFile } from "./env-validation-Dea3v3ej.mjs";
19
- import { i as resolveLayoutIds, o as isRouteEnabledForEnvironment } from "./scan-NU4xKGci.mjs";
20
- import { n as STAGING_PROXY_URL, t as DEFAULT_PROXY_URL } from "./proxy-D-3_D-Gl.mjs";
21
- import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-ChAADPQu.mjs";
11
+ import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-D8QfRX9Y.mjs";
12
+ import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-Ql2XsSQp.mjs";
13
+ import { a as inferBindingsFromSource, i as inferProjectBindings, n as FRAMEWORK_SCAN_DIRS, o as walk, r as detectFramework, t as DEFAULT_SCAN_DIRS } from "./plugin-inference-BDRfZngg.mjs";
14
+ import { a as validateSsrEntries, c as isIdentifier, l as isLiteral, n as SSR_SERVER_ENTRY_RELATIVE_PATHS, o as validateSsrEntry, t as SSR_CLIENT_ENTRY_RELATIVE_PATHS, u as isMemberExpression } from "./entry-DU3oDoQ3.mjs";
15
+ import { n as writeLocalD1PersistState, t as resolveLocalD1Target } from "./local-d1-D2I6Ox5F.mjs";
16
+ import { n as parseDotenvFile } from "./dotenv-VQxupEUv.mjs";
17
+ import { i as generateVoidAuthDrizzleSchema, n as generateQueueTypes, o as DRIZZLE_OPERATORS, r as generateVoidAuthDatabaseModule, s as generateDbDts, t as generateRouteTypes } from "./route-types-Da-DpyUp.mjs";
18
+ import { t as discoverSchema } from "./discover-xvfrgJeo.mjs";
19
+ 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-DkgRWLkW.mjs";
20
+ import { t as MagicString } from "./dist-BR1quN_w.mjs";
21
+ import { n as findEnvFileForPaths, r as generateEnvTypes, t as findEnvFile } from "./env-types-BNPhro-M.mjs";
22
+ import { h as loadUserEnvFile } from "./env-validation-ENpMy6Ez.mjs";
23
+ import { a as loadLocalEnv, o as resolveRemoteMode, r as findLegacyEnvFiles, s as stripInternalEnvKeys, t as CLIENT_ENV_PREFIX$2 } from "./env-D4Emu-M_.mjs";
24
+ import { i as resolveLayoutIds, o as isRouteEnabledForEnvironment } from "./scan-CpK-57ug.mjs";
25
+ import { a as scanEmailHandlersSync, r as hasEmailHandlers } from "./email-Ce6SQq-i.mjs";
22
26
  import { n as INTERNAL_BINDING_PREFIXES, t as INTERNAL_BINDING_EXACT_NAMES } from "./env-raw-Cx8ElDdj.mjs";
23
27
  import { n as defer } from "./defer-YsYDUoii.mjs";
24
28
  import { VoidAssetRewriteError, basicAuth, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled } from "./runtime/handler.mjs";
25
- import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, writeFileSync, writeSync } from "node:fs";
29
+ import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync, writeSync } from "node:fs";
26
30
  import { fileURLToPath, pathToFileURL } from "node:url";
27
31
  import { cloudflare } from "@cloudflare/vite-plugin";
28
- import { loadEnv, normalizePath, parseSync } from "vite";
32
+ import { appendSqliteClassMigration } from "@void/deploy-core";
33
+ import { timingSafeEqual } from "node:crypto";
29
34
  import { homedir } from "node:os";
35
+ import { normalizePath, parseSync } from "vite";
30
36
  import { init, parse } from "es-module-lexer";
31
37
  //#region src/pages/dev-css-assets.ts
32
38
  var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
@@ -272,7 +278,7 @@ function pagesDevCssAssetsPlugin(root, token) {
272
278
  resolveId(id) {
273
279
  if (id !== "virtual:void-pages-dev-assets") return;
274
280
  if (this.environment?.name === "client") throw new Error(`${DEV_CSS_ASSET_MODULE_ID} is server-only and cannot be imported by client code.`);
275
- return "\0virtual:void-pages-dev-assets";
281
+ return "\0" + DEV_CSS_ASSET_MODULE_ID;
276
282
  },
277
283
  load(id) {
278
284
  if (id !== "\0virtual:void-pages-dev-assets") return;
@@ -412,8 +418,8 @@ export async function getVoidPagesDevCss(request, env, payload) {
412
418
  }
413
419
  const edges = [];
414
420
  for (const item of imports) {
415
- if (item.d !== -1 || !item.n) continue;
416
- const resolved = await this.resolve(item.n, id, { skipSelf: true });
421
+ if (item.type !== "static" && item.type !== "reexport-star" || !item.specifier) continue;
422
+ const resolved = await this.resolve(item.specifier, id, { skipSelf: true });
417
423
  if (resolved?.id) edges.push(normalizeDevCssUrl(resolved.id, root));
418
424
  }
419
425
  edgeStore.set(id, edges);
@@ -505,7 +511,7 @@ function devTriggersHintPlugin(root, options) {
505
511
  if (queues.length > 0) summary.push(`${queues.length} queue${queues.length === 1 ? "" : "s"}`);
506
512
  console.log();
507
513
  voidLog(`Detected ${summary.join(" and ")}. Trigger locally:`);
508
- const configuredProxyToken = parseDotenvFile(join(root, ".dev.vars"), { expand: false }).__VOID_PROXY_TOKEN;
514
+ const configuredProxyToken = parseDotenvFile(join(root, ".env"), { expand: false }).__VOID_PROXY_TOKEN;
509
515
  const authHeader = configuredProxyToken ? `x-void-internal: ${configuredProxyToken}` : `x-void-dev-trigger: ${devTriggerToken}`;
510
516
  if (jobs.length > 0) {
511
517
  const sample = jobs[0].crons[0];
@@ -525,7 +531,9 @@ const drizzleD1Resolved = fileURLToPath(import.meta.resolve("drizzle-orm/d1"));
525
531
  const drizzleOrmResolved = fileURLToPath(import.meta.resolve("drizzle-orm"));
526
532
  const envModuleResolved = fileURLToPath(import.meta.resolve("#self/_env"));
527
533
  const drizzleNodePgResolved = fileURLToPath(import.meta.resolve("drizzle-orm/node-postgres"));
534
+ const drizzleMysql2Resolved = fileURLToPath(import.meta.resolve("drizzle-orm/mysql2"));
528
535
  const pgResolved = fileURLToPath(import.meta.resolve("pg"));
536
+ const mysql2Resolved = fileURLToPath(import.meta.resolve("mysql2"));
529
537
  const VIRTUAL_DB_ID = "\0virtual:void-db";
530
538
  const VIRTUAL_SCHEMA_DB_ID = "\0virtual:void-db-schema";
531
539
  /**
@@ -589,34 +597,72 @@ export function createDb() {
589
597
  }
590
598
  `;
591
599
  const schemaImport = schema ? `import * as schema from ${JSON.stringify(schema.path)};` : "";
592
- const drizzleConfig = schema ? ", { schema }" : "";
600
+ const drizzleConfig = schema ? dialect === "mysql" ? ", { schema, mode: \"default\" }" : ", { schema }" : "";
601
+ if (dialect === "mysql") return `
602
+ import { drizzle } from ${JSON.stringify(drizzleMysql2Resolved)};
603
+ import { createPool } from ${JSON.stringify(mysql2Resolved)};
604
+ import { getRuntimeBinding, getRuntimeRequestResource } from ${JSON.stringify(envModuleResolved)};
605
+ ${schemaImport}
606
+ export { ${DRIZZLE_OPERATORS} } from ${JSON.stringify(drizzleOrmResolved)};
607
+
608
+ const _requestResourceKey = {};
609
+
610
+ function getConnectionString() {
611
+ const url = getRuntimeBinding("DATABASE_URL");
612
+ if (url) return url;
613
+ const hd = getRuntimeBinding("HYPERDRIVE");
614
+ if (hd?.connectionString) return hd.connectionString;
615
+ throw new Error("db: MySQL is not configured. Add DATABASE_URL to .env for local dev, or configure Hyperdrive for production.");
616
+ }
617
+
618
+ function getInstance() {
619
+ return getRuntimeRequestResource(_requestResourceKey, () => {
620
+ const pool = createPool({
621
+ uri: getConnectionString(),
622
+ connectionLimit: 1,
623
+ maxIdle: 1,
624
+ idleTimeout: 1000,
625
+ });
626
+ return drizzle(pool${drizzleConfig});
627
+ });
628
+ }
629
+
630
+ export const db = new Proxy({}, {
631
+ get(_, prop) {
632
+ return Reflect.get(getInstance(), prop);
633
+ },
634
+ });
635
+
636
+ export function createDb(connectionString) {
637
+ return drizzle(connectionString${drizzleConfig});
638
+ }
639
+ `;
593
640
  if (dialect === "postgresql") return `
594
641
  import { drizzle } from ${JSON.stringify(drizzleNodePgResolved)};
595
642
  import pg from "pg";
596
- import { getRuntimeBinding } from ${JSON.stringify(envModuleResolved)};
643
+ import { getRuntimeBinding, getRuntimeRequestResource } from ${JSON.stringify(envModuleResolved)};
597
644
  ${schemaImport}
598
645
  export { ${DRIZZLE_OPERATORS} } from ${JSON.stringify(drizzleOrmResolved)};
599
646
 
600
- let _prodInstance;
647
+ const _requestResourceKey = {};
601
648
 
602
649
  function getConnectionString() {
603
650
  const url = getRuntimeBinding("DATABASE_URL");
604
651
  if (url) return url;
605
652
  const hd = getRuntimeBinding("HYPERDRIVE");
606
653
  if (hd?.connectionString) return hd.connectionString;
607
- throw new Error("db: PostgreSQL is not configured. Add DATABASE_URL to .env.local for local dev, or configure Hyperdrive for production.");
654
+ throw new Error("db: PostgreSQL is not configured. Add DATABASE_URL to .env for local dev, or configure Hyperdrive for production.");
608
655
  }
609
656
 
610
657
  function getInstance() {
611
- // Local dev (DATABASE_URL): create a fresh Pool per query chain.
612
- // workerd kills TCP sockets between requests, so pg.Pool's cached
613
- // connections go stale and hang. Fresh Pool = fresh connection every time.
614
- if (getRuntimeBinding("DATABASE_URL")) {
615
- return drizzle(new pg.Pool({ connectionString: getRuntimeBinding("DATABASE_URL"), max: 1 })${drizzleConfig});
616
- }
617
- // Production (Hyperdrive): cache the instance — Hyperdrive handles pooling.
618
- _prodInstance ??= drizzle(getConnectionString()${drizzleConfig});
619
- return _prodInstance;
658
+ return getRuntimeRequestResource(_requestResourceKey, () => {
659
+ const pool = new pg.Pool({
660
+ connectionString: getConnectionString(),
661
+ max: 1,
662
+ idleTimeoutMillis: 1000,
663
+ });
664
+ return drizzle(pool${drizzleConfig});
665
+ });
620
666
  }
621
667
 
622
668
  export const db = new Proxy({}, {
@@ -655,7 +701,7 @@ export function createDb(d1) {
655
701
  const migrationsDir = paths.migrationsDir;
656
702
  if (!(existsSync(migrationsDir) && readdirSync(migrationsDir).some((f) => f.endsWith(".sql")))) {
657
703
  if (dialect === "sqlite") {
658
- const { getApplicationTables, getMiniflareDatabasePath } = await import("./runner-kapo9aPs.mjs");
704
+ const { getApplicationTables, getMiniflareDatabasePath } = await import("./local-d1-D2I6Ox5F.mjs").then((n) => n.i);
659
705
  if ((await getApplicationTables(getMiniflareDatabasePath(options.localD1PersistRoot ?? join(root, ".void"), options.localD1DatabaseId ?? "local")).catch(() => [])).length > 0) return;
660
706
  }
661
707
  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.
@@ -672,6 +718,264 @@ export function createDb(d1) {
672
718
  };
673
719
  }
674
720
  //#endregion
721
+ //#region src/plugin-email-dev-inbox.ts
722
+ const INBOX_PREFIX = DEV_INBOX_PREFIX;
723
+ const SECURITY_HEADERS = {
724
+ "Cache-Control": "no-store",
725
+ "X-Content-Type-Options": "nosniff",
726
+ "X-Frame-Options": "DENY",
727
+ "Referrer-Policy": "no-referrer",
728
+ "Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; frame-src data:; img-src data:; connect-src 'none'"
729
+ };
730
+ function emailDevInboxPlugin(options) {
731
+ const { devTriggerToken } = options;
732
+ let devMode = false;
733
+ let unregister;
734
+ /** `href` for an inbox path that keeps the browser session authorized. */
735
+ const inboxHref = (path) => `${INBOX_PREFIX}${path}?${DEV_INBOX_TOKEN_QUERY}=${encodeURIComponent(devTriggerToken)}`;
736
+ return {
737
+ name: "void:email-dev-inbox",
738
+ apply: "serve",
739
+ config() {
740
+ devMode = true;
741
+ },
742
+ buildStart() {
743
+ if (!devMode || unregister) return;
744
+ unregister = registerEmailSink((m) => pushToDevBuffer(m));
745
+ },
746
+ buildEnd() {
747
+ if (unregister) {
748
+ unregister();
749
+ unregister = void 0;
750
+ }
751
+ },
752
+ configureServer(server) {
753
+ if (server.config.command !== "serve") return;
754
+ const printInboxUrl = () => {
755
+ const address = server.httpServer?.address();
756
+ const port = typeof address === "object" && address ? address.port : null;
757
+ if (port == null) return;
758
+ voidLog(`Email inbox: http://localhost:${port}${inboxHref("")}`);
759
+ };
760
+ if (server.httpServer) server.httpServer.once("listening", () => setTimeout(printInboxUrl, 100));
761
+ server.middlewares.use(INBOX_PREFIX, (req, res) => {
762
+ if (server.config.command !== "serve") {
763
+ res.writeHead(404, SECURITY_HEADERS);
764
+ res.end();
765
+ return;
766
+ }
767
+ const url = new URL(req.url ?? "/", "http://localhost");
768
+ const path = url.pathname;
769
+ if (!isAuthorized(req.headers["x-void-dev-trigger"], url, devTriggerToken)) {
770
+ res.writeHead(401, {
771
+ ...SECURITY_HEADERS,
772
+ "Content-Type": "text/plain; charset=utf-8"
773
+ });
774
+ res.end(`Unauthorized: the void dev inbox requires the dev-trigger token — send it as the ${DEV_INBOX_TOKEN_HEADER} header or the ?${DEV_INBOX_TOKEN_QUERY}= query (printed as the inbox URL when void dev starts; stored in .void/dev-trigger-token).`);
775
+ return;
776
+ }
777
+ if (req.method === "POST" && path === "/_capture") {
778
+ handleCapture(req, res);
779
+ return;
780
+ }
781
+ if (req.method === "DELETE" && path === "/") {
782
+ clearDevBuffer();
783
+ res.writeHead(204, SECURITY_HEADERS);
784
+ res.end();
785
+ return;
786
+ }
787
+ if (req.method !== "GET") {
788
+ res.writeHead(405, SECURITY_HEADERS);
789
+ res.end();
790
+ return;
791
+ }
792
+ if (path === "/" || path === "") {
793
+ res.writeHead(200, {
794
+ ...SECURITY_HEADERS,
795
+ "Content-Type": "text/html; charset=utf-8"
796
+ });
797
+ res.end(renderListHtml(listDevMessages(), inboxHref));
798
+ return;
799
+ }
800
+ const match = path.match(/^\/([^/]+)(\/raw)?$/);
801
+ if (!match) {
802
+ res.writeHead(404, SECURITY_HEADERS);
803
+ res.end();
804
+ return;
805
+ }
806
+ const id = match[1];
807
+ const wantsRaw = Boolean(match[2]);
808
+ const message = findDevMessage(id);
809
+ if (!message) {
810
+ res.writeHead(404, {
811
+ ...SECURITY_HEADERS,
812
+ "Content-Type": "text/plain"
813
+ });
814
+ res.end("Email not found.");
815
+ return;
816
+ }
817
+ if (wantsRaw) {
818
+ const safeId = id.replace(/["\r\n]/g, "");
819
+ res.writeHead(200, {
820
+ ...SECURITY_HEADERS,
821
+ "Content-Type": "message/rfc822",
822
+ "Content-Disposition": `attachment; filename="${safeId}.eml"`
823
+ });
824
+ res.end(Buffer.from(message.raw));
825
+ return;
826
+ }
827
+ res.writeHead(200, {
828
+ ...SECURITY_HEADERS,
829
+ "Content-Type": "text/html; charset=utf-8"
830
+ });
831
+ res.end(renderDetailHtml(message, inboxHref));
832
+ });
833
+ }
834
+ };
835
+ }
836
+ /**
837
+ * Constant-time check of the presented token — header first (worker, curl),
838
+ * then the query (browser). Both are compared against the one project token.
839
+ */
840
+ function isAuthorized(headerValue, url, expected) {
841
+ const presented = typeof headerValue === "string" ? headerValue : url.searchParams.get(DEV_INBOX_TOKEN_QUERY);
842
+ if (typeof presented !== "string") return false;
843
+ const a = Buffer.from(presented);
844
+ const b = Buffer.from(expected);
845
+ return a.byteLength === b.byteLength && timingSafeEqual(a, b);
846
+ }
847
+ /**
848
+ * `POST /__void/inbox/_capture` — the worker's dev-inbox hop.
849
+ *
850
+ * Fails with a status the worker can distinguish, and only a success carries
851
+ * the ack header, so the worker never mistakes an ordinary asset 200 for a
852
+ * capture. Never throws: an exception here would surface as a dev-server 500
853
+ * in the middle of the user's `sendEmail`.
854
+ */
855
+ async function handleCapture(req, res) {
856
+ try {
857
+ if (!String(req.headers["content-type"] ?? "").includes("application/json")) {
858
+ res.writeHead(415, SECURITY_HEADERS);
859
+ res.end();
860
+ return;
861
+ }
862
+ const chunks = [];
863
+ let size = 0;
864
+ for await (const chunk of req) {
865
+ const buf = typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
866
+ size += buf.byteLength;
867
+ if (size > DEV_INBOX_MAX_CAPTURE_BYTES) {
868
+ res.writeHead(413, SECURITY_HEADERS);
869
+ res.end();
870
+ return;
871
+ }
872
+ chunks.push(buf);
873
+ }
874
+ let parsed;
875
+ try {
876
+ parsed = JSON.parse(Buffer.concat(chunks).toString("utf8"));
877
+ } catch {
878
+ res.writeHead(400, SECURITY_HEADERS);
879
+ res.end();
880
+ return;
881
+ }
882
+ const message = parseCapturedMessage(parsed);
883
+ if (!message) {
884
+ res.writeHead(400, SECURITY_HEADERS);
885
+ res.end();
886
+ return;
887
+ }
888
+ pushToDevBuffer(message);
889
+ res.writeHead(200, {
890
+ ...SECURITY_HEADERS,
891
+ [DEV_INBOX_ACK_HEADER]: DEV_INBOX_ACK_VALUE,
892
+ "Content-Type": "application/json"
893
+ });
894
+ res.end(JSON.stringify({
895
+ ok: true,
896
+ id: message.id
897
+ }));
898
+ } catch {
899
+ res.writeHead(500, SECURITY_HEADERS);
900
+ res.end();
901
+ }
902
+ }
903
+ function escapeHtml(s) {
904
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
905
+ }
906
+ function formatAddress(a) {
907
+ return a.name ? `${a.name} <${a.email}>` : a.email;
908
+ }
909
+ function formatTime(ts) {
910
+ return new Date(ts).toLocaleString();
911
+ }
912
+ const STYLE = `
913
+ :root { color-scheme: light dark; }
914
+ body { font: 14px/1.5 ui-sans-serif, system-ui, sans-serif; margin: 0; padding: 24px; max-width: 1024px; margin: 0 auto; }
915
+ h1 { font-size: 18px; margin: 0 0 16px; }
916
+ h2 { font-size: 14px; margin: 24px 0 8px; text-transform: uppercase; letter-spacing: 0.05em; opacity: 0.7; }
917
+ table { border-collapse: collapse; width: 100%; }
918
+ th, td { text-align: left; padding: 8px 12px; border-bottom: 1px solid color-mix(in srgb, currentColor 15%, transparent); vertical-align: top; }
919
+ th { font-weight: 600; opacity: 0.7; font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; }
920
+ a { color: #5b8def; text-decoration: none; }
921
+ a:hover { text-decoration: underline; }
922
+ .meta { font-size: 12px; opacity: 0.7; }
923
+ .empty { padding: 48px; text-align: center; opacity: 0.6; }
924
+ .row { display: flex; gap: 12px; align-items: baseline; }
925
+ .row .subject { font-weight: 600; }
926
+ iframe { width: 100%; min-height: 480px; border: 1px solid color-mix(in srgb, currentColor 15%, transparent); border-radius: 8px; background: white; }
927
+ .actions { display: flex; gap: 12px; margin-bottom: 16px; }
928
+ .button { display: inline-block; padding: 6px 12px; border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-radius: 6px; cursor: pointer; background: transparent; color: inherit; font: inherit; }
929
+ pre { white-space: pre-wrap; word-break: break-word; padding: 12px; background: color-mix(in srgb, currentColor 5%, transparent); border-radius: 6px; }
930
+ .header-table th { width: 160px; }
931
+ `;
932
+ function renderListHtml(messages, href) {
933
+ const rows = messages.map((m) => `
934
+ <tr>
935
+ <td><a href="${escapeHtml(href(`/${m.id}`))}">${escapeHtml(m.subject || "(no subject)")}</a></td>
936
+ <td>${escapeHtml(formatAddress(m.from))}</td>
937
+ <td>${escapeHtml(m.to.map(formatAddress).join(", "))}</td>
938
+ <td class="meta">${escapeHtml(formatTime(m.capturedAt))}</td>
939
+ <td class="meta"><a href="${escapeHtml(href(`/${m.id}/raw`))}">.eml</a></td>
940
+ </tr>`).join("");
941
+ return `<!DOCTYPE html>
942
+ <html><head><meta charset="utf-8"><title>Void — Email inbox</title><style>${STYLE}</style></head>
943
+ <body>
944
+ <h1>Email inbox <span class="meta">(${messages.length} message${messages.length === 1 ? "" : "s"})</span></h1>
945
+ ${messages.length === 0 ? `<div class="empty">No messages captured yet. Call <code>sendEmail()</code> from your app to populate.</div>` : `<table>
946
+ <thead><tr><th>Subject</th><th>From</th><th>To</th><th>Captured</th><th></th></tr></thead>
947
+ <tbody>${rows}</tbody>
948
+ </table>`}
949
+ </body></html>`;
950
+ }
951
+ function renderDetailHtml(m, href) {
952
+ const headerRows = Object.entries(m.headers).map(([k, v]) => `<tr><th>${escapeHtml(k)}</th><td>${escapeHtml(v)}</td></tr>`).join("");
953
+ const attachmentRows = m.attachments.map((a) => `<tr><td>${escapeHtml(a.filename)}</td><td>${escapeHtml(a.contentType)}</td><td>${a.size} B</td><td>${a.disposition}</td></tr>`).join("");
954
+ const bodyHtml = m.html ? `<iframe sandbox srcdoc="${escapeHtml(m.html)}"></iframe>` : `<pre>${escapeHtml(m.text ?? "")}</pre>`;
955
+ return `<!DOCTYPE html>
956
+ <html><head><meta charset="utf-8"><title>${escapeHtml(m.subject || "(no subject)")} — Void inbox</title><style>${STYLE}</style></head>
957
+ <body>
958
+ <div class="actions">
959
+ <a class="button" href="${escapeHtml(href(""))}">← Back</a>
960
+ <a class="button" href="${escapeHtml(href(`/${m.id}/raw`))}">Download .eml</a>
961
+ </div>
962
+ <h1>${escapeHtml(m.subject || "(no subject)")}</h1>
963
+ <table class="header-table">
964
+ <tr><th>From</th><td>${escapeHtml(formatAddress(m.from))}</td></tr>
965
+ <tr><th>To</th><td>${escapeHtml(m.to.map(formatAddress).join(", "))}</td></tr>
966
+ ${m.cc.length > 0 ? `<tr><th>Cc</th><td>${escapeHtml(m.cc.map(formatAddress).join(", "))}</td></tr>` : ""}
967
+ ${m.bcc.length > 0 ? `<tr><th>Bcc</th><td>${escapeHtml(m.bcc.map(formatAddress).join(", "))}</td></tr>` : ""}
968
+ ${m.replyTo ? `<tr><th>Reply-To</th><td>${escapeHtml(formatAddress(m.replyTo))}</td></tr>` : ""}
969
+ <tr><th>Captured</th><td class="meta">${escapeHtml(formatTime(m.capturedAt))}</td></tr>
970
+ <tr><th>Message-ID</th><td class="meta">${escapeHtml(m.messageId)}</td></tr>
971
+ </table>
972
+ ${headerRows ? `<h2>Custom headers</h2><table class="header-table">${headerRows}</table>` : ""}
973
+ ${attachmentRows ? `<h2>Attachments</h2><table><thead><tr><th>Filename</th><th>Type</th><th>Size</th><th>Disposition</th></tr></thead><tbody>${attachmentRows}</tbody></table>` : ""}
974
+ <h2>Body</h2>
975
+ ${bodyHtml}
976
+ </body></html>`;
977
+ }
978
+ //#endregion
675
979
  //#region src/plugin-env-client-fold.ts
676
980
  let cachedModule$1 = null;
677
981
  async function loadEnvPublic$1() {
@@ -743,8 +1047,7 @@ function pushHit(node, hits) {
743
1047
  return;
744
1048
  }
745
1049
  }
746
- /** Default Vite `envPrefix`. */
747
- const DEFAULT_ENV_PREFIXES$2 = ["VITE_"];
1050
+ const CLIENT_ENV_PREFIX$1 = "VITE_";
748
1051
  /**
749
1052
  * Build-time constant folding for client-only `env.FOO` reads.
750
1053
  *
@@ -759,21 +1062,19 @@ const DEFAULT_ENV_PREFIXES$2 = ["VITE_"];
759
1062
  * • Runs only when `this.environment.name === 'client'`. Server / SSR /
760
1063
  * worker builds keep the full runtime Proxy (secrets, runtime-injected
761
1064
  * vars, HMR remain live).
762
- * • Only folds keys that match the configured `envPrefix` — the exact
763
- * set Vite is already willing to ship to the browser bundle. A
1065
+ * • Only folds keys that match Void's fixed `VITE_` client prefix. A
764
1066
  * server-only key is never folded (and the env-client-guard plugin
765
1067
  * would have errored out earlier anyway).
766
1068
  * • Only folds static member access: `env.FOO` and `env["FOO"]`.
767
1069
  * Dynamic access, destructuring, and reassignment fall through to the
768
1070
  * runtime Proxy.
769
- * • Only folds keys with a known build-time value (loaded .env* merged
770
- * with schema `.default(...)` entries). Unknown keys are left as-is
1071
+ * • Only folds keys with a known build-time value (build shell or schema
1072
+ * `.default(...)`). Unknown keys are left as-is
771
1073
  * and the Proxy handles them at runtime — no "undefined" shoved in.
772
- * • Build-only (`apply: 'build'`). Dev runs through the Proxy so HMR
773
- * and editing `.env.local` take effect without a full rebuild.
1074
+ * • Build-only (`apply: 'build'`). Dev runs through the Proxy so local
1075
+ * `.env` edits take effect without a full rebuild.
774
1076
  */
775
1077
  function envClientFoldPlugin() {
776
- let envPrefixes = DEFAULT_ENV_PREFIXES$2;
777
1078
  let loadedEnv = {};
778
1079
  const schemaDefaults = {};
779
1080
  /** The merged view the transform consults for each key. */
@@ -782,18 +1083,14 @@ function envClientFoldPlugin() {
782
1083
  if (key in schemaDefaults) return schemaDefaults[key];
783
1084
  }
784
1085
  function isExposedToClient(key) {
785
- return envPrefixes.some((p) => key.startsWith(p));
1086
+ return key.startsWith(CLIENT_ENV_PREFIX$1);
786
1087
  }
787
1088
  return {
788
1089
  name: "void:env-client-fold",
789
1090
  apply: "build",
790
1091
  enforce: "post",
791
1092
  async configResolved(config) {
792
- const prefix = config.envPrefix;
793
- if (typeof prefix === "string") envPrefixes = [prefix];
794
- else if (Array.isArray(prefix) && prefix.length > 0) envPrefixes = prefix;
795
- else envPrefixes = DEFAULT_ENV_PREFIXES$2;
796
- loadedEnv = { ...config.env ?? {} };
1093
+ loadedEnv = config.env ?? {};
797
1094
  try {
798
1095
  const mod = await loadEnvPublic$1();
799
1096
  if (mod._hasRegisteredSchema()) {
@@ -947,18 +1244,11 @@ function recordHit(node, hits) {
947
1244
  * No-op when no `env.ts` is registered. Server / SSR / worker environments
948
1245
  * are skipped — only the `client` environment is checked.
949
1246
  */
950
- /** Default value matches Vite's default `envPrefix`. */
951
- const DEFAULT_ENV_PREFIXES$1 = ["VITE_"];
1247
+ const CLIENT_ENV_PREFIX = "VITE_";
952
1248
  function envClientGuardPlugin(root) {
953
- let envPrefixes = DEFAULT_ENV_PREFIXES$1;
954
1249
  return {
955
1250
  name: "void:env-client-guard",
956
1251
  enforce: "post",
957
- configResolved(config) {
958
- const prefix = config.envPrefix;
959
- if (typeof prefix === "string") envPrefixes = [prefix];
960
- else if (Array.isArray(prefix) && prefix.length > 0) envPrefixes = prefix;
961
- },
962
1252
  async transform(code, id) {
963
1253
  if (this.environment?.name !== "client") return;
964
1254
  if (id.includes("/node_modules/")) return;
@@ -1001,10 +1291,9 @@ function envClientGuardPlugin(root) {
1001
1291
  }
1002
1292
  const key = hit.property;
1003
1293
  if (!(key in schema)) continue;
1004
- if (envPrefixes.some((p) => key.startsWith(p))) continue;
1005
- const prefixHint = envPrefixes.length === 1 ? `${envPrefixes[0]}* prefix` : `one of these prefixes: ${envPrefixes.join(", ")}`;
1294
+ if (key.startsWith(CLIENT_ENV_PREFIX)) continue;
1006
1295
  this.error({
1007
- 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 ${prefixHint} in env.ts.\nIf it must stay server-only, remove this reference and read the value via a server route or loader.`,
1296
+ 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.`,
1008
1297
  loc: {
1009
1298
  line,
1010
1299
  column,
@@ -1028,32 +1317,42 @@ async function loadSchemaModule() {
1028
1317
  * Vite plugin that auto-discovers `env.ts` at the project root and:
1029
1318
  * - imports it (registering the user's schema via `defineEnv`)
1030
1319
  * - generates `.void/env.d.ts` so `env.X` and `c.env.X` are typed
1031
- * - validates the merged dev env (.env files + process.env) and warns
1320
+ * - validates the local dev env (`.env` + process.env) and warns
1032
1321
  * on missing/invalid required keys
1033
1322
  * - regenerates types + re-validates when env.ts changes (HMR)
1034
1323
  *
1035
1324
  * Hard errors are reserved for `void deploy`; dev keeps moving with warnings
1036
- * so unrelated work isn't blocked by a stale .env.local.
1325
+ * so unrelated work isn't blocked by a stale local value.
1037
1326
  */
1038
1327
  /** Patterns Vite's `define` typically uses for env-shaped constants. */
1039
1328
  const DEFINE_ENV_PATTERN = /^(?:globalThis\.)?(?:process\.env|import\.meta\.env)\.([A-Z_][A-Z0-9_]*)$/;
1040
- const DEFAULT_ENV_PREFIXES = ["VITE_"];
1041
- function resolveEnvPrefixes(prefix) {
1042
- if (typeof prefix === "string") return [prefix];
1043
- if (Array.isArray(prefix) && prefix.length > 0) return prefix;
1044
- return DEFAULT_ENV_PREFIXES;
1045
- }
1329
+ const CLIENT_ENV_PREFIXES = [CLIENT_ENV_PREFIX$2];
1046
1330
  function envSchemaPlugin(root, options = {}) {
1047
1331
  const outDir = join(root, ".void");
1048
1332
  let envFile = null;
1049
- let viteMode = "development";
1050
- let envDir = root;
1333
+ let command = "serve";
1051
1334
  const appliedClientDefaults = /* @__PURE__ */ new Map();
1052
- async function emitDefaults() {
1053
- if (!envFile || !options.onDefaults) return;
1054
- const { _hasRegisteredSchema, _getSchemaDefaults } = await loadSchemaModule();
1335
+ async function emitSchemaFacts(prefixes) {
1336
+ if (!envFile || !options.onSchema) return;
1337
+ const { _hasRegisteredSchema, _getRegisteredSchema } = await loadSchemaModule();
1055
1338
  if (!_hasRegisteredSchema()) return;
1056
- options.onDefaults(_getSchemaDefaults());
1339
+ const schema = _getRegisteredSchema() ?? {};
1340
+ const keys = Object.keys(schema);
1341
+ const clientKeys = keys.filter((key) => prefixes.some((prefix) => key.startsWith(prefix)));
1342
+ const clientKeySet = new Set(clientKeys);
1343
+ const serverKeys = keys.filter((key) => !clientKeySet.has(key));
1344
+ const requiredServerKeys = [];
1345
+ for (const key of serverKeys) {
1346
+ const result = schema[key]["~standard"].validate(void 0);
1347
+ const resolved = result instanceof Promise ? await result : result;
1348
+ if (resolved && typeof resolved === "object" && "issues" in resolved && resolved.issues) requiredServerKeys.push(key);
1349
+ }
1350
+ options.onSchema({
1351
+ keys,
1352
+ clientKeys,
1353
+ serverKeys,
1354
+ requiredServerKeys
1355
+ });
1057
1356
  }
1058
1357
  function regenerateTypes() {
1059
1358
  if (!envFile) return;
@@ -1061,11 +1360,12 @@ function envSchemaPlugin(root, options = {}) {
1061
1360
  const { dts } = generateEnvTypes(envFile, outDir);
1062
1361
  writeFileIfChanged(join(outDir, "env.d.ts"), dts);
1063
1362
  }
1064
- async function importUserEnv() {
1363
+ async function importUserEnv(strict = false) {
1065
1364
  if (!envFile) return;
1066
1365
  try {
1067
1366
  await loadUserEnvFile(envFile);
1068
1367
  } catch (err) {
1368
+ if (strict) throw err;
1069
1369
  voidWarn(err.message);
1070
1370
  }
1071
1371
  }
@@ -1093,41 +1393,76 @@ function envSchemaPlugin(root, options = {}) {
1093
1393
  if (!envFile) return;
1094
1394
  const { _hasRegisteredSchema, validateAllEnv } = await loadSchemaModule();
1095
1395
  if (!_hasRegisteredSchema()) return;
1096
- const { loadEnv } = await import("vite");
1097
- const report = await validateAllEnv(filterLoadedEnv(loadEnv(viteMode, envDir, "")));
1396
+ const report = await validateAllEnv({
1397
+ ...loadLocalEnv(root, { onWarn: (message) => voidWarn(message) }),
1398
+ ...process.env
1399
+ });
1098
1400
  if (report.valid) return;
1099
1401
  const lines = ["Env validation issues:"];
1100
- for (const key of report.missing) lines.push(` • ${key} is required but missing — add it to .env.local or set in your shell`);
1402
+ for (const key of report.missing) lines.push(` • ${key} is required but missing — add it to .env or set it in your shell`);
1101
1403
  for (const { key, messages } of report.invalid) lines.push(` • ${key}: ${messages.join("; ")}`);
1102
1404
  voidWarn(lines.join("\n"));
1103
1405
  }
1104
- async function applyClientDefaultsToViteEnv(config) {
1406
+ async function applyClientValuesToViteEnv(config) {
1105
1407
  if (!envFile) return;
1106
- for (const [key, value] of appliedClientDefaults) if (config.env[key] === value) delete config.env[key];
1408
+ const configEnv = config.env ?? {};
1409
+ for (const [key, value] of appliedClientDefaults) if (configEnv[key] === value) delete configEnv[key];
1107
1410
  appliedClientDefaults.clear();
1108
- const { _hasRegisteredSchema, _getSchemaDefaults } = await loadSchemaModule();
1411
+ const { _hasRegisteredSchema, _getRegisteredSchema, _getSchemaDefaults } = await loadSchemaModule();
1109
1412
  if (!_hasRegisteredSchema()) return;
1110
- const prefixes = resolveEnvPrefixes(config.envPrefix);
1413
+ const schema = _getRegisteredSchema() ?? {};
1414
+ const readable = {
1415
+ ...command === "serve" ? loadLocalEnv(root) : {},
1416
+ ...process.env
1417
+ };
1418
+ for (const key of Object.keys(schema)) {
1419
+ if (!key.startsWith("VITE_") || configEnv[key] !== void 0) continue;
1420
+ const value = readable[key];
1421
+ if (value !== void 0) configEnv[key] = value;
1422
+ }
1111
1423
  for (const [key, value] of Object.entries(_getSchemaDefaults())) {
1112
- if (!prefixes.some((prefix) => key.startsWith(prefix))) continue;
1113
- if (config.env[key] !== void 0) continue;
1114
- config.env[key] = value;
1424
+ if (!key.startsWith("VITE_")) continue;
1425
+ if (configEnv[key] !== void 0) continue;
1426
+ configEnv[key] = value;
1115
1427
  appliedClientDefaults.set(key, value);
1116
1428
  }
1117
1429
  }
1118
1430
  return {
1119
1431
  name: "void:env-schema",
1120
- async config() {
1432
+ enforce: "pre",
1433
+ async config(_config, environment) {
1434
+ const legacyFiles = findLegacyEnvFiles(root);
1435
+ if (legacyFiles.length > 0) throw new Error(`void/env supports one local file: .env. Remove ${legacyFiles.join(", ")} and follow the env migration guide.`);
1121
1436
  envFile = findEnvFile(root);
1122
1437
  if (!envFile) return;
1123
1438
  regenerateTypes();
1124
- await importUserEnv();
1125
- await emitDefaults();
1439
+ await importUserEnv(environment.command === "build");
1440
+ await emitSchemaFacts(CLIENT_ENV_PREFIXES);
1126
1441
  },
1127
1442
  async configResolved(config) {
1128
- viteMode = config.mode;
1129
- envDir = typeof config.envDir === "string" ? config.envDir : root;
1130
- await applyClientDefaultsToViteEnv(config);
1443
+ command = config.command;
1444
+ await emitSchemaFacts(CLIENT_ENV_PREFIXES);
1445
+ if (command === "build") {
1446
+ const local = loadLocalEnv(root, { onWarn: (message) => voidWarn(message) });
1447
+ for (const [key, value] of Object.entries(local)) if (process.env[key] === void 0 && config.env[key] === value) delete config.env[key];
1448
+ }
1449
+ await applyClientValuesToViteEnv(config);
1450
+ if (command === "build" && envFile) {
1451
+ const { validateAllEnv } = await loadSchemaModule();
1452
+ const report = await validateAllEnv({
1453
+ ...config.env,
1454
+ ...process.env
1455
+ });
1456
+ const clientKey = (key) => key.startsWith(CLIENT_ENV_PREFIX$2);
1457
+ const missing = report.missing.filter(clientKey);
1458
+ const invalid = report.invalid.filter(({ key }) => clientKey(key));
1459
+ if (missing.length > 0 || invalid.length > 0) {
1460
+ const lines = ["Client env validation failed:"];
1461
+ for (const key of missing) lines.push(` • ${key} is required — set it in the build shell or add a schema default`);
1462
+ for (const { key, messages } of invalid) lines.push(` • ${key}: ${messages.join("; ")}`);
1463
+ throw new Error(lines.join("\n"));
1464
+ }
1465
+ }
1131
1466
  if (envFile && config.define) await warnOnDefineOverlap(config.define);
1132
1467
  },
1133
1468
  async configureServer(server) {
@@ -1144,8 +1479,8 @@ function envSchemaPlugin(root, options = {}) {
1144
1479
  voidLog("env.ts changed — regenerating types");
1145
1480
  regenerateTypes();
1146
1481
  await importUserEnv();
1147
- await emitDefaults();
1148
- await applyClientDefaultsToViteEnv(server.config);
1482
+ await emitSchemaFacts(CLIENT_ENV_PREFIXES);
1483
+ await applyClientValuesToViteEnv(server.config);
1149
1484
  await validateAndWarn();
1150
1485
  server.ws.send({ type: "full-reload" });
1151
1486
  }
@@ -1158,6 +1493,7 @@ function envSchemaPlugin(root, options = {}) {
1158
1493
  }
1159
1494
  //#endregion
1160
1495
  //#region src/plugin-framework-triggers.ts
1496
+ const DISPATCH_FILE_MODE = 384;
1161
1497
  /** Absolute path of the generated dispatch file. Stable across the dev process. */
1162
1498
  function frameworkTriggersDispatchPath(root) {
1163
1499
  return join(root, ".void", "dev-triggers.ts");
@@ -1174,7 +1510,10 @@ function writeFrameworkTriggersDispatch(root, devTriggerToken) {
1174
1510
  const queues = scanQueuesSync(root, paths.queuesDir);
1175
1511
  const target = frameworkTriggersDispatchPath(root);
1176
1512
  mkdirSync(dirname(target), { recursive: true });
1177
- writeFileSync(target, generateModule(paths.cronsDir, paths.queuesDir, jobs, queues, devTriggerToken));
1513
+ writeFileSync(target, generateModule(paths.cronsDir, paths.queuesDir, jobs, queues, devTriggerToken), { mode: DISPATCH_FILE_MODE });
1514
+ if (process.platform !== "win32") try {
1515
+ chmodSync(target, DISPATCH_FILE_MODE);
1516
+ } catch {}
1178
1517
  }
1179
1518
  /**
1180
1519
  * Writes a real file at `<root>/.void/dev-triggers.ts` exporting
@@ -1616,24 +1955,12 @@ function astroDevTriggerInjectPlugin(root) {
1616
1955
  //#endregion
1617
1956
  //#region src/plugin-migration.ts
1618
1957
  function readDatabaseUrl(root) {
1619
- const envPath = join(root, ".env.local");
1620
- if (!existsSync(envPath)) return;
1621
- const content = readFileSync(envPath, "utf-8");
1622
- for (const line of content.split("\n")) {
1623
- const trimmed = line.trim();
1624
- if (!trimmed || trimmed.startsWith("#")) continue;
1625
- const eqIndex = trimmed.indexOf("=");
1626
- if (eqIndex === -1) continue;
1627
- if (trimmed.slice(0, eqIndex).trim() !== "DATABASE_URL") continue;
1628
- let value = trimmed.slice(eqIndex + 1).trim();
1629
- if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
1630
- return value;
1631
- }
1958
+ return loadLocalEnv(root).DATABASE_URL;
1632
1959
  }
1633
1960
  /**
1634
1961
  * Vite plugin that runs local migrations on dev server startup.
1635
1962
  * For SQLite/D1: applies migrations to the local miniflare database.
1636
- * For PostgreSQL: connects via DATABASE_URL from .env.local.
1963
+ * For PostgreSQL/MySQL: connects via DATABASE_URL from .env.
1637
1964
  */
1638
1965
  function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join(root, ".void"), auth) {
1639
1966
  return {
@@ -1644,35 +1971,34 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
1644
1971
  const hasSqlMigrations = existsSync(migrationsDir);
1645
1972
  if (!hasSqlMigrations && !auth?.enabled) return;
1646
1973
  try {
1647
- if (hasSqlMigrations && dialect === "postgresql") {
1974
+ if (hasSqlMigrations && dialect !== "sqlite") {
1648
1975
  const url = readDatabaseUrl(root);
1649
1976
  if (!url) {
1650
1977
  const warn = () => {
1651
1978
  console.log();
1652
- voidError("DATABASE_URL not found in .env.local — skipping PostgreSQL migrations.");
1979
+ voidError(`DATABASE_URL not found in .env — skipping ${dialect === "mysql" ? "MySQL" : "PostgreSQL"} migrations.`);
1653
1980
  };
1654
1981
  if (server.httpServer) server.httpServer.once("listening", () => setTimeout(warn, 100));
1655
1982
  else warn();
1656
1983
  return;
1657
1984
  }
1658
- const { runPgMigrations, getPgAppliedMigrations } = await import("./runner-pg-CHM76xuC.mjs");
1659
- const { collectMigrations } = await import("./validate-Cw_RLeTj.mjs").then((n) => n.l);
1660
- const { validateMigrations } = await import("./validate-Cw_RLeTj.mjs").then((n) => n.i);
1985
+ const { collectMigrations } = await import("./validate-tBBN_dXH.mjs").then((n) => n.l);
1986
+ const { validateMigrations } = await import("./validate-tBBN_dXH.mjs").then((n) => n.i);
1661
1987
  const allMigrations = collectMigrations(migrationsDir);
1662
- const pgApplied = await getPgAppliedMigrations(url);
1663
- const pgAppliedSet = new Set(pgApplied);
1664
- const pgPending = allMigrations.filter((m) => !pgAppliedSet.has(m.name));
1665
- if (pgPending.length > 0) try {
1666
- validateMigrations(pgPending);
1988
+ const appliedNames = dialect === "mysql" ? await import("./runner-mysql-7BPUNGmL.mjs").then((n) => n.n).then((runner) => runner.getMySqlAppliedMigrations(url)) : await import("./runner-pg-BkEza-dX.mjs").then((n) => n.n).then((runner) => runner.getPgAppliedMigrations(url));
1989
+ const appliedSet = new Set(appliedNames);
1990
+ const pending = allMigrations.filter((migration) => !appliedSet.has(migration.name));
1991
+ if (pending.length > 0) try {
1992
+ validateMigrations(pending);
1667
1993
  } catch (err) {
1668
1994
  voidWarn(err instanceof Error ? err.message : String(err));
1669
1995
  }
1670
- const applied = await runPgMigrations(migrationsDir, url);
1996
+ const applied = dialect === "mysql" ? await import("./runner-mysql-7BPUNGmL.mjs").then((n) => n.n).then((runner) => runner.runMySqlMigrations(migrationsDir, url)) : await import("./runner-pg-BkEza-dX.mjs").then((n) => n.n).then((runner) => runner.runPgMigrations(migrationsDir, url));
1671
1997
  if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
1672
1998
  } else if (hasSqlMigrations) {
1673
- const { runMigrations, getMiniflareDatabasePath, getAppliedMigrations } = await import("./runner-kapo9aPs.mjs");
1674
- const { collectMigrations } = await import("./validate-Cw_RLeTj.mjs").then((n) => n.l);
1675
- const { validateMigrations } = await import("./validate-Cw_RLeTj.mjs").then((n) => n.i);
1999
+ const { runMigrations, getMiniflareDatabasePath, getAppliedMigrations } = await import("./local-d1-D2I6Ox5F.mjs").then((n) => n.i);
2000
+ const { collectMigrations } = await import("./validate-tBBN_dXH.mjs").then((n) => n.l);
2001
+ const { validateMigrations } = await import("./validate-tBBN_dXH.mjs").then((n) => n.i);
1676
2002
  const dbPath = getMiniflareDatabasePath(persistRoot, databaseId);
1677
2003
  const allMigrations = collectMigrations(migrationsDir);
1678
2004
  const appliedRows = await getAppliedMigrations(dbPath);
@@ -1687,7 +2013,7 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
1687
2013
  if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
1688
2014
  }
1689
2015
  if (auth?.enabled) {
1690
- const { runLocalVoidAuthMigrations } = await import("./node-pRg81HqV.mjs");
2016
+ const { runLocalVoidAuthMigrations } = await import("./node-Dk3H2jmU.mjs");
1691
2017
  await runLocalVoidAuthMigrations(root, dialect, persistRoot, databaseId, auth.configPath);
1692
2018
  voidLog("Applied Better Auth migrations");
1693
2019
  }
@@ -1775,6 +2101,14 @@ function nodeTargetPlugin(target) {
1775
2101
  throw new Error(`target: '${id}' is not available with target '${target}'. CF bindings require target: "cloudflare".`);
1776
2102
  }
1777
2103
  },
2104
+ renderChunk(code, chunk) {
2105
+ if (!code.includes("import.meta.voidClientDirectory")) return null;
2106
+ const clientPath = relative(dirname(chunk.fileName), "../client");
2107
+ return {
2108
+ code: code.replaceAll("import.meta.voidClientDirectory", `new URL(${JSON.stringify(`${clientPath}/`)}, import.meta.url)`),
2109
+ map: null
2110
+ };
2111
+ },
1778
2112
  config(_, { command }) {
1779
2113
  if (command !== "build") return;
1780
2114
  const root = process.cwd();
@@ -1839,6 +2173,7 @@ function prerenderPlugin(root, config) {
1839
2173
  apply: "build",
1840
2174
  async closeBundle() {
1841
2175
  if (config.output !== "static") return;
2176
+ if (process.env.VOID_DEPLOY_BUILD === "1") return;
1842
2177
  if (this.environment?.name !== "client") return;
1843
2178
  setShowTimestamp(false);
1844
2179
  const distDir = resolveDistDir(root);
@@ -1851,12 +2186,15 @@ function prerenderPlugin(root, config) {
1851
2186
  });
1852
2187
  else {
1853
2188
  const persistDir = join(root, ".void");
2189
+ const workerDirName = resolveWorkerDirName(distDir);
2190
+ const workerDir = join(distDir, workerDirName);
2191
+ const remoteMode = resolveRemoteMode(config.remote);
1854
2192
  result = await prerenderPages({
1855
2193
  root,
1856
- workerDir: join(distDir, resolveWorkerDirName(distDir)),
2194
+ workerDir,
1857
2195
  clientDir,
1858
2196
  persistDir,
1859
- remoteMode: resolveRemoteMode(config.remote),
2197
+ remoteMode,
1860
2198
  projectId: process.env.VOID_DEPLOY_PROJECT_ID || void 0
1861
2199
  });
1862
2200
  }
@@ -2535,10 +2873,11 @@ function pushDefaultErrorPageHelpers(lines, options) {
2535
2873
  * - Middleware registered via app.use()
2536
2874
  * - Routes registered with lazy-loaded handlers and auto-conversion
2537
2875
  */
2538
- function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, ssr, options, pages = null, mergedRevalidate) {
2876
+ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, ssr, options, pages = null, mergedRevalidate, isrPages, isrPathRevalidate) {
2539
2877
  const lines = [];
2540
2878
  const auth = options.auth;
2541
2879
  const webSockets = scan.websockets ?? [];
2880
+ const managedDispatch = options.nodeTarget !== true && options.dev !== true && options.managedDispatch === true;
2542
2881
  lines.push(`import { AsyncLocalStorage } from "node:async_hooks";`);
2543
2882
  lines.push(`import { Hono } from ${JSON.stringify(options.honoPath)};`);
2544
2883
  lines.push(`import { convertReturnValue } from ${JSON.stringify(options.responsePath)};`);
@@ -2557,7 +2896,7 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
2557
2896
  if (auth.authSchemaPath) lines.push(`import { migrationSchema as __voidAuthMigrationSchema } from ${JSON.stringify(auth.authSchemaPath)};`);
2558
2897
  if (auth.authConfigPath) lines.push(`import __voidAuthConfig from ${JSON.stringify(auth.authConfigPath)};`);
2559
2898
  }
2560
- if (webSockets.length > 0 && options.webSocketRuntimePath) lines.push(`import { createWebSocketDurableObject as __createWebSocketDurableObject, handleWebSocketUpgrade as __handleWebSocketUpgrade, forwardWebSocketRequest as __forwardWebSocketRequest } from ${JSON.stringify(options.webSocketRuntimePath)};`);
2899
+ if ((webSockets.length > 0 || options.webSocketTombstones?.length) && options.webSocketRuntimePath) lines.push(`import { createWebSocketDurableObject as __createWebSocketDurableObject, createWebSocketTombstoneDurableObject as __createWebSocketTombstoneDurableObject, forwardWebSocketRequest as __forwardWebSocketRequest } from ${JSON.stringify(options.webSocketRuntimePath)};`);
2561
2900
  if (options.hasLive && options.liveRuntimePath) lines.push(`import { createLiveDurableObject as __createLiveDurableObject } from ${JSON.stringify(options.liveRuntimePath)};`);
2562
2901
  lines.push("");
2563
2902
  for (let i = 0; i < scan.middleware.length; i++) {
@@ -2642,7 +2981,7 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
2642
2981
  lines.push("");
2643
2982
  ensureMergeQueryHelperEmitted(lines);
2644
2983
  lines.push("app.use(async (c, next) => {");
2645
- if (!options.nodeTarget && options.dev !== true) {
2984
+ if (managedDispatch) {
2646
2985
  lines.push(" if (!__voidOriginalUrls.has(c.req.raw)) {");
2647
2986
  lines.push(" const __h = c.req.raw.headers.get(\"X-Void-Original-URL\");");
2648
2987
  lines.push(" if (__h != null) {");
@@ -2692,7 +3031,7 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
2692
3031
  else lines.push(" await requestContext.run({ request: c.req.raw, executionCtx: c.executionCtx }, next);");
2693
3032
  lines.push("});");
2694
3033
  lines.push("");
2695
- const emitEdgeRules = options.nodeTarget || options.dev === true;
3034
+ const emitEdgeRules = !managedDispatch;
2696
3035
  if (emitEdgeRules && options.redirectRules?.length) compileRoutingRulesMiddleware(lines, options.redirectRules, options.dev === true, options.nodeTarget === true);
2697
3036
  if (emitEdgeRules && options.headerRules?.length) compileHeaderMiddleware(lines, options.headerRules);
2698
3037
  if (auth) {
@@ -2703,6 +3042,14 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
2703
3042
  }
2704
3043
  for (let i = 0; i < scan.middleware.length; i++) lines.push(`app.use(middleware${i});`);
2705
3044
  if (scan.middleware.length > 0) lines.push("");
3045
+ for (let i = 0; i < webSockets.length; i++) {
3046
+ const route = webSockets[i];
3047
+ lines.push(`app.all(${JSON.stringify(route.pattern)}, async (c, next) => {`);
3048
+ lines.push(` if (c.req.raw.headers.get("upgrade")?.toLowerCase() !== "websocket") return next();`);
3049
+ lines.push(` return __forwardWebSocketRequest(c.req.raw, c.env, __voidWebSocketRoutes[${i}]);`);
3050
+ lines.push("});");
3051
+ }
3052
+ if (webSockets.length > 0) lines.push("");
2706
3053
  if (auth) {
2707
3054
  lines.push("app.all(\"/api/auth\", async (c) => {");
2708
3055
  lines.push(` return handleVoidAuthRequest(c, __voidAuthOptions${auth.authDatabasePath ? ", __voidAuthDatabase" : ""});`);
@@ -2760,21 +3107,10 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
2760
3107
  className: route.className,
2761
3108
  bindingName: route.bindingName
2762
3109
  })))};`);
2763
- lines.push("export async function __voidHandleWebSocket(request, env) {");
2764
- lines.push(" return __handleWebSocketUpgrade(request, env, __voidWebSocketRoutes);");
2765
- lines.push("}");
2766
- lines.push("");
2767
- for (let i = 0; i < webSockets.length; i++) {
2768
- const route = webSockets[i];
2769
- lines.push(`app.all(${JSON.stringify(route.pattern)}, async (c) => {`);
2770
- lines.push(` return __forwardWebSocketRequest(c.req.raw, c.env, __voidWebSocketRoutes[${i}]);`);
2771
- lines.push("});");
2772
- }
2773
- lines.push("");
2774
- } else {
2775
- lines.push("export async function __voidHandleWebSocket() { return null; }");
2776
3110
  lines.push("");
2777
3111
  }
3112
+ for (const className of options.webSocketTombstones ?? []) lines.push(`export const ${className} = __createWebSocketTombstoneDurableObject(${JSON.stringify(className)});`);
3113
+ if (options.webSocketTombstones?.length) lines.push("");
2778
3114
  if (options.hasLive) {
2779
3115
  lines.push("export const VoidLiveStreamDurableObject = __createLiveDurableObject();");
2780
3116
  lines.push("");
@@ -2899,7 +3235,7 @@ window.__vite_plugin_react_preamble_installed__ = true;
2899
3235
  lines.push(`app.notFound(async (c) => {`);
2900
3236
  if (options.nodeTarget && options.fallbackRules?.length) emitFallbackRulesCheck(lines, options.fallbackRules, options.dev === true, " ", options.nodeTarget === true);
2901
3237
  lines.push(` if (!shouldHandleSsrRequest(c)) {`);
2902
- if (!options.nodeTarget && options.dev === true && options.fallbackRules?.length) {
3238
+ if (!options.nodeTarget && emitEdgeRules && options.fallbackRules?.length) {
2903
3239
  lines.push(` const __assetRes = await fetchFromAssets(c);`);
2904
3240
  lines.push(` if (__assetRes.status !== 404) return __assetRes;`);
2905
3241
  emitFallbackRulesCheck(lines, options.fallbackRules, options.dev === true, " ", false);
@@ -2910,16 +3246,16 @@ window.__vite_plugin_react_preamble_installed__ = true;
2910
3246
  lines.push(` return renderSsrResponse(c, mod);`);
2911
3247
  lines.push(`});`);
2912
3248
  lines.push("");
2913
- } else if ((options.nodeTarget || options.dev === true) && options.fallbackRules?.length && !(pages && scan.pages.pages.length > 0)) {
3249
+ } else if (emitEdgeRules && (options.fallbackRules?.length || !options.nodeTarget && options.headerRules?.length) && !(pages && scan.pages.pages.length > 0)) {
2914
3250
  lines.push(`app.notFound(async (c) => {`);
2915
- if (!options.nodeTarget && options.dev === true) {
3251
+ if (!options.nodeTarget) {
2916
3252
  lines.push(` const assets = c.env?.ASSETS;`);
2917
3253
  lines.push(` if (assets && typeof assets.fetch === "function") {`);
2918
3254
  lines.push(` const __assetRes = await assets.fetch(c.req.raw);`);
2919
3255
  lines.push(` if (__assetRes.status !== 404) return __assetRes;`);
2920
3256
  lines.push(` }`);
2921
3257
  }
2922
- emitFallbackRulesCheck(lines, options.fallbackRules, options.dev === true, " ", options.nodeTarget === true);
3258
+ if (options.fallbackRules?.length) emitFallbackRulesCheck(lines, options.fallbackRules, options.dev === true, " ", options.nodeTarget === true);
2923
3259
  lines.push(` return __voidMarkNoMatch(c.req.raw, new Response("Not Found", { status: 404 }));`);
2924
3260
  lines.push(`});`);
2925
3261
  lines.push("");
@@ -2931,22 +3267,20 @@ window.__vite_plugin_react_preamble_installed__ = true;
2931
3267
  }
2932
3268
  lines.push("");
2933
3269
  lines.push("export async function scheduled(controller, env, ctx) {");
2934
- lines.push(" switch (controller.cron) {");
3270
+ lines.push(" const tasks = [];");
2935
3271
  for (let i = 0; i < scan.jobs.length; i++) {
2936
3272
  const job = scan.jobs[i];
2937
3273
  const filePath = JSON.stringify(`${jobsDir}/${job.filePath}`);
2938
- for (let j = 0; j < job.crons.length; j++) if (j < job.crons.length - 1) lines.push(` case job${i}Crons[${j}]:`);
2939
- else lines.push(` case job${i}Crons[${j}]: {`);
3274
+ lines.push(` if (job${i}Crons.includes(controller.cron)) {`);
3275
+ lines.push(" tasks.push((async () => {");
2940
3276
  lines.push(` const mod = await import(${filePath});`);
2941
- lines.push(" if (typeof mod.default === 'function') {");
2942
- lines.push(" await mod.default(controller, env, ctx);");
2943
- lines.push(" }");
2944
- lines.push(" return;");
2945
- lines.push(" }");
3277
+ lines.push(" if (typeof mod.default === 'function') await mod.default(controller, env, ctx);");
3278
+ lines.push(" })());");
3279
+ lines.push(" }");
2946
3280
  }
2947
- lines.push(" default:");
2948
- lines.push(" return;");
2949
- lines.push(" }");
3281
+ lines.push(" const results = await Promise.allSettled(tasks);");
3282
+ lines.push(" const errors = results.filter(result => result.status === \"rejected\").map(result => result.reason);");
3283
+ lines.push(" if (errors.length) throw new AggregateError(errors, \"Scheduled jobs failed\");");
2950
3284
  lines.push("}");
2951
3285
  lines.push("");
2952
3286
  } else {
@@ -2970,10 +3304,14 @@ window.__vite_plugin_react_preamble_installed__ = true;
2970
3304
  lines.push("");
2971
3305
  }
2972
3306
  if (scan.queues.length > 0) {
2973
- for (let i = 0; i < scan.queues.length; i++) lines.push(`const queue${i}Name = ${JSON.stringify(scan.queues[i].name)};`);
3307
+ lines.push(generateQueueResolverSource(scan.queues.map((queue) => queue.name)));
2974
3308
  lines.push("");
2975
3309
  lines.push("export async function __processQueue(payload, env) {");
2976
3310
  lines.push(" const queueSuffix = payload.queue;");
3311
+ lines.push(" const __queueIndex = __resolveQueueIndex(queueSuffix);");
3312
+ lines.push(" if (__queueIndex === -1) {");
3313
+ lines.push(" return { ok: false, error: `No unambiguous queue handler matches \"${String(queueSuffix)}\"` };");
3314
+ lines.push(" }");
2977
3315
  lines.push(" const __decisions = new Map();");
2978
3316
  lines.push(" function __wrapMsg(m) {");
2979
3317
  lines.push(" let decided = false;");
@@ -2992,12 +3330,14 @@ window.__vite_plugin_react_preamble_installed__ = true;
2992
3330
  lines.push(" };");
2993
3331
  lines.push(" let __ok = true;");
2994
3332
  lines.push(" try {");
3333
+ lines.push(" switch (__queueIndex) {");
2995
3334
  for (let i = 0; i < scan.queues.length; i++) {
2996
3335
  const q = scan.queues[i];
2997
- const cond = i === 0 ? "if" : "} else if";
2998
- lines.push(` ${cond} (queueSuffix === queue${i}Name || queueSuffix.endsWith("-" + queue${i}Name)) {`);
2999
- lines.push(` const mod = await import(${JSON.stringify(`${queuesDir}/${q.filePath}`)});`);
3000
- lines.push(" if (typeof mod.default === 'function') await mod.default(batch, env);");
3336
+ lines.push(` case ${i}: {`);
3337
+ lines.push(` const mod = await import(${JSON.stringify(`${queuesDir}/${q.filePath}`)});`);
3338
+ lines.push(" if (typeof mod.default === 'function') await mod.default(batch, env);");
3339
+ lines.push(" break;");
3340
+ lines.push(" }");
3001
3341
  }
3002
3342
  lines.push(" }");
3003
3343
  lines.push(" } catch { __ok = false; }");
@@ -3018,6 +3358,63 @@ window.__vite_plugin_react_preamble_installed__ = true;
3018
3358
  lines.push("app.post(\"/__void/queue\", __queueHandler);");
3019
3359
  lines.push("");
3020
3360
  }
3361
+ const email = scan.email;
3362
+ const emailDir = options.emailDir;
3363
+ if (emailDir && email && (email.addresses.length > 0 || email.defaultHandler)) {
3364
+ if (email.defaultHandler) lines.push(`const __emailDefault = () => import(${JSON.stringify(`${emailDir}/${email.defaultHandler}`)});`);
3365
+ for (let i = 0; i < email.addresses.length; i++) {
3366
+ const def = email.addresses[i];
3367
+ lines.push(`const __emailAddr${i} = () => import(${JSON.stringify(`${emailDir}/${def.filePath}`)});`);
3368
+ }
3369
+ if (email.addresses.length > 0) {
3370
+ lines.push("const __emailPatterns = [");
3371
+ for (let i = 0; i < email.addresses.length; i++) {
3372
+ const def = email.addresses[i];
3373
+ const path = `${emailDir}/${def.filePath}`;
3374
+ lines.push(` { re: new RegExp(${JSON.stringify(def.pattern.regexSource)}, "i"), params: ${JSON.stringify(def.pattern.paramNames)}, path: ${JSON.stringify(path)}, load: __emailAddr${i} },`);
3375
+ }
3376
+ lines.push("];");
3377
+ }
3378
+ lines.push("");
3379
+ lines.push("export async function __emailHandler(message, env, ctx, __opts) {");
3380
+ lines.push(" const __platformTo = message ? message.__voidPlatformTo : undefined;");
3381
+ lines.push(" const __to = String((__platformTo ?? (message && message.to)) || \"\");");
3382
+ lines.push(" const __localFull = __to.split(\"@\")[0] || \"\";");
3383
+ lines.push(" let __local = __localFull;");
3384
+ lines.push(" if (__opts && __opts.slug) {");
3385
+ lines.push(" const __plus = __localFull.indexOf(\"+\");");
3386
+ lines.push(" __local = __plus === -1 ? \"\" : __localFull.slice(__plus + 1);");
3387
+ lines.push(" }");
3388
+ if (email.addresses.length > 0) {
3389
+ lines.push(" for (const __p of __emailPatterns) {");
3390
+ lines.push(" const __m = __p.re.exec(__local);");
3391
+ lines.push(" if (!__m) continue;");
3392
+ lines.push(" const __params = {};");
3393
+ lines.push(" for (let __i = 0; __i < __p.params.length; __i++) {");
3394
+ lines.push(" __params[__p.params[__i]] = __m[__i + 1];");
3395
+ lines.push(" }");
3396
+ lines.push(" const __mod = await __p.load();");
3397
+ lines.push(" if (typeof __mod.default !== \"function\") {");
3398
+ lines.push(" throw new Error(\"email handler at \" + __p.path + \" has no default export\");");
3399
+ lines.push(" }");
3400
+ lines.push(" return await __mod.default(message, env, ctx, { params: __params });");
3401
+ lines.push(" }");
3402
+ }
3403
+ if (email.defaultHandler) {
3404
+ const defaultPath = `${emailDir}/${email.defaultHandler}`;
3405
+ lines.push(" const __dmod = await __emailDefault();");
3406
+ lines.push(" if (typeof __dmod.default !== \"function\") {");
3407
+ lines.push(` throw new Error("email handler at " + ${JSON.stringify(defaultPath)} + " has no default export");`);
3408
+ lines.push(" }");
3409
+ lines.push(" return await __dmod.default(message, env, ctx, { params: {} });");
3410
+ } else {
3411
+ lines.push(" if (typeof message.setReject === \"function\") {");
3412
+ lines.push(" message.setReject(\"no handler matched recipient \\\"\" + __to + \"\\\" — add email/_default.ts to handle unmatched mail\");");
3413
+ lines.push(" }");
3414
+ }
3415
+ lines.push("}");
3416
+ lines.push("");
3417
+ }
3021
3418
  if (options.migrationHandlerPath && (options.migrations?.length || auth)) {
3022
3419
  const d1BindingName = options.bindingNames?.d1;
3023
3420
  lines.push(`import { createMigrationHandler as __createMigrationHandler } from ${JSON.stringify(options.migrationHandlerPath)};`);
@@ -3136,6 +3533,8 @@ window.__vite_plugin_react_preamble_installed__ = true;
3136
3533
  lines.push(`export const __VOID_BUILD_KEY = ${JSON.stringify(crypto.randomUUID())};`);
3137
3534
  lines.push(`export const __VOID_DEV_TRIGGER_TOKEN = ${JSON.stringify(options.devTriggerToken ?? crypto.randomUUID())};`);
3138
3535
  lines.push(`export const __VOID_ISR_REVALIDATE = ${JSON.stringify(mergedRevalidate)};`);
3536
+ lines.push(`export const __VOID_ISR_PAGES = ${JSON.stringify(isrPages)};`);
3537
+ lines.push(`export const __VOID_ISR_PATH_REVALIDATE = ${JSON.stringify(isrPathRevalidate)};`);
3139
3538
  lines.push(`export const __VOID_ISR_DEPLOYMENT_ID = ${JSON.stringify(crypto.randomUUID())};`);
3140
3539
  lines.push("");
3141
3540
  lines.push(`export default app;`);
@@ -3540,7 +3939,7 @@ function compilePageRoutes(lines, scan, pages, options) {
3540
3939
  lines.push(` }`);
3541
3940
  lines.push(` const assetRes = await fetchPagesAsset(c);`);
3542
3941
  lines.push(` if (assetRes.status !== 404) return assetRes;`);
3543
- if ((options.nodeTarget || options.dev === true) && options.fallbackRules?.length) emitFallbackRulesCheck(lines, options.fallbackRules, options.dev === true, " ", options.nodeTarget === true);
3942
+ if (!(options.nodeTarget !== true && options.dev !== true && options.managedDispatch === true) && options.fallbackRules?.length) emitFallbackRulesCheck(lines, options.fallbackRules, options.dev === true, " ", options.nodeTarget === true);
3544
3943
  lines.push(` if (path.includes("/.")) {`);
3545
3944
  lines.push(` return serveCustomErrorPage(c, 404, "Not Found");`);
3546
3945
  lines.push(` }`);
@@ -3633,7 +4032,11 @@ ${fallthroughCode}
3633
4032
  * remote mode (`__VOID_REMOTE`) and replace local bindings with
3634
4033
  * proxy-backed versions.
3635
4034
  */
3636
- function compileWorkerEntry(hasQueues = false, remote, assetsDir = "assets", webSocketClasses = [], runtimeEnvPath = "void/_env", envSchemaModule, sandbox, serveSpaFallback = false, live, isr, notFound = "none") {
4035
+ function deriveEmailRuntimePath(runtimeEnvPath) {
4036
+ return runtimeEnvPath.replace(/_?env(\.[^/]+)?$/, (_m, ext) => "email" + (ext ?? ""));
4037
+ }
4038
+ function compileWorkerEntry(options = {}) {
4039
+ const { hasQueues = false, remote, assetsDir = "assets", webSocketClasses = [], runtimeEnvPath = "void/_env", runtimeEmailPath, envSchemaModule, sandbox, serveSpaFallback = false, hasEmail = false, live, isr, notFound = "none", durableObjects = [] } = options;
3637
4040
  const remotePath = remote?.remotePath;
3638
4041
  const remoteBindingNames = JSON.stringify(remote?.bindingNames);
3639
4042
  const createRemoteEnvCall = `createRemoteEnv(env${remote?.bindingNames ? `, ${remoteBindingNames}` : ""})`;
@@ -3649,10 +4052,13 @@ function compileWorkerEntry(hasQueues = false, remote, assetsDir = "assets", web
3649
4052
  const liveRouteImport = hasLive ? ", VoidLiveStreamDurableObject" : "";
3650
4053
  const liveRuntimeImport = hasLive ? `import { __setLiveBindingName } from "void/live";\n` : "";
3651
4054
  const liveExport = hasLive ? liveClassName === "VoidLiveStreamDurableObject" ? `export { VoidLiveStreamDurableObject };\n__setLiveBindingName(${JSON.stringify(liveBindingName)});\n` : `export { VoidLiveStreamDurableObject as ${liveClassName} };\n__setLiveBindingName(${JSON.stringify(liveBindingName)});\n` : "";
4055
+ const durableObjectImports = durableObjects.map(({ filePath }, index) => `import __voidDurableObject${index} from ${JSON.stringify(filePath)};`).join("\n");
4056
+ const durableObjectExports = durableObjects.map(({ bindingName, className }, index) => `__voidDurableObject${index}.__bind(${JSON.stringify(bindingName)});\nexport const ${className} = __voidDurableObject${index}.class;`).join("\n");
3652
4057
  const runtimeEnvImport = `import { withRuntimeEnv } from ${JSON.stringify(runtimeEnvPath)};\n`;
3653
4058
  const remoteImport = remote ? `import { createRemoteEnv } from ${JSON.stringify(remote.remotePath)};\n` : "";
3654
4059
  const envSchemaImport = envSchemaModule ? `import ${JSON.stringify(envSchemaModule.importPath)};\nimport { _validateEnvOnce } from ${JSON.stringify(envSchemaModule.helperPath)};\n` : "";
3655
4060
  const envValidateCall = envSchemaModule ? " await _validateEnvOnce(env);\n" : "";
4061
+ const fetchEnvValidateCall = envSchemaModule ? " if (request.method !== \"POST\" || new URL(request.url).pathname !== \"/__void/prerender-paths\") await _validateEnvOnce(env);\n" : "";
3656
4062
  const exactNames = isr ? INTERNAL_BINDING_EXACT_NAMES : [];
3657
4063
  const prefixCheck = [...INTERNAL_BINDING_PREFIXES.map((p) => `prop.startsWith(${JSON.stringify(p)})`), ...exactNames.map((n) => `prop === ${JSON.stringify(n)}`)].join(" || ");
3658
4064
  const filterBindingsHelper = `
@@ -3679,19 +4085,17 @@ function __filterInternalBindings(env) {
3679
4085
  }
3680
4086
  `;
3681
4087
  const assetHelper = compileAssetFallbackHelper(assetsDir, serveSpaFallback, notFound);
3682
- const isrImport = isr ? `import { handleIsr } from ${JSON.stringify(isr.runtimePath)};\nimport { __VOID_ISR_REVALIDATE, __VOID_ISR_DEPLOYMENT_ID } from 'virtual:void-routes';\n` : "";
4088
+ const isrImport = isr ? `import { handleIsr } from ${JSON.stringify(isr.runtimePath)};\nimport { __VOID_ISR_REVALIDATE, __VOID_ISR_PAGES, __VOID_ISR_PATH_REVALIDATE, __VOID_ISR_DEPLOYMENT_ID } from 'virtual:void-routes';\n` : "";
3683
4089
  const isrEnvCapture = isr ? ` env.__VOID_ISR_DEPLOYMENT_ID ??= __VOID_ISR_DEPLOYMENT_ID;\n env.__VOID_ISR_HOST = new URL(request.url).hostname;\n` : "";
3684
4090
  const isrDeploymentStamp = isr ? ` env.__VOID_ISR_DEPLOYMENT_ID ??= __VOID_ISR_DEPLOYMENT_ID;\n` : "";
3685
4091
  const renderBlock = isr ? ` const __render = async (req) => serveWithAssets(req, env, await app.fetch(req, userEnv, ctx));
3686
- if (env.ISR_CACHE) {
3687
- const __isr = await handleIsr(request, env, ctx, { revalidate: __VOID_ISR_REVALIDATE, deploymentId: __VOID_ISR_DEPLOYMENT_ID }, __render);
4092
+ if (env.ISR_CACHE && request.headers.get("upgrade")?.toLowerCase() !== "websocket") {
4093
+ const __isr = await handleIsr(request, env, ctx, { revalidate: __VOID_ISR_REVALIDATE, pages: __VOID_ISR_PAGES, pathRevalidate: __VOID_ISR_PATH_REVALIDATE, deploymentId: __VOID_ISR_DEPLOYMENT_ID, queryAllowlist: ${JSON.stringify(isr?.queryAllowlist)} }, __render);
3688
4094
  if (__isr) return __isr;
3689
4095
  }
3690
4096
  return __render(request);` : ` return serveWithAssets(request, env, await app.fetch(request, userEnv, ctx));`;
3691
4097
  const fetchHandler = remotePath ? `async (request, env, ctx) => {
3692
- ${envValidateCall} const ws = await __voidHandleWebSocket(request, env);
3693
- if (ws) return ws;
3694
- if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};
4098
+ ${fetchEnvValidateCall} if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};
3695
4099
  ${isrEnvCapture} return __withRawEnv(env, () => {
3696
4100
  const userEnv = __filterInternalBindings(env);
3697
4101
  return withRuntimeEnv(env, async () => {
@@ -3701,9 +4105,7 @@ ${renderBlock}
3701
4105
  });
3702
4106
  });
3703
4107
  }` : `async (request, env, ctx) => {
3704
- ${envValidateCall} const ws = await __voidHandleWebSocket(request, env);
3705
- if (ws) return ws;
3706
- ${isrEnvCapture} return __withRawEnv(env, () => {
4108
+ ${fetchEnvValidateCall}${isrEnvCapture} return __withRawEnv(env, () => {
3707
4109
  const userEnv = __filterInternalBindings(env);
3708
4110
  return withRuntimeEnv(env, async () => {
3709
4111
  const internal = await __handleInternal(request, env, ctx);
@@ -3740,11 +4142,42 @@ ${isrDeploymentStamp} const userEnv = __filterInternalBindings(env);
3740
4142
  ${envValidateCall}${isrDeploymentStamp} const userEnv = __filterInternalBindings(env);
3741
4143
  return withRuntimeEnv(env, () => scheduled(controller, userEnv, ctx));
3742
4144
  }`;
3743
- if (hasQueues) return `${sandboxImport}${liveRuntimeImport}import app, { scheduled, __voidCollectPrerenderPaths, __voidHandleInternalRoute, __voidHandleWebSocket, __withRawEnv, __processQueue${webSocketClasses.length > 0 ? `, ${webSocketClasses.join(", ")}` : ""}${liveRouteImport} } from 'virtual:void-routes';
3744
- ${runtimeEnvImport}${envSchemaImport}${remoteImport}${isrImport}${filterBindingsHelper}${assetHelper}
4145
+ const emailImport = hasEmail ? ", __emailHandler" : "";
4146
+ const emailRuntimePath = runtimeEmailPath ?? deriveEmailRuntimePath(runtimeEnvPath);
4147
+ const emailProxyImport = hasEmail ? `import { createMessageProxy as __createMessageProxy, enactEmailActions as __enactEmailActions, isForwardableEmailMessage as __isForwardableEmailMessage } from ${JSON.stringify(emailRuntimePath)};\n` : "";
4148
+ const emailHandler = hasEmail ? remotePath ? `async function handleEmail(envelope, env, ctx) {
4149
+ ${envValidateCall} if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};
4150
+ const userEnv = __filterInternalBindings(env);
4151
+ const __relayed = typeof envelope.viaDomain === "string" && envelope.viaDomain.length > 0;
4152
+ const __slug = __relayed ? undefined : (typeof env.__VOID_PROJECT_SLUG === "string" && env.__VOID_PROJECT_SLUG.length > 0 ? env.__VOID_PROJECT_SLUG : undefined);
4153
+ const { message, getActions } = __createMessageProxy(envelope, { slug: __slug });
4154
+ await __withRawEnv(env, () => withRuntimeEnv(env, () => __emailHandler(message, userEnv, ctx, { slug: __slug })));
4155
+ const __actions = getActions();
4156
+ if (__isForwardableEmailMessage(envelope)) await __enactEmailActions(envelope, __actions);
4157
+ return __actions;
4158
+ }` : `async function handleEmail(envelope, env, ctx) {
4159
+ ${envValidateCall} const userEnv = __filterInternalBindings(env);
4160
+ const __relayed = typeof envelope.viaDomain === "string" && envelope.viaDomain.length > 0;
4161
+ const __slug = __relayed ? undefined : (typeof env.__VOID_PROJECT_SLUG === "string" && env.__VOID_PROJECT_SLUG.length > 0 ? env.__VOID_PROJECT_SLUG : undefined);
4162
+ const { message, getActions } = __createMessageProxy(envelope, { slug: __slug });
4163
+ await __withRawEnv(env, () => withRuntimeEnv(env, () => __emailHandler(message, userEnv, ctx, { slug: __slug })));
4164
+ const __actions = getActions();
4165
+ if (__isForwardableEmailMessage(envelope)) await __enactEmailActions(envelope, __actions);
4166
+ return __actions;
4167
+ }` : "";
4168
+ const entryImport = `import { WorkerEntrypoint } from "cloudflare:workers";\n`;
4169
+ const fetchDeclaration = `const __voidFetch = ${fetchHandler};`;
4170
+ const entryClass = `export default class extends WorkerEntrypoint {
4171
+ async fetch(request) { return __voidFetch(request, this.env, this.ctx); }
4172
+ async scheduled(controller) { return handleScheduled(controller, this.env, this.ctx); }
4173
+ ${hasQueues ? ` async queue(batch) { return __voidQueue(batch, this.env, this.ctx); }\n` : ""}${hasEmail ? ` async email(envelope) { return handleEmail(envelope, this.env, this.ctx); }\n` : ""}}`;
4174
+ if (hasQueues) return `${sandboxImport}${entryImport}${liveRuntimeImport}import app, { scheduled, __voidCollectPrerenderPaths, __voidHandleInternalRoute, __withRawEnv, __processQueue${emailImport}${webSocketClasses.length > 0 ? `, ${webSocketClasses.join(", ")}` : ""}${liveRouteImport} } from 'virtual:void-routes';
4175
+ ${durableObjectImports}\n${runtimeEnvImport}${emailProxyImport}${envSchemaImport}${remoteImport}${isrImport}${filterBindingsHelper}${assetHelper}
3745
4176
  ${internalRouteHelper}
4177
+ ${fetchDeclaration}
3746
4178
  ${scheduledHandler}
3747
- async function queue(batch, env, ctx) {
4179
+ ${emailHandler}
4180
+ async function __voidQueue(batch, env, ctx) {
3748
4181
  ${envValidateCall}${remotePath ? ` if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};\n` : ""}${isrDeploymentStamp} const userEnv = __filterInternalBindings(env);
3749
4182
  const messages = [...batch.messages].map(m => ({
3750
4183
  id: m.id, timestamp: m.timestamp.toISOString(),
@@ -3768,15 +4201,19 @@ ${envValidateCall}${remotePath ? ` if (env.__VOID_REMOTE === "1") env = ${creat
3768
4201
  ${webSocketClasses.map((name) => `export { ${name} };`).join("\n")}
3769
4202
  ${liveExport}
3770
4203
  ${sandboxExport}
3771
- export default { fetch: ${fetchHandler}, scheduled: handleScheduled, queue };`;
3772
- return `${sandboxImport}${liveRuntimeImport}import app, { scheduled, __voidCollectPrerenderPaths, __voidHandleInternalRoute, __voidHandleWebSocket, __withRawEnv${webSocketClasses.length > 0 ? `, ${webSocketClasses.join(", ")}` : ""}${liveRouteImport} } from 'virtual:void-routes';
3773
- ${runtimeEnvImport}${envSchemaImport}${remoteImport}${isrImport}${filterBindingsHelper}${assetHelper}
4204
+ ${durableObjectExports}
4205
+ ${entryClass}`;
4206
+ return `${sandboxImport}${entryImport}${liveRuntimeImport}import app, { scheduled, __voidCollectPrerenderPaths, __voidHandleInternalRoute, __withRawEnv${emailImport}${webSocketClasses.length > 0 ? `, ${webSocketClasses.join(", ")}` : ""}${liveRouteImport} } from 'virtual:void-routes';
4207
+ ${durableObjectImports}\n${runtimeEnvImport}${emailProxyImport}${envSchemaImport}${remoteImport}${isrImport}${filterBindingsHelper}${assetHelper}
3774
4208
  ${internalRouteHelper}
4209
+ ${fetchDeclaration}
3775
4210
  ${scheduledHandler}
4211
+ ${emailHandler}
3776
4212
  ${webSocketClasses.map((name) => `export { ${name} };`).join("\n")}
3777
4213
  ${liveExport}
3778
4214
  ${sandboxExport}
3779
- export default { fetch: ${fetchHandler}, scheduled: handleScheduled };`;
4215
+ ${durableObjectExports}
4216
+ ${entryClass}`;
3780
4217
  }
3781
4218
  /**
3782
4219
  * Generate the app module — exports the Hono app with static asset serving.
@@ -3792,11 +4229,17 @@ export default { fetch: ${fetchHandler}, scheduled: handleScheduled };`;
3792
4229
  function compileNodeApp(target, honoPath, envSchemaModule) {
3793
4230
  const staticImport = target === "bun" ? `import { serveStatic } from "hono/bun";` : target === "deno" ? `import { serveStatic } from "hono/deno";` : `import { serveStatic } from "@hono/node-server/serve-static";`;
3794
4231
  const envSchemaImport = envSchemaModule ? `import ${JSON.stringify(envSchemaModule.importPath)};\nimport { _validateEnvOnce } from ${JSON.stringify(envSchemaModule.helperPath)};\n` : "";
3795
- const envMiddleware = envSchemaModule ? `app.use("*", async (c, next) => { await _validateEnvOnce(${target === "deno" ? "Deno.env.toObject()" : "process.env"}); return next(); });\n` : "";
4232
+ 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` : "";
3796
4233
  return `${staticImport}
4234
+ import { fileURLToPath } from "node:url";
3797
4235
  import { Hono } from ${JSON.stringify(honoPath)};
3798
4236
  import innerApp, { __voidCollectPrerenderPaths } from "virtual:void-routes";
3799
4237
  ${envSchemaImport}
4238
+ // Resolved by nodeTargetPlugin for this emitted chunk, including shared chunks.
4239
+ const clientRoot = fileURLToPath(import.meta.voidClientDirectory);
4240
+ const assetApp = new Hono();
4241
+ assetApp.get("*", serveStatic({ root: clientRoot }));
4242
+ const assets = { fetch: (request) => assetApp.fetch(request) };
3800
4243
  const app = new Hono();
3801
4244
  ${envMiddleware}app.post("/__void/prerender-paths", async (c) => {
3802
4245
  const __expected = c.env?.__VOID_PROXY_TOKEN;
@@ -3807,8 +4250,8 @@ ${envMiddleware}app.post("/__void/prerender-paths", async (c) => {
3807
4250
  const paths = await __voidCollectPrerenderPaths(c.req.raw);
3808
4251
  return c.json({ paths });
3809
4252
  });
3810
- app.use("/assets/*", serveStatic({ root: "./client" }));
3811
- app.all("*", (c) => innerApp.fetch(c.req.raw));
4253
+ app.get("/assets/*", (c) => assets.fetch(c.req.raw));
4254
+ app.all("*", (c) => innerApp.fetch(c.req.raw, { ...c.env, ASSETS: assets }));
3812
4255
  export default app;`;
3813
4256
  }
3814
4257
  /**
@@ -4260,6 +4703,7 @@ function routingPlugin(root, options) {
4260
4703
  const middlewareDir = paths.middlewareDir;
4261
4704
  const jobsDir = paths.cronsDir;
4262
4705
  const queuesDir = paths.queuesDir;
4706
+ const emailDir = paths.emailDir;
4263
4707
  const pagesDir = paths.pagesDir;
4264
4708
  const ssrDir = paths.ssrDir;
4265
4709
  const authConfigPaths = new Set(paths.authConfigCandidates.map((path) => normalizePath(path)));
@@ -4268,9 +4712,14 @@ function routingPlugin(root, options) {
4268
4712
  const normalizedMiddlewareDir = normalizePath(middlewareDir);
4269
4713
  const normalizedJobsDir = normalizePath(jobsDir);
4270
4714
  const normalizedQueuesDir = normalizePath(queuesDir);
4715
+ const normalizedEmailDir = normalizePath(emailDir);
4271
4716
  const normalizedPagesDir = normalizePath(pagesDir);
4272
4717
  const normalizedSsrDir = normalizePath(ssrDir);
4273
4718
  const ssrEntryAbsPaths = new Set([...SSR_SERVER_ENTRY_RELATIVE_PATHS, ...SSR_CLIENT_ENTRY_RELATIVE_PATHS].map((path) => normalizePath(join(ssrDir, path.split("/").pop()))));
4719
+ function hasEmailHandlers() {
4720
+ const email = scan?.email;
4721
+ return Boolean(email && (email.addresses.length > 0 || email.defaultHandler));
4722
+ }
4274
4723
  function isInDirectory(path, directory) {
4275
4724
  return path === directory || path.startsWith(directory + "/");
4276
4725
  }
@@ -4329,16 +4778,20 @@ function routingPlugin(root, options) {
4329
4778
  responsePath: resolveFile("#self/response"),
4330
4779
  handlerPath: resolveFile("#self/handler"),
4331
4780
  runtimeEnvPath: resolveFile("#self/_env"),
4781
+ runtimeEmailPath: resolveFile("#self/email"),
4782
+ emailDir,
4332
4783
  projectRoot: root,
4333
4784
  homeDir: homedir(),
4334
4785
  webSocketRuntimePath: resolveFile("#self/runtime/ws-server"),
4335
4786
  liveRuntimePath: resolveFile("#self/runtime/live-server"),
4336
4787
  hasLive: options.live !== void 0,
4788
+ webSocketTombstones: options.websocketTombstones,
4337
4789
  reactRefresh: false,
4338
4790
  dev: false,
4339
4791
  devTriggerToken: options.devTriggerToken,
4340
4792
  auth: null,
4341
4793
  nodeTarget: !!options.target,
4794
+ managedDispatch: false,
4342
4795
  explicitPrerenderPaths: options.prerenderPaths,
4343
4796
  bindingHandlerPath: options.target ? void 0 : resolveFile("#self/remote/binding-handler"),
4344
4797
  bindingNames: options.bindingNames
@@ -4366,7 +4819,7 @@ function routingPlugin(root, options) {
4366
4819
  const config = readConfig(root);
4367
4820
  const dialect = getDatabaseDialect(config);
4368
4821
  compileOptions.auth = {
4369
- authRuntimePath: dialect === "postgresql" ? join(runtimeDir, "better-auth-pg.mjs") : join(runtimeDir, "better-auth.mjs"),
4822
+ authRuntimePath: dialect === "postgresql" ? join(runtimeDir, "better-auth-pg.mjs") : dialect === "mysql" ? join(runtimeDir, "better-auth-mysql.mjs") : join(runtimeDir, "better-auth.mjs"),
4370
4823
  authConfigPath,
4371
4824
  authDatabasePath: null,
4372
4825
  authSchemaPath: null,
@@ -4381,7 +4834,10 @@ function routingPlugin(root, options) {
4381
4834
  providers: compileOptions.auth.providers,
4382
4835
  userConfig: await loadVoidAuthConfig(compileOptions.auth.authConfigPath)
4383
4836
  };
4384
- const env = loadEnv(resolvedConfig?.mode ?? "development", root, "");
4837
+ const env = {
4838
+ ...resolvedConfig?.command === "serve" ? loadLocalEnv(root) : {},
4839
+ ...process.env
4840
+ };
4385
4841
  const schemaPath = join(outDir, "better-auth-schema.ts");
4386
4842
  const databasePath = join(outDir, "better-auth-db.ts");
4387
4843
  writeFileIfChanged(schemaPath, generateVoidAuthDrizzleSchema(authRuntime, env));
@@ -4395,6 +4851,8 @@ function routingPlugin(root, options) {
4395
4851
  }
4396
4852
  let resolvedConfig = null;
4397
4853
  let isrRevalidate;
4854
+ let isrPages;
4855
+ let isrPathRevalidate;
4398
4856
  function resolveAuthClientPath(config) {
4399
4857
  if (config.plugins.some((plugin) => plugin.name === "void-react:pages")) return explicitAuthClientPaths.react;
4400
4858
  if (config.plugins.some((plugin) => plugin.name === "void-vue:pages")) return explicitAuthClientPaths.vue;
@@ -4475,11 +4933,20 @@ function routingPlugin(root, options) {
4475
4933
  if (options.target) {
4476
4934
  writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(options.target, compileOptions.honoPath, getEnvSchemaModule()));
4477
4935
  writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
4478
- } else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry(false, remote, void 0, void 0, compileOptions.runtimeEnvPath, getEnvSchemaModule(), options.hasSandbox ? {
4479
- bindingName: options.sandboxBindingName,
4480
- className: options.sandboxClassName,
4481
- runtimePath: sandboxRuntimePath
4482
- } : false, void 0, options.live));
4936
+ } else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
4937
+ remote,
4938
+ webSocketClasses: options.websocketTombstones,
4939
+ runtimeEnvPath: compileOptions.runtimeEnvPath,
4940
+ runtimeEmailPath: compileOptions.runtimeEmailPath,
4941
+ envSchemaModule: getEnvSchemaModule(),
4942
+ sandbox: options.hasSandbox ? {
4943
+ bindingName: options.sandboxBindingName,
4944
+ className: options.sandboxClassName,
4945
+ runtimePath: sandboxRuntimePath
4946
+ } : false,
4947
+ live: options.live,
4948
+ durableObjects: options.durableObjects
4949
+ }));
4483
4950
  return {
4484
4951
  name: "void:routing",
4485
4952
  enforce: "pre",
@@ -4496,6 +4963,7 @@ function routingPlugin(root, options) {
4496
4963
  },
4497
4964
  config(_, env) {
4498
4965
  routeEnvironment = env.command === "serve" && !env.isPreview ? "dev" : "prod";
4966
+ compileOptions.managedDispatch = !options.target && env.command === "build" && Boolean(process.env.VOID_DEPLOY_PROJECT_ID);
4499
4967
  if (env.command === "serve" && options.isAuthEnabled()) return { resolve: { alias: [
4500
4968
  "client",
4501
4969
  "react",
@@ -4529,11 +4997,11 @@ function routingPlugin(root, options) {
4529
4997
  if (!options.target) {
4530
4998
  const migrationsDir = paths.migrationsDir;
4531
4999
  if (existsSync(migrationsDir) || compileOptions.auth) {
4532
- const { collectMigrations } = await import("./validate-Cw_RLeTj.mjs").then((n) => n.l);
4533
- const { buildMigrationPlanBundle } = await import("./validate-Cw_RLeTj.mjs").then((n) => n.s);
5000
+ const { collectMigrations } = await import("./validate-tBBN_dXH.mjs").then((n) => n.l);
5001
+ const { buildMigrationPlanBundle } = await import("./validate-tBBN_dXH.mjs").then((n) => n.s);
4534
5002
  const migrations = existsSync(migrationsDir) ? collectMigrations(migrationsDir) : [];
4535
5003
  const dialect = getDatabaseDialect(readConfig(root));
4536
- compileOptions.migrationHandlerPath = resolveFile(dialect === "postgresql" ? "#self/runtime/migration-handler-pg" : "#self/runtime/migration-handler");
5004
+ compileOptions.migrationHandlerPath = resolveFile(dialect === "postgresql" ? "#self/runtime/migration-handler-pg" : dialect === "mysql" ? "#self/runtime/migration-handler-mysql" : "#self/runtime/migration-handler");
4537
5005
  compileOptions.migrations = migrations;
4538
5006
  compileOptions.migrationPlanBundle = dialect === "sqlite" ? buildMigrationPlanBundle(migrations) : void 0;
4539
5007
  compileOptions.migrationDialect = dialect;
@@ -4543,15 +5011,37 @@ function routingPlugin(root, options) {
4543
5011
  writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(options.target, compileOptions.honoPath, getEnvSchemaModule()));
4544
5012
  writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
4545
5013
  } else {
4546
- const merged = collectRevalidate(readConfig(root), scan.pages);
5014
+ const buildConfig = readConfig(root);
5015
+ const merged = collectRevalidate(buildConfig, scan.pages);
5016
+ const configuredRevalidate = buildConfig.routing?.revalidate;
4547
5017
  const emitIsr = !options.target && resolvedConfig?.command === "build" && !process.env.VOID_DEPLOY_PROJECT_ID && hasRevalidate(merged);
4548
5018
  isrRevalidate = emitIsr ? merged : void 0;
4549
- const isr = emitIsr ? { runtimePath: resolveFile("#self/runtime/isr-cache") } : void 0;
4550
- writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry(scan.queues.length > 0, remote, resolvedConfig?.build.assetsDir, (scan.websockets ?? []).map((route) => route.className), compileOptions.runtimeEnvPath, getEnvSchemaModule(), options.hasSandbox ? {
4551
- bindingName: options.sandboxBindingName,
4552
- className: options.sandboxClassName,
4553
- runtimePath: sandboxRuntimePath
4554
- } : false, shouldServeSpaFallback(), options.live, isr, currentNotFoundHandling()));
5019
+ isrPages = emitIsr ? collectPages(scan.pages, configuredRevalidate) : void 0;
5020
+ isrPathRevalidate = emitIsr && configuredRevalidate && typeof configuredRevalidate === "object" ? configuredRevalidate : void 0;
5021
+ const isr = emitIsr ? {
5022
+ runtimePath: resolveFile("@void/isr/runtime"),
5023
+ queryAllowlist: buildConfig.routing?.revalidateQueryAllowlist
5024
+ } : void 0;
5025
+ writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
5026
+ hasQueues: scan.queues.length > 0,
5027
+ remote,
5028
+ assetsDir: resolvedConfig?.build.assetsDir,
5029
+ webSocketClasses: [...(scan.websockets ?? []).map((route) => route.className), ...options.websocketTombstones ?? []],
5030
+ runtimeEnvPath: compileOptions.runtimeEnvPath,
5031
+ runtimeEmailPath: compileOptions.runtimeEmailPath,
5032
+ envSchemaModule: getEnvSchemaModule(),
5033
+ sandbox: options.hasSandbox ? {
5034
+ bindingName: options.sandboxBindingName,
5035
+ className: options.sandboxClassName,
5036
+ runtimePath: sandboxRuntimePath
5037
+ } : false,
5038
+ hasEmail: hasEmailHandlers(),
5039
+ serveSpaFallback: shouldServeSpaFallback(),
5040
+ live: options.live,
5041
+ isr,
5042
+ notFound: currentNotFoundHandling(),
5043
+ durableObjects: options.durableObjects
5044
+ }));
4555
5045
  }
4556
5046
  },
4557
5047
  resolveId: {
@@ -4576,14 +5066,14 @@ function routingPlugin(root, options) {
4576
5066
  }
4577
5067
  if (id !== VIRTUAL_ROUTES_ID) return;
4578
5068
  if (!scan) await rescan();
4579
- return compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, ssr, compileOptions, getPagesCompileOptions(), isrRevalidate);
5069
+ return compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, ssr, compileOptions, getPagesCompileOptions(), isrRevalidate, isrPages, isrPathRevalidate);
4580
5070
  }
4581
5071
  },
4582
5072
  configureServer(devServer) {
4583
5073
  const watcher = devServer.watcher;
4584
5074
  const handleFileChange = async (path, event) => {
4585
5075
  const normalizedPath = normalizePath(path);
4586
- const sourceConventionChanged = isInDirectory(normalizedPath, normalizedRoutesDir) || isInDirectory(normalizedPath, normalizedMiddlewareDir) || isInDirectory(normalizedPath, normalizedJobsDir) || isInDirectory(normalizedPath, normalizedQueuesDir) || isInDirectory(normalizedPath, normalizedPagesDir) || isPotentialAuthConfigPath(normalizedPath) || isPotentialEnvFilePath(normalizedPath);
5076
+ const sourceConventionChanged = isInDirectory(normalizedPath, normalizedRoutesDir) || isInDirectory(normalizedPath, normalizedMiddlewareDir) || isInDirectory(normalizedPath, normalizedJobsDir) || isInDirectory(normalizedPath, normalizedQueuesDir) || isInDirectory(normalizedPath, normalizedEmailDir) || isInDirectory(normalizedPath, normalizedPagesDir) || isPotentialAuthConfigPath(normalizedPath) || isPotentialEnvFilePath(normalizedPath);
4587
5077
  if (!sourceConventionChanged && !isInDirectory(normalizedPath, normalizedSsrDir) && !isPotentialEnvFilePath(normalizedPath)) return;
4588
5078
  if (!sourceConventionChanged && isInDirectory(normalizedPath, normalizedSsrDir) && !ssrEntryAbsPaths.has(normalizedPath)) return;
4589
5079
  await rescan();
@@ -4598,11 +5088,26 @@ function routingPlugin(root, options) {
4598
5088
  if (options.target) {
4599
5089
  writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(options.target, compileOptions.honoPath, getEnvSchemaModule()));
4600
5090
  writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
4601
- } else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry(scan.queues.length > 0, remote, resolvedConfig?.build.assetsDir, (scan.websockets ?? []).map((route) => route.className), compileOptions.runtimeEnvPath, getEnvSchemaModule(), options.hasSandbox ? {
4602
- bindingName: options.sandboxBindingName,
4603
- className: options.sandboxClassName,
4604
- runtimePath: sandboxRuntimePath
4605
- } : false, shouldServeSpaFallback(), options.live, void 0, currentNotFoundHandling()));
5091
+ } else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
5092
+ hasQueues: scan.queues.length > 0,
5093
+ remote,
5094
+ assetsDir: resolvedConfig?.build.assetsDir,
5095
+ webSocketClasses: [...(scan.websockets ?? []).map((route) => route.className), ...options.websocketTombstones ?? []],
5096
+ runtimeEnvPath: compileOptions.runtimeEnvPath,
5097
+ runtimeEmailPath: compileOptions.runtimeEmailPath,
5098
+ envSchemaModule: getEnvSchemaModule(),
5099
+ sandbox: options.hasSandbox ? {
5100
+ bindingName: options.sandboxBindingName,
5101
+ className: options.sandboxClassName,
5102
+ runtimePath: sandboxRuntimePath
5103
+ } : false,
5104
+ hasEmail: hasEmailHandlers(),
5105
+ serveSpaFallback: shouldServeSpaFallback(),
5106
+ live: options.live,
5107
+ isr: void 0,
5108
+ notFound: currentNotFoundHandling(),
5109
+ durableObjects: options.durableObjects
5110
+ }));
4606
5111
  const structural = event !== "change";
4607
5112
  const pageManifestChanged = isInDirectory(normalizedPath, normalizedPagesDir) && (structural || normalizedPath.endsWith(".md"));
4608
5113
  for (const env of Object.values(devServer.environments)) {
@@ -4660,6 +5165,13 @@ function routingPlugin(root, options) {
4660
5165
  * The token is local to the project and only honored by a worker bound to
4661
5166
  * localhost; treat it like the rest of `.void/` — disposable scratch state,
4662
5167
  * safe to remove manually if desired.
5168
+ *
5169
+ * The file is owner-only (0600). It is the one credential the dev-inbox
5170
+ * routes accept, and loopback is reachable by every account on the host,
5171
+ * so a default-umask 0644 file would hand any other local user the raw MIME,
5172
+ * the DELETE, and the capture endpoint. A file persisted by an earlier
5173
+ * release is tightened in place, never rotated. Windows has no group/other
5174
+ * bits — `mode` and `chmod` only toggle write there — so it is left alone.
4663
5175
  */
4664
5176
  /**
4665
5177
  * Best-effort detection of `vite build` (and `vite preview`) so we can skip
@@ -4674,6 +5186,7 @@ function isViteBuildOrPreview() {
4674
5186
  const subcommand = process.argv[2];
4675
5187
  return subcommand === "build" || subcommand === "preview";
4676
5188
  }
5189
+ const DEV_TRIGGER_TOKEN_FILE_MODE = 384;
4677
5190
  function getOrCreateDevTriggerToken(root) {
4678
5191
  const tokenPath = join(root, ".void", "dev-trigger-token");
4679
5192
  const readExisting = () => {
@@ -4684,12 +5197,21 @@ function getOrCreateDevTriggerToken(root) {
4684
5197
  return;
4685
5198
  }
4686
5199
  };
5200
+ const tightenExisting = () => {
5201
+ if (process.platform === "win32") return;
5202
+ try {
5203
+ if ((statSync(tokenPath).mode & 63) !== 0) chmodSync(tokenPath, DEV_TRIGGER_TOKEN_FILE_MODE);
5204
+ } catch {}
5205
+ };
4687
5206
  const existing = readExisting();
4688
- if (existing) return existing;
5207
+ if (existing) {
5208
+ tightenExisting();
5209
+ return existing;
5210
+ }
4689
5211
  mkdirSync(join(root, ".void"), { recursive: true });
4690
5212
  const fresh = crypto.randomUUID();
4691
5213
  try {
4692
- const fd = openSync(tokenPath, "wx");
5214
+ const fd = openSync(tokenPath, "wx", DEV_TRIGGER_TOKEN_FILE_MODE);
4693
5215
  try {
4694
5216
  writeSync(fd, fresh);
4695
5217
  } finally {
@@ -4773,7 +5295,7 @@ function generateClassATriggerWrapper(root, framework, original, isBuild, assets
4773
5295
  const outDir = join(root, ".void");
4774
5296
  mkdirSync(outDir, { recursive: true });
4775
5297
  const wrapperPath = join(outDir, `${framework}-trigger-wrapper.ts`);
4776
- writeFileIfChanged(wrapperPath, isBuild ? `// @ts-nocheck — Auto-generated by Void for vite build. Do NOT edit.
5298
+ const body = isBuild ? `// @ts-nocheck — Auto-generated by Void for vite build. Do NOT edit.
4777
5299
  import original from ${JSON.stringify(original)};
4778
5300
 
4779
5301
  export default {
@@ -4795,7 +5317,8 @@ export default {
4795
5317
  return original.fetch(request, env, ctx);
4796
5318
  },
4797
5319
  };
4798
- `);
5320
+ `;
5321
+ writeFileIfChanged(wrapperPath, body);
4799
5322
  return wrapperPath;
4800
5323
  }
4801
5324
  /** Generate a worker entry for vinext Pages Router projects. */
@@ -4853,16 +5376,49 @@ function hasBinding(list, name) {
4853
5376
  /**
4854
5377
  * Decide whether to inject a Workers AI binding into the built wrangler config.
4855
5378
  *
4856
- * Build-only: dev/miniflare has no local Workers AI, so serve must never bind
4857
- * `env.AI` (dev uses the P2 `__VOID_TOKEN` proxy path instead).
5379
+ * Builds use the production binding. During dev, a project explicitly saved
5380
+ * with the direct Cloudflare destination uses Cloudflare's remote binding;
5381
+ * managed and unsaved projects keep using the Void HTTPS proxy.
4858
5382
  *
4859
5383
  * `!resolvedAi` is load-bearing: mergeBindings' return is defu-merged over the
4860
5384
  * resolved root-wrangler config and wins, so skipping when the user already
4861
5385
  * declared an `ai` binding avoids clobbering theirs. `!resultAi` is harmless
4862
5386
  * defense-in-depth (nothing else in mergeBindings sets `result.ai`).
4863
5387
  */
4864
- function shouldBindWorkersAi(command, needsAI, resolvedAi, resultAi) {
4865
- return needsAI && command === "build" && !resultAi && !resolvedAi;
5388
+ function shouldBindWorkersAi(command, needsAI, resolvedAi, resultAi, directCloudflareDev = false) {
5389
+ return needsAI && (command === "build" || command === "serve" && directCloudflareDev) && !resultAi && !resolvedAi;
5390
+ }
5391
+ /**
5392
+ * Decide whether to inject the `void dev` email-inbox loopback var.
5393
+ *
5394
+ * The dev inbox buffer lives in the Vite dev server's Node process;
5395
+ * `sendEmail` runs in workerd and reaches it by fetching the inbox capture
5396
+ * endpoint through the worker's assets binding, which
5397
+ * `@cloudflare/vite-plugin` wires straight into the dev server's middleware
5398
+ * stack. The var carries that binding's NAME (`ASSETS` for a Void app, or
5399
+ * whatever a Class A framework's own wrangler config declares) and its
5400
+ * presence is what arms the dev path at runtime — so it must never reach a
5401
+ * production build's `wrangler.json`, exactly like the `__VOID_TOKEN`
5402
+ * credential gate.
5403
+ *
5404
+ * Gated on `routeEnvironment`, not on `command`: `vite preview` also reports
5405
+ * `command: 'serve'`, but it runs the BUILT worker whose assets binding is a
5406
+ * real asset directory rather than a dev-server loopback. `routeEnvironment`
5407
+ * is the existing `command === 'serve' && !isPreview` distinction.
5408
+ *
5409
+ * Without an assets binding there is no loopback, so injecting the var would
5410
+ * only arm a path that must then fail; gate on it instead.
5411
+ */
5412
+ function shouldInjectEmailDevInboxVar(routeEnvironment, needsEmail, assetsBinding) {
5413
+ return routeEnvironment === "dev" && !!needsEmail && typeof assetsBinding === "string" && !!assetsBinding;
5414
+ }
5415
+ /** Resolve the runtime Workers AI binding, preserving a root Wrangler declaration. */
5416
+ function resolveWorkersAiBindingName(resolvedAi, configuredName) {
5417
+ if (typeof resolvedAi === "object" && resolvedAi !== null) {
5418
+ const binding = resolvedAi.binding;
5419
+ if (typeof binding === "string") return binding;
5420
+ }
5421
+ return configuredName ?? "AI";
4866
5422
  }
4867
5423
  const CLOUDFLARE_BUILTIN_IMPORT = /^cloudflare:/;
4868
5424
  const SANDBOX_SDK_IMPORT = "@cloudflare/sandbox";
@@ -4959,9 +5515,11 @@ function readVoidDevCredentials(root) {
4959
5515
  const token = getToken(root);
4960
5516
  if (!token) return { status: "no-token" };
4961
5517
  const staging = isStagingMode(root) || void 0;
5518
+ const apiUrl = getApiUrl(root);
4962
5519
  const project = readProjectConfig(root);
4963
5520
  if (!project) return {
4964
5521
  status: "no-project",
5522
+ apiUrl,
4965
5523
  token,
4966
5524
  staging
4967
5525
  };
@@ -4969,22 +5527,21 @@ function readVoidDevCredentials(root) {
4969
5527
  status: "ok",
4970
5528
  token,
4971
5529
  projectId: project.projectId,
5530
+ proxyUrl: getProxyUrl(root),
4972
5531
  staging
4973
5532
  };
4974
5533
  }
4975
- const DEFAULT_API_URL = "https://api.void.cloud";
4976
- const STAGING_API_URL = "https://api.staging.void.cloud";
4977
5534
  /**
4978
5535
  * Interactive AI setup prompt shown during dev server startup when the user
4979
5536
  * has a token but no linked project. Runs as fire-and-forget so it doesn't
4980
5537
  * block the server. On completion, writes project.json and restarts the server.
4981
5538
  */
4982
- async function runAiSetupPrompt(root, token, staging, server) {
4983
- const { select, text, isCancel, log } = await import("./agents-CtgBYqld.mjs").then((n) => n.C);
4984
- const { PlatformClient } = await import("./client-Gb71-XkG.mjs").then((n) => n.r);
4985
- const { writeProjectConfig } = await import("./client-Gb71-XkG.mjs").then((n) => n.y);
4986
- const { inferDefaultSlug } = await import("./resolve-project-BBMtLLV9.mjs").then((n) => n.i);
4987
- const client = new PlatformClient(token, staging ? STAGING_API_URL : DEFAULT_API_URL);
5539
+ async function runAiSetupPrompt(root, token, apiUrl, server) {
5540
+ const { select, text, isCancel, log } = await import("./output-tFQLLj26.mjs").then((n) => n.T);
5541
+ const { PlatformClient } = await import("./client-Clirrol3.mjs").then((n) => n.r);
5542
+ const { attachSelectedPlatform, writeProjectConfig } = await import("./auth-W9WII-mN.mjs").then((n) => n.y);
5543
+ const { inferDefaultSlug } = await import("./resolve-project--Vxawf7z.mjs").then((n) => n.i);
5544
+ const client = new PlatformClient(token, apiUrl);
4988
5545
  let projects;
4989
5546
  try {
4990
5547
  projects = await client.listProjects();
@@ -5009,8 +5566,8 @@ async function runAiSetupPrompt(root, token, staging, server) {
5009
5566
  let projectId;
5010
5567
  let slug;
5011
5568
  if (result === "__create__") {
5012
- const { validateProjectSlug } = await import("./create-project-DsYvl3TB.mjs").then((n) => n.a);
5013
- const { createProjectWithSlugRetry } = await import("./create-project-DsYvl3TB.mjs").then((n) => n.t);
5569
+ const { validateProjectSlug } = await import("./create-project-ChGZ1DFd.mjs").then((n) => n.a);
5570
+ const { createProjectWithSlugRetry } = await import("./create-project-ChGZ1DFd.mjs").then((n) => n.t);
5014
5571
  let created;
5015
5572
  try {
5016
5573
  created = await createProjectWithSlugRetry(client, inferDefaultSlug(root), async (defaultSlug) => {
@@ -5020,7 +5577,7 @@ async function runAiSetupPrompt(root, token, staging, server) {
5020
5577
  initialValue: defaultSlug,
5021
5578
  validate: validateProjectSlug
5022
5579
  });
5023
- if (isCancel(slugResult)) return;
5580
+ if (isCancel(slugResult) || typeof slugResult === "symbol") return;
5024
5581
  return slugResult.trim();
5025
5582
  });
5026
5583
  } catch (err) {
@@ -5035,10 +5592,10 @@ async function runAiSetupPrompt(root, token, staging, server) {
5035
5592
  projectId = project.id;
5036
5593
  slug = project.slug;
5037
5594
  }
5038
- writeProjectConfig(root, {
5595
+ writeProjectConfig(root, attachSelectedPlatform(root, {
5039
5596
  projectId,
5040
5597
  slug
5041
- });
5598
+ }, client.apiUrl));
5042
5599
  log.success(`Linked to ${import_picocolors.default.blue(slug)}`);
5043
5600
  await server.restart();
5044
5601
  }
@@ -5062,6 +5619,7 @@ async function runAiSetupPrompt(root, token, staging, server) {
5062
5619
  */
5063
5620
  function voidPlugin(options) {
5064
5621
  const root = process.cwd();
5622
+ const directCloudflareDestination = readDeploymentPlatform(root) === "cloudflare";
5065
5623
  warnIfPackageJsonIsNotEsm(root);
5066
5624
  const devTriggerToken = isViteBuildOrPreview() ? crypto.randomUUID() : getOrCreateDevTriggerToken(root);
5067
5625
  const config = readConfig(root);
@@ -5074,6 +5632,13 @@ function voidPlugin(options) {
5074
5632
  const scanDirs = config.inference?.scanDirs ?? (detected ? FRAMEWORK_SCAN_DIRS : void 0);
5075
5633
  const inferenceOptions = config.inference?.bindings ? { bindings: config.inference.bindings } : void 0;
5076
5634
  const bindingNames = resolveBindingNames(config.inference?.bindings);
5635
+ const frameworkOwnsCloudflareRuntime = detected !== void 0 && (detected.class === "b" || detected.class === "c");
5636
+ const localD1PersistRoot = frameworkOwnsCloudflareRuntime ? join(root, ".wrangler", "state") : options?.persistTo ?? join(root, ".void");
5637
+ const localD1 = resolveLocalD1Target(root, {
5638
+ bindingName: bindingNames?.d1 ?? "DB",
5639
+ persistRoot: localD1PersistRoot
5640
+ });
5641
+ writeLocalD1PersistState(root, !isNodeTarget(config.target) && !frameworkOwnsCloudflareRuntime ? options?.persistTo : void 0);
5077
5642
  const inferredBindings = inferProjectBindings(root, {
5078
5643
  ...inferenceOptions,
5079
5644
  paths
@@ -5088,6 +5653,8 @@ function voidPlugin(options) {
5088
5653
  if (detected && authEnabled) throw new Error("auth: Void-managed Better Auth is only supported in Void apps. Use Better Auth's official integration for this framework instead.");
5089
5654
  if (detected && bindings.needsSandbox) throw new Error("sandbox: Cloudflare Sandboxes are currently supported in Void apps only.");
5090
5655
  if (detected && bindings.needsLive) throw new Error("live: void/live is currently supported in Void apps only.");
5656
+ const hasInboundEmail = hasEmailHandlers(scanEmailHandlersSync(paths.sourceRoot));
5657
+ if (detected && hasInboundEmail) throw new Error("email: inbound email handlers are only supported in Void apps. Remove the email/ directory — `sendEmail` from void/email keeps working.");
5091
5658
  const STUB_PREFIX = "\0void-client-stub:";
5092
5659
  const SERVER_ONLY_STUBS = {
5093
5660
  "void/db": "export const db = {}; export const createDb = () => ({});",
@@ -5104,12 +5671,37 @@ function voidPlugin(options) {
5104
5671
  this.reason = reason;
5105
5672
  }
5106
5673
  }
5674
+ export class SandboxUnavailableError extends Error {
5675
+ constructor(message = 'Managed Sandbox is unavailable on this Void platform.') {
5676
+ super(message);
5677
+ this.name = 'SandboxUnavailableError';
5678
+ }
5679
+ }
5107
5680
  export async function getSandbox() {
5108
5681
  throw new Error('void/sandbox is server-only and cannot be imported by browser code.');
5109
5682
  }
5110
5683
  export const sandbox = { get: getSandbox };
5111
5684
  `,
5112
- "void/live": ""
5685
+ "void/email": `
5686
+ const serverOnly = (name) => () => {
5687
+ throw new Error(name + ' is server-only and cannot be imported by browser code.');
5688
+ };
5689
+ export const sendEmail = serverOnly('sendEmail');
5690
+ export const createEmail = serverOnly('createEmail');
5691
+ export const defineEmail = serverOnly('defineEmail');
5692
+ export const parseEmail = serverOnly('parseEmail');
5693
+ export const replyEmail = serverOnly('replyEmail');
5694
+ export const createMessageProxy = serverOnly('createMessageProxy');
5695
+ `,
5696
+ "void/email/testing": `
5697
+ const serverOnly = (name) => () => {
5698
+ throw new Error(name + ' is server-only and cannot be imported by browser code.');
5699
+ };
5700
+ export const createEmailTestHarness = serverOnly('createEmailTestHarness');
5701
+ export const createInboundTestHarness = serverOnly('createInboundTestHarness');
5702
+ `,
5703
+ "void/live": "",
5704
+ "void/durable": ""
5113
5705
  };
5114
5706
  const STUB_RESOLVE_FILTER = new RegExp(`^(${Object.keys(SERVER_ONLY_STUBS).join("|").replaceAll("/", "\\/")})$`);
5115
5707
  const STUB_LOAD_FILTER = new RegExp(`^\\0void-client-stub:`);
@@ -5120,7 +5712,7 @@ function voidPlugin(options) {
5120
5712
  filter: { id: STUB_RESOLVE_FILTER },
5121
5713
  handler(id) {
5122
5714
  if (this.environment?.name === "client") {
5123
- if (id === "void/live") this.error("void/live is server-only and cannot be imported by browser code. Import void/live/client instead.");
5715
+ if (id === "void/live" || id === "void/durable") this.error(id === "void/live" ? "void/live is server-only and cannot be imported by browser code. Import void/live/client instead." : "void/durable is server-only and cannot be imported by browser code.");
5124
5716
  return STUB_PREFIX + id;
5125
5717
  }
5126
5718
  }
@@ -5132,16 +5724,40 @@ function voidPlugin(options) {
5132
5724
  }
5133
5725
  }
5134
5726
  };
5727
+ const serverRuntimeOptimizerPlugin = {
5728
+ name: "void:server-runtime-optimizer",
5729
+ configEnvironment(name) {
5730
+ if (name !== "client") return { optimizeDeps: { exclude: ["void"] } };
5731
+ }
5732
+ };
5135
5733
  const queues = framework ? [] : scanQueuesSync(root, paths.queuesDir);
5136
5734
  const websocketRoutes = framework ? [] : scanWebSocketRoutesSync(root, paths);
5735
+ const durableObjects = scanDurableObjectModules(root);
5736
+ if (detected && durableObjects.length > 0) throw new Error("durable: durable-objects/ is currently supported in Void apps only, not framework adapter mode.");
5737
+ for (const module of durableObjects) {
5738
+ const websocketCollision = websocketRoutes.find((route) => route.bindingName === module.bindingName || route.className === module.className);
5739
+ if (websocketCollision) throw new Error(`durable: durable-objects/${module.filePath.split("/").pop()} conflicts with WebSocket route '${websocketCollision.filePath}'. Rename one of the files.`);
5740
+ if (sandboxConfig && (sandboxConfig.binding === module.bindingName || sandboxConfig.className === module.className)) throw new Error(`durable: durable-objects/${module.filePath.split("/").pop()} conflicts with the configured Sandbox binding or class. Rename the Durable Object file.`);
5741
+ }
5742
+ let websocketTombstones = [];
5743
+ if (!isNodeTarget(config.target)) {
5744
+ syncWranglerDurableObjects(root, durableObjects);
5745
+ const productionWebSocketRoutes = websocketRoutes.filter((route) => isRouteEnabledForEnvironment(route, "prod"));
5746
+ syncWranglerWebSockets(root, productionWebSocketRoutes);
5747
+ websocketTombstones = readWranglerWebSocketTombstones(root, productionWebSocketRoutes);
5748
+ }
5137
5749
  const liveConfig = !isNodeTarget(config.target) ? bindings.needsLive ? resolveLiveBindingConfig(root, {
5138
- reservedBindings: websocketRoutes.map((route) => route.bindingName),
5139
- reservedClasses: [...websocketRoutes.map((route) => route.className), sandboxConfig?.className]
5750
+ reservedBindings: [...websocketRoutes.map((route) => route.bindingName), ...durableObjects.map((module) => module.bindingName)],
5751
+ reservedClasses: [
5752
+ ...websocketRoutes.map((route) => route.className),
5753
+ ...durableObjects.map((module) => module.className),
5754
+ sandboxConfig?.className
5755
+ ]
5140
5756
  }) : readExistingLiveBindingConfig(root) : void 0;
5141
5757
  if (detected && (detected.class === "b" || detected.class === "c")) {
5142
- const frameworkPersistRoot = join(root, ".wrangler", "state");
5758
+ const frameworkPersistRoot = localD1.persistRoot;
5143
5759
  const syncFrameworkCompat = (compat) => {
5144
- if (ensureWranglerCompatibilityDate(root, compat.compatibilityDate)) voidLog("Synced compatibility_date to wrangler config");
5760
+ if (ensureWranglerCompatibilityDate(root, compat.compatibilityDate)) voidLog("Synced compatibility_date to Cloudflare config");
5145
5761
  };
5146
5762
  const wranglerCompatPlugin = {
5147
5763
  name: "void:wrangler-compat",
@@ -5149,7 +5765,7 @@ function voidPlugin(options) {
5149
5765
  config(_, env) {
5150
5766
  syncFrameworkCompat(ensureWranglerCompat(root, config));
5151
5767
  if (env.command !== "serve") return;
5152
- if (ensureWranglerNodejsAlsFlag(root)) voidLog("Synced nodejs_als to wrangler config");
5768
+ if (ensureWranglerNodejsAlsFlag(root)) voidLog("Synced nodejs_als to Cloudflare config");
5153
5769
  },
5154
5770
  buildStart() {
5155
5771
  syncFrameworkCompat(ensureWranglerCompat(root, config));
@@ -5206,6 +5822,7 @@ function voidPlugin(options) {
5206
5822
  ...nuxtVite8WebSocketCompatPlugin ? [nuxtVite8WebSocketCompatPlugin] : [],
5207
5823
  svelteKitHooksInjectPlugin,
5208
5824
  clientStubsPlugin,
5825
+ serverRuntimeOptimizerPlugin,
5209
5826
  bindingRuntimePlugin(bindingNames),
5210
5827
  envSchemaPlugin(root),
5211
5828
  envClientGuardPlugin(root),
@@ -5213,9 +5830,10 @@ function voidPlugin(options) {
5213
5830
  drizzlePlugin(root, false, dialect, {
5214
5831
  d1BindingName: bindingNames?.d1,
5215
5832
  forceVirtualDb: authEnabled || bindingNames?.d1 !== void 0,
5833
+ localD1DatabaseId: localD1.databaseId,
5216
5834
  localD1PersistRoot: frameworkPersistRoot
5217
5835
  }),
5218
- migrationPlugin(root, dialect, "local", frameworkPersistRoot, { enabled: false }),
5836
+ migrationPlugin(root, dialect, localD1.databaseId, frameworkPersistRoot, { enabled: false }),
5219
5837
  devTriggersHintPlugin(root, {
5220
5838
  framework: true,
5221
5839
  devTriggerToken
@@ -5228,6 +5846,9 @@ function voidPlugin(options) {
5228
5846
  }
5229
5847
  const remoteMode = resolveRemoteMode(config.remote);
5230
5848
  const workerConfig = {};
5849
+ const jobs = scanJobsSync(root, paths.cronsDir);
5850
+ if (jobs.length > 0) workerConfig.triggers = { crons: [...new Set(jobs.flatMap((job) => job.crons))] };
5851
+ let envSchemaFacts;
5231
5852
  let assetRouting;
5232
5853
  if (config.worker?.vars) workerConfig.vars = { ...config.worker.vars };
5233
5854
  let viteCommand = "build";
@@ -5245,6 +5866,23 @@ function voidPlugin(options) {
5245
5866
  function mergeBindings(resolved) {
5246
5867
  const environmentWebsocketRoutes = websocketRoutes.filter((route) => isRouteEnabledForEnvironment(route, routeEnvironment));
5247
5868
  const result = { ...workerConfig };
5869
+ result.compatibility_flags = [.../* @__PURE__ */ new Set([...result.compatibility_flags ?? [], ...resolved.compatibility_flags ?? []])];
5870
+ resolved.compatibility_flags = [];
5871
+ if (envSchemaFacts || authEnabled && process.env["VOID_CLOUDFLARE_DEPLOY_BUILD"] === "1") {
5872
+ const plaintextVars = {
5873
+ ...resolved.vars ?? {},
5874
+ ...result.vars ?? {}
5875
+ };
5876
+ const plaintextServerKeys = (envSchemaFacts?.serverKeys ?? []).filter((key) => key in plaintextVars);
5877
+ if (viteCommand === "build" && plaintextServerKeys.length > 0) throw new Error(`Server env keys cannot be Worker plaintext vars: ${plaintextServerKeys.join(", ")}. Remove them from worker.vars/wrangler vars and set them with the remote secret store.`);
5878
+ const configuredRequired = resolved.secrets?.required ?? [];
5879
+ const required = [.../* @__PURE__ */ new Set([
5880
+ ...configuredRequired,
5881
+ ...envSchemaFacts?.requiredServerKeys ?? [],
5882
+ ...authEnabled && process.env["VOID_CLOUDFLARE_DEPLOY_BUILD"] === "1" ? ["BETTER_AUTH_SECRET"] : []
5883
+ ])];
5884
+ if (required.length > 0) result.secrets = { required };
5885
+ }
5248
5886
  if (workerConfig.assets && resolved.assets) {
5249
5887
  delete resolved.assets.not_found_handling;
5250
5888
  delete resolved.assets.run_worker_first;
@@ -5269,10 +5907,41 @@ function voidPlugin(options) {
5269
5907
  binding: r2Name,
5270
5908
  bucket_name: "default"
5271
5909
  }];
5272
- if (shouldBindWorkersAi(viteCommand, bindings.needsAI, resolved.ai, result.ai)) result.ai = { binding: "AI" };
5273
- if (config.ai?.gateway) {
5910
+ const resolvedAi = resolved.ai;
5911
+ const resultAi = result.ai;
5912
+ const aiBindingName = resolveWorkersAiBindingName(resolvedAi, bindingNames?.ai);
5913
+ if (viteCommand === "serve" && bindings.needsAI && directCloudflareDestination && !resultAi && typeof resolvedAi === "object" && resolvedAi !== null && "binding" in resolvedAi && typeof resolvedAi.binding === "string") result.ai = {
5914
+ ...resolvedAi,
5915
+ remote: true
5916
+ };
5917
+ else if (shouldBindWorkersAi(viteCommand, bindings.needsAI, resolvedAi, resultAi, directCloudflareDestination)) result.ai = {
5918
+ binding: aiBindingName,
5919
+ ...viteCommand === "serve" && { remote: true }
5920
+ };
5921
+ if (aiBindingName !== "AI" || config.ai?.gateway) {
5922
+ const vars = result.vars ?? {};
5923
+ if (aiBindingName !== "AI") vars.__VOID_AI_BINDING = aiBindingName;
5924
+ if (config.ai?.gateway) vars.__VOID_AI_GATEWAY = config.ai.gateway;
5925
+ result.vars = vars;
5926
+ }
5927
+ const emailDevInboxBinding = workerConfig.assets?.binding ?? resolved.assets?.binding;
5928
+ if (shouldInjectEmailDevInboxVar(routeEnvironment, bindings.needsEmail, emailDevInboxBinding)) {
5929
+ const vars = result.vars ?? {};
5930
+ vars[DEV_INBOX_ENV_VAR] = emailDevInboxBinding;
5931
+ vars[DEV_INBOX_TOKEN_ENV_VAR] = devTriggerToken;
5932
+ result.vars = vars;
5933
+ }
5934
+ if (viteCommand === "serve" && bindings.needsEmail) {
5935
+ const sendEmail = resolved.send_email;
5936
+ if (Array.isArray(sendEmail)) {
5937
+ const kept = sendEmail.filter((entry) => entry?.name !== SEND_EMAIL_BINDING);
5938
+ if (kept.length > 0) resolved.send_email = kept;
5939
+ else delete resolved.send_email;
5940
+ }
5941
+ }
5942
+ if (viteCommand === "serve" && config.email?.from) {
5274
5943
  const vars = result.vars ?? {};
5275
- vars.__VOID_AI_GATEWAY = config.ai.gateway;
5944
+ vars.__VOID_EMAIL_FROM = config.email.from;
5276
5945
  result.vars = vars;
5277
5946
  }
5278
5947
  if (queues.length > 0) result.queues = {
@@ -5290,15 +5959,38 @@ function voidPlugin(options) {
5290
5959
  };
5291
5960
  if (environmentWebsocketRoutes.length > 0) {
5292
5961
  const existingDurableObjectBindings = resolved.durable_objects?.bindings ?? [];
5962
+ for (const route of environmentWebsocketRoutes) {
5963
+ const conflict = existingDurableObjectBindings.find((binding) => binding.name === route.bindingName && binding.class_name !== route.className);
5964
+ if (conflict) throw new Error(`websocket: binding '${route.bindingName}' is already configured for class '${conflict.class_name}', but '${route.filePath}' requires '${route.className}'.`);
5965
+ }
5293
5966
  const missingDurableObjectBindings = environmentWebsocketRoutes.filter((route) => !existingDurableObjectBindings.some((binding) => binding.name === route.bindingName)).map((route) => ({
5294
5967
  name: route.bindingName,
5295
5968
  class_name: route.className
5296
5969
  }));
5297
5970
  if (missingDurableObjectBindings.length > 0) result.durable_objects = { bindings: [...existingDurableObjectBindings, ...missingDurableObjectBindings] };
5298
- if (!Array.isArray(resolved.migrations)) result.migrations = [{
5299
- tag: "void-ws-v1",
5300
- new_classes: environmentWebsocketRoutes.map((route) => route.className)
5301
- }];
5971
+ const currentMigrations = resolved.migrations;
5972
+ const planned = appendSqliteClassMigration(Array.isArray(currentMigrations) ? currentMigrations : [], environmentWebsocketRoutes.map((route) => route.className), "void-ws-v1", existingDurableObjectBindings.map((binding) => binding.class_name));
5973
+ if (planned.added) result.migrations = planned.migrations;
5974
+ }
5975
+ if (durableObjects.length > 0) {
5976
+ const existingBindings = (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
5977
+ for (const module of durableObjects) {
5978
+ const conflict = existingBindings.find((binding) => binding.name === module.bindingName && binding.class_name !== module.className);
5979
+ if (conflict) throw new Error(`durable: binding '${module.bindingName}' is already configured for class '${conflict.class_name}', but durable-objects/ requires '${module.className}'.`);
5980
+ }
5981
+ const additions = durableObjects.filter((module) => !existingBindings.some((binding) => binding.name === module.bindingName)).map((module) => ({
5982
+ name: module.bindingName,
5983
+ class_name: module.className
5984
+ }));
5985
+ if (additions.length > 0) result.durable_objects = { bindings: [...existingBindings, ...additions] };
5986
+ const currentMigrations = result.migrations ?? resolved.migrations;
5987
+ const existingMigrations = Array.isArray(currentMigrations) ? currentMigrations : [];
5988
+ 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.`);
5989
+ const migrationAdditions = durableObjects.filter((module) => !existingMigrations.some((migration) => migration.tag === module.migrationTag || migration.new_sqlite_classes?.includes(module.className))).map((module) => ({
5990
+ tag: module.migrationTag,
5991
+ new_sqlite_classes: [module.className]
5992
+ }));
5993
+ if (migrationAdditions.length > 0) result.migrations = [...existingMigrations, ...migrationAdditions];
5302
5994
  }
5303
5995
  if (bindings.needsSandbox && sandboxConfig) {
5304
5996
  const existingDurableObjectBindings = (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
@@ -5335,12 +6027,13 @@ function voidPlugin(options) {
5335
6027
  new_sqlite_classes: [liveConfig.className]
5336
6028
  }];
5337
6029
  }
5338
- if (viteCommand === "serve" && bindings.needsAI) {
6030
+ if (viteCommand === "serve" && bindings.needsAI && !directCloudflareDestination) {
5339
6031
  const credentials = readVoidDevCredentials(root);
5340
6032
  if (credentials.status === "ok") {
5341
6033
  const vars = result.vars ?? {};
5342
6034
  vars.__VOID_TOKEN = credentials.token;
5343
6035
  vars.__VOID_PROJECT_ID = credentials.projectId;
6036
+ vars.__VOID_PROXY_URL = credentials.proxyUrl;
5344
6037
  if (credentials.staging) vars.__VOID_STAGING = "1";
5345
6038
  result.vars = vars;
5346
6039
  }
@@ -5349,7 +6042,7 @@ function voidPlugin(options) {
5349
6042
  const credentials = readVoidDevCredentials(root);
5350
6043
  if (credentials.status === "ok") {
5351
6044
  const vars = result.vars ?? {};
5352
- vars.__VOID_PROXY_URL = credentials.staging ? STAGING_PROXY_URL : DEFAULT_PROXY_URL;
6045
+ vars.__VOID_PROXY_URL = credentials.proxyUrl;
5353
6046
  vars.__VOID_TOKEN = credentials.token;
5354
6047
  vars.__VOID_PROJECT_ID = credentials.projectId;
5355
6048
  vars.__VOID_REMOTE = "1";
@@ -5358,34 +6051,32 @@ function voidPlugin(options) {
5358
6051
  }
5359
6052
  return result;
5360
6053
  }
5361
- function applySchemaDefaultsToWorkerVars(defaults) {
5362
- const vars = workerConfig.vars ?? {};
5363
- for (const [key, value] of Object.entries(defaults)) if (vars[key] === void 0) vars[key] = value;
5364
- workerConfig.vars = vars;
6054
+ function applyEnvSchema(facts) {
6055
+ envSchemaFacts = facts;
5365
6056
  }
5366
- let voidDeployBuildConfig;
5367
6057
  const envPlugin = {
5368
6058
  name: "void:env",
5369
- api: { getVoidDeployBuildConfig: () => voidDeployBuildConfig },
5370
- config(userConfig, { mode, command, isPreview }) {
6059
+ config(userConfig, { command, isPreview }) {
5371
6060
  viteCommand = command;
5372
6061
  routeEnvironment = command === "serve" && !isPreview ? "dev" : "prod";
6062
+ if (command === "serve" && directCloudflareDestination && bindings.needsAI) process.env.CLOUDFLARE_AUTH_USE_KEYRING = "true";
5373
6063
  if (!framework && !isNodeTarget(config.target)) {
6064
+ managedCloudflareBuild = command === "build" && Boolean(process.env.VOID_DEPLOY_PROJECT_ID);
6065
+ assetRoutingPublicDir = userConfig.publicDir === false ? null : typeof userConfig.publicDir === "string" ? resolve(root, userConfig.publicDir) : join(root, "public");
5374
6066
  assetRouting = resolveConfiguredAssetRoutingPolicy();
5375
6067
  applyAssetRoutingPolicy(assetRouting);
5376
6068
  }
5377
6069
  const ignored = ["**/.void/**"];
5378
- const rawEnvDir = typeof userConfig.envDir === "string" && userConfig.envDir.length > 0 ? userConfig.envDir : void 0;
5379
- voidDeployBuildConfig = {
5380
- mode,
5381
- envDir: rawEnvDir
5382
- };
5383
- const { sanitized: env, stripped } = stripInternalEnvKeys(filterLoadedEnv(loadEnv(mode, rawEnvDir ? join(root, rawEnvDir) : root, "")));
5384
- if (stripped.length > 0) voidWarn(`Ignoring internal env keys from .env files: ${stripped.join(", ")}. These are reserved for Void runtime use.`);
5385
- workerConfig.vars = {
5386
- ...workerConfig.vars,
5387
- ...env
5388
- };
6070
+ if (command === "serve") {
6071
+ const local = loadLocalEnv(root, { onWarn: (message) => voidWarn(message) });
6072
+ const { sanitized: env, stripped } = stripInternalEnvKeys(local);
6073
+ if (stripped.length > 0) voidWarn(`Ignoring internal env keys from .env: ${stripped.join(", ")}. These are reserved for Void runtime use.`);
6074
+ workerConfig.vars = {
6075
+ ...workerConfig.vars,
6076
+ ...env
6077
+ };
6078
+ if (isPreview) for (const [key, value] of Object.entries(env)) process.env[key] ??= value;
6079
+ }
5389
6080
  const assets = workerConfig.assets;
5390
6081
  if (command === "serve" && !isPreview && assets?.run_worker_first?.includes("/**")) assets.run_worker_first = [...DEV_ASSET_ROUTING_EXCLUSIONS, "/**"];
5391
6082
  const watchConfig = { server: { watch: { ignored } } };
@@ -5425,20 +6116,22 @@ function voidPlugin(options) {
5425
6116
  voidWarn("Falling back to local bindings.");
5426
6117
  });
5427
6118
  }
5428
- if (bindings.needsAI) {
6119
+ if (bindings.needsAI && !directCloudflareDestination) {
5429
6120
  const credentials = readVoidDevCredentials(root);
5430
6121
  if (credentials.status === "no-token") afterReady(() => {
5431
6122
  console.log();
5432
6123
  voidWarn("AI requires authentication. Run `void auth login`, then restart the dev server.");
5433
6124
  });
5434
- else if (credentials.status === "no-project") if (process.stdin.isTTY) afterReady(() => {
5435
- console.log();
5436
- runAiSetupPrompt(root, credentials.token, credentials.staging, server);
5437
- });
5438
- else afterReady(() => {
5439
- console.log();
5440
- voidWarn("AI requires a linked project. Run `void project link` to set one up.");
5441
- });
6125
+ else if (credentials.status === "no-project") {
6126
+ if (process.stdin.isTTY) afterReady(() => {
6127
+ console.log();
6128
+ runAiSetupPrompt(root, credentials.token, credentials.apiUrl, server);
6129
+ });
6130
+ else afterReady(() => {
6131
+ console.log();
6132
+ voidWarn("AI requires a linked project. Run `void project link` to set one up.");
6133
+ });
6134
+ }
5442
6135
  }
5443
6136
  }
5444
6137
  };
@@ -5455,7 +6148,8 @@ function voidPlugin(options) {
5455
6148
  if (hintShown) return;
5456
6149
  if (!watchDirs.some((d) => path.startsWith(d))) return;
5457
6150
  try {
5458
- const fileBindings = inferBindingsFromSource(readFileSync(path, "utf-8"), path.split("/").pop());
6151
+ const code = readFileSync(path, "utf-8");
6152
+ const fileBindings = inferBindingsFromSource(code, path.split("/").pop());
5459
6153
  const newBindings = [];
5460
6154
  if (autoDetectBindings) {
5461
6155
  if (fileBindings.needsD1 && !bindings.needsD1) newBindings.push("D1 (void/db)");
@@ -5463,6 +6157,7 @@ function voidPlugin(options) {
5463
6157
  if (fileBindings.needsR2 && !bindings.needsR2) newBindings.push("R2 (void/storage)");
5464
6158
  if (fileBindings.needsAI && !bindings.needsAI) newBindings.push("AI (void/ai)");
5465
6159
  if (fileBindings.needsSandbox && !bindings.needsSandbox) newBindings.push("Sandbox (void/sandbox)");
6160
+ if (fileBindings.needsEmail && !bindings.needsEmail) newBindings.push("Email (void/email)");
5466
6161
  }
5467
6162
  if (autoDetectAuth && fileBindings.needsAuth) newBindings.push("Auth (void/auth)");
5468
6163
  if (newBindings.length > 0) {
@@ -5496,7 +6191,7 @@ function voidPlugin(options) {
5496
6191
  if (resolved.command !== "build") return;
5497
6192
  if (config.routing?.notFound !== void 0 && !process.env.VOID_DEPLOY_BUILD && !notFoundIgnoredWarned) {
5498
6193
  notFoundIgnoredWarned = true;
5499
- voidWarn(`Void generates no asset routing policy for a ${framework} build, so "routing.notFound" in void.json is not written to the built worker config. On a self-hosted \`vite build && wrangler deploy\` it takes effect only if your own wrangler config declares a complete \`assets\` policy (\`binding\`, \`directory\`, \`not_found_handling\`, \`run_worker_first\`); otherwise Cloudflare's default applies. Managed \`void deploy\` supplies the policy for you.`);
6194
+ voidWarn(`Void generates no asset routing policy for a ${framework} build, so "routing.notFound" in void.json is not written to the built worker config. During a self-hosted build and direct Cloudflare deployment, it takes effect only if your own Cloudflare config declares a complete \`assets\` policy (\`binding\`, \`directory\`, \`not_found_handling\`, \`run_worker_first\`); otherwise Cloudflare's default applies. Managed \`void deploy\` supplies the policy for you.`);
5500
6195
  }
5501
6196
  generateClassATriggerWrapper(root, framework, getOriginalFrameworkEntry(framework, root), true, resolved.build.assetsDir, config.routing?.notFound);
5502
6197
  }
@@ -5525,19 +6220,22 @@ function voidPlugin(options) {
5525
6220
  createWranglerCompatPersistPlugin(root, config),
5526
6221
  restartPlugin(),
5527
6222
  bindingWatchPlugin,
6223
+ ...bindings.needsEmail ? [emailDevInboxPlugin({ devTriggerToken })] : [],
5528
6224
  envPlugin,
5529
- envSchemaPlugin(root, { onDefaults: applySchemaDefaultsToWorkerVars }),
6225
+ envSchemaPlugin(root, { onSchema: applyEnvSchema }),
5530
6226
  envClientGuardPlugin(root),
5531
6227
  envClientFoldPlugin(),
5532
6228
  clientStubsPlugin,
6229
+ serverRuntimeOptimizerPlugin,
5533
6230
  bindingRuntimePlugin(bindingNames),
5534
6231
  drizzlePlugin(root, false, getDatabaseDialect(config), {
5535
6232
  d1BindingName: bindingNames?.d1,
5536
6233
  forceVirtualDb: authEnabled || bindingNames?.d1 !== void 0,
5537
- localD1PersistRoot: options?.persistTo ?? outDir,
6234
+ localD1DatabaseId: localD1.databaseId,
6235
+ localD1PersistRoot: localD1.persistRoot,
5538
6236
  redirectDbTypePaths
5539
6237
  }),
5540
- migrationPlugin(root, getDatabaseDialect(config)),
6238
+ migrationPlugin(root, getDatabaseDialect(config), localD1.databaseId, localD1.persistRoot),
5541
6239
  ...vinextCompat ? [vinextCompat] : [],
5542
6240
  frameworkBuildWrapperPlugin,
5543
6241
  frameworkTriggersPlugin(root, { devTriggerToken }),
@@ -5547,7 +6245,7 @@ function voidPlugin(options) {
5547
6245
  }),
5548
6246
  ...cloudflare({
5549
6247
  ...inspectorPortOption,
5550
- persistState: { path: options?.persistTo ?? outDir },
6248
+ persistState: { path: localD1.persistRoot },
5551
6249
  auxiliaryWorkers: options?.auxiliaryWorkers,
5552
6250
  viteEnvironment,
5553
6251
  config: mergeBindings
@@ -5561,10 +6259,13 @@ function voidPlugin(options) {
5561
6259
  if (config.remote) voidWarn("\"remote\" in void.json is ignored with target: \"" + config.target + "\"");
5562
6260
  if (bindings.needsSandbox) throw new Error(`sandbox: Cloudflare Sandboxes require target: "cloudflare". They are not available with target: '${config.target}'.`);
5563
6261
  if (bindings.needsLive) throw new Error(`live: void/live requires target: "cloudflare". It is not available with target: '${config.target}'.`);
6262
+ if (durableObjects.length > 0) throw new Error(`durable: durable-objects/ requires target: "cloudflare". It is not available with target: '${config.target}'.`);
6263
+ if (hasInboundEmail) throw new Error(`email: inbound email handlers (email/) require target: "cloudflare". They are not available with target: '${config.target}'.`);
5564
6264
  if (config.routing?.revalidate) voidWarn("\"routing.revalidate\" in void.json is ignored with target: \"" + config.target + "\" (edge caching is CF-only)");
5565
6265
  return [
5566
6266
  restartPlugin(),
5567
6267
  envPlugin,
6268
+ serverRuntimeOptimizerPlugin,
5568
6269
  envSchemaPlugin(root),
5569
6270
  ...pagesDevCssAssetsPlugin(root, devTriggerToken),
5570
6271
  routingPlugin(root, {
@@ -5582,16 +6283,18 @@ function voidPlugin(options) {
5582
6283
  ];
5583
6284
  }
5584
6285
  const existingFlags = workerConfig.compatibility_flags ?? [];
5585
- const normalizedFlags = normalizeWranglerCompatibilityFlags(existingFlags, { needsNodejsCompat: authEnabled || dialect === "postgresql" || bindings.needsSandbox });
6286
+ const needsNodejsCompat = authEnabled || dialect !== "sqlite" || bindings.needsSandbox;
6287
+ const normalizedFlags = normalizeWranglerCompatibilityFlags(existingFlags, { needsNodejsCompat });
5586
6288
  if (normalizedFlags && normalizedFlags !== existingFlags) workerConfig.compatibility_flags = normalizedFlags;
5587
- const jobs = scanJobsSync(root, paths.cronsDir);
5588
6289
  const ssrEntry = validateSsrEntry(root, paths);
5589
6290
  const hasPages = existsSync(paths.pagesDir);
5590
6291
  const hasMiddleware = scanMiddlewareSync(root, paths).length > 0;
6292
+ let assetRoutingPublicDir = join(root, "public");
6293
+ let managedCloudflareBuild = false;
5591
6294
  function resolveConfiguredAssetRoutingPolicy() {
5592
6295
  const routePatterns = scanRoutePatternsSync(root, paths, routeEnvironment);
5593
6296
  const environmentWebsocketRoutes = websocketRoutes.filter((route) => isRouteEnabledForEnvironment(route, routeEnvironment));
5594
- return resolveAssetRoutingPolicy({
6297
+ const policy = resolveAssetRoutingPolicy({
5595
6298
  isSsr: Boolean(ssrEntry),
5596
6299
  hasPages,
5597
6300
  hasApiRoutes: hasApiRoutes(routePatterns) || hasApiRoutes(environmentWebsocketRoutes) || authEnabled,
@@ -5602,10 +6305,21 @@ function voidPlugin(options) {
5602
6305
  liveEnabled: liveConfig !== void 0,
5603
6306
  notFound: config.routing?.notFound
5604
6307
  });
6308
+ const rules = mergeRoutingRules({
6309
+ dir: assetRoutingPublicDir,
6310
+ routing: config.routing
6311
+ });
6312
+ if (!managedCloudflareBuild && (rules.headerRules.length > 0 || rules.redirectRules.length > 0 || (rules.fallbackRules?.length ?? 0) > 0)) {
6313
+ policy.assetConfig.run_worker_first = ["/**"];
6314
+ if (rules.fallbackRules?.length && policy.assetConfig.not_found_handling === "single-page-application") {
6315
+ policy.assetConfig.not_found_handling = "none";
6316
+ policy.workerSpaFallback = true;
6317
+ }
6318
+ }
6319
+ return policy;
5605
6320
  }
5606
6321
  assetRouting = resolveConfiguredAssetRoutingPolicy();
5607
6322
  workerConfig.main = join(root, ".void", "entry.ts");
5608
- if (jobs.length > 0) workerConfig.triggers = { crons: jobs.flatMap((job) => job.crons) };
5609
6323
  function applyAssetRoutingPolicy(policy) {
5610
6324
  workerConfig.assets = {
5611
6325
  binding: "ASSETS",
@@ -5627,11 +6341,13 @@ function voidPlugin(options) {
5627
6341
  createWranglerCompatPersistPlugin(root, config),
5628
6342
  restartPlugin(),
5629
6343
  bindingWatchPlugin,
6344
+ ...bindings.needsEmail ? [emailDevInboxPlugin({ devTriggerToken })] : [],
5630
6345
  envPlugin,
5631
- envSchemaPlugin(root, { onDefaults: applySchemaDefaultsToWorkerVars }),
6346
+ envSchemaPlugin(root, { onSchema: applyEnvSchema }),
5632
6347
  envClientGuardPlugin(root),
5633
6348
  envClientFoldPlugin(),
5634
6349
  clientStubsPlugin,
6350
+ serverRuntimeOptimizerPlugin,
5635
6351
  bindingRuntimePlugin(bindingNames),
5636
6352
  ...bindings.needsSandbox ? [createSandboxSdkResolvePlugin()] : [],
5637
6353
  ...pagesDevCssAssetsPlugin(root, devTriggerToken),
@@ -5648,14 +6364,17 @@ function voidPlugin(options) {
5648
6364
  sandboxBindingName: sandboxConfig?.binding,
5649
6365
  sandboxClassName: sandboxConfig?.className,
5650
6366
  live: liveConfig,
6367
+ durableObjects,
6368
+ websocketTombstones,
5651
6369
  onDevAssetRoutingPolicyChange: updateDevAssetRoutingPolicy
5652
6370
  }),
5653
6371
  drizzlePlugin(root, true, dialect, {
5654
6372
  d1BindingName: bindingNames?.d1,
5655
6373
  forceVirtualDb: authEnabled || bindingNames?.d1 !== void 0,
5656
- localD1PersistRoot: options?.persistTo ?? outDir
6374
+ localD1DatabaseId: localD1.databaseId,
6375
+ localD1PersistRoot: localD1.persistRoot
5657
6376
  }),
5658
- migrationPlugin(root, dialect, "local", join(root, ".void"), {
6377
+ migrationPlugin(root, dialect, localD1.databaseId, localD1.persistRoot, {
5659
6378
  enabled: authEnabled,
5660
6379
  configPath: authConfigPath
5661
6380
  }),
@@ -5666,7 +6385,7 @@ function voidPlugin(options) {
5666
6385
  }),
5667
6386
  ...cloudflare({
5668
6387
  ...inspectorPortOption,
5669
- persistState: { path: options?.persistTo ?? outDir },
6388
+ persistState: { path: localD1.persistRoot },
5670
6389
  auxiliaryWorkers: options?.auxiliaryWorkers,
5671
6390
  viteEnvironment: { name: DEFAULT_WORKER_ENVIRONMENT_NAME },
5672
6391
  config: mergeBindings
@@ -5674,4 +6393,4 @@ function voidPlugin(options) {
5674
6393
  ];
5675
6394
  }
5676
6395
  //#endregion
5677
- export { VoidAssetRewriteError, basicAuth, defer, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled, shouldBindWorkersAi, voidPlugin };
6396
+ export { VoidAssetRewriteError, basicAuth, defer, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled, resolveWorkersAiBindingName, shouldBindWorkersAi, shouldInjectEmailDevInboxVar, voidPlugin };