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
@@ -1,4 +1,4 @@
1
- import { t as FetchError } from "../fetch-error-BFIoojjf.mjs";
1
+ import { t as FetchError } from "../fetch-error-C6qffTl2.mjs";
2
2
  import { fetch } from "./fetch.mjs";
3
3
  import { fetchStream } from "./fetch-stream.mjs";
4
4
  import { auth, createAuthClient } from "./auth-client.mjs";
@@ -1,2 +1,2 @@
1
- import { a as getSecretOverride, c as oneOf, i as email, l as string, n as SECRET_OVERRIDE_SYMBOL, o as json, r as boolean, s as number, t as EnvSchema, u as url } from "../env-helpers-Bd2wVdxz.mjs";
1
+ import { a as getSecretOverride, c as oneOf, i as email, l as string, n as SECRET_OVERRIDE_SYMBOL, o as json, r as boolean, s as number, t as EnvSchema, u as url } from "../env-helpers-ZatKyjen.mjs";
2
2
  export { EnvSchema, SECRET_OVERRIDE_SYMBOL, boolean, email, getSecretOverride, json, number, oneOf, string, url };
@@ -1,4 +1,4 @@
1
- import { c as oneOf, i as email, l as string, o as json, r as boolean, s as number, t as EnvSchema, u as url } from "../env-helpers-Bd2wVdxz.mjs";
1
+ import { c as oneOf, i as email, l as string, o as json, r as boolean, s as number, t as EnvSchema, u as url } from "../env-helpers-ZatKyjen.mjs";
2
2
 
3
3
  //#region src/runtime/env-public-client.d.ts
4
4
  declare const env: Record<string, unknown>;
@@ -1,6 +1,6 @@
1
1
  /// <reference types="@cloudflare/workers-types" />
2
- import { t as StandardSchemaV1 } from "../standard-schema-Dnsh2vnl.mjs";
3
- import { c as oneOf, i as email, l as string, o as json, r as boolean, s as number, t as EnvSchema, u as url } from "../env-helpers-Bd2wVdxz.mjs";
2
+ import { t as StandardSchemaV1 } from "../standard-schema-BCBv-QaP.mjs";
3
+ import { c as oneOf, i as email, l as string, o as json, r as boolean, s as number, t as EnvSchema, u as url } from "../env-helpers-ZatKyjen.mjs";
4
4
  import * as _$_cloudflare_sandbox0 from "@cloudflare/sandbox";
5
5
 
6
6
  //#region src/runtime/env-public.d.ts
@@ -1,4 +1,4 @@
1
- import { i as getRawRuntimeEnv } from "../env-raw-RDkMnSTC.mjs";
1
+ import { i as getRawRuntimeEnv } from "../env-raw-Dtj1UAoK.mjs";
2
2
  import { getRuntimeEnv } from "./env.mjs";
3
3
  import { boolean, email, getSecretOverride, json, number, oneOf, string, url } from "./env-helpers.mjs";
4
4
  //#region src/shared/env-mask.ts
@@ -1,4 +1,4 @@
1
- import { i as getRawRuntimeEnv, n as asEnv, r as envContext, t as INTERNAL_BINDING_PREFIXES } from "../env-raw-RDkMnSTC.mjs";
1
+ import { i as getRawRuntimeEnv, n as asEnv, r as envContext, t as INTERNAL_BINDING_PREFIXES } from "../env-raw-Dtj1UAoK.mjs";
2
2
  //#region src/runtime/env.ts
3
3
  /**
4
4
  * Public runtime env API — exposed via "void/_env".
@@ -1,4 +1,4 @@
1
- import { t as FetchError } from "../fetch-error-CimrygMq.mjs";
1
+ import { t as FetchError } from "../fetch-error-BQMdPvge.mjs";
2
2
  import { RouteMap } from "void/routes";
3
3
 
4
4
  //#region src/runtime/fetch-stream.d.ts
@@ -1,4 +1,4 @@
1
- import { n as ofetch, t as FetchError } from "../fetch-error-BFIoojjf.mjs";
1
+ import { n as ofetch, t as FetchError } from "../fetch-error-C6qffTl2.mjs";
2
2
  //#region src/runtime/fetch-stream.ts
3
3
  /**
4
4
  * Consume an SSE endpoint as an async generator of text chunks.
@@ -1,4 +1,4 @@
1
- import { t as FetchError } from "../fetch-error-CimrygMq.mjs";
1
+ import { t as FetchError } from "../fetch-error-BQMdPvge.mjs";
2
2
  import { RouteMap } from "void/routes";
3
3
 
4
4
  //#region src/runtime/fetch.d.ts
@@ -1,4 +1,4 @@
1
- import { n as ofetch, t as FetchError } from "../fetch-error-BFIoojjf.mjs";
1
+ import { n as ofetch, t as FetchError } from "../fetch-error-C6qffTl2.mjs";
2
2
  //#region src/runtime/fetch.ts
3
3
  async function fetch(path, options) {
4
4
  let url = path;
@@ -1,3 +1,3 @@
1
1
  /// <reference types="@cloudflare/workers-types" />
2
- import { C as HandlerInput, S as ValidationError, _ as defineScheduled, a as QueueBatch, b as InferProps, c as RenderAssetTags, d as VoidAssetRewriteError, f as defineHandler, g as defineRender, h as defineQueue, i as CloudEnv, l as TypedHandler, m as defineMiddleware, n as CloudContext, o as QueueMessage, p as defineHead, r as CloudContextVariables, s as QueueRetryOptions, t as CloudBindings, u as TypedQueueHandler, v as Deferred, w as ValidatorSlots, x as defer } from "../handler-xflsMwWM.mjs";
3
- export { CloudBindings, CloudContext, CloudContextVariables, CloudEnv, Deferred, HandlerInput, InferProps, QueueBatch, QueueMessage, QueueRetryOptions, RenderAssetTags, TypedHandler, TypedQueueHandler, ValidationError, ValidatorSlots, VoidAssetRewriteError, defer, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled };
2
+ import { C as defer, E as ValidatorSlots, S as InferProps, T as HandlerInput, _ as defineQueue, a as CloudEnv, b as Deferred, c as QueueRetryOptions, d as TypedQueueHandler, f as VoidAssetRewriteError, g as defineMiddleware, h as defineHead, i as CloudContextVariables, l as RenderAssetTags, m as defineHandler, n as CloudBindings, o as QueueBatch, p as basicAuth, r as CloudContext, s as QueueMessage, t as BasicAuthOptions, u as TypedHandler, v as defineRender, w as ValidationError, y as defineScheduled } from "../handler-DczZlaXR.mjs";
3
+ export { BasicAuthOptions, CloudBindings, CloudContext, CloudContextVariables, CloudEnv, Deferred, HandlerInput, InferProps, QueueBatch, QueueMessage, QueueRetryOptions, RenderAssetTags, TypedHandler, TypedQueueHandler, ValidationError, ValidatorSlots, VoidAssetRewriteError, basicAuth, defer, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled };
@@ -1,6 +1,6 @@
1
1
  import { convertReturnValue } from "./response.mjs";
2
2
  import { runValidation } from "./validator.mjs";
3
- import { i as ValidationError, n as defer } from "../defer-CouVukrf.mjs";
3
+ import { i as ValidationError, n as defer } from "../defer-C-bdSM_b.mjs";
4
4
  //#region src/runtime/handler.ts
5
5
  /**
6
6
  * Thrown when `c.rewrite()` is called with a destination whose final path
@@ -53,6 +53,72 @@ function defineHandler(...args) {
53
53
  function defineMiddleware(handler) {
54
54
  return handler;
55
55
  }
56
+ const basicAuthEncoder = new TextEncoder();
57
+ const basicAuthDecoder = new TextDecoder("utf-8", { fatal: true });
58
+ function parseBasicAuthHeader(authorization) {
59
+ if (!authorization?.toLowerCase().startsWith("basic ")) return null;
60
+ try {
61
+ const encoded = authorization.slice(6).trim();
62
+ const binary = atob(encoded);
63
+ const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
64
+ const decoded = basicAuthDecoder.decode(bytes);
65
+ const separator = decoded.indexOf(":");
66
+ if (separator === -1) return null;
67
+ return {
68
+ username: decoded.slice(0, separator),
69
+ password: decoded.slice(separator + 1)
70
+ };
71
+ } catch {
72
+ return null;
73
+ }
74
+ }
75
+ function timingSafeStringEqual(actual, expected) {
76
+ const actualBytes = basicAuthEncoder.encode(actual);
77
+ const expectedBytes = basicAuthEncoder.encode(expected);
78
+ let difference = actualBytes.length ^ expectedBytes.length;
79
+ for (let i = 0; i < expectedBytes.length; i++) difference |= (actualBytes[i] ?? 0) ^ expectedBytes[i];
80
+ return difference === 0;
81
+ }
82
+ function isVoidInternalPath(pathname) {
83
+ return pathname === "/__void" || pathname.startsWith("/__void/");
84
+ }
85
+ function basicAuthChallenge(realm, message) {
86
+ const escapedRealm = realm.replace(/["\\]/g, "\\$&");
87
+ return new Response(message, {
88
+ status: 401,
89
+ headers: {
90
+ "Cache-Control": "no-store",
91
+ "WWW-Authenticate": `Basic realm="${escapedRealm}", charset="UTF-8"`
92
+ }
93
+ });
94
+ }
95
+ function resolveBasicAuthOption(value, fallback) {
96
+ if (value === void 0) return fallback;
97
+ return typeof value === "function" ? value() : value;
98
+ }
99
+ /**
100
+ * Create Basic authentication middleware for temporary site gates and
101
+ * pre-launch protection.
102
+ *
103
+ * Void internal endpoints under `/__void` are always excluded so deploy-time
104
+ * migrations, prerendering, cron/queue dispatch, and remote binding helpers
105
+ * continue to reach Void's own internal-token checks.
106
+ */
107
+ function basicAuth(options) {
108
+ return defineMiddleware(async (c, next) => {
109
+ if (isVoidInternalPath(c.req.path)) {
110
+ await next();
111
+ return;
112
+ }
113
+ const username = resolveBasicAuthOption(options.username, "");
114
+ const password = resolveBasicAuthOption(options.password, "");
115
+ const credentials = parseBasicAuthHeader(c.req.raw.headers.get("Authorization"));
116
+ const usernameMatches = credentials && timingSafeStringEqual(credentials.username, username);
117
+ const passwordMatches = credentials && timingSafeStringEqual(credentials.password, password);
118
+ if (!usernameMatches || !passwordMatches) return basicAuthChallenge(resolveBasicAuthOption(options.realm, "Restricted"), resolveBasicAuthOption(options.message, "Authentication required"));
119
+ await next();
120
+ });
121
+ }
56
122
  /**
57
123
  * Identity function that provides type inference for scheduled jobs.
58
124
  * At runtime it simply returns the function passed to it.
@@ -82,4 +148,4 @@ function defineQueue(handler) {
82
148
  return handler;
83
149
  }
84
150
  //#endregion
85
- export { ValidationError, VoidAssetRewriteError, defer, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled };
151
+ export { ValidationError, VoidAssetRewriteError, basicAuth, defer, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled };
@@ -1,4 +1,4 @@
1
- import { i as getRawRuntimeEnv } from "../env-raw-RDkMnSTC.mjs";
1
+ import { i as getRawRuntimeEnv } from "../env-raw-Dtj1UAoK.mjs";
2
2
  //#region src/runtime/isr.ts
3
3
  /**
4
4
  * Revalidate ISR-cached pages on demand.
@@ -1,6 +1,6 @@
1
1
  /// <reference types="@cloudflare/workers-types" />
2
- import { r as AuthUser } from "../auth-CDeb2XAl.mjs";
3
- import { i as CloudEnv, n as CloudContext } from "../handler-xflsMwWM.mjs";
2
+ import { r as AuthUser } from "../auth-nEUVHuFI.mjs";
3
+ import { a as CloudEnv, r as CloudContext } from "../handler-DczZlaXR.mjs";
4
4
 
5
5
  //#region src/runtime/live.d.ts
6
6
  type MaybePromise<T> = T | Promise<T>;
@@ -1,4 +1,4 @@
1
- import { i as getRawRuntimeEnv } from "../env-raw-RDkMnSTC.mjs";
1
+ import { i as getRawRuntimeEnv } from "../env-raw-Dtj1UAoK.mjs";
2
2
  import { getUser } from "./auth.mjs";
3
3
  //#region src/runtime/live.ts
4
4
  let liveBindingName = "VOID_LIVE";
@@ -1,4 +1,4 @@
1
- import { t as disablePgPassFallback } from "../pg-CMZ_5wsC.mjs";
1
+ import { t as disablePgPassFallback } from "../pg-CempqvEJ.mjs";
2
2
  //#region src/runtime/migration-handler-pg.ts
3
3
  function createMigrationHandler(env, migrations, _dialect, runAuthMigrations) {
4
4
  return async (request) => {
@@ -1,4 +1,4 @@
1
- import { i as getRawRuntimeEnv } from "../env-raw-RDkMnSTC.mjs";
1
+ import { i as getRawRuntimeEnv } from "../env-raw-Dtj1UAoK.mjs";
2
2
  import { requireRuntimeBinding } from "./env.mjs";
3
3
  import { Sandbox, getSandbox as getSandbox$1 } from "@cloudflare/sandbox";
4
4
  //#region src/runtime/sandbox.ts
@@ -1,2 +1,2 @@
1
- import { C as HandlerInput, T as runValidation, w as ValidatorSlots } from "../handler-xflsMwWM.mjs";
1
+ import { D as runValidation, E as ValidatorSlots, T as HandlerInput } from "../handler-DczZlaXR.mjs";
2
2
  export { HandlerInput, ValidatorSlots, runValidation };
@@ -1,5 +1,5 @@
1
- import { n as AuthState } from "../auth-CDeb2XAl.mjs";
2
- import { i as VoidAuthRuntimeOptions, n as VoidAuthEnv } from "../better-auth-shared-CmEQZs1T.mjs";
1
+ import { n as AuthState } from "../auth-nEUVHuFI.mjs";
2
+ import { i as VoidAuthRuntimeOptions, n as VoidAuthEnv } from "../better-auth-shared-a7H3wk9J.mjs";
3
3
  import { AnyWebSocketDefinition } from "./ws.mjs";
4
4
 
5
5
  //#region src/runtime/ws-server.d.ts
@@ -1,6 +1,6 @@
1
- import { r as AuthUser } from "../auth-CDeb2XAl.mjs";
2
- import { t as StandardSchemaV1 } from "../standard-schema-Dnsh2vnl.mjs";
3
- import { i as CloudEnv } from "../handler-xflsMwWM.mjs";
1
+ import { r as AuthUser } from "../auth-nEUVHuFI.mjs";
2
+ import { t as StandardSchemaV1 } from "../standard-schema-BCBv-QaP.mjs";
3
+ import { a as CloudEnv } from "../handler-DczZlaXR.mjs";
4
4
  import { WebSocketRouteMap } from "void/routes";
5
5
 
6
6
  //#region src/runtime/ws.d.ts
@@ -1,8 +1,8 @@
1
1
  import { a as join, i as isAbsolute, t as basename } from "./pathe.M-eThtNZ-BrPhGF_K.mjs";
2
2
  import { n as readProjectPaths } from "./project-paths-CCMrHYQm.mjs";
3
- import { n as globSync, t as glob } from "./dist-DR9sIMbM.mjs";
4
- import { d as isCallExpression, f as isIdentifier, h as isObjectExpression, p as isLiteral } from "./plugin-inference-BUBhHt_1.mjs";
5
- import { o as parseRouteFilename, s as parseWebSocketFilename, t as scanPages } from "./scan-2YmJkYAf.mjs";
3
+ import { n as globSync, t as glob } from "./dist-5cGIJHQQ.mjs";
4
+ import { d as isCallExpression, f as isIdentifier, h as isObjectExpression, p as isLiteral } from "./plugin-inference-C3fLzFvP.mjs";
5
+ import { o as parseRouteFilename, s as parseWebSocketFilename, t as scanPages } from "./scan-VCAM1oh3.mjs";
6
6
  import { readFileSync } from "node:fs";
7
7
  import { parseSync } from "vite";
8
8
  //#region src/crons/scan.ts
@@ -147,6 +147,7 @@ function detectHttpExports(absPath) {
147
147
  const program = parsed.program;
148
148
  for (const node of program.body ?? []) if (node.type === "ExportNamedDeclaration") {
149
149
  const declarationNode = node;
150
+ if (declarationNode.exportKind === "type") continue;
150
151
  const decl = declarationNode.declaration;
151
152
  if (decl) {
152
153
  if (decl.type === "VariableDeclaration") {
@@ -157,6 +158,7 @@ function detectHttpExports(absPath) {
157
158
  }
158
159
  }
159
160
  if (declarationNode.specifiers) for (const spec of declarationNode.specifiers) {
161
+ if (spec.exportKind === "type") continue;
160
162
  const exported = isIdentifier(spec.exported) ? spec.exported.name : isIdentifier(spec.local) ? spec.local.name : void 0;
161
163
  if (exported && HTTP_METHODS.has(exported)) methods.push(exported);
162
164
  }
@@ -286,6 +288,18 @@ function parseMiddlewareDefinitions(files) {
286
288
  };
287
289
  }).sort((a, b) => a.order - b.order);
288
290
  }
291
+ function compareRouteSpecificity(a, b) {
292
+ if (a.catchAll !== b.catchAll) return a.catchAll ? 1 : -1;
293
+ const aSegments = a.pattern.split("/");
294
+ const bSegments = b.pattern.split("/");
295
+ const shared = Math.min(aSegments.length, bSegments.length);
296
+ for (let i = 0; i < shared; i++) {
297
+ const aDynamic = aSegments[i].startsWith(":") || aSegments[i] === "*";
298
+ if (aDynamic !== (bSegments[i].startsWith(":") || bSegments[i] === "*")) return aDynamic ? 1 : -1;
299
+ if (aSegments[i] !== bSegments[i]) return aSegments[i].localeCompare(bSegments[i]);
300
+ }
301
+ return aSegments.length - bSegments.length;
302
+ }
289
303
  /**
290
304
  * Scan routes/ and middleware/ directories and return a structured manifest.
291
305
  * Files starting with _ are ignored.
@@ -315,10 +329,7 @@ async function scanRoutes(root, options) {
315
329
  const route = parseRouteFilename(f);
316
330
  route.methods = detectHttpExports(join(routesDir, f));
317
331
  return route;
318
- }).sort((a, b) => {
319
- if (a.catchAll !== b.catchAll) return a.catchAll ? 1 : -1;
320
- return a.pattern.localeCompare(b.pattern);
321
- }),
332
+ }).sort(compareRouteSpecificity),
322
333
  websockets,
323
334
  middleware: parseMiddlewareDefinitions(middlewareFiles),
324
335
  jobs,
@@ -348,10 +359,7 @@ function scanRoutePatternsSync(root, paths = readProjectPaths(root)) {
348
359
  } catch {
349
360
  routeFiles = [];
350
361
  }
351
- return routeFiles.filter((f) => !isWebSocketPatternFile(f)).filter((f) => !f.split("/").some((s) => s.startsWith("_"))).map((f) => parseRouteFilename(f)).sort((a, b) => {
352
- if (a.catchAll !== b.catchAll) return a.catchAll ? 1 : -1;
353
- return a.pattern.localeCompare(b.pattern);
354
- });
362
+ return routeFiles.filter((f) => !isWebSocketPatternFile(f)).filter((f) => !f.split("/").some((s) => s.startsWith("_"))).map((f) => parseRouteFilename(f)).sort(compareRouteSpecificity);
355
363
  }
356
364
  //#endregion
357
365
  export { scanQueuesSync as a, scanWebSocketRoutesSync as i, scanRoutePatternsSync as n, scanJobsSync as o, scanRoutes as r, scanMiddlewareSync as t };
@@ -1,6 +1,6 @@
1
1
  import { a as join, n as dirname, r as extname, t as basename } from "./pathe.M-eThtNZ-BrPhGF_K.mjs";
2
2
  import { n as readProjectPaths } from "./project-paths-CCMrHYQm.mjs";
3
- import { t as glob } from "./dist-DR9sIMbM.mjs";
3
+ import { t as glob } from "./dist-5cGIJHQQ.mjs";
4
4
  import { existsSync, readFileSync } from "node:fs";
5
5
  //#region src/router/parse-filename.ts
6
6
  const EXTENSIONS = [
@@ -83,30 +83,29 @@ function toLayoutId(layout) {
83
83
  if (isNamedLayout(layout)) return layout.directory ? `${layout.directory}/_layouts/${layout.name}` : `_layouts/${layout.name}`;
84
84
  return layout.directory ? `${layout.directory}/layout` : "layout";
85
85
  }
86
- /** Resolve the layout chain for a component (innermost last) */
87
- function resolveLayoutChain(componentId, layouts) {
86
+ function resolveLayoutDirectories(componentId, componentPath) {
87
+ if (componentPath) {
88
+ const segments = componentPath.split("/");
89
+ segments.pop();
90
+ return segments.map((_, index) => segments.slice(0, index + 1).join("/"));
91
+ }
88
92
  const parts = componentId.split("/");
93
+ return parts.slice(0, -1).map((_, index) => parts.slice(0, index + 1).join("/"));
94
+ }
95
+ /** Resolve the layout chain for a component (innermost last) */
96
+ function resolveLayoutChain(componentId, layouts, componentPath) {
89
97
  const chain = [];
90
98
  const root = layouts.find((layout) => layout.directory === "");
91
99
  if (root) chain.push(root);
92
- let dir = "";
93
- for (let partIndex = 0; partIndex < parts.length - 1; partIndex++) {
94
- dir = dir ? `${dir}/${parts[partIndex]}` : parts[partIndex];
100
+ for (const dir of resolveLayoutDirectories(componentId, componentPath)) {
95
101
  const layout = layouts.find((candidate) => candidate.directory === dir);
96
102
  if (layout) chain.push(layout);
97
103
  }
98
104
  return chain;
99
105
  }
100
106
  /** Walk up directory tree to find closest _layouts/<name> */
101
- function resolveNamedLayout(name, componentId, namedLayouts) {
102
- const parts = componentId.split("/");
103
- const dirs = [];
104
- let dir = "";
105
- for (let partIndex = 0; partIndex < parts.length - 1; partIndex++) {
106
- dir = dir ? `${dir}/${parts[partIndex]}` : parts[partIndex];
107
- dirs.push(dir);
108
- }
109
- dirs.reverse();
107
+ function resolveNamedLayout(name, componentId, namedLayouts, componentPath) {
108
+ const dirs = resolveLayoutDirectories(componentId, componentPath).reverse();
110
109
  dirs.push("");
111
110
  for (const directory of dirs) {
112
111
  const found = namedLayouts.find((namedLayout) => namedLayout.name === name && namedLayout.directory === directory);
@@ -116,7 +115,7 @@ function resolveNamedLayout(name, componentId, namedLayouts) {
116
115
  }
117
116
  function resolveEffectiveLayoutEntries(page, layouts, namedLayouts) {
118
117
  if (page.layout === false) return [];
119
- const defaultChain = resolveLayoutChain(page.componentId, layouts).map((layout) => ({
118
+ const defaultChain = resolveLayoutChain(page.componentId, layouts, page.componentPath).map((layout) => ({
120
119
  id: toLayoutId(layout),
121
120
  definition: layout
122
121
  }));
@@ -124,7 +123,7 @@ function resolveEffectiveLayoutEntries(page, layouts, namedLayouts) {
124
123
  const isExclusive = page.layout.startsWith("!");
125
124
  const name = isExclusive ? page.layout.slice(1) : page.layout;
126
125
  if (name.startsWith(".") || name.startsWith("/")) return defaultChain;
127
- const resolved = resolveNamedLayout(name, page.componentId, namedLayouts);
126
+ const resolved = resolveNamedLayout(name, page.componentId, namedLayouts, page.componentPath);
128
127
  if (!resolved) return defaultChain;
129
128
  const namedEntry = {
130
129
  id: toLayoutId(resolved),
@@ -160,13 +159,31 @@ const SERVER_EXTENSIONS = [
160
159
  "mts"
161
160
  ];
162
161
  const PAGES_EXPORT_RE = /export\s+(?:const|function|async\s+function)\s+(loader|actions?)\b/g;
163
- const REVALIDATE_RE = /export\s+const\s+revalidate\s*=\s*(\d+)\s*;/;
164
- const PRERENDER_RE = /export\s+const\s+prerender\s*=\s*true\s*;/;
165
- const PRERENDER_FALSE_RE = /export\s+const\s+prerender\s*=\s*false\s*;/;
162
+ const REVALIDATE_RE = /export\s+const\s+revalidate\s*=\s*(\d+)\s*;?/;
163
+ const PRERENDER_RE = /export\s+const\s+prerender\s*=\s*true\s*;?/;
164
+ const PRERENDER_FALSE_RE = /export\s+const\s+prerender\s*=\s*false\s*;?/;
165
+ const SSR_TRUE_RE = /export\s+const\s+ssr\s*=\s*true\s*;?/;
166
+ const SSR_FALSE_RE = /export\s+const\s+ssr\s*=\s*false\s*;?/;
166
167
  const GET_PRERENDER_PATHS_RE = /export\s+(?:async\s+)?function\s+getPrerenderPaths\b/;
167
168
  const LAYOUT_STRING_RE = /export\s+const\s+layout\s*=\s*["']([^"']+)["']\s*;?/;
168
169
  const LAYOUT_FALSE_RE = /export\s+const\s+layout\s*=\s*false\s*;?/;
169
170
  const MAX_REVALIDATE = 31536e3;
171
+ function inferAutoPrerender(page, options) {
172
+ const isDynamic = page.params.length > 0 || page.catchAll;
173
+ if (options?.output === "static") return !isDynamic || page.hasGetPrerenderPaths === true;
174
+ return page.island === true && !page.methods.includes("loader") && !isDynamic;
175
+ }
176
+ function resolvePageRendering(page, options) {
177
+ if (page.island && page.ssr === false) throw new Error(`pages: ssr = false is not valid for island page '${page.componentPath}'.`);
178
+ const ssrMode = page.ssr === true ? "server" : page.ssr === false ? "client" : "auto";
179
+ const prerenderMode = page.prerender === true ? "force" : page.prerender === false ? "disabled" : "auto";
180
+ return {
181
+ ssrMode,
182
+ prerenderMode,
183
+ renderMode: page.island === true ? "island" : ssrMode === "client" ? "client" : "server",
184
+ shouldPrerender: prerenderMode === "force" || prerenderMode === "auto" && inferAutoPrerender(page, options)
185
+ };
186
+ }
170
187
  async function scanPages(root, options) {
171
188
  const pagesDir = options?.pagesDir ?? options?.paths?.pagesDir ?? readProjectPaths(root).pagesDir;
172
189
  if (!existsSync(pagesDir)) return {
@@ -219,7 +236,7 @@ async function scanPages(root, options) {
219
236
  const methods = [];
220
237
  let revalidate;
221
238
  let prerender;
222
- let prerenderExplicitlyFalse = false;
239
+ let ssr;
223
240
  let hasGetPrerenderPaths;
224
241
  const serverBase = hasIslandSuffix ? file.replace(`.island${ext}`, "") : file.replace(ext, "");
225
242
  for (const serverExt of SERVER_EXTENSIONS) {
@@ -231,17 +248,24 @@ async function scanPages(root, options) {
231
248
  const revalidateMatch = content.match(REVALIDATE_RE);
232
249
  if (revalidateMatch) revalidate = Number(revalidateMatch[1]);
233
250
  if (PRERENDER_RE.test(content)) prerender = true;
234
- if (PRERENDER_FALSE_RE.test(content)) prerenderExplicitlyFalse = true;
251
+ else if (PRERENDER_FALSE_RE.test(content)) prerender = false;
252
+ if (SSR_TRUE_RE.test(content)) ssr = true;
253
+ else if (SSR_FALSE_RE.test(content)) ssr = false;
235
254
  if (GET_PRERENDER_PATHS_RE.test(content)) hasGetPrerenderPaths = true;
236
255
  break;
237
256
  }
238
257
  }
239
- if (options?.output === "static") {
240
- if (!prerenderExplicitlyFalse && prerender !== true) {
241
- if (!(route.params.length > 0 || route.catchAll) || hasGetPrerenderPaths) prerender = true;
242
- }
243
- } else if (island && !methods.includes("loader") && route.params.length === 0 && !route.catchAll && !prerenderExplicitlyFalse) prerender = true;
244
- if (prerender && revalidate === void 0) revalidate = MAX_REVALIDATE;
258
+ const rendering = resolvePageRendering({
259
+ componentPath: file,
260
+ island,
261
+ ssr,
262
+ prerender,
263
+ methods,
264
+ params: route.params,
265
+ catchAll: route.catchAll,
266
+ hasGetPrerenderPaths
267
+ }, options);
268
+ if (rendering.shouldPrerender && revalidate === void 0) revalidate = MAX_REVALIDATE;
245
269
  let metadata;
246
270
  let layout;
247
271
  if (ext === ".md") {
@@ -264,6 +288,8 @@ async function scanPages(root, options) {
264
288
  componentId,
265
289
  revalidate,
266
290
  prerender,
291
+ ssr,
292
+ ...rendering,
267
293
  hasGetPrerenderPaths,
268
294
  island,
269
295
  metadata,
@@ -291,7 +317,7 @@ async function scanPages(root, options) {
291
317
  if (page.layout === void 0 || page.layout === false) continue;
292
318
  const name = page.layout.startsWith("!") ? page.layout.slice(1) : page.layout;
293
319
  if (name.startsWith(".") || name.startsWith("/")) continue;
294
- if (!resolveNamedLayout(name, page.componentId, namedLayouts)) throw new Error(`pages: Page '${page.componentPath}' specifies layout '${page.layout}', but _layouts/${name}.vue was not found in any ancestor directory.`);
320
+ if (!resolveNamedLayout(name, page.componentId, namedLayouts, page.componentPath)) throw new Error(`pages: Page '${page.componentPath}' specifies layout '${page.layout}', but _layouts/${name}.vue was not found in any ancestor directory.`);
295
321
  }
296
322
  return {
297
323
  pages,
@@ -1,5 +1,5 @@
1
1
  import { n as cliTitle } from "./output-DeiS4oEX.mjs";
2
- import { c as getToken, n as PlatformClient } from "./client-DIfOFf1W.mjs";
2
+ import { d as getToken, n as PlatformClient } from "./client-D4awzTnj.mjs";
3
3
  import { _ as me, c as R, g as ge, n as Ce, u as Se, v as ue, x as q, y as ye } from "./dist-abzUneor.mjs";
4
4
  import { r as readProjectConfig } from "./project-CH9pdo16.mjs";
5
5
  import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project-D4O1_fZz.mjs";
@@ -1,4 +1,8 @@
1
1
  //#region src/pages/types.d.ts
2
+ type ExplicitBoolean = true | false | undefined;
3
+ type SsrMode = "auto" | "server" | "client";
4
+ type PrerenderMode = "auto" | "force" | "disabled";
5
+ type PageRenderMode = "server" | "client" | "island";
2
6
  type PageDefinition = {
3
7
  /** URL pattern, e.g. "/users/:id" */pattern: string; /** Route params, e.g. ["id"] */
4
8
  params: Array<string>; /** Whether this is a catch-all route */
@@ -7,8 +11,13 @@ type PageDefinition = {
7
11
  serverPath: string | null; /** HTTP methods exported by server handler */
8
12
  methods: Array<string>; /** Component ID used in page object, e.g. "users/[id]" */
9
13
  componentId: string; /** Edge caching revalidate TTL in seconds, from .server.ts export */
10
- revalidate?: number; /** Whether `export const prerender = true` is set in server handler */
11
- prerender?: boolean; /** Whether `export function getPrerenderPaths` is defined in server handler */
14
+ revalidate?: number; /** Whether `export const prerender = true/false` is set in server handler */
15
+ prerender?: ExplicitBoolean; /** Whether `export const ssr = true/false` is set in server handler */
16
+ ssr?: ExplicitBoolean; /** Normalized SSR mode from the public `ssr` export */
17
+ ssrMode: SsrMode; /** Normalized prerender mode from the public `prerender` export */
18
+ prerenderMode: PrerenderMode; /** Normalized page render mode */
19
+ renderMode: PageRenderMode; /** Whether this page should participate in prerender collection */
20
+ shouldPrerender: boolean; /** Whether `export function getPrerenderPaths` is defined in server handler */
12
21
  hasGetPrerenderPaths?: boolean; /** Whether this is an island page (.island.tsx/.island.vue) */
13
22
  island?: boolean; /** Frontmatter metadata extracted from .md files (title, description) */
14
23
  metadata?: {
@@ -39,4 +48,4 @@ type DevCssAsset = {
39
48
  css: string;
40
49
  };
41
50
  //#endregion
42
- export { PageScanResult as a, PageDefinition as i, LayoutDefinition as n, NamedLayoutDefinition as r, DevCssAsset as t };
51
+ export { PageRenderMode as a, PageDefinition as i, LayoutDefinition as n, PageScanResult as o, NamedLayoutDefinition as r, DevCssAsset as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "void",
3
- "version": "0.9.2",
3
+ "version": "0.10.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/voidzero-dev/void.git",
@@ -353,7 +353,7 @@
353
353
  "valibot": ">=1.0.0-beta.7",
354
354
  "vite": "^8.0.0",
355
355
  "zod": "^3.25.0 || ^4.0.0",
356
- "@void/md": "0.9.2"
356
+ "@void/md": "0.10.1"
357
357
  },
358
358
  "peerDependenciesMeta": {
359
359
  "@void/md": {
package/schema.json CHANGED
@@ -154,7 +154,7 @@
154
154
  {
155
155
  "type": "object",
156
156
  "patternProperties": {
157
- "^(/|\\*)": {
157
+ "^(\\/|\\*$)": {
158
158
  "type": "number",
159
159
  "minimum": 0
160
160
  }
@@ -216,7 +216,7 @@
216
216
  "type": "object",
217
217
  "description": "URL rewrites. Keys are source URL patterns (path-only `/path` or host-scoped `https://host/path`); values are destination paths. Serves content from the destination without changing the browser URL. Destinations must start with \"/\" and must not contain \"?\" — the incoming request's query string is preserved automatically.",
218
218
  "patternProperties": {
219
- "^(/|https://)": { "type": "string", "pattern": "^/[^?]*$" }
219
+ "^(/|https://)": { "type": "string", "pattern": "^/(?!/)[^?]*$" }
220
220
  },
221
221
  "additionalProperties": false
222
222
  },
@@ -224,7 +224,7 @@
224
224
  "type": "object",
225
225
  "description": "Fallback rewrites. Same shape as `rewrites`, but only fire when no static asset matched the request. Useful for SPA shells or default-locale catch-alls without pre-empting real routes. Destinations must start with \"/\" and must not contain \"?\".",
226
226
  "patternProperties": {
227
- "^(/|https://)": { "type": "string", "pattern": "^/[^?]*$" }
227
+ "^(/|https://)": { "type": "string", "pattern": "^/(?!/)[^?]*$" }
228
228
  },
229
229
  "additionalProperties": false
230
230
  }
@@ -64,30 +64,56 @@ If your CI pipeline runs typechecking or other static analysis before deploy, ru
64
64
 
65
65
  ## GitHub Actions
66
66
 
67
- You can deploy from a GitHub repo with a simple GitHub Actions workflow. Running `void init --github` will generate this as `.github/workflows/deploy.yml` in your project:
67
+ You can deploy from a GitHub repo on every push to `main`. Running `void init --github` generates `.github/workflows/void-deploy.yml` in your project.
68
+
69
+ The workflow authenticates with [GitHub OIDC](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect) — there is **no long-lived `VOID_TOKEN` secret to store or rotate**. The job requests a short-lived OIDC token from GitHub, exchanges it at `POST $VOID_API_URL/auth/github-oidc` for a short-lived project-scoped deploy token (about 15 minutes on the free tier; longer on higher plans, capped at ~60 minutes), then runs `void deploy --project <slug>`:
68
70
 
69
71
  ```yaml
70
- name: Deploy
72
+ name: Deploy to Void
71
73
  on:
72
74
  push:
73
75
  branches: [main]
74
76
 
77
+ # Latest push wins: a newer commit cancels an in-flight deploy for the same
78
+ # repo + branch, so an older commit can never overtake a newer one.
79
+ concurrency:
80
+ group: void-deploy-${{ github.repository }}-${{ github.ref }}
81
+ cancel-in-progress: true
82
+
83
+ permissions:
84
+ id-token: write
85
+ contents: read
86
+
87
+ env:
88
+ VOID_API_URL: ${{ vars.VOID_API_URL || 'https://api.void.cloud' }}
89
+ VOID_PROJECT: my-app
90
+
75
91
  jobs:
76
92
  deploy:
77
93
  runs-on: ubuntu-latest
78
94
  steps:
79
- - uses: actions/checkout@v4
80
- - uses: voidzero-dev/setup-vp@v1
95
+ - uses: actions/checkout@v6
96
+ - uses: actions/setup-node@v6
81
97
  with:
82
- node-version: '22'
83
- cache: true
84
- - run: vp exec void deploy
85
- env:
86
- VOID_TOKEN: ${{ secrets.VOID_TOKEN }}
87
- VOID_PROJECT: my-app
98
+ node-version: lts/*
99
+ cache: npm
100
+ - run: npm ci
101
+ - name: Exchange GitHub OIDC token for a Void deploy token
102
+ run: |
103
+ OIDC_TOKEN="$(curl -sf \
104
+ -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
105
+ "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=void" | jq -r '.value')"
106
+ VOID_TOKEN="$(curl -sf \
107
+ -H 'Content-Type: application/json' \
108
+ -d "$(jq -n --arg t "$OIDC_TOKEN" --arg p "$VOID_PROJECT" '{oidcToken: $t, project: $p}')" \
109
+ "$VOID_API_URL/auth/github-oidc" | jq -r '.token')"
110
+ echo "::add-mask::$VOID_TOKEN"
111
+ echo "VOID_TOKEN=$VOID_TOKEN" >> "$GITHUB_ENV"
112
+ - name: Deploy
113
+ run: npx void deploy --project "$VOID_PROJECT"
88
114
  ```
89
115
 
90
- To get a deploy token, run `void auth token` to copy it to your clipboard, then add it as a GitHub Actions secret.
116
+ Connect the repository to your project once with `void github connect <project> --repo <owner/repo> --executor github_actions` to authorize it. This GitHub Actions path requires the `github_actions` build executor — the default `container` executor will not work with this workflow. The exact steps vary by package manager (the scaffold detects pnpm, yarn, bun, or npm). The `VOID_PROJECT` value is baked in from your linked project when known; otherwise the workflow reads a `VOID_PROJECT` repository variable. To target staging, set a `VOID_API_URL` repository variable to `https://api.staging.void.cloud`.
91
117
 
92
118
  ## Other Targets
93
119