void 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/README.md +5 -1
  2. package/dist/{account-cmd-CjyxcGkM.mjs → account-cmd-C84Ee8cO.mjs} +4 -4
  3. package/dist/{scan-ClYmX3sa.mjs → application-analysis-BVsVqc11.mjs} +135 -18
  4. package/dist/application-routing-B7QEjqSR.mjs +27 -0
  5. package/dist/{auth-CZuiVsFh.mjs → auth-CCjt0hcq.mjs} +1 -1
  6. package/dist/{auth-DLNN0D3Z.mjs → auth-CSkdO2Bb.mjs} +4 -47
  7. package/dist/{auth-link-BLO4ptzo.mjs → auth-link-CioEg6uY.mjs} +4 -4
  8. package/dist/{auth-router-ZUg9MF_U.mjs → auth-router-BsR981d4.mjs} +4 -4
  9. package/dist/{better-auth-shared-rsBGBvWJ.mjs → better-auth-shared-hy6RPh9W.mjs} +13 -2
  10. package/dist/{build-cmd-BxR5FROK.mjs → build-cmd-LzvNJORH.mjs} +17 -5
  11. package/dist/{cache-D0sWhgKI.mjs → cache-C4MvnrMH.mjs} +2 -2
  12. package/dist/{cancel-deploy-D4NUqFbi.mjs → cancel-deploy-abUxpP2n.mjs} +2 -2
  13. package/dist/{cf-build-output-BJ6yGEIS.mjs → cf-build-output-BPqOT964.mjs} +2 -1
  14. package/dist/cf-build-output-_0HNWysu.mjs +2 -0
  15. package/dist/cli/cli.mjs +80 -455
  16. package/dist/cli/{cf-compat.mjs → cloudflare-operation-process.mjs} +442 -421
  17. package/dist/cli/env-schema-probe.d.mts +2 -1
  18. package/dist/cli/env-schema-probe.mjs +3 -3
  19. package/dist/client-Cu7jWiF1.mjs +2 -0
  20. package/dist/{client-BTZ3XkrB.mjs → client-RV8NVeB8.mjs} +140 -21
  21. package/dist/{cloudflare-auth-DQkqoMYa.mjs → cloudflare-auth-Qdc7tw8F.mjs} +41 -38
  22. package/dist/{cloudflare-cmd-BKlfGHAy.mjs → cloudflare-cmd-BcrVyTmJ.mjs} +5 -5
  23. package/dist/cloudflare-config-Bktvwtpf.mjs +182 -0
  24. package/dist/{cloudflare-connect-Ctmrw579.mjs → cloudflare-connect-B8uPZ4nx.mjs} +3 -3
  25. package/dist/{cloudflare-operations-BT6OWFBk.mjs → cloudflare-operations-AiWashgg.mjs} +1 -1
  26. package/dist/{cloudflare-operations-B9tzgjmf.mjs → cloudflare-operations-fxHb-byx.mjs} +116 -172
  27. package/dist/{preset-UHj9ARyP.mjs → cloudflare-process-B-wekeR6.mjs} +93 -6
  28. package/dist/{config-VavjpDnp.d.mts → config-BMHb8RCj.d.mts} +1 -0
  29. package/dist/config-C_XRIPx2.mjs +89 -0
  30. package/dist/config-entry.d.mts +1 -1
  31. package/dist/{connect-Dg-WkW-C.mjs → connect-Bd4kJd9U.mjs} +5 -5
  32. package/dist/{create-project-DdoqwFLF.mjs → create-project-Boczwj5r.mjs} +1 -1
  33. package/dist/{create-project-l9J7pbtm.mjs → create-project-bMf6ffLZ.mjs} +2 -2
  34. package/dist/{db-gp2sCXyN.mjs → db-8uG64XLl.mjs} +21 -21
  35. package/dist/{delete-TTedbH6B.mjs → delete-NvbiyeJf.mjs} +2 -2
  36. package/dist/{deploy-CH-o2CaZ.mjs → deploy-DzAIwqNU.mjs} +908 -582
  37. package/dist/{deploy-B19L2Bra.mjs → deploy-bjXdFCtn.mjs} +1 -1
  38. package/dist/{domain-JRF59P_r.mjs → domain-y5Tvydvo.mjs} +3 -3
  39. package/dist/{email-Bo6G9LOZ.mjs → email-B3umsW75.mjs} +13 -28
  40. package/dist/{env-BYOrWQnv.mjs → env-BS6qYHDb.mjs} +4 -4
  41. package/dist/{env-public-BxU_0yTL.d.mts → env-public-BX_r8HR6.d.mts} +1 -1
  42. package/dist/{env-validation-BB4GkLxn.mjs → env-validation-BPn7vk-V.mjs} +18 -71
  43. package/dist/{env-validation-BXge7uyK.mjs → env-validation-DbTg7-ar.mjs} +1 -1
  44. package/dist/{fetch-CXDChK7B.mjs → fetch-BIZJh7vR.mjs} +2 -2
  45. package/dist/{fetch-stream-AOByI7Ki.mjs → fetch-stream-IvCYKyQL.mjs} +24 -4
  46. package/dist/{gen-sCtlCOdA.mjs → gen-B580--vC.mjs} +2 -2
  47. package/dist/gen-BVaUUumi.mjs +2 -0
  48. package/dist/{github-cmd-0-7PexDq.mjs → github-cmd-v45BdfKX.mjs} +2 -2
  49. package/dist/{handler-HEcZsaij.d.mts → handler-CZ4nAylQ.d.mts} +1 -1
  50. package/dist/help-DofyZuY7.mjs +2 -0
  51. package/dist/{help-GKtwl07I.mjs → help-daGKjXGk.mjs} +229 -747
  52. package/dist/index.d.mts +1 -1
  53. package/dist/index.mjs +288 -1502
  54. package/dist/info-B97bTX9N.mjs +113 -0
  55. package/dist/{init-FZx3Elvz.mjs → init-Bvy7zrBo.mjs} +46 -15
  56. package/dist/limits-Bq5LG8Id.d.mts +27 -0
  57. package/dist/limits-Cjuk2VPm.mjs +68 -0
  58. package/dist/{link-BrQfb_CU.mjs → link-CDqqCjFl.mjs} +3 -3
  59. package/dist/{list-D44jmIAM.mjs → list-CZj0dzKY.mjs} +3 -3
  60. package/dist/{live-CKJlvlNp.d.mts → live-Chw1eIMv.d.mts} +1 -1
  61. package/dist/{local-d1-Bg9OzEEO.mjs → local-d1-2CMnpuW_.mjs} +2 -2
  62. package/dist/login-DYt_An22.mjs +2 -0
  63. package/dist/{login-CAsAsQ-_.mjs → login-UZKFM_u7.mjs} +3 -3
  64. package/dist/{logs-BjKvFnVM.mjs → logs-CUZ6t9t3.mjs} +3 -3
  65. package/dist/migrate-8_2u55MD.mjs +2 -0
  66. package/dist/{migrate-BPITvDJN.mjs → migrate-DHul7PRV.mjs} +2 -1
  67. package/dist/{node-Dt7z256D.mjs → node-BkyRWRx8.mjs} +1 -1
  68. package/dist/operator-args-CLgKGlwU.mjs +690 -0
  69. package/dist/{operator-cmd-BK5CCiT9.mjs → operator-cmd-DBA6dl0m.mjs} +68 -22
  70. package/dist/pages/client.d.mts +34 -2
  71. package/dist/pages/client.mjs +59 -3
  72. package/dist/pages/index.d.mts +1 -1
  73. package/dist/pages/index.mjs +1 -1
  74. package/dist/pages/islands-plugin.mjs +1 -1
  75. package/dist/pages/protocol.d.mts +2 -2
  76. package/dist/pages/protocol.mjs +2 -308
  77. package/dist/{parse-filename-DioPHiR9.mjs → parse-filename-CUbj-1MP.mjs} +35 -1
  78. package/dist/plan-D1Q5rf-r.mjs +2 -0
  79. package/dist/plan-NPpwZ_kc.mjs +58 -0
  80. package/dist/platform-args-BJdRtlLq.mjs +506 -0
  81. package/dist/platform-args-D3RXyR6h.mjs +2 -0
  82. package/dist/{platform-auth-config-BlN8xTdD.mjs → platform-auth-config-B56E9YP1.mjs} +3 -3
  83. package/dist/{platform-auth-protection-7d5aV2Jg.mjs → platform-auth-protection-BDWO_aER.mjs} +2 -2
  84. package/dist/{platform-auth-recovery-Dxij8ZbR.mjs → platform-auth-recovery-1gg4CSRe.mjs} +3 -3
  85. package/dist/{platform-cmd-E0FuL212.mjs → platform-cmd-Bt-1w6pY.mjs} +1 -1
  86. package/dist/{platform-cmd-B3hwKrFK.mjs → platform-cmd-DMcQStSc.mjs} +24 -3
  87. package/dist/{platform-domain-D6Xcy9ZX.mjs → platform-domain-BNkcz0OB.mjs} +2 -2
  88. package/dist/{platform-lifecycle-k0E0xoxx.mjs → platform-lifecycle-9OyBALhH.mjs} +327 -215
  89. package/dist/{platform-lifecycle-DMh_qrry.mjs → platform-lifecycle-BE6C_jxh.mjs} +1 -1
  90. package/dist/{platform-management-Brz_BDiT.mjs → platform-management-CjwLVQwN.mjs} +6 -3
  91. package/dist/{platform-management-DOtss0BN.mjs → platform-management-CnyTdcWX.mjs} +1 -1
  92. package/dist/platform-plans-config-BNGKGr4P.mjs +359 -0
  93. package/dist/{platform-recovery-BvtcXON5.mjs → platform-recovery-D6KSpuFm.mjs} +2 -2
  94. package/dist/{plugin-inference-BMfKRSqE.mjs → plugin-inference-CXWnn79A.mjs} +174 -64
  95. package/dist/{prepare-C-6YZyyg.mjs → prepare-B5Mkic5u.mjs} +1 -1
  96. package/dist/{prepare-CRhJbVrG.mjs → prepare-DOsL0CC9.mjs} +4 -20
  97. package/dist/prepare-cgvDMtSb.mjs +2 -0
  98. package/dist/{project-cmd-BZLCqOqw.mjs → project-cmd-CNzrqvBv.mjs} +16 -16
  99. package/dist/{project-team-BvgM0WoX.mjs → project-team-DQOWPfQT.mjs} +2 -2
  100. package/dist/{project-token-l5DqK6Az.mjs → project-token-CTDYU0Cc.mjs} +2 -2
  101. package/dist/{project-zero-trust-v6Mvpp8y.mjs → project-zero-trust-BNAkW-_0.mjs} +2 -2
  102. package/dist/{protocol-BBa6cstI.d.mts → protocol-CjF_iI9X.d.mts} +2 -2
  103. package/dist/protocol-U7bfjHmA.mjs +331 -0
  104. package/dist/{provision-Fr9pRqce.mjs → provision-C4ORE7G7.mjs} +1 -1
  105. package/dist/{provision-C4IGqkBf.mjs → provision-CJFgTZY8.mjs} +89 -198
  106. package/dist/{requests-DN-BbNiM.mjs → requests-MhxYnau8.mjs} +2 -2
  107. package/dist/resource-name-C7LVpcRm.mjs +11 -0
  108. package/dist/{rollback-D7rzSaZM.mjs → rollback-CJ6iSDoU.mjs} +3 -3
  109. package/dist/route-url-CG7U-cRN.mjs +15 -0
  110. package/dist/{runner-B8wXwWlo.mjs → runner-CXA9Fh8h.mjs} +1 -1
  111. package/dist/{runner-p-dMs2UN.mjs → runner-Ol0TNjk6.mjs} +2 -2
  112. package/dist/runtime/ai.d.mts +12 -8
  113. package/dist/runtime/ai.mjs +84 -17
  114. package/dist/runtime/better-auth-mysql.mjs +1 -1
  115. package/dist/runtime/better-auth-pg.mjs +1 -1
  116. package/dist/runtime/better-auth.mjs +1 -1
  117. package/dist/runtime/client-react.mjs +2 -2
  118. package/dist/runtime/client-solid.mjs +2 -2
  119. package/dist/runtime/client-svelte.mjs +2 -2
  120. package/dist/runtime/client-vue.mjs +2 -2
  121. package/dist/runtime/client.mjs +2 -2
  122. package/dist/runtime/durable.d.mts +3 -1
  123. package/dist/runtime/durable.mjs +4 -1
  124. package/dist/runtime/email/testing.mjs +1 -1
  125. package/dist/runtime/env-public.d.mts +1 -1
  126. package/dist/runtime/fetch-stream.mjs +1 -1
  127. package/dist/runtime/fetch.mjs +1 -1
  128. package/dist/runtime/handler.d.mts +1 -1
  129. package/dist/runtime/kv.mjs +0 -1
  130. package/dist/runtime/limits.d.mts +2 -0
  131. package/dist/runtime/limits.mjs +2 -0
  132. package/dist/runtime/live-client.d.mts +1 -1
  133. package/dist/runtime/live-server.mjs +25 -16
  134. package/dist/runtime/live.d.mts +1 -1
  135. package/dist/runtime/migration-handler.mjs +62 -42
  136. package/dist/runtime/route-url.d.mts +4 -0
  137. package/dist/runtime/route-url.mjs +2 -0
  138. package/dist/runtime/routing.d.mts +180 -0
  139. package/dist/runtime/routing.mjs +1082 -0
  140. package/dist/runtime/sandbox-container.d.mts +1 -1
  141. package/dist/runtime/sandbox-container.mjs +1 -1
  142. package/dist/runtime/sandbox.d.mts +3 -3
  143. package/dist/runtime/sandbox.mjs +75 -45
  144. package/dist/runtime/sse.mjs +1 -1
  145. package/dist/runtime/validator.d.mts +1 -1
  146. package/dist/runtime/ws-server.d.mts +4 -2
  147. package/dist/runtime/ws-server.mjs +27 -2
  148. package/dist/runtime/ws.d.mts +2 -2
  149. package/dist/runtime/ws.mjs +8 -6
  150. package/dist/{sandbox-qpNBT8a3.d.mts → sandbox-XZAqzFlG.d.mts} +11 -8
  151. package/dist/{sandbox-container-DdNEBfCc.d.mts → sandbox-container-Bo0eiFKz.d.mts} +2 -1
  152. package/dist/{sandbox-container-4fdqLnyb.mjs → sandbox-container-C6ItmVuN.mjs} +4 -2
  153. package/dist/{scan-4tfN-PSn.mjs → scan-C7okrLyM.mjs} +4 -35
  154. package/dist/{secret-BOtOl_cb.mjs → secret-Dli5fP0B.mjs} +4 -4
  155. package/dist/{sse-BaC1jXko.mjs → sse-CQNaDFFV.mjs} +6 -3
  156. package/dist/validate-Dq_L3s0S.mjs +2 -0
  157. package/dist/{validate-CIUwFpjB.mjs → validate-ctOrgiS3.mjs} +2 -1
  158. package/dist/{wrangler-DQF1vKyf.mjs → wrangler-7K-bW_DL.mjs} +8 -239
  159. package/dist/{ws-BoY7vQML.d.mts → ws-CL1w7GXU.d.mts} +13 -2
  160. package/package.json +15 -8
  161. package/skills/migrate-vite-cloudflare-to-void/SKILL.md +34 -157
  162. package/skills/void/SKILL.md +50 -135
  163. package/skills/void/docs/guide/ai.md +94 -84
  164. package/skills/void/docs/guide/app-types.md +3 -32
  165. package/skills/void/docs/guide/auth.md +12 -116
  166. package/skills/void/docs/guide/database/d1.md +9 -54
  167. package/skills/void/docs/guide/database/mysql.md +1 -1
  168. package/skills/void/docs/guide/database/postgresql.md +5 -26
  169. package/skills/void/docs/guide/database.md +23 -75
  170. package/skills/void/docs/guide/deployment.md +27 -113
  171. package/skills/void/docs/guide/durable-state.md +43 -18
  172. package/skills/void/docs/guide/edge/headers.md +3 -47
  173. package/skills/void/docs/guide/edge/prerendering.md +5 -20
  174. package/skills/void/docs/guide/edge/redirects.md +11 -64
  175. package/skills/void/docs/guide/edge/revalidation.md +5 -18
  176. package/skills/void/docs/guide/edge/rewrites.md +56 -284
  177. package/skills/void/docs/guide/edge/static-assets.md +22 -72
  178. package/skills/void/docs/guide/email/domains.md +112 -0
  179. package/skills/void/docs/guide/email/receiving.md +139 -0
  180. package/skills/void/docs/guide/email/sending.md +231 -0
  181. package/skills/void/docs/guide/email.md +13 -619
  182. package/skills/void/docs/guide/env-migration.md +11 -11
  183. package/skills/void/docs/guide/env-vars.md +9 -29
  184. package/skills/void/docs/guide/index.md +0 -15
  185. package/skills/void/docs/guide/jobs.md +3 -18
  186. package/skills/void/docs/guide/kv.md +5 -11
  187. package/skills/void/docs/guide/live.md +5 -56
  188. package/skills/void/docs/guide/pages-routing/actions-and-forms.md +78 -125
  189. package/skills/void/docs/guide/pages-routing/head.md +10 -10
  190. package/skills/void/docs/guide/pages-routing/islands.md +6 -36
  191. package/skills/void/docs/guide/pages-routing/layouts.md +6 -128
  192. package/skills/void/docs/guide/pages-routing/loaders.md +3 -19
  193. package/skills/void/docs/guide/pages-routing/markdown.md +13 -171
  194. package/skills/void/docs/guide/pages-routing/overview.md +7 -17
  195. package/skills/void/docs/guide/pages-routing/view-transitions.md +1 -1
  196. package/skills/void/docs/guide/platform/administration/access.md +1 -4
  197. package/skills/void/docs/guide/platform/administration/email.md +35 -8
  198. package/skills/void/docs/guide/platform/administration/operations.md +26 -4
  199. package/skills/void/docs/guide/platform/administration/plans.md +126 -0
  200. package/skills/void/docs/guide/platform/administration/projects.md +5 -2
  201. package/skills/void/docs/guide/platform/administration/zero-trust.md +23 -152
  202. package/skills/void/docs/guide/platform/development/runtime.md +3 -13
  203. package/skills/void/docs/guide/platform/development/schema-ci.md +0 -58
  204. package/skills/void/docs/guide/platform/installation/credentials.md +6 -4
  205. package/skills/void/docs/guide/platform/installation/domains.md +30 -2
  206. package/skills/void/docs/guide/platform/installation/first-deployment.md +2 -0
  207. package/skills/void/docs/guide/platform/installation/maintenance.md +3 -1
  208. package/skills/void/docs/guide/platform/installation/prerequisites.md +20 -15
  209. package/skills/void/docs/guide/platform/installation/setup.md +9 -5
  210. package/skills/void/docs/guide/platform-administration.md +1 -0
  211. package/skills/void/docs/guide/queues.md +7 -9
  212. package/skills/void/docs/guide/quickstart.md +38 -37
  213. package/skills/void/docs/guide/remote-dev.md +4 -9
  214. package/skills/void/docs/guide/sandboxes.md +29 -21
  215. package/skills/void/docs/guide/server-routing.md +9 -72
  216. package/skills/void/docs/guide/sse.md +4 -18
  217. package/skills/void/docs/guide/ssg.md +3 -15
  218. package/skills/void/docs/guide/ssr.md +14 -62
  219. package/skills/void/docs/guide/storage.md +9 -4
  220. package/skills/void/docs/guide/type-safety.md +3 -14
  221. package/skills/void/docs/guide/typed-fetch.md +3 -7
  222. package/skills/void/docs/guide/websockets.md +68 -40
  223. package/skills/void/docs/integrations/agents.md +3 -3
  224. package/skills/void/docs/integrations/cloudflare.md +85 -316
  225. package/skills/void/docs/integrations/frameworks/analog.md +5 -64
  226. package/skills/void/docs/integrations/frameworks/astro.md +4 -73
  227. package/skills/void/docs/integrations/frameworks/nuxt.md +5 -62
  228. package/skills/void/docs/integrations/frameworks/overview.md +11 -54
  229. package/skills/void/docs/integrations/frameworks/react-router.md +5 -60
  230. package/skills/void/docs/integrations/frameworks/sveltekit.md +6 -65
  231. package/skills/void/docs/integrations/frameworks/tanstack-start.md +4 -62
  232. package/skills/void/docs/integrations/nodejs-bun-deno.md +5 -69
  233. package/skills/void/docs/reference/api/auth.md +156 -0
  234. package/skills/void/docs/reference/api/client.md +87 -0
  235. package/skills/void/docs/reference/api/database.md +95 -0
  236. package/skills/void/docs/reference/api/durable.md +46 -0
  237. package/skills/void/docs/reference/api/env.md +50 -0
  238. package/skills/void/docs/reference/api/handlers.md +254 -0
  239. package/skills/void/docs/reference/api/pages.md +241 -0
  240. package/skills/void/docs/reference/api/plugin.md +39 -0
  241. package/skills/void/docs/reference/api/resources.md +109 -0
  242. package/skills/void/docs/reference/api/rewrites.md +76 -0
  243. package/skills/void/docs/reference/api/types.md +92 -0
  244. package/skills/void/docs/reference/api.md +56 -1218
  245. package/skills/void/docs/reference/cli/auth.md +88 -0
  246. package/skills/void/docs/reference/cli/database.md +128 -0
  247. package/skills/void/docs/reference/cli/deploy.md +85 -0
  248. package/skills/void/docs/reference/cli/domains.md +41 -0
  249. package/skills/void/docs/reference/cli/email.md +129 -0
  250. package/skills/void/docs/reference/cli/generate.md +116 -0
  251. package/skills/void/docs/reference/cli/github.md +189 -0
  252. package/skills/void/docs/reference/cli/platform-config.md +92 -0
  253. package/skills/void/docs/reference/cli/platform-email.md +81 -0
  254. package/skills/void/docs/reference/cli/platform-installation.md +127 -0
  255. package/skills/void/docs/reference/cli/platform-operations.md +90 -0
  256. package/skills/void/docs/reference/cli/platform-users.md +89 -0
  257. package/skills/void/docs/reference/cli/platform-zero-trust.md +45 -0
  258. package/skills/void/docs/reference/cli/platform.md +70 -0
  259. package/skills/void/docs/reference/cli/project.md +214 -0
  260. package/skills/void/docs/reference/cli/secrets.md +76 -0
  261. package/skills/void/docs/reference/cli/setup.md +70 -0
  262. package/skills/void/docs/reference/cli.md +32 -1682
  263. package/skills/void/docs/reference/config.md +12 -18
  264. package/skills/void/docs/reference/resource-inference.md +3 -58
  265. package/skills/void/docs/reference/structure.md +14 -41
  266. package/dist/canonical-json-DuDiiUsQ.mjs +0 -13
  267. package/dist/client-Czz8o5jP.mjs +0 -2
  268. package/dist/gen-CNJ62MM7.mjs +0 -2
  269. package/dist/help-CmZzxUba.mjs +0 -2
  270. package/dist/login-CGcRKEoi.mjs +0 -2
  271. package/dist/migrate-CYfbKkXh.mjs +0 -2
  272. package/dist/plan-BEZ8VJW0.mjs +0 -256
  273. package/dist/plan-DpuOr14e.mjs +0 -2
  274. package/dist/prepare-Bm3iq-u4.mjs +0 -2
  275. package/dist/validate-EKmJWxmy.mjs +0 -2
  276. /package/dist/cli/{cf-compat.d.mts → cloudflare-operation-process.d.mts} +0 -0
@@ -25,15 +25,9 @@ routes/
25
25
  api/metrics.prod.ts → production only
26
26
  ```
27
27
 
28
- `dev` is the Vite dev server. `prod` is every build, `void deploy`, and `vite preview` — preview serves a production build, so it uses the production routes.
28
+ `.dev` routes run only in development; `.prod` routes run in builds, deployments, and production previews. A resource used only by `.dev` routes is not provisioned for production.
29
29
 
30
- Excluded routes aren't included in the Worker bundle, generated types, or route and WebSocket configuration.
31
-
32
- `void prepare` is the exception: it boots no Vite, so it has no environment to read. It generates types for routes in both environments.
33
-
34
- [Binding inference](../reference/resource-inference.md) follows the suffix for files in `routes/`. For example, a `void/storage` import used only in `api/debug.dev.ts` adds a development R2 binding, but does not provision a production bucket. Imports in other source directories remain available in both environments.
35
-
36
- The suffix applies to files in `routes/` only. It has no effect in `pages/`, `middleware/`, `crons/`, or `queues/`.
30
+ Suffixes apply only in `routes/`, not in `pages/`, `middleware/`, `crons/`, or `queues/`. `void prepare` generates types for both environments.
37
31
 
38
32
  Each file exports named HTTP method constants to handle specific methods:
39
33
 
@@ -65,20 +59,10 @@ export const POST = defineHandler(async (c) => {
65
59
  });
66
60
  ```
67
61
 
68
- The `db` helper provides a typed query API over D1. See [Database](./database.md) for the full API.
62
+ See [Database](./database.md) for queries with D1, PostgreSQL, or MySQL.
69
63
 
70
64
  ## `defineHandler`
71
65
 
72
- `defineHandler` wraps a route handler function:
73
-
74
- ```ts
75
- import { defineHandler } from 'void';
76
-
77
- export const GET = defineHandler((c) => {
78
- return { data: 'hello' };
79
- });
80
- ```
81
-
82
66
  The handler receives a Hono `Context` with typed Cloudflare bindings on `c.env` (see [Cloudflare](../integrations/cloudflare.md)). You can use the full Hono API (`c.json()`, `c.text()`, `c.header()`, etc.).
83
67
 
84
68
  Return values are automatically converted:
@@ -108,23 +92,6 @@ export const POST = defineHandler.withValidator({
108
92
 
109
93
  See [Database: Schema-Derived Validators](./database.md#schema-derived-validators) for how to set up `createInsertSchema` with column refinements.
110
94
 
111
- You can validate the body, query, and route parameters together:
112
-
113
- ```ts
114
- // routes/api/users/[id].ts
115
- import { defineHandler } from 'void';
116
- import { db, eq } from 'void/db';
117
- import { users, updateUserSchema } from '@schema';
118
-
119
- export const PUT = defineHandler.withValidator({
120
- body: updateUserSchema,
121
- })(async (c, { body }) => {
122
- const id = Number(c.req.param('id'));
123
- const [updated] = await db.update(users).set(body).where(eq(users.id, id)).returning();
124
- return updated;
125
- });
126
- ```
127
-
128
95
  ### Manual validators
129
96
 
130
97
  For endpoints that don't map to a database table, you can write validators by hand using any [Standard Schema](https://standardschema.dev/)-compatible library (Valibot, Zod, ArkType, etc.):
@@ -160,7 +127,7 @@ When validation fails, a `400` response is returned with structured error detail
160
127
  }
161
128
  ```
162
129
 
163
- No extra dependencies are required. Void inlines the Standard Schema types, so you only need your chosen schema library.
130
+ Install your chosen validator library; no separate Standard Schema dependency is needed.
164
131
 
165
132
  Validator schemas also power the [typed fetch client](./typed-fetch.md), so `body`, `query`, and `params` types are enforced at the call site.
166
133
 
@@ -208,9 +175,7 @@ export default defineMiddleware(async (c, next) => {
208
175
  });
209
176
  ```
210
177
 
211
- `defineMiddleware` uses Hono middleware semantics: `(c, next) => Promise<void> | void`.
212
-
213
- For a temporary full-site gate, use the built-in `basicAuth()` middleware with credentials from `void/env`. Void internal endpoints under `/__void` are excluded automatically so deploy migrations and dev tooling continue to work. Wrap `void/env` reads in functions so they are resolved per request after Void has bound the runtime env.
178
+ Use `basicAuth()` for a temporary site gate. Void's reserved `/__void` endpoints remain accessible for deployment and development tooling. Read credentials through callbacks:
214
179
 
215
180
  ```ts
216
181
  // env.ts
@@ -238,25 +203,7 @@ Set `BASIC_AUTH_USERNAME` and `BASIC_AUTH_PASSWORD` as local environment variabl
238
203
 
239
204
  For app-specific bypasses such as health checks or public webhooks, compose that logic in your own middleware before calling `basicAuth()`.
240
205
 
241
- Middleware can set typed context variables using `c.set()`. Augment the `CloudContextVariables` interface so downstream handlers get full type safety:
242
-
243
- ```ts
244
- // middleware/01.request-id.ts
245
- import { defineMiddleware } from 'void';
246
-
247
- declare module 'void' {
248
- interface CloudContextVariables {
249
- requestId: string;
250
- }
251
- }
252
-
253
- export default defineMiddleware(async (c, next) => {
254
- c.set('requestId', crypto.randomUUID());
255
- await next();
256
- });
257
- ```
258
-
259
- Now every route handler can call `c.get("requestId")` and get `string` back, with no type assertion needed. See [Type Safety](./type-safety.md#context-variables) for more details.
206
+ Use `c.set()` to share data with downstream handlers. Augment `CloudContextVariables` for typed access with `c.get()`. See [Context variables](./type-safety.md#context-variables).
260
207
 
261
208
  ### Per-route middleware
262
209
 
@@ -273,19 +220,9 @@ Middleware runs in order. Each can short-circuit (return a response without call
273
220
  import { defineHandler } from 'void';
274
221
  import { cors } from 'hono/cors';
275
222
 
276
- const addServerTiming = async (c, next) => {
277
- const start = performance.now();
278
- await next();
279
- c.header('Server-Timing', `app;dur=${Math.round(performance.now() - start)}`);
280
- };
281
-
282
- export const GET = defineHandler(
283
- cors({ origin: 'https://app.example.com' }),
284
- addServerTiming,
285
- (c) => {
286
- return { stats: '...' };
287
- },
288
- );
223
+ export const GET = defineHandler(cors({ origin: 'https://app.example.com' }), (c) => ({
224
+ stats: '...',
225
+ }));
289
226
  ```
290
227
 
291
228
  Up to 5 middleware can be passed before the handler, with full type inference for each position.
@@ -65,11 +65,11 @@ await stream.send({
65
65
  await stream.comment('still connected');
66
66
  ```
67
67
 
68
- `data` may be a string or JSON-serializable value. Strings are sent as-is; other values are serialized with `JSON.stringify()`. Multi-line strings are split into multiple `data:` lines. Binary data is rejected because SSE is text-only.
68
+ `data` accepts strings or JSON-serializable values. SSE is text-only.
69
69
 
70
- Void validates `event`, `id`, and `retry` before writing, so their values can't accidentally introduce extra SSE fields or events. Writing to a closed stream throws `SseStreamClosedError`.
70
+ Writing to a closed stream throws `SseStreamClosedError`.
71
71
 
72
- If you already serialized the payload, use `formatSseText()` for lower-level formatting while keeping the same `id`, `event`, and `retry` validation:
72
+ Use `formatSseText()` to format a payload you have already serialized:
73
73
 
74
74
  ```ts
75
75
  import { formatSseText } from 'void/sse';
@@ -98,8 +98,6 @@ return eventStream(start, {
98
98
  });
99
99
  ```
100
100
 
101
- The interval must be a positive finite number.
102
-
103
101
  ## Last Event ID
104
102
 
105
103
  Browsers send `Last-Event-ID` when reconnecting after an event with an `id` field. Use `getLastEventId()` to resume from your own storage:
@@ -115,8 +113,6 @@ export const GET = defineHandler((c) => {
115
113
  });
116
114
  ```
117
115
 
118
- `void/sse` does not store or replay events. Persist event offsets in your own database, queue, or Durable Object when replay matters.
119
-
120
116
  ## Client
121
117
 
122
118
  Use `connectEventStream()` from the browser-only `void/sse/client` subpath:
@@ -174,14 +170,4 @@ export const GET = defineHandler(async (c) => {
174
170
 
175
171
  Native `EventSource` can send cookies with `withCredentials: true`. For non-cookie auth, generate a short-lived signed URL and validate it in the route handler.
176
172
 
177
- ## When to use SSE
178
-
179
- Plain SSE is enough when the producer belongs to the same request that opened the stream:
180
-
181
- - AI token streaming
182
- - One-off progress updates
183
- - Command output
184
- - Per-request deployment or build logs
185
- - Incremental status for a long-running action
186
-
187
- For shared topics and subscriptions, use [Live Event Streams](./live.md). For rooms with two-way communication, use [WebSockets](./websockets.md). Replay and database change streams need an application-level storage or delivery layer.
173
+ For shared topics and subscriptions, use [Live Event Streams](./live.md). For two-way communication, use [WebSockets](./websockets.md).
@@ -14,15 +14,11 @@ Set `output: "static"` in `void.config.ts` to prerender all pages at build time:
14
14
 
15
15
  When `output` is `"static"`:
16
16
 
17
- - All pages default to `prerender = true` and are written as HTML files to `dist/client/`. A standalone `vite build` renders them during the build; managed `void deploy` renders immediately afterward in its trusted parent process.
17
+ - Pages default to `prerender = true` and are written as HTML files to `dist/client/`.
18
18
  - Use `export const prerender = false` in a page's `.server.ts` to opt out. That page will be server-rendered on request.
19
19
  - Dynamic pages without `getPrerenderPaths()` are implicitly not prerendered (the paths aren't known at build time).
20
20
  - The build output is self-contained and works for direct Cloudflare deployment, self-hosting, or `void deploy`.
21
21
 
22
- ## How it works
23
-
24
- During `vite build`, after both the worker and client bundles are written to disk, Void spins up Miniflare with the built worker and fetches each page. The HTML responses are written to `dist/client/` as static files (e.g. `/about` becomes `dist/client/about.html`).
25
-
26
22
  ## Per-page overrides
27
23
 
28
24
  Individual pages can opt out of prerendering:
@@ -41,8 +37,6 @@ export async function getPrerenderPaths() {
41
37
  }
42
38
  ```
43
39
 
44
- Dynamic pages **without** `getPrerenderPaths()` are not prerendered because the paths are not known at build time. These pages are served dynamically by the worker at runtime.
45
-
46
40
  ## Comparison with edge prerendering
47
41
 
48
42
  | `output` value | Default prerender | Per-page override | Prerender timing |
@@ -50,19 +44,15 @@ Dynamic pages **without** `getPrerenderPaths()` are not prerendered because the
50
44
  | `"server"` (default) | `false` | `export const prerender = true` | Deploy-time (platform ISR) |
51
45
  | `"static"` | `true` | `export const prerender = false` | Build or deploy post-build |
52
46
 
53
- When `output` is omitted or set to `"server"`, behavior is unchanged. `export const prerender = true` opts individual pages into deploy-time [edge prerendering](./edge/prerendering.md).
54
-
55
- Managed deploy keeps its deployment credential out of project-controlled build scripts and Vite plugins. When static rendering needs remote D1, KV, R2, or AI, the trusted deploy process supplies the built worker with a five-minute credential scoped to that project's binding proxy only.
47
+ With the default `output: "server"`, use `export const prerender = true` for deploy-time [edge prerendering](./edge/prerendering.md).
56
48
 
57
49
  ## Deployment behavior
58
50
 
59
- When you run `void deploy` with `output: "static"`, Void inspects the build output to decide the optimal deploy strategy:
51
+ `void deploy` chooses the deployment automatically:
60
52
 
61
53
  - **Fully static:** if every page is prerendered and there are no API routes, middleware, cron jobs, or queues, Void deploys as a pure static site with no worker.
62
54
  - **Hybrid:** if any pages are not prerenderable, such as dynamic pages without `getPrerenderPaths()` or pages with `export const prerender = false`, a worker is deployed to handle those routes at runtime. Prerendered pages are still served as static assets.
63
55
 
64
- You do not need to configure this. Void detects it automatically from your pages and project structure.
65
-
66
56
  ## Relationship to `inference.appType: "static"`
67
57
 
68
58
  The `inference.appType` field describes app type (SPA, static, void), while `output` controls rendering strategy:
@@ -72,5 +62,3 @@ The `inference.appType` field describes app type (SPA, static, void), while `out
72
62
  | **What** | Deploy a pre-built static site (no Void plugin) | Prerender a Void app at build time |
73
63
  | **Worker** | None (static assets only) | Only if some pages can't be prerendered |
74
64
  | **Use case** | VitePress, plain HTML, external SSG tools | Void apps with mostly or fully static content |
75
-
76
- When all pages are prerendered and there are no backend features, `output: "static"` produces the same deploy result as `inference.appType: "static"`: pure static assets with no worker. The difference is that `output: "static"` figures this out by analyzing your build output, while `inference.appType: "static"` is a manual declaration for non-Void projects.
@@ -4,18 +4,11 @@ outline: deep
4
4
 
5
5
  # Custom SSR
6
6
 
7
- Void supports framework-agnostic SSR via explicit server and client entries. This is for advanced use cases where you want full control over rendering and hydration.
7
+ Use custom SSR to choose your own router, data loading, HTML shell, and hydration code. If you want Void to handle these for you, use [Pages Routing](./pages-routing/overview).
8
8
 
9
- ::: tip
10
- For most apps, [Pages Routing](./pages-routing/overview) handles SSR automatically. You do not need entry files or hydration code.
11
- :::
9
+ ## Create the app
12
10
 
13
- ## Relationship to Pages Routing
14
-
15
- Custom SSR is separate from Pages Routing. Use Custom SSR when you want to bring
16
- your own root component, router, data loading, HTML shell, and hydration logic.
17
-
18
- The `App` component below is the root of this custom-rendered application:
11
+ Start with a root component. This React example renders a page based on the request path:
19
12
 
20
13
  ```tsx
21
14
  // src/App.tsx
@@ -24,50 +17,21 @@ export default function App({ url }: { url: string }) {
24
17
  }
25
18
  ```
26
19
 
27
- Pages Routing adapters generate their own SSR and hydration entries and compose
28
- `pages/layout.*`, route components, loaders, and actions.
29
-
30
20
  ## Required entries
31
21
 
32
- SSR mode is enabled when both of these exist:
22
+ Create both entry files:
33
23
 
34
24
  - `src/main.ssr.ts` or `src/main.ssr.tsx`
35
25
  - `src/main.client.ts` or `src/main.client.tsx`
36
26
 
37
- Only one server entry and one client entry may exist.
38
- If only one side is present, build/deploy fails with a clear error.
27
+ Use one server entry and one client entry. Both are required.
39
28
 
40
29
  ## Render API
41
30
 
42
- `src/main.ssr.ts(x)` must export either:
43
-
44
- ```ts
45
- render(c: CloudContext, assetTags: RenderAssetTags): Response | Promise<Response>
46
- ```
47
-
48
- or:
49
-
50
- ```ts
51
- export default defineRender((c, assetTags) => Response | Promise<Response>);
52
- ```
53
-
54
- The recommended form is `defineRender(...)` for inferred types.
55
-
56
- `assetTags` contains the HTML tags for your client assets:
57
-
58
- ```ts
59
- {
60
- css: string; // stylesheet links for <head>
61
- preloads: string; // modulepreload/Vite client+preamble tags for <head>
62
- body: string; // main client entry script tag before </body>
63
- }
64
- ```
65
-
66
- If no render export is found, build/deploy fails with a clear error.
67
-
68
- Example:
31
+ Wrap your server renderer with `defineRender()` and return an HTML response. Insert `assetTags.css` and `assetTags.preloads` in `<head>`, and `assetTags.body` before `</body>`:
69
32
 
70
33
  ```tsx
34
+ // src/main.ssr.tsx
71
35
  import { renderToString } from 'react-dom/server';
72
36
  import { defineRender } from 'void';
73
37
  import App from './App';
@@ -89,34 +53,22 @@ export default defineRender(async (c, assetTags) => {
89
53
  });
90
54
  ```
91
55
 
92
- `src/main.client.ts(x)` should hydrate/mount your app:
56
+ You can also export a named `render(c, assetTags)` function with the same signature. See [`defineRender`](../reference/api/handlers.md#definerender-handler) for the types.
57
+
58
+ ## Hydrate in the browser
59
+
60
+ In the client entry, hydrate the same component:
93
61
 
94
62
  ```tsx
63
+ // src/main.client.tsx
95
64
  import { hydrateRoot } from 'react-dom/client';
96
65
  import App from './App';
97
66
 
98
67
  hydrateRoot(document.getElementById('root')!, <App url={window.location.pathname} />);
99
68
  ```
100
69
 
101
- ## Client Asset Injection
102
-
103
- Place the client asset tags in the HTML returned by your `render()` function.
104
-
105
- The `assetTags` values are computed by Void:
106
-
107
- - In production: from `dist/client/.vite/manifest.json` (entry script, CSS, modulepreload)
108
- - In dev: includes Vite HMR client and React refresh preamble (when React plugin is active), plus the client entry script
109
-
110
70
  ## Caching
111
71
 
112
72
  See [Revalidation](./edge/revalidation.md) for stale-while-revalidate caching of SSR pages.
113
73
 
114
- ## Request flow
115
-
116
- With SSR enabled:
117
-
118
- 1. `/api/*` requests go to worker API routes
119
- 2. static asset hits are served from R2
120
- 3. unmatched non-API requests fall back to `render(c, assetTags)`
121
-
122
- Without SSR entries, non-API requests keep SPA static fallback behavior.
74
+ API routes and static files are served before custom rendering. Unmatched non-API requests use `render(c, assetTags)`.
@@ -35,13 +35,14 @@ for (const obj of listed.objects) {
35
35
  const head = await storage.head('uploads/photo.jpg');
36
36
  ```
37
37
 
38
- The `storage` object is a full `R2Bucket`. Every method from the [Cloudflare R2 API](https://developers.cloudflare.com/r2/api/workers/workers-api-reference/) is available directly, with no wrapper layer.
38
+ `storage` supports the full [R2Bucket API](https://developers.cloudflare.com/r2/api/workers/workers-api-reference/).
39
39
 
40
40
  ## Serving Files
41
41
 
42
42
  A common pattern is serving uploaded files from an API route:
43
43
 
44
44
  ```ts
45
+ import { defineHandler } from 'void';
45
46
  import { storage } from 'void/storage';
46
47
 
47
48
  export const GET = defineHandler(async (c) => {
@@ -60,8 +61,12 @@ export const GET = defineHandler(async (c) => {
60
61
  });
61
62
  ```
62
63
 
63
- ## How It Works
64
+ ## Custom bindings
64
65
 
65
- `storage` resolves the `env.STORAGE` binding when you use it. It exposes the R2 API directly, so methods and options work as described in Cloudflare's documentation.
66
+ Use `createStorage(bucket)` with your own R2 binding, or in tests:
66
67
 
67
- The `createStorage()` factory exists for testing and for frameworks that manage their own routing. It accepts an `R2Bucket` and returns it directly.
68
+ ```ts
69
+ import { createStorage } from 'void/storage';
70
+
71
+ const storage = createStorage(env.MY_BUCKET);
72
+ ```
@@ -4,7 +4,7 @@ outline: deep
4
4
 
5
5
  # Type Safety
6
6
 
7
- Void provides end-to-end type safety across the stack. Types come from your source code and Drizzle schema, so you are not hand-writing or duplicating interfaces.
7
+ Void infers types from your database schema, route handlers, and page loaders. When you change a field, TypeScript shows which queries, requests, and components need to change.
8
8
 
9
9
  ## The Type Pipeline
10
10
 
@@ -114,7 +114,7 @@ The `action()` helper gets the same type checking. See [Actions & Forms](./pages
114
114
 
115
115
  ## Serialization
116
116
 
117
- Handler return types are transformed via `Serialize<T>` so the client sees what actually arrives over the wire:
117
+ The client types reflect JSON serialization through `Serialize<T>`:
118
118
 
119
119
  | Source type | Serialized type |
120
120
  | --------------------------------- | ------------------------------------------------------ |
@@ -163,17 +163,6 @@ Extend the generated tsconfig in your project:
163
163
  }
164
164
  ```
165
165
 
166
- If your project already extends another config, use `void init --tsconfig` so Void can patch the file without dropping existing `files` or `compilerOptions.paths` entries. The resulting config may use TypeScript's multi-extends form:
167
-
168
- ```json
169
- {
170
- "extends": ["./tsconfig.base.json", "./.void/tsconfig.json"],
171
- "compilerOptions": {
172
- "types": ["void/env"]
173
- }
174
- }
175
- ```
176
-
177
- The `.void/tsconfig.json` uses `"files"` and `compilerOptions.paths` for generated declarations such as `routes.d.ts`, `db.d.ts`, and `queues.d.ts`. TypeScript inherits those fields, but `files` and `paths` are replaced rather than deeply merged when another config defines them. `void init --tsconfig` handles the common existing-config cases by adding Void's generated files and aliases directly to the root config when needed.
166
+ If you already have a TypeScript config, run `void init --tsconfig` to add Void types while preserving your settings.
178
167
 
179
168
  Run `void prepare` in CI or after a fresh clone, or let `vite dev` / `vite build` generate the `.void/` files during normal app workflows.
@@ -4,7 +4,7 @@ outline: deep
4
4
 
5
5
  # Typed Fetch
6
6
 
7
- Void ships a typed `fetch` client that knows every route in your app. Import it from `void/client` and get autocomplete for paths, type-checked request bodies, and fully inferred response types.
7
+ Import `fetch` from `void/client` for route autocomplete, checked request bodies, and inferred response types.
8
8
 
9
9
  ## Basic Usage
10
10
 
@@ -26,8 +26,6 @@ const user = await fetch('/api/users/:id', {
26
26
  });
27
27
  ```
28
28
 
29
- No type annotations needed. Everything is inferred from your route handlers.
30
-
31
29
  ## What Gets Type-Checked
32
30
 
33
31
  The client constrains every part of the request:
@@ -90,7 +88,7 @@ try {
90
88
 
91
89
  ## Isomorphic Fetch During SSR
92
90
 
93
- `fetch()` from `void/client` works during server-side rendering and inside route handlers without an HTTP round-trip. In the worker environment, it calls your Hono app directly through `app.fetch()` and skips the network entirely.
91
+ `fetch()` also works in server-side rendering and route handlers. Calls to your app run without an HTTP round-trip.
94
92
 
95
93
  ```ts
96
94
  // src/main.ssr.tsx
@@ -108,6 +106,4 @@ export default defineRender(async (c, assetTags) => {
108
106
  });
109
107
  ```
110
108
 
111
- **Automatic header forwarding**: `cookie` and `authorization` headers from the incoming request are automatically forwarded to subrequests, so authentication context is preserved. If you pass these headers explicitly, your values take precedence.
112
-
113
- **How it works**: In the browser, `fetch()` uses the normal HTTP client. In the worker, Void redirects the import to a virtual module that calls `app.fetch()` directly using the Hono app instance. AsyncLocalStorage threads the outer request context so headers and `waitUntil()` work correctly.
109
+ Server-side calls forward the incoming request's `cookie` and `authorization` headers. Explicit headers take precedence.
@@ -8,11 +8,9 @@ outline: deep
8
8
  Typed WebSocket routes currently work in native Void apps. They aren't available in meta-framework mode yet.
9
9
  :::
10
10
 
11
- Create a `.ws.ts` route to add a typed WebSocket endpoint. Void runs each route instance in a Cloudflare Durable Object, which coordinates the clients connected to it. These routes require the Cloudflare target; Node.js, Bun, and Deno builds reject them.
11
+ Create a `.ws.ts` route to send typed messages between your server and connected clients. Each route instance has its own Cloudflare Durable Object for shared state. WebSocket routes require the Cloudflare target.
12
12
 
13
- New WebSocket route classes use SQLite-backed Durable Objects on both deployment platforms.
14
-
15
- For example, a chat route at `/rooms/[id]` gives each room its own instance. Use it for chat, presence, collaborative documents, or notifications.
13
+ For example, `/rooms/[id]` gives each chat room its own instance. Use it for chat, presence, collaborative documents, or notifications.
16
14
 
17
15
  ## Route files
18
16
 
@@ -115,6 +113,28 @@ export default defineWebSocket({
115
113
  });
116
114
  ```
117
115
 
116
+ ## Client
117
+
118
+ Use `connect()` from `void/ws` on the client:
119
+
120
+ ```ts
121
+ import { connect } from 'void/ws';
122
+
123
+ const socket = connect('/chat/:room', {
124
+ params: { room: 'general' },
125
+ });
126
+
127
+ socket.on('message', (event) => {
128
+ if (event.type === 'chat.message') {
129
+ console.log(event.text);
130
+ }
131
+ });
132
+
133
+ socket.send({ type: 'chat.message', text: 'hello' });
134
+ ```
135
+
136
+ `connect()` resolves relative URLs against the current origin and automatically uses `ws:` or `wss:`. It also buffers messages until the socket opens and reconnects by default.
137
+
118
138
  ## Typed messages
119
139
 
120
140
  Define schemas for messages sent by the client and server:
@@ -125,19 +145,11 @@ Define schemas for messages sent by the client and server:
125
145
  - `ctx.room.broadcast()`, `ctx.connection.send()`, and `ctx.socket.send()` are typed from `messages.server`
126
146
  - `connect()` infers route params, outgoing client messages, and incoming server messages from generated route types
127
147
 
128
- The default protocol is JSON events. Raw string or binary framing is not the primary API.
129
-
130
- ## Ambient auth
148
+ Messages are JSON events. Raw string and binary messages are not supported.
131
149
 
132
- WebSocket hooks use the same built-in session resolution as HTTP auth. When Void auth is enabled, `ctx.user` is available in:
150
+ ## Authentication
133
151
 
134
- - `onBeforeConnect`
135
- - `onConnect`
136
- - `onMessage`
137
- - `onClose`
138
- - `onRequest`
139
-
140
- This makes cookie-authenticated sockets work without re-parsing the session manually.
152
+ When Void auth is enabled, every WebSocket hook receives the current user as `ctx.user`, or `null` for an anonymous connection. Use `onBeforeConnect` to reject unauthenticated clients.
141
153
 
142
154
  ## Hooks
143
155
 
@@ -149,6 +161,9 @@ Both `defineRoom()` and `defineWebSocket()` support:
149
161
  - `onClose(ctx, details)`: receives `{ code, reason, wasClean }`
150
162
  - `onRequest(ctx)`: handles ordinary HTTP requests to the same path
151
163
 
164
+ Void completes the WebSocket close handshake automatically. Use `onClose` for application cleanup;
165
+ you do not need to close the socket again in this hook.
166
+
152
167
  Every hook receives a context with:
153
168
 
154
169
  - `ctx.id`: deterministic route instance id
@@ -160,42 +175,55 @@ Every hook receives a context with:
160
175
 
161
176
  If a route does not define `onRequest()`, non-WebSocket requests return `426 Upgrade Required`.
162
177
 
163
- ## Client
178
+ ## Move a route while keeping its state
164
179
 
165
- Use `connect()` from `void/ws` on the client:
180
+ Before moving a deployed route, run `void info`. For `routes/chat/[room].ws.ts`, the output includes:
166
181
 
167
- ```ts
168
- import { connect } from 'void/ws';
182
+ ```text
183
+ To preserve this resource when moving its code, add this to 'defineRoom':
184
+ name: "chat-room",
185
+ ```
169
186
 
170
- const socket = connect('/chat/:room', {
171
- params: { room: 'general' },
172
- });
187
+ Add the displayed name to your existing definition:
173
188
 
174
- socket.on('message', (event) => {
175
- if (event.type === 'chat.message') {
176
- console.log(event.text);
177
- }
189
+ ```ts
190
+ export default defineRoom({
191
+ name: 'chat-room',
192
+ messages: { client: ClientMessage, server: ServerMessage },
193
+ // Keep your existing hooks.
178
194
  });
179
-
180
- socket.send({ type: 'chat.message', text: 'hello' });
181
195
  ```
182
196
 
183
- `connect()` resolves relative URLs against the current origin and automatically uses `ws:` or `wss:`. It also buffers messages until the socket opens and reconnects by default.
197
+ Move the file to `routes/rooms/[room].ws.ts`, update clients to connect to `/rooms/:room`, and deploy normally. Keep the same parameter names and values: room `general` still uses its existing storage. The Worker class, binding, and migration history stay the same on both Cloudflare and Void platform deployments.
184
198
 
185
- ## Constraints
199
+ Both `defineRoom()` and `defineWebSocket()` accept optional `name`. Use a static string, inline or in a local `const`. Each route must produce a distinct Worker class; changing the name selects a different resource. If you already moved a route, `void info` also shows unmatched identities recorded in `void.lock.json`. Identify the original resource before adopting its suggested name; otherwise restore the original source and discover the name there.
186
200
 
187
- WebSocket routes currently support:
201
+ ### Rename a route parameter
188
202
 
189
- - Cloudflare-only
190
- - one route-derived connection target per socket
191
- - no Socket.IO-style dynamic room join/leave API
192
- - no global pub/sub abstraction
193
- - JSON event messages only
203
+ By default, the instance key includes parameter names. `/chat/:room` with `room: 'general'` uses `room=general`; a route without parameters uses `default`. Multiple parameters are joined in route order, for example `team=acme&room=general`. If a multi-parameter route has a value containing `&`, its key uses a versioned encoding to keep rooms separate.
194
204
 
195
- Each socket connects to one route instance. Applications that need to switch rooms should manage that connection change explicitly.
205
+ When upgrading an existing multi-parameter route with `&` in its parameter values, those rooms start with isolated storage. The previous storage is retained, but may have been shared by multiple rooms. Migrate only data whose ownership you have verified into each new room. Single-parameter rooms and multi-parameter rooms without `&` keep their existing identities.
196
206
 
197
- ## Deployment
207
+ If you also rename `[room]` to `[id]`, preserve the old key explicitly:
208
+
209
+ ```ts
210
+ // routes/rooms/[id].ws.ts
211
+ export default defineRoom({
212
+ name: 'chat-room',
213
+ key: ({ params }) => `room=${params.id}`,
214
+ messages: { client: ClientMessage, server: ServerMessage },
215
+ // Keep your existing hooks.
216
+ });
217
+ ```
218
+
219
+ Clients now connect to `/rooms/:id` with `params: { id: 'general' }`. The key remains `room=general`, so storage and `ctx.id` stay the same. Returning only `params.id` would select a different instance.
198
220
 
199
- `void deploy --platform cloudflare` persists the required binding and append-only SQLite class migration in `void.lock.json`, then deploys the generated Worker directly to your account. Commit this migration history and never delete or reorder a step after deployment.
221
+ `key` is optional on both WebSocket helpers. It must return a string synchronously and consistently for the same parameters. Preserve the complete old key when migrating multiple parameters. Keep the callback stable after deployment.
222
+
223
+ ## Constraints
224
+
225
+ Each socket connects to one route instance. To switch rooms, close the current connection and open another. For publishing to multiple topics, see [Live Event Streams](./live.md).
226
+
227
+ ## Deployment
200
228
 
201
- `void deploy --platform void` uses the same shared migration planner in the hosted uploader. Existing hosted WebSocket classes created on legacy storage remain there; only genuinely new classes use SQLite.
229
+ WebSocket routes work on Cloudflare and Void platform deployments. Commit `void.lock.json` when Void adds a binding or migration. Do not delete or reorder deployed migration steps.
@@ -10,7 +10,7 @@ outline: deep
10
10
  npx void init --agents
11
11
  ```
12
12
 
13
- This always creates or updates `AGENTS.md` with four brief bullets covering Void, deployment targets, the development workflow, and where to find the docs. It preserves your existing content outside the versioned Void block and leaves other instruction files untouched. There is no coding-agent selection prompt.
13
+ Void updates `AGENTS.md` with development guidance and links to the docs, preserving your existing instructions.
14
14
 
15
15
  The complete Markdown docs ship with the installed package at `node_modules/void/skills/void/docs/`. Agents can read them directly, even without a linked skill.
16
16
 
@@ -20,7 +20,7 @@ Skills point your agent to the commands and docs it needs for the task. They lin
20
20
 
21
21
  Void ships two skills:
22
22
 
23
- - **`void`:** main development skill. Routes agent requests to the right documentation for CLI commands, routing, pages, database, auth, deployment, and more.
23
+ - **`void`:** commands and docs for app development.
24
24
  - **`migrate-vite-cloudflare-to-void`:** migration skill for converting existing `@cloudflare/vite-plugin` apps to Void.
25
25
 
26
- Skills are linked automatically by `void init --agents` when it detects a supported agent's configuration. For Claude Code, they are symlinked into `.claude/skills/`. Other detected agents have them linked to their respective directories. If no agent is detected, skill linking is skipped and `AGENTS.md` points directly to the bundled docs.
26
+ Skills are linked automatically by `void init --agents` when it detects a supported agent's configuration. For example, Claude Code uses `.claude/skills/`. If no agent is detected, skill linking is skipped and `AGENTS.md` points directly to the bundled docs.