void 0.10.12 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (431) 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-CYVhSNFy.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-CkVD1uOh.mjs} +6 -4
  11. package/dist/{cache-C11V8Fxq.mjs → cache-QcUfR-ff.mjs} +9 -5
  12. package/dist/{cancel-deploy-fwFYF04b.mjs → cancel-deploy-DsvWKFTe.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 +1592 -153
  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-CyCHWSO_.mjs} +61 -463
  20. package/dist/cloudflare-auth-DRkGe7-s.mjs +170 -0
  21. package/dist/cloudflare-cmd-D3ME2GAe.mjs +62 -0
  22. package/dist/cloudflare-connect-BivMefMA.mjs +56 -0
  23. package/dist/cloudflare-operations-CPTpRW6d.mjs +566 -0
  24. package/dist/{config-BkTvs43g.mjs → config-BQFq7QvD.mjs} +85 -41
  25. package/dist/{config-CutEMNGJ.mjs → config-NOG_U1aK.mjs} +10 -15
  26. package/dist/connect-D9Yr-T5f.mjs +79 -0
  27. package/dist/{create-project-DsYvl3TB.mjs → create-project-DMV-csEm.mjs} +26 -16
  28. package/dist/database-provider.d.mts +21 -0
  29. package/dist/database-provider.mjs +6 -0
  30. package/dist/{db-DOiJMRt2.mjs → db-BxoUWL0F.mjs} +600 -115
  31. package/dist/{delete-mh6p-zkQ.mjs → delete-iJOqxBz1.mjs} +9 -6
  32. package/dist/{deploy-u7Rv9q_q.mjs → deploy-CcDoeBIf.mjs} +3451 -1986
  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-gu_iaHmn.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-r6DHJyAB.mjs +262 -0
  44. package/dist/{entry-D7yy4xVH.mjs → entry-DU3oDoQ3.mjs} +2 -2
  45. package/dist/env-D4Emu-M_.mjs +95 -0
  46. package/dist/env-DmU2To0C.mjs +73 -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-o_w-8yI8.mjs → gen-BKw6qHIg.mjs} +70 -15
  52. package/dist/{github-cmd-DKcGUNsj.mjs → github-cmd-DJS5Ab-H.mjs} +39 -31
  53. package/dist/{handler-Cjh8uM3Y.d.mts → handler-D1hLsObx.d.mts} +124 -122
  54. package/dist/{head-nmvOgFjd.d.mts → head-Do8P4puT.d.mts} +9 -8
  55. package/dist/{headers-nsHIFixA.mjs → headers-D8QfRX9Y.mjs} +12 -10
  56. package/dist/inbound-BJ70in1n.d.mts +354 -0
  57. package/dist/inbound-CNKxb3FY.mjs +694 -0
  58. package/dist/index.d.mts +61 -34
  59. package/dist/index.mjs +1048 -321
  60. package/dist/{init-3rgHKBVi.mjs → init-Dx5cgKqK.mjs} +329 -271
  61. package/dist/link-D_rm5sRH.mjs +52 -0
  62. package/dist/{list-CPwFDZ_c.mjs → list-M8XShti7.mjs} +37 -5
  63. package/dist/{runner-kapo9aPs.mjs → local-d1-BE8KBbMy.mjs} +116 -6
  64. package/dist/{login-BT3H8PN3.mjs → login-C8-UuLnp.mjs} +17 -15
  65. package/dist/{logs-Bt313ax7.mjs → logs-DwFU7dMW.mjs} +16 -2
  66. package/dist/mime-BJD7d_qL.mjs +1216 -0
  67. package/dist/neon-DHwd2zvC.mjs +54 -0
  68. package/dist/{node-Da0UcsGA.mjs → node-BkaXcpAc.mjs} +29 -8
  69. package/dist/operator-cmd-DYWRbWUA.mjs +348 -0
  70. package/dist/{agents-CtgBYqld.mjs → output-tFQLLj26.mjs} +676 -211
  71. package/dist/{package-json-Cx1osYo6.mjs → package-json-CPoWX79C.mjs} +1 -1
  72. package/dist/pages/client.d.mts +43 -41
  73. package/dist/pages/client.mjs +40 -33
  74. package/dist/pages/head-client.d.mts +10 -12
  75. package/dist/pages/head.d.mts +1 -1
  76. package/dist/pages/index.d.mts +37 -14
  77. package/dist/pages/index.mjs +5 -5
  78. package/dist/pages/islands-plugin.d.mts +25 -27
  79. package/dist/pages/islands-plugin.mjs +6 -4
  80. package/dist/pages/prefetch.d.mts +6 -7
  81. package/dist/pages/protocol.d.mts +2 -2
  82. package/dist/pages/protocol.mjs +2 -1
  83. package/dist/pages/serialize.d.mts +7 -8
  84. package/dist/platform-cmd-BEOVv1PN.mjs +123 -0
  85. package/dist/platform-domain-BszUfiS8.mjs +228 -0
  86. package/dist/platform-lifecycle-CjSr6xf0.mjs +4450 -0
  87. package/dist/platform-management-W30iCaTL.mjs +698 -0
  88. package/dist/platform-recovery-pHHv4ZSg.mjs +99 -0
  89. package/dist/{plugin-inference-DMeavIJ6.mjs → plugin-inference-BDRfZngg.mjs} +35 -19
  90. package/dist/prepare-BfJvFUtJ.mjs +14 -0
  91. package/dist/{prepare-BoKHgMNx.mjs → prepare-CBetXvsN.mjs} +15 -24
  92. package/dist/{preset-BjyR3lzz.mjs → preset-lAy0B0BQ.mjs} +25 -212
  93. package/dist/{project-cmd-D_w-4w5B.mjs → project-cmd-DCpk1cDt.mjs} +25 -12
  94. package/dist/{project-paths-BQd7OmIo.mjs → project-paths-SK8nMHPp.mjs} +3 -1
  95. package/dist/{project-tsconfig-B-QtXjLQ.mjs → project-tsconfig-Ql2XsSQp.mjs} +2 -2
  96. package/dist/{protocol-Bnb0LFp3.d.mts → protocol-C-pqYJjE.d.mts} +3 -4
  97. package/dist/{provision-rShh6MKY.mjs → provision-Blnstcm2.mjs} +78 -45
  98. package/dist/r2-conditions-D1Wk8i7b.mjs +30 -0
  99. package/dist/{requests-B8sZxaFM.mjs → requests-Dn8Vheh1.mjs} +5 -3
  100. package/dist/{resolve-project-BBMtLLV9.mjs → resolve-project--Vxawf7z.mjs} +2 -2
  101. package/dist/rollback-CtlPXEBi.mjs +166 -0
  102. package/dist/{rolldown-runtime-DJK8HYOj.mjs → rolldown-runtime-rQ84J-ij.mjs} +1 -1
  103. package/dist/{route-types-COI2DsZv.mjs → route-types-Da-DpyUp.mjs} +85 -26
  104. package/dist/routes-stub.d.mts +22 -23
  105. package/dist/runner-mysql-CgRFl3s6.mjs +61 -0
  106. package/dist/{runner-pg-CHM76xuC.mjs → runner-pg-DCkWPsWS.mjs} +18 -6
  107. package/dist/runtime/ai.d.mts +21 -14
  108. package/dist/runtime/ai.mjs +5 -4
  109. package/dist/runtime/auth-client-react.d.mts +3 -5
  110. package/dist/runtime/auth-client-solid.d.mts +3 -5
  111. package/dist/runtime/auth-client-svelte.d.mts +3 -5
  112. package/dist/runtime/auth-client-vue.d.mts +3 -5
  113. package/dist/runtime/auth-client.d.mts +3 -5
  114. package/dist/runtime/auth.d.mts +1 -1
  115. package/dist/runtime/better-auth-mysql.d.mts +10 -0
  116. package/dist/runtime/better-auth-mysql.mjs +49 -0
  117. package/dist/runtime/better-auth-pg.d.mts +8 -9
  118. package/dist/runtime/better-auth-pg.mjs +2 -2
  119. package/dist/runtime/better-auth.d.mts +8 -9
  120. package/dist/runtime/better-auth.mjs +2 -2
  121. package/dist/runtime/client-react.d.mts +1 -1
  122. package/dist/runtime/client-solid.d.mts +1 -1
  123. package/dist/runtime/client-svelte.d.mts +1 -1
  124. package/dist/runtime/client-vue.d.mts +1 -1
  125. package/dist/runtime/client.d.mts +1 -1
  126. package/dist/runtime/db-mysql.d.mts +2 -0
  127. package/dist/runtime/db-mysql.mjs +1 -0
  128. package/dist/runtime/db.d.mts +10 -11
  129. package/dist/runtime/durable.d.mts +47 -0
  130. package/dist/runtime/durable.mjs +146 -0
  131. package/dist/runtime/email/testing.d.mts +112 -0
  132. package/dist/runtime/email/testing.mjs +283 -0
  133. package/dist/runtime/email.d.mts +30 -0
  134. package/dist/runtime/email.mjs +573 -0
  135. package/dist/runtime/env-helpers.d.mts +2 -2
  136. package/dist/runtime/env-helpers.mjs +5 -42
  137. package/dist/runtime/env-public-client.d.mts +10 -11
  138. package/dist/runtime/env-public-client.mjs +1 -1
  139. package/dist/runtime/env-public.d.mts +2 -2
  140. package/dist/runtime/env-public.mjs +104 -49
  141. package/dist/runtime/env.d.mts +19 -18
  142. package/dist/runtime/env.mjs +15 -2
  143. package/dist/runtime/fetch-stream.d.mts +20 -21
  144. package/dist/runtime/fetch.d.mts +15 -16
  145. package/dist/runtime/handler.d.mts +1 -1
  146. package/dist/runtime/isr.d.mts +21 -22
  147. package/dist/runtime/isr.mjs +26 -8
  148. package/dist/runtime/kv.d.mts +9 -10
  149. package/dist/runtime/live-client.d.mts +5 -7
  150. package/dist/runtime/live-client.mjs +9 -7
  151. package/dist/runtime/live-server.d.mts +4 -5
  152. package/dist/runtime/live.d.mts +22 -24
  153. package/dist/runtime/live.mjs +1 -1
  154. package/dist/runtime/log.d.mts +16 -17
  155. package/dist/runtime/migration-handler-mysql.d.mts +4 -0
  156. package/dist/runtime/migration-handler-mysql.mjs +81 -0
  157. package/dist/runtime/migration-handler-pg.d.mts +2 -4
  158. package/dist/runtime/migration-handler.d.mts +5 -6
  159. package/dist/runtime/migration-handler.mjs +4 -3
  160. package/dist/runtime/queues.d.mts +3 -4
  161. package/dist/runtime/queues.mjs +2 -1
  162. package/dist/runtime/remote/binding-handler.d.mts +10 -12
  163. package/dist/runtime/remote/binding-handler.mjs +24 -3
  164. package/dist/runtime/remote/index.d.mts +5 -6
  165. package/dist/runtime/remote/index.mjs +21 -18
  166. package/dist/runtime/response.d.mts +10 -11
  167. package/dist/runtime/sandbox.d.mts +56 -55
  168. package/dist/runtime/sandbox.mjs +57 -49
  169. package/dist/runtime/schema-mysql.d.mts +1 -0
  170. package/dist/runtime/schema-mysql.mjs +2 -0
  171. package/dist/runtime/seed.d.mts +14 -9
  172. package/dist/runtime/sse-client.d.mts +6 -7
  173. package/dist/runtime/sse.d.mts +11 -12
  174. package/dist/runtime/storage.d.mts +3 -4
  175. package/dist/runtime/validator.d.mts +1 -1
  176. package/dist/runtime/ws-server.d.mts +12 -12
  177. package/dist/runtime/ws-server.mjs +32 -4
  178. package/dist/runtime/ws.d.mts +19 -21
  179. package/dist/{scan-DYXkrasO.mjs → scan-BMH4rzlv.mjs} +53 -31
  180. package/dist/{scan-DEwlM_Xy.mjs → scan-CpK-57ug.mjs} +9 -5
  181. package/dist/{secret-Dt32J6RI.mjs → secret-BqTxGqki.mjs} +62 -5
  182. package/dist/{skills-CLjN0uUO.mjs → skills-Q46GZMO-.mjs} +6 -4
  183. package/dist/{standard-schema-DJ0HW7QP.d.mts → standard-schema-Fo_vCAZh.d.mts} +6 -6
  184. package/dist/{subcommand-prompt-BzV8iQZo.mjs → subcommand-prompt-WfySCQ7S.mjs} +67 -48
  185. package/dist/sveltekit.d.mts +12 -11
  186. package/dist/sveltekit.mjs +1 -1
  187. package/dist/types-BAp5AEBU.d.mts +79 -0
  188. package/dist/types-CKWnYgfy.d.mts +1 -0
  189. package/dist/{validate-Cw_RLeTj.mjs → validate-Bihr8WBi.mjs} +3 -3
  190. package/dist/wrangler--imS8n0d.mjs +1796 -0
  191. package/dist/{yarn-pnp-DJn3SAHF.mjs → yarn-pnp-DxSInkzL.mjs} +1 -1
  192. package/package.json +79 -65
  193. package/schema.json +35 -3
  194. package/skills/migrate-vite-cloudflare-to-void/SKILL.md +1 -1
  195. package/skills/void/SKILL.md +59 -2
  196. package/skills/void/docs/guide/ai.md +32 -14
  197. package/skills/void/docs/guide/app-types.md +6 -6
  198. package/skills/void/docs/guide/auth.md +14 -16
  199. package/skills/void/docs/guide/database/d1.md +6 -0
  200. package/skills/void/docs/guide/database/mysql.md +60 -0
  201. package/skills/void/docs/guide/database/postgresql.md +14 -9
  202. package/skills/void/docs/guide/database.md +39 -26
  203. package/skills/void/docs/guide/deployment.md +163 -22
  204. package/skills/void/docs/guide/durable-state.md +140 -0
  205. package/skills/void/docs/guide/edge/headers.md +5 -5
  206. package/skills/void/docs/guide/edge/prerendering.md +2 -0
  207. package/skills/void/docs/guide/edge/revalidation.md +21 -6
  208. package/skills/void/docs/guide/edge/rewrites.md +18 -14
  209. package/skills/void/docs/guide/edge/static-assets.md +23 -8
  210. package/skills/void/docs/guide/email.md +609 -0
  211. package/skills/void/docs/guide/env-migration.md +109 -0
  212. package/skills/void/docs/guide/env-vars.md +70 -248
  213. package/skills/void/docs/guide/index.md +15 -17
  214. package/skills/void/docs/guide/jobs.md +8 -5
  215. package/skills/void/docs/guide/live.md +7 -15
  216. package/skills/void/docs/guide/pages-routing/actions-and-forms.md +8 -4
  217. package/skills/void/docs/guide/pages-routing/islands.md +3 -3
  218. package/skills/void/docs/guide/pages-routing/loaders.md +6 -4
  219. package/skills/void/docs/guide/pages-routing/overview.md +7 -7
  220. package/skills/void/docs/guide/platform-administration.md +212 -0
  221. package/skills/void/docs/guide/platform-development.md +235 -0
  222. package/skills/void/docs/guide/queues.md +10 -10
  223. package/skills/void/docs/guide/quickstart.md +46 -67
  224. package/skills/void/docs/guide/remote-dev.md +8 -6
  225. package/skills/void/docs/guide/sandboxes.md +33 -16
  226. package/skills/void/docs/guide/self-hosted-platform.md +553 -0
  227. package/skills/void/docs/guide/server-routing.md +25 -5
  228. package/skills/void/docs/guide/sse.md +4 -4
  229. package/skills/void/docs/guide/ssg.md +5 -3
  230. package/skills/void/docs/guide/storage.md +2 -2
  231. package/skills/void/docs/guide/websockets.md +18 -7
  232. package/skills/void/docs/index.md +3 -3
  233. package/skills/void/docs/integrations/agents.md +6 -64
  234. package/skills/void/docs/integrations/cloudflare.md +165 -146
  235. package/skills/void/docs/integrations/frameworks/analog.md +4 -4
  236. package/skills/void/docs/integrations/frameworks/astro.md +5 -5
  237. package/skills/void/docs/integrations/frameworks/nuxt.md +5 -5
  238. package/skills/void/docs/integrations/frameworks/overview.md +3 -3
  239. package/skills/void/docs/integrations/frameworks/react-router.md +3 -3
  240. package/skills/void/docs/integrations/frameworks/sveltekit.md +3 -3
  241. package/skills/void/docs/integrations/frameworks/tanstack-start.md +2 -2
  242. package/skills/void/docs/integrations/nodejs-bun-deno.md +11 -4
  243. package/skills/void/docs/reference/api.md +42 -6
  244. package/skills/void/docs/reference/cli.md +637 -159
  245. package/skills/void/docs/reference/config.md +80 -25
  246. package/skills/void/docs/reference/resource-inference.md +13 -9
  247. package/skills/void/docs/reference/structure.md +10 -11
  248. package/AGENT_PROMPT.md +0 -19
  249. package/dist/cf-access-Bqw81xAf.mjs +0 -22
  250. package/dist/env-CZy5MorI.mjs +0 -299
  251. package/dist/env-helpers-z4stu8uc.d.mts +0 -52
  252. package/dist/env-mask-Dd47NbR6.mjs +0 -90
  253. package/dist/env-public-BfiLcMBk.d.mts +0 -140
  254. package/dist/link-CdGHSIy-.mjs +0 -45
  255. package/dist/mcp-DoM3_nhd.mjs +0 -377
  256. package/dist/project-paths-GpziKeQQ.d.mts +0 -25
  257. package/dist/providers-BNKRacMr.d.mts +0 -7
  258. package/dist/proxy-D-3_D-Gl.mjs +0 -5
  259. package/dist/rollback-CkvTFXx5.mjs +0 -90
  260. package/dist/runtime/isr-cache.d.mts +0 -207
  261. package/dist/runtime/isr-cache.mjs +0 -523
  262. package/dist/types-lLjNE9Qp.d.mts +0 -51
  263. package/getting-started-prompt.txt +0 -28
  264. package/skills/void/command/void.md +0 -7
  265. package/skills/void/docs/integrations/auth-providers.md +0 -0
  266. package/skills/void/docs/integrations/payment-processors.md +0 -0
  267. package/skills/void/docs/node_modules/@iconify/vue/README.md +0 -408
  268. package/skills/void/docs/node_modules/@iconify/vue/offline/readme.md +0 -5
  269. package/skills/void/docs/node_modules/@voidzero-dev/vitepress-theme/README.md +0 -103
  270. package/skills/void/docs/node_modules/oxc-minify/README.md +0 -78
  271. package/skills/void/docs/node_modules/reka-ui/README.md +0 -80
  272. package/skills/void/docs/node_modules/vitepress/README.md +0 -28
  273. package/skills/void/docs/node_modules/vitepress/template/api-examples.md +0 -49
  274. package/skills/void/docs/node_modules/vitepress/template/index.md +0 -28
  275. package/skills/void/docs/node_modules/vitepress/template/markdown-examples.md +0 -85
  276. package/skills/void/docs/node_modules/vitepress-plugin-group-icons/README.md +0 -101
  277. package/skills/void/docs/node_modules/void/AGENT_PROMPT.md +0 -19
  278. package/skills/void/docs/node_modules/void/CLAUDE.md +0 -219
  279. package/skills/void/docs/node_modules/void/README.md +0 -90
  280. package/skills/void/docs/node_modules/void/node_modules/@clack/prompts/CHANGELOG.md +0 -685
  281. package/skills/void/docs/node_modules/void/node_modules/@clack/prompts/README.md +0 -396
  282. package/skills/void/docs/node_modules/void/node_modules/@cloudflare/sandbox/README.md +0 -219
  283. package/skills/void/docs/node_modules/void/node_modules/@cloudflare/vite-plugin/README.md +0 -37
  284. package/skills/void/docs/node_modules/void/node_modules/@cloudflare/workers-types/README.md +0 -135
  285. package/skills/void/docs/node_modules/void/node_modules/@electric-sql/pglite/README.md +0 -189
  286. package/skills/void/docs/node_modules/void/node_modules/@hono/oauth-providers/CHANGELOG.md +0 -143
  287. package/skills/void/docs/node_modules/void/node_modules/@hono/oauth-providers/README.md +0 -1272
  288. package/skills/void/docs/node_modules/void/node_modules/@napi-rs/keyring/README.md +0 -19
  289. package/skills/void/docs/node_modules/void/node_modules/@types/better-sqlite3/README.md +0 -15
  290. package/skills/void/docs/node_modules/void/node_modules/@types/node/README.md +0 -15
  291. package/skills/void/docs/node_modules/void/node_modules/@types/pg/README.md +0 -15
  292. package/skills/void/docs/node_modules/void/node_modules/@types/proper-lockfile/README.md +0 -51
  293. package/skills/void/docs/node_modules/void/node_modules/@typescript/native-preview/README.md +0 -22
  294. package/skills/void/docs/node_modules/void/node_modules/@typescript/native-preview/vendor/vscode-jsonrpc/README.md +0 -69
  295. package/skills/void/docs/node_modules/void/node_modules/@void/md/README.md +0 -153
  296. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/@shikijs/engine-javascript/README.md +0 -9
  297. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/@shikijs/transformers/README.md +0 -9
  298. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/@types/node/README.md +0 -15
  299. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/gray-matter/CHANGELOG.md +0 -24
  300. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/gray-matter/README.md +0 -565
  301. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-exit/README.md +0 -127
  302. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-anchor/README.md +0 -600
  303. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-attrs/README.md +0 -386
  304. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-container/README.md +0 -95
  305. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-emoji/README.md +0 -101
  306. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-footnote/README.md +0 -135
  307. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/pathslash/README.md +0 -64
  308. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/shiki/README.md +0 -15
  309. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/tinyglobby/README.md +0 -25
  310. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/AGENTS.md +0 -16
  311. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/README.md +0 -220
  312. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/build.md +0 -21
  313. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/check.md +0 -35
  314. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/create.md +0 -70
  315. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/fmt.md +0 -20
  316. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/index.md +0 -35
  317. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/lint.md +0 -26
  318. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/pack.md +0 -17
  319. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/run.md +0 -364
  320. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/staged.md +0 -15
  321. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/test.md +0 -18
  322. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/automatic-data-tracking.md +0 -145
  323. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/build.md +0 -40
  324. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/cache.md +0 -107
  325. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/check.md +0 -60
  326. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/ci.md +0 -62
  327. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/commit-hooks.md +0 -60
  328. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/create.md +0 -341
  329. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/dev.md +0 -24
  330. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/docker.md +0 -175
  331. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/env.md +0 -167
  332. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/fmt.md +0 -41
  333. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/github-actions-cache.md +0 -165
  334. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/ide-integration.md +0 -101
  335. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/implode.md +0 -23
  336. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/index.md +0 -134
  337. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/install.md +0 -199
  338. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/lint.md +0 -50
  339. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/migrate-rules.md +0 -347
  340. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/migrate.md +0 -197
  341. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/monorepo.md +0 -176
  342. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/pack.md +0 -69
  343. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/run.md +0 -356
  344. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/test.md +0 -35
  345. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/troubleshooting.md +0 -108
  346. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/upgrade.md +0 -101
  347. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/vpx.md +0 -66
  348. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/why.md +0 -39
  349. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/index.md +0 -12
  350. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/team.md +0 -35
  351. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/templates/generator/README.md +0 -35
  352. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/templates/monorepo/README.md +0 -29
  353. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vue/README.md +0 -58
  354. package/skills/void/docs/node_modules/void/node_modules/arktype/README.md +0 -165
  355. package/skills/void/docs/node_modules/void/node_modules/better-auth/LICENSE.md +0 -20
  356. package/skills/void/docs/node_modules/void/node_modules/better-auth/README.md +0 -32
  357. package/skills/void/docs/node_modules/void/node_modules/better-sqlite3/README.md +0 -99
  358. package/skills/void/docs/node_modules/void/node_modules/blake3-jit/README.md +0 -108
  359. package/skills/void/docs/node_modules/void/node_modules/drizzle-arktype/README.md +0 -51
  360. package/skills/void/docs/node_modules/void/node_modules/drizzle-kit/README.md +0 -79
  361. package/skills/void/docs/node_modules/void/node_modules/drizzle-orm/README.md +0 -44
  362. package/skills/void/docs/node_modules/void/node_modules/drizzle-valibot/README.md +0 -51
  363. package/skills/void/docs/node_modules/void/node_modules/drizzle-zod/README.md +0 -65
  364. package/skills/void/docs/node_modules/void/node_modules/es-module-lexer/README.md +0 -403
  365. package/skills/void/docs/node_modules/void/node_modules/estree-walker/README.md +0 -48
  366. package/skills/void/docs/node_modules/void/node_modules/hono/README.md +0 -85
  367. package/skills/void/docs/node_modules/void/node_modules/ignore/README.md +0 -452
  368. package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/CHANGELOG.md +0 -76
  369. package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/LICENSE.md +0 -21
  370. package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/README.md +0 -364
  371. package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/SECURITY.md +0 -41
  372. package/skills/void/docs/node_modules/void/node_modules/magic-string/README.md +0 -325
  373. package/skills/void/docs/node_modules/void/node_modules/ofetch/README.md +0 -398
  374. package/skills/void/docs/node_modules/void/node_modules/pathslash/README.md +0 -64
  375. package/skills/void/docs/node_modules/void/node_modules/pg/README.md +0 -96
  376. package/skills/void/docs/node_modules/void/node_modules/pglite-server/README.md +0 -135
  377. package/skills/void/docs/node_modules/void/node_modules/picocolors/README.md +0 -21
  378. package/skills/void/docs/node_modules/void/node_modules/proper-lockfile/CHANGELOG.md +0 -108
  379. package/skills/void/docs/node_modules/void/node_modules/proper-lockfile/README.md +0 -183
  380. package/skills/void/docs/node_modules/void/node_modules/tinyglobby/README.md +0 -25
  381. package/skills/void/docs/node_modules/void/node_modules/valibot/LICENSE.md +0 -9
  382. package/skills/void/docs/node_modules/void/node_modules/valibot/README.md +0 -94
  383. package/skills/void/docs/node_modules/void/node_modules/vite-plus/AGENTS.md +0 -16
  384. package/skills/void/docs/node_modules/void/node_modules/vite-plus/README.md +0 -220
  385. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/build.md +0 -21
  386. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/check.md +0 -35
  387. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/create.md +0 -70
  388. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/fmt.md +0 -20
  389. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/index.md +0 -35
  390. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/lint.md +0 -26
  391. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/pack.md +0 -17
  392. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/run.md +0 -364
  393. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/staged.md +0 -15
  394. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/test.md +0 -18
  395. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/automatic-data-tracking.md +0 -145
  396. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/build.md +0 -40
  397. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/cache.md +0 -107
  398. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/check.md +0 -60
  399. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/ci.md +0 -62
  400. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/commit-hooks.md +0 -60
  401. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/create.md +0 -341
  402. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/dev.md +0 -24
  403. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/docker.md +0 -175
  404. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/env.md +0 -167
  405. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/fmt.md +0 -41
  406. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/github-actions-cache.md +0 -165
  407. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/ide-integration.md +0 -101
  408. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/implode.md +0 -23
  409. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/index.md +0 -134
  410. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/install.md +0 -199
  411. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/lint.md +0 -50
  412. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/migrate-rules.md +0 -347
  413. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/migrate.md +0 -197
  414. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/monorepo.md +0 -176
  415. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/pack.md +0 -69
  416. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/run.md +0 -356
  417. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/test.md +0 -35
  418. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/troubleshooting.md +0 -108
  419. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/upgrade.md +0 -101
  420. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/vpx.md +0 -66
  421. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/why.md +0 -39
  422. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/index.md +0 -12
  423. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/team.md +0 -35
  424. package/skills/void/docs/node_modules/void/node_modules/vite-plus/templates/generator/README.md +0 -35
  425. package/skills/void/docs/node_modules/void/node_modules/vite-plus/templates/monorepo/README.md +0 -29
  426. package/skills/void/docs/node_modules/void/node_modules/wrangler/README.md +0 -63
  427. package/skills/void/docs/node_modules/void/node_modules/zod/README.md +0 -191
  428. package/skills/void/docs/node_modules/void/skills/migrate-vite-cloudflare-to-void/SKILL.md +0 -175
  429. package/skills/void/docs/node_modules/void/skills/void/SKILL.md +0 -76
  430. package/skills/void/docs/node_modules/void/skills/void/command/void.md +0 -7
  431. 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-u7Rv9q_q.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-DYXkrasO.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-BkTvs43g.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-CcDoeBIf.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-COI2DsZv.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 } from "./scan-DEwlM_Xy.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-nsHIFixA.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-BE8KBbMy.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-BE8KBbMy.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-Bihr8WBi.mjs").then((n) => n.l);
1986
+ const { validateMigrations } = await import("./validate-Bihr8WBi.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-CgRFl3s6.mjs").then((n) => n.n).then((runner) => runner.getMySqlAppliedMigrations(url)) : await import("./runner-pg-DCkWPsWS.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-CgRFl3s6.mjs").then((n) => n.n).then((runner) => runner.runMySqlMigrations(migrationsDir, url)) : await import("./runner-pg-DCkWPsWS.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-BE8KBbMy.mjs").then((n) => n.i);
2000
+ const { collectMigrations } = await import("./validate-Bihr8WBi.mjs").then((n) => n.l);
2001
+ const { validateMigrations } = await import("./validate-Bihr8WBi.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-Da0UcsGA.mjs");
2016
+ const { runLocalVoidAuthMigrations } = await import("./node-BkaXcpAc.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,40 @@ ${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 __slug = typeof env.__VOID_PROJECT_SLUG === "string" && env.__VOID_PROJECT_SLUG.length > 0 ? env.__VOID_PROJECT_SLUG : undefined;
4152
+ const { message, getActions } = __createMessageProxy(envelope, { slug: __slug });
4153
+ await __withRawEnv(env, () => withRuntimeEnv(env, () => __emailHandler(message, userEnv, ctx, { slug: __slug })));
4154
+ const __actions = getActions();
4155
+ if (__isForwardableEmailMessage(envelope)) await __enactEmailActions(envelope, __actions);
4156
+ return __actions;
4157
+ }` : `async function handleEmail(envelope, env, ctx) {
4158
+ ${envValidateCall} const userEnv = __filterInternalBindings(env);
4159
+ const __slug = typeof env.__VOID_PROJECT_SLUG === "string" && env.__VOID_PROJECT_SLUG.length > 0 ? env.__VOID_PROJECT_SLUG : undefined;
4160
+ const { message, getActions } = __createMessageProxy(envelope, { slug: __slug });
4161
+ await __withRawEnv(env, () => withRuntimeEnv(env, () => __emailHandler(message, userEnv, ctx, { slug: __slug })));
4162
+ const __actions = getActions();
4163
+ if (__isForwardableEmailMessage(envelope)) await __enactEmailActions(envelope, __actions);
4164
+ return __actions;
4165
+ }` : "";
4166
+ const entryImport = `import { WorkerEntrypoint } from "cloudflare:workers";\n`;
4167
+ const fetchDeclaration = `const __voidFetch = ${fetchHandler};`;
4168
+ const entryClass = `export default class extends WorkerEntrypoint {
4169
+ async fetch(request) { return __voidFetch(request, this.env, this.ctx); }
4170
+ async scheduled(controller) { return handleScheduled(controller, this.env, this.ctx); }
4171
+ ${hasQueues ? ` async queue(batch) { return __voidQueue(batch, this.env, this.ctx); }\n` : ""}${hasEmail ? ` async email(envelope) { return handleEmail(envelope, this.env, this.ctx); }\n` : ""}}`;
4172
+ if (hasQueues) return `${sandboxImport}${entryImport}${liveRuntimeImport}import app, { scheduled, __voidCollectPrerenderPaths, __voidHandleInternalRoute, __withRawEnv, __processQueue${emailImport}${webSocketClasses.length > 0 ? `, ${webSocketClasses.join(", ")}` : ""}${liveRouteImport} } from 'virtual:void-routes';
4173
+ ${durableObjectImports}\n${runtimeEnvImport}${emailProxyImport}${envSchemaImport}${remoteImport}${isrImport}${filterBindingsHelper}${assetHelper}
3745
4174
  ${internalRouteHelper}
4175
+ ${fetchDeclaration}
3746
4176
  ${scheduledHandler}
3747
- async function queue(batch, env, ctx) {
4177
+ ${emailHandler}
4178
+ async function __voidQueue(batch, env, ctx) {
3748
4179
  ${envValidateCall}${remotePath ? ` if (env.__VOID_REMOTE === "1") env = ${createRemoteEnvCall};\n` : ""}${isrDeploymentStamp} const userEnv = __filterInternalBindings(env);
3749
4180
  const messages = [...batch.messages].map(m => ({
3750
4181
  id: m.id, timestamp: m.timestamp.toISOString(),
@@ -3768,15 +4199,19 @@ ${envValidateCall}${remotePath ? ` if (env.__VOID_REMOTE === "1") env = ${creat
3768
4199
  ${webSocketClasses.map((name) => `export { ${name} };`).join("\n")}
3769
4200
  ${liveExport}
3770
4201
  ${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}
4202
+ ${durableObjectExports}
4203
+ ${entryClass}`;
4204
+ return `${sandboxImport}${entryImport}${liveRuntimeImport}import app, { scheduled, __voidCollectPrerenderPaths, __voidHandleInternalRoute, __withRawEnv${emailImport}${webSocketClasses.length > 0 ? `, ${webSocketClasses.join(", ")}` : ""}${liveRouteImport} } from 'virtual:void-routes';
4205
+ ${durableObjectImports}\n${runtimeEnvImport}${emailProxyImport}${envSchemaImport}${remoteImport}${isrImport}${filterBindingsHelper}${assetHelper}
3774
4206
  ${internalRouteHelper}
4207
+ ${fetchDeclaration}
3775
4208
  ${scheduledHandler}
4209
+ ${emailHandler}
3776
4210
  ${webSocketClasses.map((name) => `export { ${name} };`).join("\n")}
3777
4211
  ${liveExport}
3778
4212
  ${sandboxExport}
3779
- export default { fetch: ${fetchHandler}, scheduled: handleScheduled };`;
4213
+ ${durableObjectExports}
4214
+ ${entryClass}`;
3780
4215
  }
3781
4216
  /**
3782
4217
  * Generate the app module — exports the Hono app with static asset serving.
@@ -3792,11 +4227,17 @@ export default { fetch: ${fetchHandler}, scheduled: handleScheduled };`;
3792
4227
  function compileNodeApp(target, honoPath, envSchemaModule) {
3793
4228
  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
4229
  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` : "";
4230
+ 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
4231
  return `${staticImport}
4232
+ import { fileURLToPath } from "node:url";
3797
4233
  import { Hono } from ${JSON.stringify(honoPath)};
3798
4234
  import innerApp, { __voidCollectPrerenderPaths } from "virtual:void-routes";
3799
4235
  ${envSchemaImport}
4236
+ // Resolved by nodeTargetPlugin for this emitted chunk, including shared chunks.
4237
+ const clientRoot = fileURLToPath(import.meta.voidClientDirectory);
4238
+ const assetApp = new Hono();
4239
+ assetApp.get("*", serveStatic({ root: clientRoot }));
4240
+ const assets = { fetch: (request) => assetApp.fetch(request) };
3800
4241
  const app = new Hono();
3801
4242
  ${envMiddleware}app.post("/__void/prerender-paths", async (c) => {
3802
4243
  const __expected = c.env?.__VOID_PROXY_TOKEN;
@@ -3807,8 +4248,8 @@ ${envMiddleware}app.post("/__void/prerender-paths", async (c) => {
3807
4248
  const paths = await __voidCollectPrerenderPaths(c.req.raw);
3808
4249
  return c.json({ paths });
3809
4250
  });
3810
- app.use("/assets/*", serveStatic({ root: "./client" }));
3811
- app.all("*", (c) => innerApp.fetch(c.req.raw));
4251
+ app.get("/assets/*", (c) => assets.fetch(c.req.raw));
4252
+ app.all("*", (c) => innerApp.fetch(c.req.raw, { ...c.env, ASSETS: assets }));
3812
4253
  export default app;`;
3813
4254
  }
3814
4255
  /**
@@ -4252,6 +4693,7 @@ const RESOLVE_ID_FILTER = /^(virtual:void-(routes|client-worker(?::(react|vue|sv
4252
4693
  const LOAD_ID_FILTER = /^virtual:void-(routes|client-worker(?::(react|vue|svelte|solid))?|client-browser(?::(react|vue|svelte|solid))?)$/;
4253
4694
  function routingPlugin(root, options) {
4254
4695
  let scan = null;
4696
+ let routeEnvironment = "prod";
4255
4697
  let ssr = null;
4256
4698
  let authConfigPath = null;
4257
4699
  const paths = readProjectPaths(root);
@@ -4259,6 +4701,7 @@ function routingPlugin(root, options) {
4259
4701
  const middlewareDir = paths.middlewareDir;
4260
4702
  const jobsDir = paths.cronsDir;
4261
4703
  const queuesDir = paths.queuesDir;
4704
+ const emailDir = paths.emailDir;
4262
4705
  const pagesDir = paths.pagesDir;
4263
4706
  const ssrDir = paths.ssrDir;
4264
4707
  const authConfigPaths = new Set(paths.authConfigCandidates.map((path) => normalizePath(path)));
@@ -4267,9 +4710,14 @@ function routingPlugin(root, options) {
4267
4710
  const normalizedMiddlewareDir = normalizePath(middlewareDir);
4268
4711
  const normalizedJobsDir = normalizePath(jobsDir);
4269
4712
  const normalizedQueuesDir = normalizePath(queuesDir);
4713
+ const normalizedEmailDir = normalizePath(emailDir);
4270
4714
  const normalizedPagesDir = normalizePath(pagesDir);
4271
4715
  const normalizedSsrDir = normalizePath(ssrDir);
4272
4716
  const ssrEntryAbsPaths = new Set([...SSR_SERVER_ENTRY_RELATIVE_PATHS, ...SSR_CLIENT_ENTRY_RELATIVE_PATHS].map((path) => normalizePath(join(ssrDir, path.split("/").pop()))));
4717
+ function hasEmailHandlers() {
4718
+ const email = scan?.email;
4719
+ return Boolean(email && (email.addresses.length > 0 || email.defaultHandler));
4720
+ }
4273
4721
  function isInDirectory(path, directory) {
4274
4722
  return path === directory || path.startsWith(directory + "/");
4275
4723
  }
@@ -4286,7 +4734,8 @@ function routingPlugin(root, options) {
4286
4734
  async function rescan() {
4287
4735
  scan = await scanRoutes(root, {
4288
4736
  output: options.output,
4289
- paths
4737
+ paths,
4738
+ environment: routeEnvironment
4290
4739
  });
4291
4740
  authConfigPath = findVoidAuthConfig(root, paths);
4292
4741
  const entries = validateSsrEntries(root, paths);
@@ -4327,16 +4776,20 @@ function routingPlugin(root, options) {
4327
4776
  responsePath: resolveFile("#self/response"),
4328
4777
  handlerPath: resolveFile("#self/handler"),
4329
4778
  runtimeEnvPath: resolveFile("#self/_env"),
4779
+ runtimeEmailPath: resolveFile("#self/email"),
4780
+ emailDir,
4330
4781
  projectRoot: root,
4331
4782
  homeDir: homedir(),
4332
4783
  webSocketRuntimePath: resolveFile("#self/runtime/ws-server"),
4333
4784
  liveRuntimePath: resolveFile("#self/runtime/live-server"),
4334
4785
  hasLive: options.live !== void 0,
4786
+ webSocketTombstones: options.websocketTombstones,
4335
4787
  reactRefresh: false,
4336
4788
  dev: false,
4337
4789
  devTriggerToken: options.devTriggerToken,
4338
4790
  auth: null,
4339
4791
  nodeTarget: !!options.target,
4792
+ managedDispatch: false,
4340
4793
  explicitPrerenderPaths: options.prerenderPaths,
4341
4794
  bindingHandlerPath: options.target ? void 0 : resolveFile("#self/remote/binding-handler"),
4342
4795
  bindingNames: options.bindingNames
@@ -4364,7 +4817,7 @@ function routingPlugin(root, options) {
4364
4817
  const config = readConfig(root);
4365
4818
  const dialect = getDatabaseDialect(config);
4366
4819
  compileOptions.auth = {
4367
- authRuntimePath: dialect === "postgresql" ? join(runtimeDir, "better-auth-pg.mjs") : join(runtimeDir, "better-auth.mjs"),
4820
+ authRuntimePath: dialect === "postgresql" ? join(runtimeDir, "better-auth-pg.mjs") : dialect === "mysql" ? join(runtimeDir, "better-auth-mysql.mjs") : join(runtimeDir, "better-auth.mjs"),
4368
4821
  authConfigPath,
4369
4822
  authDatabasePath: null,
4370
4823
  authSchemaPath: null,
@@ -4379,7 +4832,10 @@ function routingPlugin(root, options) {
4379
4832
  providers: compileOptions.auth.providers,
4380
4833
  userConfig: await loadVoidAuthConfig(compileOptions.auth.authConfigPath)
4381
4834
  };
4382
- const env = loadEnv(resolvedConfig?.mode ?? "development", root, "");
4835
+ const env = {
4836
+ ...resolvedConfig?.command === "serve" ? loadLocalEnv(root) : {},
4837
+ ...process.env
4838
+ };
4383
4839
  const schemaPath = join(outDir, "better-auth-schema.ts");
4384
4840
  const databasePath = join(outDir, "better-auth-db.ts");
4385
4841
  writeFileIfChanged(schemaPath, generateVoidAuthDrizzleSchema(authRuntime, env));
@@ -4393,6 +4849,8 @@ function routingPlugin(root, options) {
4393
4849
  }
4394
4850
  let resolvedConfig = null;
4395
4851
  let isrRevalidate;
4852
+ let isrPages;
4853
+ let isrPathRevalidate;
4396
4854
  function resolveAuthClientPath(config) {
4397
4855
  if (config.plugins.some((plugin) => plugin.name === "void-react:pages")) return explicitAuthClientPaths.react;
4398
4856
  if (config.plugins.some((plugin) => plugin.name === "void-vue:pages")) return explicitAuthClientPaths.vue;
@@ -4473,11 +4931,20 @@ function routingPlugin(root, options) {
4473
4931
  if (options.target) {
4474
4932
  writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(options.target, compileOptions.honoPath, getEnvSchemaModule()));
4475
4933
  writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
4476
- } else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry(false, remote, void 0, void 0, compileOptions.runtimeEnvPath, getEnvSchemaModule(), options.hasSandbox ? {
4477
- bindingName: options.sandboxBindingName,
4478
- className: options.sandboxClassName,
4479
- runtimePath: sandboxRuntimePath
4480
- } : false, void 0, options.live));
4934
+ } else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
4935
+ remote,
4936
+ webSocketClasses: options.websocketTombstones,
4937
+ runtimeEnvPath: compileOptions.runtimeEnvPath,
4938
+ runtimeEmailPath: compileOptions.runtimeEmailPath,
4939
+ envSchemaModule: getEnvSchemaModule(),
4940
+ sandbox: options.hasSandbox ? {
4941
+ bindingName: options.sandboxBindingName,
4942
+ className: options.sandboxClassName,
4943
+ runtimePath: sandboxRuntimePath
4944
+ } : false,
4945
+ live: options.live,
4946
+ durableObjects: options.durableObjects
4947
+ }));
4481
4948
  return {
4482
4949
  name: "void:routing",
4483
4950
  enforce: "pre",
@@ -4493,6 +4960,8 @@ function routingPlugin(root, options) {
4493
4960
  }
4494
4961
  },
4495
4962
  config(_, env) {
4963
+ routeEnvironment = env.command === "serve" && !env.isPreview ? "dev" : "prod";
4964
+ compileOptions.managedDispatch = !options.target && env.command === "build" && Boolean(process.env.VOID_DEPLOY_PROJECT_ID);
4496
4965
  if (env.command === "serve" && options.isAuthEnabled()) return { resolve: { alias: [
4497
4966
  "client",
4498
4967
  "react",
@@ -4526,11 +4995,11 @@ function routingPlugin(root, options) {
4526
4995
  if (!options.target) {
4527
4996
  const migrationsDir = paths.migrationsDir;
4528
4997
  if (existsSync(migrationsDir) || compileOptions.auth) {
4529
- const { collectMigrations } = await import("./validate-Cw_RLeTj.mjs").then((n) => n.l);
4530
- const { buildMigrationPlanBundle } = await import("./validate-Cw_RLeTj.mjs").then((n) => n.s);
4998
+ const { collectMigrations } = await import("./validate-Bihr8WBi.mjs").then((n) => n.l);
4999
+ const { buildMigrationPlanBundle } = await import("./validate-Bihr8WBi.mjs").then((n) => n.s);
4531
5000
  const migrations = existsSync(migrationsDir) ? collectMigrations(migrationsDir) : [];
4532
5001
  const dialect = getDatabaseDialect(readConfig(root));
4533
- compileOptions.migrationHandlerPath = resolveFile(dialect === "postgresql" ? "#self/runtime/migration-handler-pg" : "#self/runtime/migration-handler");
5002
+ compileOptions.migrationHandlerPath = resolveFile(dialect === "postgresql" ? "#self/runtime/migration-handler-pg" : dialect === "mysql" ? "#self/runtime/migration-handler-mysql" : "#self/runtime/migration-handler");
4534
5003
  compileOptions.migrations = migrations;
4535
5004
  compileOptions.migrationPlanBundle = dialect === "sqlite" ? buildMigrationPlanBundle(migrations) : void 0;
4536
5005
  compileOptions.migrationDialect = dialect;
@@ -4540,15 +5009,37 @@ function routingPlugin(root, options) {
4540
5009
  writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(options.target, compileOptions.honoPath, getEnvSchemaModule()));
4541
5010
  writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
4542
5011
  } else {
4543
- const merged = collectRevalidate(readConfig(root), scan.pages);
5012
+ const buildConfig = readConfig(root);
5013
+ const merged = collectRevalidate(buildConfig, scan.pages);
5014
+ const configuredRevalidate = buildConfig.routing?.revalidate;
4544
5015
  const emitIsr = !options.target && resolvedConfig?.command === "build" && !process.env.VOID_DEPLOY_PROJECT_ID && hasRevalidate(merged);
4545
5016
  isrRevalidate = emitIsr ? merged : void 0;
4546
- const isr = emitIsr ? { runtimePath: resolveFile("#self/runtime/isr-cache") } : void 0;
4547
- 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 ? {
4548
- bindingName: options.sandboxBindingName,
4549
- className: options.sandboxClassName,
4550
- runtimePath: sandboxRuntimePath
4551
- } : false, shouldServeSpaFallback(), options.live, isr, currentNotFoundHandling()));
5017
+ isrPages = emitIsr ? collectPages(scan.pages, configuredRevalidate) : void 0;
5018
+ isrPathRevalidate = emitIsr && configuredRevalidate && typeof configuredRevalidate === "object" ? configuredRevalidate : void 0;
5019
+ const isr = emitIsr ? {
5020
+ runtimePath: resolveFile("@void/isr/runtime"),
5021
+ queryAllowlist: buildConfig.routing?.revalidateQueryAllowlist
5022
+ } : void 0;
5023
+ writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
5024
+ hasQueues: scan.queues.length > 0,
5025
+ remote,
5026
+ assetsDir: resolvedConfig?.build.assetsDir,
5027
+ webSocketClasses: [...(scan.websockets ?? []).map((route) => route.className), ...options.websocketTombstones ?? []],
5028
+ runtimeEnvPath: compileOptions.runtimeEnvPath,
5029
+ runtimeEmailPath: compileOptions.runtimeEmailPath,
5030
+ envSchemaModule: getEnvSchemaModule(),
5031
+ sandbox: options.hasSandbox ? {
5032
+ bindingName: options.sandboxBindingName,
5033
+ className: options.sandboxClassName,
5034
+ runtimePath: sandboxRuntimePath
5035
+ } : false,
5036
+ hasEmail: hasEmailHandlers(),
5037
+ serveSpaFallback: shouldServeSpaFallback(),
5038
+ live: options.live,
5039
+ isr,
5040
+ notFound: currentNotFoundHandling(),
5041
+ durableObjects: options.durableObjects
5042
+ }));
4552
5043
  }
4553
5044
  },
4554
5045
  resolveId: {
@@ -4573,14 +5064,14 @@ function routingPlugin(root, options) {
4573
5064
  }
4574
5065
  if (id !== VIRTUAL_ROUTES_ID) return;
4575
5066
  if (!scan) await rescan();
4576
- return compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, ssr, compileOptions, getPagesCompileOptions(), isrRevalidate);
5067
+ return compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, ssr, compileOptions, getPagesCompileOptions(), isrRevalidate, isrPages, isrPathRevalidate);
4577
5068
  }
4578
5069
  },
4579
5070
  configureServer(devServer) {
4580
5071
  const watcher = devServer.watcher;
4581
5072
  const handleFileChange = async (path, event) => {
4582
5073
  const normalizedPath = normalizePath(path);
4583
- const sourceConventionChanged = isInDirectory(normalizedPath, normalizedRoutesDir) || isInDirectory(normalizedPath, normalizedMiddlewareDir) || isInDirectory(normalizedPath, normalizedJobsDir) || isInDirectory(normalizedPath, normalizedQueuesDir) || isInDirectory(normalizedPath, normalizedPagesDir) || isPotentialAuthConfigPath(normalizedPath) || isPotentialEnvFilePath(normalizedPath);
5074
+ const sourceConventionChanged = isInDirectory(normalizedPath, normalizedRoutesDir) || isInDirectory(normalizedPath, normalizedMiddlewareDir) || isInDirectory(normalizedPath, normalizedJobsDir) || isInDirectory(normalizedPath, normalizedQueuesDir) || isInDirectory(normalizedPath, normalizedEmailDir) || isInDirectory(normalizedPath, normalizedPagesDir) || isPotentialAuthConfigPath(normalizedPath) || isPotentialEnvFilePath(normalizedPath);
4584
5075
  if (!sourceConventionChanged && !isInDirectory(normalizedPath, normalizedSsrDir) && !isPotentialEnvFilePath(normalizedPath)) return;
4585
5076
  if (!sourceConventionChanged && isInDirectory(normalizedPath, normalizedSsrDir) && !ssrEntryAbsPaths.has(normalizedPath)) return;
4586
5077
  await rescan();
@@ -4595,11 +5086,26 @@ function routingPlugin(root, options) {
4595
5086
  if (options.target) {
4596
5087
  writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(options.target, compileOptions.honoPath, getEnvSchemaModule()));
4597
5088
  writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
4598
- } 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 ? {
4599
- bindingName: options.sandboxBindingName,
4600
- className: options.sandboxClassName,
4601
- runtimePath: sandboxRuntimePath
4602
- } : false, shouldServeSpaFallback(), options.live, void 0, currentNotFoundHandling()));
5089
+ } else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
5090
+ hasQueues: scan.queues.length > 0,
5091
+ remote,
5092
+ assetsDir: resolvedConfig?.build.assetsDir,
5093
+ webSocketClasses: [...(scan.websockets ?? []).map((route) => route.className), ...options.websocketTombstones ?? []],
5094
+ runtimeEnvPath: compileOptions.runtimeEnvPath,
5095
+ runtimeEmailPath: compileOptions.runtimeEmailPath,
5096
+ envSchemaModule: getEnvSchemaModule(),
5097
+ sandbox: options.hasSandbox ? {
5098
+ bindingName: options.sandboxBindingName,
5099
+ className: options.sandboxClassName,
5100
+ runtimePath: sandboxRuntimePath
5101
+ } : false,
5102
+ hasEmail: hasEmailHandlers(),
5103
+ serveSpaFallback: shouldServeSpaFallback(),
5104
+ live: options.live,
5105
+ isr: void 0,
5106
+ notFound: currentNotFoundHandling(),
5107
+ durableObjects: options.durableObjects
5108
+ }));
4603
5109
  const structural = event !== "change";
4604
5110
  const pageManifestChanged = isInDirectory(normalizedPath, normalizedPagesDir) && (structural || normalizedPath.endsWith(".md"));
4605
5111
  for (const env of Object.values(devServer.environments)) {
@@ -4657,6 +5163,13 @@ function routingPlugin(root, options) {
4657
5163
  * The token is local to the project and only honored by a worker bound to
4658
5164
  * localhost; treat it like the rest of `.void/` — disposable scratch state,
4659
5165
  * safe to remove manually if desired.
5166
+ *
5167
+ * The file is owner-only (0600). It is the one credential the dev-inbox
5168
+ * routes accept, and loopback is reachable by every account on the host,
5169
+ * so a default-umask 0644 file would hand any other local user the raw MIME,
5170
+ * the DELETE, and the capture endpoint. A file persisted by an earlier
5171
+ * release is tightened in place, never rotated. Windows has no group/other
5172
+ * bits — `mode` and `chmod` only toggle write there — so it is left alone.
4660
5173
  */
4661
5174
  /**
4662
5175
  * Best-effort detection of `vite build` (and `vite preview`) so we can skip
@@ -4671,6 +5184,7 @@ function isViteBuildOrPreview() {
4671
5184
  const subcommand = process.argv[2];
4672
5185
  return subcommand === "build" || subcommand === "preview";
4673
5186
  }
5187
+ const DEV_TRIGGER_TOKEN_FILE_MODE = 384;
4674
5188
  function getOrCreateDevTriggerToken(root) {
4675
5189
  const tokenPath = join(root, ".void", "dev-trigger-token");
4676
5190
  const readExisting = () => {
@@ -4681,12 +5195,21 @@ function getOrCreateDevTriggerToken(root) {
4681
5195
  return;
4682
5196
  }
4683
5197
  };
5198
+ const tightenExisting = () => {
5199
+ if (process.platform === "win32") return;
5200
+ try {
5201
+ if ((statSync(tokenPath).mode & 63) !== 0) chmodSync(tokenPath, DEV_TRIGGER_TOKEN_FILE_MODE);
5202
+ } catch {}
5203
+ };
4684
5204
  const existing = readExisting();
4685
- if (existing) return existing;
5205
+ if (existing) {
5206
+ tightenExisting();
5207
+ return existing;
5208
+ }
4686
5209
  mkdirSync(join(root, ".void"), { recursive: true });
4687
5210
  const fresh = crypto.randomUUID();
4688
5211
  try {
4689
- const fd = openSync(tokenPath, "wx");
5212
+ const fd = openSync(tokenPath, "wx", DEV_TRIGGER_TOKEN_FILE_MODE);
4690
5213
  try {
4691
5214
  writeSync(fd, fresh);
4692
5215
  } finally {
@@ -4770,7 +5293,7 @@ function generateClassATriggerWrapper(root, framework, original, isBuild, assets
4770
5293
  const outDir = join(root, ".void");
4771
5294
  mkdirSync(outDir, { recursive: true });
4772
5295
  const wrapperPath = join(outDir, `${framework}-trigger-wrapper.ts`);
4773
- writeFileIfChanged(wrapperPath, isBuild ? `// @ts-nocheck — Auto-generated by Void for vite build. Do NOT edit.
5296
+ const body = isBuild ? `// @ts-nocheck — Auto-generated by Void for vite build. Do NOT edit.
4774
5297
  import original from ${JSON.stringify(original)};
4775
5298
 
4776
5299
  export default {
@@ -4792,7 +5315,8 @@ export default {
4792
5315
  return original.fetch(request, env, ctx);
4793
5316
  },
4794
5317
  };
4795
- `);
5318
+ `;
5319
+ writeFileIfChanged(wrapperPath, body);
4796
5320
  return wrapperPath;
4797
5321
  }
4798
5322
  /** Generate a worker entry for vinext Pages Router projects. */
@@ -4850,16 +5374,49 @@ function hasBinding(list, name) {
4850
5374
  /**
4851
5375
  * Decide whether to inject a Workers AI binding into the built wrangler config.
4852
5376
  *
4853
- * Build-only: dev/miniflare has no local Workers AI, so serve must never bind
4854
- * `env.AI` (dev uses the P2 `__VOID_TOKEN` proxy path instead).
5377
+ * Builds use the production binding. During dev, a project explicitly saved
5378
+ * with the direct Cloudflare destination uses Cloudflare's remote binding;
5379
+ * managed and unsaved projects keep using the Void HTTPS proxy.
4855
5380
  *
4856
5381
  * `!resolvedAi` is load-bearing: mergeBindings' return is defu-merged over the
4857
5382
  * resolved root-wrangler config and wins, so skipping when the user already
4858
5383
  * declared an `ai` binding avoids clobbering theirs. `!resultAi` is harmless
4859
5384
  * defense-in-depth (nothing else in mergeBindings sets `result.ai`).
4860
5385
  */
4861
- function shouldBindWorkersAi(command, needsAI, resolvedAi, resultAi) {
4862
- return needsAI && command === "build" && !resultAi && !resolvedAi;
5386
+ function shouldBindWorkersAi(command, needsAI, resolvedAi, resultAi, directCloudflareDev = false) {
5387
+ return needsAI && (command === "build" || command === "serve" && directCloudflareDev) && !resultAi && !resolvedAi;
5388
+ }
5389
+ /**
5390
+ * Decide whether to inject the `void dev` email-inbox loopback var.
5391
+ *
5392
+ * The dev inbox buffer lives in the Vite dev server's Node process;
5393
+ * `sendEmail` runs in workerd and reaches it by fetching the inbox capture
5394
+ * endpoint through the worker's assets binding, which
5395
+ * `@cloudflare/vite-plugin` wires straight into the dev server's middleware
5396
+ * stack. The var carries that binding's NAME (`ASSETS` for a Void app, or
5397
+ * whatever a Class A framework's own wrangler config declares) and its
5398
+ * presence is what arms the dev path at runtime — so it must never reach a
5399
+ * production build's `wrangler.json`, exactly like the `__VOID_TOKEN`
5400
+ * credential gate.
5401
+ *
5402
+ * Gated on `routeEnvironment`, not on `command`: `vite preview` also reports
5403
+ * `command: 'serve'`, but it runs the BUILT worker whose assets binding is a
5404
+ * real asset directory rather than a dev-server loopback. `routeEnvironment`
5405
+ * is the existing `command === 'serve' && !isPreview` distinction.
5406
+ *
5407
+ * Without an assets binding there is no loopback, so injecting the var would
5408
+ * only arm a path that must then fail; gate on it instead.
5409
+ */
5410
+ function shouldInjectEmailDevInboxVar(routeEnvironment, needsEmail, assetsBinding) {
5411
+ return routeEnvironment === "dev" && !!needsEmail && typeof assetsBinding === "string" && !!assetsBinding;
5412
+ }
5413
+ /** Resolve the runtime Workers AI binding, preserving a root Wrangler declaration. */
5414
+ function resolveWorkersAiBindingName(resolvedAi, configuredName) {
5415
+ if (typeof resolvedAi === "object" && resolvedAi !== null) {
5416
+ const binding = resolvedAi.binding;
5417
+ if (typeof binding === "string") return binding;
5418
+ }
5419
+ return configuredName ?? "AI";
4863
5420
  }
4864
5421
  const CLOUDFLARE_BUILTIN_IMPORT = /^cloudflare:/;
4865
5422
  const SANDBOX_SDK_IMPORT = "@cloudflare/sandbox";
@@ -4956,9 +5513,11 @@ function readVoidDevCredentials(root) {
4956
5513
  const token = getToken(root);
4957
5514
  if (!token) return { status: "no-token" };
4958
5515
  const staging = isStagingMode(root) || void 0;
5516
+ const apiUrl = getApiUrl(root);
4959
5517
  const project = readProjectConfig(root);
4960
5518
  if (!project) return {
4961
5519
  status: "no-project",
5520
+ apiUrl,
4962
5521
  token,
4963
5522
  staging
4964
5523
  };
@@ -4966,22 +5525,21 @@ function readVoidDevCredentials(root) {
4966
5525
  status: "ok",
4967
5526
  token,
4968
5527
  projectId: project.projectId,
5528
+ proxyUrl: getProxyUrl(root),
4969
5529
  staging
4970
5530
  };
4971
5531
  }
4972
- const DEFAULT_API_URL = "https://api.void.cloud";
4973
- const STAGING_API_URL = "https://api.staging.void.cloud";
4974
5532
  /**
4975
5533
  * Interactive AI setup prompt shown during dev server startup when the user
4976
5534
  * has a token but no linked project. Runs as fire-and-forget so it doesn't
4977
5535
  * block the server. On completion, writes project.json and restarts the server.
4978
5536
  */
4979
- async function runAiSetupPrompt(root, token, staging, server) {
4980
- const { select, text, isCancel, log } = await import("./agents-CtgBYqld.mjs").then((n) => n.C);
4981
- const { PlatformClient } = await import("./client-Gb71-XkG.mjs").then((n) => n.r);
4982
- const { writeProjectConfig } = await import("./client-Gb71-XkG.mjs").then((n) => n.y);
4983
- const { inferDefaultSlug } = await import("./resolve-project-BBMtLLV9.mjs").then((n) => n.i);
4984
- const client = new PlatformClient(token, staging ? STAGING_API_URL : DEFAULT_API_URL);
5537
+ async function runAiSetupPrompt(root, token, apiUrl, server) {
5538
+ const { select, text, isCancel, log } = await import("./output-tFQLLj26.mjs").then((n) => n.T);
5539
+ const { PlatformClient } = await import("./client-CyCHWSO_.mjs").then((n) => n.r);
5540
+ const { attachSelectedPlatform, writeProjectConfig } = await import("./auth-W9WII-mN.mjs").then((n) => n.y);
5541
+ const { inferDefaultSlug } = await import("./resolve-project--Vxawf7z.mjs").then((n) => n.i);
5542
+ const client = new PlatformClient(token, apiUrl);
4985
5543
  let projects;
4986
5544
  try {
4987
5545
  projects = await client.listProjects();
@@ -5006,8 +5564,8 @@ async function runAiSetupPrompt(root, token, staging, server) {
5006
5564
  let projectId;
5007
5565
  let slug;
5008
5566
  if (result === "__create__") {
5009
- const { validateProjectSlug } = await import("./create-project-DsYvl3TB.mjs").then((n) => n.a);
5010
- const { createProjectWithSlugRetry } = await import("./create-project-DsYvl3TB.mjs").then((n) => n.t);
5567
+ const { validateProjectSlug } = await import("./create-project-DMV-csEm.mjs").then((n) => n.a);
5568
+ const { createProjectWithSlugRetry } = await import("./create-project-DMV-csEm.mjs").then((n) => n.t);
5011
5569
  let created;
5012
5570
  try {
5013
5571
  created = await createProjectWithSlugRetry(client, inferDefaultSlug(root), async (defaultSlug) => {
@@ -5017,7 +5575,7 @@ async function runAiSetupPrompt(root, token, staging, server) {
5017
5575
  initialValue: defaultSlug,
5018
5576
  validate: validateProjectSlug
5019
5577
  });
5020
- if (isCancel(slugResult)) return;
5578
+ if (isCancel(slugResult) || typeof slugResult === "symbol") return;
5021
5579
  return slugResult.trim();
5022
5580
  });
5023
5581
  } catch (err) {
@@ -5032,10 +5590,10 @@ async function runAiSetupPrompt(root, token, staging, server) {
5032
5590
  projectId = project.id;
5033
5591
  slug = project.slug;
5034
5592
  }
5035
- writeProjectConfig(root, {
5593
+ writeProjectConfig(root, attachSelectedPlatform(root, {
5036
5594
  projectId,
5037
5595
  slug
5038
- });
5596
+ }, client.apiUrl));
5039
5597
  log.success(`Linked to ${import_picocolors.default.blue(slug)}`);
5040
5598
  await server.restart();
5041
5599
  }
@@ -5059,10 +5617,12 @@ async function runAiSetupPrompt(root, token, staging, server) {
5059
5617
  */
5060
5618
  function voidPlugin(options) {
5061
5619
  const root = process.cwd();
5620
+ const directCloudflareDestination = readDeploymentPlatform(root) === "cloudflare";
5062
5621
  warnIfPackageJsonIsNotEsm(root);
5063
5622
  const devTriggerToken = isViteBuildOrPreview() ? crypto.randomUUID() : getOrCreateDevTriggerToken(root);
5064
5623
  const config = readConfig(root);
5065
5624
  const paths = resolveProjectPaths(root, config);
5625
+ let routeEnvironment = "prod";
5066
5626
  assertNoRootSourceConventionConflicts(paths);
5067
5627
  const dialect = getDatabaseDialect(config);
5068
5628
  const detected = detectFramework(root);
@@ -5070,6 +5630,13 @@ function voidPlugin(options) {
5070
5630
  const scanDirs = config.inference?.scanDirs ?? (detected ? FRAMEWORK_SCAN_DIRS : void 0);
5071
5631
  const inferenceOptions = config.inference?.bindings ? { bindings: config.inference.bindings } : void 0;
5072
5632
  const bindingNames = resolveBindingNames(config.inference?.bindings);
5633
+ const frameworkOwnsCloudflareRuntime = detected !== void 0 && (detected.class === "b" || detected.class === "c");
5634
+ const localD1PersistRoot = frameworkOwnsCloudflareRuntime ? join(root, ".wrangler", "state") : options?.persistTo ?? join(root, ".void");
5635
+ const localD1 = resolveLocalD1Target(root, {
5636
+ bindingName: bindingNames?.d1 ?? "DB",
5637
+ persistRoot: localD1PersistRoot
5638
+ });
5639
+ writeLocalD1PersistState(root, !isNodeTarget(config.target) && !frameworkOwnsCloudflareRuntime ? options?.persistTo : void 0);
5073
5640
  const inferredBindings = inferProjectBindings(root, {
5074
5641
  ...inferenceOptions,
5075
5642
  paths
@@ -5084,6 +5651,8 @@ function voidPlugin(options) {
5084
5651
  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.");
5085
5652
  if (detected && bindings.needsSandbox) throw new Error("sandbox: Cloudflare Sandboxes are currently supported in Void apps only.");
5086
5653
  if (detected && bindings.needsLive) throw new Error("live: void/live is currently supported in Void apps only.");
5654
+ const hasInboundEmail = hasEmailHandlers(scanEmailHandlersSync(paths.sourceRoot));
5655
+ 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.");
5087
5656
  const STUB_PREFIX = "\0void-client-stub:";
5088
5657
  const SERVER_ONLY_STUBS = {
5089
5658
  "void/db": "export const db = {}; export const createDb = () => ({});",
@@ -5100,12 +5669,37 @@ function voidPlugin(options) {
5100
5669
  this.reason = reason;
5101
5670
  }
5102
5671
  }
5672
+ export class SandboxUnavailableError extends Error {
5673
+ constructor(message = 'Managed Sandbox is unavailable on this Void platform.') {
5674
+ super(message);
5675
+ this.name = 'SandboxUnavailableError';
5676
+ }
5677
+ }
5103
5678
  export async function getSandbox() {
5104
5679
  throw new Error('void/sandbox is server-only and cannot be imported by browser code.');
5105
5680
  }
5106
5681
  export const sandbox = { get: getSandbox };
5107
5682
  `,
5108
- "void/live": ""
5683
+ "void/email": `
5684
+ const serverOnly = (name) => () => {
5685
+ throw new Error(name + ' is server-only and cannot be imported by browser code.');
5686
+ };
5687
+ export const sendEmail = serverOnly('sendEmail');
5688
+ export const createEmail = serverOnly('createEmail');
5689
+ export const defineEmail = serverOnly('defineEmail');
5690
+ export const parseEmail = serverOnly('parseEmail');
5691
+ export const replyEmail = serverOnly('replyEmail');
5692
+ export const createMessageProxy = serverOnly('createMessageProxy');
5693
+ `,
5694
+ "void/email/testing": `
5695
+ const serverOnly = (name) => () => {
5696
+ throw new Error(name + ' is server-only and cannot be imported by browser code.');
5697
+ };
5698
+ export const createEmailTestHarness = serverOnly('createEmailTestHarness');
5699
+ export const createInboundTestHarness = serverOnly('createInboundTestHarness');
5700
+ `,
5701
+ "void/live": "",
5702
+ "void/durable": ""
5109
5703
  };
5110
5704
  const STUB_RESOLVE_FILTER = new RegExp(`^(${Object.keys(SERVER_ONLY_STUBS).join("|").replaceAll("/", "\\/")})$`);
5111
5705
  const STUB_LOAD_FILTER = new RegExp(`^\\0void-client-stub:`);
@@ -5116,7 +5710,7 @@ function voidPlugin(options) {
5116
5710
  filter: { id: STUB_RESOLVE_FILTER },
5117
5711
  handler(id) {
5118
5712
  if (this.environment?.name === "client") {
5119
- if (id === "void/live") this.error("void/live is server-only and cannot be imported by browser code. Import void/live/client instead.");
5713
+ 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.");
5120
5714
  return STUB_PREFIX + id;
5121
5715
  }
5122
5716
  }
@@ -5128,16 +5722,40 @@ function voidPlugin(options) {
5128
5722
  }
5129
5723
  }
5130
5724
  };
5725
+ const serverRuntimeOptimizerPlugin = {
5726
+ name: "void:server-runtime-optimizer",
5727
+ configEnvironment(name) {
5728
+ if (name !== "client") return { optimizeDeps: { exclude: ["void"] } };
5729
+ }
5730
+ };
5131
5731
  const queues = framework ? [] : scanQueuesSync(root, paths.queuesDir);
5132
5732
  const websocketRoutes = framework ? [] : scanWebSocketRoutesSync(root, paths);
5733
+ const durableObjects = scanDurableObjectModules(root);
5734
+ if (detected && durableObjects.length > 0) throw new Error("durable: durable-objects/ is currently supported in Void apps only, not framework adapter mode.");
5735
+ for (const module of durableObjects) {
5736
+ const websocketCollision = websocketRoutes.find((route) => route.bindingName === module.bindingName || route.className === module.className);
5737
+ if (websocketCollision) throw new Error(`durable: durable-objects/${module.filePath.split("/").pop()} conflicts with WebSocket route '${websocketCollision.filePath}'. Rename one of the files.`);
5738
+ 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.`);
5739
+ }
5740
+ let websocketTombstones = [];
5741
+ if (!isNodeTarget(config.target)) {
5742
+ syncWranglerDurableObjects(root, durableObjects);
5743
+ const productionWebSocketRoutes = websocketRoutes.filter((route) => isRouteEnabledForEnvironment(route, "prod"));
5744
+ syncWranglerWebSockets(root, productionWebSocketRoutes);
5745
+ websocketTombstones = readWranglerWebSocketTombstones(root, productionWebSocketRoutes);
5746
+ }
5133
5747
  const liveConfig = !isNodeTarget(config.target) ? bindings.needsLive ? resolveLiveBindingConfig(root, {
5134
- reservedBindings: websocketRoutes.map((route) => route.bindingName),
5135
- reservedClasses: [...websocketRoutes.map((route) => route.className), sandboxConfig?.className]
5748
+ reservedBindings: [...websocketRoutes.map((route) => route.bindingName), ...durableObjects.map((module) => module.bindingName)],
5749
+ reservedClasses: [
5750
+ ...websocketRoutes.map((route) => route.className),
5751
+ ...durableObjects.map((module) => module.className),
5752
+ sandboxConfig?.className
5753
+ ]
5136
5754
  }) : readExistingLiveBindingConfig(root) : void 0;
5137
5755
  if (detected && (detected.class === "b" || detected.class === "c")) {
5138
- const frameworkPersistRoot = join(root, ".wrangler", "state");
5756
+ const frameworkPersistRoot = localD1.persistRoot;
5139
5757
  const syncFrameworkCompat = (compat) => {
5140
- if (ensureWranglerCompatibilityDate(root, compat.compatibilityDate)) voidLog("Synced compatibility_date to wrangler config");
5758
+ if (ensureWranglerCompatibilityDate(root, compat.compatibilityDate)) voidLog("Synced compatibility_date to Cloudflare config");
5141
5759
  };
5142
5760
  const wranglerCompatPlugin = {
5143
5761
  name: "void:wrangler-compat",
@@ -5145,7 +5763,7 @@ function voidPlugin(options) {
5145
5763
  config(_, env) {
5146
5764
  syncFrameworkCompat(ensureWranglerCompat(root, config));
5147
5765
  if (env.command !== "serve") return;
5148
- if (ensureWranglerNodejsAlsFlag(root)) voidLog("Synced nodejs_als to wrangler config");
5766
+ if (ensureWranglerNodejsAlsFlag(root)) voidLog("Synced nodejs_als to Cloudflare config");
5149
5767
  },
5150
5768
  buildStart() {
5151
5769
  syncFrameworkCompat(ensureWranglerCompat(root, config));
@@ -5202,6 +5820,7 @@ function voidPlugin(options) {
5202
5820
  ...nuxtVite8WebSocketCompatPlugin ? [nuxtVite8WebSocketCompatPlugin] : [],
5203
5821
  svelteKitHooksInjectPlugin,
5204
5822
  clientStubsPlugin,
5823
+ serverRuntimeOptimizerPlugin,
5205
5824
  bindingRuntimePlugin(bindingNames),
5206
5825
  envSchemaPlugin(root),
5207
5826
  envClientGuardPlugin(root),
@@ -5209,9 +5828,10 @@ function voidPlugin(options) {
5209
5828
  drizzlePlugin(root, false, dialect, {
5210
5829
  d1BindingName: bindingNames?.d1,
5211
5830
  forceVirtualDb: authEnabled || bindingNames?.d1 !== void 0,
5831
+ localD1DatabaseId: localD1.databaseId,
5212
5832
  localD1PersistRoot: frameworkPersistRoot
5213
5833
  }),
5214
- migrationPlugin(root, dialect, "local", frameworkPersistRoot, { enabled: false }),
5834
+ migrationPlugin(root, dialect, localD1.databaseId, frameworkPersistRoot, { enabled: false }),
5215
5835
  devTriggersHintPlugin(root, {
5216
5836
  framework: true,
5217
5837
  devTriggerToken
@@ -5224,6 +5844,10 @@ function voidPlugin(options) {
5224
5844
  }
5225
5845
  const remoteMode = resolveRemoteMode(config.remote);
5226
5846
  const workerConfig = {};
5847
+ const jobs = scanJobsSync(root, paths.cronsDir);
5848
+ if (jobs.length > 0) workerConfig.triggers = { crons: [...new Set(jobs.flatMap((job) => job.crons))] };
5849
+ let envSchemaFacts;
5850
+ let assetRouting;
5227
5851
  if (config.worker?.vars) workerConfig.vars = { ...config.worker.vars };
5228
5852
  let viteCommand = "build";
5229
5853
  const wranglerCompat = isNodeTarget(config.target) ? void 0 : readWranglerCompat(root, config);
@@ -5231,17 +5855,35 @@ function voidPlugin(options) {
5231
5855
  workerConfig.compatibility_date = wranglerCompat.compatibilityDate;
5232
5856
  if (wranglerCompat.compatibilityFlags) workerConfig.compatibility_flags = wranglerCompat.compatibilityFlags;
5233
5857
  }
5858
+ if (config.worker?.limits?.cpu_ms) workerConfig.limits = { cpu_ms: config.worker.limits.cpu_ms };
5234
5859
  const inspectorPortEnv = process.env.VOID_INSPECTOR_PORT;
5235
5860
  const inspectorPort = inspectorPortEnv === "false" ? false : typeof inspectorPortEnv === "string" && inspectorPortEnv.length > 0 ? Number.parseInt(inspectorPortEnv, 10) : void 0;
5236
5861
  const inspectorPortOption = inspectorPort === false ? { inspectorPort: false } : typeof inspectorPort === "number" ? { inspectorPort } : {};
5237
5862
  const outDir = join(root, ".void");
5238
5863
  mkdirSync(outDir, { recursive: true });
5239
5864
  function mergeBindings(resolved) {
5865
+ const environmentWebsocketRoutes = websocketRoutes.filter((route) => isRouteEnabledForEnvironment(route, routeEnvironment));
5240
5866
  const result = { ...workerConfig };
5241
- const generatedAssets = workerConfig.assets;
5242
- if (generatedAssets && resolved.assets) {
5243
- if (generatedAssets.not_found_handling !== void 0) delete resolved.assets.not_found_handling;
5244
- if (generatedAssets.run_worker_first !== void 0) delete resolved.assets.run_worker_first;
5867
+ result.compatibility_flags = [.../* @__PURE__ */ new Set([...result.compatibility_flags ?? [], ...resolved.compatibility_flags ?? []])];
5868
+ resolved.compatibility_flags = [];
5869
+ if (envSchemaFacts || authEnabled && process.env["VOID_CLOUDFLARE_DEPLOY_BUILD"] === "1") {
5870
+ const plaintextVars = {
5871
+ ...resolved.vars ?? {},
5872
+ ...result.vars ?? {}
5873
+ };
5874
+ const plaintextServerKeys = (envSchemaFacts?.serverKeys ?? []).filter((key) => key in plaintextVars);
5875
+ 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.`);
5876
+ const configuredRequired = resolved.secrets?.required ?? [];
5877
+ const required = [.../* @__PURE__ */ new Set([
5878
+ ...configuredRequired,
5879
+ ...envSchemaFacts?.requiredServerKeys ?? [],
5880
+ ...authEnabled && process.env["VOID_CLOUDFLARE_DEPLOY_BUILD"] === "1" ? ["BETTER_AUTH_SECRET"] : []
5881
+ ])];
5882
+ if (required.length > 0) result.secrets = { required };
5883
+ }
5884
+ if (workerConfig.assets && resolved.assets) {
5885
+ delete resolved.assets.not_found_handling;
5886
+ delete resolved.assets.run_worker_first;
5245
5887
  }
5246
5888
  const d1Name = bindingNames?.d1 ?? "DB";
5247
5889
  const d1 = [];
@@ -5263,10 +5905,41 @@ function voidPlugin(options) {
5263
5905
  binding: r2Name,
5264
5906
  bucket_name: "default"
5265
5907
  }];
5266
- if (shouldBindWorkersAi(viteCommand, bindings.needsAI, resolved.ai, result.ai)) result.ai = { binding: "AI" };
5267
- if (config.ai?.gateway) {
5908
+ const resolvedAi = resolved.ai;
5909
+ const resultAi = result.ai;
5910
+ const aiBindingName = resolveWorkersAiBindingName(resolvedAi, bindingNames?.ai);
5911
+ if (viteCommand === "serve" && bindings.needsAI && directCloudflareDestination && !resultAi && typeof resolvedAi === "object" && resolvedAi !== null && "binding" in resolvedAi && typeof resolvedAi.binding === "string") result.ai = {
5912
+ ...resolvedAi,
5913
+ remote: true
5914
+ };
5915
+ else if (shouldBindWorkersAi(viteCommand, bindings.needsAI, resolvedAi, resultAi, directCloudflareDestination)) result.ai = {
5916
+ binding: aiBindingName,
5917
+ ...viteCommand === "serve" && { remote: true }
5918
+ };
5919
+ if (aiBindingName !== "AI" || config.ai?.gateway) {
5268
5920
  const vars = result.vars ?? {};
5269
- vars.__VOID_AI_GATEWAY = config.ai.gateway;
5921
+ if (aiBindingName !== "AI") vars.__VOID_AI_BINDING = aiBindingName;
5922
+ if (config.ai?.gateway) vars.__VOID_AI_GATEWAY = config.ai.gateway;
5923
+ result.vars = vars;
5924
+ }
5925
+ const emailDevInboxBinding = workerConfig.assets?.binding ?? resolved.assets?.binding;
5926
+ if (shouldInjectEmailDevInboxVar(routeEnvironment, bindings.needsEmail, emailDevInboxBinding)) {
5927
+ const vars = result.vars ?? {};
5928
+ vars[DEV_INBOX_ENV_VAR] = emailDevInboxBinding;
5929
+ vars[DEV_INBOX_TOKEN_ENV_VAR] = devTriggerToken;
5930
+ result.vars = vars;
5931
+ }
5932
+ if (viteCommand === "serve" && bindings.needsEmail) {
5933
+ const sendEmail = resolved.send_email;
5934
+ if (Array.isArray(sendEmail)) {
5935
+ const kept = sendEmail.filter((entry) => entry?.name !== SEND_EMAIL_BINDING);
5936
+ if (kept.length > 0) resolved.send_email = kept;
5937
+ else delete resolved.send_email;
5938
+ }
5939
+ }
5940
+ if (viteCommand === "serve" && config.email?.from) {
5941
+ const vars = result.vars ?? {};
5942
+ vars.__VOID_EMAIL_FROM = config.email.from;
5270
5943
  result.vars = vars;
5271
5944
  }
5272
5945
  if (queues.length > 0) result.queues = {
@@ -5282,17 +5955,40 @@ function voidPlugin(options) {
5282
5955
  ...q.retryDelay != null && { retry_delay: q.retryDelay }
5283
5956
  }))
5284
5957
  };
5285
- if (websocketRoutes.length > 0) {
5958
+ if (environmentWebsocketRoutes.length > 0) {
5286
5959
  const existingDurableObjectBindings = resolved.durable_objects?.bindings ?? [];
5287
- const missingDurableObjectBindings = websocketRoutes.filter((route) => !existingDurableObjectBindings.some((binding) => binding.name === route.bindingName)).map((route) => ({
5960
+ for (const route of environmentWebsocketRoutes) {
5961
+ const conflict = existingDurableObjectBindings.find((binding) => binding.name === route.bindingName && binding.class_name !== route.className);
5962
+ if (conflict) throw new Error(`websocket: binding '${route.bindingName}' is already configured for class '${conflict.class_name}', but '${route.filePath}' requires '${route.className}'.`);
5963
+ }
5964
+ const missingDurableObjectBindings = environmentWebsocketRoutes.filter((route) => !existingDurableObjectBindings.some((binding) => binding.name === route.bindingName)).map((route) => ({
5288
5965
  name: route.bindingName,
5289
5966
  class_name: route.className
5290
5967
  }));
5291
5968
  if (missingDurableObjectBindings.length > 0) result.durable_objects = { bindings: [...existingDurableObjectBindings, ...missingDurableObjectBindings] };
5292
- if (!Array.isArray(resolved.migrations)) result.migrations = [{
5293
- tag: "void-ws-v1",
5294
- new_classes: websocketRoutes.map((route) => route.className)
5295
- }];
5969
+ const currentMigrations = resolved.migrations;
5970
+ const planned = appendSqliteClassMigration(Array.isArray(currentMigrations) ? currentMigrations : [], environmentWebsocketRoutes.map((route) => route.className), "void-ws-v1", existingDurableObjectBindings.map((binding) => binding.class_name));
5971
+ if (planned.added) result.migrations = planned.migrations;
5972
+ }
5973
+ if (durableObjects.length > 0) {
5974
+ const existingBindings = (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
5975
+ for (const module of durableObjects) {
5976
+ const conflict = existingBindings.find((binding) => binding.name === module.bindingName && binding.class_name !== module.className);
5977
+ if (conflict) throw new Error(`durable: binding '${module.bindingName}' is already configured for class '${conflict.class_name}', but durable-objects/ requires '${module.className}'.`);
5978
+ }
5979
+ const additions = durableObjects.filter((module) => !existingBindings.some((binding) => binding.name === module.bindingName)).map((module) => ({
5980
+ name: module.bindingName,
5981
+ class_name: module.className
5982
+ }));
5983
+ if (additions.length > 0) result.durable_objects = { bindings: [...existingBindings, ...additions] };
5984
+ const currentMigrations = result.migrations ?? resolved.migrations;
5985
+ const existingMigrations = Array.isArray(currentMigrations) ? currentMigrations : [];
5986
+ 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.`);
5987
+ const migrationAdditions = durableObjects.filter((module) => !existingMigrations.some((migration) => migration.tag === module.migrationTag || migration.new_sqlite_classes?.includes(module.className))).map((module) => ({
5988
+ tag: module.migrationTag,
5989
+ new_sqlite_classes: [module.className]
5990
+ }));
5991
+ if (migrationAdditions.length > 0) result.migrations = [...existingMigrations, ...migrationAdditions];
5296
5992
  }
5297
5993
  if (bindings.needsSandbox && sandboxConfig) {
5298
5994
  const existingDurableObjectBindings = (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
@@ -5329,12 +6025,13 @@ function voidPlugin(options) {
5329
6025
  new_sqlite_classes: [liveConfig.className]
5330
6026
  }];
5331
6027
  }
5332
- if (viteCommand === "serve" && bindings.needsAI) {
6028
+ if (viteCommand === "serve" && bindings.needsAI && !directCloudflareDestination) {
5333
6029
  const credentials = readVoidDevCredentials(root);
5334
6030
  if (credentials.status === "ok") {
5335
6031
  const vars = result.vars ?? {};
5336
6032
  vars.__VOID_TOKEN = credentials.token;
5337
6033
  vars.__VOID_PROJECT_ID = credentials.projectId;
6034
+ vars.__VOID_PROXY_URL = credentials.proxyUrl;
5338
6035
  if (credentials.staging) vars.__VOID_STAGING = "1";
5339
6036
  result.vars = vars;
5340
6037
  }
@@ -5343,7 +6040,7 @@ function voidPlugin(options) {
5343
6040
  const credentials = readVoidDevCredentials(root);
5344
6041
  if (credentials.status === "ok") {
5345
6042
  const vars = result.vars ?? {};
5346
- vars.__VOID_PROXY_URL = credentials.staging ? STAGING_PROXY_URL : DEFAULT_PROXY_URL;
6043
+ vars.__VOID_PROXY_URL = credentials.proxyUrl;
5347
6044
  vars.__VOID_TOKEN = credentials.token;
5348
6045
  vars.__VOID_PROJECT_ID = credentials.projectId;
5349
6046
  vars.__VOID_REMOTE = "1";
@@ -5352,31 +6049,34 @@ function voidPlugin(options) {
5352
6049
  }
5353
6050
  return result;
5354
6051
  }
5355
- function applySchemaDefaultsToWorkerVars(defaults) {
5356
- const vars = workerConfig.vars ?? {};
5357
- for (const [key, value] of Object.entries(defaults)) if (vars[key] === void 0) vars[key] = value;
5358
- workerConfig.vars = vars;
6052
+ function applyEnvSchema(facts) {
6053
+ envSchemaFacts = facts;
5359
6054
  }
5360
- let voidDeployBuildConfig;
5361
6055
  const envPlugin = {
5362
6056
  name: "void:env",
5363
- api: { getVoidDeployBuildConfig: () => voidDeployBuildConfig },
5364
- config(userConfig, { mode, command }) {
6057
+ config(userConfig, { command, isPreview }) {
5365
6058
  viteCommand = command;
6059
+ routeEnvironment = command === "serve" && !isPreview ? "dev" : "prod";
6060
+ if (command === "serve" && directCloudflareDestination && bindings.needsAI) process.env.CLOUDFLARE_AUTH_USE_KEYRING = "true";
6061
+ if (!framework && !isNodeTarget(config.target)) {
6062
+ managedCloudflareBuild = command === "build" && Boolean(process.env.VOID_DEPLOY_PROJECT_ID);
6063
+ assetRoutingPublicDir = userConfig.publicDir === false ? null : typeof userConfig.publicDir === "string" ? resolve(root, userConfig.publicDir) : join(root, "public");
6064
+ assetRouting = resolveConfiguredAssetRoutingPolicy();
6065
+ applyAssetRoutingPolicy(assetRouting);
6066
+ }
5366
6067
  const ignored = ["**/.void/**"];
5367
- const rawEnvDir = typeof userConfig.envDir === "string" && userConfig.envDir.length > 0 ? userConfig.envDir : void 0;
5368
- voidDeployBuildConfig = {
5369
- mode,
5370
- envDir: rawEnvDir
5371
- };
5372
- const { sanitized: env, stripped } = stripInternalEnvKeys(filterLoadedEnv(loadEnv(mode, rawEnvDir ? join(root, rawEnvDir) : root, "")));
5373
- if (stripped.length > 0) voidWarn(`Ignoring internal env keys from .env files: ${stripped.join(", ")}. These are reserved for Void runtime use.`);
5374
- workerConfig.vars = {
5375
- ...workerConfig.vars,
5376
- ...env
5377
- };
6068
+ if (command === "serve") {
6069
+ const local = loadLocalEnv(root, { onWarn: (message) => voidWarn(message) });
6070
+ const { sanitized: env, stripped } = stripInternalEnvKeys(local);
6071
+ if (stripped.length > 0) voidWarn(`Ignoring internal env keys from .env: ${stripped.join(", ")}. These are reserved for Void runtime use.`);
6072
+ workerConfig.vars = {
6073
+ ...workerConfig.vars,
6074
+ ...env
6075
+ };
6076
+ if (isPreview) for (const [key, value] of Object.entries(env)) process.env[key] ??= value;
6077
+ }
5378
6078
  const assets = workerConfig.assets;
5379
- if (command === "serve" && assets?.run_worker_first?.includes("/**")) assets.run_worker_first = [...DEV_ASSET_ROUTING_EXCLUSIONS, "/**"];
6079
+ if (command === "serve" && !isPreview && assets?.run_worker_first?.includes("/**")) assets.run_worker_first = [...DEV_ASSET_ROUTING_EXCLUSIONS, "/**"];
5380
6080
  const watchConfig = { server: { watch: { ignored } } };
5381
6081
  if (!isNodeTarget(config.target) && config.output !== "static") {
5382
6082
  const envNames = framework === "vinext-app" ? ["rsc", "ssr"] : ["ssr"];
@@ -5414,20 +6114,22 @@ function voidPlugin(options) {
5414
6114
  voidWarn("Falling back to local bindings.");
5415
6115
  });
5416
6116
  }
5417
- if (bindings.needsAI) {
6117
+ if (bindings.needsAI && !directCloudflareDestination) {
5418
6118
  const credentials = readVoidDevCredentials(root);
5419
6119
  if (credentials.status === "no-token") afterReady(() => {
5420
6120
  console.log();
5421
6121
  voidWarn("AI requires authentication. Run `void auth login`, then restart the dev server.");
5422
6122
  });
5423
- else if (credentials.status === "no-project") if (process.stdin.isTTY) afterReady(() => {
5424
- console.log();
5425
- runAiSetupPrompt(root, credentials.token, credentials.staging, server);
5426
- });
5427
- else afterReady(() => {
5428
- console.log();
5429
- voidWarn("AI requires a linked project. Run `void project link` to set one up.");
5430
- });
6123
+ else if (credentials.status === "no-project") {
6124
+ if (process.stdin.isTTY) afterReady(() => {
6125
+ console.log();
6126
+ runAiSetupPrompt(root, credentials.token, credentials.apiUrl, server);
6127
+ });
6128
+ else afterReady(() => {
6129
+ console.log();
6130
+ voidWarn("AI requires a linked project. Run `void project link` to set one up.");
6131
+ });
6132
+ }
5431
6133
  }
5432
6134
  }
5433
6135
  };
@@ -5444,7 +6146,8 @@ function voidPlugin(options) {
5444
6146
  if (hintShown) return;
5445
6147
  if (!watchDirs.some((d) => path.startsWith(d))) return;
5446
6148
  try {
5447
- const fileBindings = inferBindingsFromSource(readFileSync(path, "utf-8"), path.split("/").pop());
6149
+ const code = readFileSync(path, "utf-8");
6150
+ const fileBindings = inferBindingsFromSource(code, path.split("/").pop());
5448
6151
  const newBindings = [];
5449
6152
  if (autoDetectBindings) {
5450
6153
  if (fileBindings.needsD1 && !bindings.needsD1) newBindings.push("D1 (void/db)");
@@ -5452,6 +6155,7 @@ function voidPlugin(options) {
5452
6155
  if (fileBindings.needsR2 && !bindings.needsR2) newBindings.push("R2 (void/storage)");
5453
6156
  if (fileBindings.needsAI && !bindings.needsAI) newBindings.push("AI (void/ai)");
5454
6157
  if (fileBindings.needsSandbox && !bindings.needsSandbox) newBindings.push("Sandbox (void/sandbox)");
6158
+ if (fileBindings.needsEmail && !bindings.needsEmail) newBindings.push("Email (void/email)");
5455
6159
  }
5456
6160
  if (autoDetectAuth && fileBindings.needsAuth) newBindings.push("Auth (void/auth)");
5457
6161
  if (newBindings.length > 0) {
@@ -5478,18 +6182,15 @@ function voidPlugin(options) {
5478
6182
  };
5479
6183
  if (framework) {
5480
6184
  workerConfig.main = getFrameworkEntry(framework, root, devTriggerToken, config.routing?.notFound);
6185
+ let notFoundIgnoredWarned = false;
5481
6186
  const frameworkBuildWrapperPlugin = {
5482
6187
  name: "void:framework-build-wrapper",
5483
- config(_userConfig, env) {
5484
- const notFound = config.routing?.notFound;
5485
- if (env.command !== "build" || notFound === void 0) return;
5486
- workerConfig.assets = {
5487
- ...workerConfig.assets,
5488
- not_found_handling: notFound
5489
- };
5490
- },
5491
6188
  configResolved(resolved) {
5492
6189
  if (resolved.command !== "build") return;
6190
+ if (config.routing?.notFound !== void 0 && !process.env.VOID_DEPLOY_BUILD && !notFoundIgnoredWarned) {
6191
+ notFoundIgnoredWarned = true;
6192
+ 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.`);
6193
+ }
5493
6194
  generateClassATriggerWrapper(root, framework, getOriginalFrameworkEntry(framework, root), true, resolved.build.assetsDir, config.routing?.notFound);
5494
6195
  }
5495
6196
  };
@@ -5517,19 +6218,22 @@ function voidPlugin(options) {
5517
6218
  createWranglerCompatPersistPlugin(root, config),
5518
6219
  restartPlugin(),
5519
6220
  bindingWatchPlugin,
6221
+ ...bindings.needsEmail ? [emailDevInboxPlugin({ devTriggerToken })] : [],
5520
6222
  envPlugin,
5521
- envSchemaPlugin(root, { onDefaults: applySchemaDefaultsToWorkerVars }),
6223
+ envSchemaPlugin(root, { onSchema: applyEnvSchema }),
5522
6224
  envClientGuardPlugin(root),
5523
6225
  envClientFoldPlugin(),
5524
6226
  clientStubsPlugin,
6227
+ serverRuntimeOptimizerPlugin,
5525
6228
  bindingRuntimePlugin(bindingNames),
5526
6229
  drizzlePlugin(root, false, getDatabaseDialect(config), {
5527
6230
  d1BindingName: bindingNames?.d1,
5528
6231
  forceVirtualDb: authEnabled || bindingNames?.d1 !== void 0,
5529
- localD1PersistRoot: options?.persistTo ?? outDir,
6232
+ localD1DatabaseId: localD1.databaseId,
6233
+ localD1PersistRoot: localD1.persistRoot,
5530
6234
  redirectDbTypePaths
5531
6235
  }),
5532
- migrationPlugin(root, getDatabaseDialect(config)),
6236
+ migrationPlugin(root, getDatabaseDialect(config), localD1.databaseId, localD1.persistRoot),
5533
6237
  ...vinextCompat ? [vinextCompat] : [],
5534
6238
  frameworkBuildWrapperPlugin,
5535
6239
  frameworkTriggersPlugin(root, { devTriggerToken }),
@@ -5539,7 +6243,7 @@ function voidPlugin(options) {
5539
6243
  }),
5540
6244
  ...cloudflare({
5541
6245
  ...inspectorPortOption,
5542
- persistState: { path: options?.persistTo ?? outDir },
6246
+ persistState: { path: localD1.persistRoot },
5543
6247
  auxiliaryWorkers: options?.auxiliaryWorkers,
5544
6248
  viteEnvironment,
5545
6249
  config: mergeBindings
@@ -5553,10 +6257,13 @@ function voidPlugin(options) {
5553
6257
  if (config.remote) voidWarn("\"remote\" in void.json is ignored with target: \"" + config.target + "\"");
5554
6258
  if (bindings.needsSandbox) throw new Error(`sandbox: Cloudflare Sandboxes require target: "cloudflare". They are not available with target: '${config.target}'.`);
5555
6259
  if (bindings.needsLive) throw new Error(`live: void/live requires target: "cloudflare". It is not available with target: '${config.target}'.`);
6260
+ if (durableObjects.length > 0) throw new Error(`durable: durable-objects/ requires target: "cloudflare". It is not available with target: '${config.target}'.`);
6261
+ if (hasInboundEmail) throw new Error(`email: inbound email handlers (email/) require target: "cloudflare". They are not available with target: '${config.target}'.`);
5556
6262
  if (config.routing?.revalidate) voidWarn("\"routing.revalidate\" in void.json is ignored with target: \"" + config.target + "\" (edge caching is CF-only)");
5557
6263
  return [
5558
6264
  restartPlugin(),
5559
6265
  envPlugin,
6266
+ serverRuntimeOptimizerPlugin,
5560
6267
  envSchemaPlugin(root),
5561
6268
  ...pagesDevCssAssetsPlugin(root, devTriggerToken),
5562
6269
  routingPlugin(root, {
@@ -5574,28 +6281,43 @@ function voidPlugin(options) {
5574
6281
  ];
5575
6282
  }
5576
6283
  const existingFlags = workerConfig.compatibility_flags ?? [];
5577
- const normalizedFlags = normalizeWranglerCompatibilityFlags(existingFlags, { needsNodejsCompat: authEnabled || dialect === "postgresql" || bindings.needsSandbox });
6284
+ const needsNodejsCompat = authEnabled || dialect !== "sqlite" || bindings.needsSandbox;
6285
+ const normalizedFlags = normalizeWranglerCompatibilityFlags(existingFlags, { needsNodejsCompat });
5578
6286
  if (normalizedFlags && normalizedFlags !== existingFlags) workerConfig.compatibility_flags = normalizedFlags;
5579
- const jobs = scanJobsSync(root, paths.cronsDir);
5580
6287
  const ssrEntry = validateSsrEntry(root, paths);
5581
6288
  const hasPages = existsSync(paths.pagesDir);
5582
6289
  const hasMiddleware = scanMiddlewareSync(root, paths).length > 0;
5583
- const routePatterns = scanRoutePatternsSync(root, paths);
5584
- const hasDocumentRoutes$1 = hasDocumentRoutes(routePatterns);
5585
- const hasApiRoutes$1 = hasApiRoutes(routePatterns) || hasApiRoutes(websocketRoutes) || authEnabled;
5586
- let assetRouting = resolveAssetRoutingPolicy({
5587
- isSsr: Boolean(ssrEntry),
5588
- hasPages,
5589
- hasApiRoutes: hasApiRoutes$1,
5590
- hasDocumentRoutes: hasDocumentRoutes$1,
5591
- hasMiddleware,
5592
- hasWebSockets: hasDocumentRoutes(websocketRoutes),
5593
- authEnabled,
5594
- liveEnabled: liveConfig !== void 0,
5595
- notFound: config.routing?.notFound
5596
- });
6290
+ let assetRoutingPublicDir = join(root, "public");
6291
+ let managedCloudflareBuild = false;
6292
+ function resolveConfiguredAssetRoutingPolicy() {
6293
+ const routePatterns = scanRoutePatternsSync(root, paths, routeEnvironment);
6294
+ const environmentWebsocketRoutes = websocketRoutes.filter((route) => isRouteEnabledForEnvironment(route, routeEnvironment));
6295
+ const policy = resolveAssetRoutingPolicy({
6296
+ isSsr: Boolean(ssrEntry),
6297
+ hasPages,
6298
+ hasApiRoutes: hasApiRoutes(routePatterns) || hasApiRoutes(environmentWebsocketRoutes) || authEnabled,
6299
+ hasDocumentRoutes: hasDocumentRoutes(routePatterns),
6300
+ hasMiddleware,
6301
+ hasWebSockets: hasDocumentRoutes(environmentWebsocketRoutes),
6302
+ authEnabled,
6303
+ liveEnabled: liveConfig !== void 0,
6304
+ notFound: config.routing?.notFound
6305
+ });
6306
+ const rules = mergeRoutingRules({
6307
+ dir: assetRoutingPublicDir,
6308
+ routing: config.routing
6309
+ });
6310
+ if (!managedCloudflareBuild && (rules.headerRules.length > 0 || rules.redirectRules.length > 0 || (rules.fallbackRules?.length ?? 0) > 0)) {
6311
+ policy.assetConfig.run_worker_first = ["/**"];
6312
+ if (rules.fallbackRules?.length && policy.assetConfig.not_found_handling === "single-page-application") {
6313
+ policy.assetConfig.not_found_handling = "none";
6314
+ policy.workerSpaFallback = true;
6315
+ }
6316
+ }
6317
+ return policy;
6318
+ }
6319
+ assetRouting = resolveConfiguredAssetRoutingPolicy();
5597
6320
  workerConfig.main = join(root, ".void", "entry.ts");
5598
- if (jobs.length > 0) workerConfig.triggers = { crons: jobs.flatMap((job) => job.crons) };
5599
6321
  function applyAssetRoutingPolicy(policy) {
5600
6322
  workerConfig.assets = {
5601
6323
  binding: "ASSETS",
@@ -5617,11 +6339,13 @@ function voidPlugin(options) {
5617
6339
  createWranglerCompatPersistPlugin(root, config),
5618
6340
  restartPlugin(),
5619
6341
  bindingWatchPlugin,
6342
+ ...bindings.needsEmail ? [emailDevInboxPlugin({ devTriggerToken })] : [],
5620
6343
  envPlugin,
5621
- envSchemaPlugin(root, { onDefaults: applySchemaDefaultsToWorkerVars }),
6344
+ envSchemaPlugin(root, { onSchema: applyEnvSchema }),
5622
6345
  envClientGuardPlugin(root),
5623
6346
  envClientFoldPlugin(),
5624
6347
  clientStubsPlugin,
6348
+ serverRuntimeOptimizerPlugin,
5625
6349
  bindingRuntimePlugin(bindingNames),
5626
6350
  ...bindings.needsSandbox ? [createSandboxSdkResolvePlugin()] : [],
5627
6351
  ...pagesDevCssAssetsPlugin(root, devTriggerToken),
@@ -5638,14 +6362,17 @@ function voidPlugin(options) {
5638
6362
  sandboxBindingName: sandboxConfig?.binding,
5639
6363
  sandboxClassName: sandboxConfig?.className,
5640
6364
  live: liveConfig,
6365
+ durableObjects,
6366
+ websocketTombstones,
5641
6367
  onDevAssetRoutingPolicyChange: updateDevAssetRoutingPolicy
5642
6368
  }),
5643
6369
  drizzlePlugin(root, true, dialect, {
5644
6370
  d1BindingName: bindingNames?.d1,
5645
6371
  forceVirtualDb: authEnabled || bindingNames?.d1 !== void 0,
5646
- localD1PersistRoot: options?.persistTo ?? outDir
6372
+ localD1DatabaseId: localD1.databaseId,
6373
+ localD1PersistRoot: localD1.persistRoot
5647
6374
  }),
5648
- migrationPlugin(root, dialect, "local", join(root, ".void"), {
6375
+ migrationPlugin(root, dialect, localD1.databaseId, localD1.persistRoot, {
5649
6376
  enabled: authEnabled,
5650
6377
  configPath: authConfigPath
5651
6378
  }),
@@ -5656,7 +6383,7 @@ function voidPlugin(options) {
5656
6383
  }),
5657
6384
  ...cloudflare({
5658
6385
  ...inspectorPortOption,
5659
- persistState: { path: options?.persistTo ?? outDir },
6386
+ persistState: { path: localD1.persistRoot },
5660
6387
  auxiliaryWorkers: options?.auxiliaryWorkers,
5661
6388
  viteEnvironment: { name: DEFAULT_WORKER_ENVIRONMENT_NAME },
5662
6389
  config: mergeBindings
@@ -5664,4 +6391,4 @@ function voidPlugin(options) {
5664
6391
  ];
5665
6392
  }
5666
6393
  //#endregion
5667
- export { VoidAssetRewriteError, basicAuth, defer, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled, shouldBindWorkersAi, voidPlugin };
6394
+ export { VoidAssetRewriteError, basicAuth, defer, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled, resolveWorkersAiBindingName, shouldBindWorkersAi, shouldInjectEmailDevInboxVar, voidPlugin };