void 0.20.1 → 0.20.2

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 (87) hide show
  1. package/README.md +5 -1
  2. package/dist/{auth-W9WII-mN.mjs → auth-DPl6kck4.mjs} +46 -26
  3. package/dist/{auth-cmd-CAH62yDU.mjs → auth-cmd-gniL2fNt.mjs} +5 -4
  4. package/dist/auth-link-NZdjCmSc.mjs +28 -0
  5. package/dist/{build-cmd-CJvZvPQO.mjs → build-cmd-sI18tX_O.mjs} +3 -3
  6. package/dist/{cache-BlNeQjuP.mjs → cache-IHn5MwBC.mjs} +3 -3
  7. package/dist/{cancel-deploy-CmlAZ9P6.mjs → cancel-deploy-C5qTOdLi.mjs} +3 -3
  8. package/dist/cf-access-DRsQRe6k.mjs +75 -0
  9. package/dist/cli/cli.mjs +308 -1958
  10. package/dist/cli/env-schema-probe.mjs +11 -2
  11. package/dist/{client-Clirrol3.mjs → client-dHfSJvAN.mjs} +300 -81
  12. package/dist/{cloudflare-auth-B1QtTO1b.mjs → cloudflare-auth-6M5llVPC.mjs} +2 -2
  13. package/dist/{cloudflare-cmd-B6_OZx2V.mjs → cloudflare-cmd-4RPGN3KB.mjs} +2 -2
  14. package/dist/{cloudflare-connect-j5D4hhrG.mjs → cloudflare-connect-t1UU5svD.mjs} +2 -2
  15. package/dist/{cloudflare-operations-CPTpRW6d.mjs → cloudflare-operations-BzWnlC1_.mjs} +1 -1
  16. package/dist/{config-BQFq7QvD.mjs → config-uNGuFsI2.mjs} +1 -1
  17. package/dist/{connect-C04Wdy_h.mjs → connect-Bfk31O_8.mjs} +6 -6
  18. package/dist/{create-project-ChGZ1DFd.mjs → create-project-Bk9Z0-Jg.mjs} +6 -5
  19. package/dist/{db-D2d_mUsB.mjs → db-BkRoptAt.mjs} +47 -30
  20. package/dist/{delete-D8GigDk8.mjs → delete-DouASY9P.mjs} +3 -3
  21. package/dist/{deploy-iXZ3F0N6.mjs → deploy-DTaWUS1S.mjs} +121 -110
  22. package/dist/{dev-inbox-DkgRWLkW.mjs → dev-inbox-P0u4tM8Y.mjs} +1 -1
  23. package/dist/{domain-B1VmoSr0.mjs → domain-1RhhOVrC.mjs} +4 -4
  24. package/dist/email-C-lGh51B.mjs +795 -0
  25. package/dist/{env-D4Emu-M_.mjs → env-DBKmK4vc.mjs} +1 -0
  26. package/dist/{env-BcQzYgoG.mjs → env-DJHsPE7Z.mjs} +5 -5
  27. package/dist/{env-validation-ENpMy6Ez.mjs → env-validation-CF6KvTRf.mjs} +3 -1
  28. package/dist/{gen-DI2YwdBM.mjs → gen-B_wPnVTK.mjs} +2 -2
  29. package/dist/{github-cmd-xItS5Zwf.mjs → github-cmd-PW7ZnWTp.mjs} +3 -3
  30. package/dist/{headers-D8QfRX9Y.mjs → headers-BAHwgHdW.mjs} +1 -1
  31. package/dist/help-CwOX-zmI.mjs +2216 -0
  32. package/dist/{inbound-afAcWeQ9.d.mts → inbound-CH5Mksyy.d.mts} +34 -48
  33. package/dist/{inbound-2d0zi2yS.mjs → inbound-aVHEUhKo.mjs} +130 -100
  34. package/dist/index.mjs +54 -17
  35. package/dist/{init-BD-9THgn.mjs → init-BWZ7q5Z4.mjs} +11 -11
  36. package/dist/{link-RMdgjF1v.mjs → link-Rmvu2Wl_.mjs} +4 -4
  37. package/dist/{list-3F52R_yO.mjs → list-DEE2S6mY.mjs} +4 -4
  38. package/dist/{login-pV69H-ZO.mjs → login-Uvferzmm.mjs} +28 -10
  39. package/dist/{logs-DFHHD6wE.mjs → logs-27FenuiC.mjs} +4 -4
  40. package/dist/{mime-BJD7d_qL.mjs → mime-D5Nmdzf7.mjs} +23 -9
  41. package/dist/{node-Dk3H2jmU.mjs → node-Ez5KW5rn.mjs} +2 -2
  42. package/dist/operator-auth-B3e08unv.mjs +52 -0
  43. package/dist/operator-client-LUZnlnYk.mjs +82 -0
  44. package/dist/{operator-cmd-DYWRbWUA.mjs → operator-cmd-CjOTmAYE.mjs} +35 -55
  45. package/dist/{output-tFQLLj26.mjs → output-B0cfNSx5.mjs} +316 -2
  46. package/dist/pages/index.mjs +2 -2
  47. package/dist/platform-auth-config-DrbQXXiW.mjs +368 -0
  48. package/dist/platform-auth-protection-Bhtvp0B_.mjs +219 -0
  49. package/dist/platform-auth-recovery-CeOKGVeJ.mjs +310 -0
  50. package/dist/{platform-cmd-DxJ2FRwR.mjs → platform-cmd-BFhieCdV.mjs} +16 -6
  51. package/dist/{platform-domain-ChvbJkdy.mjs → platform-domain-C74PULqV.mjs} +4 -4
  52. package/dist/{platform-lifecycle-DN4MzJF_.mjs → platform-lifecycle-BwAIgz-t.mjs} +1667 -191
  53. package/dist/{platform-management-Db2PXw0B.mjs → platform-management-COogu_Se.mjs} +33 -7
  54. package/dist/{platform-recovery-C_YO-tIs.mjs → platform-recovery-ewqLefp1.mjs} +6 -5
  55. package/dist/{prepare-CBetXvsN.mjs → prepare-CtDJjoOj.mjs} +2 -2
  56. package/dist/{prepare-BfJvFUtJ.mjs → prepare-blNRQvQl.mjs} +2 -2
  57. package/dist/prerender-render.d.mts +11 -0
  58. package/dist/prerender-render.mjs +111 -0
  59. package/dist/{project-cmd-Mo0V9yKS.mjs → project-cmd-DmZK9Hxf.mjs} +32 -14
  60. package/dist/project-team-D8jOJMUJ.mjs +130 -0
  61. package/dist/project-token-DA34bf-C.mjs +75 -0
  62. package/dist/{provision-Blnstcm2.mjs → provision-CSJOjjQk.mjs} +2 -0
  63. package/dist/{requests-BcKOVpRg.mjs → requests-CUExwGQQ.mjs} +3 -3
  64. package/dist/{rollback-Bx85-0xh.mjs → rollback-CDNGU1gr.mjs} +4 -4
  65. package/dist/runtime/ai.mjs +3 -2
  66. package/dist/runtime/email/testing.d.mts +1 -1
  67. package/dist/runtime/email/testing.mjs +3 -3
  68. package/dist/runtime/email-protocol.d.mts +15 -0
  69. package/dist/runtime/email-protocol.mjs +70 -0
  70. package/dist/runtime/email.d.mts +2 -2
  71. package/dist/runtime/email.mjs +189 -96
  72. package/dist/runtime/remote/index.mjs +5 -3
  73. package/dist/{secret-ByhJ9AMl.mjs → secret-Bzzi2e9E.mjs} +5 -5
  74. package/dist/{skills-Q46GZMO-.mjs → skills-C0RvGjeE.mjs} +1 -1
  75. package/dist/{subcommand-prompt-WfySCQ7S.mjs → subcommand-prompt-Bmyn5Rlc.mjs} +1 -1
  76. package/package.json +12 -7
  77. package/skills/void/SKILL.md +35 -4
  78. package/skills/void/docs/guide/deployment.md +2 -0
  79. package/skills/void/docs/guide/email.md +102 -110
  80. package/skills/void/docs/guide/platform-administration.md +214 -2
  81. package/skills/void/docs/guide/platform-development.md +64 -2
  82. package/skills/void/docs/guide/project-collaboration.md +94 -0
  83. package/skills/void/docs/guide/self-hosted-platform.md +184 -30
  84. package/skills/void/docs/reference/cli.md +249 -21
  85. package/dist/cf-access-AJ1ehiFR.mjs +0 -42
  86. package/dist/cf-access-DsSsZUPr.mjs +0 -67
  87. package/dist/email-uKyQYUVY.mjs +0 -1016
@@ -1,37 +1,37 @@
1
1
  import { n as __exportAll, r as __toESM, t as __commonJSMin } from "./rolldown-runtime-rQ84J-ij.mjs";
2
2
  import { c as relative, i as extname, l as resolve, n as dirname, o as join, s as normalize, t as basename } from "./dist-BrsS7cai.mjs";
3
- import { D as log$1, E as intro, F as isCancel, O as note, c as import_picocolors$1, d as formatCloudflareUserMessage, i as createSpinner, j as select, k as outro, r as cliTitle, st as require_picocolors, w as confirm } from "./output-tFQLLj26.mjs";
3
+ import { A as outro, B as ensureCloudflaredToken, D as log$1, E as intro, H as ensurePlatformProxyAccess, I as isCancel, M as select, c as import_picocolors$1, d as formatCloudflareUserMessage, ft as require_picocolors, i as createSpinner, k as note, r as cliTitle, w as confirm } from "./output-B0cfNSx5.mjs";
4
4
  import { i as resolveProjectPaths, n as readProjectPaths, t as assertNoRootSourceConventionConflicts } from "./project-paths-SK8nMHPp.mjs";
5
5
  import { a as scanQueuesSync, i as scanWebSocketRoutesSync, o as scanJobsSync, r as scanRoutes, s as findVoidAuthConfig } from "./scan-BMH4rzlv.mjs";
6
- import { r as trustedCloudflareWorkerAccessHeaders, t as cfAccessHeaders } from "./cf-access-AJ1ehiFR.mjs";
7
- import { C as writeDeploymentPlatform, T as writeProjectConfig, a as getApiUrl, b as readDeploymentPlatform, c as getToken, l as getTokenSource, o as getProxyUrl, u as isStagingMode, v as attachSelectedPlatform, x as readProjectConfig } from "./auth-W9WII-mN.mjs";
8
- import { t as ensureCloudflaredToken } from "./cf-access-DsSsZUPr.mjs";
6
+ import { i as trustedCloudflareWorkerAccessHeaders, r as scopedAccessCredentials, t as cfAccessHeaders } from "./cf-access-DRsQRe6k.mjs";
7
+ import { E as writeProjectConfig, S as readProjectConfig, d as isStagingMode, l as getToken, o as getApiUrl, s as getProxyUrl, u as getTokenSource, w as writeDeploymentPlatform, x as readDeploymentPlatform, y as attachSelectedPlatform } from "./auth-DPl6kck4.mjs";
9
8
  import { A as writeWranglerPublishedTrafficSnapshot, D as syncWranglerWebSockets, E as syncWranglerDurableObjects, M as isSandboxEnabled, N as requiresSandboxPlatformImage, P as resolveSandboxConfig, S as readWranglerPublishedTrafficSnapshot, T as syncWranglerBindings, a as LIVE_MIGRATION_TAG, c as bindingEntriesForField, f as findBindingNameOwner, g as readExistingLiveBindingConfig, h as normalizeWranglerCompatibilityFlags, k as writeProvisionedWranglerConfig, l as ensureWranglerCompat, o as acknowledgeWranglerManagedQueueRemovals, r as FRIENDLY_BINDING_NAMES, w as resolveLiveBindingConfig, x as readWranglerManagedQueueCleanupCandidates } from "./wrangler--imS8n0d.mjs";
10
- import { c as getDatabaseDialect, f as readConfig, l as isNodeTarget, p as resolveBindingNames } from "./config-BQFq7QvD.mjs";
9
+ import { c as getDatabaseDialect, f as readConfig, l as isNodeTarget, p as resolveBindingNames } from "./config-uNGuFsI2.mjs";
11
10
  import { i as voidWarn, r as voidLog } from "./log-ChfPKsVd.mjs";
12
- import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-D8QfRX9Y.mjs";
11
+ import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-BAHwgHdW.mjs";
13
12
  import { i as inferProjectBindings, n as FRAMEWORK_SCAN_DIRS, r as detectFramework } from "./plugin-inference-BDRfZngg.mjs";
14
13
  import { o as validateSsrEntry } from "./entry-DU3oDoQ3.mjs";
15
14
  import { a as buildMigrationPlanBundle, c as collectMigrations, r as validateMigrations, t as assertJournalCoherence } from "./validate-tBBN_dXH.mjs";
16
15
  import { r as scanWranglerDotenvKeys } from "./dotenv-VQxupEUv.mjs";
17
16
  import { t as discoverSchema } from "./discover-xvfrgJeo.mjs";
18
17
  import { t as findEnvFile } from "./env-types-BNPhro-M.mjs";
19
- import { a as deriveSelfHostEnvContextFromVars, c as fetchRemoteSecretNames, d as readEmittedWorkerVars, f as selfHostSchemaFailureMessage, i as SelfHostSecretListError, l as formatEnvReport, n as SelfHostFirstDeployError, o as dischargeSelfHostEnvContext, p as validateProdEnv, r as SelfHostSchemaError, t as SelfHostEnvValidationError, u as listSelfHostSecretNames } from "./env-validation-ENpMy6Ez.mjs";
20
- import { n as CLOUDFLARE_DEPLOY_BUILD_ENV, o as resolveRemoteMode, s as stripInternalEnvKeys } from "./env-D4Emu-M_.mjs";
18
+ import { a as deriveSelfHostEnvContextFromVars, c as fetchRemoteSecretNames, d as readEmittedWorkerVars, f as selfHostSchemaFailureMessage, i as SelfHostSecretListError, l as formatEnvReport, n as SelfHostFirstDeployError, o as dischargeSelfHostEnvContext, p as validateProdEnv, r as SelfHostSchemaError, t as SelfHostEnvValidationError, u as listSelfHostSecretNames } from "./env-validation-CF6KvTRf.mjs";
19
+ import { n as CLOUDFLARE_DEPLOY_BUILD_ENV, o as resolveRemoteMode, s as stripInternalEnvKeys } from "./env-DBKmK4vc.mjs";
21
20
  import { n as writeDrizzleConfig } from "./config-NOG_U1aK.mjs";
22
21
  import { t as runMySqlMigrations } from "./runner-mysql-7BPUNGmL.mjs";
23
22
  import { t as runPgMigrations } from "./runner-pg-BkEza-dX.mjs";
24
23
  import { n as openMigrationValidationDatabase, t as SqliteMigrationError } from "./sqlite-validation-BzKMWnO4.mjs";
25
24
  import { t as scanPages } from "./scan-CpK-57ug.mjs";
25
+ import { renderPrerenderPaths } from "./prerender-render.mjs";
26
26
  import { a as scanEmailHandlersSync, r as hasEmailHandlers } from "./email-Ce6SQq-i.mjs";
27
- import { a as isExpiredTokenError, i as isCliOutdatedError, n as PlatformClient, o as parsePlatformErrorBody, t as PlatformApiError } from "./client-Clirrol3.mjs";
27
+ import { a as isExpiredTokenError, i as isCliOutdatedError, n as PlatformClient, o as parsePlatformErrorBody, t as PlatformApiError } from "./client-dHfSJvAN.mjs";
28
28
  import { a as resolveProjectCommand, i as hasBackendFiles, n as detectPreset, o as resolveWranglerArgv, r as formatProjectCommand, t as FRAMEWORK_PRESETS } from "./preset-lAy0B0BQ.mjs";
29
- import { i as cloudflareKeyringNeedsTerminal, s as ensureCloudflareAuthentication, t as CloudflareAuthenticationError } from "./cloudflare-auth-B1QtTO1b.mjs";
30
- import { n as createCloudflareEmptyDomainPublisher } from "./cloudflare-operations-CPTpRW6d.mjs";
31
- import { i as promptProjectSetupAction, n as promptAndCreateProject, r as promptProjectSelection } from "./create-project-ChGZ1DFd.mjs";
29
+ import { i as cloudflareKeyringNeedsTerminal, s as ensureCloudflareAuthentication, t as CloudflareAuthenticationError } from "./cloudflare-auth-6M5llVPC.mjs";
30
+ import { n as createCloudflareEmptyDomainPublisher } from "./cloudflare-operations-BzWnlC1_.mjs";
31
+ import { i as promptProjectSetupAction, n as promptAndCreateProject, r as promptProjectSelection } from "./create-project-Bk9Z0-Jg.mjs";
32
32
  import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project--Vxawf7z.mjs";
33
33
  import { t as collectGitDeployMetadata } from "./git-metadata-D-gurjzG.mjs";
34
- import { t as promptForLoginToken } from "./login-pV69H-ZO.mjs";
34
+ import { n as promptForLoginToken } from "./login-Uvferzmm.mjs";
35
35
  import { createRequire } from "node:module";
36
36
  import { copyFileSync, cpSync, createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
37
37
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -43,7 +43,7 @@ import { applyEdits, modify, parse } from "jsonc-parser";
43
43
  import { build } from "vite";
44
44
  import { Minimatch } from "minimatch";
45
45
  import { isDeepStrictEqual } from "node:util";
46
- import { activateCloudflareVersion, assertCloudflareBindingsPreserved, assertCloudflareDurableObjectLifecycleSupported, assertExistingCloudflareConfig, encodeCloudflareVersionMessage, extractWorkersDevSubdomain, normalizeWorkersDevSubdomain, parseRootWranglerConfig, parseRootWranglerConfig as parseRootWranglerConfig$1, readBindingFieldPresence, readCloudflareTriggerConfig, readConfigEntryString, readExistingCloudflareWorker, readStoredResourceName, readWranglerTopLevelString, renderCloudflareStaticEntry, resolveD1MigrateTarget, stageCloudflareStaticAssets, writeCloudflareFrameworkDeployConfig, writeCloudflareStaticDeployConfig } from "@void/deploy-cloudflare";
46
+ import { activateCloudflareVersion, assertCloudflareBindingsPreserved, assertCloudflareDurableObjectLifecycleSupported, assertExistingCloudflareConfig, createCloudflareMetadataRunner, encodeCloudflareVersionMessage, extractWorkersDevSubdomain, normalizeWorkersDevSubdomain, parseRootWranglerConfig, parseRootWranglerConfig as parseRootWranglerConfig$1, readBindingFieldPresence, readCloudflareTriggerConfig, readConfigEntryString, readExistingCloudflareWorker, readStoredResourceName, readWranglerTopLevelString, renderCloudflareStaticEntry, resolveD1MigrateTarget, stageCloudflareStaticAssets, writeCloudflareFrameworkDeployConfig, writeCloudflareStaticDeployConfig } from "@void/deploy-cloudflare";
47
47
  import { hash } from "blake3-jit";
48
48
  import ignore from "ignore";
49
49
  //#region src/asset-routing.ts
@@ -597,7 +597,7 @@ async function preflightEmail(inputs) {
597
597
  status: settings.status,
598
598
  enabled: settings.enabled
599
599
  };
600
- if (/^(unlocked|misconfigured)/.test(settings.status)) block("all", `Email Routing on ${apex} is ${settings.status} — fix it in the Cloudflare dashboard; Void never re-locks records you unlocked`);
600
+ if (settings.status === "unlocked") block("all", `Email Routing on ${apex} is ${settings.status} — fix it in the Cloudflare dashboard; Void never re-locks records you unlocked`);
601
601
  }
602
602
  }
603
603
  if (pre.routing.state !== "ready") {
@@ -1507,6 +1507,8 @@ const UNTRUSTED_PROJECT_CODE_ENV_VARS = [
1507
1507
  ...WRANGLER_AUTH_CREDENTIAL_ENV_VARS,
1508
1508
  "CF_ACCESS_CLIENT_ID",
1509
1509
  "CF_ACCESS_CLIENT_SECRET",
1510
+ "VOID_ACCESS_CREDENTIALS",
1511
+ "VOID_ACCESS_ORIGIN",
1510
1512
  "VOID_TOKEN",
1511
1513
  "DATABASE_URL",
1512
1514
  "ACTIONS_ID_TOKEN_REQUEST_URL",
@@ -1534,61 +1536,16 @@ function buildEnv(extra, options) {
1534
1536
  //#endregion
1535
1537
  //#region src/prerender.ts
1536
1538
  const PRERENDER_PATHS_ENDPOINT = "/__void/prerender-paths";
1537
- const STATIC_PAGE_DATA_DIR = "_void/pages";
1539
+ const nodeRendererUrl = new URL(import.meta.url.endsWith(".ts") ? "./prerender-render.ts" : "./prerender-render.mjs", import.meta.url).href;
1538
1540
  function outputFileForPath(path) {
1539
1541
  return path === "/" ? "index.html" : `${path.slice(1)}.html`;
1540
1542
  }
1541
- function pageDataFileForPath(path) {
1542
- return path === "/" ? `${STATIC_PAGE_DATA_DIR}/index.json` : `${STATIC_PAGE_DATA_DIR}/${path.slice(1)}.json`;
1543
- }
1544
1543
  async function readPrerenderPathsResponse(response) {
1545
1544
  if (!response.ok) throw new Error(`prerender: Failed to collect prerender paths. HTTP ${response.status}.`);
1546
1545
  const payload = await response.json();
1547
1546
  if (!Array.isArray(payload.paths) || !payload.paths.every((path) => typeof path === "string")) throw new Error("prerender: Failed to collect prerender paths. Response payload is invalid.");
1548
1547
  return payload.paths;
1549
1548
  }
1550
- async function renderPrerenderPaths(paths, clientDir, fetchPath, fetchPageData) {
1551
- const result = {
1552
- rendered: [],
1553
- failed: []
1554
- };
1555
- for (const path of paths) try {
1556
- const response = await fetchPath(path);
1557
- const html = await response.text();
1558
- if (!response.ok) {
1559
- result.failed.push({
1560
- path,
1561
- error: `HTTP ${response.status}`
1562
- });
1563
- voidWarn(`prerender ${path} failed: HTTP ${response.status}`);
1564
- continue;
1565
- }
1566
- const outputFile = outputFileForPath(path);
1567
- const outputPath = join(clientDir, outputFile);
1568
- mkdirSync(dirname(outputPath), { recursive: true });
1569
- writeFileSync(outputPath, html);
1570
- if (fetchPageData) {
1571
- const pageDataResponse = await fetchPageData(path);
1572
- if (pageDataResponse.headers.get("X-VoidPages")) {
1573
- const pageData = await pageDataResponse.json();
1574
- const pageDataFile = pageDataFileForPath(path);
1575
- const pageDataPath = join(clientDir, pageDataFile);
1576
- mkdirSync(dirname(pageDataPath), { recursive: true });
1577
- writeFileSync(pageDataPath, JSON.stringify(pageData));
1578
- }
1579
- }
1580
- result.rendered.push(path);
1581
- voidLog(`prerender \x1b[36m${outputFile}\x1b[39m`);
1582
- } catch (err) {
1583
- const error = err instanceof Error ? err.message : String(err);
1584
- result.failed.push({
1585
- path,
1586
- error
1587
- });
1588
- voidWarn(`prerender ${path} failed: ${error}`);
1589
- }
1590
- return result;
1591
- }
1592
1549
  function normalizeWorkerModulePath(path) {
1593
1550
  return path.replace(/\\/g, "/").replace(/^\.\//, "");
1594
1551
  }
@@ -1646,6 +1603,8 @@ function buildPrerenderBindings(input) {
1646
1603
  voidBindings.__VOID_PROJECT_ID = input.voidProjectId;
1647
1604
  if (input.voidStaging) voidBindings.__VOID_STAGING = "1";
1648
1605
  voidBindings.__VOID_PROXY_URL = input.voidProxyUrl ?? (input.voidStaging ? "https://proxy.staging.void.cloud" : "https://proxy.void.cloud");
1606
+ const access = scopedAccessCredentials(input.accessEnv ?? {}, voidBindings.__VOID_PROXY_URL);
1607
+ if (access) voidBindings.__VOID_ACCESS_CREDENTIALS = access;
1649
1608
  if (input.remoteMode) voidBindings.__VOID_REMOTE = "1";
1650
1609
  }
1651
1610
  return {
@@ -1673,7 +1632,9 @@ async function createCloudflarePrerenderRunner(options) {
1673
1632
  const voidToken = voidProjectId ? options.voidToken ?? getToken(root) : null;
1674
1633
  const voidStaging = isStagingMode(root);
1675
1634
  const voidProxyUrl = voidToken && voidProjectId ? getProxyUrl(root) : void 0;
1635
+ if (voidProxyUrl) ensurePlatformProxyAccess(getApiUrl(root), voidProxyUrl);
1676
1636
  const bindings = buildPrerenderBindings({
1637
+ accessEnv: process.env,
1677
1638
  wranglerVars: wranglerConfig.vars,
1678
1639
  env,
1679
1640
  voidToken,
@@ -1771,7 +1732,10 @@ async function prerenderPages(options) {
1771
1732
  };
1772
1733
  return {
1773
1734
  paths,
1774
- ...await renderPrerenderPaths(paths, options.clientDir, (path) => runner.fetchPath(path), (path) => runner.fetchPageData(path))
1735
+ ...await renderPrerenderPaths(paths, options.clientDir, (path) => runner.fetchPath(path), (path) => runner.fetchPageData(path), (path, outputFile, error) => {
1736
+ if (error !== void 0) voidWarn(`prerender ${path} failed: ${error}`);
1737
+ else voidLog(`prerender \x1b[36m${outputFile}\x1b[39m`);
1738
+ })
1775
1739
  };
1776
1740
  } finally {
1777
1741
  await runner.close();
@@ -1783,7 +1747,7 @@ async function runNodePrerenderRunner(root, config) {
1783
1747
  writeFileSync(runnerPath, `
1784
1748
  import { pathToFileURL } from "node:url";
1785
1749
  import { dirname, join } from "node:path";
1786
- import { mkdirSync, writeFileSync } from "node:fs";
1750
+ import { renderPrerenderPaths } from ${JSON.stringify(nodeRendererUrl)};
1787
1751
 
1788
1752
  const config = JSON.parse(process.argv[2]);
1789
1753
  const { mode, ssrDir, clientDir } = config;
@@ -1814,37 +1778,13 @@ async function collectPaths() {
1814
1778
 
1815
1779
  async function renderPaths(paths) {
1816
1780
  if (!clientDir) return { rendered: [], failed: [] };
1817
- const result = { rendered: [], failed: [] };
1818
-
1819
- for (const path of paths) {
1820
- try {
1821
- const response = await fetch(new Request("http://localhost" + path));
1822
- const html = await response.text();
1823
- if (!response.ok) {
1824
- result.failed.push({ path, error: "HTTP " + response.status });
1825
- continue;
1826
- }
1827
- const outputFile = path === "/" ? "index.html" : path.slice(1) + ".html";
1828
- const outputPath = join(clientDir, outputFile);
1829
- mkdirSync(dirname(outputPath), { recursive: true });
1830
- writeFileSync(outputPath, html);
1831
- const pageDataResponse = await fetch(new Request("http://localhost" + path, {
1832
- headers: { "Accept": "application/json", "Purpose": "prefetch", "X-VoidPages": "true" },
1833
- }));
1834
- if (pageDataResponse.headers.get("X-VoidPages")) {
1835
- const pageData = await pageDataResponse.json();
1836
- const pageDataFile = path === "/" ? "_void/pages/index.json" : "_void/pages/" + path.slice(1) + ".json";
1837
- const pageDataPath = join(clientDir, pageDataFile);
1838
- mkdirSync(dirname(pageDataPath), { recursive: true });
1839
- writeFileSync(pageDataPath, JSON.stringify(pageData));
1840
- }
1841
- result.rendered.push(path);
1842
- } catch (err) {
1843
- result.failed.push({ path, error: err instanceof Error ? err.message : String(err) });
1844
- }
1845
- }
1846
-
1847
- return result;
1781
+ return renderPrerenderPaths(
1782
+ paths, clientDir,
1783
+ (path) => fetch(new Request("http://localhost" + path)),
1784
+ (path) => fetch(new Request("http://localhost" + path, {
1785
+ headers: { "Accept": "application/json", "Purpose": "prefetch", "X-VoidPages": "true" },
1786
+ })),
1787
+ );
1848
1788
  }
1849
1789
 
1850
1790
  if (mode === "collect") {
@@ -2410,7 +2350,10 @@ async function requestGithubOidcToken(requestUrl, requestToken) {
2410
2350
  url.searchParams.set("audience", OIDC_AUDIENCE);
2411
2351
  let res;
2412
2352
  try {
2413
- res = await fetch(url, { headers: { Authorization: `Bearer ${requestToken}` } });
2353
+ res = await fetch(url, {
2354
+ headers: { Authorization: `Bearer ${requestToken}` },
2355
+ redirect: "manual"
2356
+ });
2414
2357
  } catch (err) {
2415
2358
  throw new Error(`github-oidc: Failed to reach the GitHub OIDC token endpoint: ${errText(err)}`);
2416
2359
  }
@@ -2443,7 +2386,8 @@ async function exchangeOidcTokenAtApi(apiUrl, project, oidcToken) {
2443
2386
  body: JSON.stringify({
2444
2387
  oidcToken,
2445
2388
  project
2446
- })
2389
+ }),
2390
+ redirect: "manual"
2447
2391
  });
2448
2392
  } catch (err) {
2449
2393
  throw new Error(`github-oidc: Failed to reach ${url}: ${errText(err)}`);
@@ -3169,6 +3113,7 @@ function buildVoidManifest(opts) {
3169
3113
  assetManifestVersion: 2,
3170
3114
  bindings: {}
3171
3115
  };
3116
+ if (opts.hasEmailHandlers) manifest.emailProtocolVersion = 1;
3172
3117
  if (bindings.needsAuth) manifest.auth = true;
3173
3118
  const hasMigrations = migrations && migrations.length > 0;
3174
3119
  if (bindings.needsD1 || bindings.needsAuth || hasMigrations) {
@@ -3899,7 +3844,7 @@ let loadedProvisionModule;
3899
3844
  * so a managed deploy never imports it — and thus never loads `proper-lockfile`.
3900
3845
  */
3901
3846
  async function loadProvisionModule() {
3902
- loadedProvisionModule ??= await import("./provision-Blnstcm2.mjs").then((n) => n.a);
3847
+ loadedProvisionModule ??= await import("./provision-CSJOjjQk.mjs").then((n) => n.a);
3903
3848
  return loadedProvisionModule;
3904
3849
  }
3905
3850
  /**
@@ -5809,14 +5754,27 @@ async function runWranglerAuthToken(runWrangler, configPath) {
5809
5754
  return (type === "oauth" || type === "api_token") && typeof token === "string" && token.length > 0 ? token : null;
5810
5755
  }
5811
5756
  /**
5757
+ * Immediately after setup, the local resolver can retain ENODATA while Cloudflare's public DNS
5758
+ * already serves the new subdomain MX. When the committed config is exactly setup's plan and
5759
+ * every other inbound prerequisite is still ready, DNS is ambiguous: deleting `addresses`
5760
+ * destroys the handoff, while deploying it could fail after upload if routing is genuinely off.
5761
+ */
5762
+ function isCommittedSubdomainPlanWaitingForDns(pre, rootConfig) {
5763
+ if (pre.mode !== "subdomain" || pre.domainMx.state !== "empty" && pre.domainMx.state !== "unknown" || pre.routing.enabled !== true || pre.subaddressing.state !== "on" || pre.binding.state !== "present" || pre.sender.state !== "ready" || pre.addressesOwnership.state !== "mine" || pre.addressesStale || pre.rules.state === "unknown" || pre.rules.foreign.length > 0 || !Array.isArray(rootConfig.addresses) || rootConfig.addresses.length === 0) return false;
5764
+ const allBlockers = pre.blockers.filter((blocker) => blocker.scope === "all");
5765
+ if (pre.domainMx.state === "empty") return allBlockers.length === 0;
5766
+ return allBlockers.length === 1;
5767
+ }
5768
+ /**
5812
5769
  * Step 3b-ii — Mode B email on the developer's own account (design doc 0078, "One keystroke").
5813
5770
  * Preflight is read-only; the checklist is shown; ONE `confirm` (default Yes) authorizes the
5814
5771
  * apply, which runs only the rows not ready and returns the root-config patch this writes.
5815
- * Every branch that cannot finish prints why and returns `satisfied: false` so the deploy
5816
- * continues (never a deploy failure on its own — `--require-email` is the caller's call),
5817
- * WITH the email setup the config already carries when the binding is committed, and
5818
- * without email otherwise. No output of this step ever contains the session bearer: the
5819
- * module borrows it through `getSessionBearer` inside its own closures and drops it.
5772
+ * Most branches that cannot finish return `satisfied: false`, leaving `--require-email` to
5773
+ * decide whether deployment continues. One ambiguous handoff fails closed for every deploy:
5774
+ * an exact committed subdomain plan whose new MX is still empty or unreadable. It is preserved,
5775
+ * and the caller stops before build/upload until DNS can be verified. No output of this step
5776
+ * ever contains the session bearer: the module borrows it through `getSessionBearer` inside its
5777
+ * own closures and drops it.
5820
5778
  */
5821
5779
  async function runCloudflareEmailStep(input) {
5822
5780
  const without = {
@@ -5905,6 +5863,19 @@ async function runCloudflareEmailStep(input) {
5905
5863
  };
5906
5864
  }
5907
5865
  log.message(renderEmailChecklist(pre));
5866
+ if (isCommittedSubdomainPlanWaitingForDns(pre, rootConfig)) {
5867
+ const dnsState = pre.domainMx.state === "empty" ? "this resolver sees no MX records yet" : "the MX lookup failed";
5868
+ log.error([
5869
+ `deploy: cannot verify Email Routing on ${pre.domain} because ${dnsState}.`,
5870
+ `The existing addresses plan in ${configName} is preserved. DNS may still be propagating after \`void email setup\`; run \`void email status --platform cloudflare\` and retry the deploy when this resolver sees Cloudflare's MX records.`,
5871
+ "Nothing was built or uploaded."
5872
+ ].join("\n"));
5873
+ return {
5874
+ ...without,
5875
+ abortDeploy: true,
5876
+ map: mapFor(existingAddresses(rootConfig, pre), pre.sending.state === "ready")
5877
+ };
5878
+ }
5908
5879
  const shipsBinding = pre.binding.state === "present";
5909
5880
  const leaveAccount = (reason) => {
5910
5881
  let configWritten = false;
@@ -6159,6 +6130,7 @@ async function runCloudflareDeploy(root, options, cliLog, deps = {}) {
6159
6130
  fetchImpl: deps.emailFetch ?? fetch,
6160
6131
  dns: deps.emailDns ?? await createNodeEmailDns()
6161
6132
  });
6133
+ if (email.abortDeploy) return exit(1);
6162
6134
  if (options.requireEmail && !email.satisfied) {
6163
6135
  log.error(["deploy: --require-email — email is not fully set up for this deploy (see above); refusing to deploy.", "Nothing was built or uploaded. Set email up, or drop --require-email."].join("\n"));
6164
6136
  return exit(1);
@@ -6711,8 +6683,18 @@ async function runCloudflareDeploy(root, options, cliLog, deps = {}) {
6711
6683
  const queueConsumerCleanup = readWranglerManagedQueueCleanupCandidates(configPath);
6712
6684
  if (existingWorker) assertCloudflareLinkUnchanged(root, pinnedHandoffLink);
6713
6685
  const candidateReceipt = pinnedHandoffLink?.candidateReceipt;
6714
- const execution = await (deps.activateVersion ?? activateCloudflareVersion)({
6686
+ const activationRunner = preBuildRootConfig.compliance_region ? runWrangler : createCloudflareMetadataRunner({
6715
6687
  runWrangler,
6688
+ accountId,
6689
+ apiBaseUrl,
6690
+ workerName,
6691
+ configPath: generatedConfigPath,
6692
+ fetch: deps.metadataFetch,
6693
+ verifyUploader: assertUploaderStillPinned,
6694
+ getToken: () => runWranglerAuthToken(runWrangler, generatedConfigPath)
6695
+ });
6696
+ const execution = await (deps.activateVersion ?? activateCloudflareVersion)({
6697
+ runWrangler: activationRunner,
6716
6698
  verifyUploader: assertUploaderStillPinned,
6717
6699
  workerName,
6718
6700
  configPath: generatedConfigPath,
@@ -7103,8 +7085,18 @@ async function runCloudflareStaticDeploy(root, options, voidConfig, preset, cliL
7103
7085
  const queueConsumerCleanup = readWranglerManagedQueueCleanupCandidates(configPath);
7104
7086
  if (existingWorker) assertCloudflareLinkUnchanged(root, pinnedHandoffLink);
7105
7087
  const candidateReceipt = pinnedHandoffLink?.candidateReceipt;
7106
- const execution = await (deps.activateVersion ?? activateCloudflareVersion)({
7088
+ const activationRunner = rootConfig.compliance_region ? runWrangler : createCloudflareMetadataRunner({
7107
7089
  runWrangler,
7090
+ accountId,
7091
+ apiBaseUrl,
7092
+ workerName,
7093
+ configPath: staticConfigPath,
7094
+ fetch: deps.metadataFetch,
7095
+ verifyUploader: uploaderIsPinned,
7096
+ getToken: () => runWranglerAuthToken(runWrangler, staticConfigPath)
7097
+ });
7098
+ const execution = await (deps.activateVersion ?? activateCloudflareVersion)({
7099
+ runWrangler: activationRunner,
7108
7100
  verifyUploader: uploaderIsPinned,
7109
7101
  configPath: staticConfigPath,
7110
7102
  preserveTriggers: existingWorker !== null,
@@ -7270,6 +7262,7 @@ async function runManagedDeploy(root, options, cliLog) {
7270
7262
  let config = null;
7271
7263
  if (requestedProjectSlug) {
7272
7264
  const projects = await client.listProjects();
7265
+ assertProjectDeployAccess(projects.find((project) => project.slug === requestedProjectSlug));
7273
7266
  config = resolveProjectBySlug(projects, requestedProjectSlug);
7274
7267
  if (!config) {
7275
7268
  if (!process.stdin.isTTY) config = await createProjectFromSlug(root, client, requestedProjectSlug, cliLog);
@@ -7282,7 +7275,16 @@ async function runManagedDeploy(root, options, cliLog) {
7282
7275
  config = await createProjectFromSlug(root, client, requestedProjectSlug, cliLog);
7283
7276
  }
7284
7277
  }
7285
- } else config = readProjectConfig(root);
7278
+ } else {
7279
+ config = readProjectConfig(root);
7280
+ if (config) {
7281
+ const projects = await client.listProjects();
7282
+ const { projectId, slug } = config;
7283
+ const project = projects.find((project) => project.id === projectId);
7284
+ if (!project) throw new Error(`deploy: The linked project "${slug}" is unavailable or you no longer have access. Ask a project admin or owner to restore access, or choose another project with \`--project <slug>\`.`);
7285
+ assertProjectDeployAccess(project);
7286
+ }
7287
+ }
7286
7288
  if (!config && !process.stdin.isTTY) {
7287
7289
  log.error("No project specified. Set `VOID_PROJECT`, pass `--project <slug>`, or commit `.void/project.json`.");
7288
7290
  process.exit(1);
@@ -7345,10 +7347,15 @@ async function runManagedDeploy(root, options, cliLog) {
7345
7347
  throw error;
7346
7348
  }
7347
7349
  }
7350
+ function assertProjectDeployAccess(project) {
7351
+ if (project?.role === "reader") throw new Error(`deploy: You have reader access to "${project.slug}" and cannot deploy. Ask a project admin or owner to grant collaborator access, or choose another project with \`--project <slug>\`.`);
7352
+ }
7348
7353
  async function promptDeployProjectConfig(root, client, cliLog) {
7349
7354
  const projects = await client.listProjects();
7350
- if (await promptProjectSetupAction("Set up a Void project for deploy:", { includeLink: projects.length > 0 }) === "create") return promptAndCreateProject(root, client);
7351
- const result = await promptProjectSelection(projects, "Select a project to deploy to:", root, {
7355
+ const deployableProjects = projects.filter((project) => project.role !== "reader");
7356
+ if (deployableProjects.length !== projects.length) log.warn("Projects with reader access are hidden from deploy selection. Ask a project admin or owner to grant collaborator access to deploy to them.");
7357
+ if (await promptProjectSetupAction("Set up a Void project for deploy:", { includeLink: deployableProjects.length > 0 }) === "create") return promptAndCreateProject(root, client);
7358
+ const result = await promptProjectSelection(deployableProjects, "Select a project to deploy to:", root, {
7352
7359
  confirmExisting: true,
7353
7360
  includeCreate: false
7354
7361
  });
@@ -7645,7 +7652,10 @@ async function streamDirectR2Deploy(client, projectId, manifest, assetManifest,
7645
7652
  await uploadAssetsToR2(items, cliLog, void 0, (item) => {
7646
7653
  assetFiles.delete(item.path);
7647
7654
  });
7648
- } else cliLog?.info("presign_skipped", { reason: "no_needed_assets" });
7655
+ } else {
7656
+ assetFiles.clear();
7657
+ cliLog?.info("presign_skipped", { reason: "no_needed_assets" });
7658
+ }
7649
7659
  let encodedWorkerFiles;
7650
7660
  if (workerFiles && workerFiles.size > 0) {
7651
7661
  encodedWorkerFiles = {};
@@ -7894,7 +7904,7 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
7894
7904
  } catch {}
7895
7905
  if (resources) {
7896
7906
  if (!resources.some((resource) => resource.type === "hyperdrive")) {
7897
- const { text: promptText, isCancel: isCancelPrompt } = await import("./output-tFQLLj26.mjs").then((n) => n.T);
7907
+ const { text: promptText, isCancel: isCancelPrompt } = await import("./output-B0cfNSx5.mjs").then((n) => n.T);
7898
7908
  const connectionString = await promptText({
7899
7909
  message: `Your project uses ${dialect === "mysql" ? "MySQL" : "PostgreSQL"}. Enter your connection string:`,
7900
7910
  placeholder: dialect === "mysql" ? "mysql://user:pass@host:3306/dbname" : "postgresql://user:pass@host:5432/dbname",
@@ -8140,7 +8150,7 @@ async function runFullDeploy(root, config, client, voidConfig, fwPreset, skipBui
8140
8150
  } catch {}
8141
8151
  if (resources) {
8142
8152
  if (!resources.some((resource) => resource.type === "hyperdrive")) {
8143
- const { text: promptText, isCancel: isCancelPrompt } = await import("./output-tFQLLj26.mjs").then((n) => n.T);
8153
+ const { text: promptText, isCancel: isCancelPrompt } = await import("./output-B0cfNSx5.mjs").then((n) => n.T);
8144
8154
  const connectionString = await promptText({
8145
8155
  message: `Your project uses ${dialect === "mysql" ? "MySQL" : "PostgreSQL"}. Enter your connection string:`,
8146
8156
  placeholder: dialect === "mysql" ? "mysql://user:pass@host:3306/dbname" : "postgresql://user:pass@host:5432/dbname",
@@ -8296,6 +8306,7 @@ async function runFullDeploy(root, config, client, voidConfig, fwPreset, skipBui
8296
8306
  ...effectiveBindings,
8297
8307
  needsLive: true
8298
8308
  } : effectiveBindings,
8309
+ hasEmailHandlers: !isFrameworkMode && routeScan.email !== void 0 && hasEmailHandlers(routeScan.email),
8299
8310
  migrations: validatedMigrations,
8300
8311
  schedules,
8301
8312
  ssr: isSsr || hasRoutes || hasMiddleware || hasWebSockets || authEnabled,
@@ -1,4 +1,4 @@
1
- import { t as MAX_MESSAGE_BYTES } from "./mime-BJD7d_qL.mjs";
1
+ import { t as MAX_MESSAGE_BYTES } from "./mime-D5Nmdzf7.mjs";
2
2
  //#region src/runtime/email/dev-inbox.ts
3
3
  /**
4
4
  * In-memory capture sink for emails during local dev / tests.
@@ -1,6 +1,6 @@
1
- import { D as log, O as note, c as import_picocolors } from "./output-tFQLLj26.mjs";
2
- import { b as readDeploymentPlatform, c as getToken, x as readProjectConfig } from "./auth-W9WII-mN.mjs";
3
- import { n as PlatformClient } from "./client-Clirrol3.mjs";
1
+ import { D as log, c as import_picocolors, k as note } from "./output-B0cfNSx5.mjs";
2
+ import { S as readProjectConfig, l as getToken, x as readDeploymentPlatform } from "./auth-DPl6kck4.mjs";
3
+ import { n as PlatformClient } from "./client-dHfSJvAN.mjs";
4
4
  //#region src/cli/domain.ts
5
5
  async function runDomainCommand(root, args) {
6
6
  const platform = readDeploymentPlatform(root);
@@ -38,7 +38,7 @@ async function runDomainCommand(root, args) {
38
38
  }
39
39
  async function runCloudflareDomainCommand(root, args) {
40
40
  if (args.projectSlug) throw new Error("domain: --project selects a Void platform project and is not used for direct Cloudflare apps.");
41
- const { changeCloudflareDomain, listCloudflareDomains } = await import("./cloudflare-operations-CPTpRW6d.mjs").then((n) => n.t);
41
+ const { changeCloudflareDomain, listCloudflareDomains } = await import("./cloudflare-operations-BzWnlC1_.mjs").then((n) => n.t);
42
42
  if (args.subcommand === "add") {
43
43
  const result = await changeCloudflareDomain(root, args.hostname, "add");
44
44
  log.success(result.modified ? `Domain ${args.hostname} added and activated on Cloudflare.` : `Domain ${args.hostname} is already configured.`);