void 0.10.13 → 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 (430) 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-qHGgPuWT.mjs → config-BQFq7QvD.mjs} +75 -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-C0i0sYMS.mjs → db-BxoUWL0F.mjs} +600 -115
  31. package/dist/{delete-mh6p-zkQ.mjs → delete-iJOqxBz1.mjs} +9 -6
  32. package/dist/{deploy-jBJT1fUT.mjs → deploy-CcDoeBIf.mjs} +3249 -1990
  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-BzXf3Jh2.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-ChAADPQu.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 +1010 -293
  60. package/dist/{init-7JCAcKNQ.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-pRg81HqV.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-BvvgAz-3.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-CPx2ZxsH.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-z1jtHEi_.mjs → route-types-Da-DpyUp.mjs} +77 -25
  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-ChWt4pX1.mjs → scan-BMH4rzlv.mjs} +42 -19
  180. package/dist/{scan-NU4xKGci.mjs → scan-CpK-57ug.mjs} +5 -4
  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 +22 -3
  194. package/skills/void/SKILL.md +59 -2
  195. package/skills/void/docs/guide/ai.md +32 -14
  196. package/skills/void/docs/guide/app-types.md +6 -6
  197. package/skills/void/docs/guide/auth.md +14 -16
  198. package/skills/void/docs/guide/database/d1.md +6 -0
  199. package/skills/void/docs/guide/database/mysql.md +60 -0
  200. package/skills/void/docs/guide/database/postgresql.md +14 -9
  201. package/skills/void/docs/guide/database.md +39 -26
  202. package/skills/void/docs/guide/deployment.md +99 -25
  203. package/skills/void/docs/guide/durable-state.md +140 -0
  204. package/skills/void/docs/guide/edge/headers.md +5 -5
  205. package/skills/void/docs/guide/edge/prerendering.md +2 -0
  206. package/skills/void/docs/guide/edge/revalidation.md +21 -6
  207. package/skills/void/docs/guide/edge/rewrites.md +18 -14
  208. package/skills/void/docs/guide/edge/static-assets.md +12 -10
  209. package/skills/void/docs/guide/email.md +609 -0
  210. package/skills/void/docs/guide/env-migration.md +109 -0
  211. package/skills/void/docs/guide/env-vars.md +70 -248
  212. package/skills/void/docs/guide/index.md +15 -17
  213. package/skills/void/docs/guide/jobs.md +8 -5
  214. package/skills/void/docs/guide/live.md +7 -15
  215. package/skills/void/docs/guide/pages-routing/actions-and-forms.md +8 -4
  216. package/skills/void/docs/guide/pages-routing/islands.md +3 -3
  217. package/skills/void/docs/guide/pages-routing/loaders.md +6 -4
  218. package/skills/void/docs/guide/pages-routing/overview.md +7 -7
  219. package/skills/void/docs/guide/platform-administration.md +212 -0
  220. package/skills/void/docs/guide/platform-development.md +235 -0
  221. package/skills/void/docs/guide/queues.md +10 -10
  222. package/skills/void/docs/guide/quickstart.md +46 -67
  223. package/skills/void/docs/guide/remote-dev.md +8 -6
  224. package/skills/void/docs/guide/sandboxes.md +33 -16
  225. package/skills/void/docs/guide/self-hosted-platform.md +553 -0
  226. package/skills/void/docs/guide/server-routing.md +5 -5
  227. package/skills/void/docs/guide/sse.md +4 -4
  228. package/skills/void/docs/guide/ssg.md +5 -3
  229. package/skills/void/docs/guide/storage.md +2 -2
  230. package/skills/void/docs/guide/websockets.md +15 -7
  231. package/skills/void/docs/index.md +3 -3
  232. package/skills/void/docs/integrations/agents.md +6 -64
  233. package/skills/void/docs/integrations/cloudflare.md +165 -146
  234. package/skills/void/docs/integrations/frameworks/analog.md +4 -4
  235. package/skills/void/docs/integrations/frameworks/astro.md +5 -5
  236. package/skills/void/docs/integrations/frameworks/nuxt.md +5 -5
  237. package/skills/void/docs/integrations/frameworks/overview.md +3 -3
  238. package/skills/void/docs/integrations/frameworks/react-router.md +3 -3
  239. package/skills/void/docs/integrations/frameworks/sveltekit.md +3 -3
  240. package/skills/void/docs/integrations/frameworks/tanstack-start.md +2 -2
  241. package/skills/void/docs/integrations/nodejs-bun-deno.md +11 -4
  242. package/skills/void/docs/reference/api.md +42 -6
  243. package/skills/void/docs/reference/cli.md +614 -160
  244. package/skills/void/docs/reference/config.md +67 -27
  245. package/skills/void/docs/reference/resource-inference.md +13 -9
  246. package/skills/void/docs/reference/structure.md +9 -11
  247. package/AGENT_PROMPT.md +0 -19
  248. package/dist/cf-access-Bqw81xAf.mjs +0 -22
  249. package/dist/env-CZy5MorI.mjs +0 -299
  250. package/dist/env-helpers-z4stu8uc.d.mts +0 -52
  251. package/dist/env-mask-Dd47NbR6.mjs +0 -90
  252. package/dist/env-public-BfiLcMBk.d.mts +0 -140
  253. package/dist/link-CdGHSIy-.mjs +0 -45
  254. package/dist/mcp-DoM3_nhd.mjs +0 -377
  255. package/dist/project-paths-GpziKeQQ.d.mts +0 -25
  256. package/dist/providers-BNKRacMr.d.mts +0 -7
  257. package/dist/proxy-D-3_D-Gl.mjs +0 -5
  258. package/dist/rollback-CkvTFXx5.mjs +0 -90
  259. package/dist/runtime/isr-cache.d.mts +0 -207
  260. package/dist/runtime/isr-cache.mjs +0 -523
  261. package/dist/types-lLjNE9Qp.d.mts +0 -51
  262. package/getting-started-prompt.txt +0 -28
  263. package/skills/void/command/void.md +0 -7
  264. package/skills/void/docs/integrations/auth-providers.md +0 -0
  265. package/skills/void/docs/integrations/payment-processors.md +0 -0
  266. package/skills/void/docs/node_modules/@iconify/vue/README.md +0 -408
  267. package/skills/void/docs/node_modules/@iconify/vue/offline/readme.md +0 -5
  268. package/skills/void/docs/node_modules/@voidzero-dev/vitepress-theme/README.md +0 -103
  269. package/skills/void/docs/node_modules/oxc-minify/README.md +0 -78
  270. package/skills/void/docs/node_modules/reka-ui/README.md +0 -80
  271. package/skills/void/docs/node_modules/vitepress/README.md +0 -28
  272. package/skills/void/docs/node_modules/vitepress/template/api-examples.md +0 -49
  273. package/skills/void/docs/node_modules/vitepress/template/index.md +0 -28
  274. package/skills/void/docs/node_modules/vitepress/template/markdown-examples.md +0 -85
  275. package/skills/void/docs/node_modules/vitepress-plugin-group-icons/README.md +0 -101
  276. package/skills/void/docs/node_modules/void/AGENT_PROMPT.md +0 -19
  277. package/skills/void/docs/node_modules/void/CLAUDE.md +0 -221
  278. package/skills/void/docs/node_modules/void/README.md +0 -90
  279. package/skills/void/docs/node_modules/void/node_modules/@clack/prompts/CHANGELOG.md +0 -685
  280. package/skills/void/docs/node_modules/void/node_modules/@clack/prompts/README.md +0 -396
  281. package/skills/void/docs/node_modules/void/node_modules/@cloudflare/sandbox/README.md +0 -219
  282. package/skills/void/docs/node_modules/void/node_modules/@cloudflare/vite-plugin/README.md +0 -37
  283. package/skills/void/docs/node_modules/void/node_modules/@cloudflare/workers-types/README.md +0 -135
  284. package/skills/void/docs/node_modules/void/node_modules/@electric-sql/pglite/README.md +0 -189
  285. package/skills/void/docs/node_modules/void/node_modules/@hono/oauth-providers/CHANGELOG.md +0 -143
  286. package/skills/void/docs/node_modules/void/node_modules/@hono/oauth-providers/README.md +0 -1272
  287. package/skills/void/docs/node_modules/void/node_modules/@napi-rs/keyring/README.md +0 -19
  288. package/skills/void/docs/node_modules/void/node_modules/@types/better-sqlite3/README.md +0 -15
  289. package/skills/void/docs/node_modules/void/node_modules/@types/node/README.md +0 -15
  290. package/skills/void/docs/node_modules/void/node_modules/@types/pg/README.md +0 -15
  291. package/skills/void/docs/node_modules/void/node_modules/@types/proper-lockfile/README.md +0 -51
  292. package/skills/void/docs/node_modules/void/node_modules/@typescript/native-preview/README.md +0 -22
  293. package/skills/void/docs/node_modules/void/node_modules/@typescript/native-preview/vendor/vscode-jsonrpc/README.md +0 -69
  294. package/skills/void/docs/node_modules/void/node_modules/@void/md/README.md +0 -153
  295. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/@shikijs/engine-javascript/README.md +0 -9
  296. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/@shikijs/transformers/README.md +0 -9
  297. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/@types/node/README.md +0 -15
  298. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/gray-matter/CHANGELOG.md +0 -24
  299. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/gray-matter/README.md +0 -565
  300. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-exit/README.md +0 -127
  301. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-anchor/README.md +0 -600
  302. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-attrs/README.md +0 -386
  303. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-container/README.md +0 -95
  304. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-emoji/README.md +0 -101
  305. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-footnote/README.md +0 -135
  306. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/pathslash/README.md +0 -64
  307. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/shiki/README.md +0 -15
  308. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/tinyglobby/README.md +0 -25
  309. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/AGENTS.md +0 -16
  310. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/README.md +0 -220
  311. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/build.md +0 -21
  312. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/check.md +0 -35
  313. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/create.md +0 -70
  314. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/fmt.md +0 -20
  315. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/index.md +0 -35
  316. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/lint.md +0 -26
  317. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/pack.md +0 -17
  318. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/run.md +0 -364
  319. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/staged.md +0 -15
  320. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/test.md +0 -18
  321. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/automatic-data-tracking.md +0 -145
  322. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/build.md +0 -40
  323. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/cache.md +0 -107
  324. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/check.md +0 -60
  325. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/ci.md +0 -62
  326. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/commit-hooks.md +0 -60
  327. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/create.md +0 -341
  328. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/dev.md +0 -24
  329. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/docker.md +0 -175
  330. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/env.md +0 -167
  331. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/fmt.md +0 -41
  332. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/github-actions-cache.md +0 -165
  333. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/ide-integration.md +0 -101
  334. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/implode.md +0 -23
  335. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/index.md +0 -134
  336. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/install.md +0 -199
  337. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/lint.md +0 -50
  338. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/migrate-rules.md +0 -347
  339. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/migrate.md +0 -197
  340. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/monorepo.md +0 -176
  341. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/pack.md +0 -69
  342. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/run.md +0 -356
  343. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/test.md +0 -35
  344. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/troubleshooting.md +0 -108
  345. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/upgrade.md +0 -101
  346. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/vpx.md +0 -66
  347. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/why.md +0 -39
  348. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/index.md +0 -12
  349. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/team.md +0 -35
  350. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/templates/generator/README.md +0 -35
  351. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/templates/monorepo/README.md +0 -29
  352. package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vue/README.md +0 -58
  353. package/skills/void/docs/node_modules/void/node_modules/arktype/README.md +0 -165
  354. package/skills/void/docs/node_modules/void/node_modules/better-auth/LICENSE.md +0 -20
  355. package/skills/void/docs/node_modules/void/node_modules/better-auth/README.md +0 -32
  356. package/skills/void/docs/node_modules/void/node_modules/better-sqlite3/README.md +0 -99
  357. package/skills/void/docs/node_modules/void/node_modules/blake3-jit/README.md +0 -108
  358. package/skills/void/docs/node_modules/void/node_modules/drizzle-arktype/README.md +0 -51
  359. package/skills/void/docs/node_modules/void/node_modules/drizzle-kit/README.md +0 -79
  360. package/skills/void/docs/node_modules/void/node_modules/drizzle-orm/README.md +0 -44
  361. package/skills/void/docs/node_modules/void/node_modules/drizzle-valibot/README.md +0 -51
  362. package/skills/void/docs/node_modules/void/node_modules/drizzle-zod/README.md +0 -65
  363. package/skills/void/docs/node_modules/void/node_modules/es-module-lexer/README.md +0 -403
  364. package/skills/void/docs/node_modules/void/node_modules/estree-walker/README.md +0 -48
  365. package/skills/void/docs/node_modules/void/node_modules/hono/README.md +0 -85
  366. package/skills/void/docs/node_modules/void/node_modules/ignore/README.md +0 -452
  367. package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/CHANGELOG.md +0 -76
  368. package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/LICENSE.md +0 -21
  369. package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/README.md +0 -364
  370. package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/SECURITY.md +0 -41
  371. package/skills/void/docs/node_modules/void/node_modules/magic-string/README.md +0 -325
  372. package/skills/void/docs/node_modules/void/node_modules/ofetch/README.md +0 -398
  373. package/skills/void/docs/node_modules/void/node_modules/pathslash/README.md +0 -64
  374. package/skills/void/docs/node_modules/void/node_modules/pg/README.md +0 -96
  375. package/skills/void/docs/node_modules/void/node_modules/pglite-server/README.md +0 -135
  376. package/skills/void/docs/node_modules/void/node_modules/picocolors/README.md +0 -21
  377. package/skills/void/docs/node_modules/void/node_modules/proper-lockfile/CHANGELOG.md +0 -108
  378. package/skills/void/docs/node_modules/void/node_modules/proper-lockfile/README.md +0 -183
  379. package/skills/void/docs/node_modules/void/node_modules/tinyglobby/README.md +0 -25
  380. package/skills/void/docs/node_modules/void/node_modules/valibot/LICENSE.md +0 -9
  381. package/skills/void/docs/node_modules/void/node_modules/valibot/README.md +0 -94
  382. package/skills/void/docs/node_modules/void/node_modules/vite-plus/AGENTS.md +0 -16
  383. package/skills/void/docs/node_modules/void/node_modules/vite-plus/README.md +0 -220
  384. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/build.md +0 -21
  385. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/check.md +0 -35
  386. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/create.md +0 -70
  387. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/fmt.md +0 -20
  388. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/index.md +0 -35
  389. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/lint.md +0 -26
  390. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/pack.md +0 -17
  391. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/run.md +0 -364
  392. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/staged.md +0 -15
  393. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/test.md +0 -18
  394. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/automatic-data-tracking.md +0 -145
  395. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/build.md +0 -40
  396. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/cache.md +0 -107
  397. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/check.md +0 -60
  398. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/ci.md +0 -62
  399. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/commit-hooks.md +0 -60
  400. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/create.md +0 -341
  401. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/dev.md +0 -24
  402. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/docker.md +0 -175
  403. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/env.md +0 -167
  404. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/fmt.md +0 -41
  405. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/github-actions-cache.md +0 -165
  406. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/ide-integration.md +0 -101
  407. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/implode.md +0 -23
  408. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/index.md +0 -134
  409. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/install.md +0 -199
  410. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/lint.md +0 -50
  411. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/migrate-rules.md +0 -347
  412. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/migrate.md +0 -197
  413. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/monorepo.md +0 -176
  414. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/pack.md +0 -69
  415. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/run.md +0 -356
  416. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/test.md +0 -35
  417. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/troubleshooting.md +0 -108
  418. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/upgrade.md +0 -101
  419. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/vpx.md +0 -66
  420. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/why.md +0 -39
  421. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/index.md +0 -12
  422. package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/team.md +0 -35
  423. package/skills/void/docs/node_modules/void/node_modules/vite-plus/templates/generator/README.md +0 -35
  424. package/skills/void/docs/node_modules/void/node_modules/vite-plus/templates/monorepo/README.md +0 -29
  425. package/skills/void/docs/node_modules/void/node_modules/wrangler/README.md +0 -63
  426. package/skills/void/docs/node_modules/void/node_modules/zod/README.md +0 -191
  427. package/skills/void/docs/node_modules/void/skills/migrate-vite-cloudflare-to-void/SKILL.md +0 -175
  428. package/skills/void/docs/node_modules/void/skills/void/SKILL.md +0 -76
  429. package/skills/void/docs/node_modules/void/skills/void/command/void.md +0 -7
  430. package/skills/void/docs/node_modules/void/test/e2e/README.md +0 -85
package/dist/index.mjs CHANGED
@@ -1,32 +1,38 @@
1
- import { r as __toESM } from "./rolldown-runtime-DJK8HYOj.mjs";
2
- import { a as join, n as dirname, s as relative } from "./dist-DaKKDf8D.mjs";
3
- import { M as require_picocolors } from "./agents-CtgBYqld.mjs";
4
- import { A as resolveAssetRoutingPolicy, C as syncWranglerBindings, D as resolveSandboxConfig, E as isSandboxEnabled, O as hasApiRoutes, S as resolveLiveBindingConfig, T as SANDBOX_MIGRATION_TAG, _ as nextDurableObjectMigrationTag, a as prerenderPagesNode, c as filterLoadedEnv, d as LIVE_MIGRATION_TAG, g as hasDurableObjectSqliteMigration, h as ensureWranglerNodejsAlsFlag, i as prerenderPages, k as hasDocumentRoutes, l as resolveRemoteMode, m as ensureWranglerCompatibilityDate, n as resolveDistDir, o as collectRevalidate, p as ensureWranglerCompat, r as resolveWorkerDirName, s as hasRevalidate, u as stripInternalEnvKeys, v as normalizeWranglerCompatibilityFlags, x as readWranglerCompat, y as readExistingLiveBindingConfig } from "./deploy-jBJT1fUT.mjs";
5
- import { i as resolveProjectPaths, n as readProjectPaths, t as assertNoRootSourceConventionConflicts } from "./project-paths-BQd7OmIo.mjs";
6
- import { a as scanQueuesSync, c as loadVoidAuthConfig, i as scanWebSocketRoutesSync, n as scanRoutePatternsSync, o as scanJobsSync, r as scanRoutes, s as findVoidAuthConfig, t as scanMiddlewareSync } from "./scan-ChWt4pX1.mjs";
7
- import { b as readProjectConfig, f as getToken, m as isStagingMode } from "./client-Gb71-XkG.mjs";
8
- import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-B-QtXjLQ.mjs";
9
- import { c as getDatabaseDialect, f as readConfig, l as isNodeTarget, p as resolveBindingNames } from "./config-qHGgPuWT.mjs";
10
- import { a as inferBindingsFromSource, i as inferProjectBindings, n as FRAMEWORK_SCAN_DIRS, o as walk, r as detectFramework, t as DEFAULT_SCAN_DIRS } from "./plugin-inference-DMeavIJ6.mjs";
11
- import { a as validateSsrEntries, c as isIdentifier, l as isLiteral, n as SSR_SERVER_ENTRY_RELATIVE_PATHS, o as validateSsrEntry, t as SSR_CLIENT_ENTRY_RELATIVE_PATHS, u as isMemberExpression } from "./entry-D7yy4xVH.mjs";
1
+ import { r as __toESM } from "./rolldown-runtime-rQ84J-ij.mjs";
2
+ import { c as relative, l as resolve, n as dirname, o as join } from "./dist-BrsS7cai.mjs";
3
+ import { st as require_picocolors } from "./output-tFQLLj26.mjs";
4
+ import { D as resolveAssetRoutingPolicy, E as hasDocumentRoutes, T as hasApiRoutes, _ as scanDurableObjectModules, c as resolveDistDir, d as generateQueueResolverSource, f as prerenderPages, g as hasRevalidate, h as collectRevalidate, l as resolveWorkerDirName, m as collectPages, p as prerenderPagesNode, v as SEND_EMAIL_BINDING } from "./deploy-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-z1jtHEi_.mjs";
15
- import { t as discoverSchema } from "./discover-CJHyvYfR.mjs";
16
- import { t as MagicString } from "./magic-string.es-ZQjdJFFn.mjs";
17
- import { n as findEnvFileForPaths, r as generateEnvTypes, t as findEnvFile } from "./env-types-QBj-ndax.mjs";
18
- import { h as loadUserEnvFile } from "./env-validation-Dea3v3ej.mjs";
19
- import { i as resolveLayoutIds, o as isRouteEnabledForEnvironment } from "./scan-NU4xKGci.mjs";
20
- import { n as STAGING_PROXY_URL, t as DEFAULT_PROXY_URL } from "./proxy-D-3_D-Gl.mjs";
21
- import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-ChAADPQu.mjs";
11
+ import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-D8QfRX9Y.mjs";
12
+ import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-Ql2XsSQp.mjs";
13
+ import { a as inferBindingsFromSource, i as inferProjectBindings, n as FRAMEWORK_SCAN_DIRS, o as walk, r as detectFramework, t as DEFAULT_SCAN_DIRS } from "./plugin-inference-BDRfZngg.mjs";
14
+ import { a as validateSsrEntries, c as isIdentifier, l as isLiteral, n as SSR_SERVER_ENTRY_RELATIVE_PATHS, o as validateSsrEntry, t as SSR_CLIENT_ENTRY_RELATIVE_PATHS, u as isMemberExpression } from "./entry-DU3oDoQ3.mjs";
15
+ import { n as writeLocalD1PersistState, t as resolveLocalD1Target } from "./local-d1-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-pRg81HqV.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
  /**
@@ -4260,6 +4701,7 @@ function routingPlugin(root, options) {
4260
4701
  const middlewareDir = paths.middlewareDir;
4261
4702
  const jobsDir = paths.cronsDir;
4262
4703
  const queuesDir = paths.queuesDir;
4704
+ const emailDir = paths.emailDir;
4263
4705
  const pagesDir = paths.pagesDir;
4264
4706
  const ssrDir = paths.ssrDir;
4265
4707
  const authConfigPaths = new Set(paths.authConfigCandidates.map((path) => normalizePath(path)));
@@ -4268,9 +4710,14 @@ function routingPlugin(root, options) {
4268
4710
  const normalizedMiddlewareDir = normalizePath(middlewareDir);
4269
4711
  const normalizedJobsDir = normalizePath(jobsDir);
4270
4712
  const normalizedQueuesDir = normalizePath(queuesDir);
4713
+ const normalizedEmailDir = normalizePath(emailDir);
4271
4714
  const normalizedPagesDir = normalizePath(pagesDir);
4272
4715
  const normalizedSsrDir = normalizePath(ssrDir);
4273
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
+ }
4274
4721
  function isInDirectory(path, directory) {
4275
4722
  return path === directory || path.startsWith(directory + "/");
4276
4723
  }
@@ -4329,16 +4776,20 @@ function routingPlugin(root, options) {
4329
4776
  responsePath: resolveFile("#self/response"),
4330
4777
  handlerPath: resolveFile("#self/handler"),
4331
4778
  runtimeEnvPath: resolveFile("#self/_env"),
4779
+ runtimeEmailPath: resolveFile("#self/email"),
4780
+ emailDir,
4332
4781
  projectRoot: root,
4333
4782
  homeDir: homedir(),
4334
4783
  webSocketRuntimePath: resolveFile("#self/runtime/ws-server"),
4335
4784
  liveRuntimePath: resolveFile("#self/runtime/live-server"),
4336
4785
  hasLive: options.live !== void 0,
4786
+ webSocketTombstones: options.websocketTombstones,
4337
4787
  reactRefresh: false,
4338
4788
  dev: false,
4339
4789
  devTriggerToken: options.devTriggerToken,
4340
4790
  auth: null,
4341
4791
  nodeTarget: !!options.target,
4792
+ managedDispatch: false,
4342
4793
  explicitPrerenderPaths: options.prerenderPaths,
4343
4794
  bindingHandlerPath: options.target ? void 0 : resolveFile("#self/remote/binding-handler"),
4344
4795
  bindingNames: options.bindingNames
@@ -4366,7 +4817,7 @@ function routingPlugin(root, options) {
4366
4817
  const config = readConfig(root);
4367
4818
  const dialect = getDatabaseDialect(config);
4368
4819
  compileOptions.auth = {
4369
- 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"),
4370
4821
  authConfigPath,
4371
4822
  authDatabasePath: null,
4372
4823
  authSchemaPath: null,
@@ -4381,7 +4832,10 @@ function routingPlugin(root, options) {
4381
4832
  providers: compileOptions.auth.providers,
4382
4833
  userConfig: await loadVoidAuthConfig(compileOptions.auth.authConfigPath)
4383
4834
  };
4384
- const env = loadEnv(resolvedConfig?.mode ?? "development", root, "");
4835
+ const env = {
4836
+ ...resolvedConfig?.command === "serve" ? loadLocalEnv(root) : {},
4837
+ ...process.env
4838
+ };
4385
4839
  const schemaPath = join(outDir, "better-auth-schema.ts");
4386
4840
  const databasePath = join(outDir, "better-auth-db.ts");
4387
4841
  writeFileIfChanged(schemaPath, generateVoidAuthDrizzleSchema(authRuntime, env));
@@ -4395,6 +4849,8 @@ function routingPlugin(root, options) {
4395
4849
  }
4396
4850
  let resolvedConfig = null;
4397
4851
  let isrRevalidate;
4852
+ let isrPages;
4853
+ let isrPathRevalidate;
4398
4854
  function resolveAuthClientPath(config) {
4399
4855
  if (config.plugins.some((plugin) => plugin.name === "void-react:pages")) return explicitAuthClientPaths.react;
4400
4856
  if (config.plugins.some((plugin) => plugin.name === "void-vue:pages")) return explicitAuthClientPaths.vue;
@@ -4475,11 +4931,20 @@ function routingPlugin(root, options) {
4475
4931
  if (options.target) {
4476
4932
  writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(options.target, compileOptions.honoPath, getEnvSchemaModule()));
4477
4933
  writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
4478
- } else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry(false, remote, void 0, void 0, compileOptions.runtimeEnvPath, getEnvSchemaModule(), options.hasSandbox ? {
4479
- bindingName: options.sandboxBindingName,
4480
- className: options.sandboxClassName,
4481
- runtimePath: sandboxRuntimePath
4482
- } : false, void 0, options.live));
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
+ }));
4483
4948
  return {
4484
4949
  name: "void:routing",
4485
4950
  enforce: "pre",
@@ -4496,6 +4961,7 @@ function routingPlugin(root, options) {
4496
4961
  },
4497
4962
  config(_, env) {
4498
4963
  routeEnvironment = env.command === "serve" && !env.isPreview ? "dev" : "prod";
4964
+ compileOptions.managedDispatch = !options.target && env.command === "build" && Boolean(process.env.VOID_DEPLOY_PROJECT_ID);
4499
4965
  if (env.command === "serve" && options.isAuthEnabled()) return { resolve: { alias: [
4500
4966
  "client",
4501
4967
  "react",
@@ -4529,11 +4995,11 @@ function routingPlugin(root, options) {
4529
4995
  if (!options.target) {
4530
4996
  const migrationsDir = paths.migrationsDir;
4531
4997
  if (existsSync(migrationsDir) || compileOptions.auth) {
4532
- const { collectMigrations } = await import("./validate-Cw_RLeTj.mjs").then((n) => n.l);
4533
- const { buildMigrationPlanBundle } = await import("./validate-Cw_RLeTj.mjs").then((n) => n.s);
4998
+ const { collectMigrations } = await import("./validate-Bihr8WBi.mjs").then((n) => n.l);
4999
+ const { buildMigrationPlanBundle } = await import("./validate-Bihr8WBi.mjs").then((n) => n.s);
4534
5000
  const migrations = existsSync(migrationsDir) ? collectMigrations(migrationsDir) : [];
4535
5001
  const dialect = getDatabaseDialect(readConfig(root));
4536
- 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");
4537
5003
  compileOptions.migrations = migrations;
4538
5004
  compileOptions.migrationPlanBundle = dialect === "sqlite" ? buildMigrationPlanBundle(migrations) : void 0;
4539
5005
  compileOptions.migrationDialect = dialect;
@@ -4543,15 +5009,37 @@ function routingPlugin(root, options) {
4543
5009
  writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(options.target, compileOptions.honoPath, getEnvSchemaModule()));
4544
5010
  writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
4545
5011
  } else {
4546
- 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;
4547
5015
  const emitIsr = !options.target && resolvedConfig?.command === "build" && !process.env.VOID_DEPLOY_PROJECT_ID && hasRevalidate(merged);
4548
5016
  isrRevalidate = emitIsr ? merged : void 0;
4549
- const isr = emitIsr ? { runtimePath: resolveFile("#self/runtime/isr-cache") } : void 0;
4550
- writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry(scan.queues.length > 0, remote, resolvedConfig?.build.assetsDir, (scan.websockets ?? []).map((route) => route.className), compileOptions.runtimeEnvPath, getEnvSchemaModule(), options.hasSandbox ? {
4551
- bindingName: options.sandboxBindingName,
4552
- className: options.sandboxClassName,
4553
- runtimePath: sandboxRuntimePath
4554
- } : false, shouldServeSpaFallback(), options.live, isr, currentNotFoundHandling()));
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
+ }));
4555
5043
  }
4556
5044
  },
4557
5045
  resolveId: {
@@ -4576,14 +5064,14 @@ function routingPlugin(root, options) {
4576
5064
  }
4577
5065
  if (id !== VIRTUAL_ROUTES_ID) return;
4578
5066
  if (!scan) await rescan();
4579
- 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);
4580
5068
  }
4581
5069
  },
4582
5070
  configureServer(devServer) {
4583
5071
  const watcher = devServer.watcher;
4584
5072
  const handleFileChange = async (path, event) => {
4585
5073
  const normalizedPath = normalizePath(path);
4586
- const sourceConventionChanged = isInDirectory(normalizedPath, normalizedRoutesDir) || isInDirectory(normalizedPath, normalizedMiddlewareDir) || isInDirectory(normalizedPath, normalizedJobsDir) || isInDirectory(normalizedPath, normalizedQueuesDir) || isInDirectory(normalizedPath, normalizedPagesDir) || isPotentialAuthConfigPath(normalizedPath) || isPotentialEnvFilePath(normalizedPath);
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);
4587
5075
  if (!sourceConventionChanged && !isInDirectory(normalizedPath, normalizedSsrDir) && !isPotentialEnvFilePath(normalizedPath)) return;
4588
5076
  if (!sourceConventionChanged && isInDirectory(normalizedPath, normalizedSsrDir) && !ssrEntryAbsPaths.has(normalizedPath)) return;
4589
5077
  await rescan();
@@ -4598,11 +5086,26 @@ function routingPlugin(root, options) {
4598
5086
  if (options.target) {
4599
5087
  writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(options.target, compileOptions.honoPath, getEnvSchemaModule()));
4600
5088
  writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
4601
- } else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry(scan.queues.length > 0, remote, resolvedConfig?.build.assetsDir, (scan.websockets ?? []).map((route) => route.className), compileOptions.runtimeEnvPath, getEnvSchemaModule(), options.hasSandbox ? {
4602
- bindingName: options.sandboxBindingName,
4603
- className: options.sandboxClassName,
4604
- runtimePath: sandboxRuntimePath
4605
- } : false, shouldServeSpaFallback(), options.live, void 0, currentNotFoundHandling()));
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
+ }));
4606
5109
  const structural = event !== "change";
4607
5110
  const pageManifestChanged = isInDirectory(normalizedPath, normalizedPagesDir) && (structural || normalizedPath.endsWith(".md"));
4608
5111
  for (const env of Object.values(devServer.environments)) {
@@ -4660,6 +5163,13 @@ function routingPlugin(root, options) {
4660
5163
  * The token is local to the project and only honored by a worker bound to
4661
5164
  * localhost; treat it like the rest of `.void/` — disposable scratch state,
4662
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.
4663
5173
  */
4664
5174
  /**
4665
5175
  * Best-effort detection of `vite build` (and `vite preview`) so we can skip
@@ -4674,6 +5184,7 @@ function isViteBuildOrPreview() {
4674
5184
  const subcommand = process.argv[2];
4675
5185
  return subcommand === "build" || subcommand === "preview";
4676
5186
  }
5187
+ const DEV_TRIGGER_TOKEN_FILE_MODE = 384;
4677
5188
  function getOrCreateDevTriggerToken(root) {
4678
5189
  const tokenPath = join(root, ".void", "dev-trigger-token");
4679
5190
  const readExisting = () => {
@@ -4684,12 +5195,21 @@ function getOrCreateDevTriggerToken(root) {
4684
5195
  return;
4685
5196
  }
4686
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
+ };
4687
5204
  const existing = readExisting();
4688
- if (existing) return existing;
5205
+ if (existing) {
5206
+ tightenExisting();
5207
+ return existing;
5208
+ }
4689
5209
  mkdirSync(join(root, ".void"), { recursive: true });
4690
5210
  const fresh = crypto.randomUUID();
4691
5211
  try {
4692
- const fd = openSync(tokenPath, "wx");
5212
+ const fd = openSync(tokenPath, "wx", DEV_TRIGGER_TOKEN_FILE_MODE);
4693
5213
  try {
4694
5214
  writeSync(fd, fresh);
4695
5215
  } finally {
@@ -4773,7 +5293,7 @@ function generateClassATriggerWrapper(root, framework, original, isBuild, assets
4773
5293
  const outDir = join(root, ".void");
4774
5294
  mkdirSync(outDir, { recursive: true });
4775
5295
  const wrapperPath = join(outDir, `${framework}-trigger-wrapper.ts`);
4776
- 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.
4777
5297
  import original from ${JSON.stringify(original)};
4778
5298
 
4779
5299
  export default {
@@ -4795,7 +5315,8 @@ export default {
4795
5315
  return original.fetch(request, env, ctx);
4796
5316
  },
4797
5317
  };
4798
- `);
5318
+ `;
5319
+ writeFileIfChanged(wrapperPath, body);
4799
5320
  return wrapperPath;
4800
5321
  }
4801
5322
  /** Generate a worker entry for vinext Pages Router projects. */
@@ -4853,16 +5374,49 @@ function hasBinding(list, name) {
4853
5374
  /**
4854
5375
  * Decide whether to inject a Workers AI binding into the built wrangler config.
4855
5376
  *
4856
- * Build-only: dev/miniflare has no local Workers AI, so serve must never bind
4857
- * `env.AI` (dev uses the P2 `__VOID_TOKEN` proxy path instead).
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.
4858
5380
  *
4859
5381
  * `!resolvedAi` is load-bearing: mergeBindings' return is defu-merged over the
4860
5382
  * resolved root-wrangler config and wins, so skipping when the user already
4861
5383
  * declared an `ai` binding avoids clobbering theirs. `!resultAi` is harmless
4862
5384
  * defense-in-depth (nothing else in mergeBindings sets `result.ai`).
4863
5385
  */
4864
- function shouldBindWorkersAi(command, needsAI, resolvedAi, resultAi) {
4865
- 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";
4866
5420
  }
4867
5421
  const CLOUDFLARE_BUILTIN_IMPORT = /^cloudflare:/;
4868
5422
  const SANDBOX_SDK_IMPORT = "@cloudflare/sandbox";
@@ -4959,9 +5513,11 @@ function readVoidDevCredentials(root) {
4959
5513
  const token = getToken(root);
4960
5514
  if (!token) return { status: "no-token" };
4961
5515
  const staging = isStagingMode(root) || void 0;
5516
+ const apiUrl = getApiUrl(root);
4962
5517
  const project = readProjectConfig(root);
4963
5518
  if (!project) return {
4964
5519
  status: "no-project",
5520
+ apiUrl,
4965
5521
  token,
4966
5522
  staging
4967
5523
  };
@@ -4969,22 +5525,21 @@ function readVoidDevCredentials(root) {
4969
5525
  status: "ok",
4970
5526
  token,
4971
5527
  projectId: project.projectId,
5528
+ proxyUrl: getProxyUrl(root),
4972
5529
  staging
4973
5530
  };
4974
5531
  }
4975
- const DEFAULT_API_URL = "https://api.void.cloud";
4976
- const STAGING_API_URL = "https://api.staging.void.cloud";
4977
5532
  /**
4978
5533
  * Interactive AI setup prompt shown during dev server startup when the user
4979
5534
  * has a token but no linked project. Runs as fire-and-forget so it doesn't
4980
5535
  * block the server. On completion, writes project.json and restarts the server.
4981
5536
  */
4982
- async function runAiSetupPrompt(root, token, staging, server) {
4983
- const { select, text, isCancel, log } = await import("./agents-CtgBYqld.mjs").then((n) => n.C);
4984
- const { PlatformClient } = await import("./client-Gb71-XkG.mjs").then((n) => n.r);
4985
- const { writeProjectConfig } = await import("./client-Gb71-XkG.mjs").then((n) => n.y);
4986
- const { inferDefaultSlug } = await import("./resolve-project-BBMtLLV9.mjs").then((n) => n.i);
4987
- const client = new PlatformClient(token, staging ? STAGING_API_URL : DEFAULT_API_URL);
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);
4988
5543
  let projects;
4989
5544
  try {
4990
5545
  projects = await client.listProjects();
@@ -5009,8 +5564,8 @@ async function runAiSetupPrompt(root, token, staging, server) {
5009
5564
  let projectId;
5010
5565
  let slug;
5011
5566
  if (result === "__create__") {
5012
- const { validateProjectSlug } = await import("./create-project-DsYvl3TB.mjs").then((n) => n.a);
5013
- const { createProjectWithSlugRetry } = await import("./create-project-DsYvl3TB.mjs").then((n) => n.t);
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);
5014
5569
  let created;
5015
5570
  try {
5016
5571
  created = await createProjectWithSlugRetry(client, inferDefaultSlug(root), async (defaultSlug) => {
@@ -5020,7 +5575,7 @@ async function runAiSetupPrompt(root, token, staging, server) {
5020
5575
  initialValue: defaultSlug,
5021
5576
  validate: validateProjectSlug
5022
5577
  });
5023
- if (isCancel(slugResult)) return;
5578
+ if (isCancel(slugResult) || typeof slugResult === "symbol") return;
5024
5579
  return slugResult.trim();
5025
5580
  });
5026
5581
  } catch (err) {
@@ -5035,10 +5590,10 @@ async function runAiSetupPrompt(root, token, staging, server) {
5035
5590
  projectId = project.id;
5036
5591
  slug = project.slug;
5037
5592
  }
5038
- writeProjectConfig(root, {
5593
+ writeProjectConfig(root, attachSelectedPlatform(root, {
5039
5594
  projectId,
5040
5595
  slug
5041
- });
5596
+ }, client.apiUrl));
5042
5597
  log.success(`Linked to ${import_picocolors.default.blue(slug)}`);
5043
5598
  await server.restart();
5044
5599
  }
@@ -5062,6 +5617,7 @@ async function runAiSetupPrompt(root, token, staging, server) {
5062
5617
  */
5063
5618
  function voidPlugin(options) {
5064
5619
  const root = process.cwd();
5620
+ const directCloudflareDestination = readDeploymentPlatform(root) === "cloudflare";
5065
5621
  warnIfPackageJsonIsNotEsm(root);
5066
5622
  const devTriggerToken = isViteBuildOrPreview() ? crypto.randomUUID() : getOrCreateDevTriggerToken(root);
5067
5623
  const config = readConfig(root);
@@ -5074,6 +5630,13 @@ function voidPlugin(options) {
5074
5630
  const scanDirs = config.inference?.scanDirs ?? (detected ? FRAMEWORK_SCAN_DIRS : void 0);
5075
5631
  const inferenceOptions = config.inference?.bindings ? { bindings: config.inference.bindings } : void 0;
5076
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);
5077
5640
  const inferredBindings = inferProjectBindings(root, {
5078
5641
  ...inferenceOptions,
5079
5642
  paths
@@ -5088,6 +5651,8 @@ function voidPlugin(options) {
5088
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.");
5089
5652
  if (detected && bindings.needsSandbox) throw new Error("sandbox: Cloudflare Sandboxes are currently supported in Void apps only.");
5090
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.");
5091
5656
  const STUB_PREFIX = "\0void-client-stub:";
5092
5657
  const SERVER_ONLY_STUBS = {
5093
5658
  "void/db": "export const db = {}; export const createDb = () => ({});",
@@ -5104,12 +5669,37 @@ function voidPlugin(options) {
5104
5669
  this.reason = reason;
5105
5670
  }
5106
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
+ }
5107
5678
  export async function getSandbox() {
5108
5679
  throw new Error('void/sandbox is server-only and cannot be imported by browser code.');
5109
5680
  }
5110
5681
  export const sandbox = { get: getSandbox };
5111
5682
  `,
5112
- "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": ""
5113
5703
  };
5114
5704
  const STUB_RESOLVE_FILTER = new RegExp(`^(${Object.keys(SERVER_ONLY_STUBS).join("|").replaceAll("/", "\\/")})$`);
5115
5705
  const STUB_LOAD_FILTER = new RegExp(`^\\0void-client-stub:`);
@@ -5120,7 +5710,7 @@ function voidPlugin(options) {
5120
5710
  filter: { id: STUB_RESOLVE_FILTER },
5121
5711
  handler(id) {
5122
5712
  if (this.environment?.name === "client") {
5123
- if (id === "void/live") this.error("void/live is server-only and cannot be imported by browser code. Import void/live/client instead.");
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.");
5124
5714
  return STUB_PREFIX + id;
5125
5715
  }
5126
5716
  }
@@ -5132,16 +5722,40 @@ function voidPlugin(options) {
5132
5722
  }
5133
5723
  }
5134
5724
  };
5725
+ const serverRuntimeOptimizerPlugin = {
5726
+ name: "void:server-runtime-optimizer",
5727
+ configEnvironment(name) {
5728
+ if (name !== "client") return { optimizeDeps: { exclude: ["void"] } };
5729
+ }
5730
+ };
5135
5731
  const queues = framework ? [] : scanQueuesSync(root, paths.queuesDir);
5136
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
+ }
5137
5747
  const liveConfig = !isNodeTarget(config.target) ? bindings.needsLive ? resolveLiveBindingConfig(root, {
5138
- reservedBindings: websocketRoutes.map((route) => route.bindingName),
5139
- reservedClasses: [...websocketRoutes.map((route) => route.className), sandboxConfig?.className]
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
+ ]
5140
5754
  }) : readExistingLiveBindingConfig(root) : void 0;
5141
5755
  if (detected && (detected.class === "b" || detected.class === "c")) {
5142
- const frameworkPersistRoot = join(root, ".wrangler", "state");
5756
+ const frameworkPersistRoot = localD1.persistRoot;
5143
5757
  const syncFrameworkCompat = (compat) => {
5144
- 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");
5145
5759
  };
5146
5760
  const wranglerCompatPlugin = {
5147
5761
  name: "void:wrangler-compat",
@@ -5149,7 +5763,7 @@ function voidPlugin(options) {
5149
5763
  config(_, env) {
5150
5764
  syncFrameworkCompat(ensureWranglerCompat(root, config));
5151
5765
  if (env.command !== "serve") return;
5152
- if (ensureWranglerNodejsAlsFlag(root)) voidLog("Synced nodejs_als to wrangler config");
5766
+ if (ensureWranglerNodejsAlsFlag(root)) voidLog("Synced nodejs_als to Cloudflare config");
5153
5767
  },
5154
5768
  buildStart() {
5155
5769
  syncFrameworkCompat(ensureWranglerCompat(root, config));
@@ -5206,6 +5820,7 @@ function voidPlugin(options) {
5206
5820
  ...nuxtVite8WebSocketCompatPlugin ? [nuxtVite8WebSocketCompatPlugin] : [],
5207
5821
  svelteKitHooksInjectPlugin,
5208
5822
  clientStubsPlugin,
5823
+ serverRuntimeOptimizerPlugin,
5209
5824
  bindingRuntimePlugin(bindingNames),
5210
5825
  envSchemaPlugin(root),
5211
5826
  envClientGuardPlugin(root),
@@ -5213,9 +5828,10 @@ function voidPlugin(options) {
5213
5828
  drizzlePlugin(root, false, dialect, {
5214
5829
  d1BindingName: bindingNames?.d1,
5215
5830
  forceVirtualDb: authEnabled || bindingNames?.d1 !== void 0,
5831
+ localD1DatabaseId: localD1.databaseId,
5216
5832
  localD1PersistRoot: frameworkPersistRoot
5217
5833
  }),
5218
- migrationPlugin(root, dialect, "local", frameworkPersistRoot, { enabled: false }),
5834
+ migrationPlugin(root, dialect, localD1.databaseId, frameworkPersistRoot, { enabled: false }),
5219
5835
  devTriggersHintPlugin(root, {
5220
5836
  framework: true,
5221
5837
  devTriggerToken
@@ -5228,6 +5844,9 @@ function voidPlugin(options) {
5228
5844
  }
5229
5845
  const remoteMode = resolveRemoteMode(config.remote);
5230
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;
5231
5850
  let assetRouting;
5232
5851
  if (config.worker?.vars) workerConfig.vars = { ...config.worker.vars };
5233
5852
  let viteCommand = "build";
@@ -5245,6 +5864,23 @@ function voidPlugin(options) {
5245
5864
  function mergeBindings(resolved) {
5246
5865
  const environmentWebsocketRoutes = websocketRoutes.filter((route) => isRouteEnabledForEnvironment(route, routeEnvironment));
5247
5866
  const result = { ...workerConfig };
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
+ }
5248
5884
  if (workerConfig.assets && resolved.assets) {
5249
5885
  delete resolved.assets.not_found_handling;
5250
5886
  delete resolved.assets.run_worker_first;
@@ -5269,10 +5905,41 @@ function voidPlugin(options) {
5269
5905
  binding: r2Name,
5270
5906
  bucket_name: "default"
5271
5907
  }];
5272
- if (shouldBindWorkersAi(viteCommand, bindings.needsAI, resolved.ai, result.ai)) result.ai = { binding: "AI" };
5273
- 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) {
5920
+ const vars = result.vars ?? {};
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) {
5274
5941
  const vars = result.vars ?? {};
5275
- vars.__VOID_AI_GATEWAY = config.ai.gateway;
5942
+ vars.__VOID_EMAIL_FROM = config.email.from;
5276
5943
  result.vars = vars;
5277
5944
  }
5278
5945
  if (queues.length > 0) result.queues = {
@@ -5290,15 +5957,38 @@ function voidPlugin(options) {
5290
5957
  };
5291
5958
  if (environmentWebsocketRoutes.length > 0) {
5292
5959
  const existingDurableObjectBindings = resolved.durable_objects?.bindings ?? [];
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
+ }
5293
5964
  const missingDurableObjectBindings = environmentWebsocketRoutes.filter((route) => !existingDurableObjectBindings.some((binding) => binding.name === route.bindingName)).map((route) => ({
5294
5965
  name: route.bindingName,
5295
5966
  class_name: route.className
5296
5967
  }));
5297
5968
  if (missingDurableObjectBindings.length > 0) result.durable_objects = { bindings: [...existingDurableObjectBindings, ...missingDurableObjectBindings] };
5298
- if (!Array.isArray(resolved.migrations)) result.migrations = [{
5299
- tag: "void-ws-v1",
5300
- new_classes: environmentWebsocketRoutes.map((route) => route.className)
5301
- }];
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];
5302
5992
  }
5303
5993
  if (bindings.needsSandbox && sandboxConfig) {
5304
5994
  const existingDurableObjectBindings = (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
@@ -5335,12 +6025,13 @@ function voidPlugin(options) {
5335
6025
  new_sqlite_classes: [liveConfig.className]
5336
6026
  }];
5337
6027
  }
5338
- if (viteCommand === "serve" && bindings.needsAI) {
6028
+ if (viteCommand === "serve" && bindings.needsAI && !directCloudflareDestination) {
5339
6029
  const credentials = readVoidDevCredentials(root);
5340
6030
  if (credentials.status === "ok") {
5341
6031
  const vars = result.vars ?? {};
5342
6032
  vars.__VOID_TOKEN = credentials.token;
5343
6033
  vars.__VOID_PROJECT_ID = credentials.projectId;
6034
+ vars.__VOID_PROXY_URL = credentials.proxyUrl;
5344
6035
  if (credentials.staging) vars.__VOID_STAGING = "1";
5345
6036
  result.vars = vars;
5346
6037
  }
@@ -5349,7 +6040,7 @@ function voidPlugin(options) {
5349
6040
  const credentials = readVoidDevCredentials(root);
5350
6041
  if (credentials.status === "ok") {
5351
6042
  const vars = result.vars ?? {};
5352
- vars.__VOID_PROXY_URL = credentials.staging ? STAGING_PROXY_URL : DEFAULT_PROXY_URL;
6043
+ vars.__VOID_PROXY_URL = credentials.proxyUrl;
5353
6044
  vars.__VOID_TOKEN = credentials.token;
5354
6045
  vars.__VOID_PROJECT_ID = credentials.projectId;
5355
6046
  vars.__VOID_REMOTE = "1";
@@ -5358,34 +6049,32 @@ function voidPlugin(options) {
5358
6049
  }
5359
6050
  return result;
5360
6051
  }
5361
- function applySchemaDefaultsToWorkerVars(defaults) {
5362
- const vars = workerConfig.vars ?? {};
5363
- for (const [key, value] of Object.entries(defaults)) if (vars[key] === void 0) vars[key] = value;
5364
- workerConfig.vars = vars;
6052
+ function applyEnvSchema(facts) {
6053
+ envSchemaFacts = facts;
5365
6054
  }
5366
- let voidDeployBuildConfig;
5367
6055
  const envPlugin = {
5368
6056
  name: "void:env",
5369
- api: { getVoidDeployBuildConfig: () => voidDeployBuildConfig },
5370
- config(userConfig, { mode, command, isPreview }) {
6057
+ config(userConfig, { command, isPreview }) {
5371
6058
  viteCommand = command;
5372
6059
  routeEnvironment = command === "serve" && !isPreview ? "dev" : "prod";
6060
+ if (command === "serve" && directCloudflareDestination && bindings.needsAI) process.env.CLOUDFLARE_AUTH_USE_KEYRING = "true";
5373
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");
5374
6064
  assetRouting = resolveConfiguredAssetRoutingPolicy();
5375
6065
  applyAssetRoutingPolicy(assetRouting);
5376
6066
  }
5377
6067
  const ignored = ["**/.void/**"];
5378
- const rawEnvDir = typeof userConfig.envDir === "string" && userConfig.envDir.length > 0 ? userConfig.envDir : void 0;
5379
- voidDeployBuildConfig = {
5380
- mode,
5381
- envDir: rawEnvDir
5382
- };
5383
- const { sanitized: env, stripped } = stripInternalEnvKeys(filterLoadedEnv(loadEnv(mode, rawEnvDir ? join(root, rawEnvDir) : root, "")));
5384
- if (stripped.length > 0) voidWarn(`Ignoring internal env keys from .env files: ${stripped.join(", ")}. These are reserved for Void runtime use.`);
5385
- workerConfig.vars = {
5386
- ...workerConfig.vars,
5387
- ...env
5388
- };
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
+ }
5389
6078
  const assets = workerConfig.assets;
5390
6079
  if (command === "serve" && !isPreview && assets?.run_worker_first?.includes("/**")) assets.run_worker_first = [...DEV_ASSET_ROUTING_EXCLUSIONS, "/**"];
5391
6080
  const watchConfig = { server: { watch: { ignored } } };
@@ -5425,20 +6114,22 @@ function voidPlugin(options) {
5425
6114
  voidWarn("Falling back to local bindings.");
5426
6115
  });
5427
6116
  }
5428
- if (bindings.needsAI) {
6117
+ if (bindings.needsAI && !directCloudflareDestination) {
5429
6118
  const credentials = readVoidDevCredentials(root);
5430
6119
  if (credentials.status === "no-token") afterReady(() => {
5431
6120
  console.log();
5432
6121
  voidWarn("AI requires authentication. Run `void auth login`, then restart the dev server.");
5433
6122
  });
5434
- else if (credentials.status === "no-project") if (process.stdin.isTTY) afterReady(() => {
5435
- console.log();
5436
- runAiSetupPrompt(root, credentials.token, credentials.staging, server);
5437
- });
5438
- else afterReady(() => {
5439
- console.log();
5440
- voidWarn("AI requires a linked project. Run `void project link` to set one up.");
5441
- });
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
+ }
5442
6133
  }
5443
6134
  }
5444
6135
  };
@@ -5455,7 +6146,8 @@ function voidPlugin(options) {
5455
6146
  if (hintShown) return;
5456
6147
  if (!watchDirs.some((d) => path.startsWith(d))) return;
5457
6148
  try {
5458
- 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());
5459
6151
  const newBindings = [];
5460
6152
  if (autoDetectBindings) {
5461
6153
  if (fileBindings.needsD1 && !bindings.needsD1) newBindings.push("D1 (void/db)");
@@ -5463,6 +6155,7 @@ function voidPlugin(options) {
5463
6155
  if (fileBindings.needsR2 && !bindings.needsR2) newBindings.push("R2 (void/storage)");
5464
6156
  if (fileBindings.needsAI && !bindings.needsAI) newBindings.push("AI (void/ai)");
5465
6157
  if (fileBindings.needsSandbox && !bindings.needsSandbox) newBindings.push("Sandbox (void/sandbox)");
6158
+ if (fileBindings.needsEmail && !bindings.needsEmail) newBindings.push("Email (void/email)");
5466
6159
  }
5467
6160
  if (autoDetectAuth && fileBindings.needsAuth) newBindings.push("Auth (void/auth)");
5468
6161
  if (newBindings.length > 0) {
@@ -5496,7 +6189,7 @@ function voidPlugin(options) {
5496
6189
  if (resolved.command !== "build") return;
5497
6190
  if (config.routing?.notFound !== void 0 && !process.env.VOID_DEPLOY_BUILD && !notFoundIgnoredWarned) {
5498
6191
  notFoundIgnoredWarned = true;
5499
- voidWarn(`Void generates no asset routing policy for a ${framework} build, so "routing.notFound" in void.json is not written to the built worker config. On a self-hosted \`vite build && wrangler deploy\` it takes effect only if your own wrangler config declares a complete \`assets\` policy (\`binding\`, \`directory\`, \`not_found_handling\`, \`run_worker_first\`); otherwise Cloudflare's default applies. Managed \`void deploy\` supplies the policy for you.`);
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.`);
5500
6193
  }
5501
6194
  generateClassATriggerWrapper(root, framework, getOriginalFrameworkEntry(framework, root), true, resolved.build.assetsDir, config.routing?.notFound);
5502
6195
  }
@@ -5525,19 +6218,22 @@ function voidPlugin(options) {
5525
6218
  createWranglerCompatPersistPlugin(root, config),
5526
6219
  restartPlugin(),
5527
6220
  bindingWatchPlugin,
6221
+ ...bindings.needsEmail ? [emailDevInboxPlugin({ devTriggerToken })] : [],
5528
6222
  envPlugin,
5529
- envSchemaPlugin(root, { onDefaults: applySchemaDefaultsToWorkerVars }),
6223
+ envSchemaPlugin(root, { onSchema: applyEnvSchema }),
5530
6224
  envClientGuardPlugin(root),
5531
6225
  envClientFoldPlugin(),
5532
6226
  clientStubsPlugin,
6227
+ serverRuntimeOptimizerPlugin,
5533
6228
  bindingRuntimePlugin(bindingNames),
5534
6229
  drizzlePlugin(root, false, getDatabaseDialect(config), {
5535
6230
  d1BindingName: bindingNames?.d1,
5536
6231
  forceVirtualDb: authEnabled || bindingNames?.d1 !== void 0,
5537
- localD1PersistRoot: options?.persistTo ?? outDir,
6232
+ localD1DatabaseId: localD1.databaseId,
6233
+ localD1PersistRoot: localD1.persistRoot,
5538
6234
  redirectDbTypePaths
5539
6235
  }),
5540
- migrationPlugin(root, getDatabaseDialect(config)),
6236
+ migrationPlugin(root, getDatabaseDialect(config), localD1.databaseId, localD1.persistRoot),
5541
6237
  ...vinextCompat ? [vinextCompat] : [],
5542
6238
  frameworkBuildWrapperPlugin,
5543
6239
  frameworkTriggersPlugin(root, { devTriggerToken }),
@@ -5547,7 +6243,7 @@ function voidPlugin(options) {
5547
6243
  }),
5548
6244
  ...cloudflare({
5549
6245
  ...inspectorPortOption,
5550
- persistState: { path: options?.persistTo ?? outDir },
6246
+ persistState: { path: localD1.persistRoot },
5551
6247
  auxiliaryWorkers: options?.auxiliaryWorkers,
5552
6248
  viteEnvironment,
5553
6249
  config: mergeBindings
@@ -5561,10 +6257,13 @@ function voidPlugin(options) {
5561
6257
  if (config.remote) voidWarn("\"remote\" in void.json is ignored with target: \"" + config.target + "\"");
5562
6258
  if (bindings.needsSandbox) throw new Error(`sandbox: Cloudflare Sandboxes require target: "cloudflare". They are not available with target: '${config.target}'.`);
5563
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}'.`);
5564
6262
  if (config.routing?.revalidate) voidWarn("\"routing.revalidate\" in void.json is ignored with target: \"" + config.target + "\" (edge caching is CF-only)");
5565
6263
  return [
5566
6264
  restartPlugin(),
5567
6265
  envPlugin,
6266
+ serverRuntimeOptimizerPlugin,
5568
6267
  envSchemaPlugin(root),
5569
6268
  ...pagesDevCssAssetsPlugin(root, devTriggerToken),
5570
6269
  routingPlugin(root, {
@@ -5582,16 +6281,18 @@ function voidPlugin(options) {
5582
6281
  ];
5583
6282
  }
5584
6283
  const existingFlags = workerConfig.compatibility_flags ?? [];
5585
- const normalizedFlags = normalizeWranglerCompatibilityFlags(existingFlags, { needsNodejsCompat: authEnabled || dialect === "postgresql" || bindings.needsSandbox });
6284
+ const needsNodejsCompat = authEnabled || dialect !== "sqlite" || bindings.needsSandbox;
6285
+ const normalizedFlags = normalizeWranglerCompatibilityFlags(existingFlags, { needsNodejsCompat });
5586
6286
  if (normalizedFlags && normalizedFlags !== existingFlags) workerConfig.compatibility_flags = normalizedFlags;
5587
- const jobs = scanJobsSync(root, paths.cronsDir);
5588
6287
  const ssrEntry = validateSsrEntry(root, paths);
5589
6288
  const hasPages = existsSync(paths.pagesDir);
5590
6289
  const hasMiddleware = scanMiddlewareSync(root, paths).length > 0;
6290
+ let assetRoutingPublicDir = join(root, "public");
6291
+ let managedCloudflareBuild = false;
5591
6292
  function resolveConfiguredAssetRoutingPolicy() {
5592
6293
  const routePatterns = scanRoutePatternsSync(root, paths, routeEnvironment);
5593
6294
  const environmentWebsocketRoutes = websocketRoutes.filter((route) => isRouteEnabledForEnvironment(route, routeEnvironment));
5594
- return resolveAssetRoutingPolicy({
6295
+ const policy = resolveAssetRoutingPolicy({
5595
6296
  isSsr: Boolean(ssrEntry),
5596
6297
  hasPages,
5597
6298
  hasApiRoutes: hasApiRoutes(routePatterns) || hasApiRoutes(environmentWebsocketRoutes) || authEnabled,
@@ -5602,10 +6303,21 @@ function voidPlugin(options) {
5602
6303
  liveEnabled: liveConfig !== void 0,
5603
6304
  notFound: config.routing?.notFound
5604
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;
5605
6318
  }
5606
6319
  assetRouting = resolveConfiguredAssetRoutingPolicy();
5607
6320
  workerConfig.main = join(root, ".void", "entry.ts");
5608
- if (jobs.length > 0) workerConfig.triggers = { crons: jobs.flatMap((job) => job.crons) };
5609
6321
  function applyAssetRoutingPolicy(policy) {
5610
6322
  workerConfig.assets = {
5611
6323
  binding: "ASSETS",
@@ -5627,11 +6339,13 @@ function voidPlugin(options) {
5627
6339
  createWranglerCompatPersistPlugin(root, config),
5628
6340
  restartPlugin(),
5629
6341
  bindingWatchPlugin,
6342
+ ...bindings.needsEmail ? [emailDevInboxPlugin({ devTriggerToken })] : [],
5630
6343
  envPlugin,
5631
- envSchemaPlugin(root, { onDefaults: applySchemaDefaultsToWorkerVars }),
6344
+ envSchemaPlugin(root, { onSchema: applyEnvSchema }),
5632
6345
  envClientGuardPlugin(root),
5633
6346
  envClientFoldPlugin(),
5634
6347
  clientStubsPlugin,
6348
+ serverRuntimeOptimizerPlugin,
5635
6349
  bindingRuntimePlugin(bindingNames),
5636
6350
  ...bindings.needsSandbox ? [createSandboxSdkResolvePlugin()] : [],
5637
6351
  ...pagesDevCssAssetsPlugin(root, devTriggerToken),
@@ -5648,14 +6362,17 @@ function voidPlugin(options) {
5648
6362
  sandboxBindingName: sandboxConfig?.binding,
5649
6363
  sandboxClassName: sandboxConfig?.className,
5650
6364
  live: liveConfig,
6365
+ durableObjects,
6366
+ websocketTombstones,
5651
6367
  onDevAssetRoutingPolicyChange: updateDevAssetRoutingPolicy
5652
6368
  }),
5653
6369
  drizzlePlugin(root, true, dialect, {
5654
6370
  d1BindingName: bindingNames?.d1,
5655
6371
  forceVirtualDb: authEnabled || bindingNames?.d1 !== void 0,
5656
- localD1PersistRoot: options?.persistTo ?? outDir
6372
+ localD1DatabaseId: localD1.databaseId,
6373
+ localD1PersistRoot: localD1.persistRoot
5657
6374
  }),
5658
- migrationPlugin(root, dialect, "local", join(root, ".void"), {
6375
+ migrationPlugin(root, dialect, localD1.databaseId, localD1.persistRoot, {
5659
6376
  enabled: authEnabled,
5660
6377
  configPath: authConfigPath
5661
6378
  }),
@@ -5666,7 +6383,7 @@ function voidPlugin(options) {
5666
6383
  }),
5667
6384
  ...cloudflare({
5668
6385
  ...inspectorPortOption,
5669
- persistState: { path: options?.persistTo ?? outDir },
6386
+ persistState: { path: localD1.persistRoot },
5670
6387
  auxiliaryWorkers: options?.auxiliaryWorkers,
5671
6388
  viteEnvironment: { name: DEFAULT_WORKER_ENVIRONMENT_NAME },
5672
6389
  config: mergeBindings
@@ -5674,4 +6391,4 @@ function voidPlugin(options) {
5674
6391
  ];
5675
6392
  }
5676
6393
  //#endregion
5677
- 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 };