void 0.22.0 → 0.23.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 (276) hide show
  1. package/README.md +5 -1
  2. package/dist/{account-cmd-CjyxcGkM.mjs → account-cmd-C84Ee8cO.mjs} +4 -4
  3. package/dist/{scan-ClYmX3sa.mjs → application-analysis-BVsVqc11.mjs} +135 -18
  4. package/dist/application-routing-B7QEjqSR.mjs +27 -0
  5. package/dist/{auth-CZuiVsFh.mjs → auth-CCjt0hcq.mjs} +1 -1
  6. package/dist/{auth-DLNN0D3Z.mjs → auth-CSkdO2Bb.mjs} +4 -47
  7. package/dist/{auth-link-BLO4ptzo.mjs → auth-link-CioEg6uY.mjs} +4 -4
  8. package/dist/{auth-router-ZUg9MF_U.mjs → auth-router-BsR981d4.mjs} +4 -4
  9. package/dist/{better-auth-shared-rsBGBvWJ.mjs → better-auth-shared-hy6RPh9W.mjs} +13 -2
  10. package/dist/{build-cmd-BxR5FROK.mjs → build-cmd-LzvNJORH.mjs} +17 -5
  11. package/dist/{cache-D0sWhgKI.mjs → cache-C4MvnrMH.mjs} +2 -2
  12. package/dist/{cancel-deploy-D4NUqFbi.mjs → cancel-deploy-abUxpP2n.mjs} +2 -2
  13. package/dist/{cf-build-output-BJ6yGEIS.mjs → cf-build-output-BPqOT964.mjs} +2 -1
  14. package/dist/cf-build-output-_0HNWysu.mjs +2 -0
  15. package/dist/cli/cli.mjs +80 -455
  16. package/dist/cli/{cf-compat.mjs → cloudflare-operation-process.mjs} +442 -421
  17. package/dist/cli/env-schema-probe.d.mts +2 -1
  18. package/dist/cli/env-schema-probe.mjs +3 -3
  19. package/dist/client-Cu7jWiF1.mjs +2 -0
  20. package/dist/{client-BTZ3XkrB.mjs → client-RV8NVeB8.mjs} +140 -21
  21. package/dist/{cloudflare-auth-DQkqoMYa.mjs → cloudflare-auth-Qdc7tw8F.mjs} +41 -38
  22. package/dist/{cloudflare-cmd-BKlfGHAy.mjs → cloudflare-cmd-BcrVyTmJ.mjs} +5 -5
  23. package/dist/cloudflare-config-Bktvwtpf.mjs +182 -0
  24. package/dist/{cloudflare-connect-Ctmrw579.mjs → cloudflare-connect-B8uPZ4nx.mjs} +3 -3
  25. package/dist/{cloudflare-operations-BT6OWFBk.mjs → cloudflare-operations-AiWashgg.mjs} +1 -1
  26. package/dist/{cloudflare-operations-B9tzgjmf.mjs → cloudflare-operations-fxHb-byx.mjs} +116 -172
  27. package/dist/{preset-UHj9ARyP.mjs → cloudflare-process-B-wekeR6.mjs} +93 -6
  28. package/dist/{config-VavjpDnp.d.mts → config-BMHb8RCj.d.mts} +1 -0
  29. package/dist/config-C_XRIPx2.mjs +89 -0
  30. package/dist/config-entry.d.mts +1 -1
  31. package/dist/{connect-Dg-WkW-C.mjs → connect-Bd4kJd9U.mjs} +5 -5
  32. package/dist/{create-project-DdoqwFLF.mjs → create-project-Boczwj5r.mjs} +1 -1
  33. package/dist/{create-project-l9J7pbtm.mjs → create-project-bMf6ffLZ.mjs} +2 -2
  34. package/dist/{db-gp2sCXyN.mjs → db-8uG64XLl.mjs} +21 -21
  35. package/dist/{delete-TTedbH6B.mjs → delete-NvbiyeJf.mjs} +2 -2
  36. package/dist/{deploy-CH-o2CaZ.mjs → deploy-DzAIwqNU.mjs} +908 -582
  37. package/dist/{deploy-B19L2Bra.mjs → deploy-bjXdFCtn.mjs} +1 -1
  38. package/dist/{domain-JRF59P_r.mjs → domain-y5Tvydvo.mjs} +3 -3
  39. package/dist/{email-Bo6G9LOZ.mjs → email-B3umsW75.mjs} +13 -28
  40. package/dist/{env-BYOrWQnv.mjs → env-BS6qYHDb.mjs} +4 -4
  41. package/dist/{env-public-BxU_0yTL.d.mts → env-public-BX_r8HR6.d.mts} +1 -1
  42. package/dist/{env-validation-BB4GkLxn.mjs → env-validation-BPn7vk-V.mjs} +18 -71
  43. package/dist/{env-validation-BXge7uyK.mjs → env-validation-DbTg7-ar.mjs} +1 -1
  44. package/dist/{fetch-CXDChK7B.mjs → fetch-BIZJh7vR.mjs} +2 -2
  45. package/dist/{fetch-stream-AOByI7Ki.mjs → fetch-stream-IvCYKyQL.mjs} +24 -4
  46. package/dist/{gen-sCtlCOdA.mjs → gen-B580--vC.mjs} +2 -2
  47. package/dist/gen-BVaUUumi.mjs +2 -0
  48. package/dist/{github-cmd-0-7PexDq.mjs → github-cmd-v45BdfKX.mjs} +2 -2
  49. package/dist/{handler-HEcZsaij.d.mts → handler-CZ4nAylQ.d.mts} +1 -1
  50. package/dist/help-DofyZuY7.mjs +2 -0
  51. package/dist/{help-GKtwl07I.mjs → help-daGKjXGk.mjs} +229 -747
  52. package/dist/index.d.mts +1 -1
  53. package/dist/index.mjs +288 -1502
  54. package/dist/info-B97bTX9N.mjs +113 -0
  55. package/dist/{init-FZx3Elvz.mjs → init-Bvy7zrBo.mjs} +46 -15
  56. package/dist/limits-Bq5LG8Id.d.mts +27 -0
  57. package/dist/limits-Cjuk2VPm.mjs +68 -0
  58. package/dist/{link-BrQfb_CU.mjs → link-CDqqCjFl.mjs} +3 -3
  59. package/dist/{list-D44jmIAM.mjs → list-CZj0dzKY.mjs} +3 -3
  60. package/dist/{live-CKJlvlNp.d.mts → live-Chw1eIMv.d.mts} +1 -1
  61. package/dist/{local-d1-Bg9OzEEO.mjs → local-d1-2CMnpuW_.mjs} +2 -2
  62. package/dist/login-DYt_An22.mjs +2 -0
  63. package/dist/{login-CAsAsQ-_.mjs → login-UZKFM_u7.mjs} +3 -3
  64. package/dist/{logs-BjKvFnVM.mjs → logs-CUZ6t9t3.mjs} +3 -3
  65. package/dist/migrate-8_2u55MD.mjs +2 -0
  66. package/dist/{migrate-BPITvDJN.mjs → migrate-DHul7PRV.mjs} +2 -1
  67. package/dist/{node-Dt7z256D.mjs → node-BkyRWRx8.mjs} +1 -1
  68. package/dist/operator-args-CLgKGlwU.mjs +690 -0
  69. package/dist/{operator-cmd-BK5CCiT9.mjs → operator-cmd-DBA6dl0m.mjs} +68 -22
  70. package/dist/pages/client.d.mts +34 -2
  71. package/dist/pages/client.mjs +59 -3
  72. package/dist/pages/index.d.mts +1 -1
  73. package/dist/pages/index.mjs +1 -1
  74. package/dist/pages/islands-plugin.mjs +1 -1
  75. package/dist/pages/protocol.d.mts +2 -2
  76. package/dist/pages/protocol.mjs +2 -308
  77. package/dist/{parse-filename-DioPHiR9.mjs → parse-filename-CUbj-1MP.mjs} +35 -1
  78. package/dist/plan-D1Q5rf-r.mjs +2 -0
  79. package/dist/plan-NPpwZ_kc.mjs +58 -0
  80. package/dist/platform-args-BJdRtlLq.mjs +506 -0
  81. package/dist/platform-args-D3RXyR6h.mjs +2 -0
  82. package/dist/{platform-auth-config-BlN8xTdD.mjs → platform-auth-config-B56E9YP1.mjs} +3 -3
  83. package/dist/{platform-auth-protection-7d5aV2Jg.mjs → platform-auth-protection-BDWO_aER.mjs} +2 -2
  84. package/dist/{platform-auth-recovery-Dxij8ZbR.mjs → platform-auth-recovery-1gg4CSRe.mjs} +3 -3
  85. package/dist/{platform-cmd-E0FuL212.mjs → platform-cmd-Bt-1w6pY.mjs} +1 -1
  86. package/dist/{platform-cmd-B3hwKrFK.mjs → platform-cmd-DMcQStSc.mjs} +24 -3
  87. package/dist/{platform-domain-D6Xcy9ZX.mjs → platform-domain-BNkcz0OB.mjs} +2 -2
  88. package/dist/{platform-lifecycle-k0E0xoxx.mjs → platform-lifecycle-9OyBALhH.mjs} +327 -215
  89. package/dist/{platform-lifecycle-DMh_qrry.mjs → platform-lifecycle-BE6C_jxh.mjs} +1 -1
  90. package/dist/{platform-management-Brz_BDiT.mjs → platform-management-CjwLVQwN.mjs} +6 -3
  91. package/dist/{platform-management-DOtss0BN.mjs → platform-management-CnyTdcWX.mjs} +1 -1
  92. package/dist/platform-plans-config-BNGKGr4P.mjs +359 -0
  93. package/dist/{platform-recovery-BvtcXON5.mjs → platform-recovery-D6KSpuFm.mjs} +2 -2
  94. package/dist/{plugin-inference-BMfKRSqE.mjs → plugin-inference-CXWnn79A.mjs} +174 -64
  95. package/dist/{prepare-C-6YZyyg.mjs → prepare-B5Mkic5u.mjs} +1 -1
  96. package/dist/{prepare-CRhJbVrG.mjs → prepare-DOsL0CC9.mjs} +4 -20
  97. package/dist/prepare-cgvDMtSb.mjs +2 -0
  98. package/dist/{project-cmd-BZLCqOqw.mjs → project-cmd-CNzrqvBv.mjs} +16 -16
  99. package/dist/{project-team-BvgM0WoX.mjs → project-team-DQOWPfQT.mjs} +2 -2
  100. package/dist/{project-token-l5DqK6Az.mjs → project-token-CTDYU0Cc.mjs} +2 -2
  101. package/dist/{project-zero-trust-v6Mvpp8y.mjs → project-zero-trust-BNAkW-_0.mjs} +2 -2
  102. package/dist/{protocol-BBa6cstI.d.mts → protocol-CjF_iI9X.d.mts} +2 -2
  103. package/dist/protocol-U7bfjHmA.mjs +331 -0
  104. package/dist/{provision-Fr9pRqce.mjs → provision-C4ORE7G7.mjs} +1 -1
  105. package/dist/{provision-C4IGqkBf.mjs → provision-CJFgTZY8.mjs} +89 -198
  106. package/dist/{requests-DN-BbNiM.mjs → requests-MhxYnau8.mjs} +2 -2
  107. package/dist/resource-name-C7LVpcRm.mjs +11 -0
  108. package/dist/{rollback-D7rzSaZM.mjs → rollback-CJ6iSDoU.mjs} +3 -3
  109. package/dist/route-url-CG7U-cRN.mjs +15 -0
  110. package/dist/{runner-B8wXwWlo.mjs → runner-CXA9Fh8h.mjs} +1 -1
  111. package/dist/{runner-p-dMs2UN.mjs → runner-Ol0TNjk6.mjs} +2 -2
  112. package/dist/runtime/ai.d.mts +12 -8
  113. package/dist/runtime/ai.mjs +84 -17
  114. package/dist/runtime/better-auth-mysql.mjs +1 -1
  115. package/dist/runtime/better-auth-pg.mjs +1 -1
  116. package/dist/runtime/better-auth.mjs +1 -1
  117. package/dist/runtime/client-react.mjs +2 -2
  118. package/dist/runtime/client-solid.mjs +2 -2
  119. package/dist/runtime/client-svelte.mjs +2 -2
  120. package/dist/runtime/client-vue.mjs +2 -2
  121. package/dist/runtime/client.mjs +2 -2
  122. package/dist/runtime/durable.d.mts +3 -1
  123. package/dist/runtime/durable.mjs +4 -1
  124. package/dist/runtime/email/testing.mjs +1 -1
  125. package/dist/runtime/env-public.d.mts +1 -1
  126. package/dist/runtime/fetch-stream.mjs +1 -1
  127. package/dist/runtime/fetch.mjs +1 -1
  128. package/dist/runtime/handler.d.mts +1 -1
  129. package/dist/runtime/kv.mjs +0 -1
  130. package/dist/runtime/limits.d.mts +2 -0
  131. package/dist/runtime/limits.mjs +2 -0
  132. package/dist/runtime/live-client.d.mts +1 -1
  133. package/dist/runtime/live-server.mjs +25 -16
  134. package/dist/runtime/live.d.mts +1 -1
  135. package/dist/runtime/migration-handler.mjs +62 -42
  136. package/dist/runtime/route-url.d.mts +4 -0
  137. package/dist/runtime/route-url.mjs +2 -0
  138. package/dist/runtime/routing.d.mts +180 -0
  139. package/dist/runtime/routing.mjs +1082 -0
  140. package/dist/runtime/sandbox-container.d.mts +1 -1
  141. package/dist/runtime/sandbox-container.mjs +1 -1
  142. package/dist/runtime/sandbox.d.mts +3 -3
  143. package/dist/runtime/sandbox.mjs +75 -45
  144. package/dist/runtime/sse.mjs +1 -1
  145. package/dist/runtime/validator.d.mts +1 -1
  146. package/dist/runtime/ws-server.d.mts +4 -2
  147. package/dist/runtime/ws-server.mjs +27 -2
  148. package/dist/runtime/ws.d.mts +2 -2
  149. package/dist/runtime/ws.mjs +8 -6
  150. package/dist/{sandbox-qpNBT8a3.d.mts → sandbox-XZAqzFlG.d.mts} +11 -8
  151. package/dist/{sandbox-container-DdNEBfCc.d.mts → sandbox-container-Bo0eiFKz.d.mts} +2 -1
  152. package/dist/{sandbox-container-4fdqLnyb.mjs → sandbox-container-C6ItmVuN.mjs} +4 -2
  153. package/dist/{scan-4tfN-PSn.mjs → scan-C7okrLyM.mjs} +4 -35
  154. package/dist/{secret-BOtOl_cb.mjs → secret-Dli5fP0B.mjs} +4 -4
  155. package/dist/{sse-BaC1jXko.mjs → sse-CQNaDFFV.mjs} +6 -3
  156. package/dist/validate-Dq_L3s0S.mjs +2 -0
  157. package/dist/{validate-CIUwFpjB.mjs → validate-ctOrgiS3.mjs} +2 -1
  158. package/dist/{wrangler-DQF1vKyf.mjs → wrangler-7K-bW_DL.mjs} +8 -239
  159. package/dist/{ws-BoY7vQML.d.mts → ws-CL1w7GXU.d.mts} +13 -2
  160. package/package.json +15 -8
  161. package/skills/migrate-vite-cloudflare-to-void/SKILL.md +34 -157
  162. package/skills/void/SKILL.md +50 -135
  163. package/skills/void/docs/guide/ai.md +94 -84
  164. package/skills/void/docs/guide/app-types.md +3 -32
  165. package/skills/void/docs/guide/auth.md +12 -116
  166. package/skills/void/docs/guide/database/d1.md +9 -54
  167. package/skills/void/docs/guide/database/mysql.md +1 -1
  168. package/skills/void/docs/guide/database/postgresql.md +5 -26
  169. package/skills/void/docs/guide/database.md +23 -75
  170. package/skills/void/docs/guide/deployment.md +27 -113
  171. package/skills/void/docs/guide/durable-state.md +43 -18
  172. package/skills/void/docs/guide/edge/headers.md +3 -47
  173. package/skills/void/docs/guide/edge/prerendering.md +5 -20
  174. package/skills/void/docs/guide/edge/redirects.md +11 -64
  175. package/skills/void/docs/guide/edge/revalidation.md +5 -18
  176. package/skills/void/docs/guide/edge/rewrites.md +56 -284
  177. package/skills/void/docs/guide/edge/static-assets.md +22 -72
  178. package/skills/void/docs/guide/email/domains.md +112 -0
  179. package/skills/void/docs/guide/email/receiving.md +139 -0
  180. package/skills/void/docs/guide/email/sending.md +231 -0
  181. package/skills/void/docs/guide/email.md +13 -619
  182. package/skills/void/docs/guide/env-migration.md +11 -11
  183. package/skills/void/docs/guide/env-vars.md +9 -29
  184. package/skills/void/docs/guide/index.md +0 -15
  185. package/skills/void/docs/guide/jobs.md +3 -18
  186. package/skills/void/docs/guide/kv.md +5 -11
  187. package/skills/void/docs/guide/live.md +5 -56
  188. package/skills/void/docs/guide/pages-routing/actions-and-forms.md +78 -125
  189. package/skills/void/docs/guide/pages-routing/head.md +10 -10
  190. package/skills/void/docs/guide/pages-routing/islands.md +6 -36
  191. package/skills/void/docs/guide/pages-routing/layouts.md +6 -128
  192. package/skills/void/docs/guide/pages-routing/loaders.md +3 -19
  193. package/skills/void/docs/guide/pages-routing/markdown.md +13 -171
  194. package/skills/void/docs/guide/pages-routing/overview.md +7 -17
  195. package/skills/void/docs/guide/pages-routing/view-transitions.md +1 -1
  196. package/skills/void/docs/guide/platform/administration/access.md +1 -4
  197. package/skills/void/docs/guide/platform/administration/email.md +35 -8
  198. package/skills/void/docs/guide/platform/administration/operations.md +26 -4
  199. package/skills/void/docs/guide/platform/administration/plans.md +126 -0
  200. package/skills/void/docs/guide/platform/administration/projects.md +5 -2
  201. package/skills/void/docs/guide/platform/administration/zero-trust.md +23 -152
  202. package/skills/void/docs/guide/platform/development/runtime.md +3 -13
  203. package/skills/void/docs/guide/platform/development/schema-ci.md +0 -58
  204. package/skills/void/docs/guide/platform/installation/credentials.md +6 -4
  205. package/skills/void/docs/guide/platform/installation/domains.md +30 -2
  206. package/skills/void/docs/guide/platform/installation/first-deployment.md +2 -0
  207. package/skills/void/docs/guide/platform/installation/maintenance.md +3 -1
  208. package/skills/void/docs/guide/platform/installation/prerequisites.md +20 -15
  209. package/skills/void/docs/guide/platform/installation/setup.md +9 -5
  210. package/skills/void/docs/guide/platform-administration.md +1 -0
  211. package/skills/void/docs/guide/queues.md +7 -9
  212. package/skills/void/docs/guide/quickstart.md +38 -37
  213. package/skills/void/docs/guide/remote-dev.md +4 -9
  214. package/skills/void/docs/guide/sandboxes.md +29 -21
  215. package/skills/void/docs/guide/server-routing.md +9 -72
  216. package/skills/void/docs/guide/sse.md +4 -18
  217. package/skills/void/docs/guide/ssg.md +3 -15
  218. package/skills/void/docs/guide/ssr.md +14 -62
  219. package/skills/void/docs/guide/storage.md +9 -4
  220. package/skills/void/docs/guide/type-safety.md +3 -14
  221. package/skills/void/docs/guide/typed-fetch.md +3 -7
  222. package/skills/void/docs/guide/websockets.md +68 -40
  223. package/skills/void/docs/integrations/agents.md +3 -3
  224. package/skills/void/docs/integrations/cloudflare.md +85 -316
  225. package/skills/void/docs/integrations/frameworks/analog.md +5 -64
  226. package/skills/void/docs/integrations/frameworks/astro.md +4 -73
  227. package/skills/void/docs/integrations/frameworks/nuxt.md +5 -62
  228. package/skills/void/docs/integrations/frameworks/overview.md +11 -54
  229. package/skills/void/docs/integrations/frameworks/react-router.md +5 -60
  230. package/skills/void/docs/integrations/frameworks/sveltekit.md +6 -65
  231. package/skills/void/docs/integrations/frameworks/tanstack-start.md +4 -62
  232. package/skills/void/docs/integrations/nodejs-bun-deno.md +5 -69
  233. package/skills/void/docs/reference/api/auth.md +156 -0
  234. package/skills/void/docs/reference/api/client.md +87 -0
  235. package/skills/void/docs/reference/api/database.md +95 -0
  236. package/skills/void/docs/reference/api/durable.md +46 -0
  237. package/skills/void/docs/reference/api/env.md +50 -0
  238. package/skills/void/docs/reference/api/handlers.md +254 -0
  239. package/skills/void/docs/reference/api/pages.md +241 -0
  240. package/skills/void/docs/reference/api/plugin.md +39 -0
  241. package/skills/void/docs/reference/api/resources.md +109 -0
  242. package/skills/void/docs/reference/api/rewrites.md +76 -0
  243. package/skills/void/docs/reference/api/types.md +92 -0
  244. package/skills/void/docs/reference/api.md +56 -1218
  245. package/skills/void/docs/reference/cli/auth.md +88 -0
  246. package/skills/void/docs/reference/cli/database.md +128 -0
  247. package/skills/void/docs/reference/cli/deploy.md +85 -0
  248. package/skills/void/docs/reference/cli/domains.md +41 -0
  249. package/skills/void/docs/reference/cli/email.md +129 -0
  250. package/skills/void/docs/reference/cli/generate.md +116 -0
  251. package/skills/void/docs/reference/cli/github.md +189 -0
  252. package/skills/void/docs/reference/cli/platform-config.md +92 -0
  253. package/skills/void/docs/reference/cli/platform-email.md +81 -0
  254. package/skills/void/docs/reference/cli/platform-installation.md +127 -0
  255. package/skills/void/docs/reference/cli/platform-operations.md +90 -0
  256. package/skills/void/docs/reference/cli/platform-users.md +89 -0
  257. package/skills/void/docs/reference/cli/platform-zero-trust.md +45 -0
  258. package/skills/void/docs/reference/cli/platform.md +70 -0
  259. package/skills/void/docs/reference/cli/project.md +214 -0
  260. package/skills/void/docs/reference/cli/secrets.md +76 -0
  261. package/skills/void/docs/reference/cli/setup.md +70 -0
  262. package/skills/void/docs/reference/cli.md +32 -1682
  263. package/skills/void/docs/reference/config.md +12 -18
  264. package/skills/void/docs/reference/resource-inference.md +3 -58
  265. package/skills/void/docs/reference/structure.md +14 -41
  266. package/dist/canonical-json-DuDiiUsQ.mjs +0 -13
  267. package/dist/client-Czz8o5jP.mjs +0 -2
  268. package/dist/gen-CNJ62MM7.mjs +0 -2
  269. package/dist/help-CmZzxUba.mjs +0 -2
  270. package/dist/login-CGcRKEoi.mjs +0 -2
  271. package/dist/migrate-CYfbKkXh.mjs +0 -2
  272. package/dist/plan-BEZ8VJW0.mjs +0 -256
  273. package/dist/plan-DpuOr14e.mjs +0 -2
  274. package/dist/prepare-Bm3iq-u4.mjs +0 -2
  275. package/dist/validate-EKmJWxmy.mjs +0 -2
  276. /package/dist/cli/{cf-compat.d.mts → cloudflare-operation-process.d.mts} +0 -0
@@ -4,15 +4,11 @@ outline: deep
4
4
 
5
5
  # Cloudflare
6
6
 
7
- Void runs on Cloudflare Workers. Use this guide to access bindings, configure your Worker, and deploy to your own account.
7
+ Void runs on Cloudflare Workers. It detects the resources your app uses, creates bindings, and deploys to your account.
8
8
 
9
9
  ## Bindings
10
10
 
11
- Void detects supported resource use in your source and provisions the corresponding bindings. How you access them depends on your framework.
12
-
13
- ### Via Hono context (`c.env`)
14
-
15
- In Void's default routing mode, route handlers and middleware receive a Hono `Context` object with bindings on `c.env`:
11
+ In Void routes and middleware, use `c.env`:
16
12
 
17
13
  ```ts
18
14
  // routes/api/users.ts
@@ -20,58 +16,23 @@ import { defineHandler } from 'void';
20
16
 
21
17
  export const GET = defineHandler(async (c) => {
22
18
  const { results } = await c.env.DB.prepare('SELECT * FROM users').all();
23
- return c.json(results);
19
+ return c.json({ users: results });
24
20
  });
25
21
  ```
26
22
 
27
- ```ts
28
- // routes/api/cache.ts
29
- import { defineHandler } from 'void';
30
-
31
- export const GET = defineHandler(async (c) => {
32
- const value = await c.env.KV.get('key');
33
- return c.json({ value });
34
- });
35
- ```
36
-
37
- `CloudContext` provides the types for `c.env`, so you don't need to declare them yourself.
38
-
39
- ### Via `cloudflare:workers` import
40
-
41
- When using framework mode (TanStack Start or React Router), the framework owns routing and you access bindings through the `cloudflare:workers` module instead:
42
-
43
- ::: warning ⚠️ Cloudflare env access in meta frameworks
44
- Some frameworks, like Nuxt and SvelteKit, do not run in workerd during dev and therefore do not support directly importing from `cloudflare:workers`.
45
- :::
23
+ TanStack Start and React Router can import bindings from `cloudflare:workers`:
46
24
 
47
25
  ```ts
48
26
  import { env } from 'cloudflare:workers';
49
27
 
50
- const result = await env.DB.prepare('SELECT * FROM users').all();
28
+ const { results } = await env.DB.prepare('SELECT * FROM users').all();
51
29
  ```
52
30
 
53
- This also works in server functions:
54
-
55
- ```tsx
56
- // src/routes/users.tsx (TanStack Start)
57
- import { createFileRoute } from '@tanstack/react-router';
58
- import { createServerFn } from '@tanstack/react-start';
59
- import { env } from 'cloudflare:workers';
60
-
61
- const getUsers = createServerFn().handler(async () => {
62
- const { results } = await env.DB.prepare('SELECT * FROM users').all();
63
- return results;
64
- });
65
-
66
- export const Route = createFileRoute('/users')({
67
- loader: () => getUsers(),
68
- component: UsersPage,
69
- });
70
- ```
31
+ Other frameworks use their own binding access. Follow your [framework's setup guide](./frameworks/overview.md).
71
32
 
72
33
  ### TypeScript setup
73
34
 
74
- Add `"void/env"` to your tsconfig `types` to get typed bindings on `env`:
35
+ Add `void/env` to your TypeScript types:
75
36
 
76
37
  ```json
77
38
  {
@@ -81,38 +42,24 @@ Add `"void/env"` to your tsconfig `types` to get typed bindings on `env`:
81
42
  }
82
43
  ```
83
44
 
84
- This augments the `Cloudflare.Env` interface with `DB`, `KV`, `STORAGE`, `AI`, and `QUEUE_*` and also pulls in `@cloudflare/workers-types`, so you don't need to add that separately.
45
+ This provides binding types and Workers runtime types.
85
46
 
86
47
  ### Available bindings
87
48
 
88
- | Binding | Type | Trigger |
89
- | ---------------- | ------------------------ | --------------------------------------------------------------- |
90
- | `DB` | `D1Database` | `env.DB` / `c.env.DB` or `import from "void/db"` |
91
- | `KV` | `KVNamespace` | `env.KV` / `c.env.KV` or `import from "void/kv"` |
92
- | `STORAGE` | `R2Bucket` | `env.STORAGE` / `c.env.STORAGE` or `import from "void/storage"` |
93
- | `AI` | `Ai` | `env.AI` / `c.env.AI` or `import from "void/ai"` |
94
- | `QUEUE_*` | `Queue<T>` | `defineQueue()` or `import { queues } from "void/queues"` |
95
- | filename-derived | `DurableObjectNamespace` | module in `durable-objects/` |
96
-
97
- Bindings are [inferred automatically](../reference/resource-inference.md) by scanning your source files for import and access patterns. You can also set them explicitly in `void.config.ts`:
98
-
99
- ```ts
100
- import { defineConfig } from 'void/config';
101
-
102
- export default defineConfig({
103
- inference: {
104
- bindings: { db: true, kv: true, storage: false, ai: 'MY_AI' },
105
- },
106
- });
107
- ```
108
-
109
- `db`, `kv`, `storage`, and `ai` accept a string to customize the binding name (for example, `"db": "MY_DB"` or `"ai": "MY_AI"`). `email` is a boolean feature switch.
49
+ | Binding | Resource | Use in your app |
50
+ | ---------------- | --------------- | --------------------------------- |
51
+ | `DB` | D1 | `c.env.DB` or `void/db` |
52
+ | `KV` | KV | `c.env.KV` or `void/kv` |
53
+ | `STORAGE` | R2 | `c.env.STORAGE` or `void/storage` |
54
+ | `AI` | Workers AI | `c.env.AI` or `void/ai` |
55
+ | `QUEUE_*` | Queues | `defineQueue()` or `void/queues` |
56
+ | Filename-derived | Durable Objects | A module in `durable-objects/` |
110
57
 
111
- See [Configuration](../reference/config.md) for details.
58
+ Use [binding configuration](../reference/resource-inference.md#explicit-binding-overrides) when you need to enable, disable, or rename an inferred binding.
112
59
 
113
- ### Cloudflare configuration passthrough
60
+ ## Cloudflare Configuration
114
61
 
115
- You can set non-binding Cloudflare fields like `compatibility_date` and `compatibility_flags` in `void.config.ts`:
62
+ Define Worker settings in `void.config.ts`:
116
63
 
117
64
  ```ts
118
65
  import { defineConfig } from 'void/config';
@@ -122,91 +69,18 @@ export default defineConfig({
122
69
  compatibility_date: '2026-02-24',
123
70
  compatibility_flags: ['nodejs_compat'],
124
71
  },
72
+ cloudflare: {
73
+ name: 'my-app',
74
+ services: [{ binding: 'API', service: 'my-api-worker' }],
75
+ },
125
76
  });
126
77
  ```
127
78
 
128
- For environment variables, declare the schema in `env.ts` and put local values in the single root `.env` file. Void loads `.env` into local development bindings only; it never becomes production configuration:
129
-
130
- ```bash
131
- # .env
132
- API_URL=https://api.example.com
133
- ```
134
-
135
- Binding arrays such as `d1_databases`, `kv_namespaces`, and `r2_buckets` belong in the `cloudflare` field. Void adds inferred bindings that are missing by name and preserves custom bindings with real resource IDs. See [Cloudflare Configuration](#cloudflare-configuration) for details.
136
-
137
- For non-secret plain-text defaults, you can also set `worker.vars` in `void.config.ts`. Local `.env` values override `worker.vars` during development. Production builds reject any `worker.vars` name that is declared as a server key in `env.ts`; store those values remotely with `void secret put` instead.
138
-
139
- ## Cloudflare Configuration
140
-
141
- Set Worker options in `void.config.ts`. Void combines its `cloudflare` settings with resource IDs in `void.lock.json` for development and deployment. `void init` and `void deploy` migrate root `wrangler.jsonc` or `wrangler.json` files from existing apps.
142
-
143
- Commit `void.lock.json` when Void records resource IDs or Durable Object migration history.
144
-
145
- ### Void-only mode and Vite-based frameworks
146
-
147
- In native Void apps, TanStack Start, and React Router, Void manages the Cloudflare Vite plugin. It merges inferred bindings with your `cloudflare` settings:
148
-
149
- - An existing binding with the same name stays unchanged.
150
- - Missing inferred bindings get local placeholder IDs for development. Deploy replaces them with provisioned IDs.
151
- - Other `cloudflare` settings, such as routes, services, variables, and compatibility settings, pass through.
152
-
153
- Void sets `main`, `triggers`, and `assets` from your project structure. It also records typed Durable Object bindings and migration history from `durable-objects/` in `void.lock.json`.
154
-
155
- ### Adapter-based frameworks (SvelteKit, Nuxt, Astro)
156
-
157
- SvelteKit, Nuxt, and Astro build Workers through their own adapters. Void provides database types, migrations, and binding sync without replacing those adapters.
158
-
159
- At dev startup, Void updates the framework's generated Cloudflare config:
160
-
161
- - It adds missing bindings by name without changing existing ones.
162
- - It copies `worker.compatibility_date`, when set, and adds the `nodejs_als` compatibility flag.
163
-
164
- `void deploy --platform cloudflare` provisions production resources for inferred bindings.
165
-
166
- ### Merge precedence
167
-
168
- | Source | Priority | What it controls |
169
- | ------------------------------- | ----------------------------- | ------------------------------------------------------------------- |
170
- | `void.config.ts` `cloudflare` | Highest for authored settings | Resource IDs, service bindings, routes, vars, environments |
171
- | `void.config.ts` `worker` field | Highest for compat | `compatibility_date`, `compatibility_flags`, `vars` |
172
- | Void inference | Fills gaps only | Adds placeholder bindings for inferred resources not in your config |
173
-
174
- If no date is found in `void.config.ts` or the generated build config, Void records the latest known-good date and uses it for that run.
175
-
176
- ### Example
177
-
178
- If your code uses `c.env.DB` and `c.env.KV`, and `cloudflare` only defines D1:
179
-
180
- ```ts
181
- // Inside defineConfig({ ... })
182
- cloudflare: {
183
- name: 'my-app',
184
- d1_databases: [{
185
- binding: 'DB',
186
- database_name: 'my-app-db',
187
- database_id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
188
- }],
189
- services: [{ binding: 'API', service: 'my-api-worker' }],
190
- },
191
- ```
192
-
193
- Void sees that `DB` is already configured and leaves it alone (including your real `database_id`), but adds a local placeholder for `KV` since it's missing. The `services` array passes through unchanged.
79
+ Put binding arrays, resource IDs, routes, and service bindings under `cloudflare`. Void preserves your configured bindings and adds missing inferred resources. The `worker` field takes precedence for compatibility settings and variables. See [Configuration](../reference/config.md).
194
80
 
195
- This means `pnpm dev` works out of the box (Miniflare creates local instances of all bindings), while `void deploy --platform cloudflare` uses your real D1 database ID and service bindings.
81
+ Commit `void.lock.json` when Void records resource IDs or Durable Object migrations. Keep generated Cloudflare config out of Git. Frameworks that require an adapter config path use `.void-wrangler.jsonc`; their setup guides show the required options.
196
82
 
197
- ### What ends up in the build output
198
-
199
- After `vite build`, the Cloudflare Vite plugin writes a merged `wrangler.json` to the `dist/` directory. This file contains:
200
-
201
- - All `cloudflare` fields from `void.config.ts` (bindings with real IDs, routes, services, vars, environments)
202
- - Any inferred bindings Void added (local placeholders during development; provisioned IDs during deploy)
203
- - Fields set by Void (`main`, `assets`, `triggers`)
204
-
205
- When you run `void deploy --platform cloudflare`, Void validates this generated `wrangler.json` and deploys it safely.
206
-
207
- ::: tip
208
- When deploying to a Void platform, the `wrangler.json` in the build output is **skipped** because the platform manages Worker configuration through its own deploy manifest. The merge behavior described here applies to direct Cloudflare deployment.
209
- :::
83
+ Declare environment variables in `env.ts`, put local values in `.env`, and set production server values with `void secret put`. See [Environment Variables](../guide/env-vars.md).
210
84
 
211
85
  ## Deploy to your own Cloudflare account
212
86
 
@@ -216,255 +90,150 @@ Choose Cloudflare during `void init`, then deploy:
216
90
  void deploy
217
91
  ```
218
92
 
219
- Void opens your browser to sign in when needed. If you have access to several accounts, it asks which one to use and records the account in `void.lock.json`. Credentials are stored in your operating system's keychain. Press Ctrl+C to cancel login, or manage your session with `void cloudflare login`, `status`, and `logout`.
93
+ Void opens your browser to sign in and asks which account to use when you have several. If the browser doesn't open, follow the link and confirm the code printed in the terminal.
220
94
 
221
- To configure deployment separately from project setup, run `void connect --platform cloudflare`. It uses the same browser login and account selection as `void init`, and saves Cloudflare as the target for subsequent `void deploy` commands.
222
-
223
- ### One command: `void deploy --platform cloudflare`
224
-
225
- You can also choose Cloudflare for a single deploy:
95
+ To set up deployment later, run `void connect --platform cloudflare`. To select Cloudflare for one deploy, run:
226
96
 
227
97
  ```sh
228
98
  void deploy --platform cloudflare
229
99
  ```
230
100
 
231
- This deploys directly to your account. You don't need a Void platform connection or project. Once Cloudflare is saved in `.void/project.json`, `void deploy` uses it automatically.
101
+ Use `void cloudflare login`, `status`, and `logout` to manage your session.
232
102
 
233
103
  ### Deploy an existing Worker
234
104
 
235
- This handoff is for the existing source of an app already built with Void and previously deployed directly to Cloudflare. Keep its `wrangler.jsonc` or `wrangler.json` for the first deploy; Void migrates it to `void.config.ts` and `void.lock.json` after linking the Worker. Then run:
105
+ For an existing Void app deployed directly to Cloudflare, keep its root `wrangler.jsonc` or `wrangler.json` and run:
236
106
 
237
107
  ```sh
238
108
  void deploy
239
109
  ```
240
110
 
241
- If the project has no deployment destination, Void asks whether to link and deploy to Cloudflare using the existing Worker and resources. Accept to sign in, verify the configured Worker, and deploy in the same command. Your account and Worker name stay the same. The next deployment is just `void deploy`.
242
-
243
- The first handoff preserves resource bindings, production variables, encrypted secrets, event handlers, and existing routes and schedules. It builds the app, checks the uploaded version's bindings and handler set, and verifies readiness before activating it. Void saves `keep_vars: true` in your config so later deployments retain dashboard-only variables too. The active version must also be the latest uploaded version; activate or remove an unpublished candidate in Cloudflare before retrying so secret inheritance has an unambiguous source.
111
+ If no destination is selected, Void offers to link the existing Worker. Accept to sign in and deploy. Void migrates the configuration to `void.config.ts` and `void.lock.json`, preserving bindings, variables, secrets, routes, and schedules. Commit both files.
244
112
 
245
- Keep this first deployment focused on the existing site. Apart from the explicit ISR cache choice below, new resources, auth setup, runtime features, database migrations, and secret overrides stop the handoff before activation. A later code-only deploy continues to preserve dashboard-managed routes and visibility. Before adding a cron, queue consumer, workflow, or visibility change when those values exist only in the dashboard, add the current `routes`, `workers_dev`, and `preview_urls` values under `cloudflare` in `void.config.ts`. If the build fails, fix it and rerun `void deploy`; Void remembers the destination and checks the existing Worker again.
113
+ Keep the first deployment focused on the existing app. Add resources, auth, migrations, or other runtime features after linking. If the Worker has a newer inactive upload, activate or remove it in Cloudflare before retrying.
246
114
 
247
- Pages with `prerender = true` (including automatically prerendered Markdown pages) also request ISR caching. When the existing Worker has no cache, Void asks whether to enable it and saves the choice as `routing.isr` in `void.config.ts`:
115
+ Readiness checks need either an existing [version metadata binding](https://developers.cloudflare.com/workers/runtime-apis/bindings/version-metadata/) or access to [Version URLs](https://developers.cloudflare.com/workers/versions-and-deployments/version-urls/). Void stops before activation if neither is available.
248
116
 
249
- - **Yes:** provisions the KV cache and enables ISR in this deployment. The uploaded Worker must retain every existing binding and use exactly the approved cache namespace.
250
- - **No:** saves `routing.isr: false`. Pages render on each request, and later deployments keep ISR disabled until you change that setting.
117
+ Prerendered pages can require a new ISR cache. Void asks whether to enable it and saves the choice as `routing.isr`. Choose `false` to render those pages on each request instead. In CI, set that choice before deploying. Existing caches retain their IDs.
251
118
 
252
- You can keep your page-level prerender and revalidate exports with either choice. The same saved choice applies to retries, other machines, and CI; completing the handoff does not change it. To choose ahead of time, set `routing.isr` to `true` or `false`. A non-interactive handoff with no saved choice stops with instructions to set it. Existing caches keep their namespace IDs. Commit `void.config.ts` and `void.lock.json` when it changes.
119
+ If linking reports a missing binding, use the existing Worker's binding name and resource ID in `cloudflare` and `inference.bindings`. Keep `.void/cloudflare-link.json` when retrying.
253
120
 
254
- If linking reports other missing inferred resources, the error lists each binding and why the app needs it. Check that `cloudflare` in `void.config.ts` uses the existing Worker's binding names and resource IDs, and that `inference.bindings` in `void.config.ts` matches those names. An application KV binding explicitly named `ISR_CACHE` is still required. An existing D1 binding without checked-in migrations can keep its current `migrations_dir`, including an omitted value; linking does not provision a database or change its migration settings. Keep `.void/cloudflare-link.json` when retrying so the handoff checks remain in place.
255
-
256
- Declining the initial link-and-deploy prompt leaves the project unchanged. Explicit platform choices and existing project links take precedence. In CI, select the destination with `--platform cloudflare` and provide Cloudflare credentials.
257
-
258
- Direct deployment uses the top-level `cloudflare` settings in `void.config.ts`; convert a `wrangler.toml` to JSON/JSONC before linking. Named environments need a separate Void project config. If previews are disabled, include the existing production hostname in your config so Void can check the candidate through it.
121
+ Before changing dashboard-managed triggers or visibility later, add the current `routes`, `workers_dev`, and `preview_urls` to your config. Convert `wrangler.toml` to JSON/JSONC before linking. Named environments need separate Void project configs.
259
122
 
260
123
  ### What happens during deploy
261
124
 
262
- Void reads your app's configuration and source, then:
263
-
264
- 1. Checks the app type, selected account, and Cloudflare credentials.
265
- 2. If the app uses email (`sendEmail()` or `email/` handlers), reads your zone's email state and, before any project code runs, asks once to set it up. See [Your own Cloudflare account](../guide/email.md#your-own-cloudflare-account) in the email guide.
266
- 3. Creates missing resources or reuses resources already in your account.
267
- 4. Builds the app and checks the generated Worker config, secrets, and migration history.
268
- 5. Preserves or creates the auth secret, verifies required server keys in encrypted remote storage, and applies pending migrations.
269
- 6. Uploads a Worker Version and checks that it is ready.
270
- 7. Sends traffic to the new version and applies routes, custom domains, cron schedules, queue consumers, and Email Routing rules.
271
-
272
- The secret and migration checks need the built Worker, so they happen after provisioning and building. If a check fails, resources may already exist, but Void has not applied remote D1 migrations or uploaded the application Worker.
125
+ Void creates missing resources, builds the app, checks secrets and migrations, uploads the Worker, verifies readiness, and updates traffic and triggers. Resource IDs are saved in `void.lock.json`.
273
126
 
274
- Resource IDs are saved in `void.lock.json` for the next deploy. Commit the lock so other machines and CI can reuse the same resources. The old `--provision` flag is still accepted, but provisioning is now automatic.
127
+ A failed build or validation can leave provisioned resources in your account. Run the first deploy from one machine at a time, then commit the lock file before deploying from other machines or CI.
275
128
 
276
- Run the first deploy from one machine at a time. The provisioning lock protects a local config file; it cannot coordinate two fresh CI runners, which could create duplicate resources.
129
+ Apps using email need `email.from` in `void.config.ts`. Void reviews the domain's setup before building and asks before changing it. See [Email](../guide/email/domains.md#your-own-cloudflare-account).
277
130
 
278
131
  ### Deploying from CI
279
132
 
280
- Set `CLOUDFLARE_API_TOKEN` in your CI secrets. The token needs Workers Scripts: Edit and read access to bound resources, plus edit permission for each product Void needs to provision. Set `CLOUDFLARE_ACCOUNT_ID` if the token can access more than one account.
133
+ Set these CI secrets and variables as needed:
281
134
 
282
- Email setup needs a browser session from `void cloudflare login`, which carries the Email Routing and Email Sending scopes, or a `CLOUDFLARE_API_TOKEN` that also has Email Routing Edit and Email Sending Edit. A Global API Key pair is refused. In CI the email step never prompts: run `void email setup --platform cloudflare` once locally, commit `void.lock.json`, then deploy with `--require-email`.
135
+ | Name | When needed |
136
+ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
137
+ | `CLOUDFLARE_API_TOKEN` | Required. Workers Scripts: Edit, read access to bound resources, and edit access to products Void provisions. |
138
+ | `CLOUDFLARE_ACCOUNT_ID` | When the token can access several accounts. |
139
+ | `DATABASE_URL` | PostgreSQL and MySQL provisioning and migrations. |
140
+ | `CLOUDFLARE_WORKERS_SUBDOMAIN` | When the Worker has no version preview URL. Use `my-team` or `my-team.workers.dev`. |
141
+ | `CF_ACCESS_CLIENT_ID`, `CF_ACCESS_CLIENT_SECRET` | When Cloudflare Access protects the readiness URL. |
283
142
 
284
- When a Worker has no version preview URL, such as a Worker with Durable Objects, also set `CLOUDFLARE_WORKERS_SUBDOMAIN`. Use the account subdomain, for example `my-team` or `my-team.workers.dev`. Local deploys cache this value in ignored `.void/cloudflare.json` after Cloudflare reports a deployment URL; a fresh CI checkout has no such cache.
143
+ For email, run `void email setup --platform cloudflare` locally, commit `void.lock.json`, and pass `--require-email` in CI. The token also needs Email Routing Edit and Email Sending Edit.
285
144
 
286
145
  #### Cloudflare Access
287
146
 
288
- If Cloudflare Access protects the Worker's `workers.dev` hostname, Void needs credentials to check deployment readiness. In CI, provide an allowed `CF_ACCESS_CLIENT_ID` and `CF_ACCESS_CLIENT_SECRET` pair. For a local deploy, you can use a short-lived user session:
147
+ For local deployment to an Access-protected Worker, you can use a short-lived user session:
289
148
 
290
149
  ```sh
291
150
  export CF_ACCESS_TOKEN="$(cloudflared access token --app=https://<worker>.<account>.workers.dev)"
292
151
  ```
293
152
 
294
- Void sends these credentials only to a matching HTTPS readiness URL. It doesn't save them or pass them to your build or deployment tools.
295
-
296
- If Access blocks a version preview but allows the stable Worker hostname, Void checks the new version through that hostname using a version override. If Access still denies the request, Void leaves the upload inactive or restores the previous deployment. A required first-deploy bootstrap is an exception: that Worker is already active behind Access when the check runs.
153
+ Void uses Access credentials for readiness checks without saving them. If checks are denied, the deployment fails. A first-deploy bootstrap can already be active behind Access when this happens.
297
154
 
298
155
  ### Databases and secrets
299
156
 
300
- PostgreSQL and MySQL apps need a production `DATABASE_URL` in the deploy process environment. Void uses it for Hyperdrive provisioning and migrations, and never writes it to generated config. PostgreSQL migrations are transactional; MySQL schema changes may partially apply before an error.
301
-
302
- Creating a Hyperdrive config for the first time also requires `CLOUDFLARE_API_TOKEN` with Hyperdrive edit permission. Void needs the REST API to find existing configurations, and the current provisioning path can't use browser OAuth for that lookup. Alternatively, create the config in the Cloudflare dashboard and add its ID to the `cloudflare.hyperdrive` binding in `void.config.ts`. An app with an existing Hyperdrive binding can deploy through browser login.
303
-
304
- For auth-enabled apps, run `void db generate` and commit the migrations. Void includes the production Better Auth schema, including renamed tables and plugin tables, and checks it against your migration history during deploy. D1 also gets an in-memory schema check before remote migration. A mismatch stops deployment and asks you to regenerate the SQL.
157
+ For PostgreSQL and MySQL, supply `DATABASE_URL` to the deploy process. Void uses it for Hyperdrive and migrations without writing it to config. MySQL schema changes can partially apply before an error.
305
158
 
306
- Custom D1 layouts are supported through `migrations_dir`, `migrations_table`, and `migrations_pattern`. The files Cloudflare will apply must match the files Void validated, including their content and numeric order. Missing, extra, or reordered migrations stop deployment.
159
+ Creating Hyperdrive requires an API token with Hyperdrive edit permission. Alternatively, create it in Cloudflare and add its ID to `cloudflare.hyperdrive`; an existing binding works with browser login.
307
160
 
308
- Root `.env` is local-only and is never emitted into Worker vars. Every non-client key declared in `env.ts` is a server value: store it with `void secret put <NAME>`. Void emits required server names through `secrets.required`, rejects plaintext Worker vars with those names, and preserves existing remote secrets.
161
+ Run `void db generate` and commit migrations before deploying schema changes. This includes Better Auth tables. Custom D1 migration directories, tables, and patterns are supported; deploy stops if the files to apply differ from the validated history.
309
162
 
310
- For apps using auth, Void creates a random 32-byte `BETTER_AUTH_SECRET` only when the remote secret is missing. It reuses that value on later deploys. Both auth secrets and schema-marked secrets can be set up on a new Worker's draft before its first upload.
311
- If an earlier deployment leaves a newer inactive upload, Void pins later uploads to the active version's secret bindings. You do not need to recover the plaintext values from Cloudflare.
163
+ Set production server values with `void secret put <NAME>`. Void preserves existing remote secrets and creates `BETTER_AUTH_SECRET` when an auth app needs one. Local `.env` values are never deployed.
312
164
 
313
165
  ### Readiness and rollback
314
166
 
315
- Void usually checks an uploaded version before sending it traffic. When Cloudflare provides a version preview URL, Void probes that URL. Otherwise, it stages the version at 0% traffic and checks it through `workers.dev` using Cloudflare's version-override header.
167
+ Void normally verifies an uploaded version before sending it traffic. If readiness or trigger updates fail, it restores the previous deployment. Finish or cancel any gradual rollout in Cloudflare before deploying through Void.
316
168
 
317
- For an existing Durable Object Worker that Cloudflare cannot stage, review the Worker and database changes, then run `void deploy --platform cloudflare --atomic`. To use that mode on every deploy, set `deploy: { cloudflare: { mode: 'atomic' } }` in `void.config.ts`. Void will then publish directly, without first attempting staging, and check `/__void/ready` afterward. Production traffic can reach the new version before that check finishes, and a Durable Object class migration cannot be rolled back across its migration boundary. The default `staged` mode continues to check readiness before traffic. A failed staged upload is recorded locally in `.void/cloudflare-candidate.json`; keep that file for a safe retry from the same checkout.
169
+ For an existing Durable Object Worker that Cloudflare cannot stage, review the changes and use:
318
170
 
319
- If readiness or trigger synchronization fails, Void restores the previous deployment. If you already have a gradual rollout splitting traffic across versions, finish or cancel it in Cloudflare before deploying through Void.
171
+ ```sh
172
+ void deploy --platform cloudflare --atomic
173
+ ```
320
174
 
321
- A new Worker may need one ordinary deployment before version uploads work. This can happen when creating the Worker, establishing its first Durable Object migration history, or enabling preview URLs. Void checks that the Worker did not exist before first-deploy secret setup, deploys it once, then checks the active Worker through `/__void/ready`. It keeps that earlier check even if attaching a secret creates a placeholder version. This bootstrap is never used for an existing application.
175
+ To use this on every deploy, set `deploy: { cloudflare: { mode: 'atomic' } }`. Atomic deployment sends traffic to the new version before its readiness check. Durable Object class migrations cannot be rolled back across their migration boundary.
322
176
 
323
- To inspect versions or roll back:
177
+ A new Worker may need an active first deployment before readiness can be checked. Keep `.void/cloudflare-candidate.json` if a staged upload fails so you can retry from the same checkout.
178
+
179
+ Inspect or restore versions with:
324
180
 
325
181
  ```sh
326
182
  void project status
327
183
  void project rollback [version]
328
184
  ```
329
185
 
330
- Versions with a complete trigger snapshot can restore their schedules, queues, workflows, routes, and domains along with the code. For an older version deployed outside Void, or a handoff that preserved existing triggers, rollback keeps the current routes and schedules. You can return to the original Worker Version without redeploying its source.
331
-
332
- Rollback does not reverse database migrations. Void asks for confirmation when the schema may be newer than the code or migration metadata is missing.
186
+ Void restores recorded triggers along with the code. Versions without a complete trigger snapshot keep current routes and schedules. Rollback does not reverse database migrations.
333
187
 
334
188
  #### Scope and limitations
335
189
 
336
- Void supports Worker apps, static sites, SPAs, known SSGs, `--dir` deploys, and fully prerenderable Pages apps with `output: "static"`. It also deploys Cloudflare output from TanStack Start, React Router, SvelteKit, Nuxt, Analog, and Astro. The vinext adapters remain experimental and are outside the beta support commitment.
337
-
338
- Static sites use a small Worker in front of Workers Assets to handle redirects, rewrites, fallbacks, and headers. Hybrid and SSR apps keep their application Worker. A page that opts out of prerendering, a dynamic page without `getPrerenderPaths()`, or another runtime feature keeps the app on a Worker even with `output: "static"`.
339
-
340
- Native Void applications apply `void.config.ts` routing rules in their Worker.
341
- Framework-owned Workers do not yet support those Void redirects, rewrites,
342
- fallbacks, or headers on the direct target. The CLI rejects such configuration
343
- before provisioning or building; configure the rules in the framework or its
344
- Worker instead. This restriction does not change an explicit asset policy for
345
- `routing.notFound`.
346
-
347
- Worker apps support D1, KV, R2, Queues, typed state, PostgreSQL and MySQL through Hyperdrive, auth, WebSockets, AI, cron jobs, and ISR. The main limits are:
190
+ Void deploys native apps, static sites, SPAs, supported SSGs, and Cloudflare builds from TanStack Start, React Router, SvelteKit, Nuxt, Analog, and Astro. The vinext adapters are experimental.
348
191
 
349
- - **Sandbox needs Workers Paid and Docker.** Importing `void/sandbox` or enabling `sandbox` in `void.config.ts` adds a Container application. Void checks access before provisioning or building. [Enable Workers Paid](https://dash.cloudflare.com/?to=/:account/workers/plans) and sign in again if needed. API tokens need Account / Containers: Edit and Account / Cloudchamber: Edit. See [Containers pricing](https://developers.cloudflare.com/containers/pricing/). Apps without Sandbox skip this check and remain compatible with Workers Free within its quotas.
350
- - **Worker apps need a fresh build.** `--skip-build` works for existing static, SPA, and SSG output. Worker validation needs the current build's vars and auth schema.
351
- - **Named Cloudflare environments aren't supported.** Direct commands use the top-level `cloudflare` settings and reject `CLOUDFLARE_ENV`, `CLOUDFLARE_VITE_WRANGLER_CONFIG_PATH`, and project-local name overrides. Use a separate Void project config per deployment target.
352
- - **Node.js, Bun, and Deno use a different deployment path.** See their [integration guide](./nodejs-bun-deno.md).
353
- - **Email is set up on the first deploy, on a zone you own.** Put `email.from` in `void.config.ts`; the deploy reads your account, prints a checklist of what it would change, and asks once. See [Your own Cloudflare account](../guide/email.md#your-own-cloudflare-account). Outside Void's reach: `email/_default.ts` and dynamic local parts (`email/[user].ts`) need a catch-all, which exists only on a zone apex, so on a mail subdomain they get no rule; a mail domain that already has non-Cloudflare MX records is refused, never routed over; `sendEmail()` to arbitrary recipients needs Workers Paid (Email Sending onboarding), otherwise verified destinations only; Void owns the top-level `addresses` array in the generated Cloudflare config, so hand-written `cloudflare.addresses` entries skip the email step; and `void email usage`, `logs`, `allow`, and `destinations` are platform-only.
192
+ - Native apps support Void routing rules. Framework apps on the direct target must configure redirects, rewrites, fallbacks, and headers through their framework or Worker.
193
+ - `--skip-build` works for existing static output. Worker apps require a fresh build.
194
+ - Named Cloudflare environments aren't supported. Use a separate Void project config for each target.
195
+ - Sandbox requires Workers Paid and Docker. See [Sandbox requirements](../guide/sandboxes.md#deployment) for plan and token permissions.
196
+ - Email to arbitrary recipients requires Workers Paid; verified recipients and inbound mail work on Workers Free. See [Email](../guide/email/domains.md#your-own-cloudflare-account).
197
+ - Node.js, Bun, and Deno use their [own deployment path](./nodejs-bun-deno.md).
354
198
 
355
- WebSocket routes and typed state use SQLite-backed Durable Objects. Commit `void.lock.json` when Void adds bindings or `new_sqlite_classes` migration history; don't delete or reorder deployed migration steps. Older hosted classes using `new_classes` keep their existing storage.
356
-
357
- ISR uses the shared cache protocol. Entries are scoped to a deployment and hostname, and `routing.revalidateQueryAllowlist` adds bounded query variants. `revalidate()` purges matching entries and variants from KV and the local edge cache. See [ISR](#isr-self-host) below for cache behavior across regions.
199
+ Commit Durable Object migration history in `void.lock.json`. Do not delete or reorder deployed steps.
358
200
 
359
201
  ### Managing the deployed app
360
202
 
361
- With Cloudflare selected, `void secret`, `void domain`, `void project status|list|logs|rollback`, and remote `void db` commands use the saved Worker and account. Database commands operate on the selected D1 database. Logs are a live tail; Void doesn't provide hosted log history for direct deploys.
362
-
363
- Custom domain changes update routes immediately without rewriting schedules, queues, or workflows. Removing the final custom domain uses your browser session from `void cloudflare login` or an API token with Workers Scripts: Edit permission.
364
-
365
- `void project delete` doesn't remove direct Cloudflare resources, which may be shared. Review their use and remove them explicitly in Cloudflare.
366
-
367
- ::: details How Void checks the build
203
+ `void secret`, `void domain`, `void project status|list|logs|rollback`, and remote `void db` commands use the selected account and Worker. Logs are a live tail.
368
204
 
369
- Your build runs your config, Vite plugins, and dependencies with filesystem access. Void removes Cloudflare credentials from the build environment, checks the generated account, Worker name, and binding IDs against the pre-build configuration, and verifies that the bundled deployment tool hasn't changed.
205
+ Custom domain commands update routes immediately. If a first deployment times out while DNS or TLS is propagating, check the domain in Cloudflare and retry once `/__void/ready` is reachable.
370
206
 
371
- These checks detect changes to the deployment target or uploader, but they don't sandbox arbitrary build code. Build dependencies still need the same trust as other code you run on your machine.
372
-
373
- :::
207
+ `void project delete` does not remove Cloudflare resources. Review ownership and remove them in Cloudflare.
374
208
 
375
209
  ### Local development
376
210
 
377
- `pnpm dev` continues to work as before -- Miniflare creates local instances of all bindings regardless of the IDs in `void.config.ts`. Your real resource IDs are only used during direct Cloudflare deployment.
211
+ Local D1, KV, and R2 bindings use local data. Your production resource IDs are used during deployment.
378
212
 
379
- ### AI on your Cloudflare account {#ai-self-host}
213
+ Workers AI runs remotely after you connect to Cloudflare, so development calls consume your account's allowance.
380
214
 
381
- `void/ai` works on your own Cloudflare account, along two paths:
215
+ ### AI on your Cloudflare account {#ai-self-host}
382
216
 
383
- - **Workers AI** (`ai.run`, `ai.stream`, `ai.image`) works out of the box. When your app imports `void/ai`, `vite build` infers that you need AI and adds a Workers AI binding (`env.AI`) to the generated build config automatically. Set `inference.bindings.ai` to a string to use a custom name.
384
- - **Provider models** (`ai.provider("openai").fetch(...)`) route through _your own_ Cloudflare AI Gateway. Set its id in `void.config.ts` and add the provider's API key as a Worker secret.
217
+ `ai.run()`, `ai.stream()`, and `ai.image()` use your Workers AI binding. For provider models, configure your AI Gateway and store the provider key as a Worker secret:
385
218
 
386
219
  ```ts
387
220
  import { defineConfig } from 'void/config';
388
221
 
389
222
  export default defineConfig({
390
- ai: {
391
- gateway: 'my-gateway', // an AI Gateway in your Cloudflare account
392
- },
223
+ ai: { gateway: 'my-gateway' },
393
224
  });
394
225
  ```
395
226
 
396
- ```bash
397
- # provider API key, stored as a Worker secret (never committed)
227
+ ```sh
398
228
  void secret put OPENAI_API_KEY
399
229
  ```
400
230
 
401
- ```ts
402
- // routes/chat.ts
403
- import { defineHandler } from 'void';
404
- import { ai } from 'void/ai';
405
-
406
- export const POST = defineHandler(async (c) => {
407
- // Workers AI -- uses the inferred or configured AI binding directly
408
- const summary = await ai.run('@cf/meta/llama-3.3-70b-instruct-fp8-fast', {
409
- prompt: 'Summarize the changelog.',
410
- });
411
-
412
- // Provider model -- routes through your "my-gateway" AI Gateway,
413
- // authed with the OPENAI_API_KEY secret above
414
- const res = await ai.provider('openai').fetch('chat/completions', {
415
- method: 'POST',
416
- body: JSON.stringify({ model: 'gpt-4o-mini', messages: [] }),
417
- });
418
-
419
- return c.json({ summary, provider: await res.json() });
420
- });
421
- ```
422
-
423
- Set `ai.gateway` before calling `ai.provider().fetch()`; otherwise it returns `501`. Requests use the provider secrets in your Worker's environment and go through your AI Gateway to the provider. They don't pass through the Void platform's shared proxy.
424
-
425
- Notes:
426
-
427
- - **Account-owned usage.** Direct deployments use your own [Cloudflare AI Gateway analytics](https://developers.cloudflare.com/ai-gateway/). A team platform meters requests through its shared proxy; those usage records do not automatically bill application developers.
428
- - **Custom AI binding names are supported.** Set `inference.bindings.ai` to `MY_AI`, or declare `cloudflare.ai.binding` in `void.config.ts`; the generated worker records the resolved name for `void/ai` automatically.
429
- - **Workers AI runs remotely during development.** After `void connect --platform cloudflare`, an app importing `void/ai` uses your account's remote AI binding with your Cloudflare login or API token. Development and preview calls consume that account's allowance. Apps without AI imports add no AI binding or authentication probe.
231
+ `ai.provider('openai').fetch(...)` then uses that gateway and key. See the [AI guide](../guide/ai.md) for examples. Use [AI Gateway analytics](https://developers.cloudflare.com/ai-gateway/) to inspect account usage.
430
232
 
431
233
  ### ISR on your Cloudflare account {#isr-self-host}
432
234
 
433
- [Revalidation (ISR)](../guide/edge/revalidation.md) works self-hosted. `void deploy --platform cloudflare` automatically creates or reuses the cache KV namespace and records its `ISR_CACHE` binding in `void.lock.json`, just like the other inferred resources.
434
-
435
- Configure revalidation exactly as on the platform -- globally or per-path in `void.config.ts`:
436
-
437
- ```ts
438
- import { defineConfig } from 'void/config';
439
-
440
- export default defineConfig({
441
- routing: {
442
- revalidate: { '/blog/*': 3600, '*': 60 },
443
- },
444
- });
445
- ```
446
-
447
- ...or per page with an exported `revalidate` literal in a `.server.ts` companion (Pages mode):
448
-
449
- ```ts
450
- // pages/blog/[slug].server.ts
451
- export const revalidate = 3600; // seconds
452
- ```
453
-
454
- On-demand purges work through `revalidate()`:
455
-
456
- ```ts
457
- import { revalidate } from 'void/isr';
458
-
459
- await revalidate({ paths: ['/blog/hello'] });
460
- // or purge every ISR page:
461
- await revalidate({ all: true });
462
- ```
463
-
464
- Self-hosted, `revalidate()` purges this worker's own KV entries (global, authoritative) plus the current colo's edge cache.
235
+ Configure [revalidation](../guide/edge/revalidation.md) globally, per path, or per page. Void provisions the cache automatically.
465
236
 
466
- There are a few differences from platform-managed ISR:
237
+ `revalidate()` clears shared storage and the current data center's edge cache. Other data centers can serve cached responses until their cache lifetime expires. Pages data requests in a data center that hasn't rendered the HTML are generated live.
467
238
 
468
- - **Edge caches expire independently.** `revalidate()` clears KV and the current data center's edge cache. Other data centers may serve their cached response until its `s-maxage` expires, then render again.
469
- - **Pages JSON is cached after an HTML render.** A data center that hasn't rendered the HTML yet generates the JSON response live rather than reading it from KV.
470
- - **Each deploy starts with a fresh cache.** Cache keys include a new deployment ID, so cached HTML can't reference assets from an older build. The first request renders the page again.
239
+ Each deploy starts with a fresh cache.