void 0.9.2 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +1 -0
  2. package/dist/{auth-cmd-CPkWrQR9.mjs → auth-cmd-DBnbrX9Y.mjs} +2 -2
  3. package/dist/{better-auth-shared-ChVbrq52.mjs → better-auth-shared-D0Mbmx5V.mjs} +1 -1
  4. package/dist/{better-auth-shared-CmEQZs1T.d.mts → better-auth-shared-a7H3wk9J.d.mts} +2 -2
  5. package/dist/{cache-1SVxZ7BK.mjs → cache-DNX0hLsO.mjs} +1 -1
  6. package/dist/{cancel-deploy-JaFClLr7.mjs → cancel-deploy-CDzkVaYZ.mjs} +1 -1
  7. package/dist/cli/cli.mjs +205 -36
  8. package/dist/{client-DIfOFf1W.mjs → client-D4awzTnj.mjs} +149 -45
  9. package/dist/{config-BVEC0lti.mjs → config-8dLIngKW.mjs} +1 -1
  10. package/dist/{create-project-DCUrFJxa.mjs → create-project-CGodxfzH.mjs} +1 -1
  11. package/dist/{db-DiddQC2v.mjs → db-ERzz4oly.mjs} +16 -16
  12. package/dist/{delete-CYknzwXU.mjs → delete-CJZGDLBR.mjs} +1 -1
  13. package/dist/{deploy-CRU9fGjE.mjs → deploy-Cwa5sooV.mjs} +97 -108
  14. package/dist/{domain-B7RntQ1r.mjs → domain-WvoAHE6w.mjs} +1 -1
  15. package/dist/{drizzle-2fs1qTgy.mjs → drizzle-DqstQTGq.mjs} +1 -1
  16. package/dist/{env-Bvw0wMTI.mjs → env-Cx4czPsR.mjs} +3 -3
  17. package/dist/{env-helpers-Bd2wVdxz.d.mts → env-helpers-ZatKyjen.d.mts} +1 -1
  18. package/dist/{env-types-D6qI1ThV.mjs → env-types-CaJaIRXU.mjs} +1 -1
  19. package/dist/{env-validation-DBJsxZLz.mjs → env-validation-LH6eoyW-.mjs} +3 -3
  20. package/dist/{gen-CZdNWIaA.mjs → gen-Dgi4D7GC.mjs} +3 -3
  21. package/dist/git-metadata-Ce0AtSZL.mjs +82 -0
  22. package/dist/github-cmd-CUB041gj.mjs +308 -0
  23. package/dist/{handler-xflsMwWM.d.mts → handler-DczZlaXR.d.mts} +24 -4
  24. package/dist/{headers-Y0jshugF.mjs → headers-BNWymgnH.mjs} +2 -2
  25. package/dist/index.d.mts +4 -4
  26. package/dist/index.mjs +60 -55
  27. package/dist/{init-BHupO7Fm.mjs → init-DvaRDMIc.mjs} +125 -80
  28. package/dist/{link-7MoXUMaI.mjs → link-CpbWJY3P.mjs} +2 -2
  29. package/dist/{list-N2mCHv8q.mjs → list-BkwE5vH4.mjs} +1 -1
  30. package/dist/{login-tt1OmNVg.mjs → login-Ca6mhQVI.mjs} +1 -1
  31. package/dist/{logs-Cl7q2CjZ.mjs → logs-ArQzZquo.mjs} +1 -1
  32. package/dist/{mcp-BXD35N2J.mjs → mcp-Cc2MMXdQ.mjs} +1 -1
  33. package/dist/{node-CHWVVjO6.mjs → node-B07cZs0d.mjs} +4 -4
  34. package/dist/pages/client.d.mts +1 -1
  35. package/dist/pages/client.mjs +1 -0
  36. package/dist/pages/head-client.d.mts +1 -1
  37. package/dist/pages/head.d.mts +1 -1
  38. package/dist/pages/index.d.mts +8 -6
  39. package/dist/pages/index.mjs +3 -3
  40. package/dist/pages/islands-plugin.d.mts +1 -1
  41. package/dist/pages/islands-plugin.mjs +1 -1
  42. package/dist/pages/protocol.d.mts +2 -2
  43. package/dist/pages/protocol.mjs +30 -12
  44. package/dist/{plugin-inference-BUBhHt_1.mjs → plugin-inference-C3fLzFvP.mjs} +3 -1
  45. package/dist/{prepare-CrlAVbWS.mjs → prepare-DH2WnEpD.mjs} +14 -19
  46. package/dist/{preset-CIJG2O7a.mjs → preset-B7ZQZn0u.mjs} +2 -2
  47. package/dist/{project-cmd-CLrqSbqj.mjs → project-cmd-CWZeawuN.mjs} +8 -8
  48. package/dist/{project-tsconfig-HAGjfY6w.mjs → project-tsconfig-BTNuNoJ0.mjs} +18 -4
  49. package/dist/{protocol-DYca39yJ.d.mts → protocol-63PHtCHS.d.mts} +8 -5
  50. package/dist/{rollback-CEbAwbHK.mjs → rollback-BJ60ktsW.mjs} +1 -1
  51. package/dist/{route-types-jxRfWuCb.mjs → route-types-D03ryMXz.mjs} +3 -2
  52. package/dist/{runner-DLZ9E_cX.mjs → runner-BQyKUqAL.mjs} +1 -1
  53. package/dist/runtime/ai.mjs +1 -1
  54. package/dist/runtime/auth.d.mts +1 -1
  55. package/dist/runtime/better-auth-pg.d.mts +1 -1
  56. package/dist/runtime/better-auth-pg.mjs +3 -3
  57. package/dist/runtime/better-auth.d.mts +1 -1
  58. package/dist/runtime/better-auth.mjs +2 -2
  59. package/dist/runtime/client-react.d.mts +2 -2
  60. package/dist/runtime/client-react.mjs +1 -1
  61. package/dist/runtime/client-solid.d.mts +2 -2
  62. package/dist/runtime/client-solid.mjs +1 -1
  63. package/dist/runtime/client-svelte.d.mts +2 -2
  64. package/dist/runtime/client-svelte.mjs +1 -1
  65. package/dist/runtime/client-vue.d.mts +2 -2
  66. package/dist/runtime/client-vue.mjs +1 -1
  67. package/dist/runtime/client.d.mts +2 -2
  68. package/dist/runtime/client.mjs +1 -1
  69. package/dist/runtime/env-helpers.d.mts +1 -1
  70. package/dist/runtime/env-public-client.d.mts +1 -1
  71. package/dist/runtime/env-public.d.mts +2 -2
  72. package/dist/runtime/env-public.mjs +1 -1
  73. package/dist/runtime/env.mjs +1 -1
  74. package/dist/runtime/fetch-stream.d.mts +1 -1
  75. package/dist/runtime/fetch-stream.mjs +1 -1
  76. package/dist/runtime/fetch.d.mts +1 -1
  77. package/dist/runtime/fetch.mjs +1 -1
  78. package/dist/runtime/handler.d.mts +2 -2
  79. package/dist/runtime/handler.mjs +68 -2
  80. package/dist/runtime/isr.mjs +1 -1
  81. package/dist/runtime/live.d.mts +2 -2
  82. package/dist/runtime/live.mjs +1 -1
  83. package/dist/runtime/migration-handler-pg.mjs +1 -1
  84. package/dist/runtime/sandbox.mjs +1 -1
  85. package/dist/runtime/validator.d.mts +1 -1
  86. package/dist/runtime/ws-server.d.mts +2 -2
  87. package/dist/runtime/ws.d.mts +3 -3
  88. package/dist/{scan-BNC_1OsY.mjs → scan-DGEp1-1Q.mjs} +19 -11
  89. package/dist/{scan-2YmJkYAf.mjs → scan-VCAM1oh3.mjs} +55 -29
  90. package/dist/{secret-C_uTY7xx.mjs → secret-BRbXyu7E.mjs} +1 -1
  91. package/dist/{types-Dm9kep2X.d.mts → types-BnHMXezo.d.mts} +12 -3
  92. package/package.json +2 -2
  93. package/schema.json +3 -3
  94. package/skills/void/docs/guide/deployment.md +37 -11
  95. package/skills/void/docs/guide/edge/headers.md +10 -3
  96. package/skills/void/docs/guide/edge/rewrites.md +5 -5
  97. package/skills/void/docs/guide/pages-routing/layouts.md +8 -7
  98. package/skills/void/docs/guide/pages-routing/loaders.md +1 -1
  99. package/skills/void/docs/guide/pages-routing/overview.md +32 -5
  100. package/skills/void/docs/guide/server-routing.md +28 -0
  101. package/skills/void/docs/node_modules/void/AGENTS.md +15 -14
  102. package/skills/void/docs/node_modules/void/README.md +1 -0
  103. package/skills/void/docs/reference/api.md +44 -1
  104. package/skills/void/docs/reference/cli.md +65 -2
  105. package/skills/void/docs/reference/config.md +1 -1
  106. /package/dist/{auth-migrations-CuQkjjWE.mjs → auth-migrations-BP-hMzYl.mjs} +0 -0
  107. /package/dist/{auth-CDeb2XAl.d.mts → auth-nEUVHuFI.d.mts} +0 -0
  108. /package/dist/{defer-CouVukrf.mjs → defer-C-bdSM_b.mjs} +0 -0
  109. /package/dist/{dist-DR9sIMbM.mjs → dist-5cGIJHQQ.mjs} +0 -0
  110. /package/dist/{dotenv-Bkoqyq9r.mjs → dotenv-lS94ymhM.mjs} +0 -0
  111. /package/dist/{env-raw-RDkMnSTC.mjs → env-raw-Dtj1UAoK.mjs} +0 -0
  112. /package/dist/{fetch-error-CimrygMq.d.mts → fetch-error-BQMdPvge.d.mts} +0 -0
  113. /package/dist/{fetch-error-BFIoojjf.mjs → fetch-error-C6qffTl2.mjs} +0 -0
  114. /package/dist/{head-BsSTGnIe.d.mts → head-JAH2l3fg.d.mts} +0 -0
  115. /package/dist/{log-CWWZV4V1.mjs → log-BdD_Fpms.mjs} +0 -0
  116. /package/dist/{package-json-B2TD0JLu.mjs → package-json-Bg_GJdJB.mjs} +0 -0
  117. /package/dist/{pg-CMZ_5wsC.mjs → pg-CempqvEJ.mjs} +0 -0
  118. /package/dist/{providers-3Kkv63Ha.d.mts → providers-BkiAlEad.d.mts} +0 -0
  119. /package/dist/{providers-uC0PJg1c.mjs → providers-BwPbdHdi.mjs} +0 -0
  120. /package/dist/{runner-pg-Cv9V7ydr.mjs → runner-pg-BAheiT6v.mjs} +0 -0
  121. /package/dist/{skills-BRKc--As.mjs → skills--OMgLmXW.mjs} +0 -0
  122. /package/dist/{standard-schema-Dnsh2vnl.d.mts → standard-schema-BCBv-QaP.d.mts} +0 -0
  123. /package/dist/{subcommand-prompt-OD-x_Tsm.mjs → subcommand-prompt-ClwpLlKD.mjs} +0 -0
  124. /package/dist/{yarn-pnp-0SwYgQxx.mjs → yarn-pnp-DhCHHLAz.mjs} +0 -0
@@ -56,11 +56,18 @@ Define custom response headers in [`void.json`](../../reference/config) using th
56
56
 
57
57
  ## Scope
58
58
 
59
- Header rules apply to **all responses** served through the dispatch worker, including static assets, SSR pages, and API routes. They do not apply to:
59
+ Header rules apply to **all responses** served through the dispatch worker, including static assets, SSR pages, API routes, and hashed asset responses (which browsers fetch over `GET` from the immutable edge cache). Serving headers on hashed assets is what lets a cross-origin-isolated app attach `Cross-Origin-Embedder-Policy` and `Cross-Origin-Resource-Policy` to its hashed worker and wasm files — without them, a module worker spawned from a `require-corp` document is blocked.
60
+
61
+ On hashed (content-addressed, immutable) assets, rules are **additive**. You can add headers such as COOP/COEP/CORP or other security headers, but the platform-managed caching, representation, and framing headers are preserved so an immutable asset cannot be mis-cached or corrupted: `Cache-Control`, `Content-Type`, `Content-Encoding`, `Content-Length`, `Content-Range`, `Accept-Ranges`, and `Transfer-Encoding` keep their platform values even if a broad rule tries to overwrite them. (This is the one case where a user `Cache-Control` does not override the default.)
62
+
63
+ Header rules do not apply to:
60
64
 
61
- - Hashed asset cache hits (these use immutable caching set by the dispatch worker itself)
62
65
  - ISR cache responses (these have their own cache-control headers)
63
66
 
67
+ ### Blocked headers
68
+
69
+ Two headers can never be set through rules: `Set-Cookie` and `Clear-Site-Data`. On the shared `*.void.app` domain, `void.app` is the registrable domain, so a cookie operation from one project would reach every project's subdomain. Both are dropped from rule output to keep that tenant boundary intact.
70
+
64
71
  ## Framework `_headers` files
65
72
 
66
73
  Meta-frameworks like SvelteKit, Nuxt, and Astro generate a `_headers` file with cache rules for their hashed asset directories. Void automatically parses this file during deploy and merges the rules into the deploy manifest.
@@ -74,6 +81,6 @@ No configuration is needed. If the framework generates a `_headers` file, it is
74
81
 
75
82
  1. `void deploy` reads header rules from the framework `_headers` file (if present) and `routing.headers` in `void.json`, then includes them in the deploy manifest.
76
83
  2. The platform stores the rules in the KV routing entry for your project.
77
- 3. The dispatch worker applies matching rules to matching responses before returning them. Cacheable responses are cached with the final headers.
84
+ 3. The dispatch worker applies matching rules to matching responses before returning them. Most cacheable responses are cached with the final headers; hashed assets are the exception — their rules are applied on every serve (including cache hits), so a later header-rule change takes effect even when the content hash, and thus the cache entry, is unchanged.
78
85
 
79
86
  Because rules are evaluated at the edge, there is no extra latency cost. Headers are applied inline before the response is returned and cached.
@@ -332,14 +332,14 @@ If you use [`routing.revalidate`](./revalidation) on a dispatch rewrite (`routin
332
332
  During `vite dev`, every response carries an `X-Void-Routing` header that traces how the request was resolved. Open the Network tab in devtools and inspect the response headers:
333
333
 
334
334
  ```
335
- X-Void-Routing: redirect[/old] → /new 301 (_redirects:12)
336
- X-Void-Routing: rewrite[/api/*] → /backend/:splat (void.json#routing.rewrites)
337
- X-Void-Routing: fallback[/docs/*] → /docs.html (void.json#routing.fallbacks)
338
- X-Void-Routing: c.rewrite → /new-path (middleware)
335
+ X-Void-Routing: redirect[/old] -> /new 301 (_redirects:12)
336
+ X-Void-Routing: rewrite[/api/*] -> /backend/:splat (void.json#routing.rewrites)
337
+ X-Void-Routing: fallback[/docs/*] -> /docs.html (void.json#routing.fallbacks)
338
+ X-Void-Routing: c.rewrite -> /new-path (middleware)
339
339
  X-Void-Routing: pass-through
340
340
  ```
341
341
 
342
- Phases are separated by `→`. The parenthesised source hint points at the exact declaration — a line number for `_redirects`, a config path for `void.json`, or `spa-default` for the synthetic SPA catch-all. The header is **only emitted in dev** — production builds strip both the trace code and the per-rule `origin` metadata from the bundle and manifest.
342
+ Phases are separated by `->` so the diagnostic value stays valid as an HTTP header. The parenthesised source hint points at the exact declaration — a line number for `_redirects`, a config path for `void.json`, or `spa-default` for the synthetic SPA catch-all. The header is **only emitted in dev** — production builds strip both the trace code and the per-rule `origin` metadata from the bundle and manifest.
343
343
 
344
344
  ::: info What fires in `vite dev`
345
345
  `vite dev` applies the full static routing pipeline on every target — `node`, `bun`, `deno`, and the default target alike. `void.json` rules (`routing.redirects` / `routing.rewrites` / `routing.fallbacks` / `routing.headers`) and file-based rules (`public/_redirects`, `public/_headers`) are merged at plugin load and compiled into the Hono middleware your worker runs behind. Editing `_redirects` or `_headers` during a dev session re-runs the merge and reloads the page — no restart needed. `c.rewrite()` calls in middleware work everywhere because they live inside the worker itself, and the `X-Void-Routing` dev header reports every decision on every target.
@@ -149,7 +149,7 @@ import { useShared, Link } from '@void/solid';
149
149
  import type { JSX } from 'solid-js';
150
150
 
151
151
  export default function Layout(props: { children: JSX.Element }) {
152
- const shared = useShared<{ auth: { user: { name: string } | null } }>();
152
+ const shared = useShared();
153
153
  return (
154
154
  <>
155
155
  <nav>
@@ -307,15 +307,16 @@ Middleware can inject data available on every page via `c.set("shared", {...})`.
307
307
  ```ts
308
308
  // middleware/01.auth.ts
309
309
  import { defineMiddleware } from 'void';
310
+ import { getUser, type AuthUser } from 'void/auth';
310
311
 
311
312
  declare module 'void' {
312
313
  interface CloudContextVariables {
313
- shared: { auth: { user: { name: string } | null } };
314
+ shared: { auth: { user: AuthUser | null } };
314
315
  }
315
316
  }
316
317
 
317
318
  export default defineMiddleware(async (c, next) => {
318
- const user = await getSessionUser(c);
319
+ const user = getUser();
319
320
  c.set('shared', { auth: { user } });
320
321
  await next();
321
322
  });
@@ -329,7 +330,7 @@ Access it on the client with `useShared()`. The return type is inferred from you
329
330
  import { useShared } from '@void/react';
330
331
 
331
332
  export default function Page() {
332
- const { auth } = useShared(); // { auth: { user: { name: string } | null } }
333
+ const { auth } = useShared(); // { auth: { user: AuthUser | null } }
333
334
  return <p>Hello, {auth?.user?.name}</p>;
334
335
  }
335
336
  ```
@@ -337,14 +338,14 @@ export default function Page() {
337
338
  ```vue [Vue]
338
339
  <script setup lang="ts">
339
340
  import { useShared } from '@void/vue';
340
- const { auth } = useShared(); // { auth: { user: { name: string } | null } }
341
+ const { auth } = useShared(); // { auth: { user: AuthUser | null } }
341
342
  </script>
342
343
  ```
343
344
 
344
345
  ```svelte [Svelte]
345
346
  <script>
346
347
  import { useShared } from "@void/svelte";
347
- const { auth } = useShared(); // { auth: { user: { name: string } | null } }
348
+ const { auth } = useShared(); // { auth: { user: AuthUser | null } }
348
349
  </script>
349
350
  ```
350
351
 
@@ -352,7 +353,7 @@ const { auth } = useShared(); // { auth: { user: { name: string } | null } }
352
353
  import { useShared } from '@void/solid';
353
354
 
354
355
  export default function Page() {
355
- const shared = useShared(); // { auth: { user: { name: string } | null } }
356
+ const shared = useShared(); // { auth: { user: AuthUser | null } }
356
357
  return <p>Hello, {shared.auth?.user?.name}</p>;
357
358
  }
358
359
  ```
@@ -237,7 +237,7 @@ export default function Dashboard(props: Props) {
237
237
 
238
238
  ### How Streaming Works
239
239
 
240
- On the initial page load (SSR), React uses React 19 streaming SSR and renders the nearest Suspense fallback for deferred props; the other adapters render their loading state. As each deferred function resolves, the server streams an inline `<script>` tag that delivers the data, so no extra HTTP request is needed. On SPA navigation, deferred data streams via NDJSON over the same response.
240
+ On the initial page load (SSR), React uses React 19 streaming SSR and renders the nearest Suspense fallback for deferred props; the other adapters render their loading state. As each deferred function resolves, the server streams an inline `<script>` tag that delivers the data, so no extra HTTP request is needed. Routes with `export const ssr = false` skip server-rendered component HTML but still stream deferred resolution scripts after the client-mounted shell. On SPA navigation, deferred data streams via NDJSON over the same response.
241
241
 
242
242
  ### Deferred Props After Mutations
243
243
 
@@ -100,6 +100,7 @@ Each page can have a companion `.server.ts` file that runs exclusively on the se
100
100
 
101
101
  - A [**loader**](./loaders), which runs on `GET` and returns the data that becomes the page component's props
102
102
  - [**Actions**](./actions-and-forms), which handle mutations from forms and programmatic calls. Export a single `action` or multiple [named actions](./actions-and-forms#named-actions) when a page has several mutations
103
+ - `ssr = false` to opt a route out of server-rendered component HTML while keeping server loaders and client-side routing
103
104
 
104
105
  File-based routing rules are the same as [server routing](../server-routing.md): `[param]` for dynamic segments, `[...param]` for catch-all, `(group)/` for route groups.
105
106
 
@@ -107,14 +108,40 @@ File-based routing rules are the same as [server routing](../server-routing.md):
107
108
 
108
109
  Pages uses an Inertia-style protocol under the hood:
109
110
 
110
- | Request | Response |
111
- | --------------------- | --------------------------------------------------------------------- |
112
- | Initial page load | Full SSR HTML. Client hydrates automatically. |
113
- | Subsequent navigation | JSON with component name + props. Client component swap or re-render. |
114
- | Form submission | Runs action, then returns fresh props or a redirect. |
111
+ | Request | Response |
112
+ | --------------------- | -------------------------------------------------------------------------------------------------------------- |
113
+ | Initial page load | Full SSR HTML. Client hydrates automatically. Routes with `ssr = false` return a client-mounted shell instead. |
114
+ | Subsequent navigation | JSON with component name + props. Client component swap or re-render. |
115
+ | Form submission | Runs action, then returns fresh props or a redirect. |
115
116
 
116
117
  This means the first page load is server-rendered for SEO and performance, while later navigations stay fast without full page reloads.
117
118
 
119
+ To opt a specific route out of server-rendered component HTML, export `ssr = false` from its companion `.server.ts` file:
120
+
121
+ ```ts
122
+ // pages/dashboard.server.ts
123
+ import { defineHandler } from 'void';
124
+
125
+ export const ssr = false;
126
+
127
+ export const loader = defineHandler(async () => {
128
+ return { title: 'Dashboard' };
129
+ });
130
+ ```
131
+
132
+ The loader still runs on the first request, and its props are embedded in the HTML shell. The page component mounts in the browser instead of hydrating server-rendered markup.
133
+
134
+ Render and prerender flags combine like this:
135
+
136
+ | Page exports | Behavior |
137
+ | -------------------------------------- | ------------------------------------------------------------------------- |
138
+ | `ssr` unset or `true` | Server-render component HTML on request. |
139
+ | `ssr = false` | Return a client-mounted shell on request. |
140
+ | `ssr = false` + `prerender = true` | Prerender a client-mounted shell with embedded loader data. |
141
+ | `ssr = false` + `prerender = false` | Return the client-mounted shell only on request; never prerender it. |
142
+ | Island page + `ssr = false` | Invalid. Island pages already use the island renderer. |
143
+ | `output: "static"` + `prerender` unset | Auto-prerender pages that have known paths, including client-only shells. |
144
+
118
145
  Use the `Link` component for SPA navigation between pages. It renders an `<a>` tag that intercepts clicks and navigates without a full page reload:
119
146
 
120
147
  ::: code-group
@@ -191,6 +191,34 @@ export default defineMiddleware(async (c, next) => {
191
191
 
192
192
  `defineMiddleware` uses Hono middleware semantics: `(c, next) => Promise<void> | void`.
193
193
 
194
+ 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.
195
+
196
+ ```ts
197
+ // env.ts
198
+ import { defineEnv, string } from 'void/env';
199
+
200
+ export default defineEnv({
201
+ BASIC_AUTH_USERNAME: string(),
202
+ BASIC_AUTH_PASSWORD: string(),
203
+ });
204
+ ```
205
+
206
+ ```ts
207
+ // middleware/01.basic-auth.ts
208
+ import { basicAuth } from 'void';
209
+ import { env } from 'void/env';
210
+
211
+ export default basicAuth({
212
+ username: () => env.BASIC_AUTH_USERNAME,
213
+ password: () => env.BASIC_AUTH_PASSWORD,
214
+ realm: 'Preview',
215
+ });
216
+ ```
217
+
218
+ Set `BASIC_AUTH_USERNAME` and `BASIC_AUTH_PASSWORD` as local environment variables for development and production secrets before deploy.
219
+
220
+ For app-specific bypasses such as health checks or public webhooks, compose that logic in your own middleware before calling `basicAuth()`.
221
+
194
222
  Middleware can set typed context variables using `c.set()`. Augment the `CloudContextVariables` interface so downstream handlers get full type safety:
195
223
 
196
224
  ```ts
@@ -23,6 +23,7 @@ Non-CF targets disable CF-only bindings (`void/db`, `void/kv`, `void/auth`, `voi
23
23
  - `defineHandler(fn)` — wraps a route handler `(c: CloudContext) => R`, returns `TypedHandler<{}, R>`
24
24
  - `defineHandler.withValidator(validators)(fn)` — validates body/query/params via Standard Schema before calling handler, returns `TypedHandler<V, R>` with phantom types for codegen
25
25
  - `defineMiddleware(fn)` — wraps Hono middleware `(c, next) => void`
26
+ - `basicAuth(options)` — built-in Basic Auth middleware for temporary site gates; excludes Void internals under `/__void`
26
27
  - Handlers can return plain objects/strings (auto-converted via `convertReturnValue`) or use Hono's `c.json()` / `c.text()` directly.
27
28
  - Route files use **named HTTP method exports** (`export const GET`, `export const POST`, etc.) — one file per path, multiple methods per file.
28
29
 
@@ -90,20 +91,20 @@ src/
90
91
 
91
92
  ### Package Exports
92
93
 
93
- | Subpath | What |
94
- | ---------------- | ----------------------------------------------------------------------------------------------------------------------- |
95
- | `void` | `voidPlugin()` named export + `defineHandler` + `defineMiddleware` + `defineHead` + `defineQueue` + `CloudContext` type |
96
- | `void/handler` | `defineHandler()`, `defineHandler.withValidator()`, `defineMiddleware()`, `defineHead()`, types |
97
- | `void/response` | `convertReturnValue()` |
98
- | `void/validator` | `runValidation()`, `ValidatorSlots`, `HandlerInput` types |
99
- | `void/routes` | Empty `RouteMap` + `WebSocketRouteMap` stubs (augmented by generated `routes.d.ts`) |
100
- | `void/client` | Typed `fetch()` client + `fetchStream()` SSE consumer + `FetchError` |
101
- | `void/ws` | `defineRoom()`, `defineWebSocket()`, typed `connect()`, and WebSocket context types |
102
- | `void/db` | `db` Drizzle D1 instance (auto-wired with user schema) + `createDb()` for custom D1 bindings |
103
- | `void/queues` | `queues` typed proxy + `QueueMap` stub interface (augmented by generated `queues.d.ts`) |
104
- | `void/ai` | `ai` proxy — Cloudflare-native `ai.run()`/`ai.stream()`, provider-native `ai.provider().fetch()`, `ai.models()` |
105
- | `void/log` | `logger.error/warn/info(msg, fields?)` — emits stringified JSON to `console.*` so Cloudflare Tail captures level + msg |
106
- | `void/env` | `defineEnv()`, typed `env` proxy, built-in schema helpers (`string`, `number`, `oneOf`, …) + global Cloudflare types |
94
+ | Subpath | What |
95
+ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
96
+ | `void` | `voidPlugin()` named export + `defineHandler` + `defineMiddleware` + `basicAuth` + `defineHead` + `defineQueue` + `CloudContext` type |
97
+ | `void/handler` | `defineHandler()`, `defineHandler.withValidator()`, `defineMiddleware()`, `basicAuth()`, `defineHead()`, types |
98
+ | `void/response` | `convertReturnValue()` |
99
+ | `void/validator` | `runValidation()`, `ValidatorSlots`, `HandlerInput` types |
100
+ | `void/routes` | Empty `RouteMap` + `WebSocketRouteMap` stubs (augmented by generated `routes.d.ts`) |
101
+ | `void/client` | Typed `fetch()` client + `fetchStream()` SSE consumer + `FetchError` |
102
+ | `void/ws` | `defineRoom()`, `defineWebSocket()`, typed `connect()`, and WebSocket context types |
103
+ | `void/db` | `db` Drizzle D1 instance (auto-wired with user schema) + `createDb()` for custom D1 bindings |
104
+ | `void/queues` | `queues` typed proxy + `QueueMap` stub interface (augmented by generated `queues.d.ts`) |
105
+ | `void/ai` | `ai` proxy — `ai.run()`, `ai.stream()`, `ai.models()` with auto-detected backend (service binding / HTTPS / direct) |
106
+ | `void/log` | `logger.error/warn/info(msg, fields?)` — emits stringified JSON to `console.*` so Cloudflare Tail captures level + msg |
107
+ | `void/env` | `defineEnv()`, typed `env` proxy, built-in schema helpers (`string`, `number`, `oneOf`, …) + global Cloudflare types |
107
108
 
108
109
  The CLI binary is at `dist/cli/cli.mjs` (declared in `bin` field of package.json).
109
110
 
@@ -48,6 +48,7 @@ Runtime helpers include:
48
48
 
49
49
  - `defineHandler`
50
50
  - `defineMiddleware`
51
+ - `basicAuth`
51
52
  - `defineScheduled`
52
53
  - `defineQueue`
53
54
  - `void/db`
@@ -118,6 +118,49 @@ export default defineMiddleware(async (c, next) => {
118
118
  function defineMiddleware(handler: MiddlewareHandler<CloudEnv>): MiddlewareHandler<CloudEnv>;
119
119
  ```
120
120
 
121
+ ### `basicAuth(options)`
122
+
123
+ Built-in Basic authentication middleware for temporary site gates and pre-launch protection. Use it from `middleware/` with credentials from `void/env` to protect the whole app, or pass it to `defineHandler()` for a single route.
124
+
125
+ Void internal endpoints under `/__void` are always excluded so deploy-time migrations, prerendering, cron/queue dispatch, and remote binding helpers still reach Void's own internal-token checks.
126
+
127
+ When credentials come from `void/env`, wrap the reads in functions so they are resolved per request after Void has bound the runtime env.
128
+
129
+ For app-specific bypasses such as health checks or public webhooks, compose that logic in your own middleware before calling `basicAuth()`.
130
+
131
+ ```ts
132
+ // env.ts
133
+ import { defineEnv, string } from 'void/env';
134
+
135
+ export default defineEnv({
136
+ BASIC_AUTH_USERNAME: string(),
137
+ BASIC_AUTH_PASSWORD: string(),
138
+ });
139
+ ```
140
+
141
+ ```ts
142
+ // middleware/01.basic-auth.ts
143
+ import { basicAuth } from 'void';
144
+ import { env } from 'void/env';
145
+
146
+ export default basicAuth({
147
+ username: () => env.BASIC_AUTH_USERNAME,
148
+ password: () => env.BASIC_AUTH_PASSWORD,
149
+ realm: 'Preview',
150
+ });
151
+ ```
152
+
153
+ **Signature:**
154
+
155
+ ```ts
156
+ function basicAuth(options: {
157
+ username: string | (() => string);
158
+ password: string | (() => string);
159
+ realm?: string | (() => string);
160
+ message?: string | (() => string);
161
+ }): MiddlewareHandler<CloudEnv>;
162
+ ```
163
+
121
164
  ### `defineScheduled(handler)`
122
165
 
123
166
  Wraps a Cloudflare [Scheduled handler](https://developers.cloudflare.com/workers/runtime-apis/handlers/scheduled/) with type inference.
@@ -1105,7 +1148,7 @@ This table lists app-facing imports. Exported implementation subpaths such as `v
1105
1148
  | Import path | Contents |
1106
1149
  | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
1107
1150
  | `void` | `voidPlugin`, handler/type re-exports, `defer`, `Deferred`, `DeferredState`, `InferProps`, `HeadDescriptor` |
1108
- | `void/handler` | `defineHandler`, `defineMiddleware`, `defineScheduled`, `defineQueue`, `defineRender`, `defineHead`, types |
1151
+ | `void/handler` | `defineHandler`, `defineMiddleware`, `basicAuth`, `defineScheduled`, `defineQueue`, `defineRender`, `defineHead`, types |
1109
1152
  | `void/auth` | `defineAuth`, `getUser`, `getSession`, `requireAuth`, `AuthUser`, `AuthSession`, `AuthState` |
1110
1153
  | `void/client` | `fetch`, `fetchStream`, `FetchError`, `auth`, `createAuthClient`, `AuthUser`, `AuthSession`, `AuthState` |
1111
1154
  | `void/client/{framework}` | Same as `void/client`, with framework-specific Better Auth clients for `react`, `vue`, `svelte`, and `solid` |
@@ -86,7 +86,7 @@ After that, the full interactive flow walks through:
86
86
  4. **Skills:** links Void skills using the same detected or selected agent context.
87
87
  5. **MCP config:** writes MCP server config using that same agent context.
88
88
  6. **Demo code:** for existing non-Pages projects, optionally scaffolds a `db/migrations/` directory plus an API route and typed fetch example.
89
- 7. **GitHub Actions:** optionally creates `.github/workflows/deploy.yml` with the right package manager commands.
89
+ 7. **GitHub Actions:** optionally creates `.github/workflows/void-deploy.yml`. The workflow deploys on pushes to `main` and authenticates via [GitHub OIDC](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect) — no long-lived `VOID_TOKEN` secret is stored in the repo. The job requests an OIDC token (audience `void`), exchanges it at `POST $VOID_API_URL/auth/github-oidc` for a short-lived project-scoped deploy token, then runs `void deploy --project <slug>` with the right package manager. The project slug is baked in from your linked project (`.void/project.json`) when known; otherwise the workflow reads a `VOID_PROJECT` repository variable.
90
90
  8. **`env.ts` scaffold:** if the project has no `env.ts` but has `.env` / `.env.example` / `.env.local` / `.env.development*` files on disk, generates an `env.ts` pre-populated with their keys. Values get conservative type inference (`boolean`/`url`/`number`/`string`) — the file carries a banner nudging you to tighten anything the heuristic got wrong.
91
91
  9. **Project setup:** optionally logs you in, lets you select or create a project, and writes `.void/project.json` so your first deploy can just be `void deploy`.
92
92
 
@@ -98,10 +98,12 @@ Use flags to run individual steps without prompts:
98
98
  | ------------ | ------------------------------------------------- |
99
99
  | `--tsconfig` | Only update `tsconfig.json` |
100
100
  | `--agents` | Set up agent instructions, skills, and MCP config |
101
- | `--github` | Only create GitHub Actions workflow |
101
+ | `--github` | Only create the GitHub Actions deploy workflow |
102
102
 
103
103
  Flags can be combined. When any flag is provided, only the specified steps run and interactive prompts are skipped.
104
104
 
105
+ The `--github` workflow uses GitHub OIDC: you connect the repository to your Void project once with `void github connect <project> --repo <owner/repo> --executor github_actions` (see the GitHub section below), and pushes to `main` deploy automatically. There is no `VOID_TOKEN` secret to create or rotate. To target staging, set a `VOID_API_URL` repository variable to `https://api.staging.void.cloud`.
106
+
105
107
  For projects that already have `"extends"`, `void init --tsconfig` preserves the existing config and adds `./.void/tsconfig.json`. If the existing config defines `files` or `compilerOptions.paths`, Void also merges its generated declaration files and aliases into the root config because TypeScript replaces those fields across `extends` instead of deeply merging them.
106
108
 
107
109
  ### `void prepare`
@@ -564,6 +566,67 @@ PORT=3000
564
566
 
565
567
  See [Environment Variables](../guide/env-vars.md) for the full guide.
566
568
 
569
+ ## GitHub
570
+
571
+ Deploy-on-GitHub works from **any** Void login — Google, GitHub, or other SSO. The first time you connect GitHub, Void links your GitHub identity to your current account (a one-time step, independent of how you logged in); it never creates a second account.
572
+
573
+ ### `void github link`
574
+
575
+ ```
576
+ void github link
577
+ ```
578
+
579
+ Link your current Void account to a GitHub identity. Opens your browser to authorize Void on GitHub (a localhost + PKCE handshake, the same mechanics as `void auth login`), then binds that GitHub identity to the logged-in account. Requires an authenticated CLI (`void auth login` first).
580
+
581
+ You normally don't need to run this directly — `void github install` runs the link automatically when your account has no GitHub identity yet. Run it on its own to link ahead of time, or to link a GitHub identity without installing the App. If that GitHub account is already linked to a different Void account, the command fails and asks you to use a different GitHub account.
582
+
583
+ ### `void github install`
584
+
585
+ ```
586
+ void github install
587
+ ```
588
+
589
+ Open the GitHub App install page in your browser. If your account has no linked GitHub identity yet, `void github install` first runs the GitHub link automatically (browser authorize), then continues. After installing, run `void github connect` to link a repository to your project.
590
+
591
+ ### `void github installations`
592
+
593
+ ```
594
+ void github installations
595
+ ```
596
+
597
+ List all GitHub App installations linked to your account. Each entry includes the `[id: <installation_id>]` needed for `--installation` in non-interactive use.
598
+
599
+ ### `void github connect`
600
+
601
+ ```
602
+ void github connect [project] [options]
603
+ ```
604
+
605
+ Connect a GitHub repository to a Void project for automatic deploys. On every push to the configured branch, Void builds and deploys your project automatically.
606
+
607
+ **Options**
608
+
609
+ | Flag | Description |
610
+ | --------------------- | ------------------------------------------------------------------------------- |
611
+ | `--project <name>` | Project name (alias for the positional argument) |
612
+ | `--installation <id>` | GitHub App installation ID (required when you have multiple installations) |
613
+ | `--repo <owner/repo>` | Repository full name — required unless the installation grants exactly one repo |
614
+ | `--branch <name>` | Branch to deploy from — **required in non-interactive mode** |
615
+ | `--executor <type>` | Build executor: `container` (default) or `github_actions` |
616
+
617
+ **Non-interactive use (CI)**
618
+
619
+ When stdin is not a TTY, `void github connect` never prompts — it fails closed and names any flag it needs. `--branch` is always required. `--project` must be resolvable (positional / `--project` / `VOID_PROJECT` / linked `.void/project.json`). `--installation` is required only when your account has more than one installation; otherwise the sole installation is used. `--repo` is required when the installation grants access to all repos or to more than one selected repo; when it grants exactly one repo that repo is used automatically. Use `void github installations` to discover the `installation_id`.
620
+
621
+ ```
622
+ void github connect my-app \
623
+ --installation 42 \
624
+ --repo owner/my-app \
625
+ --branch main
626
+ ```
627
+
628
+ **Project resolution** follows the same order as deploy: positional / `--project`, `VOID_PROJECT`, linked project (`.void/project.json`).
629
+
567
630
  ## Custom Domains
568
631
 
569
632
  ### `void domain add`
@@ -172,7 +172,7 @@ Output mode. Controls the default rendering strategy for pages.
172
172
 
173
173
  When set to `"static"`, all pages are prerendered at build time as static HTML files written to `dist/client/`. Individual pages can opt out with `export const prerender = false`. Dynamic pages (with route params) without a `getPrerenderPaths()` export are implicitly not prerendered.
174
174
 
175
- When omitted or set to `"server"`, pages are server-rendered on request. Individual pages can opt into deploy-time prerendering with `export const prerender = true`.
175
+ When omitted or set to `"server"`, pages are server-rendered on request. Individual pages can opt into deploy-time prerendering with `export const prerender = true`, or opt out of server-rendered component HTML with `export const ssr = false`.
176
176
 
177
177
  ```json
178
178
  { "output": "static" }
File without changes
File without changes
File without changes
File without changes