void 1.2.1 → 1.4.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 (200) hide show
  1. package/dist/{account-cmd-CdUll_35.mjs → account-cmd-HP8bixYo.mjs} +3 -3
  2. package/dist/{application-analysis-fEqoSeMp.mjs → application-analysis-DDOQwiXk.mjs} +18 -12
  3. package/dist/{application-routing-DAhNP4u3.mjs → application-routing-4719u_kf.mjs} +3 -1
  4. package/dist/auth-g2TQbDLD.d.mts +70 -0
  5. package/dist/{auth-link-C87vGXM-.mjs → auth-link-CedYpDCy.mjs} +3 -3
  6. package/dist/{auth-router-6I81r-ix.mjs → auth-router-vafo7x8w.mjs} +11 -6
  7. package/dist/{better-auth-shared-D-6z0q0f.mjs → better-auth-shared-7FM3Uk2N.mjs} +48 -2
  8. package/dist/{better-auth-shared-DVk2DkTW.d.mts → better-auth-shared-BHB8kiTE.d.mts} +1 -1
  9. package/dist/{build-cmd-ex1g-j_e.mjs → build-cmd-9vUAQ1_V.mjs} +1 -1
  10. package/dist/{cache-oHzrnY3z.mjs → cache-jvwLNb0h.mjs} +1 -1
  11. package/dist/{cancel-deploy-viAMD0Q_.mjs → cancel-deploy-Dw0UmWKO.mjs} +1 -1
  12. package/dist/{cf-build-output-BhVxldCw.mjs → cf-build-output-XpNR9zoh.mjs} +122 -5
  13. package/dist/cf-build-output-bd8Uwv1P.mjs +1 -0
  14. package/dist/{cf-worker-settings-BECvvTVd.mjs → cf-worker-settings-Dg27RzyT.mjs} +11 -5
  15. package/dist/cli/cli.mjs +41 -32
  16. package/dist/cli/cloudflare-operation-process.mjs +35 -8
  17. package/dist/cli/env-schema-probe.mjs +2 -2
  18. package/dist/{client-BEkdj1kx.mjs → client-B3LnB_bE.mjs} +8 -1
  19. package/dist/client-BZb9j66F.mjs +1 -0
  20. package/dist/{cloudflare-auth-Byo_0_N9.mjs → cloudflare-auth-DyD_kVFr.mjs} +1 -1
  21. package/dist/{cloudflare-cmd-DMzCv12s.mjs → cloudflare-cmd-D-b_u3T4.mjs} +2 -2
  22. package/dist/{cloudflare-config-DchHxPTo.mjs → cloudflare-config-CH1AylZT.mjs} +14 -3
  23. package/dist/{cloudflare-connect-CCRTVg-I.mjs → cloudflare-connect-zTJh8UnN.mjs} +2 -2
  24. package/dist/{cloudflare-operations-Dh9dEHJj.mjs → cloudflare-operations-Bc-7fN44.mjs} +4 -4
  25. package/dist/{cloudflare-operations-DsSaw2BT.mjs → cloudflare-operations-BtulCNOg.mjs} +1 -1
  26. package/dist/{config-BU6hMu1W.mjs → config-BV2ASlpn.mjs} +1 -1
  27. package/dist/{config-DQfHOKh9.d.mts → config-CAdZVTLN.d.mts} +4 -1
  28. package/dist/config-D-6q7EBH.mjs +87 -0
  29. package/dist/{config-DHgSEWT7.mjs → config-OFOAimzt.mjs} +5 -3
  30. package/dist/config-entry.d.mts +1 -1
  31. package/dist/{config-BIaXIkkv.mjs → config-tqMM6DwY.mjs} +1 -1
  32. package/dist/{connect-k7R2rPVA.mjs → connect-BOM6zvFX.mjs} +4 -4
  33. package/dist/{create-project-DV71ol5d.mjs → create-project-Bxd02EHN.mjs} +1 -1
  34. package/dist/{create-project-DTuy-l0z.mjs → create-project-C2K0DN2O.mjs} +1 -1
  35. package/dist/{db-DVOkLH5X.mjs → db-Cdvr3zwG.mjs} +18 -18
  36. package/dist/{db-types-NGxPwnlt.mjs → db-types-0eCiqYb1.mjs} +1 -1
  37. package/dist/{delete-BbP5eopu.mjs → delete-lETE6Tpt.mjs} +1 -1
  38. package/dist/deploy-CX94CmKa.mjs +2 -0
  39. package/dist/{deploy-7H32EnUs.mjs → deploy-CdM-LAxX.mjs} +473 -148
  40. package/dist/{deploy-managed-static-DlvcvnQT.mjs → deploy-managed-static-DjeBFG2c.mjs} +43 -20
  41. package/dist/{deploy-transport-BzGEj0Ge.mjs → deploy-transport-DIq_gFTP.mjs} +14 -13
  42. package/dist/deploy.d.mts +2 -0
  43. package/dist/deploy.mjs +8 -5
  44. package/dist/{dispatch-DoUnxTNo.mjs → dispatch-CLa2LThw.mjs} +76 -7
  45. package/dist/{domain-CA2wBgra.mjs → domain-DyhNtdYf.mjs} +2 -2
  46. package/dist/{email-DXpCc77w.mjs → email-DM2wDcBN.mjs} +59 -10
  47. package/dist/{email-cloudflare-D03g2TkS.mjs → email-cloudflare-PMbAev9Q.mjs} +124 -10
  48. package/dist/{env-DgOQ9_8S.mjs → env-78hbN-oI.mjs} +3 -3
  49. package/dist/{env-BJjULvL0.mjs → env-BaMz0j7H.mjs} +6 -1
  50. package/dist/{env-validation-Dwq3PJzK.mjs → env-validation-B3VobvXm.mjs} +2 -1
  51. package/dist/{env-validation-BmsdCZEk.mjs → env-validation-BUfFZ6bt.mjs} +1 -1
  52. package/dist/error-pages-CLIJ7aYq.mjs +37 -0
  53. package/dist/gen-BhWBT4iu.mjs +1 -0
  54. package/dist/{gen-BfmcIi2g.mjs → gen-CFmsUsW1.mjs} +3 -3
  55. package/dist/{github-cmd-CY9LUqLh.mjs → github-cmd-De-_IWbM.mjs} +1 -1
  56. package/dist/{handler-BbkSPzGF.d.mts → handler-BHktmuTL.d.mts} +4 -2
  57. package/dist/{headers-D522zd0A.mjs → headers--vaK_chu.mjs} +1 -1
  58. package/dist/{help-CxSlPpzJ.mjs → help-CKbrhZha.mjs} +16 -1
  59. package/dist/help-qOZr3wng.mjs +1 -0
  60. package/dist/index.d.mts +3 -5
  61. package/dist/index.mjs +370 -140
  62. package/dist/{info-7VDlhtyy.mjs → info-Yg0-V5Rs.mjs} +4 -4
  63. package/dist/{init-DxYyOs4-.mjs → init-CDiJQycf.mjs} +241 -206
  64. package/dist/{link-Ddmod1dZ.mjs → link-Dllip08S.mjs} +2 -2
  65. package/dist/{list-MogaugiO.mjs → list-BYRP6am0.mjs} +2 -2
  66. package/dist/{live-Bt8Y_0bg.d.mts → live-BllfDuzA.d.mts} +2 -2
  67. package/dist/{local-d1-BHBnKWRb.mjs → local-d1-CAwAs9GH.mjs} +1 -1
  68. package/dist/login-BAPmYnb7.mjs +1 -0
  69. package/dist/{login-Cttnagmm.mjs → login-PcFy5mlC.mjs} +2 -2
  70. package/dist/{logs-CAszUWZI.mjs → logs-DmJTBhed.mjs} +2 -2
  71. package/dist/{migrate-DyT2jJ8y.mjs → migrate-B8S2xTcb.mjs} +5 -66
  72. package/dist/migrate-BIYTPQE6.mjs +1 -0
  73. package/dist/nitro.mjs +4 -4
  74. package/dist/{node-WB7c2N4L.mjs → node-CDOPrGSl.mjs} +5 -5
  75. package/dist/{operator-args-UnE8Y2eK.mjs → operator-args-BsJIYXaT.mjs} +8 -1
  76. package/dist/operator-cmd-CjazwOyb.mjs +1 -0
  77. package/dist/{operator-cmd-skshjMGl.mjs → operator-cmd-DmkmS2wL.mjs} +4 -3
  78. package/dist/pages/client.d.mts +32 -4
  79. package/dist/pages/client.mjs +186 -29
  80. package/dist/pages/index.d.mts +34 -2
  81. package/dist/pages/index.mjs +96 -55
  82. package/dist/pages/islands-plugin.d.mts +1 -1
  83. package/dist/pages/islands-plugin.mjs +9 -3
  84. package/dist/pages/protocol.d.mts +2 -2
  85. package/dist/platform-args-BC8_hsDj.mjs +1 -0
  86. package/dist/{platform-args-DVbVClZl.mjs → platform-args-BMD2gjc_.mjs} +1 -1
  87. package/dist/{platform-auth-config-C-pm5Qzx.mjs → platform-auth-config-CImnfohz.mjs} +3 -3
  88. package/dist/{platform-auth-protection-SHhvim1L.mjs → platform-auth-protection-BogLhtHi.mjs} +2 -2
  89. package/dist/{platform-auth-recovery-DXVWs-wA.mjs → platform-auth-recovery-B-mEaWEi.mjs} +2 -2
  90. package/dist/{platform-cmd-PiNRkopU.mjs → platform-cmd-CV8NxIIn.mjs} +1 -1
  91. package/dist/{platform-cmd-D5I1beUX.mjs → platform-cmd-DZK2ixMl.mjs} +4 -4
  92. package/dist/{platform-domain-jpmBfZeb.mjs → platform-domain-CRvelPGE.mjs} +2 -2
  93. package/dist/{platform-lifecycle-DyKmuNY6.mjs → platform-lifecycle-CPYY7E9B.mjs} +103 -41
  94. package/dist/{platform-lifecycle-DH3Yr1qQ.mjs → platform-lifecycle-DwXq7kZt.mjs} +1 -1
  95. package/dist/{platform-management--vDwSn35.mjs → platform-management-BOnGbMSM.mjs} +1 -1
  96. package/dist/{platform-management-7m-PLaPJ.mjs → platform-management-lXdgGgAM.mjs} +1 -1
  97. package/dist/{platform-recovery-BiNpZZab.mjs → platform-recovery-noaALXnE.mjs} +2 -2
  98. package/dist/{plugin-inference-DUjCd55H.mjs → plugin-inference-CeiB6BG5.mjs} +53 -18
  99. package/dist/{prepare-BcAE5Sdj.mjs → prepare-BuOS18AM.mjs} +1 -1
  100. package/dist/{prepare-DA-17DhO.mjs → prepare-D604Yiyr.mjs} +12 -8
  101. package/dist/prepare-DU-tOpS5.mjs +1 -0
  102. package/dist/{prerender-render-Cijv7UFo.mjs → prerender-render-CaShbHJ3.mjs} +13 -1
  103. package/dist/prerender-render.d.mts +1 -0
  104. package/dist/prerender-render.mjs +1 -1
  105. package/dist/{project-cmd-DA4K5faU.mjs → project-cmd-DBYqAIbe.mjs} +15 -15
  106. package/dist/{project-team-CQBNveX3.mjs → project-team-BjCLgTRC.mjs} +1 -1
  107. package/dist/{project-token-CCalFyQf.mjs → project-token-BB6PwaA9.mjs} +1 -1
  108. package/dist/{project-tsconfig-Itv1f4WC.mjs → project-tsconfig-CVvk6Z_e.mjs} +24 -2
  109. package/dist/{project-zero-trust-DPnkiEuT.mjs → project-zero-trust-CWDh1P_t.mjs} +1 -1
  110. package/dist/{protocol-BBDpJz3G.d.mts → protocol-D5XDySYu.d.mts} +2 -2
  111. package/dist/{provision-BFLCrnUp.mjs → provision-DfxLCg-d.mjs} +1 -1
  112. package/dist/{provision-DtROpNNI.mjs → provision-DztLJ97X.mjs} +24 -8
  113. package/dist/{requests-BpH0kImL.mjs → requests-D_pbzDyz.mjs} +1 -1
  114. package/dist/{rollback-CXS1tAa-.mjs → rollback-CjfIYsJx.mjs} +2 -2
  115. package/dist/{route-types-BZi26DVS.mjs → route-types-DvEGmPif.mjs} +109 -30
  116. package/dist/routes-stub.d.mts +22 -13
  117. package/dist/runtime/auth.d.mts +2 -2
  118. package/dist/runtime/better-auth-mysql.d.mts +1 -1
  119. package/dist/runtime/better-auth-mysql.mjs +1 -1
  120. package/dist/runtime/better-auth-pg.d.mts +1 -1
  121. package/dist/runtime/better-auth-pg.mjs +1 -1
  122. package/dist/runtime/better-auth.d.mts +1 -1
  123. package/dist/runtime/better-auth.mjs +1 -1
  124. package/dist/runtime/client-react.d.mts +1 -1
  125. package/dist/runtime/client-solid.d.mts +1 -1
  126. package/dist/runtime/client-svelte.d.mts +1 -1
  127. package/dist/runtime/client-vue.d.mts +1 -1
  128. package/dist/runtime/client.d.mts +1 -1
  129. package/dist/runtime/handler.d.mts +1 -1
  130. package/dist/runtime/live-client.d.mts +1 -1
  131. package/dist/runtime/live.d.mts +1 -1
  132. package/dist/runtime/log.mjs +19 -1
  133. package/dist/runtime/routing.d.mts +5 -4
  134. package/dist/runtime/routing.mjs +14 -73
  135. package/dist/runtime/validator.d.mts +1 -1
  136. package/dist/runtime/ws-server.d.mts +3 -3
  137. package/dist/runtime/ws.d.mts +1 -1
  138. package/dist/runtime/ws.mjs +4 -2
  139. package/dist/{scan-8IzYXiU_.mjs → scan-N9WzdcAl.mjs} +302 -23
  140. package/dist/{secret-BbXHfLAt.mjs → secret-3X0PxmuX.mjs} +9 -12
  141. package/dist/static-asset-marker-Btf-6oCD.mjs +32 -0
  142. package/dist/{types-7AIz0OZu.d.mts → types-w72dljaA.d.mts} +6 -1
  143. package/dist/{version-metadata-tS39iW09.mjs → version-metadata-D2vfMQzv.mjs} +2 -1
  144. package/dist/worker-conditions-AWFRcFxR.mjs +11 -0
  145. package/dist/{wrangler-F96NnLyb.mjs → wrangler-I-scB_1t.mjs} +82 -13
  146. package/dist/wrangler-UnwdISay.mjs +1 -0
  147. package/dist/{wrapper-D3sZyh2C.mjs → wrapper-BBl1rC32.mjs} +8 -12
  148. package/dist/{ws-BH-im_tX.d.mts → ws-B3ClMVDh.d.mts} +3 -2
  149. package/docs/guide/auth.md +4 -2
  150. package/docs/guide/edge/redirects.md +22 -0
  151. package/docs/guide/edge/static-assets.md +10 -0
  152. package/docs/guide/email/domains.md +1 -1
  153. package/docs/guide/email/receiving.md +2 -0
  154. package/docs/guide/email/sending.md +1 -1
  155. package/docs/guide/pages-routing/actions-and-forms.md +43 -13
  156. package/docs/guide/pages-routing/markdown.md +2 -0
  157. package/docs/guide/pages-routing/overview.md +33 -2
  158. package/docs/guide/platform/administration/email.md +18 -0
  159. package/docs/guide/platform/installation/maintenance.md +3 -1
  160. package/docs/guide/platform/installation/prerequisites.md +9 -4
  161. package/docs/guide/platform/installation/uninstall.md +2 -0
  162. package/docs/guide/queues.md +13 -1
  163. package/docs/guide/server-routing.md +40 -2
  164. package/docs/guide/ssg.md +8 -0
  165. package/docs/guide/type-safety.md +7 -7
  166. package/docs/guide/websockets.md +4 -2
  167. package/docs/integrations/cloudflare.md +16 -7
  168. package/docs/reference/api/auth.md +6 -2
  169. package/docs/reference/api/deploy.md +9 -5
  170. package/docs/reference/api/pages.md +6 -4
  171. package/docs/reference/cli/deploy.md +10 -9
  172. package/docs/reference/cli/email.md +13 -0
  173. package/docs/reference/cli/platform-email.md +2 -2
  174. package/docs/reference/cli.md +1 -0
  175. package/docs/reference/config.md +11 -1
  176. package/docs/reference/structure.md +3 -1
  177. package/package.json +9 -7
  178. package/schema.json +4 -0
  179. package/skills/void/SKILL.md +1 -1
  180. package/templates/assets/icons.svg +49 -0
  181. package/templates/assets/react.svg +1 -0
  182. package/templates/assets/solid.svg +1 -0
  183. package/templates/assets/stack.svg +35 -0
  184. package/templates/assets/svelte.svg +1 -0
  185. package/templates/assets/void.svg +5 -0
  186. package/templates/assets/vue.svg +1 -0
  187. package/dist/auth-DkcFflXV.d.mts +0 -42
  188. package/dist/cf-build-output-BJHuSsVN.mjs +0 -1
  189. package/dist/client-BiRDZTSd.mjs +0 -1
  190. package/dist/config-CyUJzi3q.mjs +0 -32
  191. package/dist/deploy-CE0Ev0Ob.mjs +0 -2
  192. package/dist/gen-NrOlWZaG.mjs +0 -1
  193. package/dist/help-BeAFFE5C.mjs +0 -1
  194. package/dist/login-DgDwlKc_.mjs +0 -1
  195. package/dist/migrate-n49ZeS1X.mjs +0 -1
  196. package/dist/operator-cmd-d2MvJOrl.mjs +0 -1
  197. package/dist/parse-filename-DioPHiR9.mjs +0 -77
  198. package/dist/platform-args-kQrgxScY.mjs +0 -1
  199. package/dist/prepare-C4I0f--h.mjs +0 -1
  200. package/dist/wrangler-BLb3_luP.mjs +0 -1
@@ -8,7 +8,7 @@ Create a file in `routes/` to add an endpoint, and export a handler for each HTT
8
8
 
9
9
  ## Route Files
10
10
 
11
- Create route handlers in `routes/**/*.ts`. Each file maps to a URL path based on its location in the filesystem:
11
+ Create route handlers in `routes/` using `.ts`, `.tsx`, `.js`, `.jsx`, `.mts`, or `.mjs`. Each file maps to a URL path based on its location in the filesystem:
12
12
 
13
13
  <RoutesFileTree />
14
14
 
@@ -23,11 +23,13 @@ routes/
23
23
  api/health.ts → always
24
24
  api/debug.dev.ts → development only
25
25
  api/metrics.prod.ts → production only
26
+ preview.dev.tsx → development only, with JSX
27
+ report.prod.jsx → production only, with JSX
26
28
  ```
27
29
 
28
30
  `.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
31
 
30
- Suffixes apply only in `routes/`, not in `pages/`, `middleware/`, `crons/`, or `queues/`. `void prepare` generates types for both environments.
32
+ Suffixes apply in `routes/` and [`pages/`](./pages-routing/overview.md#environment-only-pages), not in `middleware/`, `crons/`, or `queues/`. `void prepare` generates types for both environments.
31
33
 
32
34
  Each file exports named HTTP method constants to handle specific methods:
33
35
 
@@ -59,8 +61,44 @@ export const POST = defineHandler(async (c) => {
59
61
  });
60
62
  ```
61
63
 
64
+ `GET` also answers `HEAD` requests, so route files can't export `HEAD`.
65
+
62
66
  See [Database](./database.md) for queries with D1, PostgreSQL, or MySQL.
63
67
 
68
+ ### JSX handlers
69
+
70
+ Use `.tsx` or `.jsx` when a server handler contains JSX. Void doesn't choose a JSX runtime: route files compile with the same JSX settings as the rest of your Vite app. In a React or Solid app, that is the framework's JSX, so render it with the framework's server renderer and return HTML. For example, in a React app:
71
+
72
+ ```tsx
73
+ // routes/hello.tsx → GET /hello
74
+ import { renderToStaticMarkup } from 'react-dom/server';
75
+ import { defineHandler } from 'void';
76
+
77
+ export const GET = defineHandler((c) => c.html(renderToStaticMarkup(<h1>Hello!</h1>)));
78
+ ```
79
+
80
+ In an app without a JSX framework (API only, Vue, or Svelte), use [Hono's JSX](https://hono.dev/docs/guides/jsx). Your app needs `hono` in its own dependencies (new Void apps have it). Set the runtime in `vite.config.ts`, so it applies to `.tsx` and `.jsx` files alike:
81
+
82
+ ```ts
83
+ // vite.config.ts
84
+ import { defineConfig } from 'vite-plus';
85
+ import { voidPlugin } from 'void';
86
+
87
+ export default defineConfig({
88
+ oxc: { jsx: { runtime: 'automatic', importSource: 'hono/jsx' } },
89
+ plugins: [voidPlugin()],
90
+ });
91
+ ```
92
+
93
+ `c.html()` then takes the JSX directly:
94
+
95
+ ```tsx
96
+ // routes/hello.tsx → GET /hello
97
+ import { defineHandler } from 'void';
98
+
99
+ export const GET = defineHandler((c) => c.html(<h1>Hello!</h1>));
100
+ ```
101
+
64
102
  ## `defineHandler`
65
103
 
66
104
  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.).
package/docs/guide/ssg.md CHANGED
@@ -37,6 +37,14 @@ export async function getPrerenderPaths() {
37
37
  }
38
38
  ```
39
39
 
40
+ ## 404 pages
41
+
42
+ Static builds that prerender at least one page include a default `404.html` in `dist/client/`. To provide your own, add `public/404.html` or prerender a page at `/404`.
43
+
44
+ Fully static Void deployments serve this page for missing URLs with an HTTP 404 status by default. You can override this with [`routing.notFound`](../reference/config.md#routing-notfound). Hybrid deployments handle missing URLs in the Worker.
45
+
46
+ When hosting `dist/client/` yourself, configure your host to serve `404.html` with a 404 status for missing pages.
47
+
40
48
  ## Comparison with edge prerendering
41
49
 
42
50
  | `output` value | Default prerender | Per-page override | Prerender timing |
@@ -104,13 +104,13 @@ The `action()` helper gets the same type checking. See [Actions & Forms](./pages
104
104
 
105
105
  ## What Gets Checked
106
106
 
107
- | Layer | What's type-checked |
108
- | ------------ | -------------------------------------------------------------------- |
109
- | Database | Table columns, value types, insert/update shapes from Drizzle schema |
110
- | Handlers | `c.env` bindings, validator input, return type |
111
- | Fetch client | Route paths, HTTP methods, `body`/`query`/`params`, response type |
112
- | `useForm` | Action URLs, `data` fields, `errors` keys, `reset()` args, `params` |
113
- | `action()` | Action URLs, `data` payload, `params` |
107
+ | Layer | What's type-checked |
108
+ | ------------ | --------------------------------------------------------------------------------------------------------------------- |
109
+ | Database | Table columns, value types, insert/update shapes from Drizzle schema |
110
+ | Handlers | `c.env` bindings, validator input, return type |
111
+ | Fetch client | Route paths, HTTP methods, `body`/`query`/`params`, response type |
112
+ | `useForm` | Action URLs, `data` fields, `errors` keys, `reset()` args (including `{ initial: true }`), `params`, `resetOnSuccess` |
113
+ | `action()` | Action URLs, `data` payload, `params` |
114
114
 
115
115
  ## Serialization
116
116
 
@@ -14,7 +14,7 @@ For example, `/rooms/[id]` gives each chat room its own instance. Use it for cha
14
14
 
15
15
  ## Route files
16
16
 
17
- Create WebSocket routes in `routes/` with the `.ws.ts` suffix:
17
+ Create WebSocket routes in `routes/` using `.ws.ts`, `.ws.tsx`, `.ws.js`, `.ws.jsx`, `.ws.mts`, or `.ws.mjs`:
18
18
 
19
19
  ```text
20
20
  routes/
@@ -22,6 +22,8 @@ routes/
22
22
  notifications.ws.ts
23
23
  ```
24
24
 
25
+ Use `.ws.tsx` or `.ws.jsx` when a handler contains JSX, such as rendering HTML fragments to send to connected clients. See [JSX handlers](./server-routing.md#jsx-handlers) for the JSX runtime.
26
+
25
27
  Filename rules match regular server routes:
26
28
 
27
29
  - `index.ws.ts` becomes the parent path
@@ -30,7 +32,7 @@ Filename rules match regular server routes:
30
32
  - route groups like `(marketing)/chat.ws.ts` are ignored in the URL
31
33
  - `chat.dev.ws.ts` / `chat.prod.ws.ts` restrict the route to one environment
32
34
 
33
- The environment suffix goes **before** `.ws`. `chat.ws.dev.ts` is not recognised.
35
+ The environment suffix goes **before** `.ws`. `chat.ws.dev.ts` is not a WebSocket route.
34
36
 
35
37
  ## `defineRoom()`
36
38
 
@@ -133,13 +133,14 @@ Apps using email need `email.from` in `void.config.ts`. Void reviews the domain'
133
133
 
134
134
  Set these CI secrets and variables as needed:
135
135
 
136
- | Name | When needed |
137
- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
138
- | `CLOUDFLARE_API_TOKEN` | Required. Workers Scripts: Edit, read access to bound resources, and edit access to products Void provisions. |
139
- | `CLOUDFLARE_ACCOUNT_ID` | When the token can access several accounts. |
140
- | `DATABASE_URL` | PostgreSQL and MySQL provisioning and migrations. |
141
- | `CLOUDFLARE_WORKERS_SUBDOMAIN` | When the Worker has no version preview URL. Use `my-team` or `my-team.workers.dev`. |
142
- | `CF_ACCESS_CLIENT_ID`, `CF_ACCESS_CLIENT_SECRET` | When Cloudflare Access protects the readiness URL. |
136
+ | Name | When needed |
137
+ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
138
+ | `CLOUDFLARE_API_TOKEN` | Required. Workers Scripts: Edit, read access to bound resources, and edit access to products Void provisions. |
139
+ | `CLOUDFLARE_ACCOUNT_ID` | When the token can access several accounts. |
140
+ | `DATABASE_URL` | PostgreSQL and MySQL provisioning and migrations. |
141
+ | `CLOUDFLARE_WORKERS_SUBDOMAIN` | When the Worker has no version preview URL. Use `my-team` or `my-team.workers.dev`. |
142
+ | `CF_ACCESS_CLIENT_ID`, `CF_ACCESS_CLIENT_SECRET` | When Cloudflare Access protects the readiness URL. |
143
+ | `CF_ACCESS_READINESS_ORIGIN` | When Access protects a route or Custom Domain and `workers_dev` is `false`. See [Cloudflare Access](#cloudflare-access). |
143
144
 
144
145
  For email, run `void email setup --platform cloudflare` locally, commit `void.lock.json`, and pass `--require-email` in CI. The token also needs Email Routing Edit and Email Sending Edit.
145
146
 
@@ -153,6 +154,14 @@ export CF_ACCESS_TOKEN="$(cloudflared access token --app=https://<worker>.<accou
153
154
 
154
155
  Void uses Access credentials for readiness checks without saving them. If checks are denied, the deployment fails. A first-deploy bootstrap can already be active behind Access when this happens.
155
156
 
157
+ By default, Void sends Access credentials only to `workers.dev` URLs. If `workers_dev` is `false`, Void can check readiness through your first route or Custom Domain. To send the credentials there, set `CF_ACCESS_READINESS_ORIGIN` to that hostname's origin:
158
+
159
+ ```sh
160
+ export CF_ACCESS_READINESS_ORIGIN="https://app.example.com"
161
+ ```
162
+
163
+ Use one exact `https:` origin, without a wildcard or a path. Set the variable in the shell or CI environment that runs `void deploy`. Void ignores a value that `void.config.ts` sets, for example through a dotenv import. Set it only for a hostname that you control and that serves this Worker, because Void sends your Access credentials to it.
164
+
156
165
  ### Databases and secrets
157
166
 
158
167
  For PostgreSQL and MySQL, supply `DATABASE_URL` to the deploy process. Void uses it for Hyperdrive and migrations without writing it to config. MySQL schema changes can partially apply before an error; see [failed MySQL migrations](../guide/database/mysql.md#failed-migrations).
@@ -79,7 +79,7 @@ function requireAuth(c: CloudContext): AuthUser;
79
79
 
80
80
  ## `AuthUser` {#authuser}
81
81
 
82
- Better Auth user shape re-exported by Void.
82
+ Better Auth user shape re-exported by Void. It also includes the fields that Better Auth plugins and `user.additionalFields` in `auth.ts` add, for example `role` from `admin()` or a custom `plan` field. Your tsconfig needs the generated Void types for this; see [Type Safety setup](../../guide/type-safety.md#setup).
83
83
 
84
84
  ```ts
85
85
  interface AuthUser {
@@ -95,7 +95,7 @@ interface AuthUser {
95
95
 
96
96
  ## `AuthSession` {#authsession}
97
97
 
98
- Better Auth session shape re-exported by Void.
98
+ Better Auth session shape re-exported by Void. It also includes the session fields that Better Auth plugins in `auth.ts` add, for example `impersonatedBy` from `admin()`.
99
99
 
100
100
  ```ts
101
101
  interface AuthSession {
@@ -110,6 +110,10 @@ interface AuthSession {
110
110
  }
111
111
  ```
112
112
 
113
+ ::: tip Types inside `auth.ts`
114
+ `AuthUser` and `AuthSession` are built from `auth.ts`. Inside `auth.ts` and the files it imports, don't use them, or values from `getUser()`, `getSession()` and `requireAuth()`, to type a hook's parameters or returned data. TypeScript reports a circular reference and the extra fields are lost. Let Better Auth infer those types, or use its own `User` and `Session` types. Better Auth callbacks such as `sendResetPassword` pass its base `User`, without plugin fields.
115
+ :::
116
+
113
117
  ## `AuthState` {#authstate}
114
118
 
115
119
  Combined authenticated request state:
@@ -33,7 +33,10 @@ Use `prepareCfBuildOutput` to convert an existing Cloudflare Worker build into a
33
33
  ```ts
34
34
  import { prepareCfBuildOutput } from 'void/deploy';
35
35
 
36
- const prepared = await prepareCfBuildOutput('/path/to/app/dist/server', { mode: 'staging' });
36
+ const prepared = await prepareCfBuildOutput('/path/to/app/dist/server', {
37
+ projectRoot: '/path/to/app',
38
+ mode: 'staging',
39
+ });
37
40
  if (!prepared) {
38
41
  throw new Error('The Worker build could not be prepared.');
39
42
  }
@@ -47,9 +50,10 @@ try {
47
50
 
48
51
  Pass the Worker output directory that contains a generated `wrangler.json`, such as `dist/ssr`, `dist/rsc`, or `dist/server`. When Cloudflare's build tooling built the app, pass the project root that contains its native Build Output at `.cloudflare/output/v0` instead. Preparation copies modules and assets without changing that input. Native output keeps its module types, auxiliary Workers, Container declarations, and build metadata. Preparation does not provision resources, upload a Worker, or activate a deployment. Container images built from a local Dockerfile exist only in the Docker environment that built them, so publish from that environment.
49
52
 
50
- | Option | Description |
51
- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
52
- | `accountId` | Cloudflare account the build must target. A different account in the build returns `null`. When omitted, the build's account is kept or left unset. |
53
- | `mode` | Vite build mode. Native output keeps the mode it was built with, and a different value returns `null`. For other output it defaults to `production`. |
53
+ | Option | Description |
54
+ | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
55
+ | `accountId` | Cloudflare account the build must target. A different account in the build returns `null`. When omitted, the build's account is kept or left unset. |
56
+ | `mode` | Vite build mode. Native output keeps the mode it was built with, and a different value returns `null`. For other output it defaults to `production`. |
57
+ | `projectRoot` | App directory containing `package.json` and its installed dependencies. Defaults to the input directory. |
54
58
 
55
59
  The result contains `directory`, an optional `assetsDirectory` pointing to the input assets, and an asynchronous `dispose()` method. Always dispose the result after use. Unsupported build configuration returns `null`. A directory without a Worker build, a missing or invalid Worker module, or an asset symlink outside the asset directory throws an error.
@@ -49,7 +49,7 @@ Imported from `"@void/vue"`.
49
49
  | `useRouter()` | Returns the Void Router with current route state (`url`, `path`, `query`, `params`) and navigation methods: `visit`, `refresh`, awaitable `prefetch`, `flush`, `flushAll`. |
50
50
  | `useParams()` | Returns dynamic route params for the page currently being rendered. |
51
51
  | `useNavigation()` | Returns pending navigation state: `{ state, location, method }`, where `state` is `"idle"`, `"loading"`, or `"submitting"` and `location` is the pending destination. |
52
- | `useForm(url, defaults, options?)` | Typed reactive form helper bound to a page action URL. Returns `{ data, post, put, patch, delete, pending, errors, error, hasChanges, wasSuccessful, recentlySuccessful, reset, clearErrors, clearError }`. |
52
+ | `useForm(url, defaults, options?)` | Typed reactive form helper bound to a page action URL. Returns `{ data, post, put, patch, delete, pending, errors, error, hasChanges, wasSuccessful, recentlySuccessful, reset, setError, clearErrors, clearError }`. |
53
53
  | `useIslandForm(defaults)` | Form helper for island components. Same form state as `useForm()`, plus `submit(url, method)`. `post(url)`, `put(url)`, `patch(url)`, and `delete(url)` take the action URL and submit with `fetch()`. |
54
54
  | `action(url, options?)` | Awaitable one-shot page action helper. Uses `POST` by default and accepts `{ data, method, params }`, where `method` can be `"PUT"`, `"PATCH"`, or `"DELETE"`. Returns an `ActionResult`. |
55
55
  | `useShared()` | Returns shared data injected by middleware via `c.set("shared", {...})`. |
@@ -101,7 +101,7 @@ Imported from `"@void/react"`.
101
101
  | `useRouter()` | Returns the Void Router with current route state (`url`, `path`, `query`, `params`) and navigation methods: `visit`, `refresh`, awaitable `prefetch`, `flush`, `flushAll`. React navigations are scheduled as transitions. |
102
102
  | `useParams()` | Returns dynamic route params for the page currently being rendered. |
103
103
  | `useNavigation()` | Returns pending navigation state: `{ state, location, method }`, where `state` is `"idle"`, `"loading"`, or `"submitting"` and `location` is the pending destination. |
104
- | `useForm(url, defaults, options?)` | Form helper hook. Returns `{ data, setData, post, put, patch, delete, pending, errors, error, hasChanges, wasSuccessful, recentlySuccessful, reset, clearErrors, clearError }`. |
104
+ | `useForm(url, defaults, options?)` | Form helper hook. Returns `{ data, setData, post, put, patch, delete, pending, errors, error, hasChanges, wasSuccessful, recentlySuccessful, reset, setError, clearErrors, clearError }`. |
105
105
  | `useIslandForm(defaults)` | Form helper hook for island components. Same form state as `useForm()`. `post(url)`, `put(url)`, `patch(url)`, and `delete(url)` take the action URL and submit with `fetch()`. |
106
106
  | `action(url, options?)` | Awaitable one-shot page action helper. Uses `POST` by default and accepts `{ data, method, params }`, where `method` can be `"PUT"`, `"PATCH"`, or `"DELETE"`. Returns an `ActionResult`. |
107
107
  | `useShared()` | Returns shared data injected by middleware via `c.set("shared", {...})`. |
@@ -109,6 +109,8 @@ Imported from `"@void/react"`.
109
109
 
110
110
  React `Link` GET `data` is merged into the rendered `href` query string. Primitive values are serialized with `String(value)`, arrays become repeated keys, `null` and `undefined` are omitted, and nested objects throw. `prefetch` and `reloadDocument` are GET-only and throw for mutation links.
111
111
 
112
+ React `useForm()` options accept `onSuccess?: () => void | Promise<void>` and `onError?: (error: Record<string, string> | VoidActionError) => void | Promise<void>`. Each submission captures its callbacks, runs them before the updated UI commits, and awaits any returned promise. See [submission callbacks](../../guide/pages-routing/actions-and-forms.md#react-submission-callbacks).
113
+
112
114
  React deferred props returned from `defer()` are Suspense resources. Read them
113
115
  with React's `use()` inside a `<Suspense>` boundary. React Pages requires React
114
116
  19 and uses streaming SSR for the initial deferred shell. Rejections throw from
@@ -158,7 +160,7 @@ Imported from `"@void/svelte"`.
158
160
  | Export | Description |
159
161
  | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
160
162
  | `Link` | Svelte component for SPA navigation. Renders `<a>` for GET, `<button>` for non-GET methods. Props: `href`, `method`, `data`, `prefetch`, `cacheFor`, `preserveScroll`, `preserveState`, `replace`, `reloadDocument`, `viewTransition`, `onNavigate`. |
161
- | `useForm(url, defaults, options?)` | Form helper using Svelte 5 runes. Returns `{ data, post, put, patch, delete, pending, errors, error, hasChanges, wasSuccessful, recentlySuccessful, reset, clearErrors, clearError }`. |
163
+ | `useForm(url, defaults, options?)` | Form helper using Svelte 5 runes. Returns `{ data, post, put, patch, delete, pending, errors, error, hasChanges, wasSuccessful, recentlySuccessful, reset, setError, clearErrors, clearError }`. |
162
164
  | `useIslandForm(defaults)` | Form helper for island components. Same form state as `useForm()`, plus `submit(url, method)`. `post(url)`, `put(url)`, `patch(url)`, and `delete(url)` take the action URL and submit with `fetch()`. |
163
165
  | `action(url, options?)` | Awaitable one-shot page action helper. Uses `POST` by default and accepts `{ data, method, params }`, where `method` can be `"PUT"`, `"PATCH"`, or `"DELETE"`. Returns an `ActionResult`. |
164
166
  | `useShared()` | Returns shared data injected by middleware via `c.set("shared", {...})`. |
@@ -208,7 +210,7 @@ Imported from `"@void/solid"`.
208
210
  | Export | Description |
209
211
  | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
210
212
  | `Link` | Solid component for SPA navigation. Renders `<a>` for GET, `<button>` for non-GET methods. Props: `href`, `method`, `data`, `prefetch`, `cacheFor`, `preserveScroll`, `preserveState`, `replace`, `reloadDocument`, `viewTransition`, `onNavigate`. |
211
- | `useForm(url, defaults, options?)` | Form helper using Solid stores. Returns `{ data, setData, post, put, patch, delete, pending, errors, error, hasChanges, wasSuccessful, recentlySuccessful, reset, clearErrors, clearError }`. |
213
+ | `useForm(url, defaults, options?)` | Form helper using Solid stores. Returns `{ data, setData, post, put, patch, delete, pending, errors, error, hasChanges, wasSuccessful, recentlySuccessful, reset, setError, clearErrors, clearError }`. |
212
214
  | `useIslandForm(defaults)` | Form helper for island components. Same form state as `useForm()`. `post(url)`, `put(url)`, `patch(url)`, and `delete(url)` take the action URL and submit with `fetch()`. |
213
215
  | `action(url, options?)` | Awaitable one-shot page action helper. Uses `POST` by default and accepts `{ data, method, params }`, where `method` can be `"PUT"`, `"PATCH"`, or `"DELETE"`. Returns an `ActionResult`. |
214
216
  | `useShared()` | Returns shared data injected by middleware via `c.set("shared", {...})`. |
@@ -86,15 +86,16 @@ void deploy --platform cloudflare --require-email # fail instead of deploying
86
86
 
87
87
  Void signs you in through your browser when needed and saves the selected account. In CI, set `CLOUDFLARE_API_TOKEN` and, if the token can access several accounts, `CLOUDFLARE_ACCOUNT_ID`.
88
88
 
89
- | Option or setting | Cloudflare behavior |
90
- | ------------------------------ | -------------------------------------------------------------------------------------- |
91
- | `--dir`, `--spa` | Deploy static output through a small Worker and Workers Assets |
92
- | `--skip-build` | Reuse existing static, SPA, or SSG output; unavailable for Worker apps |
93
- | `--project` | Unavailable; the Worker and account come from `void.config.ts` and `void.lock.json` |
94
- | Named environments | Unavailable; use the top-level `cloudflare` config |
95
- | `CLOUDFLARE_WORKERS_SUBDOMAIN` | Needed in fresh CI when versions have no preview URL; cached locally after a deploy |
96
- | `--require-email` | Fail instead of deploying without email when the email step cannot run, as in CI |
97
- | `DATABASE_URL` | Required in the deploy environment for PostgreSQL or MySQL provisioning and migrations |
89
+ | Option or setting | Cloudflare behavior |
90
+ | ------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
91
+ | `--dir`, `--spa` | Deploy static output through a small Worker and Workers Assets |
92
+ | `--skip-build` | Reuse existing static, SPA, or SSG output; unavailable for Worker apps |
93
+ | `--project` | Unavailable; the Worker and account come from `void.config.ts` and `void.lock.json` |
94
+ | Named environments | Unavailable; use the top-level `cloudflare` config |
95
+ | `CLOUDFLARE_WORKERS_SUBDOMAIN` | Needed in fresh CI when versions have no preview URL; cached locally after a deploy |
96
+ | `CF_ACCESS_READINESS_ORIGIN` | Lets Access credentials reach the readiness check on a route or Custom Domain, for example `https://app.example.com` |
97
+ | `--require-email` | Fail instead of deploying without email when the email step cannot run, as in CI |
98
+ | `DATABASE_URL` | Required in the deploy environment for PostgreSQL or MySQL provisioning and migrations |
98
99
 
99
100
  The token needs Workers Scripts: Edit, read access to bound resources, and edit access to products Void provisions. Commit `void.lock.json` after the first deploy before using other machines or CI.
100
101
 
@@ -50,6 +50,19 @@ void email disallow <address> [--project <name>]
50
50
 
51
51
  Remove one recipient from the project's destination list. New sends to that address are refused immediately; previously admitted attempts may finish. No deploy is involved.
52
52
 
53
+ ## `void email alias` {#void-email-alias}
54
+
55
+ ```
56
+ void email alias [--project <name>]
57
+ void email alias <name> [--yes] [--project <name>]
58
+ ```
59
+
60
+ With no name, show the project's address on the platform's shared mail domain and whether inbound mail reaches it.
61
+
62
+ With a name, give that address a shorter name: `void email alias acme` makes `acme@<mail domain>` and `acme+<tag>@<mail domain>` reach the project and lets it send from them. Only the project owner can set it. The name follows the same rules as a project slug, must not be used by any other project's slug or alias, and names of five characters or fewer need a plan that allows short names.
63
+
64
+ The alias is permanent: it cannot be changed or removed, and it is freed only when the project is deleted. The slug address keeps working for sending and receiving, so nothing that already uses it breaks. The CLI asks for confirmation; pass `--yes` to skip the prompt, which is required outside an interactive terminal. Inbound mail at the alias starts once the project has `email/` handlers deployed and its recipient rule is ready; run `void email alias` to check. Redeploy with the current `void` before you share the alias: an app built with an older version sees `message.to` with the alias still in front (`acme+support@…` instead of `support@…`).
65
+
53
66
  ## `void email domain` {#void-email-domain}
54
67
 
55
68
  ```
@@ -66,14 +66,14 @@ Register and maintain email domains for projects whose owners hold no Cloudflare
66
66
 
67
67
  ```sh
68
68
  void platform email domains [--project <id|slug>]
69
- void platform email domain-add <domain> --project <id|slug> [--token-stdin]
69
+ void platform email domain-add <domain> --project <id|slug> [--token-stdin] [--outbound-only]
70
70
  void platform email domain-status <domain>
71
71
  void platform email domain-sync <domain>
72
72
  void platform email domain-rotate-secret <domain>
73
73
  void platform email domain-remove <domain> [--token-stdin]
74
74
  ```
75
75
 
76
- `domain-add` uses the platform's Cloudflare credential for zones in its account. For another account, pipe a scoped Cloudflare API token on standard input with `--token-stdin --yes`. Managed custom inbound email requires the Cloudflare zone apex (`example.com`). If its MX records already belong to another mail provider, use the project's shared email address or choose another unused zone. Native Cloudflare deployments also support literal recipient addresses on subdomains; see [Your own Cloudflare account](../../guide/email/domains.md#your-own-cloudflare-account).
76
+ `domain-add` uses the platform's Cloudflare credential for zones in its account. For another account, pipe a scoped Cloudflare API token on standard input with `--token-stdin --yes`. Managed custom inbound email requires the Cloudflare zone apex (`example.com`). If its MX records already belong to another mail provider, such as Microsoft 365 or Google Workspace, add `--outbound-only` to send from the apex while that provider keeps receiving its mail; see [Sending from a domain that already receives mail](../../guide/platform/administration/email.md#sending-from-a-domain-that-already-receives-mail). Otherwise use the project's shared email address or choose another unused zone. Native Cloudflare deployments also support literal recipient addresses on subdomains; see [Your own Cloudflare account](../../guide/email/domains.md#your-own-cloudflare-account).
77
77
 
78
78
  `domain-status` shows inbound, outbound, and credential-management readiness with the latest operation. A blocked operation resumes through `domain-sync`; a blocked rotation resumes through `domain-rotate-secret`, preserving already confirmed steps and its staged credential. An uncertain operation remains stopped until read-back proves the result or an administrator uses `operation-resolve`. Domains an administrator adds show `managed_by: admin`; their owners can list and inspect them but use these commands for `sync`, `domain-rotate-secret`, and `remove`.
79
79
 
@@ -73,6 +73,7 @@ Use this page as a command reference. If you are setting up a project for the fi
73
73
  | `void email destinations` | List verified recipient addresses |
74
74
  | `void email allow <address>` | Add a recipient and send a verification email |
75
75
  | `void email disallow <address>` | Remove a recipient from the allowlist |
76
+ | `void email alias [name]` | Show the shared mail address, or give it a shorter permanent name |
76
77
  | `void email domain` | Send and receive at your own domain on a Cloudflare zone |
77
78
  | `void init` | Set up a new or existing project, including coding agents |
78
79
  | `void migrate` | Convert legacy `void.json` and root Wrangler JSON/JSONC into `void.config.ts` |
@@ -297,7 +297,7 @@ Void-managed WebSocket route files (`*.ws.ts`) are Cloudflare-only because they
297
297
 
298
298
  ### `worker`
299
299
 
300
- Curated Cloudflare Workers configuration. `void init` pins `compatibility_date` here. Binding arrays such as `d1_databases`, `kv_namespaces`, and `r2_buckets` go in [`cloudflare`](#cloudflare), where Void preserves custom settings and resolved IDs during deployment.
300
+ Curated Cloudflare Workers configuration. `void init` pins `compatibility_date` here. If a project has no config file and no date, the first dev server or build creates `void.config.ts` with only this date and adds `.void-wrangler.jsonc` to `.gitignore`. Astro, SvelteKit, Nuxt, and Analog apps get the date in a root `wrangler.jsonc` instead, which their adapter reads by default. A project with a `wrangler.toml` gets it in `void.json`. Binding arrays such as `d1_databases`, `kv_namespaces`, and `r2_buckets` go in [`cloudflare`](#cloudflare), where Void preserves custom settings and resolved IDs during deployment.
301
301
 
302
302
  | Field | Type | Description |
303
303
  | --------------------- | ---------- | -------------------------------------- |
@@ -366,6 +366,16 @@ This choice applies to subsequent `void deploy` runs. Atomic deployment sends tr
366
366
 
367
367
  Routing and edge configuration for headers, redirects, rewrites, and caching.
368
368
 
369
+ #### `routing.alwaysUseHttps`
370
+
371
+ Redirect HTTP requests to HTTPS with a permanent `308` redirect. Omitted or `false` means off. Requires the Cloudflare target. `localhost` stays on HTTP. See [HTTP to HTTPS](../guide/edge/redirects#http-to-https) for where it works.
372
+
373
+ ```ts
374
+ routing: {
375
+ alwaysUseHttps: true,
376
+ }
377
+ ```
378
+
369
379
  #### `routing.headers`
370
380
 
371
381
  Custom response headers. Keys are URL patterns, values are arrays of `"Name: value"` strings. See [Custom Headers](../guide/edge/headers) for details.
@@ -67,6 +67,7 @@ File-based HTTP API endpoints built on [Hono](https://hono.dev). Each file expor
67
67
  <RoutesFileTree />
68
68
 
69
69
  - **Named exports**: `export const GET = defineHandler(...)`, `export const POST = ...`
70
+ - **Extensions**: `.ts`, `.tsx`, `.js`, `.jsx`, `.mts`, or `.mjs`; use `.tsx` or `.jsx` for JSX handlers
70
71
  - **Dynamic segments**: `[id]` becomes `:id` route parameter
71
72
  - **Catch-all**: `[...slug]` matches the remaining path
72
73
  - **Route groups**: `(admin)/` organizes files without affecting URL paths
@@ -81,9 +82,10 @@ Full-stack server-rendered pages with co-located data loading. Available with Vu
81
82
 
82
83
  <PagesFileTree />
83
84
 
84
- - **`.server.ts` files** export `loader` for `GET` data and `action` for mutations, paired with the page component of the same name
85
+ - **`.server.ts` (or `.tsx`, `.js`, `.jsx`, `.mts`, `.mjs`) files** export `loader` for `GET` data and `action` for mutations, paired with the page component of the same name
85
86
  - **Layouts** nest automatically and persist across navigations
86
87
  - **Markdown** pages are supported with frontmatter
88
+ - **Environment suffix**: `debug.dev.tsx` is served in development only, `stats.prod.tsx` in production only
87
89
 
88
90
  See [Pages Routing](/guide/pages-routing/overview) for the full guide.
89
91
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "void",
3
- "version": "1.2.1",
3
+ "version": "1.4.0",
4
4
  "description": "Full-stack framework and deployment CLI for Cloudflare Workers, powered by Vite.",
5
5
  "homepage": "https://void.cloud",
6
6
  "bugs": {
@@ -20,6 +20,7 @@
20
20
  "docs",
21
21
  "schema.json",
22
22
  "skills",
23
+ "templates",
23
24
  "AGENTS_TEMPLATE.md",
24
25
  "README.md",
25
26
  "sandbox.Dockerfile"
@@ -347,12 +348,13 @@
347
348
  "@cloudflare/vite-plugin": "^1.64.0",
348
349
  "@cloudflare/workers-auth": "^0.14.1",
349
350
  "@cloudflare/workers-types": "^5.20261019.1",
351
+ "@cloudflare/workers-utils": "^0.47.3",
350
352
  "@napi-rs/keyring": "^2.1.2",
351
- "@void/deploy-cloudflare": "1.2.1",
352
- "@void/deploy-core": "1.2.1",
353
- "@void/edge": "1.2.1",
354
- "@void/isr": "1.2.1",
355
- "@void/platform": "1.2.1",
353
+ "@void/deploy-cloudflare": "1.4.0",
354
+ "@void/deploy-core": "1.4.0",
355
+ "@void/edge": "1.4.0",
356
+ "@void/isr": "1.4.0",
357
+ "@void/platform": "1.4.0",
356
358
  "better-auth": "^1.7.11",
357
359
  "better-sqlite3": "^13.0.3",
358
360
  "blake3-jit": "^1.1.0",
@@ -401,7 +403,7 @@
401
403
  "zod": "^4.6.7"
402
404
  },
403
405
  "peerDependencies": {
404
- "@void/md": "1.2.1",
406
+ "@void/md": "1.4.0",
405
407
  "arktype": ">=2.0.0",
406
408
  "valibot": ">=1.0.0-beta.7",
407
409
  "vite": "^8.0.0",
package/schema.json CHANGED
@@ -144,6 +144,10 @@
144
144
  "type": "object",
145
145
  "description": "Routing configuration — headers, redirects, rewrites, fallbacks, revalidation, prerendering.",
146
146
  "properties": {
147
+ "alwaysUseHttps": {
148
+ "type": "boolean",
149
+ "description": "Redirect HTTP requests to HTTPS with a permanent 308 redirect. Omitted or false means off. Requires the Cloudflare target; localhost stays on HTTP."
150
+ },
147
151
  "isr": {
148
152
  "type": "boolean",
149
153
  "description": "Enable inferred ISR caching. Set false to disable ISR and edge prerendering, including per-page prerender and revalidate policies. Does not disable output: static build-time prerendering."
@@ -49,7 +49,7 @@ the app's existing saved destination, including when the target project is new.
49
49
  | Email | `docs/guide/email.md`, then the matching guide under `docs/guide/email/` |
50
50
  | KV / storage / queues / cron jobs | `docs/guide/kv.md`, `docs/guide/storage.md`, `docs/guide/queues.md`, `docs/guide/jobs.md` |
51
51
  | SSR and caching | `docs/guide/ssr.md`, `docs/guide/edge/*.md` |
52
- | Rewrites, redirects, fallbacks | `docs/guide/edge/rewrites.md`, `docs/guide/edge/redirects.md`, `docs/reference/config.md` |
52
+ | HTTPS, rewrites, redirects, fallbacks | `docs/guide/edge/rewrites.md`, `docs/guide/edge/redirects.md`, `docs/reference/config.md` |
53
53
  | Static site generation | `docs/guide/ssg.md` |
54
54
  | Connected apps and artifact preparation | `docs/guide/platform/administration/connected-apps.md`, `docs/reference/api/deploy.md` |
55
55
  | Deployment and CI | `docs/guide/deployment.md`, `docs/reference/cli/deploy.md` |
@@ -0,0 +1,49 @@
1
+ <!--
2
+ Portions of the starter UI and assets are adapted from Vite.
3
+
4
+ MIT License
5
+
6
+ Copyright (c) 2019-present, VoidZero Inc. and Vite contributors
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
25
+ -->
26
+ <svg xmlns="http://www.w3.org/2000/svg">
27
+ <symbol id="bluesky-icon" viewBox="0 0 16 17">
28
+ <g clip-path="url(#bluesky-clip)"><path fill="currentColor" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
29
+ <defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
30
+ </symbol>
31
+ <symbol id="discord-icon" viewBox="0 0 20 19">
32
+ <path fill="currentColor" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
33
+ </symbol>
34
+ <symbol id="documentation-icon" viewBox="0 0 21 20">
35
+ <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
36
+ <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
37
+ <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
38
+ </symbol>
39
+ <symbol id="github-icon" viewBox="0 0 19 19">
40
+ <path fill="currentColor" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
41
+ </symbol>
42
+ <symbol id="social-icon" viewBox="0 0 20 20">
43
+ <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
44
+ <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
45
+ </symbol>
46
+ <symbol id="x-icon" viewBox="0 0 19 19">
47
+ <path fill="currentColor" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
48
+ </symbol>
49
+ </svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 166 155.3"><path d="M163 35S110-4 69 5l-3 1c-6 2-11 5-14 9l-2 3-15 26 26 5c11 7 25 10 38 7l46 9 18-30z" fill="#76b3e1"/><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="27.5" y1="3" x2="152" y2="63.5"><stop offset=".1" stop-color="#76b3e1"/><stop offset=".3" stop-color="#dcf2fd"/><stop offset="1" stop-color="#76b3e1"/></linearGradient><path d="M163 35S110-4 69 5l-3 1c-6 2-11 5-14 9l-2 3-15 26 26 5c11 7 25 10 38 7l46 9 18-30z" opacity=".3" fill="url(#a)"/><path d="M52 35l-4 1c-17 5-22 21-13 35 10 13 31 20 48 15l62-21S92 26 52 35z" fill="#518ac8"/><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="95.8" y1="32.6" x2="74" y2="105.2"><stop offset="0" stop-color="#76b3e1"/><stop offset=".5" stop-color="#4377bb"/><stop offset="1" stop-color="#1f3b77"/></linearGradient><path d="M52 35l-4 1c-17 5-22 21-13 35 10 13 31 20 48 15l62-21S92 26 52 35z" opacity=".3" fill="url(#b)"/><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="18.4" y1="64.2" x2="144.3" y2="149.8"><stop offset="0" stop-color="#315aa9"/><stop offset=".5" stop-color="#518ac8"/><stop offset="1" stop-color="#315aa9"/></linearGradient><path d="M134 80a45 45 0 00-48-15L24 85 4 120l112 19 20-36c4-7 3-15-2-23z" fill="url(#c)"/><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="75.2" y1="74.5" x2="24.4" y2="260.8"><stop offset="0" stop-color="#4377bb"/><stop offset=".5" stop-color="#1a336b"/><stop offset="1" stop-color="#1a336b"/></linearGradient><path d="M114 115a45 45 0 00-48-15L4 120s53 40 94 30l3-1c17-5 23-21 13-34z" fill="url(#d)"/></svg>
@@ -0,0 +1,35 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 343 361" fill="none">
2
+ <defs>
3
+ <linearGradient id="edge" x1="13" y1="0" x2="329" y2="0" gradientUnits="userSpaceOnUse">
4
+ <stop stop-color="#5420a4" />
5
+ <stop offset=".12" stop-color="#983ee8" />
6
+ <stop offset=".24" stop-color="#5919a0" />
7
+ <stop offset=".4" stop-color="#a465ed" />
8
+ <stop offset=".52" stop-color="#8044bc" />
9
+ <stop offset=".62" stop-color="#d2a2ff" />
10
+ <stop offset=".7" stop-color="#7331c5" />
11
+ <stop offset=".82" stop-color="#a45aff" />
12
+ <stop offset=".91" stop-color="#5721a1" />
13
+ <stop offset="1" stop-color="#7737cd" />
14
+ </linearGradient>
15
+ <path id="side" d="M13 246v25q0 8 14 15l121 69q23 12 46 0l121-69q14-8 14-16v-24q0 8-14 15l-121 68q-23 12-46 0L27 261q-14-8-14-15Z" />
16
+ <clipPath id="side-clip"><use href="#side" /></clipPath>
17
+ <filter id="grain" x="0" y="0" width="100%" height="100%" color-interpolation-filters="sRGB">
18
+ <feTurbulence type="fractalNoise" baseFrequency=".7" numOctaves="3" seed="7" />
19
+ <feColorMatrix type="saturate" values="0" />
20
+ <feBlend in="SourceGraphic" mode="soft-light" />
21
+ <feComposite in2="SourceGraphic" operator="in" />
22
+ </filter>
23
+ </defs>
24
+ <path d="M14 125v104m315-104v104" stroke="#716779" stroke-width="1.5" stroke-dasharray="4 4" />
25
+ <g clip-path="url(#side-clip)" filter="url(#grain)">
26
+ <use href="#side" fill="url(#edge)" />
27
+ <path d="m30 253 15 9 9 40-15-9Zm42 23 17 10 8 39-17-10Zm53 29 13 8 8 39-13-8Zm80 1 17-10-8 43-17 10Zm52-30 13-8-8 43-13 8Zm43-25 12-7-8 43-12 7Z" fill="#35105f" opacity=".65" />
28
+ <path d="m54 267 9 5 9 40-9-5Zm45 25 9 5 8 39-9-5Zm130 1 8-5-8 43-8 5Zm53-30 7-4-8 43-7 4Z" fill="#d3a8ff" opacity=".55" />
29
+ </g>
30
+ <path d="m105 183-78 45q-14 8-14 18t14 13l121 70q23 10 46 0l121-70q14-5 14-13t-14-18l-77-45" stroke="#9b31ff" stroke-width="1.5" />
31
+ <g stroke="#938b9b" stroke-width="1.5">
32
+ <path d="M14 100v24q0 8 14 14l120 70q23 10 46 0l121-70q14-6 14-14v-24" />
33
+ <path d="M148 14q23-13 46 0l121 70q14 8 14 16t-14 14l-121 70q-23 10-46 0L28 114q-14-6-14-14t14-16Z" />
34
+ </g>
35
+ </svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="26.6" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 308"><path fill="#FF3E00" d="M239.682 40.707C211.113-.182 154.69-12.301 113.895 13.69L42.247 59.356a82.198 82.198 0 0 0-37.135 55.056a86.566 86.566 0 0 0 8.536 55.576a82.425 82.425 0 0 0-12.296 30.719a87.596 87.596 0 0 0 14.964 66.244c28.574 40.893 84.997 53.007 125.787 27.016l71.648-45.664a82.182 82.182 0 0 0 37.135-55.057a86.601 86.601 0 0 0-8.53-55.577a82.409 82.409 0 0 0 12.29-30.718a87.573 87.573 0 0 0-14.963-66.244"></path><path fill="#FFF" d="M106.889 270.841c-23.102 6.007-47.497-3.036-61.103-22.648a52.685 52.685 0 0 1-9.003-39.85a49.978 49.978 0 0 1 1.713-6.693l1.35-4.115l3.671 2.697a92.447 92.447 0 0 0 28.036 14.007l2.663.808l-.245 2.659a16.067 16.067 0 0 0 2.89 10.656a17.143 17.143 0 0 0 18.397 6.828a15.786 15.786 0 0 0 4.403-1.935l71.67-45.672a14.922 14.922 0 0 0 6.734-9.977a15.923 15.923 0 0 0-2.713-12.011a17.156 17.156 0 0 0-18.404-6.832a15.78 15.78 0 0 0-4.396 1.933l-27.35 17.434a52.298 52.298 0 0 1-14.553 6.391c-23.101 6.007-47.497-3.036-61.101-22.649a52.681 52.681 0 0 1-9.004-39.849a49.428 49.428 0 0 1 22.34-33.114l71.664-45.677a52.218 52.218 0 0 1 14.563-6.398c23.101-6.007 47.497 3.036 61.101 22.648a52.685 52.685 0 0 1 9.004 39.85a50.559 50.559 0 0 1-1.713 6.692l-1.35 4.116l-3.67-2.693a92.373 92.373 0 0 0-28.037-14.013l-2.664-.809l.246-2.658a16.099 16.099 0 0 0-2.89-10.656a17.143 17.143 0 0 0-18.398-6.828a15.786 15.786 0 0 0-4.402 1.935l-71.67 45.674a14.898 14.898 0 0 0-6.73 9.975a15.9 15.9 0 0 0 2.709 12.012a17.156 17.156 0 0 0 18.404 6.832a15.841 15.841 0 0 0 4.402-1.935l27.345-17.427a52.147 52.147 0 0 1 14.552-6.397c23.101-6.006 47.497 3.037 61.102 22.65a52.681 52.681 0 0 1 9.003 39.848a49.453 49.453 0 0 1-22.34 33.12l-71.664 45.673a52.218 52.218 0 0 1-14.563 6.398"></path></svg>