void 0.24.0 → 0.26.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 (199) hide show
  1. package/README.md +19 -106
  2. package/dist/{account-cmd-vJhh5lrb.mjs → account-cmd-CDry3B5V.mjs} +5 -5
  3. package/dist/{application-analysis-BVsVqc11.mjs → application-analysis-DC2yPvzY.mjs} +1 -1
  4. package/dist/{application-routing-B7QEjqSR.mjs → application-routing-C9khgLiS.mjs} +1 -1
  5. package/dist/{auth-DX3JURcX.mjs → auth-B60QDbjg.mjs} +1 -1
  6. package/dist/{auth-CQkSDS50.mjs → auth-D52yG7hl.mjs} +3 -3
  7. package/dist/{auth-link-D75t9wO2.mjs → auth-link-DjeOptYi.mjs} +5 -6
  8. package/dist/{auth-router-ClfhL1J7.mjs → auth-router-BRxIuqDi.mjs} +7 -7
  9. package/dist/{build-cmd-zMi1iFnS.mjs → build-cmd-Ce3C8huo.mjs} +6 -7
  10. package/dist/{cache-ZtQmyxVV.mjs → cache-CS8Yxr5a.mjs} +6 -7
  11. package/dist/{cancel-deploy-Box0kkEV.mjs → cancel-deploy-mZsD88Xa.mjs} +4 -6
  12. package/dist/{cf-access-BGe5sWXF.mjs → cf-access-YTlqvAsk.mjs} +1 -1
  13. package/dist/cf-build-output-ChY6-XWP.mjs +2 -0
  14. package/dist/cli/cli.mjs +45 -45
  15. package/dist/cli/cloudflare-operation-process.mjs +3 -3
  16. package/dist/cli/env-schema-probe.d.mts +1 -1
  17. package/dist/cli/env-schema-probe.mjs +2 -2
  18. package/dist/client-BxVixJBI.mjs +2 -0
  19. package/dist/{client-DKAsFA4X.mjs → client-mC-dFGDD.mjs} +103 -25
  20. package/dist/{cloudflare-cmd-CmY8dGkd.mjs → cloudflare-cmd-bhwCxAN6.mjs} +2 -2
  21. package/dist/{cloudflare-connect-IiR3ZieC.mjs → cloudflare-connect-CuhRgk3k.mjs} +2 -2
  22. package/dist/{cloudflare-operations-DzH-A966.mjs → cloudflare-operations-DYa3coIk.mjs} +2 -2
  23. package/dist/{cloudflare-operations-BCdXSsmo.mjs → cloudflare-operations-DoQrVHAt.mjs} +1 -1
  24. package/dist/{connect-B-usTnlF.mjs → connect-H6EvP62X.mjs} +12 -9
  25. package/dist/{create-project-CYOOAYcn.mjs → create-project-B2kk9Q0o.mjs} +6 -6
  26. package/dist/{create-project-FWbf5Olo.mjs → create-project-Dbb4uiN-.mjs} +1 -1
  27. package/dist/{db-Ck2eZ2iB.mjs → db-DpCKZto0.mjs} +35 -38
  28. package/dist/{delete-sK56qtzZ.mjs → delete-CxlSvsvW.mjs} +6 -7
  29. package/dist/{deploy-gCVaiJbS.mjs → deploy-CeEbnv4b.mjs} +9249 -8925
  30. package/dist/deploy-qmiLE1G1.mjs +2 -0
  31. package/dist/{dev-inbox-P0u4tM8Y.mjs → dev-inbox-D64ZKUod.mjs} +5 -6
  32. package/dist/{domain-DFfQE8jg.mjs → domain-B_9VgOm6.mjs} +8 -8
  33. package/dist/{email-CdnhUHvJ.mjs → email-BokHgjKj.mjs} +7 -7
  34. package/dist/{env-DmGdHi5i.mjs → env-BdTc-K2L.mjs} +7 -8
  35. package/dist/{env-public-BX_r8HR6.d.mts → env-public-DR7ZM6d-.d.mts} +1 -1
  36. package/dist/{env-validation-zZOCcQVZ.mjs → env-validation-cZBvGWaP.mjs} +1 -1
  37. package/dist/{fetch-stream-IvCYKyQL.mjs → fetch-stream-Nm3hkZbT.mjs} +8 -1
  38. package/dist/{gen-CshAkukR.mjs → gen-BhDlTpwW.mjs} +2 -4
  39. package/dist/gen-qCBwH3fZ.mjs +2 -0
  40. package/dist/{github-cmd-DftuEvIt.mjs → github-cmd-hhkTzlGS.mjs} +7 -16
  41. package/dist/{handler-CZ4nAylQ.d.mts → handler-BeK3weD4.d.mts} +1 -1
  42. package/dist/{help-Dotbf6iz.mjs → help-Bv44e2tm.mjs} +13 -9
  43. package/dist/help-Dw5f9Xrg.mjs +2 -0
  44. package/dist/index.d.mts +19 -15
  45. package/dist/index.mjs +2225 -2142
  46. package/dist/{info-B97bTX9N.mjs → info-DhtTKh65.mjs} +2 -4
  47. package/dist/{init-BTQEZZGJ.mjs → init-BMf4T03q.mjs} +17 -18
  48. package/dist/{limits-Bq5LG8Id.d.mts → limits-CFsDKQvU.d.mts} +1 -10
  49. package/dist/{limits-Cjuk2VPm.mjs → limits-D0vD1M9A.mjs} +1 -22
  50. package/dist/{link-B46sYH0K.mjs → link-BqYsSS_I.mjs} +6 -7
  51. package/dist/{list-BnPrIsU_.mjs → list-_kBToO-l.mjs} +10 -10
  52. package/dist/{live-Chw1eIMv.d.mts → live-iAuEH1hd.d.mts} +1 -1
  53. package/dist/{local-d1-2CMnpuW_.mjs → local-d1-fGxYrVD6.mjs} +2 -2
  54. package/dist/login-D7meQg_3.mjs +2 -0
  55. package/dist/{login-BuiMyUrx.mjs → login-DFuo-RWO.mjs} +9 -7
  56. package/dist/{logs-CZG9d-ic.mjs → logs-DYgnh6dH.mjs} +7 -8
  57. package/dist/migrate-CEu2X5DB.mjs +2 -0
  58. package/dist/{migrate-q4_njtaW.mjs → migrate-CV1on-yJ.mjs} +2 -4
  59. package/dist/{node-BkyRWRx8.mjs → node-C6Q6e1Es.mjs} +1 -1
  60. package/dist/{operator-args-B3BTmSoi.mjs → operator-args-DdY54Bgb.mjs} +1 -1
  61. package/dist/{operator-auth-7erYc6Lt.mjs → operator-auth-2dsGlTT5.mjs} +4 -8
  62. package/dist/{operator-client-CpHjTd0t.mjs → operator-client-BFqpp6z0.mjs} +3 -3
  63. package/dist/{operator-cmd-DGqPJUD3.mjs → operator-cmd-CUGHXrQf.mjs} +27 -9
  64. package/dist/operator-cmd-DRPvhpjM.mjs +2 -0
  65. package/dist/{output-CbM85T4k.mjs → output-BvdaehtE.mjs} +28 -4
  66. package/dist/pages/client.d.mts +4 -1
  67. package/dist/pages/client.mjs +120 -29
  68. package/dist/pages/index.mjs +1 -1
  69. package/dist/pages/islands-plugin.mjs +1 -1
  70. package/dist/pages/protocol.d.mts +2 -2
  71. package/dist/{plan-NPpwZ_kc.mjs → plan-CJGEeTEI.mjs} +2 -2
  72. package/dist/plan-COtUvdMA.mjs +2 -0
  73. package/dist/{platform-args-3k8X9-H_.mjs → platform-args-DSV2p4nY.mjs} +4 -2
  74. package/dist/platform-args-Dcy_5b_Y.mjs +2 -0
  75. package/dist/{platform-auth-config-DDrxUy4m.mjs → platform-auth-config-Bosx7U1h.mjs} +20 -19
  76. package/dist/{platform-auth-protection-BLadnnct.mjs → platform-auth-protection-DcbL_LW_.mjs} +8 -8
  77. package/dist/{platform-auth-recovery-CmCzX2Fl.mjs → platform-auth-recovery-WfTZ8K6o.mjs} +7 -7
  78. package/dist/{platform-cmd-DMDEc9uS.mjs → platform-cmd-DbKtQb_4.mjs} +73 -38
  79. package/dist/{platform-cmd-pMTRhvYs.mjs → platform-cmd-dyFUof0t.mjs} +1 -1
  80. package/dist/platform-discovery-BC0HCKYt.mjs +41 -0
  81. package/dist/{platform-domain-Di6EYrQl.mjs → platform-domain-Bsq43AJp.mjs} +5 -4
  82. package/dist/{platform-lifecycle-BFdSi9Qt.mjs → platform-lifecycle--5gWmDoI.mjs} +1 -1
  83. package/dist/{platform-lifecycle-BBoNzojY.mjs → platform-lifecycle-Cfi0o6xM.mjs} +4605 -3527
  84. package/dist/platform-management-Bbwf6-Dq.mjs +2 -0
  85. package/dist/{platform-management-CWOQOWXm.mjs → platform-management-BrF9STZp.mjs} +211 -31
  86. package/dist/{platform-plans-config-Vl9BP3H4.mjs → platform-plans-config-DU-8nC2Y.mjs} +3 -3
  87. package/dist/{platform-recovery-BJnQJEtQ.mjs → platform-recovery-DQAqAQYu.mjs} +3 -3
  88. package/dist/{platform-registry-DkPPXfp_.mjs → platform-registry-P387v42A.mjs} +18 -11
  89. package/dist/prepare-BBknoZI1.mjs +2 -0
  90. package/dist/{prepare-CBLT3CHH.mjs → prepare-BMrRWLwP.mjs} +3 -4
  91. package/dist/{prepare-DnaKBi--.mjs → prepare-WoUubFKL.mjs} +1 -1
  92. package/dist/{project-DsblYifa.mjs → project-BiaPQ7ur.mjs} +1 -1
  93. package/dist/{project-DKG5Ra8p.mjs → project-DQstYTxO.mjs} +1 -1
  94. package/dist/{project-cmd-DxnXstAq.mjs → project-cmd-BbH2LptN.mjs} +21 -22
  95. package/dist/{project-team-BmHcaALJ.mjs → project-team-Yiww3-go.mjs} +8 -9
  96. package/dist/{project-token-GionZlBI.mjs → project-token-BrtT2NkY.mjs} +6 -9
  97. package/dist/{project-zero-trust-JLCjVQA7.mjs → project-zero-trust-nYJ2V2nc.mjs} +5 -6
  98. package/dist/{prompts-BNUoTiaf.mjs → prompts-6OSVA7DJ.mjs} +58 -15
  99. package/dist/{prompts-2TGE6oXx.mjs → prompts-ZAuJlyCj.mjs} +1 -1
  100. package/dist/{protocol-CjF_iI9X.d.mts → protocol-BX8t2jiw.d.mts} +1 -1
  101. package/dist/{provision-CVku1JFU.mjs → provision-DMTPG8Rh.mjs} +1 -1
  102. package/dist/{provision-CaoxO_U7.mjs → provision-VeqAC77A.mjs} +18 -8
  103. package/dist/{requests-BOjIVAeF.mjs → requests-TIz_jtSu.mjs} +5 -6
  104. package/dist/{rollback-CwR3pLj3.mjs → rollback-dBqICPzj.mjs} +9 -10
  105. package/dist/{runner-Ol0TNjk6.mjs → runner--2KF6Kua.mjs} +1 -1
  106. package/dist/{runner-CXA9Fh8h.mjs → runner-voIltZrY.mjs} +1 -1
  107. package/dist/runtime/ai.d.mts +10 -11
  108. package/dist/runtime/ai.mjs +13 -18
  109. package/dist/runtime/client-react.mjs +1 -1
  110. package/dist/runtime/client-solid.mjs +1 -1
  111. package/dist/runtime/client-svelte.mjs +1 -1
  112. package/dist/runtime/client-vue.mjs +1 -1
  113. package/dist/runtime/client.mjs +1 -1
  114. package/dist/runtime/email/testing.mjs +1 -1
  115. package/dist/runtime/email.mjs +35 -17
  116. package/dist/runtime/env-public.d.mts +1 -1
  117. package/dist/runtime/fetch-stream.mjs +1 -1
  118. package/dist/runtime/handler.d.mts +1 -1
  119. package/dist/runtime/limits.d.mts +2 -2
  120. package/dist/runtime/limits.mjs +2 -2
  121. package/dist/runtime/live-client.d.mts +1 -1
  122. package/dist/runtime/live.d.mts +1 -1
  123. package/dist/runtime/migration-handler.mjs +33 -7
  124. package/dist/runtime/routing.d.mts +1 -1
  125. package/dist/runtime/routing.mjs +1 -1
  126. package/dist/runtime/sandbox.d.mts +1 -1
  127. package/dist/runtime/sandbox.mjs +12 -22
  128. package/dist/runtime/validator.d.mts +1 -1
  129. package/dist/runtime/ws-server.d.mts +1 -1
  130. package/dist/runtime/ws.d.mts +1 -1
  131. package/dist/{sandbox-XZAqzFlG.d.mts → sandbox-C5LFoJMf.d.mts} +7 -7
  132. package/dist/{scan-C7okrLyM.mjs → scan-C8dA7iM4.mjs} +10 -6
  133. package/dist/{secret-ByWJWBUG.mjs → secret-AKnNXNP5.mjs} +15 -16
  134. package/dist/{skills-C1PlclPt.mjs → skills-BPQUGClu.mjs} +1 -1
  135. package/dist/{subcommand-prompt-klFCfcor.mjs → subcommand-prompt-B6WS9Aoz.mjs} +2 -2
  136. package/dist/sveltekit.d.mts +1 -1
  137. package/dist/sveltekit.mjs +4 -9
  138. package/dist/validate-pna7R3Pv.mjs +2 -0
  139. package/dist/{validate-ctOrgiS3.mjs → validate-ymhap6M_.mjs} +1 -1
  140. package/dist/{wrangler-7K-bW_DL.mjs → wrangler-DhUerFkW.mjs} +10 -15
  141. package/dist/{ws-CL1w7GXU.d.mts → ws-C79j4aED.d.mts} +1 -1
  142. package/package.json +21 -21
  143. package/sandbox.Dockerfile +1 -1
  144. package/skills/void/SKILL.md +5 -2
  145. package/skills/void/docs/guide/ai.md +78 -137
  146. package/skills/void/docs/guide/app-types.md +1 -1
  147. package/skills/void/docs/guide/auth.md +0 -2
  148. package/skills/void/docs/guide/database/d1.md +7 -7
  149. package/skills/void/docs/guide/database.md +15 -0
  150. package/skills/void/docs/guide/deployment.md +58 -124
  151. package/skills/void/docs/guide/env-vars.md +1 -5
  152. package/skills/void/docs/guide/index.md +19 -18
  153. package/skills/void/docs/guide/kv.md +0 -2
  154. package/skills/void/docs/guide/live.md +2 -0
  155. package/skills/void/docs/guide/pages-routing/actions-and-forms.md +49 -47
  156. package/skills/void/docs/guide/pages-routing/overview.md +2 -2
  157. package/skills/void/docs/guide/platform/administration/email.md +0 -2
  158. package/skills/void/docs/guide/platform/administration/operations.md +11 -22
  159. package/skills/void/docs/guide/platform/administration/plans.md +1 -5
  160. package/skills/void/docs/guide/platform/administration/zero-trust.md +1 -4
  161. package/skills/void/docs/guide/platform/development/schema-ci.md +7 -1
  162. package/skills/void/docs/guide/platform/installation/credentials.md +2 -2
  163. package/skills/void/docs/guide/platform/installation/first-deployment.md +5 -1
  164. package/skills/void/docs/guide/platform/installation/maintenance.md +33 -11
  165. package/skills/void/docs/guide/platform/installation/prerequisites.md +79 -7
  166. package/skills/void/docs/guide/platform/installation/setup.md +1 -1
  167. package/skills/void/docs/guide/platform/installation/uninstall.md +44 -11
  168. package/skills/void/docs/guide/quickstart.md +37 -80
  169. package/skills/void/docs/guide/sandboxes.md +75 -66
  170. package/skills/void/docs/guide/self-hosted-platform.md +20 -0
  171. package/skills/void/docs/guide/websockets.md +0 -2
  172. package/skills/void/docs/integrations/frameworks/overview.md +2 -2
  173. package/skills/void/docs/integrations/frameworks/sveltekit.md +40 -25
  174. package/skills/void/docs/integrations/nodejs-bun-deno.md +7 -2
  175. package/skills/void/docs/reference/api/resources.md +38 -14
  176. package/skills/void/docs/reference/api.md +35 -34
  177. package/skills/void/docs/reference/cli/auth.md +3 -1
  178. package/skills/void/docs/reference/cli/deploy.md +1 -1
  179. package/skills/void/docs/reference/cli/platform-config.md +2 -2
  180. package/skills/void/docs/reference/cli/platform-installation.md +14 -12
  181. package/skills/void/docs/reference/cli/platform-operations.md +4 -8
  182. package/skills/void/docs/reference/cli/platform.md +9 -2
  183. package/skills/void/docs/reference/cli.md +1 -1
  184. package/skills/void/docs/reference/resource-inference.md +12 -8
  185. package/dist/cf-build-output-_0HNWysu.mjs +0 -2
  186. package/dist/client-B9UBI03V.mjs +0 -2
  187. package/dist/deploy-irP5q0-x.mjs +0 -2
  188. package/dist/gen-DvQTWzDU.mjs +0 -2
  189. package/dist/help-mabuIV98.mjs +0 -2
  190. package/dist/login-DIy9Sb-U.mjs +0 -2
  191. package/dist/migrate-BCiJ8w0f.mjs +0 -2
  192. package/dist/plan-D1Q5rf-r.mjs +0 -2
  193. package/dist/platform-args-DdnM6WIf.mjs +0 -2
  194. package/dist/platform-management-DG47jys8.mjs +0 -2
  195. package/dist/prepare-CnjcTV2s.mjs +0 -2
  196. package/dist/validate-Dq_L3s0S.mjs +0 -2
  197. package/skills/void/docs/guide/env-migration.md +0 -109
  198. package/dist/{cf-build-output-BPqOT964.mjs → cf-build-output-U8o1Gl6X.mjs} +1 -1
  199. package/dist/{env-validation-C_8tPdhs.mjs → env-validation-vBSQ8gw5.mjs} +1 -1
@@ -6,12 +6,6 @@ outline: deep
6
6
 
7
7
  [Cloudflare D1](https://developers.cloudflare.com/d1/) is Void's default SQLite database. Start with a [schema](../database.md#schema-definition); Void manages the database locally and when you deploy.
8
8
 
9
- ## Read replication
10
-
11
- Void enables [D1 read replication](https://developers.cloudflare.com/d1/best-practices/read-replication/) when it provisions a database. To enable it on an existing database, run `void deploy --platform cloudflare --provision`. Queries through `db` from `void/db` use a D1 session for each Void request, so reads can run near the request and later queries in that request see at least the same data. Framework handlers without Void's request scope start each database operation on the primary. `createDb` starts a replica-capable session for the binding you pass it.
12
-
13
- Replicas can lag between requests. Authentication session checks start on the primary so a revoked session is not accepted because of replica lag. If another read must start with the latest primary data, use `env.DB.withSession('first-primary')` from `cloudflare:workers`. Other query libraries and raw D1 calls need their own `withSession()` to use replicas; a bare D1 binding reads from the primary.
14
-
15
9
  ## Local Database
16
10
 
17
11
  `void dev` and local `void db` commands share the same database. If `cloudflare.d1_databases` in `void.config.ts` declares the D1 binding, Void uses its ID. A `preview_database_id` takes precedence during local development.
@@ -44,6 +38,12 @@ await db.batch([
44
38
  ]);
45
39
  ```
46
40
 
41
+ ## Read replication
42
+
43
+ Void enables [D1 read replication](https://developers.cloudflare.com/d1/best-practices/read-replication/) when it provisions a database. To enable it on an existing database, run `void deploy --platform cloudflare --provision`. Queries through `db` from `void/db` use a D1 session for each Void request, so reads can run near the request and later queries in that request see at least the same data. Framework handlers without Void's request scope start each database operation on the primary. `createDb` starts a replica-capable session for the binding you pass it.
44
+
45
+ Replicas can lag between requests. Authentication session checks start on the primary so a revoked session is not accepted because of replica lag. If another read must start with the latest primary data, use `env.DB.withSession('first-primary')` from `cloudflare:workers`. Other query libraries and raw D1 calls need their own `withSession()` to use replicas; a bare D1 binding reads from the primary.
46
+
47
47
  ## Alternative Query Layers
48
48
 
49
49
  Use `env.DB` from `cloudflare:workers` with another query library or the raw D1 API:
@@ -69,5 +69,5 @@ const { results } = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bin
69
69
  Void still handles binding inference and deployment regardless of which query layer you use.
70
70
 
71
71
  ::: warning ⚠️ Meta framework limitation
72
- Nuxt and SvelteKit do not support `cloudflare:workers` imports during local development. Use `void/db` or your framework's binding access API.
72
+ Nuxt and SvelteKit 2 do not support `cloudflare:workers` imports during local development. Use `void/db` or your framework's binding access API. SvelteKit 3 supports these imports.
73
73
  :::
@@ -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.
@@ -4,180 +4,114 @@ outline: deep
4
4
 
5
5
  # Deployment
6
6
 
7
- `void deploy` builds your app, provisions its resources, applies migrations, and deploys it to your own Cloudflare account or a Void platform run by your team.
7
+ `void deploy` builds your app, provisions resources, applies migrations, and deploys it. Choose your own Cloudflare account or a Void platform run by your team.
8
8
 
9
- You can [install a Void platform](./self-hosted-platform.md) for shared team deployments.
9
+ After a successful deployment, Void prints your deployment URL in a summary box with the details reported by your deployment target.
10
10
 
11
- ## Deployment Targets
11
+ ## Deploy to Cloudflare
12
12
 
13
- Both targets use the same application APIs. Their deployment features differ:
13
+ Choose Cloudflare during `void init`, or connect later:
14
14
 
15
- | Feature | Direct Cloudflare | Core self-hosted platform |
16
- | ------------------------------------------------ | -------------------------------- | --------------------------------------------- |
17
- | Static sites, SPAs, and native Pages SSR | Supported | Supported |
18
- | Routing rules, WebSockets, queues, cron, and ISR | Supported | Supported |
19
- | D1, KV, R2, and external SQL through Hyperdrive | Supported | Supported |
20
- | Workers AI and provider requests | Your account and gateway | Installation gateway and proxy |
21
- | Runtime logs | Live Cloudflare tail | Retained platform logs |
22
- | Application rollback | Worker versions | Retained platform deployments |
23
- | Typed Durable State | Supported | Unavailable |
24
- | Sandboxes | Requires Workers Paid and Docker | Requires Workers Paid on the platform account |
25
- | Custom application domains | Supported | Unavailable in the core installation |
26
- | Generated GitHub deployment workflow | Supported | Use your CI with a scoped developer token |
27
- | User dashboard and managed GitHub builds | Not required | Not included in a standard installation |
28
-
29
- Native apps can run on Workers Free within its quotas. A team platform requires Workers for Platforms and the [documented infrastructure](./platform/installation/domains.md#cloudflare-footprint). Rollback does not reverse database migrations.
30
-
31
- Native Void apps and static sites use Void routing rules. Framework deployments use their framework's routing and asset policies; direct deploy rejects unsupported Void rules.
32
-
33
- Use matching CLI and framework adapter versions. For a team platform, the available features depend on its installed version and configuration. Ask your administrator if a feature is unavailable or an upgrade is required.
34
-
35
- ## CLI
36
-
37
- ### First deploy
38
-
39
- ```bash
40
- void init # choose Cloudflare (default), Void, or Skip deployment setup
41
- void deploy # builds and deploys to the saved platform
15
+ ```sh
16
+ void connect --platform cloudflare
17
+ void deploy --platform cloudflare
42
18
  ```
43
19
 
44
- During setup, Void asks where you want to deploy:
45
-
46
- - **Cloudflare:** sign in through your browser and choose an account. Void saves the account in `void.config.ts` or `void.lock.json`.
47
- - **Void:** connect to a platform, sign in, and link or create a project.
48
- - **Skip:** set up deployment later with `void connect`.
49
-
50
- Your choice is saved in `.void/project.json`, so the next deploy is just `void deploy`.
51
-
52
- Already have a Cloudflare Worker and a root `wrangler.jsonc` or `wrangler.json`? Run `void deploy`. If no destination is selected, Void offers to link and deploy using the existing Worker and resources. Accept once to keep deploying to that site. See [Deploy an existing Worker](../integrations/cloudflare.md#deploy-an-existing-worker) for the first-deployment checks.
20
+ Void opens your browser to sign in and asks which account to use. Later deploys use the saved target, so you can run `void deploy`.
53
21
 
54
- To set up deployment later, use `void connect --platform cloudflare` or `void connect <platform-url>`.
55
-
56
- Owners can [share a platform project](./project-collaboration.md) with readers, collaborators, and project administrators. This does not apply to direct Cloudflare deployments.
57
-
58
- ### Deploy to another Void platform
59
-
60
- Connect to the additional platform once, then select it for the deployment:
61
-
62
- ```sh
63
- void connect https://second.example.com
64
- VOID_API_URL=https://second.example.com void deploy --platform void --project my-app
65
- ```
22
+ Native apps work on Workers Free within its quotas. [Sandboxes](./sandboxes.md#requirements) require Workers Paid and Containers access. See the [Cloudflare guide](../integrations/cloudflare.md) for configuration, token permissions, and features with additional requirements.
66
23
 
67
- Void uses `my-app` on that platform and offers to create it if needed. Your saved
68
- destination stays unchanged, including when a new project is created or deployment
69
- fails. The next plain `void deploy` still uses your original destination.
24
+ If you already have a Worker and a root `wrangler.jsonc` or `wrangler.json`, `void deploy` can link it and preserve its resources. See [Deploy an existing Worker](../integrations/cloudflare.md#deploy-an-existing-worker).
70
25
 
71
- Pass `--project` or `VOID_PROJECT` when selecting a different platform; project links
72
- belong to their original platform. Each platform has its own projects, secrets, and
73
- data. For an app without a saved destination, its first deployment still saves a link.
26
+ ## Prepare your app
74
27
 
75
28
  ### Migrations
76
29
 
77
- Deploy applies pending SQL migrations from `db/migrations/`. If your schema has changes without a migration, deploy stops. Run `void db generate`, review and commit the SQL, then deploy again. See the [Database guide](./database.md).
30
+ Deploy applies pending SQL migrations from `db/migrations/`. If your schema has changes without a migration, deploy stops:
78
31
 
79
- ### Flags
80
-
81
- ```bash
82
- void deploy [--platform <cloudflare|void>] [--project <name>] [--dir <path>] [--spa]
32
+ ```sh
33
+ void db generate
83
34
  ```
84
35
 
85
- | Flag | Purpose |
86
- | ------------------------------- | ------------------------------------------------- |
87
- | `--platform <cloudflare\|void>` | Override the saved deployment platform |
88
- | `--project <name>` | Target a specific Void project by slug |
89
- | `--dir <path>` | Deploy a pre-built static directory (skips build) |
90
- | `--spa` | Use SPA mode instead of SSG for static deploys |
91
-
92
- ### Project resolution
36
+ Review and commit the SQL, then deploy again. Rollback does not reverse database migrations. See [Database](./database.md) for the local workflow.
93
37
 
94
- For a Void platform deploy, the CLI chooses the project in this order:
95
-
96
- 1. `--project <name>` flag
97
- 2. `VOID_PROJECT` environment variable
98
- 3. Linked project in `.void/project.json`
38
+ ### Environment variables
99
39
 
100
- If none is set, Void asks you to choose a project. Direct Cloudflare deploys use the Worker and account saved in your Cloudflare config.
40
+ Declare values in `env.ts` and set production server secrets with `void secret put`. Deploy never uploads your local `.env`. Supply public `VITE_*` values through the build environment. See [Environment Variables](./env-vars.md).
101
41
 
102
- ### CI preparation
42
+ Keep the CLI and framework adapters on matching versions, and commit `void.config.ts` and `void.lock.json` when generated.
103
43
 
104
- For a Void platform, run `void connect "$VOID_API_URL" --no-login` in each fresh
105
- CI environment before deployment. Set `VOID_API_URL` to the platform you selected;
106
- there is no default hosted platform. The generated GitHub Actions workflow
107
- includes this connection step.
44
+ ## Deploy from GitHub {#github}
108
45
 
109
- If your CI pipeline runs typechecking or other static analysis before deploy, run `void prepare` after install to generate the `.void/` artifacts without booting Vite.
46
+ Generate a workflow for your saved target and package manager:
110
47
 
111
- ### Environment variables
48
+ ```sh
49
+ void init --github
50
+ ```
112
51
 
113
- | Variable | Purpose |
114
- | -------------- | -------------------------------- |
115
- | `VOID_TOKEN` | Auth token (for CI, skips OAuth) |
116
- | `VOID_PROJECT` | Project slug override |
52
+ For Cloudflare, add `CLOUDFLARE_API_TOKEN` to your repository secrets. The token needs access to the account and products your app uses. PostgreSQL and MySQL apps also need `DATABASE_URL`.
117
53
 
118
- ## GitHub
54
+ Follow the [Cloudflare CI setup](../integrations/cloudflare.md#deploying-from-ci) for Worker preview URLs and Cloudflare Access credentials.
119
55
 
120
- You can deploy from a GitHub repo on every push to `main`. Running `void init --github` generates `.github/workflows/void-deploy.yml` for the platform saved in `.void/project.json`.
56
+ ### Team platform CI
121
57
 
122
- For Cloudflare, add `CLOUDFLARE_API_TOKEN` to your repository secrets. The token needs access to the account and products your app uses. PostgreSQL and MySQL apps also need a `DATABASE_URL` secret.
58
+ For a core self-hosted platform, use your own workflow with a [project deploy token](./platform/installation/first-deployment.md). Set `VOID_TOKEN`, `VOID_API_URL`, and the project slug, then connect in each fresh CI environment:
123
59
 
124
- If your Worker has no version preview URL, set the `CLOUDFLARE_WORKERS_SUBDOMAIN` repository variable. If Cloudflare Access protects the Worker, also add `CF_ACCESS_CLIENT_ID` and `CF_ACCESS_CLIENT_SECRET` secrets so Void can check that a deployment is ready. See [Cloudflare deployment](../integrations/cloudflare.md#deploy-to-your-own-cloudflare-account) for the setup details.
60
+ ```sh
61
+ void connect "$VOID_API_URL" --no-login
62
+ void deploy --platform void --project "$VOID_PROJECT"
63
+ ```
125
64
 
126
- Void platforms with GitHub Actions support use short-lived GitHub OIDC credentials. You don't need to store a `VOID_TOKEN` for that workflow.
65
+ Platforms with the optional GitHub Actions integration use short-lived OIDC credentials. Authorize the repository with `void github connect <project> --repo <owner/repo> --executor github_actions`, set the repository's `VOID_API_URL`, and keep `permissions: id-token: write` in the generated workflow.
127
66
 
128
- ### Void GitHub App
67
+ If your platform supports managed GitHub builds, the GitHub App can deploy on push without a workflow file. See [GitHub commands](../reference/cli/github.md#github) to install the App and connect your repository.
129
68
 
130
- If your platform supports managed GitHub builds, the Void GitHub App can build and deploy your app when you push. This feature requires a platform with a configured GitHub App and build Containers; it isn't included in a core self-hosted installation.
69
+ If CI runs typechecking before deploy, run `void prepare` after installing dependencies to generate the `.void/` types.
131
70
 
132
- You don't need a deployment workflow file or a repository `VOID_TOKEN` for this path.
71
+ ## Deploy to a team platform
133
72
 
134
- With a linked project, connect the repository:
73
+ Use the API URL from your administrator. In a new or unlinked app:
135
74
 
136
75
  ```sh
137
- void github install
138
- void github connect --executor container
139
- void github status
76
+ void connect https://platform.example.com
77
+ void deploy --platform void --project my-app
140
78
  ```
141
79
 
142
- If your organization already installed the App, Void can join that installation during setup. Organization repositories require browser authorization.
80
+ Sign in and confirm project creation when asked. To use an existing project, run `void project link`. Later deploys use the saved destination.
143
81
 
144
- Push to the configured branch to deploy, then follow progress with `void build logs --follow`. See [GitHub commands](../reference/cli/github.md#github) for installation sharing and connection options.
82
+ Project owners can [share access](./project-collaboration.md). To install a platform for your team, start with [Install a Void Platform](./self-hosted-platform.md), which also compares the deployment features of both targets.
145
83
 
146
- ### GitHub Actions
84
+ ### Deploy to another Void platform
147
85
 
148
- Run `void init --github` to generate the workflow for your saved platform and package manager. On Void platforms with GitHub Actions support, authorize the repository once:
86
+ For a single deployment to a different platform, connect and name its project:
149
87
 
150
88
  ```sh
151
- void github connect <project> --repo <owner/repo> --executor github_actions
89
+ void connect https://second.example.com
90
+ VOID_API_URL=https://second.example.com void deploy --platform void --project my-app
152
91
  ```
153
92
 
154
- Set the repository's `VOID_API_URL` variable to your platform's API URL. Keep `permissions: id-token: write` in the workflow for [GitHub OIDC](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect). The workflow uses your linked project, or the `VOID_PROJECT` repository variable.
155
-
156
- Core self-hosted installations do not support this integration. Use your own CI workflow with a [project deploy token](./platform/installation/first-deployment.md).
157
-
158
- ## Other Targets
93
+ This keeps your saved destination. Each platform has its own projects, secrets, and data. See the [CLI reference](../reference/cli/deploy.md) for target selection and other flags.
159
94
 
160
- ### Your own Cloudflare account
95
+ ## Logs and rollback
161
96
 
162
- Select Cloudflare during setup, or run `void deploy --platform cloudflare`. See the [Cloudflare guide](../integrations/cloudflare.md) for configuration, CI credentials, and deployment limits.
97
+ Inspect your deployment, read runtime logs, or select a previous deployment:
163
98
 
164
- ### Node.js, Bun, and Deno
99
+ ```sh
100
+ void project status
101
+ void project logs
102
+ void project rollback
103
+ ```
165
104
 
166
- Set [`target`](../reference/config.md#target) in `void.config.ts` to build a standalone server for Node.js, Bun, or Deno. You can run the result on your own server or in a container.
105
+ Cloudflare logs use a live tail; team platforms retain log history. Before rolling back, check that the earlier code works with your current database schema. See [Project commands](../reference/cli/project.md).
167
106
 
168
- Deploy `dist/ssr` and `dist/client` together. See the target guide for startup commands.
107
+ ## Other runtimes
169
108
 
170
- These targets don't provide Cloudflare bindings such as D1, KV, R2, and Workers AI. See the [Node.js, Bun, and Deno guide](../integrations/nodejs-bun-deno.md) for the features available on each target.
109
+ Build a standalone server for [Node.js, Bun, or Deno](../integrations/nodejs-bun-deno.md) using the `target` setting. Deploy `dist/ssr` and `dist/client` together using your own hosting. Cloudflare bindings such as D1, KV, R2, and Workers AI are unavailable on these targets.
171
110
 
172
111
  ## Usage and execution limit pages
173
112
 
174
- Void platforms show a built-in page when an app exhausts its request allowance or an invocation exceeds its CPU limit. API requests and Pages action requests receive a structured HTTP 429 with `code: 'usage_limit'`, `resource`, `reason`, and `message`.
175
-
176
- To customize browser error pages, add either optional file to your app's public assets:
177
-
178
- - `public/usage-limit.html` for exhausted request allowances.
179
- - `public/execution-limit.html` for execution limits.
113
+ Team platforms return a structured HTTP 429 to API and Pages action requests when an app exceeds its request or CPU allowance. Browser requests get an error page.
180
114
 
181
- Use standalone HTML with inline CSS and data-URL images. These pages run with scripts, external assets and form submissions disabled, so they remain usable while application requests are blocked. Void serves them directly from the deployed assets; your Worker, loaders and database are not involved. A missing or unreadable custom page uses the built-in page.
115
+ To customize it, add `public/usage-limit.html` for request limits or `public/execution-limit.html` for execution limits. Use standalone HTML with inline CSS and data-URL images. Scripts, external assets, and form submissions are disabled. Missing custom pages use the built-in page.
182
116
 
183
- Responses are not cached. Once a streaming response has started, it cannot be replaced with an error page; clients should display stream failures and end their pending state. These platform pages apply to Void platform deployments. Direct Cloudflare runtime limits use Cloudflare's own response behavior.
117
+ Responses are not cached. A streaming response that has already started cannot be replaced with an error page; handle stream failures in your UI.
@@ -39,7 +39,7 @@ STRIPE_KEY=sk_test_example
39
39
  VITE_API_ORIGIN=http://localhost:5173
40
40
  ```
41
41
 
42
- Keep `.env` out of Git. Other dotenv files, including `.env.local`, `.env.production`, `.env.example`, and Wrangler’s `.dev.vars*`, are not supported. See [Environment migration](./env-migration.md) if your app uses them.
42
+ Keep `.env` out of Git. Other dotenv files, including `.env.local`, `.env.production`, `.env.example`, and Wrangler’s `.dev.vars*`, are not supported.
43
43
 
44
44
  Run the local check at any time:
45
45
 
@@ -105,7 +105,3 @@ Invalid-value diagnostics redact the supplied value by default. Set `VOID_ENV_UN
105
105
  ## Imports inside `env.ts`
106
106
 
107
107
  Use relative imports or package names inside `env.ts`. TypeScript path aliases and imports that need Vite transforms are not supported.
108
-
109
- ## Migrating from the multi-file model
110
-
111
- See [Environment migration](/guide/env-migration) for the breaking-change checklist.
@@ -4,34 +4,35 @@ outline: deep
4
4
 
5
5
  # What is Void?
6
6
 
7
- Void adds server routes, typed data access, and deployment to Vite apps. Native Void apps run on Cloudflare Workers. You can also use Void with a [supported framework](../integrations/frameworks/overview).
7
+ Void is a full-stack framework for Cloudflare Workers, powered by Vite. Build your UI with React, Vue, Svelte, or Solid, and write server code in the same project. Void connects routing, data access, and deployment while keeping types shared across your app.
8
8
 
9
- Start with the [Quickstart](./quickstart) to create an app, run it locally, and deploy it.
9
+ Start with the [Quickstart](./quickstart.md) to create and deploy an app. If you already use a meta framework, follow its [integration guide](../integrations/frameworks/overview.md).
10
10
 
11
- The CLI and build tools require Node.js 24.21.0 or later.
11
+ ## Server routes and pages
12
12
 
13
- ## Resources from your code
13
+ Files define routes. A handler in `routes/api/users.ts` serves `/api/users`; a component in `pages/users/index.tsx` renders `/users`.
14
+
15
+ Each page can have a companion `.server.ts` file. Its **loader** reads data and returns typed props to the component. Its **actions** handle forms and other mutations, then update the page. Server code stays on the server; components render there initially and run in the browser for navigation and interaction.
14
16
 
15
- Import `db` from `void/db`, and Void provides a local database and provisions one for deployment. The same pattern applies to KV, object storage, queues, and AI. Configure resources explicitly when you need to use existing infrastructure.
17
+ Use [Pages routing](./pages-routing/overview.md) for a full-stack UI, or keep your own client-side router and call [server routes](./server-routing.md) with [typed fetch](./typed-fetch.md). You can also build an API without a frontend.
18
+
19
+ ## Resources from your code
16
20
 
17
- Your Drizzle schema defines database types. Route handlers infer return types, and the [typed fetch client](./typed-fetch.md) checks calls from the frontend. Changing a field shows you which callers need to change.
21
+ Import `db` from `void/db` to query your database. Void provides a local database during development and provisions one when you deploy. Imports also enable resources such as [KV](./kv.md), [object storage](./storage.md), and [queues](./queues.md). Use configuration when you need to connect existing resources.
18
22
 
19
- Void connects these parts of your app:
23
+ Your Drizzle schema defines database types. Handlers infer response types, and typed fetch checks their callers. Page loaders and actions carry the same types into components and forms. Changing a field shows you which parts of the app need to change.
20
24
 
21
- - [Server routes](./server-routing.md) and [pages](./pages-routing/overview.md) for your app's backend and UI
22
- - [Database](./database.md), [KV](./kv.md), and [storage](./storage.md) that work locally and in production
23
- - [Standard Schema](https://standardschema.dev/) validation for runtime values and TypeScript types
24
- - `void deploy` to build, apply migrations, provision resources, and deploy
25
+ ## From local development to production
25
26
 
26
- ## Deployment targets
27
+ Vite runs your app locally with Cloudflare's runtime. `void deploy` builds it, provisions resources, applies database migrations, and publishes it. [Environment variables](./env-vars.md) use a schema, local values, and production secrets.
27
28
 
28
- Deploy to [your own Cloudflare account](../integrations/cloudflare.md#deploy-to-your-own-cloudflare-account) or to a [Void platform](./self-hosted-platform.md) run by your team. Both use the same SDK and CLI.
29
+ Deploy to [your own Cloudflare account](./deployment.md#deploy-to-cloudflare), or connect to a [Void platform](./self-hosted-platform.md) run by your team. Both use the same application APIs. A platform adds shared access, deployment history, and administration.
29
30
 
30
- [Static assets](./edge/static-assets) are cached at the edge. [Prerendering](./edge/prerendering) builds pages ahead of time, while [incremental revalidation](./edge/revalidation) caches pages rendered on demand. Database, storage, secrets, and deployment commands are available through Void.
31
+ Void also deploys [existing frameworks and static sites](./app-types.md). Your framework keeps its routing and rendering; Void handles resources and deployment.
31
32
 
32
33
  ## Next steps
33
34
 
34
- - [Quickstart](./quickstart): get a running app in minutes
35
- - [Server Routing](./server-routing): dynamic params, middleware, and validation
36
- - [Pages Routing](./pages-routing/overview): full-stack pages with server loaders and actions
37
- - [Supported App Types](./app-types): full-stack, meta-framework, and static site modes
35
+ - [Quickstart](./quickstart.md): create your first app
36
+ - [Pages Routing](./pages-routing/overview.md): loaders, layouts, and forms
37
+ - [Database](./database.md): schemas, queries, and migrations
38
+ - [Deployment](./deployment.md): Cloudflare, team platforms, and CI
@@ -27,8 +27,6 @@ await kv.put('user:123', { name: 'Alice', role: 'admin' });
27
27
 
28
28
  Values, including strings, are JSON-serialized. Reads preserve their types, so a string such as `"123"` or `"null"` comes back as a string.
29
29
 
30
- If an older Void version stored a JSON-looking string as raw text, rewrite that key with `kv.put(key, originalString)`. Existing stored values keep their previous read behavior; Void cannot distinguish an old raw string such as `"123"` from a stored number. Use the original string or read its raw text through `c.env.KV.get(key)` when rewriting it.
31
-
32
30
  Add a TTL (in seconds) or absolute expiration (Unix timestamp):
33
31
 
34
32
  ```ts
@@ -22,6 +22,8 @@ export const live = defineLiveStream({
22
22
  });
23
23
  ```
24
24
 
25
+ 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.
26
+
25
27
  Expose it from a normal route:
26
28
 
27
29
  ```ts
@@ -29,57 +29,12 @@ export const action = defineHandler.withValidator({
29
29
  | Nothing (void) | Re-runs the loader, page re-renders with fresh props. |
30
30
  | `c.redirect('/path')` | Navigates to another page. |
31
31
 
32
- ## Named Actions
33
-
34
- When a page needs multiple mutations, such as updating and deleting a user on the same page, export `actions` (plural) instead of `action`:
35
-
36
- ```ts
37
- // pages/users/[id].server.ts
38
- import { defineHandler } from 'void';
39
- import { db, eq } from 'void/db';
40
- import { users, updateUserSchema } from '@schema';
41
-
42
- export const actions = {
43
- update: defineHandler.withValidator({
44
- body: updateUserSchema,
45
- })(async (c, { body }) => {
46
- await db
47
- .update(users)
48
- .set(body)
49
- .where(eq(users.id, Number(c.req.param('id'))));
50
- }),
51
-
52
- delete: defineHandler(async (c) => {
53
- await db.delete(users).where(eq(users.id, Number(c.req.param('id'))));
54
- }),
55
- };
56
- ```
57
-
58
- Named actions are dispatched via a `?actionName` suffix on the URL (e.g. `/users/42?update`). The client primitives `useForm` and `action()` handle this automatically.
59
-
60
- Use `action` or `actions` in a file, not both.
61
-
62
- You can also define an `actions.default` key for the action that runs when no name is specified (i.e. a bare POST to the page URL):
63
-
64
- ```ts
65
- export const actions = {
66
- default: defineHandler(async (c) => {
67
- // runs on POST /users/edit (no ?suffix)
68
- }),
69
- delete: defineHandler(async (c) => {
70
- // runs on POST /users/edit?delete
71
- }),
72
- };
73
- ```
74
-
75
32
  ## Form
76
33
 
77
- Pair `Form` with `useForm` for automatic error presentation. It preserves controlled input on failure, announces validation and action errors, and displays the message from structured quota responses. Known retry deadlines prevent premature resubmission without locking the fields. It never retries a submission automatically.
34
+ Pair `Form` with `useForm` to submit values and display validation and action errors. Inputs stay editable when a submission fails.
78
35
 
79
36
  Import `Form` from your adapter. Pass `form` and, optionally, `method="put"`, `"patch"`, or `"delete"`; the default is `"post"`. Use `renderError` for a custom action-error renderer (a snippet in Svelte).
80
37
 
81
- On the server, use `limit.response({ message: 'AI is temporarily unavailable. Your draft is still editable.' })` in an AI or Sandbox operation's required `limited` handler. It supplies the status and structured metadata consumed by `Form`.
82
-
83
38
  ## `useForm`
84
39
 
85
40
  `useForm` handles submissions, loading state, and validation errors. URLs, form values, and error fields are typed from your action.
@@ -216,6 +171,49 @@ const form = useForm('/users/:id', { name: '' }, { params: { id: '42' } });
216
171
  <Form form={form} method="put">{/* submits to /users/42 */}</Form>
217
172
  ```
218
173
 
174
+ ## Named Actions
175
+
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
+
178
+ ```ts
179
+ // pages/users/[id].server.ts
180
+ import { defineHandler } from 'void';
181
+ import { db, eq } from 'void/db';
182
+ import { users, updateUserSchema } from '@schema';
183
+
184
+ export const actions = {
185
+ update: defineHandler.withValidator({
186
+ body: updateUserSchema,
187
+ })(async (c, { body }) => {
188
+ await db
189
+ .update(users)
190
+ .set(body)
191
+ .where(eq(users.id, Number(c.req.param('id'))));
192
+ }),
193
+
194
+ delete: defineHandler(async (c) => {
195
+ await db.delete(users).where(eq(users.id, Number(c.req.param('id'))));
196
+ }),
197
+ };
198
+ ```
199
+
200
+ Named actions are dispatched via a `?actionName` suffix on the URL (e.g. `/users/42?update`). The client primitives `useForm` and `action()` handle this automatically.
201
+
202
+ Use `action` or `actions` in a file, not both.
203
+
204
+ You can also define an `actions.default` key for the action that runs when no name is specified (i.e. a bare POST to the page URL):
205
+
206
+ ```ts
207
+ export const actions = {
208
+ default: defineHandler(async (c) => {
209
+ // runs on POST /users/edit (no ?suffix)
210
+ }),
211
+ delete: defineHandler(async (c) => {
212
+ // runs on POST /users/edit?delete
213
+ }),
214
+ };
215
+ ```
216
+
219
217
  ### Named Actions with `useForm`
220
218
 
221
219
  When a page exports named actions, append `?actionName` to the URL:
@@ -258,7 +256,7 @@ Authentication errors (`401`, `403`), server errors (`500`, `502`), and unexpect
258
256
  Actions can throw `ValidationError` for custom validation logic:
259
257
 
260
258
  ```ts
261
- import { defineHandler, ValidationError } from 'void';
259
+ import { defineHandler, ValidationError } from 'void/handler';
262
260
 
263
261
  export const action = defineHandler(async (c) => {
264
262
  const body = await c.req.json();
@@ -269,6 +267,10 @@ export const action = defineHandler(async (c) => {
269
267
  });
270
268
  ```
271
269
 
270
+ ### Usage limits
271
+
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
+
272
274
  ## Authentication and origin checks
273
275
 
274
276
  Browser submissions must come from the same origin as the page, including its scheme, hostname, and port. Void rejects cross-origin page actions with `403 Forbidden`. Server clients can call actions without browser origin headers; actions still need authentication and authorization for protected data.
@@ -225,9 +225,9 @@ export default function PostPage() {
225
225
 
226
226
  ## Scroll Restoration
227
227
 
228
- Client-side navigation manages scroll position automatically:
228
+ Void manages scroll position for Pages automatically:
229
229
 
230
- - **Back/forward navigation** restores the exact scroll position you were at before navigating away.
230
+ - **Back/forward navigation and reloads** restore the saved scroll position.
231
231
  - **New navigation** scrolls to the top of the page.
232
232
  - **Hash links** (`/docs#api`) scroll to the target element. Same-page hash links (`#section`) skip the server fetch entirely.
233
233
 
@@ -144,5 +144,3 @@ Run recovery with the same platform version that created the persisted intent.
144
144
  `applied` continues without replaying the write; `not-applied` permits that exact
145
145
  step to retry. Secret rotation keeps its staged generation, and removal resumes
146
146
  from the unresolved cleanup step. Age alone never authorizes a retry.
147
-
148
- Register administrator domains only after a platform upgrade has completed, and remove them before rolling the platform back to a version that predates this feature: an earlier runtime cannot use the platform credential for them and does not distinguish administrator-registered domains from owner-registered ones.
@@ -51,28 +51,6 @@ Following waits for the final logs before stopping, including diagnostics writte
51
51
 
52
52
  ## Checking the Platform
53
53
 
54
- ### Migrating older deployment assets
55
-
56
- After upgrading the platform, inspect every page of its asset inventory:
57
-
58
- ```sh
59
- void platform system asset-migrations --page 1 --limit 100
60
- void platform deployment migrate-assets <deployment-id> --plan
61
- void platform deployment migrate-assets <deployment-id> --yes
62
- ```
63
-
64
- Migrate every deployment with a nonzero `needsMigration` count, including retained
65
- rollback targets and stored Worker bundles. Investigate entries marked `invalid`
66
- before continuing. The command verifies each copied asset, preserves originals,
67
- and applies successive batches. If interrupted, inspect the result and resume
68
- with the last successful `nextCursor` using `--cursor`; restarting without a
69
- cursor also verifies and reuses completed copies.
70
-
71
- Run the inventory again across all pages when finished. Existing applications
72
- continue serving during this migration, and the provider asset mirror remains.
73
- Legacy reads remain available while older platform runtimes and cached manifests
74
- can still reference original assets.
75
-
76
54
  ### Health checks
77
55
 
78
56
  Use the overview to see recent activity, or run a health check to test the platform's services and database:
@@ -88,6 +66,17 @@ The dashboard’s **System Status** page refreshes health checks every 30 second
88
66
 
89
67
  Use `void platform upgrade`, `repair`, `disable`, and `enable` to maintain your platform. See [platform maintenance](/guide/platform/installation/maintenance#resume-repair-recover-and-upgrade).
90
68
 
69
+ Upgrades and rollbacks run through the CLI; watching their progress is optional.
70
+ During maintenance, navigating the admin UI automatically redirects to
71
+ `/admin/status`. You can also open that page directly on the API origin. It
72
+ refreshes automatically and shows saved progress and the API's traffic state
73
+ while the API and authentication database remain reachable. If a status refresh
74
+ fails, the last confirmed snapshot is marked as stale. Normal administrator
75
+ changes stay paused during maintenance.
76
+ If the CLI reports an error or a waiting state, follow its recovery instructions
77
+ to resume the operation. See
78
+ [Follow upgrade and rollback progress](/guide/platform/installation/maintenance#follow-upgrade-and-rollback-progress).
79
+
91
80
  For disaster recovery, keep a matching PostgreSQL backup, provider data backups, installation identity and complete project-encryption keyring, and immutable runtime artifacts. Keep traffic disabled while restoring data, run `discover` to reconstruct verified local metadata, and review `repair --plan` before recreating missing installer-owned infrastructure. Neither command restores backed-up data. See [Prepare for disaster recovery](/guide/platform/installation/maintenance#prepare-for-disaster-recovery) for the full sequence.
92
81
 
93
82
  ## Using Scripts