void 0.10.11 → 0.10.13
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/cli/cli.mjs +14 -14
- package/dist/cli/env-schema-probe.mjs +2 -2
- package/dist/{config-E03l1C_h.mjs → config-qHGgPuWT.mjs} +23 -0
- package/dist/{db-PZBsLSGb.mjs → db-C0i0sYMS.mjs} +3 -3
- package/dist/{deploy-DT8wsPZd.mjs → deploy-jBJT1fUT.mjs} +408 -66
- package/dist/{env-AAHU02L6.mjs → env-CZy5MorI.mjs} +1 -1
- package/dist/{env-validation-BdDlGhbN.mjs → env-validation-Dea3v3ej.mjs} +1 -1
- package/dist/{gen-oup1xBN0.mjs → gen-BzXf3Jh2.mjs} +1 -1
- package/dist/{git-metadata-CBKaL0v5.mjs → git-metadata-D-gurjzG.mjs} +6 -5
- package/dist/{github-cmd-BdNaOVNa.mjs → github-cmd-DKcGUNsj.mjs} +1 -1
- package/dist/{headers-BQknpzkn.mjs → headers-ChAADPQu.mjs} +1 -1
- package/dist/index.mjs +91 -43
- package/dist/{init-Dl2PKuQn.mjs → init-7JCAcKNQ.mjs} +2 -2
- package/dist/{node-BDx8pmhq.mjs → node-pRg81HqV.mjs} +3 -3
- package/dist/pages/index.mjs +3 -3
- package/dist/pages/islands-plugin.mjs +1 -1
- package/dist/{prepare-DOBTY0o4.mjs → prepare-BvvgAz-3.mjs} +3 -3
- package/dist/{preset-BGrvB4Bl.mjs → preset-BjyR3lzz.mjs} +55 -2
- package/dist/{provision-BLrCEBbI.mjs → provision-CPx2ZxsH.mjs} +2 -2
- package/dist/{route-types-COI2DsZv.mjs → route-types-z1jtHEi_.mjs} +8 -1
- package/dist/{scan-DYXkrasO.mjs → scan-ChWt4pX1.mjs} +12 -13
- package/dist/{scan-DEwlM_Xy.mjs → scan-NU4xKGci.mjs} +4 -1
- package/package.json +2 -2
- package/schema.json +18 -0
- package/skills/migrate-vite-cloudflare-to-void/SKILL.md +1 -1
- package/skills/void/docs/guide/app-types.md +43 -0
- package/skills/void/docs/guide/deployment.md +68 -1
- package/skills/void/docs/guide/edge/static-assets.md +37 -1
- package/skills/void/docs/guide/server-routing.md +21 -1
- package/skills/void/docs/guide/websockets.md +3 -0
- package/skills/void/docs/integrations/frameworks/overview.md +1 -0
- package/skills/void/docs/node_modules/void/CLAUDE.md +3 -1
- package/skills/void/docs/node_modules/void/skills/migrate-vite-cloudflare-to-void/SKILL.md +1 -1
- package/skills/void/docs/reference/cli.md +24 -0
- package/skills/void/docs/reference/config.md +42 -1
- package/skills/void/docs/reference/structure.md +1 -0
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import { n as __exportAll } from "./rolldown-runtime-DJK8HYOj.mjs";
|
|
1
|
+
import { n as __exportAll, r as __toESM } from "./rolldown-runtime-DJK8HYOj.mjs";
|
|
2
2
|
import { a as join, c as resolve, i as isAbsolute, n as dirname, o as normalize, r as extname, s as relative, t as basename } from "./dist-DaKKDf8D.mjs";
|
|
3
|
-
import { D as outro, E as note, S as confirm, T as log, a as cliTitle, j as isCancel, o as createSpinner, u as import_picocolors, w as intro } from "./agents-CtgBYqld.mjs";
|
|
3
|
+
import { D as outro, E as note, M as require_picocolors, S as confirm, T as log, a as cliTitle, j as isCancel, o as createSpinner, u as import_picocolors$1, w as intro } from "./agents-CtgBYqld.mjs";
|
|
4
4
|
import { i as resolveProjectPaths, n as readProjectPaths, t as assertNoRootSourceConventionConflicts } from "./project-paths-BQd7OmIo.mjs";
|
|
5
|
-
import { a as scanQueuesSync, i as scanWebSocketRoutesSync, o as scanJobsSync, r as scanRoutes, s as findVoidAuthConfig } from "./scan-
|
|
5
|
+
import { a as scanQueuesSync, i as scanWebSocketRoutesSync, o as scanJobsSync, r as scanRoutes, s as findVoidAuthConfig } from "./scan-ChWt4pX1.mjs";
|
|
6
6
|
import { t as cfAccessHeaders } from "./cf-access-Bqw81xAf.mjs";
|
|
7
7
|
import { C as ensureCloudflaredToken, S as writeProjectConfig, a as isExpiredTokenError, b as readProjectConfig, f as getToken, i as isCliOutdatedError, m as isStagingMode, n as PlatformClient, o as parsePlatformErrorBody, p as getTokenSource, t as PlatformApiError, u as getApiUrl } from "./client-Gb71-XkG.mjs";
|
|
8
|
-
import { c as getDatabaseDialect, f as readConfig, l as isNodeTarget, p as resolveBindingNames } from "./config-
|
|
8
|
+
import { c as getDatabaseDialect, f as readConfig, l as isNodeTarget, p as resolveBindingNames } from "./config-qHGgPuWT.mjs";
|
|
9
9
|
import { i as inferProjectBindings, n as FRAMEWORK_SCAN_DIRS, r as detectFramework } from "./plugin-inference-DMeavIJ6.mjs";
|
|
10
10
|
import { o as validateSsrEntry } from "./entry-D7yy4xVH.mjs";
|
|
11
11
|
import { i as voidWarn, r as voidLog } from "./log-ChfPKsVd.mjs";
|
|
12
|
-
import { a as resolveProjectCommand, c as mergeAndExpand, i as hasBackendFiles, l as parseDotenvFile, n as detectPreset, o as resolveWranglerArgv, r as formatProjectCommand, t as FRAMEWORK_PRESETS, u as scanWranglerDotenvKeys } from "./preset-
|
|
12
|
+
import { a as resolveProjectCommand, c as mergeAndExpand, i as hasBackendFiles, l as parseDotenvFile, n as detectPreset, o as resolveWranglerArgv, r as formatProjectCommand, t as FRAMEWORK_PRESETS, u as scanWranglerDotenvKeys } from "./preset-BjyR3lzz.mjs";
|
|
13
13
|
import { t as discoverSchema } from "./discover-CJHyvYfR.mjs";
|
|
14
14
|
import { t as findEnvFile } from "./env-types-QBj-ndax.mjs";
|
|
15
|
-
import { a as collectPlaintextVarWarningsFromVars, d as getDeployEnvDefaults, f as readEmittedWorkerVars, i as SelfHostSecretListError, l as fetchRemoteSecretNames, m as validateProdEnv, n as SelfHostFirstDeployError, o as deriveSelfHostEnvContextFromVars, p as selfHostSchemaFailureMessage, r as SelfHostSchemaError, s as dischargeSelfHostEnvContext, t as SelfHostEnvValidationError, u as formatEnvReport } from "./env-validation-
|
|
15
|
+
import { a as collectPlaintextVarWarningsFromVars, d as getDeployEnvDefaults, f as readEmittedWorkerVars, i as SelfHostSecretListError, l as fetchRemoteSecretNames, m as validateProdEnv, n as SelfHostFirstDeployError, o as deriveSelfHostEnvContextFromVars, p as selfHostSchemaFailureMessage, r as SelfHostSchemaError, s as dischargeSelfHostEnvContext, t as SelfHostEnvValidationError, u as formatEnvReport } from "./env-validation-Dea3v3ej.mjs";
|
|
16
16
|
import { n as writeDrizzleConfig } from "./config-CutEMNGJ.mjs";
|
|
17
17
|
import { a as buildMigrationPlanBundle, c as collectMigrations, r as validateMigrations, t as assertJournalCoherence } from "./validate-Cw_RLeTj.mjs";
|
|
18
|
-
import { t as scanPages } from "./scan-
|
|
18
|
+
import { t as scanPages } from "./scan-NU4xKGci.mjs";
|
|
19
19
|
import { n as STAGING_PROXY_URL, t as DEFAULT_PROXY_URL } from "./proxy-D-3_D-Gl.mjs";
|
|
20
20
|
import { i as promptProjectSetupAction, n as promptAndCreateProject, r as promptProjectSelection } from "./create-project-DsYvl3TB.mjs";
|
|
21
21
|
import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project-BBMtLLV9.mjs";
|
|
22
|
-
import { t as collectGitDeployMetadata } from "./git-metadata-
|
|
23
|
-
import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-
|
|
22
|
+
import { t as collectGitDeployMetadata } from "./git-metadata-D-gurjzG.mjs";
|
|
23
|
+
import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-ChAADPQu.mjs";
|
|
24
24
|
import { t as promptForLoginToken } from "./login-BT3H8PN3.mjs";
|
|
25
25
|
import { createRequire } from "node:module";
|
|
26
26
|
import { copyFileSync, cpSync, createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
@@ -56,24 +56,26 @@ const VOID_INTERNAL_WORKER_FIRST = ["/__void", "/__void/*"];
|
|
|
56
56
|
function resolveAssetRoutingPolicy(inputs) {
|
|
57
57
|
const workerFirst = workerOwnsHtmlRouting(inputs);
|
|
58
58
|
const apiWorkerFirst = !workerFirst && (inputs.hasApiRoutes === true || inputs.authEnabled === true);
|
|
59
|
+
const assetConfig = workerFirst ? {
|
|
60
|
+
not_found_handling: "none",
|
|
61
|
+
run_worker_first: ["/**"]
|
|
62
|
+
} : apiWorkerFirst ? {
|
|
63
|
+
not_found_handling: "single-page-application",
|
|
64
|
+
run_worker_first: [
|
|
65
|
+
"/api",
|
|
66
|
+
"/api/*",
|
|
67
|
+
...VOID_INTERNAL_WORKER_FIRST
|
|
68
|
+
]
|
|
69
|
+
} : {
|
|
70
|
+
not_found_handling: "single-page-application",
|
|
71
|
+
run_worker_first: [...VOID_INTERNAL_WORKER_FIRST]
|
|
72
|
+
};
|
|
73
|
+
if (inputs.notFound !== void 0) assetConfig.not_found_handling = inputs.notFound;
|
|
59
74
|
return {
|
|
60
|
-
assetConfig
|
|
61
|
-
not_found_handling: "none",
|
|
62
|
-
run_worker_first: ["/**"]
|
|
63
|
-
} : apiWorkerFirst ? {
|
|
64
|
-
not_found_handling: "single-page-application",
|
|
65
|
-
run_worker_first: [
|
|
66
|
-
"/api",
|
|
67
|
-
"/api/*",
|
|
68
|
-
...VOID_INTERNAL_WORKER_FIRST
|
|
69
|
-
]
|
|
70
|
-
} : {
|
|
71
|
-
not_found_handling: "single-page-application",
|
|
72
|
-
run_worker_first: [...VOID_INTERNAL_WORKER_FIRST]
|
|
73
|
-
},
|
|
75
|
+
assetConfig,
|
|
74
76
|
apiWorkerFirst,
|
|
75
77
|
workerFirst,
|
|
76
|
-
workerSpaFallback: workerFirst && !inputs.isSsr && !inputs.hasPages
|
|
78
|
+
workerSpaFallback: workerFirst && !inputs.isSsr && !inputs.hasPages && (inputs.notFound === void 0 || inputs.notFound === "single-page-application")
|
|
77
79
|
};
|
|
78
80
|
}
|
|
79
81
|
const SANDBOX_MIGRATION_TAG = "void-sandbox-v1";
|
|
@@ -2113,7 +2115,7 @@ async function uploadOne(item, cliLog, fetchImpl) {
|
|
|
2113
2115
|
path: item.path,
|
|
2114
2116
|
attempt: attempt + 1,
|
|
2115
2117
|
status: res.status,
|
|
2116
|
-
body: truncate(errBody)
|
|
2118
|
+
body: truncate$1(errBody)
|
|
2117
2119
|
});
|
|
2118
2120
|
await sleep(RETRY_BACKOFF_MS[attempt]);
|
|
2119
2121
|
attempt++;
|
|
@@ -2124,9 +2126,9 @@ async function uploadOne(item, cliLog, fetchImpl) {
|
|
|
2124
2126
|
path: item.path,
|
|
2125
2127
|
durationMs: Date.now() - startedAt,
|
|
2126
2128
|
status: res.status,
|
|
2127
|
-
body: truncate(errBody)
|
|
2129
|
+
body: truncate$1(errBody)
|
|
2128
2130
|
});
|
|
2129
|
-
throw new DirectR2PutError(res.status, item.hash, item.path, truncate(errBody));
|
|
2131
|
+
throw new DirectR2PutError(res.status, item.hash, item.path, truncate$1(errBody));
|
|
2130
2132
|
}
|
|
2131
2133
|
}
|
|
2132
2134
|
async function safeReadText(res) {
|
|
@@ -2136,7 +2138,7 @@ async function safeReadText(res) {
|
|
|
2136
2138
|
return "";
|
|
2137
2139
|
}
|
|
2138
2140
|
}
|
|
2139
|
-
function truncate(s) {
|
|
2141
|
+
function truncate$1(s) {
|
|
2140
2142
|
return s.length > 500 ? `${s.slice(0, 500)}...` : s;
|
|
2141
2143
|
}
|
|
2142
2144
|
function sleep(ms) {
|
|
@@ -2268,6 +2270,23 @@ async function safeText(res) {
|
|
|
2268
2270
|
//#endregion
|
|
2269
2271
|
//#region src/cli/log-file.ts
|
|
2270
2272
|
/**
|
|
2273
|
+
* Flatten an already-serialized `ErrChainNode` (as read back out of a JSONL
|
|
2274
|
+
* record) to a single inline string. Mirrors `formatErrorChainInline`, which
|
|
2275
|
+
* does the same job for a live `Error` before it is written.
|
|
2276
|
+
*/
|
|
2277
|
+
function formatErrorNodeInline(node, depth = 0) {
|
|
2278
|
+
if (node == null || typeof node !== "object") return String(node);
|
|
2279
|
+
const message = node.message == null ? "" : String(node.message);
|
|
2280
|
+
if (depth > 5) return message;
|
|
2281
|
+
let head = node.name ? `${String(node.name)}: ${message}` : message;
|
|
2282
|
+
const tags = [];
|
|
2283
|
+
if (node.code !== void 0) tags.push(`code=${node.code}`);
|
|
2284
|
+
if (node.errno !== void 0) tags.push(`errno=${node.errno}`);
|
|
2285
|
+
if (tags.length > 0) head = `${head} (${tags.join(" ")})`;
|
|
2286
|
+
if (node.cause != null) return `${head} -> ${formatErrorNodeInline(node.cause, depth + 1)}`;
|
|
2287
|
+
return head;
|
|
2288
|
+
}
|
|
2289
|
+
/**
|
|
2271
2290
|
* Walk `err.cause` up to 6 levels and return a plain JSON-friendly chain.
|
|
2272
2291
|
* Skips stack traces deliberately — keeps output line-oriented and readable.
|
|
2273
2292
|
*/
|
|
@@ -2420,6 +2439,160 @@ function openDeployLog(opts) {
|
|
|
2420
2439
|
};
|
|
2421
2440
|
}
|
|
2422
2441
|
//#endregion
|
|
2442
|
+
//#region src/cli/log-summary.ts
|
|
2443
|
+
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
2444
|
+
const FIELD_VALUE_MAX = 80;
|
|
2445
|
+
const LINE_MAX = 160;
|
|
2446
|
+
const MSG_COLUMN_MAX = 28;
|
|
2447
|
+
const ELAPSED_WIDTH = 7;
|
|
2448
|
+
const LEVEL_WIDTH = 5;
|
|
2449
|
+
/** Width of `' ' + elapsed + ' '`, used to indent the error sub-lines. */
|
|
2450
|
+
const DETAIL_INDENT = " ".repeat(11);
|
|
2451
|
+
/**
|
|
2452
|
+
* `runDeploy` writes this record last, and its `err` field is the very message
|
|
2453
|
+
* printed above the summary. Keeping it out of the error block avoids echoing
|
|
2454
|
+
* the same text twice; it still shows up as one line in the tail.
|
|
2455
|
+
*/
|
|
2456
|
+
const TERMINAL_ERROR_MSG = "deploy_end";
|
|
2457
|
+
const RESERVED_KEYS = /* @__PURE__ */ new Set([
|
|
2458
|
+
"ts",
|
|
2459
|
+
"elapsedMs",
|
|
2460
|
+
"lvl",
|
|
2461
|
+
"msg",
|
|
2462
|
+
"err"
|
|
2463
|
+
]);
|
|
2464
|
+
/**
|
|
2465
|
+
* The trace was a file the user opened by hand; the summary publishes it to
|
|
2466
|
+
* whatever reads stderr, which in CI is a stored, often public, log.
|
|
2467
|
+
*
|
|
2468
|
+
* Scrubbing here covers only credentials this code emits itself — a presigned
|
|
2469
|
+
* URL it built, an auth header it set, a field it named. Scrubbing user data is
|
|
2470
|
+
* left to the CI platform, which owns the secrets and masks them before the log
|
|
2471
|
+
* is written. GitHub Actions does this for everything under `secrets.*`. Void
|
|
2472
|
+
* does not guess at credential-shaped names or at credentials embedded in
|
|
2473
|
+
* values the user supplied.
|
|
2474
|
+
*/
|
|
2475
|
+
/** Query parameters whose value must never reach a CI log. */
|
|
2476
|
+
const SIGNED_QUERY_RE = /([?&](?:X-Amz-[A-Za-z0-9-]+|[Ss]ignature|sig|token|access_token|key)=)[^&\s]+/g;
|
|
2477
|
+
const BEARER_RE = /\b(Bearer\s+)\S+/g;
|
|
2478
|
+
/**
|
|
2479
|
+
* Field keys whose value is masked whole, whatever it looks like. `auth` is
|
|
2480
|
+
* deliberately absent — it matches metadata keys such as `authMethod` far more
|
|
2481
|
+
* often than credentials.
|
|
2482
|
+
*/
|
|
2483
|
+
const SECRET_KEY_RE = /token|secret|password|passwd|credential|cookie|signature|api[-_]?key|private[-_]?key/i;
|
|
2484
|
+
function redact(text) {
|
|
2485
|
+
return text.replace(SIGNED_QUERY_RE, "$1[redacted]").replace(BEARER_RE, "$1[redacted]");
|
|
2486
|
+
}
|
|
2487
|
+
function truncate(text, max) {
|
|
2488
|
+
return text.length <= max ? text : `${text.slice(0, max)}...`;
|
|
2489
|
+
}
|
|
2490
|
+
/** Collapse every run of whitespace so one record always renders as one line. */
|
|
2491
|
+
function oneLine(text) {
|
|
2492
|
+
return text.replace(/\s+/g, " ").trim();
|
|
2493
|
+
}
|
|
2494
|
+
function stringifyValue(value) {
|
|
2495
|
+
if (typeof value === "string") return value;
|
|
2496
|
+
if (value === null || typeof value === "number" || typeof value === "boolean") return String(value);
|
|
2497
|
+
try {
|
|
2498
|
+
return JSON.stringify(value) ?? String(value);
|
|
2499
|
+
} catch {
|
|
2500
|
+
return String(value);
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
/** Render one `key=value` pair, collapsed, redacted, and length-capped. */
|
|
2504
|
+
function formatField(key, value) {
|
|
2505
|
+
if (SECRET_KEY_RE.test(key)) return `${key}=[redacted]`;
|
|
2506
|
+
return `${key}=${truncate(redact(oneLine(stringifyValue(value))), FIELD_VALUE_MAX)}`;
|
|
2507
|
+
}
|
|
2508
|
+
function fieldPairs(record) {
|
|
2509
|
+
const pairs = [];
|
|
2510
|
+
for (const [key, value] of Object.entries(record)) {
|
|
2511
|
+
if (RESERVED_KEYS.has(key) || value === void 0) continue;
|
|
2512
|
+
pairs.push(formatField(key, value));
|
|
2513
|
+
}
|
|
2514
|
+
if (record.err !== void 0) pairs.push(`err=${truncate(redact(oneLine(formatErrorNodeInline(record.err))), FIELD_VALUE_MAX)}`);
|
|
2515
|
+
return pairs;
|
|
2516
|
+
}
|
|
2517
|
+
function formatElapsed(elapsedMs) {
|
|
2518
|
+
return `${((typeof elapsedMs === "number" && Number.isFinite(elapsedMs) ? elapsedMs : 0) / 1e3).toFixed(1)}s`.padStart(ELAPSED_WIDTH);
|
|
2519
|
+
}
|
|
2520
|
+
function colorLevel(level, padded) {
|
|
2521
|
+
if (level === "error") return import_picocolors.default.red(padded);
|
|
2522
|
+
if (level === "warn") return import_picocolors.default.yellow(padded);
|
|
2523
|
+
return import_picocolors.default.dim(padded);
|
|
2524
|
+
}
|
|
2525
|
+
function parseRecords(lines) {
|
|
2526
|
+
const records = [];
|
|
2527
|
+
for (const raw of lines) {
|
|
2528
|
+
const trimmed = raw.trim();
|
|
2529
|
+
if (!trimmed) continue;
|
|
2530
|
+
try {
|
|
2531
|
+
const parsed = JSON.parse(trimmed);
|
|
2532
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) records.push(parsed);
|
|
2533
|
+
} catch {}
|
|
2534
|
+
}
|
|
2535
|
+
return records;
|
|
2536
|
+
}
|
|
2537
|
+
/** One aligned line: ` 8.9s error deploy_server_error message=...`. */
|
|
2538
|
+
function formatTailLine(record, msgWidth) {
|
|
2539
|
+
const level = typeof record.lvl === "string" ? record.lvl : "info";
|
|
2540
|
+
const msg = typeof record.msg === "string" ? record.msg : "";
|
|
2541
|
+
const elapsed = formatElapsed(record.elapsedMs);
|
|
2542
|
+
const paddedLevel = level.padEnd(LEVEL_WIDTH);
|
|
2543
|
+
const paddedMsg = msg.padEnd(msgWidth);
|
|
2544
|
+
const head = ` ${elapsed} ${paddedLevel} ${paddedMsg}`;
|
|
2545
|
+
const fields = fieldPairs(record).join(" ");
|
|
2546
|
+
if (!fields) return ` ${elapsed} ${colorLevel(level, paddedLevel)} ${msg}`;
|
|
2547
|
+
const budget = Math.max(20, LINE_MAX - head.length);
|
|
2548
|
+
return ` ${elapsed} ${colorLevel(level, paddedLevel)} ${paddedMsg} ${truncate(fields, budget)}`;
|
|
2549
|
+
}
|
|
2550
|
+
/** An error entry: a headline, then one indented line per field. */
|
|
2551
|
+
function formatErrorEntry(record) {
|
|
2552
|
+
const msg = typeof record.msg === "string" ? record.msg : "error";
|
|
2553
|
+
const lines = [` ${formatElapsed(record.elapsedMs)} ${import_picocolors.default.red(msg)}`];
|
|
2554
|
+
for (const pair of fieldPairs(record)) lines.push(`${DETAIL_INDENT}${pair}`);
|
|
2555
|
+
return lines;
|
|
2556
|
+
}
|
|
2557
|
+
/**
|
|
2558
|
+
* Turn raw JSONL lines into the block appended to a failed deploy's error
|
|
2559
|
+
* message. Returns `null` when there is nothing worth printing.
|
|
2560
|
+
*
|
|
2561
|
+
* The block has two parts: every `error` record with its flattened cause
|
|
2562
|
+
* chain, then the last `LOG_TAIL_LIMIT` records for timeline context.
|
|
2563
|
+
*/
|
|
2564
|
+
function formatDeployLogSummary(lines) {
|
|
2565
|
+
const records = parseRecords(lines);
|
|
2566
|
+
if (records.length === 0) return null;
|
|
2567
|
+
const blocks = [];
|
|
2568
|
+
const errors = records.filter((r) => r.lvl === "error" && r.msg !== TERMINAL_ERROR_MSG);
|
|
2569
|
+
if (errors.length > 0) {
|
|
2570
|
+
const shown = errors.slice(0, 5);
|
|
2571
|
+
const errorLines = shown.flatMap((record) => formatErrorEntry(record));
|
|
2572
|
+
if (errors.length > shown.length) errorLines.push(`${DETAIL_INDENT}(+${errors.length - shown.length} more)`);
|
|
2573
|
+
blocks.push([import_picocolors.default.bold("Errors"), ...errorLines].join("\n"));
|
|
2574
|
+
}
|
|
2575
|
+
const tail = records.slice(-20);
|
|
2576
|
+
const msgWidth = Math.min(MSG_COLUMN_MAX, tail.reduce((max, r) => Math.max(max, typeof r.msg === "string" ? r.msg.length : 0), 0));
|
|
2577
|
+
const heading = records.length > tail.length ? `Last 20 of ${records.length} entries` : `Log (${records.length} ${records.length === 1 ? "entry" : "entries"})`;
|
|
2578
|
+
blocks.push([import_picocolors.default.bold(heading), ...tail.map((record) => formatTailLine(record, msgWidth))].join("\n"));
|
|
2579
|
+
return `\n${blocks.join("\n\n")}`;
|
|
2580
|
+
}
|
|
2581
|
+
/**
|
|
2582
|
+
* Read a deploy JSONL log and format its summary. Returns `null` for a missing
|
|
2583
|
+
* path or any read failure — a postmortem aid must never mask the real error.
|
|
2584
|
+
*/
|
|
2585
|
+
function buildDeployLogSummary(path) {
|
|
2586
|
+
if (!path) return null;
|
|
2587
|
+
let text;
|
|
2588
|
+
try {
|
|
2589
|
+
text = readFileSync(path, "utf-8");
|
|
2590
|
+
} catch {
|
|
2591
|
+
return null;
|
|
2592
|
+
}
|
|
2593
|
+
return formatDeployLogSummary(text.split("\n"));
|
|
2594
|
+
}
|
|
2595
|
+
//#endregion
|
|
2423
2596
|
//#region src/shared/utils.ts
|
|
2424
2597
|
const isWindows = typeof process !== "undefined" && process.platform === "win32";
|
|
2425
2598
|
function slash(p) {
|
|
@@ -2469,11 +2642,24 @@ function stripDevOnlyRuleFields(rules) {
|
|
|
2469
2642
|
*
|
|
2470
2643
|
* When the user has no fallbacks configured, the platform-level SPA switch
|
|
2471
2644
|
* is left intact (zero worker-side overhead for the common case).
|
|
2472
|
-
|
|
2473
|
-
|
|
2645
|
+
*
|
|
2646
|
+
* `notFound` is the explicit `routing.notFound` from void.json. It overrides
|
|
2647
|
+
* the value this function would otherwise infer from `appType`, mirroring
|
|
2648
|
+
* `resolveAssetRoutingPolicy` on the worker-with-assets path. It is resolved
|
|
2649
|
+
* *before* the SPA branch, not patched onto the result afterwards: an override
|
|
2650
|
+
* away from `single-page-application` must also skip the synthetic
|
|
2651
|
+
* `/* -> /index.html` rule below, otherwise the SPA shell would still be
|
|
2652
|
+
* served — just through a fallback rule instead of the platform switch — and
|
|
2653
|
+
* the override would be a no-op. This matters because `detectPreset`
|
|
2654
|
+
* classifies any Vite project without backend files as `spa`, so a plain Vite
|
|
2655
|
+
* MPA lands on `appType: 'spa'` and `routing.notFound: '404-page'` is its only
|
|
2656
|
+
* escape hatch.
|
|
2657
|
+
*/
|
|
2658
|
+
function resolveSpaFallbackChain(appType, fallbackRules, notFound) {
|
|
2474
2659
|
const userRules = fallbackRules && fallbackRules.length > 0 ? fallbackRules : void 0;
|
|
2475
|
-
|
|
2476
|
-
|
|
2660
|
+
const handling = notFound ?? (appType === "spa" ? "single-page-application" : appType === "static" ? "404-page" : "none");
|
|
2661
|
+
if (handling !== "single-page-application") return {
|
|
2662
|
+
assetConfig: { not_found_handling: handling },
|
|
2477
2663
|
fallbackRules: userRules
|
|
2478
2664
|
};
|
|
2479
2665
|
if (!userRules) return {
|
|
@@ -2724,17 +2910,21 @@ function toLiveManifest(live) {
|
|
|
2724
2910
|
};
|
|
2725
2911
|
}
|
|
2726
2912
|
/**
|
|
2727
|
-
* If the user configured `routing.fallbacks` on
|
|
2728
|
-
*
|
|
2729
|
-
*
|
|
2913
|
+
* If the user configured `routing.fallbacks` on an app whose resolved
|
|
2914
|
+
* `not_found_handling` is not `single-page-application`, the platform still
|
|
2915
|
+
* applies the rules but no `/* -> /index.html` SPA shell is synthesised —
|
|
2916
|
+
* unmatched paths fall through to the platform 404. Returns the warning
|
|
2730
2917
|
* message string when that mismatch is detected, or `null` otherwise. Pure
|
|
2731
2918
|
* function; callers decide whether/how to surface the message (CLI progress,
|
|
2732
2919
|
* `console.warn`, etc.).
|
|
2920
|
+
*
|
|
2921
|
+
* The message names `not_found_handling` rather than the app type because an
|
|
2922
|
+
* explicit `routing.notFound` can land here too — the app may well be a SPA.
|
|
2733
2923
|
*/
|
|
2734
2924
|
function detectNonSpaFallbackWarning(assetConfig, fallbackRules) {
|
|
2735
2925
|
if (assetConfig?.not_found_handling === "single-page-application") return null;
|
|
2736
2926
|
if (!fallbackRules || fallbackRules.length === 0) return null;
|
|
2737
|
-
return "routing.fallbacks is set but
|
|
2927
|
+
return "routing.fallbacks is set but not_found_handling is not \"single-page-application\"; fallbacks will be applied, but no \"/* -> /index.html\" SPA shell fallback is synthesised — unmatched paths fall through to the platform 404.";
|
|
2738
2928
|
}
|
|
2739
2929
|
/**
|
|
2740
2930
|
* Build a Void-app deploy manifest from the inferred bindings + per-feature
|
|
@@ -2804,6 +2994,7 @@ function buildVoidManifest(opts) {
|
|
|
2804
2994
|
if (options?.workerMain) manifest.workerMain = options.workerMain;
|
|
2805
2995
|
if (options?.compatibilityDate) manifest.compatibilityDate = options.compatibilityDate;
|
|
2806
2996
|
if (options?.compatibilityFlags && options.compatibilityFlags.length > 0) manifest.compatibilityFlags = options.compatibilityFlags;
|
|
2997
|
+
if (options?.cpuMs) manifest.limits = { cpuMs: options.cpuMs };
|
|
2807
2998
|
if (hashedAssetsPrefix) manifest.hashedAssetsPrefix = hashedAssetsPrefix;
|
|
2808
2999
|
if (headerRules && headerRules.length > 0) manifest.headerRules = headerRules;
|
|
2809
3000
|
if (redirectRules && redirectRules.length > 0) manifest.redirectRules = stripDevOnlyRuleFields(redirectRules);
|
|
@@ -2839,8 +3030,8 @@ function collectWorkerFiles(workerDir) {
|
|
|
2839
3030
|
* Build a static-deploy manifest. Pure shape transformation — no I/O.
|
|
2840
3031
|
*/
|
|
2841
3032
|
function buildStaticManifest(opts) {
|
|
2842
|
-
const { appType, hashedAssetsPrefix, headerRules, redirectRules, fallbackRules } = opts;
|
|
2843
|
-
const { assetConfig, fallbackRules: resolvedFallbackRules } = resolveSpaFallbackChain(appType, fallbackRules);
|
|
3033
|
+
const { appType, hashedAssetsPrefix, headerRules, redirectRules, fallbackRules, notFound } = opts;
|
|
3034
|
+
const { assetConfig, fallbackRules: resolvedFallbackRules } = resolveSpaFallbackChain(appType, fallbackRules, notFound);
|
|
2844
3035
|
const manifest = {
|
|
2845
3036
|
version: 3,
|
|
2846
3037
|
assetManifestVersion: 2,
|
|
@@ -2897,6 +3088,7 @@ function buildFrameworkManifest(opts) {
|
|
|
2897
3088
|
manifest.workerMain = po?.workerMain ?? opts.workerMain;
|
|
2898
3089
|
if (po?.compatibilityDate) manifest.compatibilityDate = po.compatibilityDate;
|
|
2899
3090
|
if (po?.compatibilityFlags && po.compatibilityFlags.length > 0) manifest.compatibilityFlags = po.compatibilityFlags;
|
|
3091
|
+
if (po?.cpuMs) manifest.limits = { cpuMs: po.cpuMs };
|
|
2900
3092
|
if (opts.hashedAssetsPrefix) manifest.hashedAssetsPrefix = opts.hashedAssetsPrefix;
|
|
2901
3093
|
if (opts.headerRules && opts.headerRules.length > 0) manifest.headerRules = opts.headerRules;
|
|
2902
3094
|
if (opts.redirectRules && opts.redirectRules.length > 0) manifest.redirectRules = stripDevOnlyRuleFields(opts.redirectRules);
|
|
@@ -3335,6 +3527,7 @@ var deploy_exports = /* @__PURE__ */ __exportAll({
|
|
|
3335
3527
|
ensurePreviouslyDeployedLiveBindingConfig: () => ensurePreviouslyDeployedLiveBindingConfig,
|
|
3336
3528
|
generateDeployWrapperIfNeeded: () => generateDeployWrapperIfNeeded,
|
|
3337
3529
|
hasDeploySchemaDrift: () => hasDeploySchemaDrift,
|
|
3530
|
+
hasPrerenderSources: () => hasPrerenderSources,
|
|
3338
3531
|
hashWranglerUploader: () => hashWranglerUploader,
|
|
3339
3532
|
loadDeployEnv: () => loadDeployEnv,
|
|
3340
3533
|
parseRootWranglerConfig: () => parseRootWranglerConfig,
|
|
@@ -3344,6 +3537,7 @@ var deploy_exports = /* @__PURE__ */ __exportAll({
|
|
|
3344
3537
|
resolveDeployWorkerMain: () => resolveDeployWorkerMain,
|
|
3345
3538
|
resolveDeployWranglerCompat: () => resolveDeployWranglerCompat,
|
|
3346
3539
|
resolveDistDir: () => resolveDistDir,
|
|
3540
|
+
resolveFullDeployBuildCommand: () => resolveFullDeployBuildCommand,
|
|
3347
3541
|
resolveStaticBuildCommand: () => resolveStaticBuildCommand,
|
|
3348
3542
|
resolveStaticDeployPreset: () => resolveStaticDeployPreset,
|
|
3349
3543
|
resolveWorkerDirName: () => resolveWorkerDirName,
|
|
@@ -3356,6 +3550,7 @@ var deploy_exports = /* @__PURE__ */ __exportAll({
|
|
|
3356
3550
|
streamDeployEvents: () => streamDeployEvents,
|
|
3357
3551
|
validateAuthMigrationSchema: () => validateAuthMigrationSchema,
|
|
3358
3552
|
verifyGeneratedDeployConfig: () => verifyGeneratedDeployConfig,
|
|
3553
|
+
warnIgnoredNotFoundForFramework: () => warnIgnoredNotFoundForFramework,
|
|
3359
3554
|
warnRedundantForceOn3xx: () => warnRedundantForceOn3xx,
|
|
3360
3555
|
warnUnpublishedRouting: () => warnUnpublishedRouting
|
|
3361
3556
|
});
|
|
@@ -3368,7 +3563,7 @@ let loadedProvisionModule;
|
|
|
3368
3563
|
* so a managed deploy never imports it — and thus never loads `proper-lockfile`.
|
|
3369
3564
|
*/
|
|
3370
3565
|
async function loadProvisionModule() {
|
|
3371
|
-
loadedProvisionModule ??= await import("./provision-
|
|
3566
|
+
loadedProvisionModule ??= await import("./provision-CPx2ZxsH.mjs");
|
|
3372
3567
|
return loadedProvisionModule;
|
|
3373
3568
|
}
|
|
3374
3569
|
/**
|
|
@@ -3490,6 +3685,13 @@ async function runDeploy(root, options) {
|
|
|
3490
3685
|
cliLog.info("deploy_end", { ok: true });
|
|
3491
3686
|
} catch (err) {
|
|
3492
3687
|
cliLog.error("deploy_end", err, { ok: false });
|
|
3688
|
+
await cliLog.close().catch(() => {});
|
|
3689
|
+
try {
|
|
3690
|
+
if (err instanceof Error) {
|
|
3691
|
+
const summary = buildDeployLogSummary(cliLog.path);
|
|
3692
|
+
if (summary) err.message = `${err.message}\n${summary}`;
|
|
3693
|
+
}
|
|
3694
|
+
} catch {}
|
|
3493
3695
|
throw err;
|
|
3494
3696
|
} finally {
|
|
3495
3697
|
await cliLog.close().catch(() => {});
|
|
@@ -3512,17 +3714,67 @@ async function runDeploy(root, options) {
|
|
|
3512
3714
|
* `cfAccessHeaders(process.env)` / the cloudflared pair-guard AFTER the build
|
|
3513
3715
|
* step. Only the service-token pair is scrubbed; the cloudflared
|
|
3514
3716
|
* `CF_ACCESS_TOKEN` JWT (dev path) is left untouched.
|
|
3515
|
-
|
|
3516
|
-
|
|
3717
|
+
*
|
|
3718
|
+
* `localBinRoot` is an explicit OPT-IN, not a default. Build commands are run
|
|
3719
|
+
* through a shell (`execSync`) and a user-configured `inference.build` (e.g.
|
|
3720
|
+
* `vitepress build && vite build`) names bare binaries that only exist in the
|
|
3721
|
+
* project's own `node_modules/.bin`; under the build container's
|
|
3722
|
+
* `npx --yes void deploy` that happens to work — npx prepends every ancestor
|
|
3723
|
+
* `.bin` itself — but a directly invoked `node_modules/.bin/void deploy` or a
|
|
3724
|
+
* globally installed `void` inherits no such PATH and the bare binary fails to
|
|
3725
|
+
* resolve. Passing it makes the project's bin dir shadow the system one for
|
|
3726
|
+
* that child, so it belongs ONLY on the three build execs. The other callers
|
|
3727
|
+
* (`getGitCommit`, the drizzle-kit spawn) exec a system binary or an absolute
|
|
3728
|
+
* path and must not hand a dependency-shipped `node_modules/.bin/git` a child
|
|
3729
|
+
* process holding `VOID_TOKEN`.
|
|
3730
|
+
*/
|
|
3731
|
+
function buildEnv(extra, options) {
|
|
3517
3732
|
const env = {
|
|
3518
3733
|
...process.env,
|
|
3519
3734
|
...extra
|
|
3520
3735
|
};
|
|
3521
3736
|
delete env.CF_ACCESS_CLIENT_ID;
|
|
3522
3737
|
delete env.CF_ACCESS_CLIENT_SECRET;
|
|
3738
|
+
return options?.localBinRoot === void 0 ? env : prependLocalBinToPath(env, options.localBinRoot);
|
|
3739
|
+
}
|
|
3740
|
+
/**
|
|
3741
|
+
* Prepend `<root>/node_modules/.bin` to the child env's PATH, preserving
|
|
3742
|
+
* whatever was already there.
|
|
3743
|
+
*
|
|
3744
|
+
* PATH is looked up case-insensitively and written back under the SAME key the
|
|
3745
|
+
* env already used: Windows reports it as `Path`, and `{ ...process.env }`
|
|
3746
|
+
* drops the case-insensitive lookup that `process.env` itself provides, so
|
|
3747
|
+
* hardcoding `PATH` would leave the real variable untouched and add a dead one.
|
|
3748
|
+
*/
|
|
3749
|
+
function prependLocalBinToPath(env, root) {
|
|
3750
|
+
const binDir = join(root, "node_modules", ".bin");
|
|
3751
|
+
const delimiter = process.platform === "win32" ? ";" : ":";
|
|
3752
|
+
const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") ?? "PATH";
|
|
3753
|
+
const existing = env[pathKey];
|
|
3754
|
+
env[pathKey] = existing ? `${binDir}${delimiter}${existing}` : binDir;
|
|
3523
3755
|
return env;
|
|
3524
3756
|
}
|
|
3525
3757
|
/**
|
|
3758
|
+
* Marks a build child as driven by a MANAGED `void deploy`.
|
|
3759
|
+
*
|
|
3760
|
+
* The platform resolves the asset routing policy out-of-band (`resolveAssetConfig`
|
|
3761
|
+
* → `manifest.assetConfig`) and applies it to the uploaded script, so settings the
|
|
3762
|
+
* Vite plugin cannot safely write into the built wrangler config — `routing.notFound`
|
|
3763
|
+
* for a Class A framework, which needs an `assets` policy Void does not own there —
|
|
3764
|
+
* are still honored on this path. `void:framework-build-wrapper` warns about exactly
|
|
3765
|
+
* those when this flag is ABSENT, i.e. on a self-hosted `vite build && wrangler deploy`.
|
|
3766
|
+
*
|
|
3767
|
+
* Passed through the CHILD env only (never `process.env`), the same way
|
|
3768
|
+
* `VOID_DEPLOY_PROJECT_ID` is, so it cannot leak into an unrelated child process or a
|
|
3769
|
+
* later `void dev` in the same shell.
|
|
3770
|
+
*
|
|
3771
|
+
* Deliberately NOT set by `runCloudflareDeploy` (`--backend cloudflare`): that flow
|
|
3772
|
+
* hands the built output to `wrangler deploy` against the user's own account and IS the
|
|
3773
|
+
* self-host path the warning is about. (It rejects framework SSR outright today, so no
|
|
3774
|
+
* Class A build reaches it either way.)
|
|
3775
|
+
*/
|
|
3776
|
+
const MANAGED_DEPLOY_BUILD_ENV = { VOID_DEPLOY_BUILD: "1" };
|
|
3777
|
+
/**
|
|
3526
3778
|
* Deploy-time env selectors that pin the WRONG worker/config out from under the
|
|
3527
3779
|
* top-level config this flow provisions + preflights. Scrubbed from every child env
|
|
3528
3780
|
* and fenced in the project dotenv (see below), on top of the provisioner's transport
|
|
@@ -5307,7 +5559,7 @@ async function runCloudflareDeploy(root, options, cliLog, deps = {}) {
|
|
|
5307
5559
|
log.error(`deploy: ${getErrorMessage(err)}`);
|
|
5308
5560
|
return exit(1);
|
|
5309
5561
|
}
|
|
5310
|
-
if (scanWebSocketRoutesSync(root, paths).length > 0) {
|
|
5562
|
+
if (scanWebSocketRoutesSync(root, paths, "prod").length > 0) {
|
|
5311
5563
|
log.error([
|
|
5312
5564
|
"deploy: --backend cloudflare does not yet support WebSocket / Durable Object routes.",
|
|
5313
5565
|
"A `.ws.ts` route makes the build emit a key-value-backed Durable Object namespace",
|
|
@@ -5847,7 +6099,7 @@ async function runDeployInner(root, options, cliLog) {
|
|
|
5847
6099
|
name: detected.name,
|
|
5848
6100
|
class: detected.class
|
|
5849
6101
|
});
|
|
5850
|
-
const gitMetadata = collectGitDeployMetadata(root, options?.skipBuild ? null : getGitCommit(root));
|
|
6102
|
+
const gitMetadata = collectGitDeployMetadata(root, options?.skipBuild ? null : getGitCommit(root), process.env, buildEnv());
|
|
5851
6103
|
const fwPreset = FRAMEWORK_PRESETS[detected.name];
|
|
5852
6104
|
if (detected.class === "b" || detected.class === "c") {
|
|
5853
6105
|
if (!fwPreset) {
|
|
@@ -5855,18 +6107,21 @@ async function runDeployInner(root, options, cliLog) {
|
|
|
5855
6107
|
process.exit(1);
|
|
5856
6108
|
}
|
|
5857
6109
|
await runFrameworkDeploy(root, config, client, detected, fwPreset, voidConfig, options?.skipBuild, gitMetadata, cliLog);
|
|
5858
|
-
} else await runFullDeploy(root, config, client, fwPreset, options?.skipBuild, gitMetadata, detected, cliLog);
|
|
6110
|
+
} else await runFullDeploy(root, config, client, voidConfig, fwPreset, options?.skipBuild, gitMetadata, detected, cliLog);
|
|
5859
6111
|
return;
|
|
5860
6112
|
}
|
|
5861
|
-
if (!preset && !voidConfig.inference?.appType) preset = detectPreset(root, {
|
|
5862
|
-
|
|
6113
|
+
if (!preset && !voidConfig.inference?.appType) preset = detectPreset(root, {
|
|
6114
|
+
spa: options?.spa,
|
|
6115
|
+
strictBackend: true
|
|
6116
|
+
});
|
|
6117
|
+
const gitMetadata = collectGitDeployMetadata(root, options?.skipBuild ? null : getGitCommit(root), process.env, buildEnv());
|
|
5863
6118
|
if (preset) {
|
|
5864
6119
|
preset = {
|
|
5865
6120
|
...preset,
|
|
5866
6121
|
buildCommand: resolveStaticBuildCommand(preset, voidConfig.inference?.build, Boolean(options?.dir))
|
|
5867
6122
|
};
|
|
5868
6123
|
await runStaticDeploy(root, config, client, preset, options?.skipBuild, gitMetadata, void 0, void 0, void 0, void 0, voidConfig.routing, cliLog);
|
|
5869
|
-
} else await runFullDeploy(root, config, client, void 0, options?.skipBuild, gitMetadata, void 0, cliLog);
|
|
6124
|
+
} else await runFullDeploy(root, config, client, voidConfig, void 0, options?.skipBuild, gitMetadata, void 0, cliLog);
|
|
5870
6125
|
return;
|
|
5871
6126
|
} catch (error) {
|
|
5872
6127
|
if (isExpiredTokenError(error)) {
|
|
@@ -5894,7 +6149,7 @@ async function promptDeployProjectConfig(root, client, cliLog) {
|
|
|
5894
6149
|
slug: result.project.slug
|
|
5895
6150
|
};
|
|
5896
6151
|
writeProjectConfig(root, config);
|
|
5897
|
-
log.step(`Linked to ${import_picocolors.default.blue(config.slug)}`);
|
|
6152
|
+
log.step(`Linked to ${import_picocolors$1.default.blue(config.slug)}`);
|
|
5898
6153
|
cliLog?.info("project_linked", {
|
|
5899
6154
|
projectId: config.projectId,
|
|
5900
6155
|
slug: config.slug
|
|
@@ -5905,6 +6160,57 @@ function resolveStaticBuildCommand(preset, buildOverride, hasExplicitDir = false
|
|
|
5905
6160
|
if (hasExplicitDir) return null;
|
|
5906
6161
|
return buildOverride ?? preset.buildCommand;
|
|
5907
6162
|
}
|
|
6163
|
+
/**
|
|
6164
|
+
* Resolve the build command for a FULL (Class A / Void app) deploy.
|
|
6165
|
+
*
|
|
6166
|
+
* `inference.build` wins when set, matching `runFrameworkDeploy` and the static
|
|
6167
|
+
* path's `resolveStaticBuildCommand`. Without it the full path was the only
|
|
6168
|
+
* deploy mode that ignored the config and hardcoded `vite build`, so an app that
|
|
6169
|
+
* needs more than one build step — an SSG that emits the site, then Void's build
|
|
6170
|
+
* packaging it via `publicDir` alongside the worker — had no way to express it
|
|
6171
|
+
* and died on the missing `index.html` entry.
|
|
6172
|
+
*
|
|
6173
|
+
* The two returned strings are deliberately different for the FALLBACK: `label`
|
|
6174
|
+
* goes to the user (`formatProjectCommand` rewrites `vite` → `vp` without
|
|
6175
|
+
* expanding an absolute bin path), `command` is what actually runs
|
|
6176
|
+
* (`resolveProjectCommand` expands to the local bin). A configured command is
|
|
6177
|
+
* the user's own text and is used verbatim for both, so what is printed is
|
|
6178
|
+
* exactly what is executed.
|
|
6179
|
+
*/
|
|
6180
|
+
function resolveFullDeployBuildCommand(root, buildOverride) {
|
|
6181
|
+
if (buildOverride) return {
|
|
6182
|
+
label: buildOverride,
|
|
6183
|
+
command: buildOverride
|
|
6184
|
+
};
|
|
6185
|
+
return {
|
|
6186
|
+
label: formatProjectCommand(root, "vite build"),
|
|
6187
|
+
command: resolveProjectCommand(root, "vite build")
|
|
6188
|
+
};
|
|
6189
|
+
}
|
|
6190
|
+
/**
|
|
6191
|
+
* Whether the built worker's `__voidCollectPrerenderPaths` can return anything
|
|
6192
|
+
* at all. When it cannot, booting it is pure cost and the deploy skips it.
|
|
6193
|
+
*
|
|
6194
|
+
* These three are exactly — and only — the sources `compilePrerenderCollector`
|
|
6195
|
+
* (`router/compile.ts`) emits a `paths.push(...)` for:
|
|
6196
|
+
* 1. `scan.pages.pages` — static patterns + dynamic pages' `getPrerenderPaths()`
|
|
6197
|
+
* 2. the custom SSR entry's top-level `getPrerenderPaths()` (`ssr` argument)
|
|
6198
|
+
* 3. `options.explicitPrerenderPaths` — void.json `routing.prerender`
|
|
6199
|
+
* With none of them present the collector is a generated function whose body
|
|
6200
|
+
* returns `[...new Set([])]`, so skipping is result-identical.
|
|
6201
|
+
*
|
|
6202
|
+
* Skipping matters beyond speed. `collectPrerenderPaths` boots the worker in
|
|
6203
|
+
* Miniflare with bindings from `wranglerVars + loadEnv('production', root, '')`
|
|
6204
|
+
* only, and the generated fetch handler validates the `env.ts` schema at boot.
|
|
6205
|
+
* An app whose secrets live on the platform (never in a committed `.env`) gets
|
|
6206
|
+
* an HTTP 500 there and the deploy dies — for an app that had nothing to
|
|
6207
|
+
* prerender in the first place. The only workaround was moving the secret into
|
|
6208
|
+
* `.env.production`, which `loadDeployEnv` then publishes as a plaintext
|
|
6209
|
+
* manifest var. So this guard also removes a real incentive to leak secrets.
|
|
6210
|
+
*/
|
|
6211
|
+
function hasPrerenderSources(inputs) {
|
|
6212
|
+
return inputs.hasPages || inputs.hasSsrEntry || (inputs.explicitPrerenderPaths?.length ?? 0) > 0;
|
|
6213
|
+
}
|
|
5908
6214
|
function resolveStaticDeployPreset(root, voidConfig, options) {
|
|
5909
6215
|
if (options?.dir) return detectPreset(root, {
|
|
5910
6216
|
dir: options.dir,
|
|
@@ -6165,7 +6471,7 @@ function assembleVoidManifest(pageScan, isFrameworkMode, opts, revalidateConfig)
|
|
|
6165
6471
|
pathRevalidate: isFrameworkMode || revalidateConfig == null || typeof revalidateConfig !== "object" ? void 0 : revalidateConfig
|
|
6166
6472
|
});
|
|
6167
6473
|
}
|
|
6168
|
-
async function runStaticDeploy(root, config, client, preset, skipBuild, gitMetadata, hashedAssetsPrefix, headerRules, redirectRules, fallbackRules, routing, cliLog) {
|
|
6474
|
+
async function runStaticDeploy(root, config, client, preset, skipBuild, gitMetadata, hashedAssetsPrefix, headerRules, redirectRules, fallbackRules, routing, cliLog, notFound) {
|
|
6169
6475
|
const typeLabel = preset.appType === "spa" ? "Static SPA" : "Static Site";
|
|
6170
6476
|
log.info(`${typeLabel} deploy`);
|
|
6171
6477
|
cliLog?.info("deploy_mode", {
|
|
@@ -6179,7 +6485,7 @@ async function runStaticDeploy(root, config, client, preset, skipBuild, gitMetad
|
|
|
6179
6485
|
execSync(preset.buildCommand, {
|
|
6180
6486
|
cwd: root,
|
|
6181
6487
|
stdio: "inherit",
|
|
6182
|
-
env: buildEnv()
|
|
6488
|
+
env: buildEnv({ ...MANAGED_DEPLOY_BUILD_ENV }, { localBinRoot: root })
|
|
6183
6489
|
});
|
|
6184
6490
|
cliLog?.info("build_end", { durationMs: Date.now() - buildStartedAt });
|
|
6185
6491
|
} else if (skipBuild) cliLog?.info("build_skipped", { reason: "skipBuild" });
|
|
@@ -6223,7 +6529,8 @@ async function runStaticDeploy(root, config, client, preset, skipBuild, gitMetad
|
|
|
6223
6529
|
hashedAssetsPrefix: hashedAssetsPrefix ?? "assets",
|
|
6224
6530
|
headerRules,
|
|
6225
6531
|
redirectRules,
|
|
6226
|
-
fallbackRules
|
|
6532
|
+
fallbackRules,
|
|
6533
|
+
notFound: notFound ?? routing?.notFound
|
|
6227
6534
|
});
|
|
6228
6535
|
cliLog?.info("package_end", {
|
|
6229
6536
|
mode: "static",
|
|
@@ -6257,6 +6564,27 @@ function warnRedundantForceOn3xx(count) {
|
|
|
6257
6564
|
log.warn(`Found ${count} redundant "!" on 3xx entries in _redirects (these always force by nature; safe to remove).`);
|
|
6258
6565
|
}
|
|
6259
6566
|
/**
|
|
6567
|
+
* Class B/C framework deploys pin `not_found_handling: 'none'`, so
|
|
6568
|
+
* `routing.notFound` does nothing there — say so instead of dropping it
|
|
6569
|
+
* silently.
|
|
6570
|
+
*
|
|
6571
|
+
* These presets emit no `run_worker_first`, so Cloudflare's default applies and
|
|
6572
|
+
* the asset layer answers before the framework worker: real prerendered files
|
|
6573
|
+
* already win. `not_found_handling` only governs what the framework's own
|
|
6574
|
+
* `env.ASSETS.fetch()` delegation returns, which is framework-internal
|
|
6575
|
+
* plumbing. Honoring the override there would be a behavior change, not a fix —
|
|
6576
|
+
* `single-page-application` makes an unmatched delegation return
|
|
6577
|
+
* `dist/client/index.html` at **200** (Astro/SvelteKit hybrid builds commonly
|
|
6578
|
+
* prerender the root, so real 404s silently become the home page), and
|
|
6579
|
+
* `404-page` steals the 404 from the framework's own error route.
|
|
6580
|
+
*
|
|
6581
|
+
* Silenced when unset.
|
|
6582
|
+
*/
|
|
6583
|
+
function warnIgnoredNotFoundForFramework(notFound, frameworkName) {
|
|
6584
|
+
if (notFound === void 0) return;
|
|
6585
|
+
log.warn(`"routing.notFound" in void.json is ignored for ${frameworkName} deploys (not_found_handling is pinned to "none" — the framework's own worker owns unmatched HTML).`);
|
|
6586
|
+
}
|
|
6587
|
+
/**
|
|
6260
6588
|
* Class B/C framework deploy: build with framework CLI, package with preset output paths.
|
|
6261
6589
|
*/
|
|
6262
6590
|
async function runFrameworkDeploy(root, config, client, detected, fwPreset, voidConfig, skipBuild, gitMetadata, cliLog) {
|
|
@@ -6274,7 +6602,7 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
|
|
|
6274
6602
|
execSync(voidConfig.inference?.build ?? resolveProjectCommand(root, fwPreset.buildCommand), {
|
|
6275
6603
|
cwd: root,
|
|
6276
6604
|
stdio: "inherit",
|
|
6277
|
-
env: buildEnv()
|
|
6605
|
+
env: buildEnv({ ...MANAGED_DEPLOY_BUILD_ENV }, { localBinRoot: root })
|
|
6278
6606
|
});
|
|
6279
6607
|
cliLog?.info("build_end", { durationMs: Date.now() - buildStartedAt });
|
|
6280
6608
|
} else cliLog?.info("build_skipped", { reason: "skipBuild" });
|
|
@@ -6286,6 +6614,7 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
|
|
|
6286
6614
|
routing: voidConfig.routing
|
|
6287
6615
|
});
|
|
6288
6616
|
warnRedundantForceOn3xx(redundantForceOn3xx);
|
|
6617
|
+
warnIgnoredNotFoundForFramework(voidConfig.routing?.notFound, detected.name);
|
|
6289
6618
|
lintDuplicateSources(redirectRules, "redirects/rewrites (pre-asset)");
|
|
6290
6619
|
lintDuplicateSources(fallbackRules, "fallbacks (post-asset)");
|
|
6291
6620
|
lintDestinationSplats(redirectRules, "redirects/rewrites (pre-asset)");
|
|
@@ -6414,7 +6743,8 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
|
|
|
6414
6743
|
bindingNames: resolveBindingNames(voidConfig.inference?.bindings),
|
|
6415
6744
|
workerMain: actualWorkerMain,
|
|
6416
6745
|
compatibilityDate: wranglerCompat?.compatibilityDate,
|
|
6417
|
-
compatibilityFlags: wranglerCompat?.compatibilityFlags
|
|
6746
|
+
compatibilityFlags: wranglerCompat?.compatibilityFlags,
|
|
6747
|
+
cpuMs: voidConfig.worker?.limits?.cpu_ms
|
|
6418
6748
|
};
|
|
6419
6749
|
const manifest = buildFrameworkManifest({
|
|
6420
6750
|
frameworkName: detected.name,
|
|
@@ -6454,21 +6784,24 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
|
|
|
6454
6784
|
warnUnpublishedRouting(result);
|
|
6455
6785
|
outro("Done!");
|
|
6456
6786
|
}
|
|
6457
|
-
async function runFullDeploy(root, config, client, fwPreset, skipBuild, gitMetadata, detected, cliLog) {
|
|
6787
|
+
async function runFullDeploy(root, config, client, voidConfig, fwPreset, skipBuild, gitMetadata, detected, cliLog) {
|
|
6458
6788
|
cliLog?.info("deploy_mode", {
|
|
6459
6789
|
mode: "full",
|
|
6460
6790
|
framework: detected?.name
|
|
6461
6791
|
});
|
|
6462
6792
|
if (!skipBuild) {
|
|
6463
|
-
const buildCmd =
|
|
6793
|
+
const { label: buildCmd, command: buildExec } = resolveFullDeployBuildCommand(root, voidConfig.inference?.build);
|
|
6464
6794
|
log.step(`Building (${buildCmd})...`);
|
|
6465
6795
|
cliLog?.info("build_start", { command: buildCmd });
|
|
6466
6796
|
const buildStartedAt = Date.now();
|
|
6467
6797
|
try {
|
|
6468
|
-
execSync(
|
|
6798
|
+
execSync(buildExec, {
|
|
6469
6799
|
cwd: root,
|
|
6470
6800
|
stdio: "inherit",
|
|
6471
|
-
env: buildEnv({
|
|
6801
|
+
env: buildEnv({
|
|
6802
|
+
VOID_DEPLOY_PROJECT_ID: config.projectId,
|
|
6803
|
+
...MANAGED_DEPLOY_BUILD_ENV
|
|
6804
|
+
}, { localBinRoot: root })
|
|
6472
6805
|
});
|
|
6473
6806
|
cliLog?.info("build_end", { durationMs: Date.now() - buildStartedAt });
|
|
6474
6807
|
} catch (err) {
|
|
@@ -6489,7 +6822,7 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, gitMetad
|
|
|
6489
6822
|
}
|
|
6490
6823
|
}
|
|
6491
6824
|
const isFrameworkMode = detected != null;
|
|
6492
|
-
const deployConfig =
|
|
6825
|
+
const deployConfig = voidConfig;
|
|
6493
6826
|
const paths = resolveProjectPaths(root, deployConfig);
|
|
6494
6827
|
const dialect = getDatabaseDialect(deployConfig);
|
|
6495
6828
|
const deployScanDirs = deployConfig.inference?.scanDirs ?? (isFrameworkMode ? FRAMEWORK_SCAN_DIRS : void 0);
|
|
@@ -6578,9 +6911,10 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, gitMetad
|
|
|
6578
6911
|
})));
|
|
6579
6912
|
const routeScan = await scanRoutes(root, {
|
|
6580
6913
|
output: deployConfig.output,
|
|
6581
|
-
paths
|
|
6914
|
+
paths,
|
|
6915
|
+
environment: "prod"
|
|
6582
6916
|
});
|
|
6583
|
-
const webSockets = routeScan.websockets ?? scanWebSocketRoutesSync(root, paths);
|
|
6917
|
+
const webSockets = routeScan.websockets ?? scanWebSocketRoutesSync(root, paths, "prod");
|
|
6584
6918
|
const liveConfig = await resolveDeployLiveBindingConfig(root, client, config.projectId, {
|
|
6585
6919
|
isFrameworkMode,
|
|
6586
6920
|
needsLive: effectiveBindings.needsLive,
|
|
@@ -6600,7 +6934,8 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, gitMetad
|
|
|
6600
6934
|
const hasDocumentRoutes$1 = hasDocumentRoutes(routeScan.routes);
|
|
6601
6935
|
const hasMiddleware = routeScan.middleware.length > 0;
|
|
6602
6936
|
const hasWebSockets = webSockets.length > 0;
|
|
6603
|
-
const
|
|
6937
|
+
const ssrEntry = isFrameworkMode ? null : validateSsrEntry(root, paths);
|
|
6938
|
+
const isSsr = isFrameworkMode ? true : Boolean(ssrEntry) || hasPages && deployConfig.output !== "static";
|
|
6604
6939
|
const pageScan = existsSync(paths.pagesDir) ? routeScan.pages.pages.length > 0 ? routeScan.pages : await scanPages(root, {
|
|
6605
6940
|
output: deployConfig.output,
|
|
6606
6941
|
paths
|
|
@@ -6614,10 +6949,15 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, gitMetad
|
|
|
6614
6949
|
buildCommand: null,
|
|
6615
6950
|
outputDir: clientDir,
|
|
6616
6951
|
appType: "static"
|
|
6617
|
-
}, true, gitMetadata, assetsPrefix, headerRules, redirectRules, fallbackRules, void 0, cliLog);
|
|
6952
|
+
}, true, gitMetadata, assetsPrefix, headerRules, redirectRules, fallbackRules, void 0, cliLog, deployConfig.routing?.notFound);
|
|
6618
6953
|
}
|
|
6619
6954
|
}
|
|
6620
|
-
const
|
|
6955
|
+
const prerenderSourcesPresent = hasPrerenderSources({
|
|
6956
|
+
hasPages,
|
|
6957
|
+
hasSsrEntry: ssrEntry !== null,
|
|
6958
|
+
explicitPrerenderPaths: deployConfig.routing?.prerender
|
|
6959
|
+
});
|
|
6960
|
+
const uniquePrerenderPaths = deployConfig.output === "static" || isFrameworkMode || !prerenderSourcesPresent ? [] : isNodeTarget(deployConfig.target) ? await collectPrerenderPathsNode({
|
|
6621
6961
|
root,
|
|
6622
6962
|
ssrDir: join(distDir, "ssr"),
|
|
6623
6963
|
clientDir
|
|
@@ -6648,7 +6988,8 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, gitMetad
|
|
|
6648
6988
|
hasMiddleware,
|
|
6649
6989
|
hasWebSockets: hasDocumentRoutes(webSockets),
|
|
6650
6990
|
authEnabled,
|
|
6651
|
-
liveEnabled: liveConfig !== void 0
|
|
6991
|
+
liveEnabled: liveConfig !== void 0,
|
|
6992
|
+
notFound: deployConfig.routing?.notFound
|
|
6652
6993
|
});
|
|
6653
6994
|
s.start("Checking for changes...");
|
|
6654
6995
|
cliLog?.info("preflight_start", { dir: clientDir });
|
|
@@ -6676,6 +7017,7 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, gitMetad
|
|
|
6676
7017
|
const voidPackageOptions = {
|
|
6677
7018
|
compatibilityDate: wranglerCompat?.compatibilityDate,
|
|
6678
7019
|
compatibilityFlags: wranglerCompat?.compatibilityFlags,
|
|
7020
|
+
cpuMs: deployConfig.worker?.limits?.cpu_ms,
|
|
6679
7021
|
...workerMain && { workerMain },
|
|
6680
7022
|
...sandboxConfig && { sandbox: sandboxConfig },
|
|
6681
7023
|
...liveConfig && { live: liveConfig },
|
|
@@ -6744,7 +7086,7 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, gitMetad
|
|
|
6744
7086
|
outro("Done!");
|
|
6745
7087
|
}
|
|
6746
7088
|
async function createProjectFromSlug(root, client, slug, cliLog) {
|
|
6747
|
-
log.step(`Creating project ${import_picocolors.default.blue(slug)}...`);
|
|
7089
|
+
log.step(`Creating project ${import_picocolors$1.default.blue(slug)}...`);
|
|
6748
7090
|
cliLog?.info("project_create_start", { slug });
|
|
6749
7091
|
const project = await client.createProject(slug);
|
|
6750
7092
|
const config = {
|
|
@@ -6752,7 +7094,7 @@ async function createProjectFromSlug(root, client, slug, cliLog) {
|
|
|
6752
7094
|
slug: project.slug
|
|
6753
7095
|
};
|
|
6754
7096
|
writeProjectConfig(root, config);
|
|
6755
|
-
log.success(`Project created: ${import_picocolors.default.blue(config.slug)}`);
|
|
7097
|
+
log.success(`Project created: ${import_picocolors$1.default.blue(config.slug)}`);
|
|
6756
7098
|
cliLog?.info("project_create_success", {
|
|
6757
7099
|
projectId: config.projectId,
|
|
6758
7100
|
slug: config.slug
|