@prisma/cli 3.0.0-dev.81.1 → 3.0.0-dev.83.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,8 +1,8 @@
1
1
  import { getAuthFilePath } from "../lib/auth/client.js";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
- import { CredentialsStore } from "@prisma/credentials-store";
5
4
  import { randomUUID } from "node:crypto";
5
+ import { CredentialsStore } from "@prisma/credentials-store";
6
6
  //#region src/adapters/token-storage.ts
7
7
  function findLatestValidTokens(allCredentials) {
8
8
  for (let i = allCredentials.length - 1; i >= 0; i -= 1) {
@@ -49,7 +49,7 @@ var FileTokenStorage = class {
49
49
  const all = await this.credentialsStore.getCredentials();
50
50
  this.signal?.throwIfAborted();
51
51
  return findLatestValidTokens(all);
52
- } catch (error) {
52
+ } catch (_error) {
53
53
  if (this.signal?.aborted) throw this.signal.reason;
54
54
  return null;
55
55
  }
package/dist/cli.js CHANGED
@@ -3,22 +3,22 @@ import { runUpdateDiscoveryWorker } from "./shell/update-check.js";
3
3
  import { runCli } from "./cli2.js";
4
4
  import process from "node:process";
5
5
  //#region src/bin.ts
6
- if (process.env.PRISMA_CLI_RUN_UPDATE_CHECK_WORKER === "1") runUpdateDiscoveryWorker().then(() => {
6
+ if (process.env.PRISMA_CLI_RUN_UPDATE_CHECK_WORKER === "1") {
7
+ await runUpdateDiscoveryWorker();
7
8
  process.exitCode = 0;
8
- });
9
- else {
9
+ } else {
10
10
  const controller = new AbortController();
11
11
  const abortCli = () => {
12
12
  if (!controller.signal.aborted) controller.abort(new DOMException("Command canceled", "AbortError"));
13
13
  };
14
14
  process.once("SIGINT", abortCli);
15
15
  process.once("SIGTERM", abortCli);
16
- runCli({ signal: controller.signal }).then((exitCode) => {
17
- process.exitCode = exitCode;
18
- }).finally(() => {
16
+ try {
17
+ process.exitCode = await runCli({ signal: controller.signal });
18
+ } finally {
19
19
  process.off("SIGINT", abortCli);
20
20
  process.off("SIGTERM", abortCli);
21
- });
21
+ }
22
22
  }
23
23
  //#endregion
24
24
  export {};
package/dist/cli2.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { CliError } from "./shell/errors.js";
2
- import { createShellUi } from "./shell/ui.js";
3
- import { formatUnexpectedError, writeHumanError, writeJsonError, writeJsonSuccess } from "./shell/output.js";
2
+ import "./shell/prompt.js";
4
3
  import { attachCommandDescriptor } from "./shell/command-meta.js";
5
4
  import { addCompactGlobalFlags } from "./shell/global-flags.js";
5
+ import { createShellUi } from "./shell/ui.js";
6
6
  import { configureRuntimeCommand, createCommandContext } from "./shell/runtime.js";
7
- import "./shell/prompt.js";
7
+ import { formatUnexpectedError, writeHumanError, writeJsonError, writeJsonSuccess } from "./shell/output.js";
8
8
  import { createAppCommand } from "./commands/app/index.js";
9
9
  import { createAuthCommand } from "./commands/auth/index.js";
10
10
  import { createBranchCommand } from "./commands/branch/index.js";
@@ -1,8 +1,8 @@
1
1
  import { CliError, authRequiredError, usageError, workspaceRequiredError } from "../shell/errors.js";
2
- import { requireComputeAuth } from "../lib/auth/guard.js";
2
+ import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
3
3
  import { formatScopeLabel, parseKeyValuePositional, resolveEnvScope } from "../lib/app/env-config.js";
4
4
  import { readEnvFileAssignments } from "../lib/app/env-file.js";
5
- import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
5
+ import { requireComputeAuth } from "../lib/auth/guard.js";
6
6
  import { readLocalGitBranch } from "../lib/git/local-branch.js";
7
7
  import { requireAuthenticatedAuthState } from "./auth.js";
8
8
  import { listRealWorkspaceProjects } from "./project.js";
@@ -204,12 +204,13 @@ async function requireClientAndProject(context, explicitProject, commandName) {
204
204
  const authState = await requireAuthenticatedAuthState(context);
205
205
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
206
206
  if (!client) throw authRequiredError(["prisma-cli auth login"]);
207
- if (!authState.workspace) throw workspaceRequiredError();
207
+ const workspace = authState.workspace;
208
+ if (!workspace) throw workspaceRequiredError();
208
209
  const targetResult = await resolveProjectTarget({
209
210
  context,
210
- workspace: authState.workspace,
211
+ workspace,
211
212
  explicitProject,
212
- listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
213
+ listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
213
214
  commandName
214
215
  });
215
216
  if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
@@ -218,7 +219,7 @@ async function requireClientAndProject(context, explicitProject, commandName) {
218
219
  client,
219
220
  projectId: target.project.id,
220
221
  verboseContext: {
221
- workspace: authState.workspace,
222
+ workspace,
222
223
  project: target.project,
223
224
  resolution: target.resolution
224
225
  }
@@ -1,37 +1,37 @@
1
1
  import { SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
2
2
  import { FileTokenStorage } from "../adapters/token-storage.js";
3
3
  import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../shell/errors.js";
4
+ import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
4
5
  import { renderCommandHeader } from "../shell/ui.js";
5
- import { writeJsonEvent } from "../shell/output.js";
6
6
  import { canPrompt } from "../shell/runtime.js";
7
- import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
8
- import { requireComputeAuth } from "../lib/auth/guard.js";
9
- import { readAuthState } from "../lib/auth/auth-ops.js";
10
- import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
11
- import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
12
- import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
13
- import { DEFAULT_LOCAL_DEV_PORT, resolveLocalBuildType, runLocalApp } from "../lib/app/local-dev.js";
14
- import { formatCommandArgument } from "../shell/command-arguments.js";
15
7
  import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
8
+ import { formatCommandArgument } from "../shell/command-arguments.js";
16
9
  import { buildProjectSetupNextActions, inferTargetName, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
17
10
  import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
18
- import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
19
- import { readLocalGitBranch } from "../lib/git/local-branch.js";
11
+ import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
12
+ import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
13
+ import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
14
+ import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
15
+ import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
16
+ import { DEFAULT_LOCAL_DEV_PORT, resolveLocalBuildType, runLocalApp } from "../lib/app/local-dev.js";
20
17
  import { resolveOrCreatePreviewBuildSettings } from "../lib/app/preview-build-settings.js";
21
18
  import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
22
19
  import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
23
- import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
24
20
  import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress } from "../lib/app/preview-progress.js";
25
21
  import { PreviewDomainApiError, createPreviewAppProvider } from "../lib/app/preview-provider.js";
26
22
  import { enforceProductionDeployGate } from "../lib/app/production-deploy-gate.js";
27
- import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
23
+ import { requireComputeAuth } from "../lib/auth/guard.js";
24
+ import { readAuthState } from "../lib/auth/auth-ops.js";
25
+ import { readLocalGitBranch } from "../lib/git/local-branch.js";
26
+ import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
27
+ import { writeJsonEvent } from "../shell/output.js";
28
28
  import { createSelectPromptPort } from "./select-prompt-port.js";
29
29
  import { requireAuthenticatedAuthState } from "./auth.js";
30
30
  import { listRealWorkspaceProjects } from "./project.js";
31
31
  import { access, readFile } from "node:fs/promises";
32
32
  import path from "node:path";
33
- import open from "open";
34
33
  import { Result, matchError } from "better-result";
34
+ import open from "open";
35
35
  //#region src/controllers/app.ts
36
36
  const DEPLOY_FRAMEWORKS = [
37
37
  "nextjs",
@@ -910,51 +910,72 @@ async function confirmDomainRemoval(context, target, hostname) {
910
910
  })) throw usageError("Custom domain removal canceled", "The command was canceled before the domain was detached.", "Rerun the command and confirm removal, or pass --yes.", [`prisma-cli app domain remove ${hostname} --app ${target.app.name} --yes`], "app");
911
911
  }
912
912
  function domainCommandError(command, error, hostname) {
913
- if (error instanceof PreviewDomainApiError) {
914
- if (command === "add" && (error.status === 400 || error.status === 422) && isDomainDnsError(error)) return domainDnsNotConfiguredError(hostname, error);
915
- if (command === "add" && error.status === 400) return new CliError({
916
- code: "DOMAIN_HOSTNAME_INVALID",
917
- domain: "app",
918
- summary: `Invalid custom domain "${hostname}"`,
919
- why: error.message,
920
- fix: "Pass a valid hostname like shop.acme.com and make sure DNS can be verified.",
921
- debug: formatDebugDetails(error),
922
- exitCode: 2,
923
- nextSteps: ["prisma-cli app domain add shop.acme.com"]
924
- });
925
- if (command === "add" && (error.status === 429 || isDomainQuotaError(error))) return new CliError({
926
- code: "DOMAIN_QUOTA_EXCEEDED",
927
- domain: "app",
928
- summary: "Custom domain quota exceeded",
929
- why: error.message,
930
- fix: "Remove an existing custom domain before adding another one.",
931
- debug: formatDebugDetails(error),
932
- exitCode: 1,
933
- nextSteps: ["prisma-cli app domain remove <hostname>"]
934
- });
935
- if (command === "add" && error.status === 409) return domainAlreadyRegisteredError(hostname, error);
936
- if (command === "add" && error.status === 422) return new CliError({
937
- code: "NO_DEPLOYMENTS",
938
- domain: "app",
939
- summary: "Custom domain requires a live production deployment",
940
- why: "The selected production app does not have a promoted version that can receive a custom domain.",
941
- fix: "Deploy the app to the production branch, then rerun the domain command.",
942
- debug: formatDebugDetails(error),
943
- exitCode: 1,
944
- nextSteps: ["prisma-cli app deploy --branch production", `prisma-cli app domain add ${hostname}`]
945
- });
946
- if ((command === "show" || command === "remove" || command === "retry" || command === "wait") && error.status === 404) return domainNotFoundError(hostname);
947
- if (command === "retry" && error.status === 409) return new CliError({
948
- code: "DOMAIN_RETRY_NOT_ELIGIBLE",
949
- domain: "app",
950
- summary: `Custom domain "${hostname}" is not eligible for retry`,
951
- why: error.message,
952
- fix: "Wait for the current verification or TLS step to finish, then rerun retry if the domain fails.",
953
- debug: formatDebugDetails(error),
954
- exitCode: 1,
955
- nextSteps: [`prisma-cli app domain show ${hostname}`]
956
- });
957
- }
913
+ if (error instanceof PreviewDomainApiError) return domainApiCommandError(command, error, hostname);
914
+ return new CliError({
915
+ code: "DEPLOY_FAILED",
916
+ domain: "app",
917
+ summary: `Custom domain ${command} failed`,
918
+ why: error instanceof Error ? error.message : String(error),
919
+ fix: "Retry the command, or rerun with --trace for more detailed diagnostics.",
920
+ debug: formatDebugDetails(error),
921
+ exitCode: 1,
922
+ nextSteps: [`prisma-cli app domain show ${hostname}`]
923
+ });
924
+ }
925
+ function domainApiCommandError(command, error, hostname) {
926
+ if (command === "add") return domainAddCommandError(error, hostname);
927
+ if (error.status === 404) return domainNotFoundError(hostname);
928
+ if (command === "retry" && error.status === 409) return domainRetryNotEligibleError(error, hostname);
929
+ return domainGenericCommandError(command, error, hostname);
930
+ }
931
+ function domainAddCommandError(error, hostname) {
932
+ if ((error.status === 400 || error.status === 422) && isDomainDnsError(error)) return domainDnsNotConfiguredError(hostname, error);
933
+ if (error.status === 400) return new CliError({
934
+ code: "DOMAIN_HOSTNAME_INVALID",
935
+ domain: "app",
936
+ summary: `Invalid custom domain "${hostname}"`,
937
+ why: error.message,
938
+ fix: "Pass a valid hostname like shop.acme.com and make sure DNS can be verified.",
939
+ debug: formatDebugDetails(error),
940
+ exitCode: 2,
941
+ nextSteps: ["prisma-cli app domain add shop.acme.com"]
942
+ });
943
+ if (error.status === 429 || isDomainQuotaError(error)) return new CliError({
944
+ code: "DOMAIN_QUOTA_EXCEEDED",
945
+ domain: "app",
946
+ summary: "Custom domain quota exceeded",
947
+ why: error.message,
948
+ fix: "Remove an existing custom domain before adding another one.",
949
+ debug: formatDebugDetails(error),
950
+ exitCode: 1,
951
+ nextSteps: ["prisma-cli app domain remove <hostname>"]
952
+ });
953
+ if (error.status === 409) return domainAlreadyRegisteredError(hostname, error);
954
+ if (error.status === 422) return new CliError({
955
+ code: "NO_DEPLOYMENTS",
956
+ domain: "app",
957
+ summary: "Custom domain requires a live production deployment",
958
+ why: "The selected production app does not have a promoted version that can receive a custom domain.",
959
+ fix: "Deploy the app to the production branch, then rerun the domain command.",
960
+ debug: formatDebugDetails(error),
961
+ exitCode: 1,
962
+ nextSteps: ["prisma-cli app deploy --branch production", `prisma-cli app domain add ${hostname}`]
963
+ });
964
+ return domainGenericCommandError("add", error, hostname);
965
+ }
966
+ function domainRetryNotEligibleError(error, hostname) {
967
+ return new CliError({
968
+ code: "DOMAIN_RETRY_NOT_ELIGIBLE",
969
+ domain: "app",
970
+ summary: `Custom domain "${hostname}" is not eligible for retry`,
971
+ why: error.message,
972
+ fix: "Wait for the current verification or TLS step to finish, then rerun retry if the domain fails.",
973
+ debug: formatDebugDetails(error),
974
+ exitCode: 1,
975
+ nextSteps: [`prisma-cli app domain show ${hostname}`]
976
+ });
977
+ }
978
+ function domainGenericCommandError(command, error, hostname) {
958
979
  return new CliError({
959
980
  code: "DEPLOY_FAILED",
960
981
  domain: "app",
@@ -1356,14 +1377,14 @@ async function requireProviderAndDeployProjectContext(context, explicitProject,
1356
1377
  };
1357
1378
  }
1358
1379
  async function resolveProjectContext(context, client, explicitProject, options) {
1359
- const authState = await requireAuthenticatedAuthState(context);
1360
- if (!authState.workspace) throw workspaceRequiredError();
1380
+ const workspace = (await requireAuthenticatedAuthState(context)).workspace;
1381
+ if (!workspace) throw workspaceRequiredError();
1361
1382
  const resolvedResult = await resolveProjectTarget({
1362
1383
  context,
1363
- workspace: authState.workspace,
1384
+ workspace,
1364
1385
  explicitProject,
1365
1386
  envProjectId: options?.envProjectId,
1366
- listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
1387
+ listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
1367
1388
  commandName: options?.commandName
1368
1389
  });
1369
1390
  if (resolvedResult.isErr()) throw projectResolutionErrorToCliError(resolvedResult.error);
@@ -1382,8 +1403,16 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1382
1403
  const workspace = (await requireAuthenticatedAuthState(context)).workspace;
1383
1404
  if (!workspace) throw workspaceRequiredError();
1384
1405
  const branch = options.branch ?? await resolveDeployBranch(context, void 0);
1385
- const projects = await listRealWorkspaceProjects(client, workspace, context.runtime.signal);
1386
- if (explicitProject) return withRemoteDeployBranch(provider, {
1406
+ return withRemoteDeployBranch(provider, await resolveDeployProjectSetup(context, provider, workspace, await listRealWorkspaceProjects(client, workspace, context.runtime.signal), explicitProject, options), branch, context.runtime.signal);
1407
+ }
1408
+ async function resolveDeployProjectSetup(context, provider, workspace, projects, explicitProject, options) {
1409
+ const selected = await resolveNonInteractiveDeployProjectSetup(context, provider, workspace, projects, explicitProject, options);
1410
+ if (selected) return selected;
1411
+ if (canPrompt(context) && !context.flags.yes) return resolveInteractiveDeployProjectSetup(context, provider, workspace, projects);
1412
+ throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
1413
+ }
1414
+ async function resolveNonInteractiveDeployProjectSetup(context, provider, workspace, projects, explicitProject, options) {
1415
+ if (explicitProject) return {
1387
1416
  workspace,
1388
1417
  project: toProjectSummary(resolveProjectForSetup(explicitProject, projects, workspace)),
1389
1418
  resolution: {
@@ -1392,11 +1421,11 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1392
1421
  targetNameSource: "explicit"
1393
1422
  },
1394
1423
  localPinAction: "linked"
1395
- }, branch, context.runtime.signal);
1424
+ };
1396
1425
  if (options.createProjectName) {
1397
1426
  const projectName = options.createProjectName.trim();
1398
1427
  if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
1399
- return withRemoteDeployBranch(provider, {
1428
+ return {
1400
1429
  workspace,
1401
1430
  project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal)),
1402
1431
  resolution: {
@@ -1405,12 +1434,12 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1405
1434
  targetNameSource: "explicit"
1406
1435
  },
1407
1436
  localPinAction: "created"
1408
- }, branch, context.runtime.signal);
1437
+ };
1409
1438
  }
1410
1439
  if (options.envProjectId) {
1411
1440
  const project = projects.find((candidate) => candidate.id === options.envProjectId);
1412
1441
  if (!project) throw projectNotFoundError(options.envProjectId, workspace);
1413
- return withRemoteDeployBranch(provider, {
1442
+ return {
1414
1443
  workspace,
1415
1444
  project: toProjectSummary(project),
1416
1445
  resolution: {
@@ -1418,14 +1447,14 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1418
1447
  targetName: options.envProjectId,
1419
1448
  targetNameSource: "env"
1420
1449
  }
1421
- }, branch, context.runtime.signal);
1450
+ };
1422
1451
  }
1423
1452
  const localPin = options.localPin;
1424
1453
  if (localPin.kind === "present") {
1425
1454
  if (localPin.pin.workspaceId !== workspace.id) throw localResolutionPinStaleError();
1426
1455
  const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
1427
1456
  if (!project) throw localResolutionPinStaleError();
1428
- return withRemoteDeployBranch(provider, {
1457
+ return {
1429
1458
  workspace,
1430
1459
  project: toProjectSummary(project),
1431
1460
  resolution: {
@@ -1433,10 +1462,10 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1433
1462
  targetName: project.name,
1434
1463
  targetNameSource: "local-pin"
1435
1464
  }
1436
- }, branch, context.runtime.signal);
1465
+ };
1437
1466
  }
1438
1467
  const platformMapping = await resolveDurablePlatformMapping();
1439
- if (platformMapping && platformMapping.workspace.id === workspace.id) return withRemoteDeployBranch(provider, {
1468
+ if (platformMapping && platformMapping.workspace.id === workspace.id) return {
1440
1469
  workspace,
1441
1470
  project: toProjectSummary(platformMapping),
1442
1471
  resolution: {
@@ -1444,9 +1473,8 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1444
1473
  targetName: platformMapping.name,
1445
1474
  targetNameSource: "platform-mapping"
1446
1475
  }
1447
- }, branch, context.runtime.signal);
1448
- if (canPrompt(context) && !context.flags.yes) return withRemoteDeployBranch(provider, await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects), branch, context.runtime.signal);
1449
- throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
1476
+ };
1477
+ return null;
1450
1478
  }
1451
1479
  async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects) {
1452
1480
  const setup = await promptForProjectSetupChoice({
@@ -1890,41 +1918,7 @@ function deployFailedError(summary, error, nextSteps) {
1890
1918
  function appDeployFailedError(error, progress) {
1891
1919
  const why = error instanceof Error ? error.message : String(error);
1892
1920
  const debug = formatDebugDetails(error);
1893
- if (progress.buildStarted && !progress.buildCompleted) {
1894
- const standaloneOutputFailure = isNextStandaloneOutputFailure(why);
1895
- const fix = standaloneOutputFailure ? "Add output: \"standalone\" to next.config.*, then rerun deploy." : "Inspect the build output above, fix the error, and redeploy.";
1896
- const nextSteps = standaloneOutputFailure ? ["Add output: \"standalone\" to next.config.*, then rerun prisma-cli app deploy"] : [];
1897
- const nextActions = standaloneOutputFailure ? [{
1898
- kind: "edit-file",
1899
- journey: "deploy-app",
1900
- label: "Add Next.js standalone output",
1901
- reason: "Prisma Compute needs Next.js standalone output to build a deployable server artifact."
1902
- }, {
1903
- kind: "run-command",
1904
- journey: "deploy-app",
1905
- label: "Rerun deploy",
1906
- command: "prisma-cli app deploy"
1907
- }] : [];
1908
- return new CliError({
1909
- code: "BUILD_FAILED",
1910
- domain: "app",
1911
- summary: "Build failed locally.",
1912
- why,
1913
- fix,
1914
- debug,
1915
- meta: { phase: "build" },
1916
- humanLines: [
1917
- "Build failed locally.",
1918
- "",
1919
- `✗ Built ${why}`,
1920
- "",
1921
- `Fix: ${fix}`
1922
- ],
1923
- exitCode: 1,
1924
- nextSteps,
1925
- nextActions
1926
- });
1927
- }
1921
+ if (progress.buildStarted && !progress.buildCompleted) return appBuildFailedError(why, debug);
1928
1922
  if (!progress.buildStarted) return deployFailedError("App deploy failed", error, ["prisma-cli app deploy"]);
1929
1923
  const phaseHeadline = progress.containerLive ? "The deployment started, but the app is not ready yet." : "Deploy failed after the build completed.";
1930
1924
  const recoveryLines = progress.versionId ? ["See what happened", `prisma-cli app logs --deployment ${progress.versionId}`] : ["Fix", "Retry the command, or rerun with --trace for more detailed diagnostics."];
@@ -1965,6 +1959,41 @@ function appDeployFailedError(error, progress) {
1965
1959
  nextSteps: []
1966
1960
  });
1967
1961
  }
1962
+ function appBuildFailedError(why, debug) {
1963
+ const standaloneOutputFailure = isNextStandaloneOutputFailure(why);
1964
+ const fix = standaloneOutputFailure ? "Add output: \"standalone\" to next.config.*, then rerun deploy." : "Inspect the build output above, fix the error, and redeploy.";
1965
+ const nextSteps = standaloneOutputFailure ? ["Add output: \"standalone\" to next.config.*, then rerun prisma-cli app deploy"] : [];
1966
+ const nextActions = standaloneOutputFailure ? [{
1967
+ kind: "edit-file",
1968
+ journey: "deploy-app",
1969
+ label: "Add Next.js standalone output",
1970
+ reason: "Prisma Compute needs Next.js standalone output to build a deployable server artifact."
1971
+ }, {
1972
+ kind: "run-command",
1973
+ journey: "deploy-app",
1974
+ label: "Rerun deploy",
1975
+ command: "prisma-cli app deploy"
1976
+ }] : [];
1977
+ return new CliError({
1978
+ code: "BUILD_FAILED",
1979
+ domain: "app",
1980
+ summary: "Build failed locally.",
1981
+ why,
1982
+ fix,
1983
+ debug,
1984
+ meta: { phase: "build" },
1985
+ humanLines: [
1986
+ "Build failed locally.",
1987
+ "",
1988
+ `✗ Built ${why}`,
1989
+ "",
1990
+ `Fix: ${fix}`
1991
+ ],
1992
+ exitCode: 1,
1993
+ nextSteps,
1994
+ nextActions
1995
+ });
1996
+ }
1968
1997
  function localResolutionPinStaleError() {
1969
1998
  return new CliError({
1970
1999
  code: "LOCAL_STATE_STALE",
@@ -1,6 +1,6 @@
1
1
  import { CliError, authRequiredError, workspaceRequiredError } from "../shell/errors.js";
2
- import { requireComputeAuth } from "../lib/auth/guard.js";
3
2
  import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
3
+ import { requireComputeAuth } from "../lib/auth/guard.js";
4
4
  import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
5
5
  import { requireAuthenticatedAuthState } from "./auth.js";
6
6
  import { listRealWorkspaceProjects } from "./project.js";
@@ -1,6 +1,6 @@
1
1
  import { CliError, authRequiredError, usageError, workspaceRequiredError } from "../shell/errors.js";
2
- import { requireComputeAuth } from "../lib/auth/guard.js";
3
2
  import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
3
+ import { requireComputeAuth } from "../lib/auth/guard.js";
4
4
  import { requireAuthenticatedAuthState } from "./auth.js";
5
5
  import { listFixtureWorkspaceProjects, listRealWorkspaceProjects } from "./project.js";
6
6
  import { createManagementDatabaseProvider, normalizeConnection, normalizeDatabase } from "../lib/database/provider.js";
@@ -1,19 +1,19 @@
1
1
  import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../shell/errors.js";
2
2
  import { renderSummaryLine } from "../shell/ui.js";
3
3
  import { canPrompt } from "../shell/runtime.js";
4
- import { requireComputeAuth } from "../lib/auth/guard.js";
5
- import { formatCommandArgument } from "../shell/command-arguments.js";
6
4
  import { readLocalResolutionPin } from "../lib/project/local-pin.js";
5
+ import { formatCommandArgument } from "../shell/command-arguments.js";
7
6
  import { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, projectResolutionErrorToCliError, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
8
7
  import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
9
- import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
10
8
  import { createPreviewAppProvider } from "../lib/app/preview-provider.js";
9
+ import { requireComputeAuth } from "../lib/auth/guard.js";
10
+ import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
11
11
  import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
12
12
  import { requireAuthenticatedAuthState } from "./auth.js";
13
13
  import { parseGitHubRepositoryUrl, readGitOriginRemote } from "../adapters/git.js";
14
14
  import { createProjectUseCases } from "../use-cases/project.js";
15
- import open from "open";
16
15
  import { matchError } from "better-result";
16
+ import open from "open";
17
17
  //#region src/controllers/project.ts
18
18
  const GITHUB_INSTALL_POLL_INTERVAL_MS = 2e3;
19
19
  const GITHUB_INSTALL_POLL_TIMEOUT_MS = 12e4;
@@ -1,7 +1,7 @@
1
1
  import { CliError, usageError } from "../../shell/errors.js";
2
+ import { confirmPrompt } from "../../shell/prompt.js";
2
3
  import { renderSummaryLine } from "../../shell/ui.js";
3
4
  import { canPrompt } from "../../shell/runtime.js";
4
- import { confirmPrompt } from "../../shell/prompt.js";
5
5
  import { formatCommandArgument } from "../../shell/command-arguments.js";
6
6
  import "../project/setup.js";
7
7
  import { hasBranchDatabaseSignal, inspectBranchDatabaseSignal, runBranchDatabaseSchemaSetup } from "./branch-database.js";
@@ -9,6 +9,22 @@ import path from "node:path";
9
9
  //#region src/lib/app/branch-database-deploy.ts
10
10
  async function maybeSetupBranchDatabase(context, provider, projectId, branch, options) {
11
11
  if (options.db === false) return emptyBranchDatabaseSetupOutcome();
12
+ const preflight = branchDatabasePreflight(branch, options);
13
+ if (preflight) return preflight;
14
+ const envState = await inspectBranchDatabaseEnv(provider, projectId, branch, context.runtime.signal);
15
+ const existingEnvOutcome = existingBranchDatabaseEnvOutcome(context, branch, getTargetDatabaseEnvVarKeys(envState), envState, options.db);
16
+ if (existingEnvOutcome) return existingEnvOutcome;
17
+ const localSignal = await inspectBranchDatabaseSignal(context.runtime.cwd, context.runtime.signal);
18
+ if (localSignal.unsupportedSchema) {
19
+ if (options.db === true) throw unsupportedBranchDatabaseSchemaError(localSignal.unsupportedSchema, branch, context);
20
+ return emptyBranchDatabaseSetupOutcome();
21
+ }
22
+ const promptOutcome = await branchDatabasePromptOutcome(context, branch, localSignal, envState, options.db);
23
+ if (promptOutcome) return promptOutcome;
24
+ if (options.db === true && !canPrompt(context) && !context.flags.yes) throw nonInteractiveDatabaseSetupRequiresYesError(branch);
25
+ return setupBranchDatabase(context, provider, projectId, branch, localSignal, envState);
26
+ }
27
+ function branchDatabasePreflight(branch, options) {
12
28
  if (hasProvidedDatabaseEnvVars(options.providedEnvVars)) {
13
29
  if (options.db === true) throw usageError("Database setup cannot be combined with provided database env vars", "The deploy command received --db and a DATABASE_URL or DIRECT_URL value from --env.", "Remove the --env database value to let --db create and wire a database, or remove --db to deploy with the provided value.", ["prisma-cli app deploy --db", "prisma-cli app deploy --env DATABASE_URL=postgresql://example"], "app");
14
30
  return emptyBranchDatabaseSetupOutcome();
@@ -17,46 +33,40 @@ async function maybeSetupBranchDatabase(context, provider, projectId, branch, op
17
33
  if (options.db === true) throw productionDatabaseSetupAfterFirstDeployError();
18
34
  return emptyBranchDatabaseSetupOutcome();
19
35
  }
20
- const envState = await inspectBranchDatabaseEnv(provider, projectId, branch, context.runtime.signal);
21
- const targetEnvVars = getTargetDatabaseEnvVarKeys(envState);
22
- if (hasExistingDatabaseEnvForTarget(branch, envState)) {
23
- const warning = options.db === true ? existingDatabaseEnvWarning(branch, targetEnvVars) : null;
24
- if (warning) emitBranchDatabaseWarning(context, warning);
36
+ return null;
37
+ }
38
+ function existingBranchDatabaseEnvOutcome(context, branch, targetEnvVars, envState, requested) {
39
+ if (!hasExistingDatabaseEnvForTarget(branch, envState)) return null;
40
+ const warning = requested === true ? existingDatabaseEnvWarning(branch, targetEnvVars) : null;
41
+ if (warning) emitBranchDatabaseWarning(context, warning);
42
+ return {
43
+ result: requested === true ? {
44
+ status: "skipped",
45
+ reason: existingDatabaseEnvReason(branch),
46
+ envVars: targetEnvVars,
47
+ schema: null
48
+ } : void 0,
49
+ warnings: warning ? [warning] : []
50
+ };
51
+ }
52
+ async function branchDatabasePromptOutcome(context, branch, localSignal, envState, requested) {
53
+ if (requested === true) return null;
54
+ if (!(hasBranchDatabaseSignal(localSignal) || Boolean(envState.inheritedPreviewDatabaseUrl))) return emptyBranchDatabaseSetupOutcome();
55
+ if (!canPrompt(context) || context.flags.yes) {
56
+ const warning = databasePromptSuppressedWarning(branch);
57
+ emitBranchDatabaseWarning(context, warning);
25
58
  return {
26
- result: options.db === true ? {
27
- status: "skipped",
28
- reason: existingDatabaseEnvReason(branch),
29
- envVars: targetEnvVars,
30
- schema: null
31
- } : void 0,
32
- warnings: warning ? [warning] : []
59
+ result: void 0,
60
+ warnings: [warning]
33
61
  };
34
62
  }
35
- const localSignal = await inspectBranchDatabaseSignal(context.runtime.cwd, context.runtime.signal);
36
- if (localSignal.unsupportedSchema) {
37
- if (options.db === true) throw unsupportedBranchDatabaseSchemaError(localSignal.unsupportedSchema, branch, context);
38
- return emptyBranchDatabaseSetupOutcome();
39
- }
40
- const hasSignal = hasBranchDatabaseSignal(localSignal) || Boolean(envState.inheritedPreviewDatabaseUrl);
41
- if (options.db !== true) {
42
- if (!hasSignal) return emptyBranchDatabaseSetupOutcome();
43
- if (!canPrompt(context) || context.flags.yes) {
44
- const warning = databasePromptSuppressedWarning(branch);
45
- emitBranchDatabaseWarning(context, warning);
46
- return {
47
- result: void 0,
48
- warnings: [warning]
49
- };
50
- }
51
- maybeRenderBranchDatabaseSignal(context, branch, localSignal, envState);
52
- if (!await confirmPrompt({
53
- input: context.runtime.stdin,
54
- output: context.output.stderr,
55
- message: databasePromptMessage(branch),
56
- initialValue: false
57
- })) return emptyBranchDatabaseSetupOutcome();
58
- } else if (!canPrompt(context) && !context.flags.yes) throw nonInteractiveDatabaseSetupRequiresYesError(branch);
59
- return setupBranchDatabase(context, provider, projectId, branch, localSignal, envState);
63
+ maybeRenderBranchDatabaseSignal(context, branch, localSignal, envState);
64
+ return await confirmPrompt({
65
+ input: context.runtime.stdin,
66
+ output: context.output.stderr,
67
+ message: databasePromptMessage(branch),
68
+ initialValue: false
69
+ }) ? null : emptyBranchDatabaseSetupOutcome();
60
70
  }
61
71
  async function setupBranchDatabase(context, provider, projectId, branch, signal, envState) {
62
72
  emitBranchDatabaseProgress(context, "pending", "Creating database");
@@ -73,15 +83,16 @@ async function setupBranchDatabase(context, provider, projectId, branch, signal,
73
83
  let schemaSetup = null;
74
84
  const warnings = [];
75
85
  let skippedSchemaWarning = null;
76
- if (signal.schema) {
77
- emitBranchDatabaseProgress(context, "pending", `Applying database schema with ${formatSchemaSetupCommand(signal.schema.command)}`);
86
+ const schema = signal.schema;
87
+ if (schema) {
88
+ emitBranchDatabaseProgress(context, "pending", `Applying database schema with ${formatSchemaSetupCommand(schema.command)}`);
78
89
  schemaSetup = await runBranchDatabaseSchemaSetup({
79
90
  context,
80
- schema: signal.schema,
91
+ schema,
81
92
  databaseUrl: database.databaseUrl,
82
93
  directUrl: database.directUrl
83
94
  }).catch((error) => {
84
- throw schemaSetupFailedError(error, signal.schema, branch, context.runtime.cwd);
95
+ throw schemaSetupFailedError(error, schema, branch, context.runtime.cwd);
85
96
  });
86
97
  emitBranchDatabaseProgress(context, "success", "Applied database schema");
87
98
  } else skippedSchemaWarning = "No supported Prisma schema source was found. Database env vars were created, but schema setup was skipped.";