void 0.7.1 → 0.7.3
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.
- package/dist/agents-DqkFfc2c.mjs +151 -0
- package/dist/{auth-cmd-DVKi6dzh.mjs → auth-cmd-Dk0acCT5.mjs} +2 -2
- package/dist/{better-auth-shared-C9_GHSkR.d.mts → better-auth-shared-CZsIpjey.d.mts} +1 -1
- package/dist/{cache-B0BgSTZi.mjs → cache-DGSZ5Bh6.mjs} +2 -2
- package/dist/{cancel-deploy-D9OFt5gA.mjs → cancel-deploy-CrY3kt93.mjs} +1 -1
- package/dist/cli/cli.mjs +27 -174
- package/dist/{client-BUdfE3QJ.mjs → client-DCqnMpDt.mjs} +97 -11
- package/dist/{create-project-CN1pF-OQ.mjs → create-project-Bg88Kq_I.mjs} +3 -3
- package/dist/{db-BIP2kuEt.mjs → db-ClNu7vYQ.mjs} +13 -13
- package/dist/{delete-DJTvwbr-.mjs → delete-DXcX1yQZ.mjs} +2 -2
- package/dist/{deploy-BqXz1ycW.mjs → deploy-BkjqNk9U.mjs} +565 -161
- package/dist/{domain-B-fIU3VE.mjs → domain-CDQhvYNZ.mjs} +1 -1
- package/dist/{env-BwbZJd2x.mjs → env-CnrQY2b6.mjs} +1 -1
- package/dist/{env-helpers-Dr9Y7RnE.d.mts → env-helpers-CbeM_7-k.d.mts} +1 -1
- package/dist/{gen-U0Ktr4Zd.mjs → gen-C0EY2k27.mjs} +1 -1
- package/dist/{handler-B0ds0OHJ.d.mts → handler-dKQWyF-G.d.mts} +3 -3
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +13 -12
- package/dist/{init-Bb_Qsdq6.mjs → init-CPny6w9D.mjs} +63 -28
- package/dist/{link-D4d26PCm.mjs → link-eZ0aiHFK.mjs} +2 -2
- package/dist/{list-bQc1eQCZ.mjs → list-ztyEz4TW.mjs} +2 -2
- package/dist/{login-RWUDCfdx.mjs → login-B5HHT32i.mjs} +1 -1
- package/dist/{logs-DrkTklop.mjs → logs-J4BN0LXd.mjs} +1 -1
- package/dist/{mcp-kZ4zg13a.mjs → mcp-Bdu9bnjR.mjs} +1 -1
- package/dist/{node-DDfXj10V.mjs → node-DFqMcZR1.mjs} +3 -3
- package/dist/pages/client.d.mts +1 -1
- package/dist/pages/client.mjs +3 -0
- package/dist/pages/head-client.d.mts +1 -1
- package/dist/pages/head.d.mts +1 -1
- package/dist/pages/index.d.mts +2 -2
- package/dist/pages/index.mjs +1 -1
- package/dist/pages/islands-plugin.d.mts +1 -1
- package/dist/pages/protocol.d.mts +2 -2
- package/dist/pages/protocol.mjs +23 -18
- package/dist/{prepare-BAtWufvm.mjs → prepare-DKkx-2Kt.mjs} +1 -1
- package/dist/{project-cmd-ATFi3kRm.mjs → project-cmd-DKiQYdSd.mjs} +8 -8
- package/dist/{protocol-BWzXs2A2.d.mts → protocol-CK4OFwfR.d.mts} +2 -2
- package/dist/{rollback-BSyita3C.mjs → rollback-ZNvT8T54.mjs} +1 -1
- package/dist/{runner-6Ep3fNQu.mjs → runner-BUPRnMFN.mjs} +1 -1
- package/dist/runtime/ai.mjs +1 -1
- package/dist/runtime/auth.d.mts +1 -1
- package/dist/runtime/better-auth-pg.d.mts +1 -1
- package/dist/runtime/better-auth-pg.mjs +2 -2
- package/dist/runtime/better-auth.d.mts +1 -1
- package/dist/runtime/better-auth.mjs +2 -2
- package/dist/runtime/client.d.mts +2 -2
- package/dist/runtime/client.mjs +1 -1
- package/dist/runtime/env-helpers.d.mts +1 -1
- package/dist/runtime/env-public-client.d.mts +1 -1
- package/dist/runtime/env-public.d.mts +2 -2
- package/dist/runtime/env-public.mjs +1 -1
- package/dist/runtime/env.mjs +1 -1
- package/dist/runtime/fetch-stream.d.mts +1 -1
- package/dist/runtime/fetch-stream.mjs +1 -1
- package/dist/runtime/fetch.d.mts +1 -1
- package/dist/runtime/fetch.mjs +1 -1
- package/dist/runtime/handler.d.mts +1 -1
- package/dist/runtime/handler.mjs +1 -1
- package/dist/runtime/isr.mjs +1 -1
- package/dist/runtime/migration-handler.mjs +2 -2
- package/dist/runtime/validator.d.mts +1 -1
- package/dist/runtime/ws-server.d.mts +2 -2
- package/dist/runtime/ws.d.mts +3 -3
- package/dist/{secret-DmjBDxB1.mjs → secret-BXHx515u.mjs} +2 -2
- package/dist/{skills-ipldjlKE.mjs → skills-CbuYOthf.mjs} +1 -1
- package/package.json +13 -13
- package/skills/void/docs/guide/deployment.md +4 -6
- package/skills/void/docs/index.md +3 -3
- package/skills/void/docs/node_modules/void/AGENTS.md +1 -1
- package/skills/void/docs/node_modules/void/node_modules/@types/node/README.md +1 -1
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/@types/node/README.md +1 -1
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/AGENTS.md +15 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/README.md +208 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/build.md +21 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/fmt.md +18 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/index.md +31 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/lint.md +24 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/pack.md +17 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/run.md +249 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/staged.md +15 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/config/test.md +18 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/build.md +40 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/cache.md +119 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/check.md +44 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/ci.md +64 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/commit-hooks.md +51 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/create.md +88 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/dev.md +24 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/env.md +102 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/fmt.md +41 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/ide-integration.md +101 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/implode.md +23 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/index.md +128 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/install.md +147 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/lint.md +50 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/migrate.md +173 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/pack.md +61 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/run.md +324 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/test.md +35 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/troubleshooting.md +132 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/upgrade.md +49 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/vpx.md +66 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/guide/why.md +39 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/index.md +12 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/docs/team.md +35 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/templates/generator/README.md +35 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite-plus/templates/monorepo/README.md +29 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/AGENTS.md +15 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/README.md +208 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/build.md +21 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/fmt.md +18 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/index.md +31 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/lint.md +24 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/pack.md +17 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/run.md +249 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/staged.md +15 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/config/test.md +18 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/build.md +40 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/cache.md +119 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/check.md +44 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/ci.md +64 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/commit-hooks.md +51 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/create.md +88 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/dev.md +24 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/env.md +102 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/fmt.md +41 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/ide-integration.md +101 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/implode.md +23 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/index.md +128 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/install.md +147 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/lint.md +50 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/migrate.md +173 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/pack.md +61 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/run.md +324 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/test.md +35 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/troubleshooting.md +132 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/upgrade.md +49 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/vpx.md +66 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/guide/why.md +39 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/index.md +12 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/docs/team.md +35 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/templates/generator/README.md +35 -0
- package/skills/void/docs/node_modules/void/node_modules/vite-plus/templates/monorepo/README.md +29 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/tsdown/README.md +0 -55
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite/LICENSE.md +0 -2230
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite/README.md +0 -20
- package/skills/void/docs/node_modules/void/node_modules/tsdown/README.md +0 -55
- package/skills/void/docs/node_modules/void/node_modules/vite/LICENSE.md +0 -2230
- package/skills/void/docs/node_modules/void/node_modules/vite/README.md +0 -20
- /package/dist/{auth-BdsJ0Aff.d.mts → auth-DrfOTMmr.d.mts} +0 -0
- /package/dist/{auth-migrations-BAtAck2g.mjs → auth-migrations-BwLPwRgH.mjs} +0 -0
- /package/dist/{better-auth-shared-CdYmQGry.mjs → better-auth-shared-APuDaPqW.mjs} +0 -0
- /package/dist/{defer-DcxEsVH1.mjs → defer-2ARBu8Et.mjs} +0 -0
- /package/dist/{drizzle-NnudE_UN.mjs → drizzle-C-NRqGhx.mjs} +0 -0
- /package/dist/{env-raw-BDL4TvdN.mjs → env-raw-DtfQ9E31.mjs} +0 -0
- /package/dist/{fetch-error-BQ8sZ5Nd.mjs → fetch-error-CEr0ACTl.mjs} +0 -0
- /package/dist/{fetch-error-CVZ5CGA-.d.mts → fetch-error-DflegrF3.d.mts} +0 -0
- /package/dist/{head-P-egrtFE.d.mts → head-CZGAunBV.d.mts} +0 -0
- /package/dist/{headers-DCXc7mDs.mjs → headers-YVkHjOyq.mjs} +0 -0
- /package/dist/{preset-D4I73kT4.mjs → preset-DFvePt0l.mjs} +0 -0
- /package/dist/{project-slug-CKam8lF9.mjs → project-slug-KRvHQEQI.mjs} +0 -0
- /package/dist/{resolve-project-Br5BR03U.mjs → resolve-project-DdjLQ2tB.mjs} +0 -0
- /package/dist/{runner-pg-D0wWHYnr.mjs → runner-pg-BI6f6Ncm.mjs} +0 -0
- /package/dist/{standard-schema-9CRjx-uR.d.mts → standard-schema-BfGDWXff.d.mts} +0 -0
- /package/dist/{subcommand-prompt-BKjuNAPb.mjs → subcommand-prompt-BMS1TNG5.mjs} +0 -0
- /package/dist/{types-mHOEwpW4.d.mts → types-AdKzPp2C.d.mts} +0 -0
- /package/dist/{yarn-pnp-BFqMV_bl.mjs → yarn-pnp-6LD6_3Ej.mjs} +0 -0
|
@@ -4,7 +4,7 @@ import { n as cliTitle, r as createSpinner, s as import_picocolors } from "./out
|
|
|
4
4
|
import { t as findVoidAuthConfig } from "./config-CvHtTM0q.mjs";
|
|
5
5
|
import { c as R, g as ge, u as Se, v as ue, x as q, y as ye } from "./dist-Dayj3gCK.mjs";
|
|
6
6
|
import { a as writeProjectConfig, r as readProjectConfig } from "./project-TqORyHn8.mjs";
|
|
7
|
-
import { a as
|
|
7
|
+
import { a as isExpiredTokenError, c as getToken, i as isCliOutdatedError, l as getTokenSource, n as PlatformClient, o as parsePlatformErrorBody, t as PlatformApiError, u as isStagingMode } from "./client-DCqnMpDt.mjs";
|
|
8
8
|
import { c as getDatabaseDialect, f as readConfig, l as isNodeTarget, p as resolveBindingNames } from "./config-BIa9HwVX.mjs";
|
|
9
9
|
import { i as scanJobsSync, n as scanWebSocketRoutesSync, r as scanQueuesSync, t as scanRoutes } from "./scan-C6HMEIdW.mjs";
|
|
10
10
|
import { c as validateSsrEntry, n as detectFramework, r as inferProjectBindings, t as FRAMEWORK_SCAN_DIRS } from "./plugin-inference-oZ6Ybu2_.mjs";
|
|
@@ -16,11 +16,11 @@ import { n as writeDrizzleConfig } from "./config-BzM9Dy7T.mjs";
|
|
|
16
16
|
import { t as collectMigrations } from "./collect-CjeZgz5D.mjs";
|
|
17
17
|
import { r as validateMigrations, t as assertJournalCoherence } from "./validate-CaMavMxu.mjs";
|
|
18
18
|
import { t as scanPages } from "./scan-Ba4hFwlH.mjs";
|
|
19
|
-
import { n as promptProjectSelection, r as promptProjectSetupAction, t as promptAndCreateProject } from "./create-project-
|
|
20
|
-
import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project-
|
|
21
|
-
import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-
|
|
22
|
-
import { t as promptForLoginToken } from "./login-
|
|
23
|
-
import { i as resolveProjectCommand, n as detectPreset, r as formatProjectCommand, t as FRAMEWORK_PRESETS } from "./preset-
|
|
19
|
+
import { n as promptProjectSelection, r as promptProjectSetupAction, t as promptAndCreateProject } from "./create-project-Bg88Kq_I.mjs";
|
|
20
|
+
import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project-DdjLQ2tB.mjs";
|
|
21
|
+
import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-YVkHjOyq.mjs";
|
|
22
|
+
import { t as promptForLoginToken } from "./login-B5HHT32i.mjs";
|
|
23
|
+
import { i as resolveProjectCommand, n as detectPreset, r as formatProjectCommand, t as FRAMEWORK_PRESETS } from "./preset-DFvePt0l.mjs";
|
|
24
24
|
import { createRequire } from "node:module";
|
|
25
25
|
import { copyFileSync, cpSync, createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
26
26
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -28,6 +28,7 @@ import { build, loadEnv } from "vite";
|
|
|
28
28
|
import { homedir, tmpdir } from "node:os";
|
|
29
29
|
import { parse } from "jsonc-parser";
|
|
30
30
|
import { execFile, execFileSync, execSync } from "node:child_process";
|
|
31
|
+
import { createHash } from "node:crypto";
|
|
31
32
|
import { hash } from "blake3-jit";
|
|
32
33
|
import ignore from "ignore";
|
|
33
34
|
const SANDBOX_MIGRATION_TAG = "void-sandbox-v1";
|
|
@@ -848,6 +849,7 @@ function formatStreamDeployError(err, deploymentId, logPath) {
|
|
|
848
849
|
const parsed = parsePlatformErrorBody(err.body);
|
|
849
850
|
const parsedError = parsed?.error ?? parsed?.message ?? null;
|
|
850
851
|
const effectiveDeploymentId = parsed?.deployment ?? null ?? deploymentId;
|
|
852
|
+
if (isCliOutdatedError(err) && parsed?.message) return new Error(appendTrailingLines(parsed.message, effectiveDeploymentId, logPath));
|
|
851
853
|
if (err.status === 409 && parsedError) return new Error(appendTrailingLines(`deploy: Deploy failed: ${parsedError}`, effectiveDeploymentId, logPath));
|
|
852
854
|
const detail = parsedError ?? trimSnippet(err.body);
|
|
853
855
|
const base = detail ? `deploy: Deploy failed (HTTP ${err.status}): ${detail}` : `deploy: Deploy failed (HTTP ${err.status}).`;
|
|
@@ -856,6 +858,241 @@ function formatStreamDeployError(err, deploymentId, logPath) {
|
|
|
856
858
|
const original = err instanceof Error ? err.message : String(err);
|
|
857
859
|
return new Error(appendTrailingLines(`deploy: Connection lost during deploy: '${original}'.`, deploymentId, logPath));
|
|
858
860
|
}
|
|
861
|
+
/**
|
|
862
|
+
* Maximum retry attempts for a single R2 PUT before giving up. Matched to
|
|
863
|
+
* the design doc 0068 spec: 5xx triggers retry, 4xx surfaces immediately.
|
|
864
|
+
*/
|
|
865
|
+
const MAX_R2_PUT_RETRIES = 3;
|
|
866
|
+
/**
|
|
867
|
+
* Retry backoff delays in milliseconds. After attempt `i` fails (5xx) we
|
|
868
|
+
* sleep `RETRY_BACKOFF_MS[i]` before retrying. Indexed from 0; once the
|
|
869
|
+
* array is exhausted we surface the error.
|
|
870
|
+
*/
|
|
871
|
+
const RETRY_BACKOFF_MS = [
|
|
872
|
+
250,
|
|
873
|
+
500,
|
|
874
|
+
1e3
|
|
875
|
+
];
|
|
876
|
+
var DirectR2PutError = class extends Error {
|
|
877
|
+
status;
|
|
878
|
+
hash;
|
|
879
|
+
path;
|
|
880
|
+
/**
|
|
881
|
+
* `body` is the response body text (may be empty / non-text). Useful in
|
|
882
|
+
* the surface error so users can correlate against R2-side errors like
|
|
883
|
+
* `BadDigest` (Content-MD5 mismatch) or signature errors.
|
|
884
|
+
*/
|
|
885
|
+
body;
|
|
886
|
+
constructor(status, hash, path, body) {
|
|
887
|
+
super(`R2 PUT failed for ${path} (${hash}): ${status}${body ? ` ${body}` : ""}`);
|
|
888
|
+
this.name = "DirectR2PutError";
|
|
889
|
+
this.status = status;
|
|
890
|
+
this.hash = hash;
|
|
891
|
+
this.path = path;
|
|
892
|
+
this.body = body;
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
/**
|
|
896
|
+
* Issue direct-to-R2 PUTs for every item in `items`, bounded to
|
|
897
|
+
* `R2_PUT_CONCURRENCY`. Surfaces the first failure (4xx immediately, 5xx
|
|
898
|
+
* after retry exhaustion) as a `DirectR2PutError` carrying the asset path
|
|
899
|
+
* and hash so the user can correlate against the JSONL log.
|
|
900
|
+
*
|
|
901
|
+
* Logs `r2_put_start` / `r2_put_end` per asset, plus `r2_put_retry` on
|
|
902
|
+
* each retry attempt. Per-asset durations help identify slow uploads.
|
|
903
|
+
*
|
|
904
|
+
* Memory: each successful PUT clears `item.body` and invokes `onAfterPut`
|
|
905
|
+
* so the caller can drop its own ref (e.g. delete from the in-memory
|
|
906
|
+
* `assetFiles` cache). For huge deploys this keeps peak memory bounded by
|
|
907
|
+
* the upload concurrency rather than the total bundle size.
|
|
908
|
+
*/
|
|
909
|
+
async function uploadAssetsToR2(items, cliLog, fetchImpl = fetch, onAfterPut) {
|
|
910
|
+
if (items.length === 0) return;
|
|
911
|
+
let firstError = null;
|
|
912
|
+
let nextIndex = 0;
|
|
913
|
+
const workerCount = Math.min(10, items.length);
|
|
914
|
+
const workers = [];
|
|
915
|
+
for (let w = 0; w < workerCount; w++) workers.push((async () => {
|
|
916
|
+
while (firstError === null) {
|
|
917
|
+
const i = nextIndex++;
|
|
918
|
+
if (i >= items.length) return;
|
|
919
|
+
try {
|
|
920
|
+
await uploadOne(items[i], cliLog, fetchImpl);
|
|
921
|
+
} catch (err) {
|
|
922
|
+
if (firstError === null) firstError = err;
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
items[i].body = void 0;
|
|
926
|
+
onAfterPut?.(items[i]);
|
|
927
|
+
}
|
|
928
|
+
})());
|
|
929
|
+
await Promise.all(workers);
|
|
930
|
+
if (firstError !== null) throw firstError;
|
|
931
|
+
}
|
|
932
|
+
async function uploadOne(item, cliLog, fetchImpl) {
|
|
933
|
+
if (!item.body) throw new Error(`internal: uploadOne invoked for ${item.path} with no body`);
|
|
934
|
+
const body = item.body;
|
|
935
|
+
const startedAt = Date.now();
|
|
936
|
+
cliLog?.info("r2_put_start", {
|
|
937
|
+
hash: item.hash,
|
|
938
|
+
path: item.path,
|
|
939
|
+
sizeBytes: body.length
|
|
940
|
+
});
|
|
941
|
+
let attempt = 0;
|
|
942
|
+
while (true) {
|
|
943
|
+
let res;
|
|
944
|
+
try {
|
|
945
|
+
res = await fetchImpl(item.url, {
|
|
946
|
+
method: "PUT",
|
|
947
|
+
body: new Uint8Array(body),
|
|
948
|
+
headers: item.headers
|
|
949
|
+
});
|
|
950
|
+
} catch (err) {
|
|
951
|
+
if (attempt < MAX_R2_PUT_RETRIES) {
|
|
952
|
+
cliLog?.warn("r2_put_retry", {
|
|
953
|
+
hash: item.hash,
|
|
954
|
+
path: item.path,
|
|
955
|
+
attempt: attempt + 1,
|
|
956
|
+
status: 0,
|
|
957
|
+
error: err instanceof Error ? err.message : String(err)
|
|
958
|
+
});
|
|
959
|
+
await sleep(RETRY_BACKOFF_MS[attempt]);
|
|
960
|
+
attempt++;
|
|
961
|
+
continue;
|
|
962
|
+
}
|
|
963
|
+
cliLog?.error("r2_put_end", err, {
|
|
964
|
+
hash: item.hash,
|
|
965
|
+
path: item.path,
|
|
966
|
+
durationMs: Date.now() - startedAt,
|
|
967
|
+
status: 0
|
|
968
|
+
});
|
|
969
|
+
throw new DirectR2PutError(0, item.hash, item.path, err instanceof Error ? err.message : String(err));
|
|
970
|
+
}
|
|
971
|
+
if (res.ok) {
|
|
972
|
+
cliLog?.info("r2_put_end", {
|
|
973
|
+
hash: item.hash,
|
|
974
|
+
path: item.path,
|
|
975
|
+
durationMs: Date.now() - startedAt,
|
|
976
|
+
status: res.status
|
|
977
|
+
});
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
const errBody = await safeReadText(res);
|
|
981
|
+
if (res.status >= 500 && attempt < MAX_R2_PUT_RETRIES) {
|
|
982
|
+
cliLog?.warn("r2_put_retry", {
|
|
983
|
+
hash: item.hash,
|
|
984
|
+
path: item.path,
|
|
985
|
+
attempt: attempt + 1,
|
|
986
|
+
status: res.status,
|
|
987
|
+
body: truncate(errBody)
|
|
988
|
+
});
|
|
989
|
+
await sleep(RETRY_BACKOFF_MS[attempt]);
|
|
990
|
+
attempt++;
|
|
991
|
+
continue;
|
|
992
|
+
}
|
|
993
|
+
cliLog?.error("r2_put_end", void 0, {
|
|
994
|
+
hash: item.hash,
|
|
995
|
+
path: item.path,
|
|
996
|
+
durationMs: Date.now() - startedAt,
|
|
997
|
+
status: res.status,
|
|
998
|
+
body: truncate(errBody)
|
|
999
|
+
});
|
|
1000
|
+
throw new DirectR2PutError(res.status, item.hash, item.path, truncate(errBody));
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
async function safeReadText(res) {
|
|
1004
|
+
try {
|
|
1005
|
+
return await res.text();
|
|
1006
|
+
} catch {
|
|
1007
|
+
return "";
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
function truncate(s) {
|
|
1011
|
+
return s.length > 500 ? `${s.slice(0, 500)}...` : s;
|
|
1012
|
+
}
|
|
1013
|
+
function sleep(ms) {
|
|
1014
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1015
|
+
}
|
|
1016
|
+
//#endregion
|
|
1017
|
+
//#region src/cli/git-metadata.ts
|
|
1018
|
+
function git(root, args) {
|
|
1019
|
+
try {
|
|
1020
|
+
return execFileSync("git", args, {
|
|
1021
|
+
cwd: root,
|
|
1022
|
+
encoding: "utf-8",
|
|
1023
|
+
stdio: [
|
|
1024
|
+
"ignore",
|
|
1025
|
+
"pipe",
|
|
1026
|
+
"ignore"
|
|
1027
|
+
]
|
|
1028
|
+
}).trim();
|
|
1029
|
+
} catch {
|
|
1030
|
+
return null;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
function stripGitSuffix(pathname) {
|
|
1034
|
+
return pathname.endsWith(".git") ? pathname.slice(0, -4) : pathname;
|
|
1035
|
+
}
|
|
1036
|
+
function normalizeGitRemoteUrl(remote) {
|
|
1037
|
+
if (!remote) return null;
|
|
1038
|
+
const trimmed = remote.trim();
|
|
1039
|
+
if (!trimmed) return null;
|
|
1040
|
+
const scpLike = /^(?:[^@]+@)?([^:]+):(.+)$/.exec(trimmed);
|
|
1041
|
+
if (scpLike && !trimmed.includes("://")) {
|
|
1042
|
+
const host = scpLike[1]?.toLowerCase();
|
|
1043
|
+
const path = stripGitSuffix(scpLike[2] ?? "").replace(/^\/+/, "");
|
|
1044
|
+
return host && path ? `https://${host}/${path}` : null;
|
|
1045
|
+
}
|
|
1046
|
+
try {
|
|
1047
|
+
const url = new URL(trimmed);
|
|
1048
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" && url.protocol !== "ssh:") return null;
|
|
1049
|
+
const host = url.hostname.toLowerCase();
|
|
1050
|
+
const path = stripGitSuffix(url.pathname).replace(/^\/+/, "");
|
|
1051
|
+
return host && path ? `https://${host}/${path}` : null;
|
|
1052
|
+
} catch {
|
|
1053
|
+
return null;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
function buildCommitUrl(repositoryUrl, commit) {
|
|
1057
|
+
if (!repositoryUrl || !commit) return null;
|
|
1058
|
+
try {
|
|
1059
|
+
const url = new URL(repositoryUrl);
|
|
1060
|
+
if (url.hostname === "github.com") return `${repositoryUrl}/commit/${commit}`;
|
|
1061
|
+
if (url.hostname === "gitlab.com") return `${repositoryUrl}/-/commit/${commit}`;
|
|
1062
|
+
if (url.hostname === "bitbucket.org") return `${repositoryUrl}/commits/${commit}`;
|
|
1063
|
+
} catch {
|
|
1064
|
+
return null;
|
|
1065
|
+
}
|
|
1066
|
+
return null;
|
|
1067
|
+
}
|
|
1068
|
+
function getGitRefFromEnv(env) {
|
|
1069
|
+
if (env.GITHUB_HEAD_REF) return env.GITHUB_HEAD_REF;
|
|
1070
|
+
if (env.GITHUB_REF_NAME) return env.GITHUB_REF_NAME;
|
|
1071
|
+
if (env.GITHUB_REF) return env.GITHUB_REF.replace(/^refs\/(?:heads|tags|pull)\//, "");
|
|
1072
|
+
return null;
|
|
1073
|
+
}
|
|
1074
|
+
function getGitHubRepositoryUrl(env) {
|
|
1075
|
+
if (!env.GITHUB_REPOSITORY) return null;
|
|
1076
|
+
return normalizeGitRemoteUrl(`${env.GITHUB_SERVER_URL ?? "https://github.com"}/${env.GITHUB_REPOSITORY}`);
|
|
1077
|
+
}
|
|
1078
|
+
function collectGitDeployMetadata(root, commit, env = process.env) {
|
|
1079
|
+
const repositoryUrl = getGitHubRepositoryUrl(env) ?? normalizeGitRemoteUrl(git(root, [
|
|
1080
|
+
"remote",
|
|
1081
|
+
"get-url",
|
|
1082
|
+
"origin"
|
|
1083
|
+
]));
|
|
1084
|
+
const gitRef = getGitRefFromEnv(env) ?? git(root, [
|
|
1085
|
+
"rev-parse",
|
|
1086
|
+
"--abbrev-ref",
|
|
1087
|
+
"HEAD"
|
|
1088
|
+
]);
|
|
1089
|
+
return {
|
|
1090
|
+
commit,
|
|
1091
|
+
commitUrl: buildCommitUrl(repositoryUrl, commit),
|
|
1092
|
+
gitRef: gitRef && gitRef !== "HEAD" ? gitRef : null,
|
|
1093
|
+
repositoryUrl
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
859
1096
|
//#endregion
|
|
860
1097
|
//#region src/cli/log-file.ts
|
|
861
1098
|
/**
|
|
@@ -1110,8 +1347,88 @@ function computeAssetHash(content, filePath) {
|
|
|
1110
1347
|
return Buffer.from(hash(input)).toString("hex").slice(0, 32);
|
|
1111
1348
|
}
|
|
1112
1349
|
/**
|
|
1350
|
+
* Map of common asset file extensions → content type. Mirrors
|
|
1351
|
+
* `packages/dispatch/src/static.ts`'s MIME_TYPES table so the value the CLI
|
|
1352
|
+
* declares for the R2 PUT signature matches what dispatch will serve later
|
|
1353
|
+
* (dispatch reads `obj.httpMetadata?.contentType` first, falling back to
|
|
1354
|
+
* path-based inference). Keeping the tables aligned avoids a needless
|
|
1355
|
+
* mismatch where R2-stored content-type and serve-time content-type
|
|
1356
|
+
* disagree on the same asset.
|
|
1357
|
+
*/
|
|
1358
|
+
const ASSET_MIME_TYPES = {
|
|
1359
|
+
html: "text/html; charset=utf-8",
|
|
1360
|
+
htm: "text/html; charset=utf-8",
|
|
1361
|
+
css: "text/css; charset=utf-8",
|
|
1362
|
+
js: "text/javascript; charset=utf-8",
|
|
1363
|
+
mjs: "text/javascript; charset=utf-8",
|
|
1364
|
+
json: "application/json; charset=utf-8",
|
|
1365
|
+
xml: "application/xml; charset=utf-8",
|
|
1366
|
+
svg: "image/svg+xml; charset=utf-8",
|
|
1367
|
+
txt: "text/plain; charset=utf-8",
|
|
1368
|
+
csv: "text/csv; charset=utf-8",
|
|
1369
|
+
png: "image/png",
|
|
1370
|
+
jpg: "image/jpeg",
|
|
1371
|
+
jpeg: "image/jpeg",
|
|
1372
|
+
gif: "image/gif",
|
|
1373
|
+
ico: "image/x-icon",
|
|
1374
|
+
webp: "image/webp",
|
|
1375
|
+
avif: "image/avif",
|
|
1376
|
+
woff: "font/woff",
|
|
1377
|
+
woff2: "font/woff2",
|
|
1378
|
+
ttf: "font/ttf",
|
|
1379
|
+
otf: "font/otf",
|
|
1380
|
+
eot: "application/vnd.ms-fontobject",
|
|
1381
|
+
pdf: "application/pdf",
|
|
1382
|
+
wasm: "application/wasm",
|
|
1383
|
+
webmanifest: "application/manifest+json; charset=utf-8",
|
|
1384
|
+
map: "application/json; charset=utf-8",
|
|
1385
|
+
mp4: "video/mp4",
|
|
1386
|
+
webm: "video/webm",
|
|
1387
|
+
mp3: "audio/mpeg",
|
|
1388
|
+
ogg: "audio/ogg"
|
|
1389
|
+
};
|
|
1390
|
+
/**
|
|
1391
|
+
* Resolve the content-type to declare on a direct-to-R2 PUT for `path`.
|
|
1392
|
+
* The value is part of the signed Content-Type header on the presigned
|
|
1393
|
+
* URL — if the bytes are uploaded with a different content-type, R2
|
|
1394
|
+
* rejects the PUT (signature mismatch). Defaults to
|
|
1395
|
+
* `application/octet-stream` for unknown extensions.
|
|
1396
|
+
*/
|
|
1397
|
+
function getAssetContentType(path) {
|
|
1398
|
+
return ASSET_MIME_TYPES[path.split(".").pop()?.toLowerCase() ?? ""] ?? "application/octet-stream";
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* Compute MD5 of raw content as a 32-char lowercase hex string.
|
|
1402
|
+
*
|
|
1403
|
+
* Used as the wire-level upload-integrity primitive on direct-to-R2 PUTs:
|
|
1404
|
+
* R2's S3 compatibility layer enforces `Content-MD5` (RFC 1864) on PutObject,
|
|
1405
|
+
* so the upload itself fails if uploaded bytes don't match the declared MD5.
|
|
1406
|
+
* MD5 collision resistance is acceptable here because (a) this hash is purely
|
|
1407
|
+
* an upload-integrity primitive (BLAKE3 remains the canonical content key),
|
|
1408
|
+
* and (b) project-scoped storage (`assets/${projectId}/${blake3}`) means any
|
|
1409
|
+
* collision attack is exploitable only against the attacker's own project.
|
|
1410
|
+
*/
|
|
1411
|
+
function computeMd5(content) {
|
|
1412
|
+
return createHash("md5").update(content).digest("hex");
|
|
1413
|
+
}
|
|
1414
|
+
/**
|
|
1415
|
+
* Compute both BLAKE3 (CF-compatible asset hash) and MD5 (wire-level R2
|
|
1416
|
+
* upload-integrity checksum) from a single in-memory buffer. Returned in
|
|
1417
|
+
* the v2 manifest entry shape for direct-to-R2 deploys.
|
|
1418
|
+
*
|
|
1419
|
+
* Single-pass: callers read the file once into `content`; this function
|
|
1420
|
+
* then derives both digests from that buffer. The file is never read twice.
|
|
1421
|
+
*/
|
|
1422
|
+
function computeAssetHashes(content, filePath) {
|
|
1423
|
+
return {
|
|
1424
|
+
blake3: computeAssetHash(content, filePath),
|
|
1425
|
+
md5: computeMd5(content)
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
/**
|
|
1113
1429
|
* Create a filter function that excludes .assetsignore, _headers, _redirects,
|
|
1114
|
-
* deploy-internal patterns, and any patterns listed in a
|
|
1430
|
+
* OS metadata files, deploy-internal patterns, and any patterns listed in a
|
|
1431
|
+
* .assetsignore file.
|
|
1115
1432
|
* Matches Wrangler's createAssetsIgnoreFunction from workers-shared/utils/helpers.ts.
|
|
1116
1433
|
*/
|
|
1117
1434
|
function createAssetsIgnoreFunction(dir, extraPatterns = []) {
|
|
@@ -1119,6 +1436,7 @@ function createAssetsIgnoreFunction(dir, extraPatterns = []) {
|
|
|
1119
1436
|
"/.assetsignore",
|
|
1120
1437
|
"/_redirects",
|
|
1121
1438
|
"/_headers",
|
|
1439
|
+
".DS_Store",
|
|
1122
1440
|
...extraPatterns
|
|
1123
1441
|
];
|
|
1124
1442
|
const assetsIgnorePath = join(dir, ".assetsignore");
|
|
@@ -1152,6 +1470,9 @@ function checkWorkerJsAsset(relativePath, assetsIgnoreFilePresent) {
|
|
|
1152
1470
|
/**
|
|
1153
1471
|
* Collect all files in a directory, compute hashes, and return both the
|
|
1154
1472
|
* manifest (for preflight) and the raw file map (for selective packaging).
|
|
1473
|
+
*
|
|
1474
|
+
* Each file is read from disk exactly once. Both BLAKE3 (storage key) and
|
|
1475
|
+
* MD5 (R2 upload-integrity) digests are derived from that single buffer.
|
|
1155
1476
|
*/
|
|
1156
1477
|
async function collectAndHashAssets(dir, onProgress, opts = {}) {
|
|
1157
1478
|
const assetManifest = {};
|
|
@@ -1167,9 +1488,12 @@ async function collectAndHashAssets(dir, onProgress, opts = {}) {
|
|
|
1167
1488
|
if (isIgnored(relativePath)) continue;
|
|
1168
1489
|
checkWorkerJsAsset(relativePath, assetsIgnoreFilePresent);
|
|
1169
1490
|
const content = readFileSync(filePath);
|
|
1491
|
+
const { blake3, md5 } = computeAssetHashes(content, filePath);
|
|
1170
1492
|
assetManifest[relativePath] = {
|
|
1171
|
-
|
|
1172
|
-
|
|
1493
|
+
blake3,
|
|
1494
|
+
md5,
|
|
1495
|
+
size: content.length,
|
|
1496
|
+
hash: blake3
|
|
1173
1497
|
};
|
|
1174
1498
|
assetFiles.set(relativePath, content);
|
|
1175
1499
|
}
|
|
@@ -1204,10 +1528,32 @@ function toSandboxManifest(sandbox) {
|
|
|
1204
1528
|
...sandbox.maxInstances != null && { maxInstances: sandbox.maxInstances }
|
|
1205
1529
|
};
|
|
1206
1530
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1531
|
+
/**
|
|
1532
|
+
* If the user configured `routing.fallbacks` on a non-SPA app, the platform
|
|
1533
|
+
* still applies the rules but no `/* -> /index.html` SPA shell is synthesised
|
|
1534
|
+
* — unmatched paths fall through to the platform 404. Returns the warning
|
|
1535
|
+
* message string when that mismatch is detected, or `null` otherwise. Pure
|
|
1536
|
+
* function; callers decide whether/how to surface the message (CLI progress,
|
|
1537
|
+
* `console.warn`, etc.).
|
|
1538
|
+
*/
|
|
1539
|
+
function detectNonSpaFallbackWarning(assetConfig, fallbackRules) {
|
|
1540
|
+
if (assetConfig?.not_found_handling === "single-page-application") return null;
|
|
1541
|
+
if (!fallbackRules || fallbackRules.length === 0) return null;
|
|
1542
|
+
return "routing.fallbacks is set but appType is not \"spa\"; fallbacks will be applied, but no \"/* -> /index.html\" SPA shell fallback is synthesised — unmatched paths fall through to the platform 404.";
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Build a Void-app deploy manifest from the inferred bindings + per-feature
|
|
1546
|
+
* inputs. Pure shape transformation: reads no files, has no I/O.
|
|
1547
|
+
*
|
|
1548
|
+
* Note: callers are responsible for surfacing the non-SPA-fallbacks warning
|
|
1549
|
+
* (see `detectNonSpaFallbackWarning`) before invoking this builder. The
|
|
1550
|
+
* builder itself stays I/O-free.
|
|
1551
|
+
*/
|
|
1552
|
+
function buildVoidManifest(opts) {
|
|
1553
|
+
const { bindings, migrations, schedules, ssr, framework, revalidate, vars, queues, prerender, assetConfig, options, hashedAssetsPrefix, headerRules, redirectRules, fallbackRules, dialect, webSockets } = opts;
|
|
1209
1554
|
const manifest = {
|
|
1210
1555
|
version: 3,
|
|
1556
|
+
assetManifestVersion: 2,
|
|
1211
1557
|
bindings: {}
|
|
1212
1558
|
};
|
|
1213
1559
|
if (bindings.needsAuth) manifest.auth = true;
|
|
@@ -1224,14 +1570,10 @@ async function packageBuild(distDir, workerDirName, bindings, migrations, schedu
|
|
|
1224
1570
|
binding: options.bindingNames?.sandbox ?? options.sandbox.binding
|
|
1225
1571
|
});
|
|
1226
1572
|
}
|
|
1227
|
-
if (migrations && migrations.length > 0) {
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
};
|
|
1232
|
-
onProgress?.(`Packaging ${migrations.length} migration${migrations.length === 1 ? "" : "s"}...`);
|
|
1233
|
-
for (const m of migrations) formData.append(`migration:${m.name}`, new Blob([m.sql]), m.name);
|
|
1234
|
-
}
|
|
1573
|
+
if (migrations && migrations.length > 0) manifest.migrations = {
|
|
1574
|
+
dialect: dialect ?? "sqlite",
|
|
1575
|
+
pending: migrations.length
|
|
1576
|
+
};
|
|
1235
1577
|
if (schedules && schedules.length > 0) manifest.schedules = schedules;
|
|
1236
1578
|
if (queues && queues.length > 0) manifest.queues = queues;
|
|
1237
1579
|
if (ssr) manifest.ssr = true;
|
|
@@ -1249,10 +1591,6 @@ async function packageBuild(distDir, workerDirName, bindings, migrations, schedu
|
|
|
1249
1591
|
...chain.assetConfig
|
|
1250
1592
|
};
|
|
1251
1593
|
resolvedFallbackRules = chain.fallbackRules;
|
|
1252
|
-
} else if (fallbackRules && fallbackRules.length > 0) {
|
|
1253
|
-
const msg = "routing.fallbacks is set but appType is not \"spa\"; fallbacks will be applied, but no \"/* -> /index.html\" SPA shell fallback is synthesised — unmatched paths fall through to the platform 404.";
|
|
1254
|
-
onProgress?.(msg);
|
|
1255
|
-
console.warn(`[void] ${msg}`);
|
|
1256
1594
|
}
|
|
1257
1595
|
if (resolvedAssetConfig) manifest.assetConfig = resolvedAssetConfig;
|
|
1258
1596
|
if (vars && Object.keys(vars).length > 0) manifest.vars = vars;
|
|
@@ -1265,45 +1603,40 @@ async function packageBuild(distDir, workerDirName, bindings, migrations, schedu
|
|
|
1265
1603
|
if (resolvedFallbackRules && resolvedFallbackRules.length > 0) manifest.fallbackRules = stripDevOnlyRuleFields(resolvedFallbackRules);
|
|
1266
1604
|
const websocketManifest = toWebSocketManifest(webSockets);
|
|
1267
1605
|
if (websocketManifest) manifest.websocket = websocketManifest;
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
const
|
|
1606
|
+
return manifest;
|
|
1607
|
+
}
|
|
1608
|
+
/**
|
|
1609
|
+
* Read every worker module under `workerDir` once and return them as a map
|
|
1610
|
+
* keyed by relative path. Skipped files (build metadata, wrangler config,
|
|
1611
|
+
* non-module extensions) are filtered out by `isWorkerModule`. Reads each
|
|
1612
|
+
* file from disk exactly once — buffer threading downstream is the caller's
|
|
1613
|
+
* responsibility.
|
|
1614
|
+
*/
|
|
1615
|
+
function collectWorkerFiles(workerDir) {
|
|
1616
|
+
const out = /* @__PURE__ */ new Map();
|
|
1617
|
+
let workerPaths;
|
|
1279
1618
|
try {
|
|
1280
|
-
|
|
1281
|
-
const clientFiles = collectFiles(clientDir);
|
|
1282
|
-
let processed = 0;
|
|
1283
|
-
for (const filePath of clientFiles) {
|
|
1284
|
-
processed++;
|
|
1285
|
-
await tickProgress(processed, clientFiles.length, onProgress, "Packaging assets");
|
|
1286
|
-
const relativePath = toSlash(relative(clientDir, filePath));
|
|
1287
|
-
if (isIgnored("/" + relativePath)) continue;
|
|
1288
|
-
checkWorkerJsAsset(relativePath, assetsIgnoreFilePresent);
|
|
1289
|
-
if (neededAssets && !neededAssets.has("/" + relativePath)) continue;
|
|
1290
|
-
const content = readFileSync(filePath);
|
|
1291
|
-
formData.append(`asset:${relativePath}`, new Blob([content]), relativePath);
|
|
1292
|
-
}
|
|
1619
|
+
workerPaths = collectFiles(workerDir);
|
|
1293
1620
|
} catch (err) {
|
|
1294
|
-
if (err instanceof Error && "code" in err && err.code === "ENOENT")
|
|
1621
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") return out;
|
|
1622
|
+
throw err;
|
|
1623
|
+
}
|
|
1624
|
+
for (const filePath of workerPaths) {
|
|
1625
|
+
const relativePath = toSlash(relative(workerDir, filePath));
|
|
1626
|
+
if (!isWorkerModule(relativePath)) continue;
|
|
1627
|
+
out.set(relativePath, readFileSync(filePath));
|
|
1295
1628
|
}
|
|
1296
|
-
|
|
1297
|
-
return formData;
|
|
1629
|
+
return out;
|
|
1298
1630
|
}
|
|
1299
1631
|
/**
|
|
1300
|
-
*
|
|
1632
|
+
* Build a static-deploy manifest. Pure shape transformation — no I/O.
|
|
1301
1633
|
*/
|
|
1302
|
-
|
|
1303
|
-
const
|
|
1634
|
+
function buildStaticManifest(opts) {
|
|
1635
|
+
const { appType, hashedAssetsPrefix, headerRules, redirectRules, fallbackRules } = opts;
|
|
1304
1636
|
const { assetConfig, fallbackRules: resolvedFallbackRules } = resolveSpaFallbackChain(appType, fallbackRules);
|
|
1305
1637
|
const manifest = {
|
|
1306
1638
|
version: 3,
|
|
1639
|
+
assetManifestVersion: 2,
|
|
1307
1640
|
bindings: {},
|
|
1308
1641
|
type: appType,
|
|
1309
1642
|
assetConfig
|
|
@@ -1312,36 +1645,16 @@ async function packageStaticBuild(outputDir, appType, neededAssets, hashedAssets
|
|
|
1312
1645
|
if (headerRules && headerRules.length > 0) manifest.headerRules = headerRules;
|
|
1313
1646
|
if (redirectRules && redirectRules.length > 0) manifest.redirectRules = stripDevOnlyRuleFields(redirectRules);
|
|
1314
1647
|
if (resolvedFallbackRules && resolvedFallbackRules.length > 0) manifest.fallbackRules = stripDevOnlyRuleFields(resolvedFallbackRules);
|
|
1315
|
-
|
|
1316
|
-
const { isIgnored, assetsIgnoreFilePresent } = createAssetsIgnoreFunction(outputDir);
|
|
1317
|
-
const files = collectFiles(outputDir);
|
|
1318
|
-
let processed = 0;
|
|
1319
|
-
for (const filePath of files) {
|
|
1320
|
-
processed++;
|
|
1321
|
-
await tickProgress(processed, files.length, onProgress, "Packaging assets");
|
|
1322
|
-
const relativePath = toSlash(relative(outputDir, filePath));
|
|
1323
|
-
if (isIgnored("/" + relativePath)) continue;
|
|
1324
|
-
checkWorkerJsAsset(relativePath, assetsIgnoreFilePresent);
|
|
1325
|
-
if (neededAssets && !neededAssets.has("/" + relativePath)) continue;
|
|
1326
|
-
const content = readFileSync(filePath);
|
|
1327
|
-
formData.append(`asset:${relativePath}`, new Blob([content]), relativePath);
|
|
1328
|
-
}
|
|
1329
|
-
if (neededAssets) formData.append("preflight", "true");
|
|
1330
|
-
return formData;
|
|
1648
|
+
return manifest;
|
|
1331
1649
|
}
|
|
1332
1650
|
/**
|
|
1333
|
-
*
|
|
1334
|
-
*
|
|
1335
|
-
* Unlike `packageBuild`, the worker and assets directories are absolute paths
|
|
1336
|
-
* (resolved from the project root + preset), and the worker directory may be
|
|
1337
|
-
* inside the assets directory (SvelteKit, Astro). Worker files are excluded
|
|
1338
|
-
* from asset collection when directories overlap.
|
|
1651
|
+
* Build a framework-deploy manifest. Pure shape transformation — no I/O.
|
|
1339
1652
|
*/
|
|
1340
|
-
|
|
1341
|
-
const formData = new FormData();
|
|
1653
|
+
function buildFrameworkManifest(opts) {
|
|
1342
1654
|
const po = opts.packageOptions;
|
|
1343
1655
|
const manifest = {
|
|
1344
1656
|
version: 3,
|
|
1657
|
+
assetManifestVersion: 2,
|
|
1345
1658
|
bindings: {}
|
|
1346
1659
|
};
|
|
1347
1660
|
const hasMigrations = opts.migrations && opts.migrations.length > 0;
|
|
@@ -1350,14 +1663,10 @@ async function packageFrameworkBuild(opts) {
|
|
|
1350
1663
|
if (opts.bindings.needsKV) manifest.bindings.kv = [po?.bindingNames?.kv ?? "KV"];
|
|
1351
1664
|
if (opts.bindings.needsR2) manifest.bindings.r2 = [po?.bindingNames?.r2 ?? "STORAGE"];
|
|
1352
1665
|
if (opts.bindings.needsAI) manifest.bindings.ai = true;
|
|
1353
|
-
if (opts.migrations && opts.migrations.length > 0) {
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
};
|
|
1358
|
-
opts.onProgress?.(`Packaging ${opts.migrations.length} migration${opts.migrations.length === 1 ? "" : "s"}...`);
|
|
1359
|
-
for (const m of opts.migrations) formData.append(`migration:${m.name}`, new Blob([m.sql]), m.name);
|
|
1360
|
-
}
|
|
1666
|
+
if (opts.migrations && opts.migrations.length > 0) manifest.migrations = {
|
|
1667
|
+
dialect: opts.dialect ?? "sqlite",
|
|
1668
|
+
pending: opts.migrations.length
|
|
1669
|
+
};
|
|
1361
1670
|
if (opts.schedules && opts.schedules.length > 0) manifest.schedules = opts.schedules;
|
|
1362
1671
|
if (opts.queues && opts.queues.length > 0) manifest.queues = opts.queues;
|
|
1363
1672
|
if (opts.revalidate != null && opts.revalidate !== 0) manifest.revalidate = opts.revalidate;
|
|
@@ -1377,38 +1686,7 @@ async function packageFrameworkBuild(opts) {
|
|
|
1377
1686
|
if (opts.fallbackRules && opts.fallbackRules.length > 0) manifest.fallbackRules = stripDevOnlyRuleFields(opts.fallbackRules);
|
|
1378
1687
|
const websocketManifest = toWebSocketManifest(opts.webSockets);
|
|
1379
1688
|
if (websocketManifest) manifest.websocket = websocketManifest;
|
|
1380
|
-
|
|
1381
|
-
const workerDirNorm = opts.workerDir.replace(/\\/g, "/");
|
|
1382
|
-
const assetsDirNorm = opts.assetsDir.replace(/\\/g, "/");
|
|
1383
|
-
const isOverlap = workerDirNorm.startsWith(assetsDirNorm + "/") || workerDirNorm === assetsDirNorm;
|
|
1384
|
-
opts.onProgress?.("Packaging worker files...");
|
|
1385
|
-
const workerFiles = collectFiles(opts.workerDir);
|
|
1386
|
-
for (const filePath of workerFiles) {
|
|
1387
|
-
const relativePath = toSlash(relative(opts.workerDir, filePath));
|
|
1388
|
-
if (!isWorkerModule(relativePath)) continue;
|
|
1389
|
-
const content = readFileSync(filePath);
|
|
1390
|
-
formData.append(`worker:${relativePath}`, new Blob([content]), relativePath);
|
|
1391
|
-
}
|
|
1392
|
-
try {
|
|
1393
|
-
const { isIgnored, assetsIgnoreFilePresent } = createAssetsIgnoreFunction(opts.assetsDir, opts.assetIgnorePatterns);
|
|
1394
|
-
const assetFiles = collectFiles(opts.assetsDir);
|
|
1395
|
-
let processed = 0;
|
|
1396
|
-
for (const filePath of assetFiles) {
|
|
1397
|
-
processed++;
|
|
1398
|
-
await tickProgress(processed, assetFiles.length, opts.onProgress, "Packaging assets");
|
|
1399
|
-
if (isOverlap && filePath.replace(/\\/g, "/").startsWith(workerDirNorm)) continue;
|
|
1400
|
-
const relativePath = toSlash(relative(opts.assetsDir, filePath));
|
|
1401
|
-
if (isIgnored("/" + relativePath)) continue;
|
|
1402
|
-
checkWorkerJsAsset(relativePath, assetsIgnoreFilePresent);
|
|
1403
|
-
if (opts.neededAssets && !opts.neededAssets.has("/" + relativePath)) continue;
|
|
1404
|
-
const content = readFileSync(filePath);
|
|
1405
|
-
formData.append(`asset:${relativePath}`, new Blob([content]), relativePath);
|
|
1406
|
-
}
|
|
1407
|
-
} catch (err) {
|
|
1408
|
-
if (err instanceof Error && "code" in err && err.code === "ENOENT") {} else throw err;
|
|
1409
|
-
}
|
|
1410
|
-
if (opts.neededAssets) formData.append("preflight", "true");
|
|
1411
|
-
return formData;
|
|
1689
|
+
return manifest;
|
|
1412
1690
|
}
|
|
1413
1691
|
/**
|
|
1414
1692
|
* Check if a file (by relative path) should be included as a worker module.
|
|
@@ -1688,6 +1966,7 @@ var deploy_exports = /* @__PURE__ */ __exportAll({
|
|
|
1688
1966
|
resolveStaticBuildCommand: () => resolveStaticBuildCommand,
|
|
1689
1967
|
resolveWorkerDirName: () => resolveWorkerDirName,
|
|
1690
1968
|
runDeploy: () => runDeploy,
|
|
1969
|
+
runStaticDeploy: () => runStaticDeploy,
|
|
1691
1970
|
warnRedundantForceOn3xx: () => warnRedundantForceOn3xx
|
|
1692
1971
|
});
|
|
1693
1972
|
const drizzleKitBin = join(fileURLToPath(import.meta.resolve("drizzle-kit")), "..", "bin.cjs");
|
|
@@ -1801,15 +2080,15 @@ async function runDeployInner(root, options, cliLog) {
|
|
|
1801
2080
|
name: detected.name,
|
|
1802
2081
|
class: detected.class
|
|
1803
2082
|
});
|
|
1804
|
-
const
|
|
2083
|
+
const gitMetadata = collectGitDeployMetadata(root, options?.skipBuild ? null : getGitCommit(root));
|
|
1805
2084
|
const fwPreset = FRAMEWORK_PRESETS[detected.name];
|
|
1806
2085
|
if (detected.class === "b" || detected.class === "c") {
|
|
1807
2086
|
if (!fwPreset) {
|
|
1808
2087
|
R.error(`No deploy preset found for framework "${detected.name}".`);
|
|
1809
2088
|
process.exit(1);
|
|
1810
2089
|
}
|
|
1811
|
-
await runFrameworkDeploy(root, config, client, detected, fwPreset, voidConfig, options?.skipBuild,
|
|
1812
|
-
} else await runFullDeploy(root, config, client, fwPreset, options?.skipBuild,
|
|
2090
|
+
await runFrameworkDeploy(root, config, client, detected, fwPreset, voidConfig, options?.skipBuild, gitMetadata, cliLog);
|
|
2091
|
+
} else await runFullDeploy(root, config, client, fwPreset, options?.skipBuild, gitMetadata, detected, cliLog);
|
|
1813
2092
|
return;
|
|
1814
2093
|
}
|
|
1815
2094
|
let preset = null;
|
|
@@ -1833,14 +2112,14 @@ async function runDeployInner(root, options, cliLog) {
|
|
|
1833
2112
|
dir: options?.dir,
|
|
1834
2113
|
spa: options?.spa
|
|
1835
2114
|
});
|
|
1836
|
-
const
|
|
2115
|
+
const gitMetadata = collectGitDeployMetadata(root, options?.skipBuild ? null : getGitCommit(root));
|
|
1837
2116
|
if (preset) {
|
|
1838
2117
|
preset = {
|
|
1839
2118
|
...preset,
|
|
1840
2119
|
buildCommand: resolveStaticBuildCommand(preset, voidConfig.inference?.build, Boolean(options?.dir))
|
|
1841
2120
|
};
|
|
1842
|
-
await runStaticDeploy(root, config, client, preset, options?.skipBuild,
|
|
1843
|
-
} else await runFullDeploy(root, config, client, void 0, options?.skipBuild,
|
|
2121
|
+
await runStaticDeploy(root, config, client, preset, options?.skipBuild, gitMetadata, void 0, void 0, void 0, void 0, voidConfig.routing, cliLog);
|
|
2122
|
+
} else await runFullDeploy(root, config, client, void 0, options?.skipBuild, gitMetadata, void 0, cliLog);
|
|
1844
2123
|
return;
|
|
1845
2124
|
} catch (error) {
|
|
1846
2125
|
if (isExpiredTokenError(error)) {
|
|
@@ -1931,7 +2210,12 @@ function applyDeployEvent(event, s) {
|
|
|
1931
2210
|
}
|
|
1932
2211
|
return { kind: "continue" };
|
|
1933
2212
|
}
|
|
1934
|
-
|
|
2213
|
+
/**
|
|
2214
|
+
* Apply a streamed `DeployEvent` iterator to the spinner / cliLog and
|
|
2215
|
+
* resolve with the terminal `done` event (or throw on terminal error /
|
|
2216
|
+
* connection loss).
|
|
2217
|
+
*/
|
|
2218
|
+
async function streamDeployEvents(projectId, events, s, cliLog) {
|
|
1935
2219
|
const startedAt = Date.now();
|
|
1936
2220
|
let deploymentId = null;
|
|
1937
2221
|
let interrupted = false;
|
|
@@ -1947,7 +2231,7 @@ async function streamDeploy(client, projectId, formData, s, cliLog) {
|
|
|
1947
2231
|
};
|
|
1948
2232
|
process.once("SIGINT", onSigint);
|
|
1949
2233
|
try {
|
|
1950
|
-
for await (const event of
|
|
2234
|
+
for await (const event of events) {
|
|
1951
2235
|
const result = applyDeployEvent(event, s);
|
|
1952
2236
|
if (result.kind === "start") {
|
|
1953
2237
|
deploymentId = result.deploymentId;
|
|
@@ -1982,11 +2266,110 @@ async function streamDeploy(client, projectId, formData, s, cliLog) {
|
|
|
1982
2266
|
s.stop("Deploy failed");
|
|
1983
2267
|
throw new Error(formatConnectionLostMessage(deploymentId, cliLog?.path ?? null));
|
|
1984
2268
|
}
|
|
2269
|
+
/**
|
|
2270
|
+
* Direct-to-R2 deploy orchestration. Issues presigned PUT URLs for each
|
|
2271
|
+
* needed asset, uploads bytes directly to R2, then calls the manifest-only
|
|
2272
|
+
* deploy endpoint to finalize.
|
|
2273
|
+
*
|
|
2274
|
+
* Key invariants:
|
|
2275
|
+
* 1. No needed assets → skip presign + R2 PUT phase entirely (saves a
|
|
2276
|
+
* round-trip when every asset was already present from a prior deploy).
|
|
2277
|
+
* 2. Asset bytes come exclusively from the in-memory `assetFiles` cache
|
|
2278
|
+
* computed by `collectAndHashAssets` — files are NEVER re-read from
|
|
2279
|
+
* disk during this flow (Codex Finding #2 in design doc 0068 review).
|
|
2280
|
+
* 3. R2 PUT failures (4xx, 5xx, network) are surfaced with the asset
|
|
2281
|
+
* path + hash so the user can correlate against the JSONL trace.
|
|
2282
|
+
*/
|
|
2283
|
+
async function streamDirectR2Deploy(client, projectId, manifest, assetManifest, assetFiles, needed, gitMetadata, preflightUsed, workerFiles, s, cliLog) {
|
|
2284
|
+
if (needed.length > 0) {
|
|
2285
|
+
const neededPaths = new Set(needed);
|
|
2286
|
+
const preBufferCount = assetFiles.size;
|
|
2287
|
+
const allPaths = Array.from(assetFiles.keys());
|
|
2288
|
+
for (const path of allPaths) if (!neededPaths.has(path)) assetFiles.delete(path);
|
|
2289
|
+
if (assetFiles.size !== preBufferCount) cliLog?.info("asset_cache_pruned", {
|
|
2290
|
+
before: preBufferCount,
|
|
2291
|
+
after: assetFiles.size
|
|
2292
|
+
});
|
|
2293
|
+
s.message(`Requesting upload URLs for ${needed.length} asset(s)...`);
|
|
2294
|
+
const seenBlake3 = /* @__PURE__ */ new Set();
|
|
2295
|
+
const uniqueNeeded = [];
|
|
2296
|
+
for (const path of needed) {
|
|
2297
|
+
const entry = assetManifest[path];
|
|
2298
|
+
if (!entry) throw new Error(`internal: preflight returned ${path} but assetManifest has no entry`);
|
|
2299
|
+
if (seenBlake3.has(entry.blake3)) continue;
|
|
2300
|
+
seenBlake3.add(entry.blake3);
|
|
2301
|
+
uniqueNeeded.push(path);
|
|
2302
|
+
}
|
|
2303
|
+
const assetsToUpload = uniqueNeeded.map((path) => {
|
|
2304
|
+
const entry = assetManifest[path];
|
|
2305
|
+
return {
|
|
2306
|
+
blake3: entry.blake3,
|
|
2307
|
+
md5: entry.md5,
|
|
2308
|
+
size: entry.size,
|
|
2309
|
+
contentType: getAssetContentType(path)
|
|
2310
|
+
};
|
|
2311
|
+
});
|
|
2312
|
+
cliLog?.info("presign_request_start", {
|
|
2313
|
+
assetCount: assetsToUpload.length,
|
|
2314
|
+
dedupedFrom: needed.length
|
|
2315
|
+
});
|
|
2316
|
+
const presignStartedAt = Date.now();
|
|
2317
|
+
const urls = await client.requestUploadUrls(projectId, assetsToUpload);
|
|
2318
|
+
cliLog?.info("presign_request_end", {
|
|
2319
|
+
urlsReceived: Object.keys(urls).length,
|
|
2320
|
+
durationMs: Date.now() - presignStartedAt
|
|
2321
|
+
});
|
|
2322
|
+
const items = uniqueNeeded.map((path) => {
|
|
2323
|
+
const entry = assetManifest[path];
|
|
2324
|
+
const presigned = urls[entry.blake3];
|
|
2325
|
+
if (!presigned) throw new Error(`Platform did not return an upload URL for ${path} (blake3=${entry.blake3}). This indicates a server-side bug or a stale client.`);
|
|
2326
|
+
const body = assetFiles.get(path);
|
|
2327
|
+
if (!body) throw new Error(`internal: asset ${path} declared in manifest but missing from buffer cache`);
|
|
2328
|
+
return {
|
|
2329
|
+
path,
|
|
2330
|
+
hash: entry.blake3,
|
|
2331
|
+
body,
|
|
2332
|
+
url: presigned.url,
|
|
2333
|
+
headers: presigned.headers
|
|
2334
|
+
};
|
|
2335
|
+
});
|
|
2336
|
+
s.message(`Uploading ${items.length} asset(s) to R2...`);
|
|
2337
|
+
await uploadAssetsToR2(items, cliLog, void 0, (item) => {
|
|
2338
|
+
assetFiles.delete(item.path);
|
|
2339
|
+
});
|
|
2340
|
+
} else cliLog?.info("presign_skipped", { reason: "no_needed_assets" });
|
|
2341
|
+
const payload = {
|
|
2342
|
+
manifest,
|
|
2343
|
+
assetManifest,
|
|
2344
|
+
source: getTokenSource(),
|
|
2345
|
+
commit: gitMetadata?.commit ?? null,
|
|
2346
|
+
repositoryUrl: gitMetadata?.repositoryUrl ?? null,
|
|
2347
|
+
commitUrl: gitMetadata?.commitUrl ?? null,
|
|
2348
|
+
gitRef: gitMetadata?.gitRef ?? null,
|
|
2349
|
+
preflight: preflightUsed ? "true" : null
|
|
2350
|
+
};
|
|
2351
|
+
if (workerFiles && workerFiles.size > 0) {
|
|
2352
|
+
const encoded = {};
|
|
2353
|
+
for (const [relPath, buf] of workerFiles) encoded[relPath] = buf.toString("base64");
|
|
2354
|
+
payload.workerFiles = encoded;
|
|
2355
|
+
}
|
|
2356
|
+
s.message("Finalizing deploy...");
|
|
2357
|
+
cliLog?.info("finalize_start", {
|
|
2358
|
+
workerFileCount: workerFiles?.size ?? 0,
|
|
2359
|
+
assetCount: Object.keys(assetManifest).length
|
|
2360
|
+
});
|
|
2361
|
+
const finalizeStartedAt = Date.now();
|
|
2362
|
+
try {
|
|
2363
|
+
return await streamDeployEvents(projectId, client.finalizeDeploy(projectId, payload), s, cliLog);
|
|
2364
|
+
} finally {
|
|
2365
|
+
cliLog?.info("finalize_end", { durationMs: Date.now() - finalizeStartedAt });
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
1985
2368
|
function formatKnownAssetSummary(result, skipped) {
|
|
1986
2369
|
const uploaded = result.assets - skipped;
|
|
1987
2370
|
return skipped > 0 ? `${result.assets} static asset(s) (${skipped} unchanged, ${uploaded} uploaded)` : `${result.assets} static asset(s)`;
|
|
1988
2371
|
}
|
|
1989
|
-
async function runStaticDeploy(root, config, client, preset, skipBuild,
|
|
2372
|
+
async function runStaticDeploy(root, config, client, preset, skipBuild, gitMetadata, hashedAssetsPrefix, headerRules, redirectRules, fallbackRules, routing, cliLog) {
|
|
1990
2373
|
const typeLabel = preset.appType === "spa" ? "Static SPA" : "Static Site";
|
|
1991
2374
|
R.info(`${typeLabel} deploy`);
|
|
1992
2375
|
cliLog?.info("deploy_mode", {
|
|
@@ -2025,7 +2408,7 @@ async function runStaticDeploy(root, config, client, preset, skipBuild, commit,
|
|
|
2025
2408
|
const onProgress = (msg) => s.message(msg);
|
|
2026
2409
|
s.start("Checking for changes...");
|
|
2027
2410
|
cliLog?.info("preflight_start", { dir: preset.outputDir });
|
|
2028
|
-
const { assetManifest } = await collectAndHashAssets(preset.outputDir, onProgress);
|
|
2411
|
+
const { assetManifest, assetFiles } = await collectAndHashAssets(preset.outputDir, onProgress);
|
|
2029
2412
|
const { needed, skipped } = await client.preflight(config.projectId, assetManifest);
|
|
2030
2413
|
s.stop("Checked for changes");
|
|
2031
2414
|
cliLog?.info("preflight_end", {
|
|
@@ -2038,15 +2421,18 @@ async function runStaticDeploy(root, config, client, preset, skipBuild, commit,
|
|
|
2038
2421
|
cliLog?.info("package_start", { mode: "static" });
|
|
2039
2422
|
const packageStartedAt = Date.now();
|
|
2040
2423
|
const neededSet = skipped > 0 ? new Set(needed) : null;
|
|
2041
|
-
const
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2424
|
+
const manifest = buildStaticManifest({
|
|
2425
|
+
appType: preset.appType,
|
|
2426
|
+
hashedAssetsPrefix: hashedAssetsPrefix ?? "assets",
|
|
2427
|
+
headerRules,
|
|
2428
|
+
redirectRules,
|
|
2429
|
+
fallbackRules
|
|
2430
|
+
});
|
|
2045
2431
|
cliLog?.info("package_end", {
|
|
2046
2432
|
mode: "static",
|
|
2047
2433
|
durationMs: Date.now() - packageStartedAt
|
|
2048
2434
|
});
|
|
2049
|
-
const result = await
|
|
2435
|
+
const result = await streamDirectR2Deploy(client, config.projectId, manifest, assetManifest, assetFiles, needed, gitMetadata, neededSet !== null, void 0, s, cliLog);
|
|
2050
2436
|
Se([`${typeLabel} — ${formatKnownAssetSummary(result, skipped)}`].join("\n"), result.url);
|
|
2051
2437
|
ye("Done!");
|
|
2052
2438
|
}
|
|
@@ -2064,7 +2450,7 @@ function warnRedundantForceOn3xx(count) {
|
|
|
2064
2450
|
/**
|
|
2065
2451
|
* Class B/C framework deploy: build with framework CLI, package with preset output paths.
|
|
2066
2452
|
*/
|
|
2067
|
-
async function runFrameworkDeploy(root, config, client, detected, fwPreset, voidConfig, skipBuild,
|
|
2453
|
+
async function runFrameworkDeploy(root, config, client, detected, fwPreset, voidConfig, skipBuild, gitMetadata, cliLog) {
|
|
2068
2454
|
R.info(`${detected.name} framework deploy`);
|
|
2069
2455
|
cliLog?.info("deploy_mode", {
|
|
2070
2456
|
mode: "framework",
|
|
@@ -2182,7 +2568,7 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
|
|
|
2182
2568
|
const onProgress = (msg) => s.message(msg);
|
|
2183
2569
|
s.start("Checking for changes...");
|
|
2184
2570
|
cliLog?.info("preflight_start", { dir: assetsDir });
|
|
2185
|
-
const { assetManifest } = await collectAndHashAssets(assetsDir, onProgress, { ignorePatterns: frameworkAssetIgnorePatterns });
|
|
2571
|
+
const { assetManifest, assetFiles } = await collectAndHashAssets(assetsDir, onProgress, { ignorePatterns: frameworkAssetIgnorePatterns });
|
|
2186
2572
|
const { needed, skipped } = await client.preflight(config.projectId, assetManifest, true);
|
|
2187
2573
|
s.stop("Checked for changes");
|
|
2188
2574
|
cliLog?.info("preflight_end", {
|
|
@@ -2198,43 +2584,37 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
|
|
|
2198
2584
|
});
|
|
2199
2585
|
const packageStartedAt = Date.now();
|
|
2200
2586
|
const neededSet = skipped > 0 ? new Set(needed) : null;
|
|
2201
|
-
const
|
|
2587
|
+
const packageOptions = {
|
|
2588
|
+
bindingNames: resolveBindingNames(voidConfig.inference?.bindings),
|
|
2589
|
+
workerMain: actualWorkerMain,
|
|
2590
|
+
compatibilityDate: wranglerCompat.compatibilityDate,
|
|
2591
|
+
compatibilityFlags: wranglerCompat.compatibilityFlags
|
|
2592
|
+
};
|
|
2593
|
+
const manifest = buildFrameworkManifest({
|
|
2202
2594
|
frameworkName: detected.name,
|
|
2203
|
-
workerDir: actualWorkerDir,
|
|
2204
2595
|
workerMain: actualWorkerMain,
|
|
2205
|
-
assetsDir,
|
|
2206
2596
|
bindings,
|
|
2207
2597
|
migrations: validatedMigrations,
|
|
2208
2598
|
schedules,
|
|
2209
2599
|
queues,
|
|
2210
2600
|
revalidate,
|
|
2601
|
+
revalidateQueryAllowlist: voidConfig.routing?.revalidateQueryAllowlist,
|
|
2211
2602
|
prerender,
|
|
2212
2603
|
assetConfig: { not_found_handling: "none" },
|
|
2213
2604
|
vars: envVars,
|
|
2214
|
-
packageOptions
|
|
2215
|
-
bindingNames: resolveBindingNames(voidConfig.inference?.bindings),
|
|
2216
|
-
workerMain: actualWorkerMain,
|
|
2217
|
-
compatibilityDate: wranglerCompat.compatibilityDate,
|
|
2218
|
-
compatibilityFlags: wranglerCompat.compatibilityFlags
|
|
2219
|
-
},
|
|
2220
|
-
revalidateQueryAllowlist: voidConfig.routing?.revalidateQueryAllowlist,
|
|
2221
|
-
neededAssets: neededSet,
|
|
2605
|
+
packageOptions,
|
|
2222
2606
|
hashedAssetsPrefix: fwPreset.hashedAssetsPrefix ?? "assets",
|
|
2223
2607
|
headerRules,
|
|
2224
2608
|
redirectRules,
|
|
2225
2609
|
fallbackRules,
|
|
2226
|
-
dialect
|
|
2227
|
-
assetIgnorePatterns: frameworkAssetIgnorePatterns,
|
|
2228
|
-
onProgress
|
|
2610
|
+
dialect
|
|
2229
2611
|
});
|
|
2230
|
-
|
|
2231
|
-
formData.append("source", getTokenSource());
|
|
2232
|
-
if (commit) formData.append("commit", commit);
|
|
2612
|
+
const workerFiles = collectWorkerFiles(actualWorkerDir);
|
|
2233
2613
|
cliLog?.info("package_end", {
|
|
2234
2614
|
mode: "framework",
|
|
2235
2615
|
durationMs: Date.now() - packageStartedAt
|
|
2236
2616
|
});
|
|
2237
|
-
const result = await
|
|
2617
|
+
const result = await streamDirectR2Deploy(client, config.projectId, manifest, assetManifest, assetFiles, needed, gitMetadata, neededSet !== null, workerFiles, s, cliLog);
|
|
2238
2618
|
const summary = [`${result.workers} worker module(s), ${formatKnownAssetSummary(result, skipped)}`];
|
|
2239
2619
|
if (result.migrations) summary.push(`${result.migrations} migration(s) applied`);
|
|
2240
2620
|
if (schedules.length > 0) summary.push(`${schedules.length} cron job(s) scheduled`);
|
|
@@ -2247,7 +2627,7 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
|
|
|
2247
2627
|
cleanupWrapper(root);
|
|
2248
2628
|
ye("Done!");
|
|
2249
2629
|
}
|
|
2250
|
-
async function runFullDeploy(root, config, client, fwPreset, skipBuild,
|
|
2630
|
+
async function runFullDeploy(root, config, client, fwPreset, skipBuild, gitMetadata, detected, cliLog) {
|
|
2251
2631
|
cliLog?.info("deploy_mode", {
|
|
2252
2632
|
mode: "full",
|
|
2253
2633
|
framework: detected?.name
|
|
@@ -2390,7 +2770,7 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, commit,
|
|
|
2390
2770
|
buildCommand: null,
|
|
2391
2771
|
outputDir: clientDir,
|
|
2392
2772
|
appType: "static"
|
|
2393
|
-
}, true,
|
|
2773
|
+
}, true, gitMetadata, assetsPrefix, headerRules, redirectRules, fallbackRules, void 0, cliLog);
|
|
2394
2774
|
}
|
|
2395
2775
|
}
|
|
2396
2776
|
const uniquePrerenderPaths = deployConfig.output === "static" || isFrameworkMode ? [] : isNodeTarget(deployConfig.target) ? await collectPrerenderPathsNode({
|
|
@@ -2421,7 +2801,7 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, commit,
|
|
|
2421
2801
|
});
|
|
2422
2802
|
s.start("Checking for changes...");
|
|
2423
2803
|
cliLog?.info("preflight_start", { dir: clientDir });
|
|
2424
|
-
const { assetManifest } = await collectAndHashAssets(clientDir, onProgress);
|
|
2804
|
+
const { assetManifest, assetFiles } = await collectAndHashAssets(clientDir, onProgress);
|
|
2425
2805
|
const { needed, skipped } = await client.preflight(config.projectId, assetManifest, true);
|
|
2426
2806
|
s.stop("Checked for changes");
|
|
2427
2807
|
cliLog?.info("preflight_end", {
|
|
@@ -2442,20 +2822,42 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, commit,
|
|
|
2442
2822
|
});
|
|
2443
2823
|
const packageStartedAt = Date.now();
|
|
2444
2824
|
const neededSet = skipped > 0 ? new Set(needed) : null;
|
|
2445
|
-
const
|
|
2825
|
+
const voidPackageOptions = {
|
|
2446
2826
|
compatibilityDate: wranglerCompat?.compatibilityDate,
|
|
2447
2827
|
compatibilityFlags: wranglerCompat?.compatibilityFlags,
|
|
2448
2828
|
...sandboxConfig && { sandbox: sandboxConfig },
|
|
2449
2829
|
revalidateQueryAllowlist: deployConfig.routing?.revalidateQueryAllowlist
|
|
2450
|
-
}
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2830
|
+
};
|
|
2831
|
+
const fallbackWarning = detectNonSpaFallbackWarning(assetConfig, fallbackRules);
|
|
2832
|
+
if (fallbackWarning) {
|
|
2833
|
+
onProgress?.(fallbackWarning);
|
|
2834
|
+
console.warn(`[void] ${fallbackWarning}`);
|
|
2835
|
+
}
|
|
2836
|
+
const manifest = buildVoidManifest({
|
|
2837
|
+
bindings: effectiveBindings,
|
|
2838
|
+
migrations: validatedMigrations,
|
|
2839
|
+
schedules,
|
|
2840
|
+
ssr: isSsr || hasRoutes || hasMiddleware || hasWebSockets || authEnabled,
|
|
2841
|
+
framework: isFrameworkMode ? detected.name : void 0,
|
|
2842
|
+
revalidate,
|
|
2843
|
+
vars: envVars,
|
|
2844
|
+
queues,
|
|
2845
|
+
prerender: uniquePrerenderPaths,
|
|
2846
|
+
assetConfig,
|
|
2847
|
+
options: voidPackageOptions,
|
|
2848
|
+
hashedAssetsPrefix: assetsPrefix,
|
|
2849
|
+
headerRules,
|
|
2850
|
+
redirectRules,
|
|
2851
|
+
fallbackRules,
|
|
2852
|
+
dialect,
|
|
2853
|
+
webSockets
|
|
2854
|
+
});
|
|
2855
|
+
const workerFiles = collectWorkerFiles(join(distDir, workerDirName));
|
|
2454
2856
|
cliLog?.info("package_end", {
|
|
2455
2857
|
mode: "full",
|
|
2456
2858
|
durationMs: Date.now() - packageStartedAt
|
|
2457
2859
|
});
|
|
2458
|
-
const result = await
|
|
2860
|
+
const result = await streamDirectR2Deploy(client, config.projectId, manifest, assetManifest, assetFiles, needed, gitMetadata, neededSet !== null, workerFiles, s, cliLog);
|
|
2459
2861
|
const summary = [`${result.workers} worker module(s), ${formatKnownAssetSummary(result, skipped)}`];
|
|
2460
2862
|
if (result.migrations) summary.push(`${result.migrations} migration(s) applied`);
|
|
2461
2863
|
if (schedules.length > 0) summary.push(`${schedules.length} cron job(s) scheduled`);
|
|
@@ -2486,6 +2888,8 @@ async function createProjectFromSlug(root, client, slug, cliLog) {
|
|
|
2486
2888
|
}
|
|
2487
2889
|
function getGitCommit(root) {
|
|
2488
2890
|
try {
|
|
2891
|
+
const githubSha = process.env.GITHUB_SHA?.trim();
|
|
2892
|
+
if (githubSha && /^[0-9a-f]{7,64}$/i.test(githubSha)) return githubSha;
|
|
2489
2893
|
execSync("git rev-parse --is-inside-work-tree", {
|
|
2490
2894
|
cwd: root,
|
|
2491
2895
|
stdio: "ignore"
|