void 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { c as R } from "./dist-Dayj3gCK.mjs";
2
- import { n as PlatformClient, s as getToken, u as removeToken } from "./client-snXOjrp1.mjs";
3
- import { n as runLoginCommand } from "./login-CkcXUiIu.mjs";
2
+ import { n as PlatformClient, s as getToken, u as removeToken } from "./client-BUdfE3QJ.mjs";
3
+ import { n as runLoginCommand } from "./login-RWUDCfdx.mjs";
4
4
  import { execSync } from "node:child_process";
5
5
  //#region src/cli/auth-cmd.ts
6
6
  async function runAuthCommand(root, args) {
@@ -1,7 +1,7 @@
1
1
  import { i as dim, n as cliTitle, r as createSpinner } from "./output-BwlcIYSR.mjs";
2
2
  import { c as R, g as ge, y as ye } from "./dist-Dayj3gCK.mjs";
3
3
  import { r as readProjectConfig } from "./project-TqORyHn8.mjs";
4
- import { n as PlatformClient, s as getToken } from "./client-snXOjrp1.mjs";
4
+ import { n as PlatformClient, s as getToken } from "./client-BUdfE3QJ.mjs";
5
5
  import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project-Br5BR03U.mjs";
6
6
  //#region src/cli/cache.ts
7
7
  async function runPurgeCacheCommand(root, projectSlug) {
@@ -1,7 +1,7 @@
1
1
  import { n as cliTitle } from "./output-BwlcIYSR.mjs";
2
2
  import { c as R, g as ge, v as ue, x as q, y as ye } from "./dist-Dayj3gCK.mjs";
3
3
  import { r as readProjectConfig } from "./project-TqORyHn8.mjs";
4
- import { n as PlatformClient, s as getToken } from "./client-snXOjrp1.mjs";
4
+ import { n as PlatformClient, s as getToken } from "./client-BUdfE3QJ.mjs";
5
5
  //#region src/cli/cancel-deploy.ts
6
6
  const ACTIVE_STATUSES = new Set([
7
7
  "pending",
package/dist/cli/cli.mjs CHANGED
@@ -162,7 +162,8 @@ function parseDeployArgs(args) {
162
162
  project: { type: "string" },
163
163
  dir: { type: "string" },
164
164
  spa: { type: "boolean" },
165
- "skip-build": { type: "boolean" }
165
+ "skip-build": { type: "boolean" },
166
+ debug: { type: "boolean" }
166
167
  },
167
168
  allowPositionals: true,
168
169
  strict: true,
@@ -184,7 +185,8 @@ function parseDeployArgs(args) {
184
185
  projectSlug,
185
186
  dir: typeof parsed.values.dir === "string" ? parsed.values.dir : void 0,
186
187
  spa: parsed.values.spa === true ? true : void 0,
187
- skipBuild: parsed.values["skip-build"] === true ? true : void 0
188
+ skipBuild: parsed.values["skip-build"] === true ? true : void 0,
189
+ debug: parsed.values.debug === true ? true : void 0
188
190
  };
189
191
  }
190
192
  //#endregion
@@ -768,6 +770,10 @@ const HELP_TREE = {
768
770
  {
769
771
  command: "--skip-build",
770
772
  description: "Skip the build step and use existing output"
773
+ },
774
+ {
775
+ command: "--debug",
776
+ description: "Mirror the deploy log to stderr (always written to ~/.void/logs)"
771
777
  }
772
778
  ],
773
779
  examples: [
@@ -1644,23 +1650,24 @@ else if (command === "deploy") {
1644
1650
  R.error(err instanceof Error ? err.message : String(err));
1645
1651
  process.exit(1);
1646
1652
  }
1647
- import("../deploy-BPKblFx6.mjs").then((n) => n.t).then(({ runDeploy }) => {
1653
+ import("../deploy-BqXz1ycW.mjs").then((n) => n.t).then(({ runDeploy }) => {
1648
1654
  runDeploy(root, {
1649
1655
  projectSlug: deployArgs.projectSlug,
1650
1656
  dir: deployArgs.dir,
1651
1657
  spa: deployArgs.spa,
1652
- skipBuild: deployArgs.skipBuild
1658
+ skipBuild: deployArgs.skipBuild,
1659
+ debug: deployArgs.debug
1653
1660
  }).catch((err) => {
1654
1661
  R.error(err.message ?? err);
1655
1662
  process.exit(1);
1656
1663
  });
1657
1664
  });
1658
- } else if (command === "auth") runSubcommandGroup("auth", authSubcommands, parseAuthArgs, (root, parsed) => import("../auth-cmd-Dx8oPKZC.mjs").then(({ runAuthCommand }) => runAuthCommand(root, parsed)));
1659
- else if (command === "project") runSubcommandGroup("project", projectSubcommands, parseProjectArgs, (root, parsed) => import("../project-cmd-B7lQp3F3.mjs").then(({ runProjectCommand }) => runProjectCommand(root, parsed)));
1660
- else if (command === "secret") runSubcommandGroup("secret", secretSubcommands, parseSecretArgs, (root, parsed) => import("../secret-CeRSukgM.mjs").then(({ runSecretCommand }) => runSecretCommand(root, parsed)));
1661
- else if (command === "domain") runSubcommandGroup("domain", domainSubcommands, parseDomainArgs, (root, parsed) => import("../domain-BGofcQ6I.mjs").then(({ runDomainCommand }) => runDomainCommand(root, parsed)));
1662
- else if (command === "db") runSubcommandGroup("db", dbSubcommands, parseDbArgs, (root, parsed) => import("../db-DsRoMcfN.mjs").then(({ runDbCommand }) => runDbCommand(root, parsed)));
1663
- else if (command === "env") runSubcommandGroup("env", envSubcommands, parseEnvArgs, (root, parsed) => import("../env-CyG3tvU0.mjs").then(({ runEnvCommand }) => runEnvCommand(root, parsed)));
1665
+ } else if (command === "auth") runSubcommandGroup("auth", authSubcommands, parseAuthArgs, (root, parsed) => import("../auth-cmd-DVKi6dzh.mjs").then(({ runAuthCommand }) => runAuthCommand(root, parsed)));
1666
+ else if (command === "project") runSubcommandGroup("project", projectSubcommands, parseProjectArgs, (root, parsed) => import("../project-cmd-ATFi3kRm.mjs").then(({ runProjectCommand }) => runProjectCommand(root, parsed)));
1667
+ else if (command === "secret") runSubcommandGroup("secret", secretSubcommands, parseSecretArgs, (root, parsed) => import("../secret-DmjBDxB1.mjs").then(({ runSecretCommand }) => runSecretCommand(root, parsed)));
1668
+ else if (command === "domain") runSubcommandGroup("domain", domainSubcommands, parseDomainArgs, (root, parsed) => import("../domain-B-fIU3VE.mjs").then(({ runDomainCommand }) => runDomainCommand(root, parsed)));
1669
+ else if (command === "db") runSubcommandGroup("db", dbSubcommands, parseDbArgs, (root, parsed) => import("../db-BIP2kuEt.mjs").then(({ runDbCommand }) => runDbCommand(root, parsed)));
1670
+ else if (command === "env") runSubcommandGroup("env", envSubcommands, parseEnvArgs, (root, parsed) => import("../env-BwbZJd2x.mjs").then(({ runEnvCommand }) => runEnvCommand(root, parsed)));
1664
1671
  else if (command === "gen") runSubcommandGroup("gen", genSubcommands, parseGenArgs, (root, parsed) => import("../gen-U0Ktr4Zd.mjs").then(({ runGenCommand }) => runGenCommand(root, parsed)));
1665
1672
  else if (command === "prepare") {
1666
1673
  const root = process.cwd();
@@ -1679,7 +1686,7 @@ else if (command === "prepare") {
1679
1686
  });
1680
1687
  } else if (command === "init") {
1681
1688
  const root = process.cwd();
1682
- import("../init-C7wS5iGP.mjs").then(({ runInitCommand, parseInitArgs }) => {
1689
+ import("../init-Bb_Qsdq6.mjs").then(({ runInitCommand, parseInitArgs }) => {
1683
1690
  let initArgs;
1684
1691
  try {
1685
1692
  initArgs = parseInitArgs(args.slice(1));
@@ -1692,7 +1699,7 @@ else if (command === "prepare") {
1692
1699
  process.exit(1);
1693
1700
  });
1694
1701
  });
1695
- } else if (command === "mcp") import("../mcp-CaQzfeUi.mjs").then(({ runMcp }) => {
1702
+ } else if (command === "mcp") import("../mcp-kZ4zg13a.mjs").then(({ runMcp }) => {
1696
1703
  runMcp();
1697
1704
  });
1698
1705
  else if (command === "-v" || command === "--version") {
@@ -1768,26 +1775,26 @@ else if (command === "-v" || command === "--version") {
1768
1775
  const root = process.cwd();
1769
1776
  switch (cmd) {
1770
1777
  case "init": {
1771
- const { runInitCommand, parseInitArgs } = await import("../init-C7wS5iGP.mjs");
1778
+ const { runInitCommand, parseInitArgs } = await import("../init-Bb_Qsdq6.mjs");
1772
1779
  await runInitCommand(root, parseInitArgs([]));
1773
1780
  return;
1774
1781
  }
1775
1782
  case "deploy": {
1776
- const { runDeploy } = await import("../deploy-BPKblFx6.mjs").then((n) => n.t);
1783
+ const { runDeploy } = await import("../deploy-BqXz1ycW.mjs").then((n) => n.t);
1777
1784
  await runDeploy(root);
1778
1785
  return;
1779
1786
  }
1780
1787
  case "mcp": {
1781
- const { runMcp } = await import("../mcp-CaQzfeUi.mjs");
1788
+ const { runMcp } = await import("../mcp-kZ4zg13a.mjs");
1782
1789
  runMcp();
1783
1790
  return;
1784
1791
  }
1785
- case "auth": return runSubcommandGroup("auth", authSubcommands, parseAuthArgs, (r, p) => import("../auth-cmd-Dx8oPKZC.mjs").then(({ runAuthCommand }) => runAuthCommand(r, p)));
1786
- case "project": return runSubcommandGroup("project", projectSubcommands, parseProjectArgs, (r, p) => import("../project-cmd-B7lQp3F3.mjs").then(({ runProjectCommand }) => runProjectCommand(r, p)));
1787
- case "secret": return runSubcommandGroup("secret", secretSubcommands, parseSecretArgs, (r, p) => import("../secret-CeRSukgM.mjs").then(({ runSecretCommand }) => runSecretCommand(r, p)));
1788
- case "domain": return runSubcommandGroup("domain", domainSubcommands, parseDomainArgs, (r, p) => import("../domain-BGofcQ6I.mjs").then(({ runDomainCommand }) => runDomainCommand(r, p)));
1789
- case "db": return runSubcommandGroup("db", dbSubcommands, parseDbArgs, (r, p) => import("../db-DsRoMcfN.mjs").then(({ runDbCommand }) => runDbCommand(r, p)));
1790
- case "env": return runSubcommandGroup("env", envSubcommands, parseEnvArgs, (r, p) => import("../env-CyG3tvU0.mjs").then(({ runEnvCommand }) => runEnvCommand(r, p)));
1792
+ case "auth": return runSubcommandGroup("auth", authSubcommands, parseAuthArgs, (r, p) => import("../auth-cmd-DVKi6dzh.mjs").then(({ runAuthCommand }) => runAuthCommand(r, p)));
1793
+ case "project": return runSubcommandGroup("project", projectSubcommands, parseProjectArgs, (r, p) => import("../project-cmd-ATFi3kRm.mjs").then(({ runProjectCommand }) => runProjectCommand(r, p)));
1794
+ case "secret": return runSubcommandGroup("secret", secretSubcommands, parseSecretArgs, (r, p) => import("../secret-DmjBDxB1.mjs").then(({ runSecretCommand }) => runSecretCommand(r, p)));
1795
+ case "domain": return runSubcommandGroup("domain", domainSubcommands, parseDomainArgs, (r, p) => import("../domain-B-fIU3VE.mjs").then(({ runDomainCommand }) => runDomainCommand(r, p)));
1796
+ case "db": return runSubcommandGroup("db", dbSubcommands, parseDbArgs, (r, p) => import("../db-BIP2kuEt.mjs").then(({ runDbCommand }) => runDbCommand(r, p)));
1797
+ case "env": return runSubcommandGroup("env", envSubcommands, parseEnvArgs, (r, p) => import("../env-BwbZJd2x.mjs").then(({ runEnvCommand }) => runEnvCommand(r, p)));
1791
1798
  case "gen": return runSubcommandGroup("gen", genSubcommands, parseGenArgs, (r, p) => import("../gen-U0Ktr4Zd.mjs").then(({ runGenCommand }) => runGenCommand(r, p)));
1792
1799
  case "prepare": {
1793
1800
  const { runPrepareCommand } = await import("../prepare-BAtWufvm.mjs");
@@ -303,9 +303,13 @@ function isExpiredTokenError(error) {
303
303
  var PlatformClient = class {
304
304
  baseUrl;
305
305
  authHeaders;
306
+ cliLog;
306
307
  constructor(token, options) {
307
308
  if (typeof options === "string") this.baseUrl = options;
308
- else this.baseUrl = options?.apiUrl ?? getApiUrl(options?.root);
309
+ else {
310
+ this.baseUrl = options?.apiUrl ?? getApiUrl(options?.root);
311
+ this.cliLog = options?.cliLog;
312
+ }
309
313
  ensureCloudflaredToken(this.baseUrl);
310
314
  this.authHeaders = {
311
315
  Authorization: `Bearer ${token}`,
@@ -395,25 +399,78 @@ var PlatformClient = class {
395
399
  }
396
400
  }
397
401
  async *deploy(projectId, formData) {
398
- const res = await fetch(`${this.baseUrl}/projects/${projectId}/deploy`, {
399
- method: "POST",
400
- headers: { ...this.authHeaders },
401
- body: formData
402
+ const cliLog = this.cliLog;
403
+ const url = `${this.baseUrl}/projects/${projectId}/deploy`;
404
+ cliLog?.info("deploy_post", { url });
405
+ let res;
406
+ try {
407
+ res = await fetch(url, {
408
+ method: "POST",
409
+ headers: { ...this.authHeaders },
410
+ body: formData
411
+ });
412
+ } catch (err) {
413
+ cliLog?.error("deploy_fetch_rejected", err, { url });
414
+ throw err;
415
+ }
416
+ const cfRay = res.headers.get("cf-ray");
417
+ const cfCacheStatus = res.headers.get("cf-cache-status");
418
+ cliLog?.info("deploy_response", {
419
+ status: res.status,
420
+ cfRay,
421
+ cfCacheStatus,
422
+ contentType: res.headers.get("content-type")
402
423
  });
403
424
  if (!res.ok) await throwPlatformApiError("Deploy failed", res);
404
425
  const reader = res.body.getReader();
405
426
  const decoder = new TextDecoder();
406
427
  let buffer = "";
407
- while (true) {
408
- const { done, value } = await reader.read();
409
- if (done) break;
410
- buffer += decoder.decode(value, { stream: true });
411
- let newlineIdx;
412
- while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
413
- const line = buffer.slice(0, newlineIdx);
414
- buffer = buffer.slice(newlineIdx + 1);
415
- if (line.trim()) yield JSON.parse(line);
428
+ let bytesRead = 0;
429
+ let firstByteAt = null;
430
+ const startedAt = Date.now();
431
+ let eventCount = 0;
432
+ let lastEventName = "<none>";
433
+ try {
434
+ while (true) {
435
+ const { done, value } = await reader.read();
436
+ if (done) {
437
+ cliLog?.info("deploy_stream_eof", {
438
+ bytesRead,
439
+ eventCount,
440
+ lastEvent: lastEventName
441
+ });
442
+ break;
443
+ }
444
+ if (firstByteAt === null) {
445
+ firstByteAt = Date.now() - startedAt;
446
+ cliLog?.info("deploy_stream_ttfb", { ttfbMs: firstByteAt });
447
+ }
448
+ bytesRead += value.byteLength;
449
+ buffer += decoder.decode(value, { stream: true });
450
+ let newlineIdx;
451
+ while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
452
+ const line = buffer.slice(0, newlineIdx);
453
+ buffer = buffer.slice(newlineIdx + 1);
454
+ if (line.trim()) {
455
+ const event = JSON.parse(line);
456
+ eventCount++;
457
+ lastEventName = event.event ?? "<no-event-field>";
458
+ cliLog?.info("deploy_event", {
459
+ eventCount,
460
+ event: lastEventName,
461
+ raw: line.slice(0, 200)
462
+ });
463
+ yield event;
464
+ }
465
+ }
416
466
  }
467
+ } catch (err) {
468
+ cliLog?.error("deploy_stream_error", err, {
469
+ bytesRead,
470
+ eventCount,
471
+ lastEvent: lastEventName
472
+ });
473
+ throw err;
417
474
  }
418
475
  }
419
476
  async listSecrets(projectId) {
@@ -1,7 +1,7 @@
1
1
  import { s as import_picocolors } from "./output-BwlcIYSR.mjs";
2
2
  import { _ as me, c as R, i as Ee, l as Re, x as q } from "./dist-Dayj3gCK.mjs";
3
3
  import { a as writeProjectConfig } from "./project-TqORyHn8.mjs";
4
- import { o as getSiteDomain, t as PlatformApiError } from "./client-snXOjrp1.mjs";
4
+ import { o as getSiteDomain, t as PlatformApiError } from "./client-BUdfE3QJ.mjs";
5
5
  import { n as validateProjectSlug } from "./project-slug-CKam8lF9.mjs";
6
6
  import { n as inferDefaultSlug } from "./resolve-project-Br5BR03U.mjs";
7
7
  //#region src/cli/prompt.ts
@@ -2,7 +2,7 @@ import { a as join, o as relative, r as extname, s as resolve } from "./pathe.M-
2
2
  import { i as dim, n as cliTitle, s as import_picocolors } from "./output-BwlcIYSR.mjs";
3
3
  import { c as R, g as ge, y as ye } from "./dist-Dayj3gCK.mjs";
4
4
  import { r as readProjectConfig } from "./project-TqORyHn8.mjs";
5
- import { n as PlatformClient, s as getToken } from "./client-snXOjrp1.mjs";
5
+ import { n as PlatformClient, s as getToken } from "./client-BUdfE3QJ.mjs";
6
6
  import { c as getDatabaseDialect, f as readConfig } from "./config-BIa9HwVX.mjs";
7
7
  import { n as detectFramework } from "./plugin-inference-oZ6Ybu2_.mjs";
8
8
  import { a as writeJournal, i as stripSqlExt, r as readJournal, t as collectMigrations } from "./collect-CjeZgz5D.mjs";
@@ -1,7 +1,7 @@
1
1
  import { n as cliTitle, r as createSpinner } from "./output-BwlcIYSR.mjs";
2
2
  import { _ as me, c as R, g as ge, l as Re, u as Se, x as q, y as ye } from "./dist-Dayj3gCK.mjs";
3
3
  import { i as removeProjectConfig, r as readProjectConfig } from "./project-TqORyHn8.mjs";
4
- import { n as PlatformClient, s as getToken } from "./client-snXOjrp1.mjs";
4
+ import { n as PlatformClient, s as getToken } from "./client-BUdfE3QJ.mjs";
5
5
  import { r as resolveProjectBySlug } from "./resolve-project-Br5BR03U.mjs";
6
6
  //#region src/cli/delete.ts
7
7
  async function runDeleteCommand(root, slug, options) {
@@ -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 parsePlatformErrorBody, c as getTokenSource, i as isExpiredTokenError, l as isStagingMode, n as PlatformClient, s as getToken, t as PlatformApiError } from "./client-snXOjrp1.mjs";
7
+ import { a as parsePlatformErrorBody, c as getTokenSource, i as isExpiredTokenError, l as isStagingMode, n as PlatformClient, s as getToken, t as PlatformApiError } from "./client-BUdfE3QJ.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,16 +16,16 @@ 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-BIA15W7z.mjs";
19
+ import { n as promptProjectSelection, r as promptProjectSetupAction, t as promptAndCreateProject } from "./create-project-CN1pF-OQ.mjs";
20
20
  import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project-Br5BR03U.mjs";
21
21
  import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-DCXc7mDs.mjs";
22
- import { t as promptForLoginToken } from "./login-CkcXUiIu.mjs";
22
+ import { t as promptForLoginToken } from "./login-RWUDCfdx.mjs";
23
23
  import { i as resolveProjectCommand, n as detectPreset, r as formatProjectCommand, t as FRAMEWORK_PRESETS } from "./preset-D4I73kT4.mjs";
24
24
  import { createRequire } from "node:module";
25
- import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
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";
27
27
  import { build, loadEnv } from "vite";
28
- import { tmpdir } from "node:os";
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
31
  import { hash } from "blake3-jit";
@@ -808,11 +808,17 @@ function trimSnippet(body) {
808
808
  function appendDeploymentLine(message, deploymentId) {
809
809
  return deploymentId ? `${message}\nDeployment: ${deploymentId}` : message;
810
810
  }
811
+ function appendLogLine(message, logPath) {
812
+ return logPath ? `${message}\nDetailed log: ${logPath}` : message;
813
+ }
814
+ function appendTrailingLines(message, deploymentId, logPath) {
815
+ return appendLogLine(appendDeploymentLine(message, deploymentId), logPath);
816
+ }
811
817
  /**
812
818
  * Format a server-emitted `event: 'error'` payload with an optional deployment id.
813
819
  */
814
- function formatDeployFailureMessage(message, deploymentId) {
815
- return appendDeploymentLine(message, deploymentId);
820
+ function formatDeployFailureMessage(message, deploymentId, logPath) {
821
+ return appendTrailingLines(message, deploymentId, logPath);
816
822
  }
817
823
  /**
818
824
  * Format the "stream ended cleanly but no terminal event arrived" case.
@@ -821,8 +827,8 @@ function formatDeployFailureMessage(message, deploymentId) {
821
827
  * deploy" message — the server closed the NDJSON stream without emitting
822
828
  * `done` or `error`, so there is no underlying error to carry.
823
829
  */
824
- function formatConnectionLostMessage(deploymentId) {
825
- return appendDeploymentLine("deploy: Connection lost during deploy.", deploymentId);
830
+ function formatConnectionLostMessage(deploymentId, logPath) {
831
+ return appendTrailingLines("deploy: Connection lost during deploy.", deploymentId, logPath);
826
832
  }
827
833
  /**
828
834
  * Convert an exception thrown during the deploy NDJSON stream into a
@@ -837,18 +843,172 @@ function formatConnectionLostMessage(deploymentId) {
837
843
  * during deploy: <original>` prefix so the caller can still tell network,
838
844
  * parse, and transport errors apart.
839
845
  */
840
- function formatStreamDeployError(err, deploymentId) {
846
+ function formatStreamDeployError(err, deploymentId, logPath) {
841
847
  if (err instanceof PlatformApiError) {
842
848
  const parsed = parsePlatformErrorBody(err.body);
843
849
  const parsedError = parsed?.error ?? parsed?.message ?? null;
844
850
  const effectiveDeploymentId = parsed?.deployment ?? null ?? deploymentId;
845
- if (err.status === 409 && parsedError) return new Error(appendDeploymentLine(`deploy: Deploy failed: ${parsedError}`, effectiveDeploymentId));
851
+ if (err.status === 409 && parsedError) return new Error(appendTrailingLines(`deploy: Deploy failed: ${parsedError}`, effectiveDeploymentId, logPath));
846
852
  const detail = parsedError ?? trimSnippet(err.body);
847
853
  const base = detail ? `deploy: Deploy failed (HTTP ${err.status}): ${detail}` : `deploy: Deploy failed (HTTP ${err.status}).`;
848
- return new Error(appendDeploymentLine(base, effectiveDeploymentId));
854
+ return new Error(appendTrailingLines(base, effectiveDeploymentId, logPath));
849
855
  }
850
856
  const original = err instanceof Error ? err.message : String(err);
851
- return new Error(appendDeploymentLine(`deploy: Connection lost during deploy: '${original}'.`, deploymentId));
857
+ return new Error(appendTrailingLines(`deploy: Connection lost during deploy: '${original}'.`, deploymentId, logPath));
858
+ }
859
+ //#endregion
860
+ //#region src/cli/log-file.ts
861
+ /**
862
+ * Walk `err.cause` up to 6 levels and return a plain JSON-friendly chain.
863
+ * Skips stack traces deliberately — keeps output line-oriented and readable.
864
+ */
865
+ function describeErrorChain(err, depth = 0) {
866
+ if (depth > 5 || err == null) return String(err);
867
+ if (err instanceof Error) {
868
+ const node = {
869
+ name: err.name,
870
+ message: err.message
871
+ };
872
+ const code = err.code;
873
+ if (code !== void 0) node.code = code;
874
+ const errno = err.errno;
875
+ if (errno !== void 0) node.errno = errno;
876
+ const cause = err.cause;
877
+ if (cause != null) {
878
+ const sub = describeErrorChain(cause, depth + 1);
879
+ if (typeof sub === "object") node.cause = sub;
880
+ else node.cause = { message: sub };
881
+ }
882
+ return node;
883
+ }
884
+ return String(err);
885
+ }
886
+ /**
887
+ * Flatten an error and its `cause` chain to a single inline string suitable
888
+ * for stderr skimming. Mirrors `describeErrorChain`'s walk (depth cap 6,
889
+ * same fields, no stack traces) but joins levels with ` -> ` instead of
890
+ * nesting JSON.
891
+ */
892
+ function formatErrorChainInline(err, depth = 0) {
893
+ if (depth > 5 || err == null) return String(err);
894
+ if (err instanceof Error) {
895
+ let head = err.name ? `${err.name}: ${err.message}` : err.message;
896
+ const code = err.code;
897
+ const errno = err.errno;
898
+ const tags = [];
899
+ if (code !== void 0) tags.push(`code=${code}`);
900
+ if (errno !== void 0) tags.push(`errno=${errno}`);
901
+ if (tags.length > 0) head = `${head} (${tags.join(" ")})`;
902
+ const cause = err.cause;
903
+ if (cause != null) return `${head} -> ${formatErrorChainInline(cause, depth + 1)}`;
904
+ return head;
905
+ }
906
+ return String(err);
907
+ }
908
+ function formatStderrLine(level, msg, err, fields) {
909
+ const parts = [level, msg];
910
+ if (fields) for (const [k, v] of Object.entries(fields)) {
911
+ if (v === void 0) continue;
912
+ let s;
913
+ if (v == null || typeof v === "number" || typeof v === "boolean") s = String(v);
914
+ else if (typeof v === "string") s = v.length > 200 ? `${v.slice(0, 200)}...` : v;
915
+ else try {
916
+ s = JSON.stringify(v);
917
+ } catch {
918
+ s = String(v);
919
+ }
920
+ parts.push(`${k}=${s}`);
921
+ }
922
+ if (err !== void 0) parts.push(`-> err=${formatErrorChainInline(err)}`);
923
+ return parts.join(" ");
924
+ }
925
+ function openDeployLog(opts) {
926
+ const mirror = opts?.mirrorToStderr === true;
927
+ const startedAt = Date.now();
928
+ let path = null;
929
+ let stream = null;
930
+ let broken = false;
931
+ let closed = false;
932
+ try {
933
+ const dir = join(homedir(), ".void", "logs");
934
+ mkdirSync(dir, { recursive: true });
935
+ const filePath = join(dir, `deploy-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.jsonl`);
936
+ stream = createWriteStream(filePath, {
937
+ flags: "a",
938
+ autoClose: true
939
+ });
940
+ stream.on("error", () => {
941
+ broken = true;
942
+ });
943
+ path = filePath;
944
+ } catch {
945
+ broken = true;
946
+ path = null;
947
+ stream = null;
948
+ }
949
+ function write(level, msg, err, fields) {
950
+ if (mirror) try {
951
+ process.stderr.write(`${formatStderrLine(level, msg, err, fields)}\n`);
952
+ } catch {}
953
+ if (broken || !stream || closed) return;
954
+ const now = Date.now();
955
+ const record = {
956
+ ts: now,
957
+ elapsedMs: now - startedAt,
958
+ lvl: level,
959
+ msg
960
+ };
961
+ if (fields) {
962
+ for (const [k, v] of Object.entries(fields)) if (v !== void 0 && k !== "ts" && k !== "elapsedMs" && k !== "lvl" && k !== "msg" && k !== "err") record[k] = v;
963
+ }
964
+ if (err !== void 0) record.err = describeErrorChain(err);
965
+ let line;
966
+ try {
967
+ line = `${JSON.stringify(record)}\n`;
968
+ } catch {
969
+ line = `${JSON.stringify({
970
+ ts: now,
971
+ elapsedMs: now - startedAt,
972
+ lvl: level,
973
+ msg,
974
+ err: "unserializable fields"
975
+ })}\n`;
976
+ }
977
+ try {
978
+ stream.write(line);
979
+ } catch {
980
+ broken = true;
981
+ }
982
+ }
983
+ return {
984
+ get path() {
985
+ return path;
986
+ },
987
+ info(msg, fields) {
988
+ write("info", msg, void 0, fields);
989
+ },
990
+ warn(msg, fields) {
991
+ write("warn", msg, void 0, fields);
992
+ },
993
+ error(msg, err, fields) {
994
+ write("error", msg, err, fields);
995
+ },
996
+ debug(msg, fields) {
997
+ write("debug", msg, void 0, fields);
998
+ },
999
+ close() {
1000
+ if (closed) return Promise.resolve();
1001
+ closed = true;
1002
+ if (!stream) return Promise.resolve();
1003
+ return new Promise((resolveClose) => {
1004
+ try {
1005
+ stream.end(() => resolveClose());
1006
+ } catch {
1007
+ resolveClose();
1008
+ }
1009
+ });
1010
+ }
1011
+ };
852
1012
  }
853
1013
  //#endregion
854
1014
  //#region src/shared/utils.ts
@@ -1573,6 +1733,28 @@ function resolveDeployWranglerCompat(root, config, buildOutputDir) {
1573
1733
  async function runDeploy(root, options) {
1574
1734
  console.log();
1575
1735
  ge(cliTitle("deploy"));
1736
+ const cliLog = openDeployLog({ mirrorToStderr: options?.debug === true || process.env.VOID_DEPLOY_DEBUG === "1" });
1737
+ cliLog.info("deploy_start", {
1738
+ cwd: root,
1739
+ projectSlug: options?.projectSlug,
1740
+ dir: options?.dir,
1741
+ spa: options?.spa === true ? true : void 0,
1742
+ skipBuild: options?.skipBuild === true ? true : void 0,
1743
+ nodeVersion: process.version,
1744
+ platform: process.platform,
1745
+ arch: process.arch
1746
+ });
1747
+ try {
1748
+ await runDeployInner(root, options, cliLog);
1749
+ cliLog.info("deploy_end", { ok: true });
1750
+ } catch (err) {
1751
+ cliLog.error("deploy_end", err, { ok: false });
1752
+ throw err;
1753
+ } finally {
1754
+ await cliLog.close().catch(() => {});
1755
+ }
1756
+ }
1757
+ async function runDeployInner(root, options, cliLog) {
1576
1758
  let reauthenticated = false;
1577
1759
  while (true) try {
1578
1760
  const token = getToken(root);
@@ -1580,29 +1762,45 @@ async function runDeploy(root, options) {
1580
1762
  R.error("No auth token found. Run `void auth login` first.");
1581
1763
  process.exit(1);
1582
1764
  }
1583
- const client = new PlatformClient(token, { root });
1765
+ const client = new PlatformClient(token, {
1766
+ root,
1767
+ cliLog
1768
+ });
1584
1769
  const requestedProjectSlug = getRequestedProjectSlug(options);
1585
1770
  let config = null;
1586
1771
  if (requestedProjectSlug) {
1587
1772
  config = resolveProjectBySlug(await client.listProjects(), requestedProjectSlug);
1588
- if (!config) if (!process.stdin.isTTY) config = await createProjectFromSlug(root, client, requestedProjectSlug);
1773
+ if (!config) if (!process.stdin.isTTY) config = await createProjectFromSlug(root, client, requestedProjectSlug, cliLog);
1589
1774
  else {
1590
1775
  const shouldCreate = await ue({ message: `Project "${requestedProjectSlug}" does not exist. Create it?` });
1591
1776
  if (q(shouldCreate) || !shouldCreate) {
1592
1777
  R.info("Deploy cancelled.");
1593
1778
  process.exit(0);
1594
1779
  }
1595
- config = await createProjectFromSlug(root, client, requestedProjectSlug);
1780
+ config = await createProjectFromSlug(root, client, requestedProjectSlug, cliLog);
1596
1781
  }
1597
1782
  } else config = readProjectConfig(root);
1598
1783
  if (!config && !process.stdin.isTTY) {
1599
1784
  R.error("No project specified. Set `VOID_PROJECT`, pass `--project <slug>`, or commit `.void/project.json`.");
1600
1785
  process.exit(1);
1601
1786
  }
1602
- if (!config) config = await promptDeployProjectConfig(root, client);
1787
+ if (!config) config = await promptDeployProjectConfig(root, client, cliLog);
1788
+ cliLog.info("project_resolved", {
1789
+ projectId: config.projectId,
1790
+ slug: config.slug
1791
+ });
1603
1792
  const voidConfig = readConfig(root);
1793
+ cliLog.info("config_resolved", {
1794
+ target: voidConfig.target,
1795
+ appType: voidConfig.inference?.appType,
1796
+ output: voidConfig.output
1797
+ });
1604
1798
  const detected = detectFramework(root);
1605
1799
  if (detected) {
1800
+ cliLog.info("framework_detected", {
1801
+ name: detected.name,
1802
+ class: detected.class
1803
+ });
1606
1804
  const gitCommit = options?.skipBuild ? null : getGitCommit(root);
1607
1805
  const fwPreset = FRAMEWORK_PRESETS[detected.name];
1608
1806
  if (detected.class === "b" || detected.class === "c") {
@@ -1610,8 +1808,8 @@ async function runDeploy(root, options) {
1610
1808
  R.error(`No deploy preset found for framework "${detected.name}".`);
1611
1809
  process.exit(1);
1612
1810
  }
1613
- await runFrameworkDeploy(root, config, client, detected, fwPreset, voidConfig, options?.skipBuild, gitCommit);
1614
- } else await runFullDeploy(root, config, client, fwPreset, options?.skipBuild, gitCommit, detected);
1811
+ await runFrameworkDeploy(root, config, client, detected, fwPreset, voidConfig, options?.skipBuild, gitCommit, cliLog);
1812
+ } else await runFullDeploy(root, config, client, fwPreset, options?.skipBuild, gitCommit, detected, cliLog);
1615
1813
  return;
1616
1814
  }
1617
1815
  let preset = null;
@@ -1641,8 +1839,8 @@ async function runDeploy(root, options) {
1641
1839
  ...preset,
1642
1840
  buildCommand: resolveStaticBuildCommand(preset, voidConfig.inference?.build, Boolean(options?.dir))
1643
1841
  };
1644
- await runStaticDeploy(root, config, client, preset, options?.skipBuild, gitCommit, void 0, void 0, void 0, void 0, voidConfig.routing);
1645
- } else await runFullDeploy(root, config, client, void 0, options?.skipBuild, gitCommit);
1842
+ await runStaticDeploy(root, config, client, preset, options?.skipBuild, gitCommit, void 0, void 0, void 0, void 0, voidConfig.routing, cliLog);
1843
+ } else await runFullDeploy(root, config, client, void 0, options?.skipBuild, gitCommit, void 0, cliLog);
1646
1844
  return;
1647
1845
  } catch (error) {
1648
1846
  if (isExpiredTokenError(error)) {
@@ -1657,7 +1855,7 @@ async function runDeploy(root, options) {
1657
1855
  throw error;
1658
1856
  }
1659
1857
  }
1660
- async function promptDeployProjectConfig(root, client) {
1858
+ async function promptDeployProjectConfig(root, client, cliLog) {
1661
1859
  const projects = await client.listProjects();
1662
1860
  if (await promptProjectSetupAction("Set up a Void project for deploy:", { includeLink: projects.length > 0 }) === "create") return promptAndCreateProject(root, client);
1663
1861
  const result = await promptProjectSelection(projects, "Select a project to deploy to:", root, { includeCreate: false });
@@ -1668,6 +1866,10 @@ async function promptDeployProjectConfig(root, client) {
1668
1866
  };
1669
1867
  writeProjectConfig(root, config);
1670
1868
  R.step(`Linked to ${import_picocolors.default.blue(config.slug)}`);
1869
+ cliLog?.info("project_linked", {
1870
+ projectId: config.projectId,
1871
+ slug: config.slug
1872
+ });
1671
1873
  return config;
1672
1874
  }
1673
1875
  function resolveStaticBuildCommand(preset, buildOverride, hasExplicitDir = false) {
@@ -1729,49 +1931,78 @@ function applyDeployEvent(event, s) {
1729
1931
  }
1730
1932
  return { kind: "continue" };
1731
1933
  }
1732
- async function streamDeploy(client, projectId, formData, s) {
1934
+ async function streamDeploy(client, projectId, formData, s, cliLog) {
1935
+ const startedAt = Date.now();
1733
1936
  let deploymentId = null;
1734
1937
  let interrupted = false;
1938
+ cliLog?.info("stream_start", { projectId });
1735
1939
  const onSigint = () => {
1736
1940
  interrupted = true;
1737
1941
  s.stop("Deploy interrupted");
1738
1942
  if (deploymentId) R.info(`Deployment: ${deploymentId}`);
1739
1943
  else R.info("Deployment id was not received before the interrupt.");
1944
+ cliLog?.warn("stream_interrupted", { deploymentId });
1945
+ cliLog?.close().catch(() => {});
1740
1946
  process.exit(130);
1741
1947
  };
1742
1948
  process.once("SIGINT", onSigint);
1743
1949
  try {
1744
1950
  for await (const event of client.deploy(projectId, formData)) {
1745
1951
  const result = applyDeployEvent(event, s);
1746
- if (result.kind === "start") deploymentId = result.deploymentId;
1747
- else if (result.kind === "done") return result.event;
1748
- else if (result.kind === "error") throw new DeployEventError(formatDeployFailureMessage(result.message, deploymentId));
1952
+ if (result.kind === "start") {
1953
+ deploymentId = result.deploymentId;
1954
+ cliLog?.info("stream_deployment_id", { deploymentId });
1955
+ } else if (result.kind === "done") {
1956
+ cliLog?.info("deploy_success", {
1957
+ deploymentId,
1958
+ url: result.event.url,
1959
+ assets: result.event.assets,
1960
+ workers: result.event.workers,
1961
+ durationMs: Date.now() - startedAt
1962
+ });
1963
+ return result.event;
1964
+ } else if (result.kind === "error") {
1965
+ cliLog?.error("deploy_server_error", void 0, {
1966
+ deploymentId,
1967
+ message: result.message
1968
+ });
1969
+ throw new DeployEventError(formatDeployFailureMessage(result.message, deploymentId, cliLog?.path ?? null));
1970
+ }
1749
1971
  }
1750
1972
  } catch (err) {
1751
1973
  if (err instanceof DeployEventError) throw err;
1752
1974
  if (interrupted) throw err;
1975
+ cliLog?.error("stream_deploy_caught", err, { deploymentId });
1753
1976
  s.stop("Deploy failed");
1754
- throw formatStreamDeployError(err, deploymentId);
1977
+ throw formatStreamDeployError(err, deploymentId, cliLog?.path ?? null);
1755
1978
  } finally {
1756
1979
  process.removeListener("SIGINT", onSigint);
1757
1980
  }
1981
+ cliLog?.warn("stream_no_terminal_event", { deploymentId });
1758
1982
  s.stop("Deploy failed");
1759
- throw new Error(formatConnectionLostMessage(deploymentId));
1983
+ throw new Error(formatConnectionLostMessage(deploymentId, cliLog?.path ?? null));
1760
1984
  }
1761
1985
  function formatKnownAssetSummary(result, skipped) {
1762
1986
  const uploaded = result.assets - skipped;
1763
1987
  return skipped > 0 ? `${result.assets} static asset(s) (${skipped} unchanged, ${uploaded} uploaded)` : `${result.assets} static asset(s)`;
1764
1988
  }
1765
- async function runStaticDeploy(root, config, client, preset, skipBuild, commit, hashedAssetsPrefix, headerRules, redirectRules, fallbackRules, routing) {
1989
+ async function runStaticDeploy(root, config, client, preset, skipBuild, commit, hashedAssetsPrefix, headerRules, redirectRules, fallbackRules, routing, cliLog) {
1766
1990
  const typeLabel = preset.appType === "spa" ? "Static SPA" : "Static Site";
1767
1991
  R.info(`${typeLabel} deploy`);
1992
+ cliLog?.info("deploy_mode", {
1993
+ mode: "static",
1994
+ appType: preset.appType
1995
+ });
1768
1996
  if (preset.buildCommand && !skipBuild) {
1769
1997
  R.step("Building...");
1998
+ cliLog?.info("build_start", { command: preset.buildCommand });
1999
+ const buildStartedAt = Date.now();
1770
2000
  execSync(preset.buildCommand, {
1771
2001
  cwd: root,
1772
2002
  stdio: "inherit"
1773
2003
  });
1774
- }
2004
+ cliLog?.info("build_end", { durationMs: Date.now() - buildStartedAt });
2005
+ } else if (skipBuild) cliLog?.info("build_skipped", { reason: "skipBuild" });
1775
2006
  if (!existsSync(preset.outputDir)) {
1776
2007
  R.error(`deploy: Output directory '${preset.outputDir}' not found. Run the build first.`);
1777
2008
  process.exit(1);
@@ -1793,17 +2024,29 @@ async function runStaticDeploy(root, config, client, preset, skipBuild, commit,
1793
2024
  const s = createSpinner();
1794
2025
  const onProgress = (msg) => s.message(msg);
1795
2026
  s.start("Checking for changes...");
2027
+ cliLog?.info("preflight_start", { dir: preset.outputDir });
1796
2028
  const { assetManifest } = await collectAndHashAssets(preset.outputDir, onProgress);
1797
2029
  const { needed, skipped } = await client.preflight(config.projectId, assetManifest);
1798
2030
  s.stop("Checked for changes");
2031
+ cliLog?.info("preflight_end", {
2032
+ total: Object.keys(assetManifest).length,
2033
+ needed: needed.length,
2034
+ skipped
2035
+ });
1799
2036
  if (skipped > 0) R.info(`Skipping ${skipped} unchanged asset(s)`);
1800
2037
  s.start("Packaging...");
2038
+ cliLog?.info("package_start", { mode: "static" });
2039
+ const packageStartedAt = Date.now();
1801
2040
  const neededSet = skipped > 0 ? new Set(needed) : null;
1802
2041
  const formData = await packageStaticBuild(preset.outputDir, preset.appType, neededSet, hashedAssetsPrefix ?? "assets", headerRules, redirectRules, fallbackRules, onProgress);
1803
2042
  formData.append("assetManifest", JSON.stringify(assetManifest));
1804
2043
  formData.append("source", getTokenSource());
1805
2044
  if (commit) formData.append("commit", commit);
1806
- const result = await streamDeploy(client, config.projectId, formData, s);
2045
+ cliLog?.info("package_end", {
2046
+ mode: "static",
2047
+ durationMs: Date.now() - packageStartedAt
2048
+ });
2049
+ const result = await streamDeploy(client, config.projectId, formData, s, cliLog);
1807
2050
  Se([`${typeLabel} — ${formatKnownAssetSummary(result, skipped)}`].join("\n"), result.url);
1808
2051
  ye("Done!");
1809
2052
  }
@@ -1821,17 +2064,24 @@ function warnRedundantForceOn3xx(count) {
1821
2064
  /**
1822
2065
  * Class B/C framework deploy: build with framework CLI, package with preset output paths.
1823
2066
  */
1824
- async function runFrameworkDeploy(root, config, client, detected, fwPreset, voidConfig, skipBuild, commit) {
2067
+ async function runFrameworkDeploy(root, config, client, detected, fwPreset, voidConfig, skipBuild, commit, cliLog) {
1825
2068
  R.info(`${detected.name} framework deploy`);
2069
+ cliLog?.info("deploy_mode", {
2070
+ mode: "framework",
2071
+ framework: detected.name
2072
+ });
1826
2073
  const dialect = getDatabaseDialect(voidConfig);
1827
2074
  const buildCmd = voidConfig.inference?.build ?? formatProjectCommand(root, fwPreset.buildCommand);
1828
2075
  if (!skipBuild) {
1829
2076
  R.step(`Building (${buildCmd})...`);
2077
+ cliLog?.info("build_start", { command: buildCmd });
2078
+ const buildStartedAt = Date.now();
1830
2079
  execSync(voidConfig.inference?.build ?? resolveProjectCommand(root, fwPreset.buildCommand), {
1831
2080
  cwd: root,
1832
2081
  stdio: "inherit"
1833
2082
  });
1834
- }
2083
+ cliLog?.info("build_end", { durationMs: Date.now() - buildStartedAt });
2084
+ } else cliLog?.info("build_skipped", { reason: "skipBuild" });
1835
2085
  const workerDir = join(root, fwPreset.workerDir);
1836
2086
  const assetsDir = join(root, fwPreset.assetsDir);
1837
2087
  const frameworkAssetIgnorePatterns = fwPreset.workerDir === fwPreset.assetsDir && fwPreset.workerMain === "_worker.js" ? [`/${fwPreset.workerMain}`, "/_routes.json"] : [];
@@ -1931,11 +2181,22 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
1931
2181
  const s = createSpinner();
1932
2182
  const onProgress = (msg) => s.message(msg);
1933
2183
  s.start("Checking for changes...");
2184
+ cliLog?.info("preflight_start", { dir: assetsDir });
1934
2185
  const { assetManifest } = await collectAndHashAssets(assetsDir, onProgress, { ignorePatterns: frameworkAssetIgnorePatterns });
1935
2186
  const { needed, skipped } = await client.preflight(config.projectId, assetManifest, true);
1936
2187
  s.stop("Checked for changes");
2188
+ cliLog?.info("preflight_end", {
2189
+ total: Object.keys(assetManifest).length,
2190
+ needed: needed.length,
2191
+ skipped
2192
+ });
1937
2193
  if (skipped > 0) R.info(`Skipping ${skipped} unchanged asset(s)`);
1938
2194
  s.start("Packaging...");
2195
+ cliLog?.info("package_start", {
2196
+ mode: "framework",
2197
+ framework: detected.name
2198
+ });
2199
+ const packageStartedAt = Date.now();
1939
2200
  const neededSet = skipped > 0 ? new Set(needed) : null;
1940
2201
  const formData = await packageFrameworkBuild({
1941
2202
  frameworkName: detected.name,
@@ -1969,7 +2230,11 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
1969
2230
  formData.append("assetManifest", JSON.stringify(assetManifest));
1970
2231
  formData.append("source", getTokenSource());
1971
2232
  if (commit) formData.append("commit", commit);
1972
- const result = await streamDeploy(client, config.projectId, formData, s);
2233
+ cliLog?.info("package_end", {
2234
+ mode: "framework",
2235
+ durationMs: Date.now() - packageStartedAt
2236
+ });
2237
+ const result = await streamDeploy(client, config.projectId, formData, s, cliLog);
1973
2238
  const summary = [`${result.workers} worker module(s), ${formatKnownAssetSummary(result, skipped)}`];
1974
2239
  if (result.migrations) summary.push(`${result.migrations} migration(s) applied`);
1975
2240
  if (schedules.length > 0) summary.push(`${schedules.length} cron job(s) scheduled`);
@@ -1982,10 +2247,16 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
1982
2247
  cleanupWrapper(root);
1983
2248
  ye("Done!");
1984
2249
  }
1985
- async function runFullDeploy(root, config, client, fwPreset, skipBuild, commit, detected) {
2250
+ async function runFullDeploy(root, config, client, fwPreset, skipBuild, commit, detected, cliLog) {
2251
+ cliLog?.info("deploy_mode", {
2252
+ mode: "full",
2253
+ framework: detected?.name
2254
+ });
1986
2255
  if (!skipBuild) {
1987
2256
  const buildCmd = formatProjectCommand(root, "vite build");
1988
2257
  R.step(`Building (${buildCmd})...`);
2258
+ cliLog?.info("build_start", { command: buildCmd });
2259
+ const buildStartedAt = Date.now();
1989
2260
  try {
1990
2261
  execSync(resolveProjectCommand(root, "vite build"), {
1991
2262
  cwd: root,
@@ -1995,10 +2266,12 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, commit,
1995
2266
  VOID_DEPLOY_PROJECT_ID: config.projectId
1996
2267
  }
1997
2268
  });
2269
+ cliLog?.info("build_end", { durationMs: Date.now() - buildStartedAt });
1998
2270
  } catch (err) {
2271
+ cliLog?.error("build_failed", err, { durationMs: Date.now() - buildStartedAt });
1999
2272
  process.exit(getExitCode(err));
2000
2273
  }
2001
- }
2274
+ } else cliLog?.info("build_skipped", { reason: "skipBuild" });
2002
2275
  const frameworkBuildCmd = fwPreset ? formatProjectCommand(root, fwPreset.buildCommand) : void 0;
2003
2276
  const distDir = resolveDistDir(root, fwPreset, frameworkBuildCmd);
2004
2277
  let assetsPrefix = "assets";
@@ -2112,11 +2385,12 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, commit,
2112
2385
  if (deployConfig.output === "static" && pageScan && !isSsr) {
2113
2386
  if (pageScan.pages.every((p) => p.prerender && (p.params.length === 0 && !p.catchAll || p.hasGetPrerenderPaths)) && !(hasRoutes || hasMiddleware || hasWebSockets || authEnabled) && schedules.length === 0 && queues.length === 0) {
2114
2387
  R.info("All pages prerendered — deploying as static site");
2388
+ cliLog?.info("full_to_static_redirect", { reason: "all_pages_prerendered" });
2115
2389
  return runStaticDeploy(root, config, client, {
2116
2390
  buildCommand: null,
2117
2391
  outputDir: clientDir,
2118
2392
  appType: "static"
2119
- }, true, commit, assetsPrefix, headerRules, redirectRules, fallbackRules);
2393
+ }, true, commit, assetsPrefix, headerRules, redirectRules, fallbackRules, void 0, cliLog);
2120
2394
  }
2121
2395
  }
2122
2396
  const uniquePrerenderPaths = deployConfig.output === "static" || isFrameworkMode ? [] : isNodeTarget(deployConfig.target) ? await collectPrerenderPathsNode({
@@ -2146,11 +2420,27 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, commit,
2146
2420
  authEnabled
2147
2421
  });
2148
2422
  s.start("Checking for changes...");
2423
+ cliLog?.info("preflight_start", { dir: clientDir });
2149
2424
  const { assetManifest } = await collectAndHashAssets(clientDir, onProgress);
2150
2425
  const { needed, skipped } = await client.preflight(config.projectId, assetManifest, true);
2151
2426
  s.stop("Checked for changes");
2427
+ cliLog?.info("preflight_end", {
2428
+ total: Object.keys(assetManifest).length,
2429
+ needed: needed.length,
2430
+ skipped
2431
+ });
2152
2432
  if (skipped > 0) R.info(`Skipping ${skipped} unchanged asset(s)`);
2153
2433
  s.start("Packaging...");
2434
+ cliLog?.info("package_start", {
2435
+ mode: "full",
2436
+ isSsr,
2437
+ hasRoutes,
2438
+ hasMiddleware,
2439
+ hasWebSockets,
2440
+ authEnabled,
2441
+ framework: detected?.name
2442
+ });
2443
+ const packageStartedAt = Date.now();
2154
2444
  const neededSet = skipped > 0 ? new Set(needed) : null;
2155
2445
  const formData = await packageBuild(distDir, workerDirName, effectiveBindings, validatedMigrations, schedules, isSsr || hasRoutes || hasMiddleware || hasWebSockets || authEnabled, isFrameworkMode ? detected.name : void 0, revalidate, envVars, queues, uniquePrerenderPaths, assetConfig, {
2156
2446
  compatibilityDate: wranglerCompat?.compatibilityDate,
@@ -2161,7 +2451,11 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, commit,
2161
2451
  formData.append("assetManifest", JSON.stringify(assetManifest));
2162
2452
  formData.append("source", getTokenSource());
2163
2453
  if (commit) formData.append("commit", commit);
2164
- const result = await streamDeploy(client, config.projectId, formData, s);
2454
+ cliLog?.info("package_end", {
2455
+ mode: "full",
2456
+ durationMs: Date.now() - packageStartedAt
2457
+ });
2458
+ const result = await streamDeploy(client, config.projectId, formData, s, cliLog);
2165
2459
  const summary = [`${result.workers} worker module(s), ${formatKnownAssetSummary(result, skipped)}`];
2166
2460
  if (result.migrations) summary.push(`${result.migrations} migration(s) applied`);
2167
2461
  if (schedules.length > 0) summary.push(`${schedules.length} cron job(s) scheduled`);
@@ -2174,8 +2468,9 @@ async function runFullDeploy(root, config, client, fwPreset, skipBuild, commit,
2174
2468
  Se(summary.join("\n"), result.url);
2175
2469
  ye("Done!");
2176
2470
  }
2177
- async function createProjectFromSlug(root, client, slug) {
2471
+ async function createProjectFromSlug(root, client, slug, cliLog) {
2178
2472
  R.step(`Creating project ${import_picocolors.default.blue(slug)}...`);
2473
+ cliLog?.info("project_create_start", { slug });
2179
2474
  const project = await client.createProject(slug);
2180
2475
  const config = {
2181
2476
  projectId: project.id,
@@ -2183,6 +2478,10 @@ async function createProjectFromSlug(root, client, slug) {
2183
2478
  };
2184
2479
  writeProjectConfig(root, config);
2185
2480
  R.success(`Project created: ${import_picocolors.default.blue(config.slug)}`);
2481
+ cliLog?.info("project_create_success", {
2482
+ projectId: config.projectId,
2483
+ slug: config.slug
2484
+ });
2186
2485
  return config;
2187
2486
  }
2188
2487
  function getGitCommit(root) {
@@ -1,7 +1,7 @@
1
1
  import { s as import_picocolors } from "./output-BwlcIYSR.mjs";
2
2
  import { c as R, u as Se } from "./dist-Dayj3gCK.mjs";
3
3
  import { r as readProjectConfig } from "./project-TqORyHn8.mjs";
4
- import { n as PlatformClient, s as getToken } from "./client-snXOjrp1.mjs";
4
+ import { n as PlatformClient, s as getToken } from "./client-BUdfE3QJ.mjs";
5
5
  //#region src/cli/domain.ts
6
6
  async function runDomainCommand(root, args) {
7
7
  const token = getToken(root);
@@ -2,7 +2,7 @@ import { a as join } from "./pathe.M-eThtNZ-D-kmWkCS.mjs";
2
2
  import { n as cliTitle } from "./output-BwlcIYSR.mjs";
3
3
  import { c as R, g as ge, u as Se, y as ye } from "./dist-Dayj3gCK.mjs";
4
4
  import { r as readProjectConfig } from "./project-TqORyHn8.mjs";
5
- import { n as PlatformClient, s as getToken } from "./client-snXOjrp1.mjs";
5
+ import { n as PlatformClient, s as getToken } from "./client-BUdfE3QJ.mjs";
6
6
  import { n as generateEnvTypes, t as findEnvFile } from "./env-types-DknSA4SO.mjs";
7
7
  import { a as loadUserEnvFile, i as validateProdEnv, n as formatEnvReport, t as fetchRemoteSecretNames } from "./env-validation-DJKjR_8q.mjs";
8
8
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
package/dist/index.mjs CHANGED
@@ -3,8 +3,8 @@ import { a as join, n as dirname, o as relative } from "./pathe.M-eThtNZ-D-kmWkC
3
3
  import { u as require_picocolors } from "./output-BwlcIYSR.mjs";
4
4
  import { n as loadVoidAuthConfig, t as findVoidAuthConfig } from "./config-CvHtTM0q.mjs";
5
5
  import { r as readProjectConfig } from "./project-TqORyHn8.mjs";
6
- import { l as isStagingMode, s as getToken } from "./client-snXOjrp1.mjs";
7
- import { _ as resolveSandboxConfig, a as prerenderPagesNode, c as filterLoadedEnv, d as ensureWranglerCompatibilityDate, f as ensureWranglerNodejsAlsFlag, g as isSandboxEnabled, h as SANDBOX_MIGRATION_TAG, i as prerenderPages, l as resolveRemoteMode, m as syncWranglerBindings, n as resolveDistDir, o as DEFAULT_PROXY_URL, p as readWranglerCompat, r as resolveWorkerDirName, s as STAGING_PROXY_URL, u as stripInternalEnvKeys } from "./deploy-BPKblFx6.mjs";
6
+ import { l as isStagingMode, s as getToken } from "./client-BUdfE3QJ.mjs";
7
+ import { _ as resolveSandboxConfig, a as prerenderPagesNode, c as filterLoadedEnv, d as ensureWranglerCompatibilityDate, f as ensureWranglerNodejsAlsFlag, g as isSandboxEnabled, h as SANDBOX_MIGRATION_TAG, i as prerenderPages, l as resolveRemoteMode, m as syncWranglerBindings, n as resolveDistDir, o as DEFAULT_PROXY_URL, p as readWranglerCompat, r as resolveWorkerDirName, s as STAGING_PROXY_URL, u as stripInternalEnvKeys } from "./deploy-BqXz1ycW.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 { a as SSR_SERVER_ENTRY_RELATIVE_PATHS, c as validateSsrEntry, d as isIdentifier, f as isLiteral, h as walk, i as SSR_CLIENT_ENTRY_RELATIVE_PATHS, l as inferBindingsFromSource, n as detectFramework, p as isMemberExpression, r as inferProjectBindings, s as validateSsrEntries, t as FRAMEWORK_SCAN_DIRS } from "./plugin-inference-oZ6Ybu2_.mjs";
@@ -4154,7 +4154,7 @@ const STAGING_API_URL = "https://api.staging.void.cloud";
4154
4154
  */
4155
4155
  async function runAiSetupPrompt(root, token, staging, server) {
4156
4156
  const { select, text, isCancel, log } = await import("./dist-Dayj3gCK.mjs").then((n) => n.p);
4157
- const { PlatformClient } = await import("./client-snXOjrp1.mjs").then((n) => n.r);
4157
+ const { PlatformClient } = await import("./client-BUdfE3QJ.mjs").then((n) => n.r);
4158
4158
  const { writeProjectConfig } = await import("./project-TqORyHn8.mjs").then((n) => n.n);
4159
4159
  const { inferDefaultSlug } = await import("./resolve-project-Br5BR03U.mjs").then((n) => n.i);
4160
4160
  const client = new PlatformClient(token, staging ? STAGING_API_URL : DEFAULT_API_URL);
@@ -2,15 +2,15 @@ import { a as join, i as isAbsolute, n as dirname, o as relative, s as resolve,
2
2
  import { n as cliTitle } from "./output-BwlcIYSR.mjs";
3
3
  import { _ as me, c as R, g as ge, i as Ee, u as Se, v as ue, x as q, y as ye } from "./dist-Dayj3gCK.mjs";
4
4
  import { a as writeProjectConfig, r as readProjectConfig } from "./project-TqORyHn8.mjs";
5
- import { l as isStagingMode, n as PlatformClient, s as getToken } from "./client-snXOjrp1.mjs";
5
+ import { l as isStagingMode, n as PlatformClient, s as getToken } from "./client-BUdfE3QJ.mjs";
6
6
  import { c as getDatabaseDialect, f as readConfig } from "./config-BIa9HwVX.mjs";
7
7
  import { n as detectFramework } from "./plugin-inference-oZ6Ybu2_.mjs";
8
8
  import { r as parseDotenvFile, t as expandDotenv } from "./dotenv-DwO4ti0Z.mjs";
9
9
  import { n as ensureProjectTsconfig, t as createProjectTsconfig } from "./project-tsconfig-DfkESbDL.mjs";
10
10
  import { t as MagicString } from "./magic-string.es-D6g9UnIy.mjs";
11
11
  import { n as writeDrizzleConfig } from "./config-BzM9Dy7T.mjs";
12
- import { n as promptProjectSelection, t as promptAndCreateProject } from "./create-project-BIA15W7z.mjs";
13
- import { t as promptForLoginToken } from "./login-CkcXUiIu.mjs";
12
+ import { n as promptProjectSelection, t as promptAndCreateProject } from "./create-project-CN1pF-OQ.mjs";
13
+ import { t as promptForLoginToken } from "./login-RWUDCfdx.mjs";
14
14
  import { r as formatProjectCommand } from "./preset-D4I73kT4.mjs";
15
15
  import { n as getAgentById, r as getPackageDir, t as detectAgents } from "./cli/cli.mjs";
16
16
  import { n as findInstalledYarnPnpRuntime, r as hasLocalLockfile, t as buildDrizzleKitSpawnEnv } from "./yarn-pnp-BFqMV_bl.mjs";
@@ -1,8 +1,8 @@
1
1
  import { n as cliTitle, s as import_picocolors } from "./output-BwlcIYSR.mjs";
2
2
  import { _ as me, c as R, g as ge, y as ye } from "./dist-Dayj3gCK.mjs";
3
3
  import { a as writeProjectConfig, r as readProjectConfig } from "./project-TqORyHn8.mjs";
4
- import { n as PlatformClient, o as getSiteDomain, s as getToken } from "./client-snXOjrp1.mjs";
5
- import { n as promptProjectSelection, t as promptAndCreateProject } from "./create-project-BIA15W7z.mjs";
4
+ import { n as PlatformClient, o as getSiteDomain, s as getToken } from "./client-BUdfE3QJ.mjs";
5
+ import { n as promptProjectSelection, t as promptAndCreateProject } from "./create-project-CN1pF-OQ.mjs";
6
6
  //#region src/cli/link.ts
7
7
  async function runLink(root, slug) {
8
8
  console.log();
@@ -1,7 +1,7 @@
1
1
  import { a as ensureUtc, i as dim, l as tablePrefix, n as cliTitle, s as import_picocolors } from "./output-BwlcIYSR.mjs";
2
2
  import { c as R, g as ge, y as ye } from "./dist-Dayj3gCK.mjs";
3
3
  import { r as readProjectConfig } from "./project-TqORyHn8.mjs";
4
- import { n as PlatformClient, s as getToken } from "./client-snXOjrp1.mjs";
4
+ import { n as PlatformClient, s as getToken } from "./client-BUdfE3QJ.mjs";
5
5
  import { r as resolveProjectBySlug } from "./resolve-project-Br5BR03U.mjs";
6
6
  //#region src/cli/list.ts
7
7
  async function runListCommand(root, slug) {
@@ -1,6 +1,6 @@
1
1
  import { n as cliTitle, r as createSpinner, s as import_picocolors } from "./output-BwlcIYSR.mjs";
2
2
  import { _ as me, c as R, g as ge, i as Ee, x as q, y as ye } from "./dist-Dayj3gCK.mjs";
3
- import { c as getTokenSource, d as runLogin, f as saveToken, i as isExpiredTokenError, l as isStagingMode, n as PlatformClient, s as getToken, u as removeToken } from "./client-snXOjrp1.mjs";
3
+ import { c as getTokenSource, d as runLogin, f as saveToken, i as isExpiredTokenError, l as isStagingMode, n as PlatformClient, s as getToken, u as removeToken } from "./client-BUdfE3QJ.mjs";
4
4
  //#region src/cli/login.ts
5
5
  async function promptForLoginToken(root = process.cwd()) {
6
6
  const provider = await Ee({
@@ -1,7 +1,7 @@
1
1
  import { i as dim, n as cliTitle, s as import_picocolors } from "./output-BwlcIYSR.mjs";
2
2
  import { c as R, g as ge, y as ye } from "./dist-Dayj3gCK.mjs";
3
3
  import { r as readProjectConfig } from "./project-TqORyHn8.mjs";
4
- import { n as PlatformClient, s as getToken } from "./client-snXOjrp1.mjs";
4
+ import { n as PlatformClient, s as getToken } from "./client-BUdfE3QJ.mjs";
5
5
  //#region src/cli/logs.ts
6
6
  async function runLogsCommand(root, filter, range, deployment, level) {
7
7
  console.log();
@@ -1,6 +1,6 @@
1
1
  import { a as join, n as dirname } from "./pathe.M-eThtNZ-D-kmWkCS.mjs";
2
2
  import { a as writeProjectConfig } from "./project-TqORyHn8.mjs";
3
- import { n as PlatformClient, s as getToken } from "./client-snXOjrp1.mjs";
3
+ import { n as PlatformClient, s as getToken } from "./client-BUdfE3QJ.mjs";
4
4
  import { existsSync, readFileSync, readdirSync } from "node:fs";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { createInterface } from "node:readline";
@@ -1,6 +1,6 @@
1
1
  import { i as dim, l as tablePrefix, n as cliTitle, s as import_picocolors } from "./output-BwlcIYSR.mjs";
2
2
  import { c as R, g as ge, y as ye } from "./dist-Dayj3gCK.mjs";
3
- import { n as PlatformClient, o as getSiteDomain, s as getToken } from "./client-snXOjrp1.mjs";
3
+ import { n as PlatformClient, o as getSiteDomain, s as getToken } from "./client-BUdfE3QJ.mjs";
4
4
  //#region src/cli/projects.ts
5
5
  async function runProjectsCommand(root) {
6
6
  console.log();
@@ -34,32 +34,32 @@ async function runProjectsCommand(root) {
34
34
  //#region src/cli/project-cmd.ts
35
35
  async function runProjectCommand(root, args) {
36
36
  if (args.subcommand === "status") {
37
- const { runListCommand } = await import("./list-Bfel-QLc.mjs");
37
+ const { runListCommand } = await import("./list-bQc1eQCZ.mjs");
38
38
  return runListCommand(root, args.name || void 0);
39
39
  }
40
40
  if (args.subcommand === "link") {
41
- const { runLink } = await import("./link-p2R6NbgN.mjs");
41
+ const { runLink } = await import("./link-D4d26PCm.mjs");
42
42
  return runLink(root, args.name || void 0);
43
43
  }
44
44
  if (args.subcommand === "list") return runProjectsCommand(root);
45
45
  if (args.subcommand === "logs") {
46
- const { runLogsCommand } = await import("./logs-DmkrRvx6.mjs");
46
+ const { runLogsCommand } = await import("./logs-DrkTklop.mjs");
47
47
  return runLogsCommand(root, args.filter, args.range, args.deployment || void 0, args.level || void 0);
48
48
  }
49
49
  if (args.subcommand === "rollback") {
50
- const { runRollbackCommand } = await import("./rollback-gyC59l7U.mjs");
50
+ const { runRollbackCommand } = await import("./rollback-BSyita3C.mjs");
51
51
  return runRollbackCommand(root, args.deployId || void 0);
52
52
  }
53
53
  if (args.subcommand === "cancel") {
54
- const { runCancelDeployCommand } = await import("./cancel-deploy-BOBTqqh0.mjs");
54
+ const { runCancelDeployCommand } = await import("./cancel-deploy-D9OFt5gA.mjs");
55
55
  return runCancelDeployCommand(root, args.deployId || void 0);
56
56
  }
57
57
  if (args.subcommand === "delete") {
58
- const { runDeleteCommand } = await import("./delete-DAP6yDc7.mjs");
58
+ const { runDeleteCommand } = await import("./delete-DJTvwbr-.mjs");
59
59
  return runDeleteCommand(root, args.name || void 0, { force: args.force });
60
60
  }
61
61
  if (args.subcommand === "purge-cache") {
62
- const { runPurgeCacheCommand } = await import("./cache-W82I8ihI.mjs");
62
+ const { runPurgeCacheCommand } = await import("./cache-B0BgSTZi.mjs");
63
63
  return runPurgeCacheCommand(root, args.projectSlug || void 0);
64
64
  }
65
65
  }
@@ -1,7 +1,7 @@
1
1
  import { a as ensureUtc, i as dim, n as cliTitle, r as createSpinner, s as import_picocolors } from "./output-BwlcIYSR.mjs";
2
2
  import { _ as me, c as R, g as ge, i as Ee, v as ue, x as q, y as ye } from "./dist-Dayj3gCK.mjs";
3
3
  import { r as readProjectConfig } from "./project-TqORyHn8.mjs";
4
- import { n as PlatformClient, s as getToken } from "./client-snXOjrp1.mjs";
4
+ import { n as PlatformClient, s as getToken } from "./client-BUdfE3QJ.mjs";
5
5
  //#region src/cli/rollback.ts
6
6
  async function runRollbackCommand(root, deployId) {
7
7
  console.log();
@@ -1,7 +1,7 @@
1
1
  import { n as cliTitle } from "./output-BwlcIYSR.mjs";
2
2
  import { _ as me, c as R, g as ge, n as Ce, u as Se, v as ue, x as q, y as ye } from "./dist-Dayj3gCK.mjs";
3
3
  import { r as readProjectConfig } from "./project-TqORyHn8.mjs";
4
- import { n as PlatformClient, s as getToken } from "./client-snXOjrp1.mjs";
4
+ import { n as PlatformClient, s as getToken } from "./client-BUdfE3QJ.mjs";
5
5
  import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project-Br5BR03U.mjs";
6
6
  import { readFileSync } from "node:fs";
7
7
  //#region src/cli/secret.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "void",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/voidzero-dev/void.git",
@@ -307,7 +307,7 @@
307
307
  "valibot": ">=1.0.0-beta.7",
308
308
  "vite": "^8.0.0",
309
309
  "zod": "^3.25.0 || ^4.0.0",
310
- "@void/md": "0.7.0"
310
+ "@void/md": "0.7.1"
311
311
  },
312
312
  "peerDependenciesMeta": {
313
313
  "@void/md": {
@@ -203,19 +203,22 @@ If `--project` is provided, purges that project's cache instead of the linked pr
203
203
  ### `void deploy`
204
204
 
205
205
  ```
206
- void deploy [--project <name>] [--dir <path>] [--spa] [--skip-build]
206
+ void deploy [--project <name>] [--dir <path>] [--spa] [--skip-build] [--debug]
207
207
  ```
208
208
 
209
209
  Auto-detects your project type and chooses the right pipeline. See [Supported App Types](../guide/app-types.md) and [Deployment](../guide/deployment.md) for details.
210
210
 
211
211
  For Drizzle projects, deploy performs a read-only schema drift check. If a new migration would be generated, deploy stops and tells you to run `void db generate`, review the migration, commit it yourself, and rerun `void deploy`.
212
212
 
213
- | Flag | Purpose |
214
- | ------------------ | ------------------------------------------------- |
215
- | `--project <name>` | Target a specific project by slug |
216
- | `--dir <path>` | Deploy a pre-built static directory (skips build) |
217
- | `--spa` | Use SPA mode instead of SSG for static deploys |
218
- | `--skip-build` | Skip the build step (use existing build output) |
213
+ | Flag | Purpose |
214
+ | ------------------ | ---------------------------------------------------------------------------- |
215
+ | `--project <name>` | Target a specific project by slug |
216
+ | `--dir <path>` | Deploy a pre-built static directory (skips build) |
217
+ | `--spa` | Use SPA mode instead of SSG for static deploys |
218
+ | `--skip-build` | Skip the build step (use existing build output) |
219
+ | `--debug` | Mirror the structured deploy log to stderr (also written to `~/.void/logs/`) |
220
+
221
+ Every deploy writes a structured JSONL trace to `~/.void/logs/deploy-<timestamp>.jsonl` regardless of `--debug`. On failure the path is printed at the end of the error message so you can attach it when reporting platform issues. `VOID_DEPLOY_DEBUG=1` is accepted as an alternate trigger for stderr mirroring.
219
222
 
220
223
  Project resolution precedence:
221
224