@prisma/cli 3.0.0-beta.3 → 3.0.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +6 -15
  2. package/dist/adapters/local-state.js +15 -4
  3. package/dist/adapters/mock-api.js +244 -0
  4. package/dist/adapters/token-storage.js +335 -34
  5. package/dist/cli.js +7 -7
  6. package/dist/cli2.js +24 -5
  7. package/dist/commands/agent/index.js +60 -0
  8. package/dist/commands/app/index.js +91 -61
  9. package/dist/commands/auth/index.js +55 -2
  10. package/dist/commands/branch/index.js +2 -27
  11. package/dist/commands/bucket/index.js +123 -0
  12. package/dist/commands/build/index.js +29 -0
  13. package/dist/commands/database/index.js +249 -0
  14. package/dist/commands/env.js +8 -4
  15. package/dist/commands/feedback/index.js +20 -0
  16. package/dist/commands/git/index.js +1 -1
  17. package/dist/commands/init/index.js +33 -0
  18. package/dist/commands/project/index.js +54 -5
  19. package/dist/controllers/agent-setup.js +52 -0
  20. package/dist/controllers/agent.js +228 -0
  21. package/dist/controllers/app-env-api.js +55 -0
  22. package/dist/controllers/app-env-file.js +181 -0
  23. package/dist/controllers/app-env.js +227 -104
  24. package/dist/controllers/app.js +746 -306
  25. package/dist/controllers/auth.js +247 -3
  26. package/dist/controllers/branch.js +78 -48
  27. package/dist/controllers/bucket.js +278 -0
  28. package/dist/controllers/build.js +88 -0
  29. package/dist/controllers/database.js +567 -0
  30. package/dist/controllers/feedback.js +86 -0
  31. package/dist/controllers/init.js +753 -0
  32. package/dist/controllers/project.js +377 -22
  33. package/dist/controllers/select-prompt-port.js +1 -0
  34. package/dist/lib/agent/cli-command.js +20 -0
  35. package/dist/lib/agent/constants.js +12 -0
  36. package/dist/lib/agent/package-manager.js +99 -0
  37. package/dist/lib/agent/setup-status.js +83 -0
  38. package/dist/lib/app/{preview-provider.js → app-provider.js} +137 -88
  39. package/dist/lib/app/branch-database-api.js +102 -0
  40. package/dist/lib/app/branch-database-deploy.js +326 -0
  41. package/dist/lib/app/branch-database.js +216 -0
  42. package/dist/lib/app/build-settings.js +93 -0
  43. package/dist/lib/app/build.js +83 -0
  44. package/dist/lib/app/bun-project.js +3 -4
  45. package/dist/lib/app/compute-config.js +145 -0
  46. package/dist/lib/app/deploy-plan.js +59 -0
  47. package/dist/lib/app/{preview-progress.js → deploy-progress.js} +12 -12
  48. package/dist/lib/app/env-config.js +1 -1
  49. package/dist/lib/app/env-file.js +82 -0
  50. package/dist/lib/app/env-vars.js +28 -2
  51. package/dist/lib/app/local-dev.js +3 -60
  52. package/dist/lib/app/production-deploy-gate.js +162 -0
  53. package/dist/lib/app/read-branch.js +30 -0
  54. package/dist/lib/auth/auth-ops.js +10 -4
  55. package/dist/lib/auth/guard.js +4 -1
  56. package/dist/lib/auth/login.js +33 -26
  57. package/dist/lib/auth/recipient.js +42 -0
  58. package/dist/lib/bucket/provider.js +139 -0
  59. package/dist/lib/database/provider.js +378 -0
  60. package/dist/lib/diagnostics.js +15 -0
  61. package/dist/lib/fs/home-path.js +24 -0
  62. package/dist/lib/git/local-branch.js +53 -0
  63. package/dist/lib/git/local-status.js +57 -0
  64. package/dist/lib/project/interactive-setup.js +5 -4
  65. package/dist/lib/project/local-pin.js +171 -41
  66. package/dist/lib/project/provider.js +92 -0
  67. package/dist/lib/project/resolution.js +199 -48
  68. package/dist/lib/project/setup.js +67 -20
  69. package/dist/output/patterns.js +1 -1
  70. package/dist/presenters/agent.js +74 -0
  71. package/dist/presenters/app-env.js +149 -14
  72. package/dist/presenters/app.js +208 -27
  73. package/dist/presenters/auth.js +99 -2
  74. package/dist/presenters/branch.js +37 -102
  75. package/dist/presenters/bucket.js +174 -0
  76. package/dist/presenters/database.js +448 -0
  77. package/dist/presenters/feedback.js +26 -0
  78. package/dist/presenters/init.js +30 -0
  79. package/dist/presenters/project.js +139 -27
  80. package/dist/presenters/verbose-context.js +64 -0
  81. package/dist/shell/cli-command.js +12 -0
  82. package/dist/shell/command-arguments.js +7 -1
  83. package/dist/shell/command-meta.js +458 -17
  84. package/dist/shell/command-runner.js +58 -18
  85. package/dist/shell/diagnostics-output.js +57 -0
  86. package/dist/shell/errors.js +56 -1
  87. package/dist/shell/help.js +31 -20
  88. package/dist/shell/output.js +72 -1
  89. package/dist/shell/prompt.js +12 -5
  90. package/dist/shell/runtime.js +8 -4
  91. package/dist/shell/ui.js +42 -3
  92. package/dist/shell/update-check.js +2 -2
  93. package/dist/use-cases/auth.js +68 -1
  94. package/dist/use-cases/branch.js +20 -68
  95. package/dist/use-cases/create-cli-gateways.js +2 -17
  96. package/dist/use-cases/project.js +2 -1
  97. package/package.json +21 -4
  98. package/dist/lib/app/preview-build.js +0 -312
  99. package/dist/lib/app/preview-interaction.js +0 -5
@@ -1,54 +1,81 @@
1
- import { SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
2
- import { FileTokenStorage } from "../adapters/token-storage.js";
1
+ import { formatCommandArgument } from "../shell/command-arguments.js";
3
2
  import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../shell/errors.js";
4
3
  import { renderCommandHeader } from "../shell/ui.js";
5
- import { writeJsonEvent } from "../shell/output.js";
6
4
  import { canPrompt } from "../shell/runtime.js";
5
+ import { writeJsonEvent } from "../shell/output.js";
6
+ import { SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
7
+ import { FileTokenStorage } from "../adapters/token-storage.js";
8
+ import { PRISMA_APP_CONFIG_FILENAME, detectLegacyBuildSettings, resolveConfiguredAppBuildSettings, resolveInferredAppBuildSettings } from "../lib/app/build-settings.js";
9
+ import { APP_BUILD_TYPES, APP_BUILD_TYPE_LABELS, RESOLVED_APP_BUILD_TYPES, executeAppBuild } from "../lib/app/build.js";
10
+ import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
11
+ import { DomainApiError, createAppProvider } from "../lib/app/app-provider.js";
7
12
  import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
13
+ import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
14
+ import { buildProjectSetupNextActions, inferTargetName, localProjectWorkspaceMismatchError, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
15
+ import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
16
+ import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
17
+ import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
18
+ import { COMPUTE_CONFIG_FILENAME as COMPUTE_CONFIG_FILENAME$1, ComputeConfigTargetRequiredError, computeConfigErrorToCliError, computeFrameworkToBuildType, computeTargetAppDir, inferComputeTargetFromCwd, loadComputeConfig as loadComputeConfig$1, mergeComputeDeployInputs, mergeComputeLocalInputs, selectComputeDeployTarget } from "../lib/app/compute-config.js";
19
+ import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
20
+ import { describeDeployAllFailure, perAppInputsForDeployAll, planAppDeploy } from "../lib/app/deploy-plan.js";
21
+ import { createDeployProgress, createDeployProgressState, createPromoteProgress } from "../lib/app/deploy-progress.js";
22
+ import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
23
+ import { DEFAULT_LOCAL_DEV_PORT, runLocalApp } from "../lib/app/local-dev.js";
24
+ import { enforceProductionDeployGate } from "../lib/app/production-deploy-gate.js";
25
+ import { resolveReadBranch } from "../lib/app/read-branch.js";
8
26
  import { requireComputeAuth } from "../lib/auth/guard.js";
9
27
  import { readAuthState } from "../lib/auth/auth-ops.js";
10
- import { parseEnvAssignments } 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
- import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
16
- import { buildProjectSetupNextActions, inferTargetName, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
17
- import { bindProjectToDirectory, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
28
+ import { readLocalGitBranch } from "../lib/git/local-branch.js";
18
29
  import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
19
- import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
20
- import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
21
- import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress } from "../lib/app/preview-progress.js";
22
- import { PreviewDomainApiError, createPreviewAppProvider } from "../lib/app/preview-provider.js";
23
- import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
30
+ import { maybePromptForAgentSetup } from "./agent-setup.js";
24
31
  import { createSelectPromptPort } from "./select-prompt-port.js";
25
32
  import { requireAuthenticatedAuthState } from "./auth.js";
26
33
  import { listRealWorkspaceProjects } from "./project.js";
27
- import { access, readFile } from "node:fs/promises";
28
34
  import path from "node:path";
35
+ import { access } from "node:fs/promises";
36
+ import { COMPUTE_REGIONS, ENTRYPOINT_BUILD_TYPES, FRAMEWORKS, LOCAL_DEV_BUILD_TYPES, frameworkByKey, frameworkFromAlias, isConfigBackedBuildType } from "@prisma/compute-sdk/config";
37
+ import { detectComputeAppFromDirectory } from "@prisma/compute-sdk/config/directory";
38
+ import { Result, matchError } from "better-result";
29
39
  import open from "open";
30
40
  //#region src/controllers/app.ts
31
- const DEPLOY_FRAMEWORKS = [
32
- "nextjs",
33
- "hono",
34
- "tanstack-start",
35
- "bun"
36
- ];
37
- const TANSTACK_START_PACKAGES = ["@tanstack/react-start", "@tanstack/solid-start"];
38
41
  const FRAMEWORK_DEFAULT_HTTP_PORT = 3e3;
39
42
  const PRISMA_PROJECT_ID_ENV_VAR = "PRISMA_PROJECT_ID";
40
43
  const PRISMA_APP_ID_ENV_VAR = "PRISMA_APP_ID";
44
+ const COMPUTE_REGION_IDS = new Set(COMPUTE_REGIONS);
41
45
  function isRealMode(context) {
42
46
  return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
43
47
  }
44
- async function runAppBuild(context, entrypoint, requestedBuildType) {
45
- const buildType = normalizeBuildType(requestedBuildType);
46
- assertSupportedEntrypoint(buildType, entrypoint, "build");
48
+ async function runAppBuild(context, options) {
49
+ const compute = await resolveComputeTargetOrThrow(context, options?.configTarget, "build");
50
+ const merged = mergeComputeLocalInputs({
51
+ cli: {
52
+ entrypoint: options?.entrypoint,
53
+ buildType: options?.buildType
54
+ },
55
+ target: compute.target
56
+ });
57
+ const appDir = await resolveComputeAppDir(context, compute);
58
+ let buildType = normalizeBuildType(merged.buildType);
59
+ if (compute.target?.build && buildType === "auto") {
60
+ const detected = await detectDeployFramework(appDir, context.runtime.signal);
61
+ if (!detected) throw frameworkNotDetectedError(appDir);
62
+ buildType = detected.buildType;
63
+ }
64
+ assertSupportedEntrypoint(buildType, merged.entrypoint, "build");
65
+ if (compute.target?.build && buildType !== "auto") assertConfigBackedBuildSettings(buildType);
66
+ const buildSettings = compute.config && compute.target?.build && isConfigBackedBuildType(buildType) ? (await resolveConfiguredAppBuildSettings({
67
+ appPath: appDir,
68
+ buildType,
69
+ configured: compute.target.build,
70
+ configPath: compute.config.configPath,
71
+ signal: context.runtime.signal
72
+ })).settings : void 0;
47
73
  try {
48
- const { artifact, buildType: actualBuildType } = await executePreviewBuild({
49
- appPath: context.runtime.cwd,
50
- entrypoint,
74
+ const { artifact, buildType: actualBuildType } = await executeAppBuild({
75
+ appPath: appDir,
76
+ entrypoint: merged.entrypoint,
51
77
  buildType,
78
+ buildSettings,
52
79
  signal: context.runtime.signal
53
80
  });
54
81
  return {
@@ -62,21 +89,38 @@ async function runAppBuild(context, entrypoint, requestedBuildType) {
62
89
  nextSteps: ["prisma-cli app deploy"]
63
90
  };
64
91
  } catch (error) {
65
- if (buildType === "auto" && isAutoBuildDetectionError(error)) throw usageError("App build requires an explicit framework when detection is ambiguous", `This preview auto-detects clear project shapes for ${RESOLVED_PREVIEW_BUILD_TYPES.map(formatBuildTypeName).join(", ")}.`, "Pass a supported --build-type value, or pass --entry <path> for a Bun app.", getBuildTypeExamples("build"), "app");
92
+ if (buildType === "auto" && isAutoBuildDetectionError(error)) throw usageError("App build requires an explicit framework when detection is ambiguous", `This preview auto-detects clear project shapes for ${RESOLVED_APP_BUILD_TYPES.map(formatBuildTypeName).join(", ")}.`, "Pass a supported --build-type value, or pass --entry <path> for a Bun app.", getBuildTypeExamples("build"), "app");
66
93
  throw buildFailedError("Local app build failed", error);
67
94
  }
68
95
  }
69
- async function runAppRun(context, entrypoint, requestedBuildType, requestedPort) {
96
+ async function runAppRun(context, options) {
70
97
  if (context.flags.json) throw usageError("App run does not support --json", "This command streams the framework dev server directly and cannot return structured JSON.", "Rerun without --json to pass framework logs through directly.", ["prisma-cli app run"], "app");
71
- const buildType = normalizeBuildType(requestedBuildType);
72
- assertSupportedEntrypoint(buildType, entrypoint, "run");
73
- const port = parseLocalPort(requestedPort);
74
- const resolvedBuildType = await requireLocalBuildType(context, buildType, "run");
98
+ const compute = await resolveComputeTargetOrThrow(context, options?.configTarget, "run");
99
+ const merged = mergeComputeLocalInputs({
100
+ cli: {
101
+ entrypoint: options?.entrypoint,
102
+ buildType: options?.buildType,
103
+ port: options?.port
104
+ },
105
+ target: compute.target
106
+ });
107
+ if (merged.buildTypeFromConfig && compute.target?.framework && !frameworkByKey(compute.target.framework).hasLocalDevServer) throw usageError(`App run does not support the ${compute.target?.framework} framework yet`, `${compute.config?.relativeConfigPath ?? COMPUTE_CONFIG_FILENAME$1} sets a framework that has no local dev server in the current preview.`, "Run the framework dev server directly, or pass --build-type nextjs or --build-type bun to override.", ["prisma-cli app run --build-type nextjs", "prisma-cli app run --build-type bun --entry server.ts"], "app");
108
+ const appDir = await resolveComputeAppDir(context, compute);
109
+ const buildType = normalizeBuildType(merged.buildType);
110
+ assertSupportedEntrypoint(buildType, merged.entrypoint, "run");
111
+ const port = parseLocalPort(merged.port);
112
+ const framework = await resolveLocalRunFramework(context, {
113
+ requestedBuildType: buildType,
114
+ configFramework: compute.target?.framework ?? null,
115
+ appDir,
116
+ entrypoint: merged.entrypoint
117
+ });
118
+ const entrypoint = framework.buildType === "bun" ? await resolveDeployEntrypoint(appDir, framework, merged.entrypoint, context.runtime.signal) : merged.entrypoint;
75
119
  let runResult;
76
120
  try {
77
121
  runResult = await runLocalApp({
78
- appPath: context.runtime.cwd,
79
- buildType: resolvedBuildType,
122
+ appPath: appDir,
123
+ buildType: framework.buildType,
80
124
  entrypoint,
81
125
  port,
82
126
  env: context.runtime.env,
@@ -101,6 +145,107 @@ async function runAppRun(context, entrypoint, requestedBuildType, requestedPort)
101
145
  }
102
146
  async function runAppDeploy(context, appName, options) {
103
147
  ensurePreviewAppMode(context);
148
+ const loaded = await loadComputeConfig$1(context.runtime.cwd, context.runtime.signal);
149
+ if (loaded.isErr()) throw computeConfigErrorToCliError(loaded.error, "deploy");
150
+ const config = loaded.value;
151
+ const plan = planAppDeploy({
152
+ config,
153
+ requestedTarget: options?.configTarget ?? (config ? inferComputeTargetFromCwd(config, context.runtime.cwd) : void 0),
154
+ hasCreateProject: options?.createProjectName !== void 0
155
+ });
156
+ if (plan.mode === "all") return runAppDeployAll(context, config, plan.targets, appName, options);
157
+ return runSingleAppDeploy(context, appName, options, config);
158
+ }
159
+ async function runAppDeployAll(context, config, plannedTargets, appName, options) {
160
+ assertNoPerAppInputsForDeployAll(context, plannedTargets, appName, options);
161
+ const deployments = [];
162
+ const warnings = [];
163
+ for (const planned of plannedTargets) {
164
+ maybeRenderDeployAllTargetHeader(context, planned);
165
+ const targetOptions = {
166
+ ...options,
167
+ configTarget: planned.targetKey,
168
+ createProjectName: planned.bindsCreateProject ? options?.createProjectName : void 0
169
+ };
170
+ try {
171
+ const single = await runSingleAppDeploy(context, void 0, targetOptions, config);
172
+ deployments.push({
173
+ target: planned.targetKey,
174
+ result: single.result
175
+ });
176
+ warnings.push(...single.warnings);
177
+ } catch (error) {
178
+ throw deployAllFailedError(error, plannedTargets, planned.index, deployments);
179
+ }
180
+ }
181
+ return {
182
+ command: "app.deploy",
183
+ result: { deployments },
184
+ warnings,
185
+ nextSteps: ["prisma-cli app list-deploys <app>"]
186
+ };
187
+ }
188
+ function assertNoPerAppInputsForDeployAll(context, plannedTargets, appName, options) {
189
+ const used = perAppInputsForDeployAll({
190
+ appName,
191
+ framework: options?.framework,
192
+ entrypoint: options?.entrypoint,
193
+ httpPort: options?.httpPort,
194
+ region: options?.region,
195
+ envAssignments: options?.envAssignments,
196
+ appIdEnvVar: {
197
+ name: PRISMA_APP_ID_ENV_VAR,
198
+ value: readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR)
199
+ }
200
+ });
201
+ if (used.length === 0) return;
202
+ const targetKeys = plannedTargets.map((target) => target.targetKey);
203
+ throw usageError(`Deploying all apps does not accept ${used.join(", ")}`, `Without a target, app deploy deploys every configured app (${targetKeys.join(", ")}), so per-app inputs are ambiguous.`, "Pass the app target to apply per-app inputs to one app, or remove them to deploy all apps.", targetKeys.map((target) => `prisma-cli app deploy ${target}`), "app");
204
+ }
205
+ function maybeRenderDeployAllTargetHeader(context, planned) {
206
+ if (context.flags.json || context.flags.quiet) return;
207
+ context.output.stderr.write(`${planned.index > 0 ? "\n" : ""}── ${planned.targetKey} (${planned.index + 1}/${planned.total}) ──\n\n`);
208
+ }
209
+ function deployAllFailedError(error, plannedTargets, failedIndex, deployments) {
210
+ if (!(error instanceof CliError)) return error;
211
+ const failure = describeDeployAllFailure({
212
+ targetKeys: plannedTargets.map((target) => target.targetKey),
213
+ failedIndex,
214
+ completed: deployments.map(({ target, result }) => ({
215
+ target,
216
+ deploymentId: result.deployment.id,
217
+ url: result.deployment.url
218
+ }))
219
+ });
220
+ const contextSentence = failure.contextLines.join(" ");
221
+ return new CliError({
222
+ code: error.code,
223
+ domain: error.domain,
224
+ summary: error.summary,
225
+ why: error.humanLines ? error.why : [error.why, contextSentence].filter(Boolean).join(" "),
226
+ fix: error.fix,
227
+ debug: error.debug,
228
+ where: error.where,
229
+ meta: {
230
+ ...error.meta,
231
+ deployAll: {
232
+ failedTarget: failure.failedTarget,
233
+ completed: failure.completed,
234
+ notAttempted: failure.notAttempted
235
+ }
236
+ },
237
+ docsUrl: error.docsUrl,
238
+ exitCode: error.exitCode,
239
+ nextSteps: error.nextSteps,
240
+ nextActions: error.nextActions,
241
+ humanLines: error.humanLines ? [
242
+ ...error.humanLines,
243
+ "",
244
+ ...failure.contextLines
245
+ ] : void 0
246
+ });
247
+ }
248
+ async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
104
249
  const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
105
250
  const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
106
251
  assertExclusiveDeployProjectInputs({
@@ -108,42 +253,67 @@ async function runAppDeploy(context, appName, options) {
108
253
  createProjectName: options?.createProjectName,
109
254
  envProjectId
110
255
  });
111
- const skipLocalPin = Boolean(envProjectId || options?.projectRef || options?.createProjectName);
112
- const localPin = skipLocalPin ? { kind: "missing" } : await readLocalResolutionPin(context.runtime.cwd, context.runtime.signal);
113
- if (!skipLocalPin && localPin.kind === "invalid") throw localResolutionPinStaleError();
256
+ const computeConfig = await resolveComputeTargetOrThrow(context, options?.configTarget, "deploy", { preloaded: preloadedConfig });
257
+ const merged = mergeComputeDeployInputs({
258
+ cli: {
259
+ framework: options?.framework,
260
+ entrypoint: options?.entrypoint,
261
+ httpPort: options?.httpPort,
262
+ region: options?.region,
263
+ envInputs: options?.envAssignments
264
+ },
265
+ target: computeConfig.target,
266
+ configFilename: computeConfig.config?.relativeConfigPath ?? COMPUTE_CONFIG_FILENAME$1
267
+ });
268
+ const appDir = await resolveComputeAppDir(context, computeConfig);
269
+ const projectDir = computeConfig.config?.configDir ?? context.runtime.cwd;
270
+ const agentSetupWarnings = await maybePromptForAgentSetup(context, projectDir);
271
+ const localPinReadResult = Boolean(envProjectId || options?.projectRef || options?.createProjectName) ? Result.ok({ kind: "missing" }) : await readLocalResolutionPin(projectDir, context.runtime.signal);
272
+ if (localPinReadResult.isErr()) throw localPinReadErrorToDeployError(localPinReadResult.error);
273
+ const localPin = localPinReadResult.value;
114
274
  const branch = await resolveDeployBranch(context, options?.branchName);
115
- if (options?.httpPort) parseDeployHttpPort(options.httpPort);
275
+ if (merged.httpPort) parseDeployHttpPort(merged.httpPort.value);
276
+ const deployRegion = normalizeDeployRegionInput(merged.region);
116
277
  assertSupportedEntrypointForRequestedDeployShape({
117
- requestedFramework: options?.framework,
118
- entrypoint: options?.entrypoint
278
+ requestedFramework: merged.framework?.value,
279
+ entrypoint: merged.entrypoint?.value
119
280
  });
120
281
  const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
121
282
  branch,
122
283
  createProjectName: options?.createProjectName,
284
+ createProjectRegion: deployRegion?.value,
123
285
  envProjectId,
124
286
  localPin
125
287
  });
126
288
  let localPinResult;
127
289
  if (target.localPinAction) {
128
- const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction);
129
- localPinResult = setupResult.localPin;
130
- maybeRenderProjectLinked(context, setupResult.directory, setupResult.project.name, setupResult.localPin.path);
290
+ const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction, projectDir);
291
+ if (setupResult.isErr()) throw projectDirectoryBindingErrorToCliError(setupResult.error);
292
+ const projectSetup = setupResult.value;
293
+ localPinResult = projectSetup.localPin;
294
+ maybeRenderProjectLinked(context, projectSetup.directory, projectSetup.project.name, projectSetup.localPin.path);
131
295
  }
132
296
  let framework = await resolveDeployFramework(context, {
133
- requestedFramework: options?.framework,
134
- entrypoint: options?.entrypoint
135
- });
136
- let runtime = resolveDeployRuntime(options?.httpPort, framework);
137
- assertSupportedEntrypoint(framework.buildType, options?.entrypoint, "deploy");
138
- const envVars = toOptionalEnvVars(parseEnvAssignments(options?.envAssignments, { commandName: "deploy" }));
297
+ requestedFramework: merged.framework?.value,
298
+ requestedFrameworkAnnotation: merged.framework?.annotation,
299
+ entrypoint: merged.entrypoint?.value,
300
+ entrypointAnnotation: merged.entrypoint?.annotation,
301
+ appDir
302
+ });
303
+ let runtime = resolveDeployRuntime(merged.httpPort?.value, merged.httpPort?.annotation, framework);
304
+ assertSupportedEntrypoint(framework.buildType, merged.entrypoint?.value, "deploy");
305
+ const envVars = toOptionalEnvVars(await parseEnvInputs(merged.envInputsFromConfig ? projectDir : context.runtime.cwd, merged.envInputs, { commandName: "deploy" }));
139
306
  const selectedApp = await resolveDeployAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
140
307
  explicitAppName: appName,
141
308
  explicitAppId: envAppId,
309
+ configAppName: merged.configAppName,
310
+ configRegion: deployRegion,
142
311
  firstDeploy: Boolean(target.localPinAction),
143
- inferName: () => inferTargetName(context.runtime.cwd, context.runtime.signal)
312
+ inferName: () => inferTargetName(appDir, context.runtime.signal)
144
313
  });
145
314
  await maybeRenderDeploySetupBlock(context, {
146
315
  includeDirectory: !target.localPinAction,
316
+ appDir,
147
317
  projectName: target.project.name,
148
318
  branchName: target.branch.name,
149
319
  appName: selectedApp.displayName
@@ -152,20 +322,41 @@ async function runAppDeploy(context, appName, options) {
152
322
  framework,
153
323
  runtime,
154
324
  firstDeploy: selectedApp.firstDeploy,
155
- explicitFramework: Boolean(options?.framework),
156
- explicitEntrypoint: Boolean(options?.entrypoint),
157
- explicitHttpPort: Boolean(options?.httpPort)
325
+ explicitFramework: Boolean(merged.framework),
326
+ explicitEntrypoint: Boolean(merged.entrypoint),
327
+ explicitHttpPort: Boolean(merged.httpPort)
158
328
  });
159
329
  framework = customized.framework;
160
330
  runtime = customized.runtime;
331
+ const noPromote = options?.noPromote === true;
332
+ const productionDeployGate = noPromote ? { firstProductionDeploy: false } : await enforceProductionDeployGate(context, provider, {
333
+ appId: selectedApp.appId,
334
+ appName: selectedApp.displayName,
335
+ branchKind: target.branch.kind,
336
+ prod: options?.prod === true
337
+ });
161
338
  const buildType = framework.buildType;
162
- assertSupportedEntrypoint(buildType, options?.entrypoint, "deploy");
163
- const entrypoint = await resolveDeployEntrypoint(context.runtime.cwd, framework, options?.entrypoint, context.runtime.signal);
339
+ assertSupportedEntrypoint(buildType, merged.entrypoint?.value, "deploy");
340
+ const entrypoint = await resolveDeployEntrypoint(appDir, framework, merged.entrypoint?.value, context.runtime.signal);
341
+ const buildSettingsResolution = await resolveDeployBuildSettings({
342
+ computeConfig,
343
+ appDir,
344
+ buildType,
345
+ signal: context.runtime.signal
346
+ });
347
+ const legacyWarnings = await handleLegacyBuildSettings(context, appDir, buildSettingsResolution.settings);
348
+ maybeRenderDeployBuildSettings(context, buildSettingsResolution);
164
349
  const portMapping = parseDeployPortMapping(String(runtime.port));
165
- const progressState = createPreviewDeployProgressState();
350
+ const branchDatabaseSetup = await maybeSetupBranchDatabase(context, provider, projectId, toBranchDatabaseDeployBranch(target.branch), {
351
+ db: options?.db,
352
+ providedEnvVars: envVars,
353
+ firstProductionDeploy: productionDeployGate.firstProductionDeploy,
354
+ projectDir
355
+ });
356
+ const progressState = createDeployProgressState();
166
357
  const deployStartedAt = Date.now();
167
358
  const deployResult = await provider.deployApp({
168
- cwd: context.runtime.cwd,
359
+ cwd: appDir,
169
360
  projectId,
170
361
  branchName: target.branch.name,
171
362
  appId: selectedApp.appId,
@@ -173,11 +364,13 @@ async function runAppDeploy(context, appName, options) {
173
364
  region: selectedApp.region,
174
365
  entrypoint,
175
366
  buildType,
367
+ buildSettings: buildSettingsResolution.settings,
176
368
  portMapping,
177
369
  envVars,
370
+ skipPromote: noPromote,
178
371
  interaction: void 0,
179
372
  signal: context.runtime.signal,
180
- progress: createPreviewDeployProgress(context.output.stderr, context.ui, !context.flags.json && !context.flags.quiet, progressState)
373
+ progress: createDeployProgress(context.output.stderr, context.ui, !context.flags.json && !context.flags.quiet, progressState)
181
374
  }).catch((error) => {
182
375
  throw appDeployFailedError(error, progressState);
183
376
  });
@@ -186,34 +379,87 @@ async function runAppDeploy(context, appName, options) {
186
379
  id: deployResult.app.id,
187
380
  name: deployResult.app.name
188
381
  });
189
- await context.stateStore.setKnownLiveDeployment(projectId, deployResult.app.id, deployResult.deployment.id);
382
+ const knownLiveDeploymentId = deployResult.promoted ? deployResult.deployment.id : deployResult.app.liveDeploymentId;
383
+ if (knownLiveDeploymentId) await context.stateStore.setKnownLiveDeployment(projectId, deployResult.app.id, knownLiveDeploymentId);
190
384
  return {
191
385
  command: "app.deploy",
192
386
  result: {
193
387
  workspace: target.workspace,
194
388
  project: target.project,
195
- branch: target.branch,
389
+ branch: toResultBranch(target.branch),
196
390
  resolution: target.resolution,
391
+ branchDatabase: branchDatabaseSetup.result,
197
392
  app: {
198
393
  id: deployResult.app.id,
199
394
  name: deployResult.app.name
200
395
  },
201
396
  deployment: deployResult.deployment,
397
+ promoted: deployResult.promoted,
398
+ deploySettings: {
399
+ config: {
400
+ path: computeConfig.config?.relativeConfigPath ?? null,
401
+ status: buildSettingsResolution.status
402
+ },
403
+ buildCommand: {
404
+ value: buildSettingsResolution.settings.buildCommand,
405
+ source: buildSettingsResolution.settings.buildCommandSource
406
+ },
407
+ outputDirectory: {
408
+ value: buildSettingsResolution.settings.outputDirectory,
409
+ source: buildSettingsResolution.settings.outputDirectorySource
410
+ },
411
+ framework: {
412
+ key: framework.key,
413
+ buildType,
414
+ name: framework.displayName,
415
+ source: framework.annotation
416
+ },
417
+ entrypoint: entrypoint ?? buildSettingsResolution.settings.entrypoint ?? null,
418
+ httpPort: runtime.port,
419
+ region: deployResult.app.region ?? selectedApp.region ?? null,
420
+ regionSource: deployRegion?.annotation ?? null,
421
+ envVars: envVarNames(envVars)
422
+ },
202
423
  durationMs: deployDurationMs,
203
424
  localPin: localPinResult
204
425
  },
205
- warnings: [],
206
- nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${deployResult.deployment.id}`]
426
+ warnings: [
427
+ ...agentSetupWarnings,
428
+ ...legacyWarnings,
429
+ ...branchDatabaseSetup.warnings
430
+ ],
431
+ nextSteps: deployResult.promoted ? ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${deployResult.deployment.id}`] : [`prisma-cli app promote ${deployResult.deployment.id}`, `prisma-cli app show-deploy ${deployResult.deployment.id}`]
207
432
  };
208
433
  }
209
- async function runAppListDeploys(context, appName, projectRef) {
434
+ async function resolveDeployBuildSettings(options) {
435
+ const { computeConfig, appDir, buildType, signal } = options;
436
+ if (computeConfig.target?.build) assertConfigBackedBuildSettings(buildType);
437
+ if (computeConfig.config && computeConfig.target?.build && isConfigBackedBuildType(buildType)) return resolveConfiguredAppBuildSettings({
438
+ appPath: appDir,
439
+ buildType,
440
+ configured: computeConfig.target.build,
441
+ configPath: computeConfig.config.configPath,
442
+ signal
443
+ });
444
+ return resolveInferredAppBuildSettings({
445
+ appPath: appDir,
446
+ buildType,
447
+ signal
448
+ });
449
+ }
450
+ async function runAppListDeploys(context, appName, projectRef, configTarget) {
210
451
  ensurePreviewAppMode(context);
211
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app list-deploys" });
212
- const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
452
+ const compute = await resolveComputeManagementContext(context, configTarget, "list-deploys");
453
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
454
+ commandName: "app list-deploys",
455
+ projectDir: compute.projectDir
456
+ });
457
+ const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName ?? compute.configAppName);
213
458
  if (!selectedApp) return {
214
459
  command: "app.list-deploys",
215
460
  result: {
216
461
  projectId,
462
+ verboseContext: toAppVerboseContext(target),
217
463
  app: null,
218
464
  deployments: []
219
465
  },
@@ -233,6 +479,7 @@ async function runAppListDeploys(context, appName, projectRef) {
233
479
  command: "app.list-deploys",
234
480
  result: {
235
481
  projectId,
482
+ verboseContext: toAppVerboseContext(target),
236
483
  app: {
237
484
  id: deploymentsResult.app.id,
238
485
  name: deploymentsResult.app.name
@@ -243,14 +490,19 @@ async function runAppListDeploys(context, appName, projectRef) {
243
490
  nextSteps: deployments.length > 0 ? [`prisma-cli app show-deploy ${deployments[0]?.id}`] : ["prisma-cli app deploy"]
244
491
  };
245
492
  }
246
- async function runAppShow(context, appName, projectRef) {
493
+ async function runAppShow(context, appName, projectRef, configTarget) {
247
494
  ensurePreviewAppMode(context);
248
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app show" });
249
- const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
495
+ const compute = await resolveComputeManagementContext(context, configTarget, "show");
496
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
497
+ commandName: "app show",
498
+ projectDir: compute.projectDir
499
+ });
500
+ const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName ?? compute.configAppName);
250
501
  if (!selectedApp) return {
251
502
  command: "app.show",
252
503
  result: {
253
504
  projectId,
505
+ verboseContext: toAppVerboseContext(target),
254
506
  app: null,
255
507
  liveDeployment: null,
256
508
  liveUrl: null,
@@ -273,6 +525,7 @@ async function runAppShow(context, appName, projectRef) {
273
525
  command: "app.show",
274
526
  result: {
275
527
  projectId,
528
+ verboseContext: toAppVerboseContext(target),
276
529
  app: {
277
530
  id: deploymentsResult.app.id,
278
531
  name: deploymentsResult.app.name
@@ -319,9 +572,14 @@ async function runAppShowDeploy(context, deploymentId) {
319
572
  nextSteps: []
320
573
  };
321
574
  }
322
- async function runAppOpen(context, appName, projectRef) {
575
+ async function runAppOpen(context, appName, projectRef, configTarget) {
323
576
  ensurePreviewAppMode(context);
324
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app open" });
577
+ const compute = await resolveComputeManagementContext(context, configTarget, "open");
578
+ appName = appName ?? compute.configAppName;
579
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
580
+ commandName: "app open",
581
+ projectDir: compute.projectDir
582
+ });
325
583
  const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
326
584
  if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
327
585
  const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
@@ -346,6 +604,7 @@ async function runAppOpen(context, appName, projectRef) {
346
604
  command: "app.open",
347
605
  result: {
348
606
  projectId,
607
+ verboseContext: toAppVerboseContext(target),
349
608
  app: {
350
609
  id: deploymentsResult.app.id,
351
610
  name: deploymentsResult.app.name
@@ -484,15 +743,19 @@ async function runAppDomainWait(context, hostname, options) {
484
743
  });
485
744
  }
486
745
  }
487
- async function runAppLogs(context, appName, deploymentId, projectRef) {
746
+ async function runAppLogs(context, appName, deploymentId, projectRef, configTarget) {
488
747
  ensurePreviewAppMode(context);
489
- const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app logs" });
748
+ const compute = await resolveComputeManagementContext(context, configTarget, "logs");
749
+ appName = appName ?? compute.configAppName;
750
+ const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef, {
751
+ commandName: "app logs",
752
+ projectDir: compute.projectDir
753
+ });
490
754
  const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName);
491
755
  if (!context.flags.json && !context.flags.quiet) {
492
756
  const lines = renderCommandHeader(context.ui, {
493
757
  commandLabel: "app logs",
494
758
  description: "Streaming logs for the selected deployment.",
495
- docsPath: "docs/product/command-spec.md#prisma-cli-app-logs---app-name---deployment-id",
496
759
  rows: [
497
760
  {
498
761
  key: "project",
@@ -609,9 +872,14 @@ function writeLogRecord(context, record) {
609
872
  if (!record.text.endsWith("\n")) context.output.stdout.write("\n");
610
873
  }
611
874
  }
612
- async function runAppPromote(context, deploymentId, appName, projectRef) {
875
+ async function runAppPromote(context, deploymentId, appName, projectRef, configTarget) {
613
876
  ensurePreviewAppMode(context);
614
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app promote" });
877
+ const compute = await resolveComputeManagementContext(context, configTarget, "promote");
878
+ appName = appName ?? compute.configAppName;
879
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
880
+ commandName: "app promote",
881
+ projectDir: compute.projectDir
882
+ });
615
883
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
616
884
  const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
617
885
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
@@ -627,7 +895,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
627
895
  appId: selectedApp.id,
628
896
  deploymentId: targetDeployment.id,
629
897
  signal: context.runtime.signal,
630
- progress: createPreviewPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
898
+ progress: createPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
631
899
  }).catch((error) => {
632
900
  throw deployFailedError("Failed to promote deployment", error, ["prisma-cli app list-deploys"]);
633
901
  });
@@ -636,6 +904,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
636
904
  command: "app.promote",
637
905
  result: {
638
906
  projectId,
907
+ verboseContext: toAppVerboseContext(target),
639
908
  app: {
640
909
  id: deploymentsResult.app.id,
641
910
  name: deploymentsResult.app.name
@@ -650,9 +919,14 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
650
919
  nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
651
920
  };
652
921
  }
653
- async function runAppRollback(context, appName, deploymentId, projectRef) {
922
+ async function runAppRollback(context, appName, deploymentId, projectRef, configTarget) {
654
923
  ensurePreviewAppMode(context);
655
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app rollback" });
924
+ const compute = await resolveComputeManagementContext(context, configTarget, "rollback");
925
+ appName = appName ?? compute.configAppName;
926
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
927
+ commandName: "app rollback",
928
+ projectDir: compute.projectDir
929
+ });
656
930
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
657
931
  const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
658
932
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
@@ -669,7 +943,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
669
943
  appId: selectedApp.id,
670
944
  deploymentId: targetDeployment.id,
671
945
  signal: context.runtime.signal,
672
- progress: createPreviewPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
946
+ progress: createPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
673
947
  }).catch((error) => {
674
948
  throw deployFailedError("Failed to roll back deployment", error, ["prisma-cli app list-deploys"]);
675
949
  });
@@ -678,6 +952,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
678
952
  command: "app.rollback",
679
953
  result: {
680
954
  projectId,
955
+ verboseContext: toAppVerboseContext(target),
681
956
  app: {
682
957
  id: deploymentsResult.app.id,
683
958
  name: deploymentsResult.app.name
@@ -693,9 +968,22 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
693
968
  nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
694
969
  };
695
970
  }
696
- async function runAppRemove(context, appName, projectRef) {
971
+ /**
972
+ * Removes an app and every deployment it owns in the resolved branch.
973
+ *
974
+ * @param branchName Scopes the removal to this branch. When omitted the branch
975
+ * is inferred from the local Git branch, falling back to the production branch.
976
+ */
977
+ async function runAppRemove(context, appName, projectRef, configTarget, branchName) {
697
978
  ensurePreviewAppMode(context);
698
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app remove" });
979
+ const compute = await resolveComputeManagementContext(context, configTarget, "remove");
980
+ appName = appName ?? compute.configAppName;
981
+ if (branchName !== void 0 && branchName.trim() === "") throw usageError("The --branch value cannot be empty", "app remove scopes the removal to the given branch; an empty --branch would silently fall back to the inferred (possibly production) branch.", "Pass a non-empty branch name, e.g. --branch <branch>, or omit --branch to use the inferred branch.", ["prisma-cli app remove --app <name> --branch <branch>"], "app");
982
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
983
+ branch: branchName !== void 0 ? await resolveDeployBranch(context, branchName) : void 0,
984
+ commandName: "app remove",
985
+ projectDir: compute.projectDir
986
+ });
699
987
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
700
988
  await confirmAppRemoval(context, selectedApp);
701
989
  const removedApp = await provider.removeApp(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
@@ -706,6 +994,7 @@ async function runAppRemove(context, appName, projectRef) {
706
994
  command: "app.remove",
707
995
  result: {
708
996
  projectId,
997
+ verboseContext: toAppVerboseContext(target),
709
998
  app: {
710
999
  id: removedApp.id,
711
1000
  name: removedApp.name
@@ -718,6 +1007,7 @@ async function runAppRemove(context, appName, projectRef) {
718
1007
  }
719
1008
  async function resolveAppDomainTarget(context, options, commandName = "app domain") {
720
1009
  ensurePreviewAppMode(context);
1010
+ const compute = await resolveComputeManagementContext(context, options?.configTarget, commandName.replace(/^app /, ""));
721
1011
  const branch = resolveDomainBranch(options?.branchName);
722
1012
  if (toBranchKind(branch.name) !== "production") throw new CliError({
723
1013
  code: "BRANCH_NOT_DEPLOYABLE",
@@ -733,10 +1023,11 @@ async function resolveAppDomainTarget(context, options, commandName = "app domai
733
1023
  const { provider, target, projectId } = await requireProviderAndProjectContext(context, options?.projectRef, {
734
1024
  branch,
735
1025
  commandName,
736
- envProjectId
1026
+ envProjectId,
1027
+ projectDir: compute.projectDir
737
1028
  });
738
1029
  const selectedApp = await resolveDomainAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
739
- explicitAppName: options?.appName,
1030
+ explicitAppName: options?.appName ?? compute.configAppName,
740
1031
  explicitAppId: envAppId
741
1032
  });
742
1033
  await context.stateStore.setSelectedApp(projectId, {
@@ -749,7 +1040,7 @@ async function resolveAppDomainTarget(context, options, commandName = "app domai
749
1040
  resultTarget: {
750
1041
  workspace: target.workspace,
751
1042
  project: target.project,
752
- branch: target.branch,
1043
+ branch: toResultBranch(target.branch),
753
1044
  app: {
754
1045
  id: selectedApp.id,
755
1046
  name: selectedApp.name
@@ -809,7 +1100,7 @@ function toAppDomainSummary(domain) {
809
1100
  type: domain.type,
810
1101
  url: domain.url,
811
1102
  hostname: domain.hostname,
812
- computeServiceId: domain.computeServiceId,
1103
+ appId: domain.appId,
813
1104
  status: domain.status,
814
1105
  foundryStatus: domain.foundryStatus,
815
1106
  failureReason: domain.failureReason,
@@ -847,12 +1138,13 @@ async function confirmDomainRemoval(context, target, hostname) {
847
1138
  if (!await confirmPrompt({
848
1139
  input: context.runtime.stdin,
849
1140
  output: context.output.stderr,
1141
+ signal: context.runtime.signal,
850
1142
  message: `Detach ${hostname} from App "${target.app.name}"?`,
851
1143
  initialValue: false
852
1144
  })) 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");
853
1145
  }
854
1146
  function domainCommandError(command, error, hostname) {
855
- if (error instanceof PreviewDomainApiError) {
1147
+ if (error instanceof DomainApiError) {
856
1148
  if (command === "add" && (error.status === 400 || error.status === 422) && isDomainDnsError(error)) return domainDnsNotConfiguredError(hostname, error);
857
1149
  if (command === "add" && error.status === 400) return new CliError({
858
1150
  code: "DOMAIN_HOSTNAME_INVALID",
@@ -1021,27 +1313,19 @@ async function sleep(milliseconds, signal) {
1021
1313
  });
1022
1314
  }
1023
1315
  async function resolveDeployAppSelection(context, projectId, apps, options) {
1024
- if (options.explicitAppName) {
1025
- const matches = findAppsByName(apps, options.explicitAppName);
1026
- if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, options.explicitAppName, options.firstDeploy);
1027
- const matched = matches[0];
1028
- if (matched) return {
1029
- appId: matched.id,
1030
- displayName: matched.name,
1031
- annotation: "set by --app",
1032
- firstDeploy: options.firstDeploy
1033
- };
1034
- return {
1035
- appName: options.explicitAppName,
1036
- region: PREVIEW_DEFAULT_REGION,
1037
- displayName: options.explicitAppName,
1038
- annotation: "set by --app",
1039
- firstDeploy: options.firstDeploy
1040
- };
1041
- }
1316
+ const newAppRegion = deployNewAppRegion(options.configRegion);
1317
+ if (options.explicitAppName) return resolveDeployAppByName(context, apps, {
1318
+ name: options.explicitAppName,
1319
+ matchedAnnotation: "set by --app",
1320
+ newAnnotation: "set by --app",
1321
+ requestedRegion: options.configRegion,
1322
+ newAppRegion,
1323
+ firstDeploy: options.firstDeploy
1324
+ });
1042
1325
  if (options.explicitAppId) {
1043
1326
  const matched = apps.find((app) => app.id === options.explicitAppId);
1044
1327
  if (!matched) throw usageError("Selected app does not exist in the resolved project", `The app "${options.explicitAppId}" from ${PRISMA_APP_ID_ENV_VAR} could not be found in resolved project "${projectId}".`, `Unset ${PRISMA_APP_ID_ENV_VAR}, pass --app <name>, or choose an app from prisma-cli app list-deploys.`, ["prisma-cli app list-deploys"], "app");
1328
+ assertDeployRegionMatchesExistingApp(matched, options.configRegion);
1045
1329
  return {
1046
1330
  appId: matched.id,
1047
1331
  displayName: matched.name,
@@ -1049,25 +1333,54 @@ async function resolveDeployAppSelection(context, projectId, apps, options) {
1049
1333
  firstDeploy: options.firstDeploy
1050
1334
  };
1051
1335
  }
1336
+ if (options.configAppName) {
1337
+ const configName = options.configAppName;
1338
+ return resolveDeployAppByName(context, apps, {
1339
+ name: configName.value,
1340
+ matchedAnnotation: configName.annotation,
1341
+ newAnnotation: configName.annotation,
1342
+ requestedRegion: options.configRegion,
1343
+ newAppRegion,
1344
+ firstDeploy: options.firstDeploy
1345
+ });
1346
+ }
1052
1347
  const inferredName = await options.inferName();
1053
- const matches = findAppsByName(apps, inferredName.name);
1054
- if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, inferredName.name, options.firstDeploy);
1055
- const matched = matches[0];
1056
- if (matched) return {
1057
- appId: matched.id,
1058
- displayName: matched.name,
1059
- annotation: "existing app on this branch",
1348
+ const newAnnotation = inferredName.source === "package-name" ? "created from package.json" : "created from directory name";
1349
+ return resolveDeployAppByName(context, apps, {
1350
+ name: inferredName.name,
1351
+ matchedAnnotation: "existing app on this branch",
1352
+ newAnnotation,
1353
+ requestedRegion: options.configRegion,
1354
+ newAppRegion,
1060
1355
  firstDeploy: options.firstDeploy
1061
- };
1356
+ });
1357
+ }
1358
+ async function resolveDeployAppByName(context, apps, options) {
1359
+ const matches = findAppsByName(apps, options.name);
1360
+ if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, options.name, options.requestedRegion, options.newAppRegion, options.firstDeploy);
1361
+ const matched = matches[0];
1362
+ if (matched) {
1363
+ assertDeployRegionMatchesExistingApp(matched, options.requestedRegion);
1364
+ return {
1365
+ appId: matched.id,
1366
+ displayName: matched.name,
1367
+ annotation: options.matchedAnnotation,
1368
+ firstDeploy: options.firstDeploy
1369
+ };
1370
+ }
1062
1371
  return {
1063
- appName: inferredName.name,
1064
- region: PREVIEW_DEFAULT_REGION,
1065
- displayName: inferredName.name,
1066
- annotation: inferredName.source === "package-name" ? "created from package.json" : "created from directory name",
1372
+ appName: options.name,
1373
+ region: options.newAppRegion,
1374
+ displayName: options.name,
1375
+ annotation: options.newAnnotation,
1067
1376
  firstDeploy: options.firstDeploy
1068
1377
  };
1069
1378
  }
1070
- async function resolveAmbiguousDeployApp(context, matches, targetName, firstDeploy) {
1379
+ function assertDeployRegionMatchesExistingApp(app, requestedRegion) {
1380
+ if (requestedRegion?.annotation !== "set by --region" || !app.region || app.region === requestedRegion.value) return;
1381
+ throw usageError("App already exists in another region", `The selected app "${app.name}" is in region "${app.region}", but --region requested "${requestedRegion.value}".`, "Remove --region to deploy the existing app, or pass --app <new-name> to create a new app in that region.", [`prisma-cli app deploy --app ${formatCommandArgument(app.name)}`, `prisma-cli app deploy --app <new-name> --region ${formatCommandArgument(requestedRegion.value)}`], "app");
1382
+ }
1383
+ async function resolveAmbiguousDeployApp(context, matches, targetName, requestedRegion, newAppRegion, firstDeploy) {
1071
1384
  if (canPrompt(context)) {
1072
1385
  const createNew = "__create_new_app__";
1073
1386
  const cancel = "__cancel__";
@@ -1093,11 +1406,12 @@ async function resolveAmbiguousDeployApp(context, matches, targetName, firstDepl
1093
1406
  if (selected === cancel) throw usageError("App selection canceled", "The command was canceled before an app was selected.", "Re-run the command and choose an app, or pass --app <name>.", ["prisma-cli app deploy --app <name>"], "app");
1094
1407
  if (selected === createNew) return {
1095
1408
  appName: targetName,
1096
- region: PREVIEW_DEFAULT_REGION,
1409
+ region: newAppRegion,
1097
1410
  displayName: targetName,
1098
1411
  annotation: "created from package.json",
1099
1412
  firstDeploy
1100
1413
  };
1414
+ assertDeployRegionMatchesExistingApp(selected, requestedRegion);
1101
1415
  return {
1102
1416
  appId: selected.id,
1103
1417
  displayName: selected.name,
@@ -1119,6 +1433,9 @@ async function resolveAmbiguousDeployApp(context, matches, targetName, firstDepl
1119
1433
  nextSteps: ["prisma-cli app deploy --app <name>"]
1120
1434
  });
1121
1435
  }
1436
+ function deployNewAppRegion(configRegion) {
1437
+ return configRegion?.value;
1438
+ }
1122
1439
  async function resolveExistingAppSelection(context, projectId, apps, explicitAppName) {
1123
1440
  if (explicitAppName) {
1124
1441
  const matched = findAppByName(apps, explicitAppName);
@@ -1161,6 +1478,7 @@ async function confirmAppRemoval(context, app) {
1161
1478
  await textPrompt({
1162
1479
  input: context.runtime.stdin,
1163
1480
  output: context.output.stderr,
1481
+ signal: context.runtime.signal,
1164
1482
  message: `Type ${app.name} to confirm app removal`,
1165
1483
  placeholder: app.name,
1166
1484
  validate: (value) => value === app.name ? void 0 : `Type "${app.name}" to confirm removal.`
@@ -1193,13 +1511,21 @@ function requireDeploymentForApp(deployments, deploymentId, appName) {
1193
1511
  nextSteps: ["prisma-cli app list-deploys"]
1194
1512
  });
1195
1513
  }
1514
+ /**
1515
+ * Resolves the app's live deployment from the app pointer, the provider's live
1516
+ * flag, then locally cached state.
1517
+ *
1518
+ * @returns the live deployment id, or null when no authoritative signal exists.
1519
+ * Callers must treat null as "not known to be live" rather than assuming the
1520
+ * newest deployment is live.
1521
+ */
1196
1522
  async function resolveCurrentLiveDeploymentId(context, projectId, app, deployments) {
1197
1523
  if (app.liveDeploymentId && deployments.some((deployment) => deployment.id === app.liveDeploymentId)) return app.liveDeploymentId;
1198
1524
  const providerLiveDeployment = deployments.find((deployment) => deployment.live === true);
1199
1525
  if (providerLiveDeployment) return providerLiveDeployment.id;
1200
1526
  const knownLiveDeploymentId = await context.stateStore.readKnownLiveDeployment(projectId, app.id);
1201
1527
  if (knownLiveDeploymentId && deployments.some((deployment) => deployment.id === knownLiveDeploymentId)) return knownLiveDeploymentId;
1202
- return deployments[0]?.id ?? null;
1528
+ return null;
1203
1529
  }
1204
1530
  function buildAppShowNextSteps(liveUrl, liveDeployment, deployments) {
1205
1531
  const nextSteps = [];
@@ -1258,7 +1584,7 @@ async function requirePreviewAppProviderWithClient(context) {
1258
1584
  if (!client) throw authRequiredError(["prisma-cli auth login"]);
1259
1585
  return {
1260
1586
  client,
1261
- provider: createPreviewAppProvider(client, createPreviewLogAuthOptions(context.runtime.env, context.runtime.signal))
1587
+ provider: createAppProvider(client, createPreviewLogAuthOptions(context.runtime.env, context.runtime.signal))
1262
1588
  };
1263
1589
  }
1264
1590
  function createPreviewLogAuthOptions(env, signal) {
@@ -1300,20 +1626,29 @@ async function requireProviderAndDeployProjectContext(context, explicitProject,
1300
1626
  async function resolveProjectContext(context, client, explicitProject, options) {
1301
1627
  const authState = await requireAuthenticatedAuthState(context);
1302
1628
  if (!authState.workspace) throw workspaceRequiredError();
1303
- const resolved = await resolveProjectTarget({
1629
+ const resolvedResult = await resolveProjectTarget({
1304
1630
  context,
1305
1631
  workspace: authState.workspace,
1306
1632
  explicitProject,
1307
1633
  envProjectId: options?.envProjectId,
1634
+ projectDir: options?.projectDir,
1308
1635
  listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
1309
1636
  commandName: options?.commandName
1310
1637
  });
1311
- const branch = options?.branch ?? await resolveDeployBranch(context, void 0);
1638
+ if (resolvedResult.isErr()) throw projectResolutionErrorToCliError(resolvedResult.error);
1639
+ const resolved = resolvedResult.value;
1640
+ const requested = options?.branch ?? await resolveDeployBranch(context, void 0);
1641
+ const remoteBranch = options?.branch ? null : await resolveReadBranch(client, {
1642
+ projectId: resolved.project.id,
1643
+ branchName: requested.name,
1644
+ signal: context.runtime.signal
1645
+ });
1312
1646
  return {
1313
1647
  ...resolved,
1314
- branch: {
1315
- name: branch.name,
1316
- kind: toBranchKind(branch.name)
1648
+ branch: remoteBranch ?? {
1649
+ id: null,
1650
+ name: requested.name,
1651
+ kind: toBranchKind(requested.name)
1317
1652
  }
1318
1653
  };
1319
1654
  }
@@ -1322,7 +1657,7 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1322
1657
  if (!workspace) throw workspaceRequiredError();
1323
1658
  const branch = options.branch ?? await resolveDeployBranch(context, void 0);
1324
1659
  const projects = await listRealWorkspaceProjects(client, workspace, context.runtime.signal);
1325
- if (explicitProject) return withDeployBranch({
1660
+ if (explicitProject) return withRemoteDeployBranch(provider, {
1326
1661
  workspace,
1327
1662
  project: toProjectSummary(resolveProjectForSetup(explicitProject, projects, workspace)),
1328
1663
  resolution: {
@@ -1331,25 +1666,25 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1331
1666
  targetNameSource: "explicit"
1332
1667
  },
1333
1668
  localPinAction: "linked"
1334
- }, branch);
1669
+ }, branch, context.runtime.signal);
1335
1670
  if (options.createProjectName) {
1336
1671
  const projectName = options.createProjectName.trim();
1337
1672
  if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
1338
- return withDeployBranch({
1673
+ return withRemoteDeployBranch(provider, {
1339
1674
  workspace,
1340
- project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal)),
1675
+ project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal, options.createProjectRegion)),
1341
1676
  resolution: {
1342
1677
  projectSource: "created",
1343
1678
  targetName: projectName,
1344
1679
  targetNameSource: "explicit"
1345
1680
  },
1346
1681
  localPinAction: "created"
1347
- }, branch);
1682
+ }, branch, context.runtime.signal);
1348
1683
  }
1349
1684
  if (options.envProjectId) {
1350
1685
  const project = projects.find((candidate) => candidate.id === options.envProjectId);
1351
1686
  if (!project) throw projectNotFoundError(options.envProjectId, workspace);
1352
- return withDeployBranch({
1687
+ return withRemoteDeployBranch(provider, {
1353
1688
  workspace,
1354
1689
  project: toProjectSummary(project),
1355
1690
  resolution: {
@@ -1357,14 +1692,18 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1357
1692
  targetName: options.envProjectId,
1358
1693
  targetNameSource: "env"
1359
1694
  }
1360
- }, branch);
1695
+ }, branch, context.runtime.signal);
1361
1696
  }
1362
1697
  const localPin = options.localPin;
1363
1698
  if (localPin.kind === "present") {
1364
- if (localPin.pin.workspaceId !== workspace.id) throw localResolutionPinStaleError();
1699
+ if (localPin.pin.workspaceId !== workspace.id) throw localProjectWorkspaceMismatchError({
1700
+ pinnedWorkspaceId: localPin.pin.workspaceId,
1701
+ pinnedProjectId: localPin.pin.projectId,
1702
+ activeWorkspace: workspace
1703
+ });
1365
1704
  const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
1366
1705
  if (!project) throw localResolutionPinStaleError();
1367
- return withDeployBranch({
1706
+ return withRemoteDeployBranch(provider, {
1368
1707
  workspace,
1369
1708
  project: toProjectSummary(project),
1370
1709
  resolution: {
@@ -1372,10 +1711,10 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1372
1711
  targetName: project.name,
1373
1712
  targetNameSource: "local-pin"
1374
1713
  }
1375
- }, branch);
1714
+ }, branch, context.runtime.signal);
1376
1715
  }
1377
1716
  const platformMapping = await resolveDurablePlatformMapping();
1378
- if (platformMapping && platformMapping.workspace.id === workspace.id) return withDeployBranch({
1717
+ if (platformMapping && platformMapping.workspace.id === workspace.id) return withRemoteDeployBranch(provider, {
1379
1718
  workspace,
1380
1719
  project: toProjectSummary(platformMapping),
1381
1720
  resolution: {
@@ -1383,15 +1722,15 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1383
1722
  targetName: platformMapping.name,
1384
1723
  targetNameSource: "platform-mapping"
1385
1724
  }
1386
- }, branch);
1387
- if (canPrompt(context) && !context.flags.yes) return withDeployBranch(await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects), branch);
1725
+ }, branch, context.runtime.signal);
1726
+ if (canPrompt(context) && !context.flags.yes) return withRemoteDeployBranch(provider, await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects, options.createProjectRegion), branch, context.runtime.signal);
1388
1727
  throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
1389
1728
  }
1390
- async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects) {
1729
+ async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects, createProjectRegion) {
1391
1730
  const setup = await promptForProjectSetupChoice({
1392
1731
  context,
1393
1732
  projects,
1394
- createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal),
1733
+ createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal, createProjectRegion),
1395
1734
  cancel: {
1396
1735
  why: "Deploy needs a Project before it can continue.",
1397
1736
  fix: "Choose an existing Project or create a new one, then rerun deploy.",
@@ -1409,9 +1748,10 @@ async function resolveInteractiveDeployProjectSetup(context, provider, workspace
1409
1748
  localPinAction: setup.action
1410
1749
  };
1411
1750
  }
1412
- async function createProjectForDeploySetup(provider, projectName, workspace, signal) {
1751
+ async function createProjectForDeploySetup(provider, projectName, workspace, signal, region) {
1413
1752
  const created = await provider.createProject({
1414
1753
  name: projectName,
1754
+ region,
1415
1755
  signal
1416
1756
  }).catch((error) => {
1417
1757
  throw projectCreateFailedError(error, projectName, workspace, {
@@ -1427,21 +1767,50 @@ async function createProjectForDeploySetup(provider, projectName, workspace, sig
1427
1767
  return {
1428
1768
  id: created.id,
1429
1769
  name: created.name,
1770
+ ...created.defaultRegion != null ? { defaultRegion: created.defaultRegion } : {},
1430
1771
  workspace
1431
1772
  };
1432
1773
  }
1433
- function withDeployBranch(target, branch) {
1774
+ async function withRemoteDeployBranch(provider, target, branch, signal) {
1775
+ const remoteBranch = await provider.resolveBranch(target.project.id, {
1776
+ branchName: branch.name,
1777
+ signal
1778
+ });
1434
1779
  return {
1435
1780
  ...target,
1436
1781
  branch: {
1437
- name: branch.name,
1438
- kind: toBranchKind(branch.name)
1782
+ id: remoteBranch.id,
1783
+ name: remoteBranch.name,
1784
+ kind: remoteBranch.role
1439
1785
  }
1440
1786
  };
1441
1787
  }
1442
1788
  function toBranchKind(name) {
1443
1789
  return name === "production" || name === "main" ? "production" : "preview";
1444
1790
  }
1791
+ function toResultBranch(branch) {
1792
+ return {
1793
+ id: branch.id,
1794
+ name: branch.name,
1795
+ kind: branch.kind
1796
+ };
1797
+ }
1798
+ function toAppVerboseContext(target) {
1799
+ return {
1800
+ workspace: target.workspace,
1801
+ project: target.project,
1802
+ branch: target.branch,
1803
+ resolution: target.resolution
1804
+ };
1805
+ }
1806
+ function toBranchDatabaseDeployBranch(branch) {
1807
+ if (!branch.id) throw new Error(`Deploy branch "${branch.name}" was not resolved remotely.`);
1808
+ return {
1809
+ id: branch.id,
1810
+ name: branch.name,
1811
+ kind: branch.kind
1812
+ };
1813
+ }
1445
1814
  function assertExclusiveDeployProjectInputs(options) {
1446
1815
  const provided = [
1447
1816
  options.projectRef ? "--project" : null,
@@ -1470,58 +1839,130 @@ async function resolveDeployBranch(context, explicitBranchName) {
1470
1839
  annotation: "default"
1471
1840
  };
1472
1841
  }
1473
- async function readLocalGitBranch(cwd, signal) {
1474
- const headPath = await resolveGitHeadPath(path.join(cwd, ".git"), signal);
1475
- if (!headPath) return null;
1476
- try {
1477
- const head = (await readFile(headPath, {
1478
- encoding: "utf8",
1479
- signal
1480
- })).trim();
1481
- if (head.startsWith("ref: refs/heads/")) return head.slice(16);
1482
- } catch (error) {
1483
- if (signal.aborted) throw error;
1484
- return null;
1842
+ async function resolveComputeTargetOrThrow(context, configTarget, commandName, options) {
1843
+ let config;
1844
+ if (options?.preloaded !== void 0) config = options.preloaded;
1845
+ else {
1846
+ const loaded = await loadComputeConfig$1(context.runtime.cwd, context.runtime.signal);
1847
+ if (loaded.isErr()) throw computeConfigErrorToCliError(loaded.error, commandName);
1848
+ config = loaded.value;
1485
1849
  }
1486
- return null;
1850
+ if (!config) {
1851
+ if (configTarget) throw usageError(`App target "${configTarget}" requires a compute config file`, `No ${COMPUTE_CONFIG_FILENAME$1} exists in the current directory, so there are no named app targets.`, `Create ${COMPUTE_CONFIG_FILENAME$1} with an apps entry named "${configTarget}", or rerun without the target argument.`, [`prisma-cli app ${commandName}`], "app");
1852
+ return {
1853
+ config: null,
1854
+ target: null
1855
+ };
1856
+ }
1857
+ const requestedTarget = configTarget ?? inferComputeTargetFromCwd(config, context.runtime.cwd);
1858
+ const selected = selectComputeDeployTarget(config, requestedTarget);
1859
+ if (selected.isErr()) {
1860
+ if (options?.targetOptional && selected.error instanceof ComputeConfigTargetRequiredError) return {
1861
+ config,
1862
+ target: null
1863
+ };
1864
+ throw computeConfigErrorToCliError(selected.error, commandName);
1865
+ }
1866
+ return {
1867
+ config,
1868
+ target: selected.value
1869
+ };
1487
1870
  }
1488
- async function resolveGitHeadPath(gitPath, signal) {
1489
- signal.throwIfAborted();
1871
+ /**
1872
+ * Compute-config context for app management commands: the project directory
1873
+ * (where `.prisma/local.json` lives) and the config-selected app name, which
1874
+ * ranks below `--app` but above the remembered app selection.
1875
+ */
1876
+ async function resolveComputeManagementContext(context, configTarget, commandName) {
1877
+ const compute = await resolveComputeTargetOrThrow(context, configTarget, commandName, { targetOptional: true });
1878
+ return {
1879
+ projectDir: compute.config?.configDir ?? context.runtime.cwd,
1880
+ configAppName: compute.target?.name ?? compute.target?.key ?? void 0
1881
+ };
1882
+ }
1883
+ async function resolveComputeAppDir(context, compute) {
1884
+ if (!compute.config || !compute.target) return context.runtime.cwd;
1885
+ const appDir = computeTargetAppDir(compute.config, compute.target);
1886
+ if (!compute.target.root) return appDir;
1887
+ context.runtime.signal.throwIfAborted();
1490
1888
  try {
1491
- const raw = await readFile(gitPath, {
1492
- encoding: "utf8",
1493
- signal
1494
- });
1495
- if (raw.startsWith("gitdir:")) return path.join(path.resolve(path.dirname(gitPath), raw.slice(7).trim()), "HEAD");
1889
+ await access(appDir);
1890
+ context.runtime.signal.throwIfAborted();
1496
1891
  } catch (error) {
1497
- if (signal.aborted) throw error;
1892
+ if (context.runtime.signal.aborted) throw error;
1893
+ throw new CliError({
1894
+ code: "COMPUTE_CONFIG_INVALID",
1895
+ domain: "app",
1896
+ summary: `App root "${compute.target.root}" does not exist`,
1897
+ why: `${compute.config.relativeConfigPath} points the selected app at "${compute.target.root}", but that directory does not exist.`,
1898
+ fix: `Fix the root path in ${compute.config.relativeConfigPath} or create the directory.`,
1899
+ where: appDir,
1900
+ meta: {
1901
+ appRoot: compute.target.root,
1902
+ appDir
1903
+ },
1904
+ exitCode: 2,
1905
+ nextSteps: ["prisma-cli app deploy"]
1906
+ });
1498
1907
  }
1499
- signal.throwIfAborted();
1500
- try {
1501
- await access(path.join(gitPath, "HEAD"));
1502
- signal.throwIfAborted();
1503
- return path.join(gitPath, "HEAD");
1504
- } catch (error) {
1505
- if (signal.aborted) throw error;
1506
- return null;
1908
+ return appDir;
1909
+ }
1910
+ /**
1911
+ * `prisma.app.json` is no longer read or written. A leftover file that
1912
+ * matches the effective settings only warns; one with custom values fails
1913
+ * with migration guidance so builds never silently change.
1914
+ */
1915
+ async function handleLegacyBuildSettings(context, appDir, effective) {
1916
+ const legacy = await detectLegacyBuildSettings({
1917
+ appPath: appDir,
1918
+ effective,
1919
+ signal: context.runtime.signal
1920
+ });
1921
+ switch (legacy.kind) {
1922
+ case "absent": return [];
1923
+ case "matching": return [`${PRISMA_APP_CONFIG_FILENAME} is no longer used and matches the resolved build settings. Delete it.`];
1924
+ case "invalid": return [`${PRISMA_APP_CONFIG_FILENAME} is no longer used and could not be parsed. Delete it.`];
1925
+ case "custom": {
1926
+ const buildBlock = [
1927
+ "build: {",
1928
+ ` command: ${legacy.buildCommand === null ? "null" : JSON.stringify(legacy.buildCommand)},`,
1929
+ ` outputDirectory: ${JSON.stringify(legacy.outputDirectory)},`,
1930
+ "}"
1931
+ ].join(" ");
1932
+ throw new CliError({
1933
+ code: "BUILD_SETTINGS_MIGRATION_REQUIRED",
1934
+ domain: "app",
1935
+ summary: `${PRISMA_APP_CONFIG_FILENAME} is no longer supported`,
1936
+ why: `${PRISMA_APP_CONFIG_FILENAME} contains custom build settings that differ from the resolved defaults, and the file is no longer read.`,
1937
+ fix: `Move the settings into prisma.compute.ts as \`${buildBlock}\` on this app, then delete ${PRISMA_APP_CONFIG_FILENAME}.`,
1938
+ where: legacy.configPath,
1939
+ meta: {
1940
+ configPath: legacy.configPath,
1941
+ buildCommand: legacy.buildCommand,
1942
+ outputDirectory: legacy.outputDirectory
1943
+ },
1944
+ exitCode: 2,
1945
+ nextSteps: ["prisma-cli app deploy"]
1946
+ });
1947
+ }
1507
1948
  }
1508
1949
  }
1509
1950
  async function resolveDeployFramework(context, options) {
1510
- if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, "set by --framework");
1951
+ if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, options.requestedFrameworkAnnotation ?? "set by --framework");
1511
1952
  if (options.entrypoint) return {
1512
1953
  key: "bun",
1513
1954
  buildType: "bun",
1514
1955
  displayName: "Bun",
1515
- annotation: "set by --entry"
1956
+ annotation: options.entrypointAnnotation ?? "set by --entry"
1516
1957
  };
1517
- const detected = await detectDeployFramework(context.runtime.cwd, context.runtime.signal);
1958
+ const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
1518
1959
  if (detected) return detected;
1519
- throw frameworkNotDetectedError(context.runtime.cwd);
1960
+ throw frameworkNotDetectedError(options.appDir);
1520
1961
  }
1521
- function resolveDeployRuntime(requestedHttpPort, framework) {
1962
+ function resolveDeployRuntime(requestedHttpPort, requestedHttpPortAnnotation, framework) {
1522
1963
  if (requestedHttpPort) return {
1523
1964
  port: parseDeployHttpPort(requestedHttpPort),
1524
- annotation: "set by --http-port"
1965
+ annotation: requestedHttpPortAnnotation ?? "set by --http-port"
1525
1966
  };
1526
1967
  return {
1527
1968
  port: FRAMEWORK_DEFAULT_HTTP_PORT,
@@ -1536,8 +1977,8 @@ async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signa
1536
1977
  if (explicitEntrypoint || framework.buildType !== "bun") return explicitEntrypoint;
1537
1978
  const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd, signal));
1538
1979
  if (packageEntrypoint) return packageEntrypoint;
1539
- if (framework.key !== "hono") return;
1540
- const defaultEntrypoint = "src/index.ts";
1980
+ const defaultEntrypoint = frameworkFromAlias(framework.key)?.defaultEntrypoint;
1981
+ if (!defaultEntrypoint) return;
1541
1982
  signal.throwIfAborted();
1542
1983
  try {
1543
1984
  await access(path.join(cwd, defaultEntrypoint));
@@ -1550,109 +1991,52 @@ async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signa
1550
1991
  }
1551
1992
  }
1552
1993
  async function detectDeployFramework(cwd, signal) {
1553
- const packageJson = await readBunPackageJson(cwd, signal);
1554
- const nextConfig = await detectNextConfig(cwd, signal);
1555
- if (nextConfig.exists || hasPackageDependency(packageJson, "next")) return {
1556
- key: "nextjs",
1557
- buildType: "nextjs",
1558
- displayName: "Next.js",
1559
- annotation: nextConfig.standalone ? "standalone output detected" : nextConfig.exists ? "detected from next.config" : "detected from package.json"
1560
- };
1561
- if (hasPackageDependency(packageJson, "hono")) return {
1562
- key: "hono",
1563
- buildType: "bun",
1564
- displayName: "Hono",
1565
- annotation: "detected from package.json"
1566
- };
1567
- if (hasAnyPackageDependency(packageJson, TANSTACK_START_PACKAGES)) return {
1568
- key: "tanstack-start",
1569
- buildType: "tanstack-start",
1570
- displayName: "TanStack Start",
1571
- annotation: "detected from package.json"
1994
+ const detected = await detectComputeAppFromDirectory({
1995
+ appPath: cwd,
1996
+ signal
1997
+ });
1998
+ if (!detected) return null;
1999
+ let annotation = "detected from package.json";
2000
+ if (detected.configFile?.standaloneOutput) annotation = "standalone output detected";
2001
+ else if (detected.configFile) annotation = `detected from ${path.basename(detected.configFile.path)}`;
2002
+ return {
2003
+ key: detected.framework,
2004
+ buildType: detected.buildType,
2005
+ displayName: detected.frameworkName,
2006
+ annotation
1572
2007
  };
1573
- return null;
1574
2008
  }
1575
- async function detectNextConfig(cwd, signal) {
1576
- for (const candidate of [
1577
- "next.config.js",
1578
- "next.config.mjs",
1579
- "next.config.cjs",
1580
- "next.config.ts",
1581
- "next.config.mts"
1582
- ]) {
1583
- const filePath = path.join(cwd, candidate);
1584
- signal.throwIfAborted();
1585
- try {
1586
- const content = await readFile(filePath, {
1587
- encoding: "utf8",
1588
- signal
1589
- });
1590
- return {
1591
- exists: true,
1592
- standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content)
1593
- };
1594
- } catch (error) {
1595
- if (signal.aborted) throw error;
1596
- if (error.code !== "ENOENT") throw error;
1597
- }
1598
- }
2009
+ function frameworkFromUserFacingValue(value, annotation) {
2010
+ const framework = frameworkFromAlias(value);
2011
+ if (!framework) throw frameworkNotDetectedError(void 0, value);
1599
2012
  return {
1600
- exists: false,
1601
- standalone: false
2013
+ key: framework.key,
2014
+ buildType: framework.buildType,
2015
+ displayName: framework.displayName,
2016
+ annotation
1602
2017
  };
1603
2018
  }
1604
- function hasPackageDependency(packageJson, dependencyName) {
1605
- return hasDependency(packageJson?.dependencies, dependencyName) || hasDependency(packageJson?.devDependencies, dependencyName);
1606
- }
1607
- function hasAnyPackageDependency(packageJson, dependencyNames) {
1608
- return dependencyNames.some((dependencyName) => hasPackageDependency(packageJson, dependencyName));
1609
- }
1610
- function hasDependency(dependencies, dependencyName) {
1611
- return Boolean(dependencies && typeof dependencies === "object" && dependencyName in dependencies);
1612
- }
1613
- function frameworkFromUserFacingValue(value, annotation) {
1614
- switch (value.trim().toLowerCase()) {
1615
- case "next":
1616
- case "next.js":
1617
- case "nextjs": return {
1618
- key: "nextjs",
1619
- buildType: "nextjs",
1620
- displayName: "Next.js",
1621
- annotation
1622
- };
1623
- case "hono": return {
1624
- key: "hono",
1625
- buildType: "bun",
1626
- displayName: "Hono",
1627
- annotation
1628
- };
1629
- case "bun": return {
1630
- key: "bun",
1631
- buildType: "bun",
1632
- displayName: "Bun",
1633
- annotation
1634
- };
1635
- case "tanstack":
1636
- case "tanstack-start":
1637
- case "@tanstack/react-start":
1638
- case "@tanstack/solid-start": return {
1639
- key: "tanstack-start",
1640
- buildType: "tanstack-start",
1641
- displayName: "TanStack Start",
1642
- annotation
1643
- };
1644
- default: throw frameworkNotDetectedError(void 0, value);
1645
- }
2019
+ function assertConfigBackedBuildSettings(buildType) {
2020
+ if (isConfigBackedBuildType(buildType)) return;
2021
+ const displayName = FRAMEWORKS.find((framework) => framework.buildType === buildType)?.displayName ?? buildType;
2022
+ throw new CliError({
2023
+ code: "BUILD_SETTINGS_UNSUPPORTED",
2024
+ domain: "app",
2025
+ summary: `build settings are not supported for ${displayName} apps`,
2026
+ why: `${displayName} deploys run \`${buildType} build\` and package its output automatically.`,
2027
+ fix: "Remove the `build` block from prisma.compute.ts for this app.",
2028
+ exitCode: 2
2029
+ });
1646
2030
  }
1647
2031
  function frameworkNotDetectedError(cwd, requestedFramework) {
1648
- const supported = "Next.js, Hono, TanStack Start, Bun";
2032
+ const supported = FRAMEWORKS.map((framework) => framework.displayName).join(", ");
1649
2033
  const directory = cwd ? ` in ${formatDeployDirectory(cwd)}` : "";
1650
2034
  return new CliError({
1651
2035
  code: "FRAMEWORK_NOT_DETECTED",
1652
2036
  domain: "app",
1653
2037
  summary: requestedFramework ? `Unsupported framework "${requestedFramework}"` : `Cannot detect a supported framework${directory}`,
1654
2038
  why: `Supported Beta frameworks: ${supported}.`,
1655
- fix: "Add one of these frameworks as a dependency, pass --framework <nextjs|hono|tanstack-start|bun>, or pass --entry <path> for a Bun app.",
2039
+ fix: `Add one of these frameworks as a dependency, pass --framework <${FRAMEWORKS.map((framework) => framework.key).join("|")}>, or pass --entry <path> for a Bun app.`,
1656
2040
  exitCode: 2,
1657
2041
  nextSteps: [
1658
2042
  "prisma-cli app deploy --framework nextjs",
@@ -1665,10 +2049,32 @@ function frameworkNotDetectedError(cwd, requestedFramework) {
1665
2049
  }
1666
2050
  async function maybeRenderDeploySetupBlock(context, details) {
1667
2051
  if (context.flags.json || context.flags.quiet) return;
1668
- const directory = formatDeployDirectory(context.runtime.cwd);
2052
+ const directory = formatAppDirectoryLabel(context.runtime.cwd, details.appDir);
1669
2053
  const prefix = details.includeDirectory ? `Deploying ${directory} to` : "Deploying to";
1670
2054
  context.output.stderr.write(`${prefix} ${details.projectName} / ${details.branchName} / ${details.appName}\n\n`);
1671
2055
  }
2056
+ function maybeRenderDeployBuildSettings(context, resolution) {
2057
+ if (context.flags.json || context.flags.quiet) return;
2058
+ const settings = resolution.settings;
2059
+ const title = resolution.status === "config" ? `Using ${resolution.relativeConfigPath}` : "Build settings";
2060
+ context.output.stderr.write(`${title}\n${renderDeployOutputRows(context.ui, [
2061
+ {
2062
+ label: "Build Command",
2063
+ value: settings.buildCommand ?? "none",
2064
+ origin: settings.buildCommandSource ?? void 0
2065
+ },
2066
+ {
2067
+ label: "Output Directory",
2068
+ value: settings.outputDirectory,
2069
+ origin: settings.outputDirectorySource ?? void 0
2070
+ },
2071
+ ...settings.entrypoint ? [{
2072
+ label: "Entrypoint",
2073
+ value: settings.entrypoint,
2074
+ origin: settings.entrypointSource ?? void 0
2075
+ }] : []
2076
+ ]).join("\n")}\n\n`);
2077
+ }
1672
2078
  function maybeRenderProjectLinked(context, directory, projectName, localPinPath) {
1673
2079
  if (context.flags.json || context.flags.quiet) return;
1674
2080
  context.output.stderr.write(`${context.ui.success("✔")} Linked "${directory}" to Project "${projectName}"\nSaved ${localPinPath}\n\n`);
@@ -1685,6 +2091,7 @@ async function maybeCustomizeDeploySettings(context, options) {
1685
2091
  if (!await confirmPrompt({
1686
2092
  input: context.runtime.stdin,
1687
2093
  output: context.runtime.stderr,
2094
+ signal: context.runtime.signal,
1688
2095
  message: "Customize build settings?",
1689
2096
  initialValue: false
1690
2097
  })) return {
@@ -1694,15 +2101,17 @@ async function maybeCustomizeDeploySettings(context, options) {
1694
2101
  const framework = frameworkFromUserFacingValue(await selectPrompt({
1695
2102
  input: context.runtime.stdin,
1696
2103
  output: context.runtime.stderr,
2104
+ signal: context.runtime.signal,
1697
2105
  message: `Framework (${options.framework.displayName})`,
1698
- choices: DEPLOY_FRAMEWORKS.map((framework) => ({
1699
- label: frameworkDisplayName(framework),
1700
- value: framework
2106
+ choices: FRAMEWORKS.map((framework) => ({
2107
+ label: framework.displayName,
2108
+ value: framework.key
1701
2109
  }))
1702
2110
  }), "set by you");
1703
2111
  const requestedPort = await textPrompt({
1704
2112
  input: context.runtime.stdin,
1705
2113
  output: context.runtime.stderr,
2114
+ signal: context.runtime.signal,
1706
2115
  message: `HTTP port (${options.runtime.port})`,
1707
2116
  placeholder: String(options.runtime.port),
1708
2117
  validate: validateDeployHttpPortText
@@ -1740,14 +2149,6 @@ function maybeRenderDeploySettingsPreview(context, options) {
1740
2149
  value: `HTTP ${options.runtime.port}`
1741
2150
  }]).join("\n")}\n\n`);
1742
2151
  }
1743
- function frameworkDisplayName(framework) {
1744
- switch (framework) {
1745
- case "nextjs": return "Next.js";
1746
- case "hono": return "Hono";
1747
- case "tanstack-start": return "TanStack Start";
1748
- case "bun": return "Bun";
1749
- }
1750
- }
1751
2152
  function validateDeployHttpPortText(value) {
1752
2153
  if (!value?.trim()) return;
1753
2154
  try {
@@ -1761,6 +2162,11 @@ function formatDeployDirectory(cwd) {
1761
2162
  const basename = path.basename(cwd);
1762
2163
  return basename ? `./${basename}` : ".";
1763
2164
  }
2165
+ function formatAppDirectoryLabel(cwd, appDir) {
2166
+ if (appDir === cwd) return formatDeployDirectory(cwd);
2167
+ const relative = path.relative(cwd, appDir).split(path.sep).join("/");
2168
+ return relative.startsWith("..") ? relative : `./${relative}`;
2169
+ }
1764
2170
  async function readCurrentWorkspaceId(context) {
1765
2171
  const state = await context.stateStore.read();
1766
2172
  if (state.auth?.workspaceId) return state.auth.workspaceId;
@@ -1769,26 +2175,37 @@ async function readCurrentWorkspaceId(context) {
1769
2175
  function normalizeBuildType(requestedBuildType) {
1770
2176
  if (!requestedBuildType) return "auto";
1771
2177
  if (isPreviewBuildType(requestedBuildType)) return requestedBuildType;
1772
- throw usageError(`Unsupported build type "${requestedBuildType}"`, `Only ${PREVIEW_BUILD_TYPES.join(", ")} are supported in the current preview.`, "Pass a supported --build-type value.", getBuildTypeExamples("build"), "app");
2178
+ throw usageError(`Unsupported build type "${requestedBuildType}"`, `Only ${APP_BUILD_TYPE_LABELS} are supported in the current preview.`, "Pass a supported --build-type value.", getBuildTypeExamples("build"), "app");
1773
2179
  }
1774
2180
  function isPreviewBuildType(value) {
1775
- return PREVIEW_BUILD_TYPES.includes(value);
2181
+ return APP_BUILD_TYPES.includes(value);
1776
2182
  }
1777
2183
  function getBuildTypeExamples(commandName) {
1778
- return RESOLVED_PREVIEW_BUILD_TYPES.map((buildType) => {
2184
+ return RESOLVED_APP_BUILD_TYPES.map((buildType) => {
1779
2185
  return `prisma-cli app ${commandName} --build-type ${buildType}${buildType === "bun" ? " --entry server.ts" : ""}`;
1780
2186
  });
1781
2187
  }
1782
2188
  function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
1783
- if (buildType !== "auto" && buildType !== "bun" && entrypoint) {
2189
+ if (buildType !== "auto" && !ENTRYPOINT_BUILD_TYPES.includes(buildType) && entrypoint) {
1784
2190
  if (commandName === "deploy") throw usageError(`App deploy does not accept --entry with ${formatBuildTypeName(buildType)}`, `${formatBuildTypeName(buildType)} apps derive their runtime entrypoint from build output.`, "Remove --entry, or use --framework bun when you want to target a Bun entrypoint directly.", [`prisma-cli app deploy --framework ${buildType}`, "prisma-cli app deploy --framework bun --entry server.ts"], "app");
1785
2191
  throw usageError(`App ${commandName} does not accept --entry with --build-type ${buildType}`, `${formatBuildTypeName(buildType)} apps do not use an entrypoint flag in the current preview.`, `Remove --entry, or rerun prisma-cli app ${commandName} with --build-type bun when you want to target a Bun entrypoint directly.`, [`prisma-cli app ${commandName} --build-type ${buildType}`, `prisma-cli app ${commandName} --build-type bun --entry server.ts`], "app");
1786
2192
  }
1787
2193
  }
1788
- async function requireLocalBuildType(context, buildType, commandName) {
1789
- const resolvedBuildType = await resolveLocalBuildType(context.runtime.cwd, buildType, context.runtime.signal);
1790
- if (resolvedBuildType) return resolvedBuildType;
1791
- throw usageError(`App ${commandName} requires an explicit framework when detection is ambiguous`, "This preview only starts local dev servers for clear Next.js or Bun project shapes.", "Pass --build-type nextjs for a Next.js app, or pass --build-type bun with --entry <path> for a Bun app.", [`prisma-cli app ${commandName} --build-type nextjs`, `prisma-cli app ${commandName} --build-type bun --entry server.ts`], "app");
2194
+ /**
2195
+ * Resolves the framework for `app run` with the same detection as deploy, so
2196
+ * a repo that deploys without flags also runs without flags. Local dev server
2197
+ * support is intentionally narrower than deploy build support: only Next.js
2198
+ * and Bun/Hono have dev servers in the current preview.
2199
+ */
2200
+ async function resolveLocalRunFramework(context, options) {
2201
+ if (options.requestedBuildType === "auto" && options.entrypoint) return frameworkFromUserFacingValue("bun", "set by --entry");
2202
+ if (LOCAL_DEV_BUILD_TYPES.includes(options.requestedBuildType)) {
2203
+ if (options.configFramework && computeFrameworkToBuildType(options.configFramework) === options.requestedBuildType) return frameworkFromUserFacingValue(options.configFramework, `set by ${COMPUTE_CONFIG_FILENAME$1}`);
2204
+ return frameworkFromUserFacingValue(options.requestedBuildType, "set by --build-type");
2205
+ }
2206
+ const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
2207
+ if (detected && LOCAL_DEV_BUILD_TYPES.includes(detected.buildType)) return detected;
2208
+ throw usageError("App run requires an explicit framework when detection is ambiguous", "This preview only starts local dev servers for clear Next.js or Bun project shapes.", "Pass --build-type nextjs for a Next.js app, or pass --build-type bun with --entry <path> for a Bun app.", ["prisma-cli app run --build-type nextjs", "prisma-cli app run --build-type bun --entry server.ts"], "app");
1792
2209
  }
1793
2210
  function parseLocalPort(requestedPort) {
1794
2211
  if (!requestedPort) return DEFAULT_LOCAL_DEV_PORT;
@@ -1805,6 +2222,19 @@ function parseDeployHttpPort(requestedPort) {
1805
2222
  if (!Number.isInteger(port) || port <= 0 || port > 65535) throw usageError(`Invalid HTTP port "${requestedPort}"`, "HTTP port must be an integer between 1 and 65535.", "Pass --http-port <number> with a valid port value.", ["prisma-cli app deploy --http-port 3000"], "app");
1806
2223
  return port;
1807
2224
  }
2225
+ function parseDeployRegion(requestedRegion, source) {
2226
+ const region = requestedRegion.trim();
2227
+ if (region.length === 0) throw usageError("Invalid app region", `The app region ${source} must be a non-empty region id.`, "Pass a Prisma Compute region id.", ["prisma-cli app deploy --region eu-central-1"], "app");
2228
+ if (!COMPUTE_REGION_IDS.has(region)) throw usageError("Invalid app region", `The app region ${source} must be one of: ${COMPUTE_REGIONS.join(", ")}.`, "Pass a supported Prisma Compute region id.", ["prisma-cli app deploy --region eu-central-1"], "app");
2229
+ return region;
2230
+ }
2231
+ function normalizeDeployRegionInput(region) {
2232
+ if (!region) return;
2233
+ return {
2234
+ ...region,
2235
+ value: parseDeployRegion(region.value, region.annotation)
2236
+ };
2237
+ }
1808
2238
  function ensurePreviewAppMode(context) {
1809
2239
  if (isRealMode(context)) return;
1810
2240
  throw featureUnavailableError("App commands are not available in fixture mode", "Preview app commands require live app deployment integration.", "Rerun without fixture mode enabled to use preview app deployment workflows.", ["prisma-cli auth login", "prisma-cli project show"], "app");
@@ -1861,7 +2291,7 @@ function appDeployFailedError(error, progress) {
1861
2291
  }
1862
2292
  if (!progress.buildStarted) return deployFailedError("App deploy failed", error, ["prisma-cli app deploy"]);
1863
2293
  const phaseHeadline = progress.containerLive ? "The deployment started, but the app is not ready yet." : "Deploy failed after the build completed.";
1864
- 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."];
2294
+ const recoveryLines = progress.deploymentId ? ["See what happened", `prisma-cli app logs --deployment ${progress.deploymentId}`] : ["Fix", "Retry the command, or rerun with --trace for more detailed diagnostics."];
1865
2295
  const urlLines = progress.deploymentUrl ? [
1866
2296
  "",
1867
2297
  "URL",
@@ -1887,11 +2317,11 @@ function appDeployFailedError(error, progress) {
1887
2317
  domain: "app",
1888
2318
  summary: phaseHeadline,
1889
2319
  why,
1890
- fix: progress.versionId ? `Inspect logs with prisma-cli app logs --deployment ${progress.versionId}.` : "Retry the command, or rerun with --trace for more detailed diagnostics.",
2320
+ fix: progress.deploymentId ? `Inspect logs with prisma-cli app logs --deployment ${progress.deploymentId}.` : "Retry the command, or rerun with --trace for more detailed diagnostics.",
1891
2321
  debug,
1892
2322
  meta: {
1893
2323
  phase: progress.containerLive ? "runtime_ready" : "deploy",
1894
- deploymentId: progress.versionId,
2324
+ deploymentId: progress.deploymentId,
1895
2325
  deploymentUrl: progress.deploymentUrl
1896
2326
  },
1897
2327
  humanLines,
@@ -1915,6 +2345,18 @@ function localResolutionPinStaleError() {
1915
2345
  ]
1916
2346
  });
1917
2347
  }
2348
+ function localPinReadErrorToDeployError(error) {
2349
+ return matchError(error, {
2350
+ LocalResolutionPinInvalidJsonError: () => localResolutionPinStaleError(),
2351
+ LocalResolutionPinInvalidShapeError: () => localResolutionPinStaleError(),
2352
+ LocalResolutionPinReadAbortedError: (error) => {
2353
+ throw error;
2354
+ },
2355
+ UnhandledException: (error) => {
2356
+ throw error;
2357
+ }
2358
+ });
2359
+ }
1918
2360
  function readDeployEnvOverride(context, name) {
1919
2361
  const value = context.runtime.env[name]?.trim();
1920
2362
  return value ? value : void 0;
@@ -1993,14 +2435,12 @@ function isAutoBuildDetectionError(error) {
1993
2435
  return error instanceof Error && error.message.startsWith("Entrypoint is required.");
1994
2436
  }
1995
2437
  function formatBuildTypeName(buildType) {
1996
- switch (buildType) {
1997
- case "nextjs": return "Next.js";
1998
- case "nuxt": return "Nuxt";
1999
- case "astro": return "Astro";
2000
- case "tanstack-start": return "TanStack Start";
2001
- case "bun": return "Bun";
2002
- case "auto": return "Auto";
2438
+ if (buildType === "auto") return "Auto";
2439
+ for (let index = FRAMEWORKS.length - 1; index >= 0; index -= 1) {
2440
+ const framework = FRAMEWORKS[index];
2441
+ if (framework?.buildType === buildType) return framework.displayName;
2003
2442
  }
2443
+ return buildType;
2004
2444
  }
2005
2445
  function removeFailedError(summary, error, nextSteps) {
2006
2446
  return new CliError({
@@ -2037,4 +2477,4 @@ function toOptionalEnvVars(envVars) {
2037
2477
  return Object.keys(envVars).length > 0 ? envVars : void 0;
2038
2478
  }
2039
2479
  //#endregion
2040
- export { runAppBuild, runAppDeploy, runAppDomainAdd, runAppDomainRemove, runAppDomainRetry, runAppDomainShow, runAppDomainWait, runAppListDeploys, runAppLogs, runAppOpen, runAppPromote, runAppRemove, runAppRollback, runAppRun, runAppShow, runAppShowDeploy };
2480
+ export { detectDeployFramework, runAppBuild, runAppDeploy, runAppDomainAdd, runAppDomainRemove, runAppDomainRetry, runAppDomainShow, runAppDomainWait, runAppListDeploys, runAppLogs, runAppOpen, runAppPromote, runAppRemove, runAppRollback, runAppRun, runAppShow, runAppShowDeploy };