void 0.25.0 → 0.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (373) hide show
  1. package/AGENTS_TEMPLATE.md +1 -3
  2. package/README.md +19 -9
  3. package/dist/{account-cmd-DcR1ywt8.mjs → account-cmd-CfUXh_nU.mjs} +8 -7
  4. package/dist/{application-analysis-BVsVqc11.mjs → application-analysis-DKy02UY0.mjs} +21 -32
  5. package/dist/{application-routing-B7QEjqSR.mjs → application-routing-DQyUh_5S.mjs} +1 -1
  6. package/dist/{auth-7wuZ5ob8.mjs → auth-B5_tZLxT.mjs} +9 -9
  7. package/dist/{auth-CdgZ9uag.mjs → auth-BgX2qYSY.mjs} +1 -1
  8. package/dist/{auth-link-Z6E5DYvM.mjs → auth-link-D5RmKnzh.mjs} +5 -5
  9. package/dist/{auth-migrations-9vif1uj8.mjs → auth-migrations-BwHgeEsD.mjs} +8 -18
  10. package/dist/{auth-router-BdM4avI8.mjs → auth-router-Bap-pvbn.mjs} +6 -19
  11. package/dist/{better-auth-shared-BVkjZ3vM.d.mts → better-auth-shared-CPvWD3nG.d.mts} +0 -4
  12. package/dist/{better-auth-shared-hy6RPh9W.mjs → better-auth-shared-DPpj8zxc.mjs} +94 -16
  13. package/dist/{build-cmd-CEE7D-hM.mjs → build-cmd-B58c8iS8.mjs} +8 -7
  14. package/dist/{cache-TcZnBLtH.mjs → cache-Bo52trKF.mjs} +8 -8
  15. package/dist/{cancel-deploy-CYoZmcmR.mjs → cancel-deploy-BZNz3Q26.mjs} +6 -5
  16. package/dist/{cf-access-AHKBzmuu.mjs → cf-access-ngGqZpHZ.mjs} +11 -12
  17. package/dist/{cf-build-output-U8o1Gl6X.mjs → cf-build-output-DjvB_872.mjs} +62 -85
  18. package/dist/cf-build-output-H9a97vME.mjs +2 -0
  19. package/dist/cli/cli.mjs +48 -83
  20. package/dist/cli/cloudflare-operation-process.mjs +71 -77
  21. package/dist/cli/env-schema-probe.d.mts +1 -1
  22. package/dist/cli/env-schema-probe.mjs +5 -6
  23. package/dist/client-By4JjMw_.mjs +2 -0
  24. package/dist/{client-RsPo8wsP.mjs → client-C2Prx_Bi.mjs} +30 -8
  25. package/dist/{cloudflare-auth-Qdc7tw8F.mjs → cloudflare-auth-CuxfF6OA.mjs} +9 -17
  26. package/dist/{cloudflare-cmd-bhwCxAN6.mjs → cloudflare-cmd-lHqNlJGW.mjs} +6 -5
  27. package/dist/{cloudflare-config-Bktvwtpf.mjs → cloudflare-config-BmhBv3HV.mjs} +1 -1
  28. package/dist/{cloudflare-connect-CuhRgk3k.mjs → cloudflare-connect-2G3rL2rP.mjs} +4 -4
  29. package/dist/{cloudflare-operations-BhbK6yTq.mjs → cloudflare-operations-BlYLlowZ.mjs} +1 -1
  30. package/dist/{cloudflare-operations-CjaV4lZi.mjs → cloudflare-operations-CwI1bChl.mjs} +41 -70
  31. package/dist/{cloudflare-process-B-wekeR6.mjs → cloudflare-process-2-TfIEzB.mjs} +20 -22
  32. package/dist/{config-CyQ-wVd7.mjs → config-5r-YpMC-.mjs} +1 -1
  33. package/dist/{config-C_XRIPx2.mjs → config-B_ggY-IO.mjs} +3 -3
  34. package/dist/config-BtSFvXZx.mjs +2 -0
  35. package/dist/config-CLYAosnQ.mjs +78 -0
  36. package/dist/{config-BMHb8RCj.d.mts → config-CSa15FvD.d.mts} +1 -1
  37. package/dist/{config-Br_JZD6u.mjs → config-DiowPT80.mjs} +24 -30
  38. package/dist/config-entry.d.mts +1 -1
  39. package/dist/{config-write-B1f88wJA.mjs → config-write-AHGesphx.mjs} +8 -7
  40. package/dist/{connect-DCvUmPXY.mjs → connect-X0MUA1gk.mjs} +12 -11
  41. package/dist/{create-project-eAPgYq35.mjs → create-project-DxiyW6iX.mjs} +7 -7
  42. package/dist/{create-project-C734I-Mu.mjs → create-project-iwkbqiBU.mjs} +1 -1
  43. package/dist/{db-C07GEdKF.mjs → db-XnVMHzoV.mjs} +101 -133
  44. package/dist/{delete-pKMVsrpJ.mjs → delete-esYlMiSJ.mjs} +8 -8
  45. package/dist/{deploy-DfsAe-T0.mjs → deploy-BsEvnlJl.mjs} +2377 -2512
  46. package/dist/deploy-C1d-YTfp.mjs +2 -0
  47. package/dist/{dev-inbox-D64ZKUod.mjs → dev-inbox-CK-UuPob.mjs} +7 -16
  48. package/dist/{discover-BcPPiF1o.mjs → discover-CkCYv9T_.mjs} +1 -2
  49. package/dist/discover-EDUf6JJw.mjs +2 -0
  50. package/dist/{prompts-ZAuJlyCj.mjs → dist-Bx1z6Eva.mjs} +1 -1
  51. package/dist/{dist-AoCzRTJE.mjs → dist-Cegxq8gs.mjs} +8 -8
  52. package/dist/{prompts-6OSVA7DJ.mjs → dist-D3m88sjf.mjs} +10 -53
  53. package/dist/{domain-D3uA9ize.mjs → domain-CbbBWhWE.mjs} +29 -30
  54. package/dist/{dotenv-VQxupEUv.mjs → dotenv-BfU6eqFt.mjs} +20 -32
  55. package/dist/{drizzle-Beb2Am5S.mjs → drizzle-CF84FOVM.mjs} +16 -13
  56. package/dist/{email-z3o2cwjH.mjs → email-CpuT42g7.mjs} +24 -19
  57. package/dist/{email--tEtMoj_.mjs → email-d_Aux1vM.mjs} +1 -1
  58. package/dist/{entry-CF5wvH4w.mjs → entry-CMAkqBmU.mjs} +6 -6
  59. package/dist/{env-_v349JBd.mjs → env-BxocywBB.mjs} +25 -6
  60. package/dist/{env-D5liuQYR.mjs → env-DMA3cXZt.mjs} +1 -1
  61. package/dist/{env-public-BX_r8HR6.d.mts → env-public-DR7ZM6d-.d.mts} +1 -1
  62. package/dist/{env-raw-Cx8ElDdj.mjs → env-raw-jZpP5n47.mjs} +1 -2
  63. package/dist/{env-types-D5kds_OZ.mjs → env-types-CwXgeMgL.mjs} +1 -1
  64. package/dist/{env-validation-cZBvGWaP.mjs → env-validation-CrVNjsPm.mjs} +1 -1
  65. package/dist/{env-validation-vBSQ8gw5.mjs → env-validation-Dxdx_5wo.mjs} +17 -21
  66. package/dist/{env-3Q8-_2IH.mjs → env-w9jOyGZn.mjs} +10 -9
  67. package/dist/{fetch-_SeGZao9.d.mts → fetch-BPCYVi3H.d.mts} +20 -1
  68. package/dist/{fetch-BIZJh7vR.mjs → fetch-sAcwjzLO.mjs} +1 -1
  69. package/dist/{fetch-stream-IvCYKyQL.mjs → fetch-stream-PGgz4uGz.mjs} +26 -24
  70. package/dist/{fetch-stream-Bjf0hoZb.d.mts → fetch-stream-obvZ7fvO.d.mts} +1 -16
  71. package/dist/{gen-NjwoG9FK.mjs → gen-DtkQhvQg.mjs} +40 -57
  72. package/dist/gen-dA9hW7AV.mjs +2 -0
  73. package/dist/{github-cmd-DquUq-b1.mjs → github-cmd-D_FsOFxq.mjs} +65 -164
  74. package/dist/{handler-DghKr6dU.mjs → handler-BHAn2fZJ.mjs} +1 -0
  75. package/dist/{handler-CZ4nAylQ.d.mts → handler-CNAwjRcW.d.mts} +5 -12
  76. package/dist/{head-Do8P4puT.d.mts → head-C8K_iGe6.d.mts} +1 -1
  77. package/dist/{head-D_QRR5Yd.mjs → head-D2VF4ApL.mjs} +29 -51
  78. package/dist/{headers-2rKda9Y6.mjs → headers-DlfR78Nu.mjs} +3 -3
  79. package/dist/{help-DRZXC4fj.mjs → help-CVFBTmBx.mjs} +23 -38
  80. package/dist/help-DzKUrQvk.mjs +2 -0
  81. package/dist/{inbound-fRkqcXaC.mjs → inbound-PfQDLXc5.mjs} +23 -24
  82. package/dist/index.d.mts +19 -2
  83. package/dist/index.mjs +632 -532
  84. package/dist/{info-BmA3_IFZ.mjs → info-PYWwkWyt.mjs} +4 -4
  85. package/dist/{init-DT7yuuyl.mjs → init-Bw-fRKGp.mjs} +161 -197
  86. package/dist/{limits-Bq5LG8Id.d.mts → limits-CFsDKQvU.d.mts} +1 -10
  87. package/dist/{limits-Cjuk2VPm.mjs → limits-D0vD1M9A.mjs} +1 -22
  88. package/dist/{link-CXOlqh2F.mjs → link-DlPG8iX3.mjs} +10 -10
  89. package/dist/{list-eEWVJEPn.mjs → list-2GN7_ueV.mjs} +11 -9
  90. package/dist/{live-CB1y5IuC.mjs → live-CgQuTpqD.mjs} +15 -16
  91. package/dist/{live-Chw1eIMv.d.mts → live-Du8jXwOW.d.mts} +4 -2
  92. package/dist/{local-d1-Df4XpwhW.mjs → local-d1-ga5O_idV.mjs} +2 -2
  93. package/dist/{login-pWQHYVu2.mjs → login-D-Q4xw3Q.mjs} +7 -7
  94. package/dist/login-Dc-wuTDq.mjs +2 -0
  95. package/dist/{logs-DnxVX7TR.mjs → logs-rd8J-OFh.mjs} +28 -11
  96. package/dist/migrate-BHp07yJE.mjs +2 -0
  97. package/dist/{migrate-CV1on-yJ.mjs → migrate-NLsjBbuM.mjs} +34 -20
  98. package/dist/{mime-D5Nmdzf7.mjs → mime-DxCznlJq.mjs} +45 -95
  99. package/dist/{node-BkyRWRx8.mjs → node-DJicPvPj.mjs} +5 -5
  100. package/dist/{operator-args-CAVcmwUt.mjs → operator-args-BNsW5gfp.mjs} +10 -1
  101. package/dist/{operator-auth-CkmTE3rt.mjs → operator-auth-AzVLbrr3.mjs} +4 -8
  102. package/dist/{operator-client-LmwjxT6q.mjs → operator-client-BQtzyG-g.mjs} +3 -3
  103. package/dist/{operator-cmd-d-IEKgNk.mjs → operator-cmd-B6f6Cg9Z.mjs} +45 -14
  104. package/dist/operator-cmd-Cwn50lOV.mjs +2 -0
  105. package/dist/{output-BBsUwdyT.mjs → output-C50ag2r3.mjs} +44 -2
  106. package/dist/pages/client.d.mts +19 -51
  107. package/dist/pages/client.mjs +204 -225
  108. package/dist/pages/head-client.d.mts +1 -1
  109. package/dist/pages/head.d.mts +1 -1
  110. package/dist/pages/head.mjs +1 -1
  111. package/dist/pages/index.d.mts +2 -2
  112. package/dist/pages/index.mjs +6 -12
  113. package/dist/pages/islands-plugin.d.mts +1 -1
  114. package/dist/pages/islands-plugin.mjs +21 -25
  115. package/dist/pages/protocol.d.mts +2 -2
  116. package/dist/pages/protocol.mjs +2 -2
  117. package/dist/{plan-NPpwZ_kc.mjs → plan-BhLO2p4w.mjs} +4 -3
  118. package/dist/plan-CH2G51je.mjs +2 -0
  119. package/dist/{platform-args-Bzf8JPc2.mjs → platform-args-BoZkWQnJ.mjs} +4 -2
  120. package/dist/platform-args-DYgITk0D.mjs +2 -0
  121. package/dist/{platform-auth-config-0V0lA3pF.mjs → platform-auth-config-n603OANg.mjs} +45 -40
  122. package/dist/{platform-auth-protection-DFeJ-JkK.mjs → platform-auth-protection-zxDr0hWW.mjs} +9 -8
  123. package/dist/{platform-auth-recovery-ICy4muq6.mjs → platform-auth-recovery-tw9lgtgE.mjs} +8 -7
  124. package/dist/{platform-cmd-D7vSWMvV.mjs → platform-cmd-B8-bCb7t.mjs} +48 -23
  125. package/dist/{platform-cmd-BPySFOew.mjs → platform-cmd-C00YM14r.mjs} +1 -1
  126. package/dist/{platform-discovery-DDDUvnqp.mjs → platform-discovery-BC0HCKYt.mjs} +18 -4
  127. package/dist/{platform-domain-CUdY064e.mjs → platform-domain-p0kobLV8.mjs} +6 -5
  128. package/dist/{platform-lifecycle-Dm09JNkn.mjs → platform-lifecycle-D_QEY32D.mjs} +906 -389
  129. package/dist/{platform-lifecycle-BaUdTRPU.mjs → platform-lifecycle-vFqtNPPa.mjs} +1 -1
  130. package/dist/{platform-management-BSEc_ZeG.mjs → platform-management-BdYblvvE.mjs} +175 -32
  131. package/dist/{platform-management-Bp6aoH2C.mjs → platform-management-oi-SvI3f.mjs} +1 -1
  132. package/dist/{platform-plans-config-B0pOVQyW.mjs → platform-plans-config-CjiFv5J4.mjs} +5 -4
  133. package/dist/{platform-recovery-WOZ95Uze.mjs → platform-recovery-PvNS9Nl2.mjs} +3 -3
  134. package/dist/{platform-registry-KfNEF84O.mjs → platform-registry-P387v42A.mjs} +17 -10
  135. package/dist/platform-setup-DVe42Ws7.mjs +299 -0
  136. package/dist/{plugin-inference-CXWnn79A.mjs → plugin-inference-BlQbOL0P.mjs} +43 -42
  137. package/dist/prepare-DRUV-g3X.mjs +2 -0
  138. package/dist/{prepare-DnaKBi--.mjs → prepare-Do96654b.mjs} +8 -8
  139. package/dist/{prepare-39EvOhI6.mjs → prepare-Dq9hTIbo.mjs} +4 -3
  140. package/dist/{prerender-render-D9qbLs2f.mjs → prerender-render-Ct20jftc.mjs} +17 -3
  141. package/dist/prerender-render.mjs +1 -1
  142. package/dist/{project-IOjBU8R6.mjs → project-BiaPQ7ur.mjs} +1 -1
  143. package/dist/{project-BkbfDs0V.mjs → project-DQstYTxO.mjs} +1 -1
  144. package/dist/{project-cmd-Bs5A392E.mjs → project-cmd-ti8_yTcA.mjs} +19 -19
  145. package/dist/{project-team-ohAWWaew.mjs → project-team-CDPE1Tsg.mjs} +15 -18
  146. package/dist/{project-token-CP01Jna6.mjs → project-token-CkZ1cN0Z.mjs} +14 -17
  147. package/dist/{project-tsconfig-B8GeM0RM.mjs → project-tsconfig-CVJhNMxs.mjs} +2 -1
  148. package/dist/{project-zero-trust-BzkVgx35.mjs → project-zero-trust-DdjLR61k.mjs} +12 -9
  149. package/dist/{protocol-CjF_iI9X.d.mts → protocol-BPZaNqBo.d.mts} +4 -2
  150. package/dist/{protocol-U7bfjHmA.mjs → protocol-CfaUwNs8.mjs} +46 -49
  151. package/dist/{providers-CJlNS3kT.mjs → providers-BXuhmuub.mjs} +1 -12
  152. package/dist/{provision-Btj1jppf.mjs → provision-BfaLiWbj.mjs} +51 -42
  153. package/dist/{provision-BEv0k-qz.mjs → provision-Wu9ZBk9L.mjs} +2 -3
  154. package/dist/{requests-B3PBuGlX.mjs → requests-DFAqyYy8.mjs} +5 -5
  155. package/dist/resolve-project-BIeYs9Cw.mjs +2 -0
  156. package/dist/{resolve-project-BG5ORGID.mjs → resolve-project-CP8rZs_p.mjs} +6 -1
  157. package/dist/{rollback-DDub9GsO.mjs → rollback-BKYXBduI.mjs} +30 -37
  158. package/dist/{route-types-Clm4WD0B.mjs → route-types-CbtsIeFn.mjs} +1 -1
  159. package/dist/route-url-BgPja1GS.d.mts +5 -0
  160. package/dist/route-url-BjGv_KKY.mjs +28 -0
  161. package/dist/runner-9wjsSZx-.mjs +2 -0
  162. package/dist/{runner-Ol0TNjk6.mjs → runner-DMBKSrRk.mjs} +2 -2
  163. package/dist/{runner-mysql-DmWchSWJ.mjs → runner-mysql-CEzKtmFb.mjs} +8 -8
  164. package/dist/{runner-mysql-CEG5JozK.mjs → runner-mysql-CkFqZ6CE.mjs} +1 -1
  165. package/dist/{runner-pg-Bh4AgZdj.mjs → runner-pg-BGYRFdRp.mjs} +1 -1
  166. package/dist/runtime/ai.d.mts +11 -12
  167. package/dist/runtime/ai.mjs +47 -113
  168. package/dist/runtime/better-auth-mysql.d.mts +1 -1
  169. package/dist/runtime/better-auth-mysql.mjs +4 -9
  170. package/dist/runtime/better-auth-pg.d.mts +1 -1
  171. package/dist/runtime/better-auth-pg.mjs +5 -10
  172. package/dist/runtime/better-auth.d.mts +1 -1
  173. package/dist/runtime/better-auth.mjs +3 -3
  174. package/dist/runtime/client-react.d.mts +2 -3
  175. package/dist/runtime/client-react.mjs +2 -2
  176. package/dist/runtime/client-solid.d.mts +2 -3
  177. package/dist/runtime/client-solid.mjs +2 -2
  178. package/dist/runtime/client-svelte.d.mts +2 -3
  179. package/dist/runtime/client-svelte.mjs +2 -2
  180. package/dist/runtime/client-vue.d.mts +2 -3
  181. package/dist/runtime/client-vue.mjs +2 -2
  182. package/dist/runtime/client.d.mts +2 -3
  183. package/dist/runtime/client.mjs +2 -2
  184. package/dist/runtime/db.mjs +1 -1
  185. package/dist/runtime/durable.mjs +3 -10
  186. package/dist/runtime/email/testing.mjs +5 -5
  187. package/dist/runtime/email.d.mts +2 -1
  188. package/dist/runtime/email.mjs +11 -27
  189. package/dist/runtime/env-public.d.mts +1 -1
  190. package/dist/runtime/env-public.mjs +15 -20
  191. package/dist/runtime/env.mjs +1 -1
  192. package/dist/runtime/fetch-stream.d.mts +2 -2
  193. package/dist/runtime/fetch-stream.mjs +1 -1
  194. package/dist/runtime/fetch.d.mts +2 -3
  195. package/dist/runtime/fetch.mjs +1 -1
  196. package/dist/runtime/handler.d.mts +1 -1
  197. package/dist/runtime/handler.mjs +2 -2
  198. package/dist/runtime/isr.mjs +1 -1
  199. package/dist/runtime/kv.mjs +10 -11
  200. package/dist/runtime/limits.d.mts +2 -2
  201. package/dist/runtime/limits.mjs +2 -2
  202. package/dist/runtime/live-client.d.mts +1 -1
  203. package/dist/runtime/live-client.mjs +12 -22
  204. package/dist/runtime/live-server.mjs +27 -35
  205. package/dist/runtime/live.d.mts +2 -2
  206. package/dist/runtime/live.mjs +2 -2
  207. package/dist/runtime/migration-handler.mjs +35 -86
  208. package/dist/runtime/queues.mjs +1 -1
  209. package/dist/runtime/remote/binding-handler.d.mts +2 -2
  210. package/dist/runtime/remote/binding-handler.mjs +8 -9
  211. package/dist/runtime/remote/index.mjs +27 -77
  212. package/dist/runtime/route-url.d.mts +2 -4
  213. package/dist/runtime/route-url.mjs +1 -1
  214. package/dist/runtime/routing.d.mts +1 -1
  215. package/dist/runtime/routing.mjs +15 -26
  216. package/dist/runtime/sandbox.d.mts +1 -1
  217. package/dist/runtime/sandbox.mjs +14 -24
  218. package/dist/runtime/sse.mjs +1 -1
  219. package/dist/runtime/storage.mjs +1 -1
  220. package/dist/runtime/validator.d.mts +1 -1
  221. package/dist/runtime/ws-server.d.mts +2 -2
  222. package/dist/runtime/ws-server.mjs +30 -61
  223. package/dist/runtime/ws.d.mts +1 -1
  224. package/dist/runtime/ws.mjs +1 -1
  225. package/dist/{sandbox-XZAqzFlG.d.mts → sandbox-C5LFoJMf.d.mts} +7 -7
  226. package/dist/{scan-C7okrLyM.mjs → scan-C3uDzz6J.mjs} +21 -18
  227. package/dist/{secret-3c72lY1U.mjs → secret-BAt2KMmO.mjs} +15 -15
  228. package/dist/{skills-BPQUGClu.mjs → skills-BgVMvQdy.mjs} +2 -2
  229. package/dist/{sse-CQNaDFFV.mjs → sse-CMvCgjNk.mjs} +1 -1
  230. package/dist/{subcommand-prompt-Dhom39bd.mjs → subcommand-prompt-BSlnD1fI.mjs} +2 -2
  231. package/dist/sveltekit.d.mts +0 -2
  232. package/dist/terminal-log-text-D9ACPOuG.mjs +32 -0
  233. package/dist/validate-BJr9LfXy.mjs +2 -0
  234. package/dist/{validate-ctOrgiS3.mjs → validate-C9qJArv7.mjs} +9 -28
  235. package/dist/{defer-YsYDUoii.mjs → validation-error-Dg9Recn1.mjs} +11 -11
  236. package/dist/{wrangler-DhUerFkW.mjs → wrangler-D9KTuqnv.mjs} +89 -194
  237. package/dist/{ws-CL1w7GXU.d.mts → ws-CChrrAU-.d.mts} +1 -1
  238. package/dist/{yarn-pnp-BV4AzNul.mjs → yarn-pnp-CUX_iTPA.mjs} +19 -27
  239. package/{skills/void/docs → docs}/guide/ai.md +69 -64
  240. package/{skills/void/docs → docs}/guide/app-types.md +1 -1
  241. package/{skills/void/docs → docs}/guide/auth.md +37 -1
  242. package/{skills/void/docs → docs}/guide/database/postgresql.md +2 -2
  243. package/{skills/void/docs → docs}/guide/database.md +23 -8
  244. package/{skills/void/docs → docs}/guide/edge/revalidation.md +1 -1
  245. package/{skills/void/docs → docs}/guide/edge/rewrites.md +2 -4
  246. package/{skills/void/docs → docs}/guide/email/receiving.md +2 -2
  247. package/{skills/void/docs → docs}/guide/email/sending.md +0 -2
  248. package/{skills/void/docs → docs}/guide/env-vars.md +4 -0
  249. package/{skills/void/docs → docs}/guide/jobs.md +2 -2
  250. package/{skills/void/docs → docs}/guide/live.md +12 -5
  251. package/{skills/void/docs → docs}/guide/pages-routing/actions-and-forms.md +21 -9
  252. package/{skills/void/docs → docs}/guide/pages-routing/loaders.md +7 -3
  253. package/{skills/void/docs → docs}/guide/pages-routing/markdown.md +15 -23
  254. package/{skills/void/docs → docs}/guide/pages-routing/overview.md +6 -4
  255. package/{skills/void/docs → docs}/guide/platform/administration/access.md +2 -10
  256. package/{skills/void/docs → docs}/guide/platform/administration/operations.md +3 -12
  257. package/{skills/void/docs → docs}/guide/platform/administration/projects.md +1 -1
  258. package/{skills/void/docs → docs}/guide/platform/development/local.md +4 -2
  259. package/{skills/void/docs → docs}/guide/platform/development/runtime.md +1 -1
  260. package/{skills/void/docs → docs}/guide/platform/development/schema-ci.md +1 -1
  261. package/{skills/void/docs → docs}/guide/platform/installation/credentials.md +2 -2
  262. package/{skills/void/docs → docs}/guide/platform/installation/domains.md +3 -1
  263. package/{skills/void/docs → docs}/guide/platform/installation/first-deployment.md +8 -2
  264. package/{skills/void/docs → docs}/guide/platform/installation/maintenance.md +7 -1
  265. package/{skills/void/docs → docs}/guide/platform/installation/setup.md +1 -1
  266. package/docs/guide/platform/installation/uninstall.md +102 -0
  267. package/{skills/void/docs → docs}/guide/project-collaboration.md +6 -2
  268. package/{skills/void/docs → docs}/guide/queues.md +0 -2
  269. package/{skills/void/docs → docs}/guide/remote-dev.md +5 -5
  270. package/{skills/void/docs → docs}/guide/sandboxes.md +41 -22
  271. package/{skills/void/docs → docs}/guide/sse.md +0 -2
  272. package/{skills/void/docs → docs}/guide/storage.md +7 -2
  273. package/{skills/void/docs → docs}/guide/websockets.md +4 -0
  274. package/docs/integrations/agents.md +37 -0
  275. package/{skills/void/docs → docs}/integrations/frameworks/astro.md +3 -3
  276. package/{skills/void/docs → docs}/integrations/frameworks/nuxt.md +6 -2
  277. package/{skills/void/docs → docs}/integrations/hono.md +15 -7
  278. package/{skills/void/docs → docs}/integrations/nodejs-bun-deno.md +1 -0
  279. package/{skills/void/docs → docs}/reference/api/auth.md +1 -1
  280. package/{skills/void/docs → docs}/reference/api/handlers.md +4 -2
  281. package/{skills/void/docs → docs}/reference/api/pages.md +4 -0
  282. package/{skills/void/docs → docs}/reference/api/resources.md +11 -14
  283. package/{skills/void/docs → docs}/reference/api.md +35 -34
  284. package/{skills/void/docs → docs}/reference/cli/auth.md +3 -6
  285. package/{skills/void/docs → docs}/reference/cli/database.md +3 -4
  286. package/{skills/void/docs → docs}/reference/cli/generate.md +1 -1
  287. package/{skills/void/docs → docs}/reference/cli/github.md +3 -12
  288. package/{skills/void/docs → docs}/reference/cli/platform-config.md +3 -3
  289. package/{skills/void/docs → docs}/reference/cli/platform-email.md +2 -3
  290. package/{skills/void/docs → docs}/reference/cli/platform-installation.md +15 -13
  291. package/{skills/void/docs → docs}/reference/cli/platform-operations.md +3 -0
  292. package/{skills/void/docs → docs}/reference/cli/platform-users.md +2 -2
  293. package/{skills/void/docs → docs}/reference/cli/platform.md +4 -2
  294. package/{skills/void/docs → docs}/reference/cli/project.md +5 -4
  295. package/{skills/void/docs → docs}/reference/cli/secrets.md +3 -3
  296. package/{skills/void/docs → docs}/reference/cli/setup.md +9 -11
  297. package/{skills/void/docs → docs}/reference/cli.md +2 -2
  298. package/{skills/void/docs → docs}/reference/config.md +13 -13
  299. package/{skills/void/docs → docs}/reference/resource-inference.md +2 -16
  300. package/package.json +34 -30
  301. package/schema.json +1 -1
  302. package/skills/migrate-vite-cloudflare-to-void/SKILL.md +4 -1
  303. package/skills/void/SKILL.md +11 -5
  304. package/dist/cf-build-output-ChY6-XWP.mjs +0 -2
  305. package/dist/client-B4Me-9HE.mjs +0 -2
  306. package/dist/config-0acf1oys.mjs +0 -2
  307. package/dist/config-D8MOgiQV.mjs +0 -32
  308. package/dist/deploy-BDu1vhSH.mjs +0 -2
  309. package/dist/discover-AMCBXivQ.mjs +0 -2
  310. package/dist/fetch-error-Sp1R4mZv.d.mts +0 -20
  311. package/dist/gen-ById9r2j.mjs +0 -2
  312. package/dist/help-DPGBwomV.mjs +0 -2
  313. package/dist/login-C98-sw79.mjs +0 -2
  314. package/dist/migrate-CEu2X5DB.mjs +0 -2
  315. package/dist/plan-D1Q5rf-r.mjs +0 -2
  316. package/dist/platform-args-Cq6EjQro.mjs +0 -2
  317. package/dist/prepare-CnjcTV2s.mjs +0 -2
  318. package/dist/resolve-project-DtbidV4y.mjs +0 -2
  319. package/dist/route-url-CG7U-cRN.mjs +0 -15
  320. package/dist/runner-CXA9Fh8h.mjs +0 -2
  321. package/dist/validate-Dq_L3s0S.mjs +0 -2
  322. package/skills/void/docs/guide/platform/installation/uninstall.md +0 -69
  323. package/skills/void/docs/integrations/agents.md +0 -26
  324. package/dist/{runner-pg-D4OPlil-.mjs → runner-pg-DV8p_zjA.mjs} +4 -4
  325. package/{skills/void/docs → docs}/guide/database/d1.md +2 -2
  326. package/{skills/void/docs → docs}/guide/database/mysql.md +0 -0
  327. package/{skills/void/docs → docs}/guide/deployment.md +0 -0
  328. package/{skills/void/docs → docs}/guide/durable-state.md +0 -0
  329. package/{skills/void/docs → docs}/guide/edge/headers.md +0 -0
  330. package/{skills/void/docs → docs}/guide/edge/prerendering.md +0 -0
  331. package/{skills/void/docs → docs}/guide/edge/redirects.md +0 -0
  332. package/{skills/void/docs → docs}/guide/edge/static-assets.md +0 -0
  333. package/{skills/void/docs → docs}/guide/email/domains.md +0 -0
  334. package/{skills/void/docs → docs}/guide/email.md +0 -0
  335. package/{skills/void/docs → docs}/guide/index.md +0 -0
  336. package/{skills/void/docs → docs}/guide/kv.md +0 -0
  337. package/{skills/void/docs → docs}/guide/pages-routing/head.md +0 -0
  338. package/{skills/void/docs → docs}/guide/pages-routing/islands.md +2 -2
  339. package/{skills/void/docs → docs}/guide/pages-routing/layouts.md +0 -0
  340. package/{skills/void/docs → docs}/guide/pages-routing/view-transitions.md +0 -0
  341. package/{skills/void/docs → docs}/guide/platform/administration/email.md +0 -0
  342. package/{skills/void/docs → docs}/guide/platform/administration/plans.md +0 -0
  343. package/{skills/void/docs → docs}/guide/platform/administration/zero-trust.md +0 -0
  344. package/{skills/void/docs → docs}/guide/platform/installation/ci.md +0 -0
  345. package/{skills/void/docs → docs}/guide/platform/installation/prerequisites.md +0 -0
  346. package/{skills/void/docs → docs}/guide/platform-administration.md +0 -0
  347. package/{skills/void/docs → docs}/guide/platform-development.md +0 -0
  348. package/{skills/void/docs → docs}/guide/quickstart.md +0 -0
  349. package/{skills/void/docs → docs}/guide/self-hosted-platform.md +0 -0
  350. package/{skills/void/docs → docs}/guide/server-routing.md +3 -3
  351. package/{skills/void/docs → docs}/guide/ssg.md +0 -0
  352. package/{skills/void/docs → docs}/guide/ssr.md +0 -0
  353. package/{skills/void/docs → docs}/guide/type-safety.md +0 -0
  354. package/{skills/void/docs → docs}/guide/typed-fetch.md +0 -0
  355. package/{skills/void/docs → docs}/index.md +0 -0
  356. package/{skills/void/docs → docs}/integrations/cloudflare.md +0 -0
  357. package/{skills/void/docs → docs}/integrations/frameworks/analog.md +3 -3
  358. package/{skills/void/docs → docs}/integrations/frameworks/overview.md +0 -0
  359. package/{skills/void/docs → docs}/integrations/frameworks/react-router.md +3 -3
  360. package/{skills/void/docs → docs}/integrations/frameworks/sveltekit.md +1 -1
  361. package/{skills/void/docs → docs}/integrations/frameworks/tanstack-start.md +2 -2
  362. package/{skills/void/docs → docs}/reference/api/client.md +0 -0
  363. package/{skills/void/docs → docs}/reference/api/database.md +2 -2
  364. /package/{skills/void/docs → docs}/reference/api/durable.md +0 -0
  365. /package/{skills/void/docs → docs}/reference/api/env.md +0 -0
  366. /package/{skills/void/docs → docs}/reference/api/plugin.md +0 -0
  367. /package/{skills/void/docs → docs}/reference/api/rewrites.md +0 -0
  368. /package/{skills/void/docs → docs}/reference/api/types.md +0 -0
  369. /package/{skills/void/docs → docs}/reference/cli/deploy.md +0 -0
  370. /package/{skills/void/docs → docs}/reference/cli/domains.md +0 -0
  371. /package/{skills/void/docs → docs}/reference/cli/email.md +0 -0
  372. /package/{skills/void/docs → docs}/reference/cli/platform-zero-trust.md +0 -0
  373. /package/{skills/void/docs → docs}/reference/structure.md +0 -0
@@ -34,15 +34,14 @@ import { ai } from 'void/ai';
34
34
  export const POST = defineHandler(async (c) => {
35
35
  const { prompt } = await c.req.json();
36
36
 
37
- return ai
38
- .run('@cf/meta/llama-3.3-70b-instruct-fp8-fast', {
39
- messages: [{ role: 'user', content: prompt }],
40
- })
41
- .match({ ok: (result) => c.json(result), limited: (limit) => limit.response() });
37
+ const result = await ai.run('@cf/meta/llama-3.3-70b-instruct-fp8-fast', {
38
+ messages: [{ role: 'user', content: prompt }],
39
+ });
40
+ return c.json(result);
42
41
  });
43
42
  ```
44
43
 
45
- Every operation runs through `.match({ ok, limited })`. Both handlers are required. Return a fallback from `limited`, or use `limit.response()` to send a structured HTTP 429. Limits include `resource`, `reason`, and an optional `retryAt`; other failures throw. Await `.match()`, rather than the operation itself.
44
+ AI methods return promises. Await them to receive the result, or return the promise directly from a handler. See [Usage Limits](#usage-limits) for limit errors.
46
45
 
47
46
  You can use any model available through Cloudflare's AI binding, including Workers AI models such as `@cf/meta/llama-3.3-70b-instruct-fp8-fast` and Cloudflare Gateway models such as `google/gemini-2.5-flash` or `openai/gpt-4.1-mini`. The input object must match the selected Cloudflare model's schema. Models that return binary data, such as generated images, are returned as a `Blob` from `ai.run()`.
48
47
 
@@ -57,29 +56,23 @@ import { ai } from 'void/ai';
57
56
  export const POST = defineHandler(async (c) => {
58
57
  const { prompt } = await c.req.json();
59
58
 
60
- return ai
61
- .stream('@cf/meta/llama-3.3-70b-instruct-fp8-fast', {
62
- messages: [{ role: 'user', content: prompt }],
63
- })
64
- .match({ ok: (result) => result, limited: (limit) => limit.response() });
59
+ return ai.stream('@cf/meta/llama-3.3-70b-instruct-fp8-fast', {
60
+ messages: [{ role: 'user', content: prompt }],
61
+ });
65
62
  });
66
63
  ```
67
64
 
68
- Streaming responses end with a `void:error` SSE event if generation is interrupted after the response starts. A recognized quota interruption calls your `limited` handler and emits `void:limit`, preserving the message from `limit.response()`. `fetchStream()` throws on either terminal event so your UI can exit its generating state. This also applies to streaming provider responses and `ai.run()` with `stream: true`.
65
+ Streaming responses end with a `void:error` SSE event if generation is interrupted after the response starts, or `void:limit` for a usage limit. `fetchStream()` from `void/client` throws `UsageLimit` for limits before or during streaming and `StreamError` for other terminal errors. Catch the error in your UI to stop its generating state, retain partial output, and offer retry. This also applies to streaming provider responses and `ai.run()` with `stream: true`.
69
66
 
70
67
  ## Listing Models
71
68
 
72
69
  Use `ai.models()` to list available models:
73
70
 
74
71
  ```ts
75
- const models = await ai
76
- .models()
77
- .match({ ok: (result) => result, limited: (limit) => limit.response() });
72
+ const models = await ai.models();
78
73
 
79
74
  // Filter by task
80
- const textModels = await ai
81
- .models({ task: 'Text Generation' })
82
- .match({ ok: (result) => result, limited: (limit) => limit.response() });
75
+ const textModels = await ai.models({ task: 'Text Generation' });
83
76
  ```
84
77
 
85
78
  ## Markdown Conversion
@@ -87,9 +80,7 @@ const textModels = await ai
87
80
  Use `ai.toMarkdown()` to convert documents to markdown:
88
81
 
89
82
  ```ts
90
- const result = await ai
91
- .toMarkdown([{ name: 'document.pdf', blob: pdfBytes }])
92
- .match({ ok: (result) => result, limited: (limit) => limit.response() });
83
+ const result = await ai.toMarkdown([{ name: 'document.pdf', blob: pdfBytes }]);
93
84
  ```
94
85
 
95
86
  ## Usage Limits
@@ -100,24 +91,49 @@ requests also use your provider credentials and their billing terms.
100
91
 
101
92
  A team platform uses its own Cloudflare account and may apply per-user limits. Ask your administrator about the available allowance.
102
93
 
94
+ Usage limits reject the operation's promise with `UsageLimit` from `void/limits`. It includes `resource`, `reason`, and an optional `retryAt` timestamp. In Void route handlers and Pages actions, an uncaught limit produces a structured HTTP 429 with retry information. In queues, scheduled jobs, and other server code, it remains a rejected promise.
95
+
96
+ Handling a limit is optional. Use `instanceof UsageLimit` when you want a fallback or a message tailored to your app:
97
+
98
+ ```ts
99
+ import { defineHandler } from 'void';
100
+ import { ai } from 'void/ai';
101
+ import { UsageLimit } from 'void/limits';
102
+
103
+ export const POST = defineHandler(async (c) => {
104
+ const { prompt } = await c.req.json();
105
+ try {
106
+ const result = await ai.run('@cf/meta/llama-3.3-70b-instruct-fp8-fast', {
107
+ messages: [{ role: 'user', content: prompt }],
108
+ });
109
+ return c.json(result);
110
+ } catch (error) {
111
+ if (error instanceof UsageLimit) {
112
+ return error.response({ message: 'Try again shortly. Your prompt has been kept.' });
113
+ }
114
+ throw error;
115
+ }
116
+ });
117
+ ```
118
+
119
+ `error.response()` creates the structured HTTP 429 and accepts an optional custom `message`. Other failures reject normally. When using `fetchStream()`, the same `instanceof UsageLimit` check gives your client access to the message and retry information.
120
+
103
121
  ## Cloudflare Gateway Models
104
122
 
105
123
  `ai.run()` mirrors Cloudflare's `env.AI.run()` model naming and input schemas. Third-party models use Cloudflare model IDs and Cloudflare-managed credentials.
106
124
 
107
125
  ```ts
108
- const result = await ai
109
- .run('google/gemini-2.5-flash', {
110
- contents: [
111
- {
112
- role: 'user',
113
- parts: [{ text: 'Explain Durable Objects in one paragraph.' }],
114
- },
115
- ],
116
- })
117
- .match({ ok: (result) => result, limited: (limit) => limit.response() });
126
+ const result = await ai.run('google/gemini-2.5-flash', {
127
+ contents: [
128
+ {
129
+ role: 'user',
130
+ parts: [{ text: 'Explain Durable Objects in one paragraph.' }],
131
+ },
132
+ ],
133
+ });
118
134
  ```
119
135
 
120
- Pass AI Gateway options as the third argument: `ai.run(model, inputs, { gateway: { skipCache: true } })`.
136
+ Pass AI Gateway options as the third argument: `ai.run(model, inputs, { gateway: { skipCache: true } })`. On a team platform, the platform chooses the gateway and sets its metadata and logging; options such as `skipCache` and `cacheTtl` still apply.
121
137
 
122
138
  For provider-native requests on a direct deployment, configure [`ai.gateway`](../integrations/cloudflare.md#ai-self-host).
123
139
 
@@ -126,7 +142,9 @@ For provider-native requests on a direct deployment, configure [`ai.gateway`](..
126
142
  Use `ai.provider(provider).fetch(path, init)` for a provider-native API with your
127
143
  own provider key. The request goes through the configured Cloudflare AI Gateway
128
144
  and retains the provider's native HTTP shape. On a team platform, it also passes
129
- through that installation's proxy.
145
+ through that installation's proxy. AI Gateway cache, timeout and retry headers,
146
+ such as `cf-aig-cache-ttl` and `cf-aig-max-attempts`, still apply there; the
147
+ platform ignores logging, metadata and cost headers.
130
148
 
131
149
  ### OpenAI
132
150
 
@@ -137,16 +155,13 @@ import { ai } from 'void/ai';
137
155
  export const POST = defineHandler(async (c) => {
138
156
  const { prompt } = await c.req.json();
139
157
 
140
- return ai
141
- .provider('openai')
142
- .fetch('/chat/completions', {
143
- body: {
144
- model: 'gpt-4o',
145
- messages: [{ role: 'user', content: prompt }],
146
- max_tokens: 512,
147
- },
148
- })
149
- .match({ ok: (result) => result, limited: (limit) => limit.response() });
158
+ return ai.provider('openai').fetch('/chat/completions', {
159
+ body: {
160
+ model: 'gpt-4o',
161
+ messages: [{ role: 'user', content: prompt }],
162
+ max_tokens: 512,
163
+ },
164
+ });
150
165
  });
151
166
  ```
152
167
 
@@ -164,8 +179,7 @@ const response = await ai
164
179
  },
165
180
  ],
166
181
  },
167
- })
168
- .match({ ok: (result) => result, limited: (limit) => limit.response() });
182
+ });
169
183
  ```
170
184
 
171
185
  ### Custom Providers
@@ -181,8 +195,7 @@ const response = await ai
181
195
  })
182
196
  .fetch('/v1/respond', {
183
197
  body: { prompt: 'Hello' },
184
- })
185
- .match({ ok: (result) => result, limited: (limit) => limit.response() });
198
+ });
186
199
  ```
187
200
 
188
201
  ### Image Generation
@@ -192,9 +205,7 @@ Use `ai.run()` or `ai.image()` for Cloudflare-native image models:
192
205
  ```ts
193
206
  export const POST = defineHandler(async (c) => {
194
207
  const { prompt } = await c.req.json();
195
- return ai
196
- .image('@cf/black-forest-labs/flux-1-schnell', { prompt })
197
- .match({ ok: (result) => result, limited: (limit) => limit.response() });
208
+ return ai.image('@cf/black-forest-labs/flux-1-schnell', { prompt });
198
209
  });
199
210
  ```
200
211
 
@@ -204,16 +215,13 @@ Use `ai.provider().fetch()` for provider-native image APIs. Match the [provider'
204
215
  export const POST = defineHandler(async (c) => {
205
216
  const { prompt } = await c.req.json();
206
217
 
207
- return ai
208
- .provider('openai')
209
- .fetch('/images/generations', {
210
- body: {
211
- model: 'gpt-image-2.5-sunburst',
212
- prompt,
213
- size: '1024x1024',
214
- },
215
- })
216
- .match({ ok: (result) => result, limited: (limit) => limit.response() });
218
+ return ai.provider('openai').fetch('/images/generations', {
219
+ body: {
220
+ model: 'gpt-image-2.5-sunburst',
221
+ prompt,
222
+ size: '1024x1024',
223
+ },
224
+ });
217
225
  });
218
226
  ```
219
227
 
@@ -227,10 +235,7 @@ export const POST = defineHandler(async (c) => {
227
235
  form.set('prompt', String(body.prompt));
228
236
  form.set('image', body.image as Blob, 'source.png');
229
237
 
230
- return ai
231
- .provider('openai')
232
- .fetch('/images/edits', { body: form })
233
- .match({ ok: (result) => result, limited: (limit) => limit.response() });
238
+ return ai.provider('openai').fetch('/images/edits', { body: form });
234
239
  });
235
240
  ```
236
241
 
@@ -45,7 +45,7 @@ Add `voidPlugin()` to the framework's Vite config to get binding inference, type
45
45
 
46
46
  Any project that produces static files, whether that is an SPA, a static site, or a plain directory. Void serves the assets without requiring application code.
47
47
 
48
- **SPAs** (client-side single-page apps) fall back all non-file paths to `index.html` with a 200 status, so client-side routing works out of the box. Detected when `vite` is a dependency and no backend files exist.
48
+ **SPAs** (client-side single-page apps) serve `index.html` with a 200 status for paths that match no file, so client-side routing works out of the box. On a Void platform, missing files under the hashed assets folder (`/assets/`) return 404 instead. Detected when `vite` is a dependency and no backend files exist.
49
49
 
50
50
  **Static sites** (static site generators like VitePress, Docusaurus, or any pre-built directory) produce per-page HTML files. Detected when a known SSG is a dependency, or when using the `--dir` flag.
51
51
 
@@ -92,7 +92,7 @@ Email/password is enabled by default. To add social providers:
92
92
  }
93
93
  ```
94
94
 
95
- Use `auth.providers` when you want to enable social providers explicitly. Provider credentials come from `AUTH_<PROVIDER>_CLIENT_ID` and `AUTH_<PROVIDER>_CLIENT_SECRET`.
95
+ Provider credentials come from `AUTH_<PROVIDER>_CLIENT_ID` and `AUTH_<PROVIDER>_CLIENT_SECRET`.
96
96
 
97
97
  See the full provider list in the [config reference](../reference/config.md#auth).
98
98
 
@@ -127,6 +127,42 @@ Auth sessions use the same database as your app. Void creates `BETTER_AUTH_SECRE
127
127
 
128
128
  Local development needs no secret setup. To preview a production build locally, provide `BETTER_AUTH_SECRET`.
129
129
 
130
+ `POST`, `PUT`, `PATCH` and `DELETE` requests and WebSocket connections from another origin don't use the signed-in session unless that origin is in `trustedOrigins`. If a separate frontend calls your app, add its origin there (see [Customization](#customization)). If your routes use their own cookies, check the request `Origin` yourself, for example with Hono's [`csrf()`](https://hono.dev/docs/middleware/builtin/csrf) middleware.
131
+
132
+ Deployed apps on Cloudflare limit auth requests per client IP with Better Auth's defaults, for example 3 sign-in attempts per 10 seconds. Each Worker instance keeps its own count. Change or turn off the limits with `rateLimit` in `auth.ts`. To share one count across instances, set `rateLimit: { storage: 'database' }`, then run `void db generate` for a direct Cloudflare deploy.
133
+
134
+ ### Proxies and end-to-end tests
135
+
136
+ All requests from one IP share one count. Two common setups reach the limits:
137
+
138
+ - A server calls your auth endpoints for your users, such as a backend-for-frontend, a rewrite from Vercel or nginx, or another Worker's `fetch`. All of those users share that server's IP.
139
+ - End-to-end tests sign up or sign in many times against a deployed app.
140
+
141
+ Don't read the client IP from `x-forwarded-for` instead: any client can send that header. Give the trusted caller a secret header, and skip the limits only for requests that carry it:
142
+
143
+ ```ts
144
+ import { defineAuth } from 'void/auth';
145
+
146
+ export default defineAuth(({ defaults, env }) => ({
147
+ ...defaults,
148
+ rateLimit: {
149
+ customRules: {
150
+ '/**': (request, rule) =>
151
+ env.AUTH_TRUSTED_KEY && request.headers.get('x-auth-trusted-key') === env.AUTH_TRUSTED_KEY
152
+ ? false
153
+ : rule,
154
+ },
155
+ },
156
+ }));
157
+ ```
158
+
159
+ `'/**'` matches every auth endpoint. Returning `false` skips the limit, and returning `rule` keeps the default limit for everyone else. Declare `AUTH_TRUSTED_KEY: string().optional()` in `env.ts` and set it with `void secret put AUTH_TRUSTED_KEY` (see [Environment variables](./env-vars.md#production-server-values)).
160
+
161
+ Your app no longer limits requests that carry the key, so:
162
+
163
+ - A proxy that sends it must limit every auth request it forwards for each of its users, including sign-up, password reset and verification emails.
164
+ - A test runner should send it only to your app. Never put the key in code that runs in your users' browsers.
165
+
130
166
  ## Customization
131
167
 
132
168
  For advanced configuration, create `auth.ts` at the project root and export `defineAuth(...)`:
@@ -59,7 +59,7 @@ export const users = pgTable('users', {
59
59
  });
60
60
  ```
61
61
 
62
- `void gen model` generates PostgreSQL-appropriate code when the dialect is set to `postgresql`.
62
+ `void gen model` generates PostgreSQL-appropriate code when `database` is set to `"pg"`.
63
63
 
64
64
  ## Migrations
65
65
 
@@ -88,4 +88,4 @@ To update a linked Void project's connection, for example after moving the datab
88
88
  void db set-url
89
89
  ```
90
90
 
91
- This prompts for the new connection string and updates the Hyperdrive configuration. The change takes effect on the next deploy.
91
+ This prompts for the new connection string and updates the Hyperdrive configuration.
@@ -54,9 +54,9 @@ Define your tables in `db/schema.ts` using column helpers from `void/schema-d1`,
54
54
  ::: code-group
55
55
 
56
56
  ```ts [SQLite (D1)]
57
+ import { sql } from 'void/db';
57
58
  // db/schema.ts
58
59
  import { sqliteTable, text, integer } from 'void/schema-d1';
59
- import { sql } from 'void/db';
60
60
 
61
61
  export const users = sqliteTable('users', {
62
62
  id: integer('id').primaryKey({ autoIncrement: true }),
@@ -117,8 +117,8 @@ export * from "./schema/posts";
117
117
  Void re-exports all common [Drizzle query operators](https://orm.drizzle.team/docs/select) from `void/db` so you don't need to install `drizzle-orm` separately. Import `db` from `void/db` and your tables from `@schema`:
118
118
 
119
119
  ```ts
120
- import { db } from 'void/db';
121
120
  import { users } from '@schema';
121
+ import { db } from 'void/db';
122
122
  ```
123
123
 
124
124
  ::: info What is `@schema`?
@@ -134,8 +134,8 @@ const allUsers = await db.select().from(users);
134
134
  ### Filter with `where`
135
135
 
136
136
  ```ts
137
- import { db, eq, and, or } from 'void/db';
138
137
  import { users } from '@schema';
138
+ import { db, eq, and, or } from 'void/db';
139
139
 
140
140
  // Single condition
141
141
  const user = await db.select().from(users).where(eq(users.id, 1));
@@ -200,8 +200,8 @@ const names = await db.select({ name: users.name, email: users.email }).from(use
200
200
  ### Ordering and pagination
201
201
 
202
202
  ```ts
203
- import { db, desc } from 'void/db';
204
203
  import { users } from '@schema';
204
+ import { db, desc } from 'void/db';
205
205
 
206
206
  const page = await db.select().from(users).orderBy(desc(users.createdAt)).limit(10).offset(20);
207
207
  ```
@@ -209,8 +209,8 @@ const page = await db.select().from(users).orderBy(desc(users.createdAt)).limit(
209
209
  ### Joins
210
210
 
211
211
  ```ts
212
- import { db, eq } from 'void/db';
213
212
  import { users, posts } from '@schema';
213
+ import { db, eq } from 'void/db';
214
214
 
215
215
  const results = await db
216
216
  .select({
@@ -290,9 +290,9 @@ export const insertUserSchema = createInsertSchema(users, {
290
290
  ```
291
291
 
292
292
  ```ts [Valibot]
293
+ import { pipe, minLength, email } from 'valibot';
293
294
  // db/schema.ts
294
295
  import { createInsertSchema } from 'void/drizzle-valibot';
295
- import { pipe, minLength, email } from 'valibot';
296
296
 
297
297
  export const insertUserSchema = createInsertSchema(users, {
298
298
  name: (schema) => pipe(schema, minLength(1)),
@@ -301,9 +301,9 @@ export const insertUserSchema = createInsertSchema(users, {
301
301
  ```
302
302
 
303
303
  ```ts [ArkType]
304
+ import { type } from 'arktype';
304
305
  // db/schema.ts
305
306
  import { createInsertSchema } from 'void/drizzle-arktype';
306
- import { type } from 'arktype';
307
307
 
308
308
  export const insertUserSchema = createInsertSchema(users, {
309
309
  name: type('string > 0'),
@@ -318,10 +318,10 @@ export const insertUserSchema = createInsertSchema(users, {
318
318
  Use the derived schema in your route handlers with [`withValidator()`](./server-routing.md#validation):
319
319
 
320
320
  ```ts
321
+ import { users, insertUserSchema } from '@schema';
321
322
  // routes/api/users/index.ts
322
323
  import { defineHandler } from 'void';
323
324
  import { db } from 'void/db';
324
- import { users, insertUserSchema } from '@schema';
325
325
 
326
326
  export const POST = defineHandler.withValidator({
327
327
  body: insertUserSchema,
@@ -361,6 +361,21 @@ npm install arktype
361
361
 
362
362
  See the [CLI reference](../reference/cli/database.md#database) for connection setup, SQL execution, Studio, exports, and other commands.
363
363
 
364
+ ## Hand-written migrations {#hand-written-migrations}
365
+
366
+ Deploy compares your Drizzle schema with the snapshots in `db/migrations/meta/`. If you have SQL migrations without this metadata, running `void db generate` would create another migration for the same tables.
367
+
368
+ To keep an existing migration history:
369
+
370
+ 1. Make your Drizzle schema match the tables your SQL migrations create.
371
+ 2. In a scratch copy of the project, remove `db/migrations/` and run `void db generate`.
372
+ 3. Copy the generated `meta/` folder into your original migration directory. In `_journal.json`, replace `entries` with an entry for each existing SQL file in order. Use consecutive `idx` values starting at `0`, the file name without `.sql` as `tag`, increasing timestamps as `when`, and the generated entry's `version`. Set `breakpoints` to whether the file uses `--> statement-breakpoint` lines.
373
+ 4. Rename the generated snapshot to `<prefix>_snapshot.json`, where `<prefix>` is the part of the last migration tag before its first `_`. For `001_create_users.sql`, use `001_snapshot.json`.
374
+
375
+ Review and commit the metadata, then run `void deploy` again. If no database has applied the SQL yet, you can instead replace it with migrations from `void db generate`.
376
+
377
+ For D1 migrations containing triggers, add a `--> statement-breakpoint` line after each complete SQL statement, including the trigger's closing `END;`. Keep the trigger body together.
378
+
364
379
  ## Scaffolding
365
380
 
366
381
  The `void gen model` command generates dialect-appropriate `sqliteTable`, `pgTable`, or `mysqlTable` definitions.
@@ -108,7 +108,7 @@ Requests with `Cookie`, `Authorization`, or Cloudflare Access's `Cf-Access-Jwt-A
108
108
 
109
109
  For a dispatch rewrite, the cache key includes both the destination and original pathname. A direct request to `/en/docs/foo` therefore uses a different entry from `/docs/foo` rewritten to that destination.
110
110
 
111
- Query parameters are excluded by default. Add `routing.revalidateQueryAllowlist` when specific parameters should produce separate cached responses. Middleware `c.rewrite()` runs after the ISR lookup, so it doesn't create an additional cache variant.
111
+ Query parameters are excluded by default. Add `routing.revalidateQueryAllowlist` when specific parameters should produce separate cached responses. Requests with other query parameters get the cached page. If nothing is cached yet, they render live and are not stored, so add every parameter a page reads to `routing.revalidateQueryAllowlist`. Middleware `c.rewrite()` runs after the ISR lookup, so it doesn't create an additional cache variant.
112
112
 
113
113
  Purge the destination pathname with `revalidate({ paths })`. Purging `/en/docs/foo` clears both direct and rewritten variants. Purging only `/docs/foo` won't clear them, because the entries are stored under the destination.
114
114
 
@@ -109,15 +109,13 @@ The file can include [3xx redirects](./redirects) too. Rules in `void.config.ts`
109
109
 
110
110
  ## Client navigation
111
111
 
112
- Rewrites run on the server. In Pages mode, a client-side `<Link to="/docs">` navigation may fetch loader data for `/docs` rather than the rewritten route `/en/docs`.
112
+ Rewrites run on the server. In Pages mode, a client-side `<Link href="/docs">` navigation may fetch loader data for `/docs` rather than the rewritten route `/en/docs`.
113
113
 
114
114
  Use `<a href="/docs">` to make a full server request when the destination has a different loader. Use a redirect if the URL should change.
115
115
 
116
116
  ## ISR cache keys with rewrites
117
117
 
118
- Static rewrites cache the destination separately for each original pathname. Query parameters vary the cache only when included in `routing.revalidateQueryAllowlist`. Middleware rewrites don't create a separate cache variant.
119
-
120
- Purge the destination with `revalidate({ paths: ['/en/docs/foo'] })` to clear direct and rewritten requests. Purging only `/docs/foo` does not clear that entry. See [Revalidation](./revalidation).
118
+ Purge the destination path, such as `/en/docs/foo`, to clear rewritten cache entries. See [Cache keys and rewrites](./revalidation#cache-keys-and-rewrites).
121
119
 
122
120
  ## Debugging with `X-Void-Routing`
123
121
 
@@ -100,13 +100,13 @@ There is no local inbound trigger yet — `void dev` serves the outbound dev inb
100
100
 
101
101
  ## Testing inbound handlers {#testing-inbound-handlers}
102
102
 
103
- Use `createInboundTestHarness` to capture replies, forwards, and rejections. For shared platform mail, pass the full address including the project prefix, such as `acme+support+abc-123@acme.dev`. For direct Cloudflare mail, set `slug: null` and use `support+abc-123@mail.acme.com`.
103
+ Use `createInboundTestHarness` to capture replies, forwards, and rejections. For shared platform mail, pass the full address including the project prefix, such as [`acme+support+abc-123@acme.dev`](mailto:acme+support+abc-123@acme.dev). For direct Cloudflare mail, set `slug: null` and use [`support+abc-123@mail.acme.com`](mailto:support+abc-123@mail.acme.com).
104
104
 
105
105
  ```ts
106
106
  import { describe, it, expect } from 'vitest';
107
107
  import { createInboundTestHarness } from 'void/email/testing';
108
- import support from './email/support+[ticket]';
109
108
  import defaultHandler from './email/_default';
109
+ import support from './email/support+[ticket]';
110
110
 
111
111
  describe('email handlers', () => {
112
112
  it('routes to support+[ticket] and extracts the tag', async () => {
@@ -59,8 +59,6 @@ Cloudflare emails the recipient with a verification link. Once they click it and
59
59
  void email destinations
60
60
  ```
61
61
 
62
- The project owner’s email is added automatically, but still needs verification as described in [Setup](#setup).
63
-
64
62
  ::: warning When this is the right fit
65
63
  The shared sender suits team alerts, project notifications, and replies to inbound mail.
66
64
 
@@ -39,8 +39,12 @@ STRIPE_KEY=sk_test_example
39
39
  VITE_API_ORIGIN=http://localhost:5173
40
40
  ```
41
41
 
42
+ An unquoted value ends at `#`, which starts a comment. Quote a value that contains `#`, such as `DATABASE_URL="postgres://user:p#ss@localhost/my-app"`.
43
+
42
44
  Keep `.env` out of Git. Other dotenv files, including `.env.local`, `.env.production`, `.env.example`, and Wrangler’s `.dev.vars*`, are not supported.
43
45
 
46
+ Names starting with `__VOID_` are reserved for Void. Void stops with an error if `.env` or `env.ts` defines one.
47
+
44
48
  Run the local check at any time:
45
49
 
46
50
  ```sh
@@ -70,10 +70,10 @@ curl -X POST http://localhost:5173/__void/scheduled \
70
70
  -d '{"cron":"0 * * * *","scheduledTime":'"$(date +%s000)"'}'
71
71
  ```
72
72
 
73
- If you set `__VOID_PROXY_TOKEN` in `.env`, that explicit token takes precedence and the printed curl command uses `x-void-internal: <your-token>` instead.
74
-
75
73
  The endpoint works in native Void apps and supported frameworks. Framework jobs run in the adapter's development runtime and use the bindings it provides.
76
74
 
77
75
  ## Deployment behavior
78
76
 
79
77
  Native scheduled events do not require a manual HTTP token. For framework deployments, HTTP requests to `/__void/scheduled` require a matching `x-void-internal` token: the managed platform supplies its proxy token, or you can explicitly configure `CRON_SECRET` for manual calls. An absent or mismatched token returns `401` without running a job.
78
+
79
+ On a Void platform, each project can use up to 100 different cron expressions and 1,000 schedules (each expression a job exports counts as one schedule). Jobs that share an expression count once toward the expression limit. When you deploy to your own Cloudflare account, Cloudflare's [Cron Trigger limits](https://developers.cloudflare.com/workers/platform/limits/) apply instead.
@@ -22,6 +22,10 @@ export const live = defineLiveStream({
22
22
  });
23
23
  ```
24
24
 
25
+ This example accepts visitors who are not signed in. Without `onSubscribe`, any connection can subscribe to any topic, so read [Authorization](#authorization) before you publish private data.
26
+
27
+ Void enables live streams when a [scanned file](../reference/resource-inference.md#scanned-directories) imports `void/live`, so keep this module in `src/` or another scanned directory. To keep it in an unscanned directory such as `lib/`, add that directory to [`inference.scanDirs`](../reference/resource-inference.md#custom-scan-directories) and list the default directories you still use, because `scanDirs` replaces them.
28
+
25
29
  Expose it from a normal route:
26
30
 
27
31
  ```ts
@@ -99,6 +103,8 @@ await live.withEnv(env).publish('post:12', { title: 'Updated title' });
99
103
 
100
104
  Only a connection's owner can change its subscriptions. The example above allows anonymous connections; authenticated streams use the current Void user by default.
101
105
 
106
+ The owner decides who can change a connection's subscriptions, not which topics the connection can read.
107
+
102
108
  For authenticated streams, pass the same owner key to `connect()` and
103
109
  `control()`:
104
110
 
@@ -125,16 +131,17 @@ current authenticated user and uses `user:${user.id}` when the user has a string
125
131
  `id`. If neither path produces an owner and `allowAnonymousControl` is not set,
126
132
  the request is rejected with `403`.
127
133
 
128
- For stream-local subscription rules, use `onSubscribe`:
134
+ Without `onSubscribe`, any connection that passes the owner check (any signed-in user, or any visitor when `allowAnonymousControl` is set) can subscribe to any topic and receives everything published to it. Topic names are easy to guess. Use `onSubscribe` to decide who can read each topic: return a `Response` to reject the subscription, or return nothing to allow it.
129
135
 
130
136
  ```ts
131
137
  export const live = defineLiveStream({
132
138
  id: 'app',
133
139
  async onSubscribe(ctx) {
134
- const match = /^post:(.+)$/.exec(ctx.topic);
135
- if (match && !(await canReadPost(ctx.env, ctx.user, match[1]))) {
136
- return new Response('Forbidden', { status: 403 });
137
- }
140
+ const post = /^post:(.+)$/.exec(ctx.topic);
141
+ if (post && (await canReadPost(ctx.env, ctx.user, post[1]))) return;
142
+ const inbox = /^user:(.+):inbox$/.exec(ctx.topic);
143
+ if (inbox && ctx.owner === `user:${inbox[1]}`) return;
144
+ return new Response('Forbidden', { status: 403 });
138
145
  },
139
146
  });
140
147
  ```
@@ -11,10 +11,10 @@ Use an action to change server data from a form or button. Define it in the page
11
11
  Export `action` from a `.server.ts` file. Use `withValidator()` with a [schema-derived validator](../database.md#schema-derived-validators) to validate the request body:
12
12
 
13
13
  ```ts
14
+ import { users, insertUserSchema } from '@schema';
14
15
  // pages/users/index.server.ts
15
16
  import { defineHandler } from 'void';
16
17
  import { db } from 'void/db';
17
- import { users, insertUserSchema } from '@schema';
18
18
 
19
19
  export const action = defineHandler.withValidator({
20
20
  body: insertUserSchema,
@@ -176,10 +176,10 @@ const form = useForm('/users/:id', { name: '' }, { params: { id: '42' } });
176
176
  When a page needs multiple mutations, such as updating and deleting a user on the same page, export `actions` (plural) instead of `action`:
177
177
 
178
178
  ```ts
179
+ import { users, updateUserSchema } from '@schema';
179
180
  // pages/users/[id].server.ts
180
181
  import { defineHandler } from 'void';
181
182
  import { db, eq } from 'void/db';
182
- import { users, updateUserSchema } from '@schema';
183
183
 
184
184
  export const actions = {
185
185
  update: defineHandler.withValidator({
@@ -269,7 +269,7 @@ export const action = defineHandler(async (c) => {
269
269
 
270
270
  ### Usage limits
271
271
 
272
- For [AI](../ai.md) and [Sandbox](../sandboxes.md) actions, return `limit.response({ message: 'Try again shortly. Your draft is still editable.' })` from the operation's `limited` handler. `Form` displays the message and waits until a known retry deadline before allowing resubmission. It keeps the fields editable and never retries automatically.
272
+ For [AI](../ai.md#usage-limits) and [Sandbox](../sandboxes.md#usage-limits) actions, an uncaught `UsageLimit` automatically produces a structured HTTP 429. To customize the message, catch the error with `instanceof UsageLimit` and return `error.response({ message: 'Try again shortly. Your draft is still editable.' })`. `Form` displays the message and waits until a known retry deadline before allowing resubmission. It keeps the fields editable and never retries automatically.
273
273
 
274
274
  ## Authentication and origin checks
275
275
 
@@ -355,20 +355,32 @@ On the server, use `c.req.parseBody()` to access the uploaded file:
355
355
 
356
356
  ```ts
357
357
  // pages/photos.server.ts
358
- import { defineHandler } from 'void';
358
+ import { defineHandler, ValidationError } from 'void/handler';
359
359
  import { storage } from 'void/storage';
360
360
 
361
+ // file.name and file.type come from the browser. Don't use them as the key or trust the type.
362
+ const imageTypes = new Map([
363
+ ['image/jpeg', 'jpg'],
364
+ ['image/png', 'png'],
365
+ ['image/webp', 'webp'],
366
+ ]);
367
+
361
368
  export const action = defineHandler(async (c) => {
362
369
  const body = await c.req.parseBody();
363
- const file = body['photo'] as File;
364
- if (file && file.size > 0) {
365
- await storage.put(file.name, file.stream(), {
366
- httpMetadata: { contentType: file.type },
367
- });
370
+ const file = body['photo'];
371
+ if (!(file instanceof File) || file.size === 0) return;
372
+ const ext = imageTypes.get(file.type);
373
+ if (!ext) {
374
+ throw new ValidationError({ photo: 'Upload a JPEG, PNG, or WebP image.' });
368
375
  }
376
+ await storage.put(`uploads/${crypto.randomUUID()}.${ext}`, file.stream(), {
377
+ httpMetadata: { contentType: file.type },
378
+ });
369
379
  });
370
380
  ```
371
381
 
382
+ Pick the storage key on the server and allow only the file types you expect. See [Serving Files](../storage.md#serving-files) to send them back safely.
383
+
372
384
  ## Choosing a Primitive
373
385
 
374
386
  | Primitive | Page update | Form state | Framework-specific |