@prisma/cli 3.0.0-beta.10 → 3.0.0-beta.12

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 (43) hide show
  1. package/dist/adapters/token-storage.js +2 -2
  2. package/dist/cli.js +7 -7
  3. package/dist/cli2.js +3 -3
  4. package/dist/commands/app/index.js +65 -52
  5. package/dist/controllers/app-env.js +6 -5
  6. package/dist/controllers/app.js +505 -184
  7. package/dist/controllers/branch.js +1 -1
  8. package/dist/controllers/database.js +1 -1
  9. package/dist/controllers/project.js +6 -6
  10. package/dist/lib/app/app-interaction.js +5 -0
  11. package/dist/lib/app/{preview-provider.js → app-provider.js} +32 -28
  12. package/dist/lib/app/{preview-branch-database.js → branch-database-api.js} +1 -1
  13. package/dist/lib/app/branch-database-deploy.js +12 -60
  14. package/dist/lib/app/branch-database.js +1 -102
  15. package/dist/lib/app/build-settings.js +93 -0
  16. package/dist/lib/app/build.js +80 -0
  17. package/dist/lib/app/bun-project.js +2 -3
  18. package/dist/lib/app/compute-config.js +147 -0
  19. package/dist/lib/app/deploy-plan.js +58 -0
  20. package/dist/lib/app/{preview-progress.js → deploy-progress.js} +5 -5
  21. package/dist/lib/app/local-dev.js +3 -60
  22. package/dist/lib/app/production-deploy-gate.js +3 -3
  23. package/dist/lib/app/read-branch.js +30 -0
  24. package/dist/lib/auth/login.js +31 -24
  25. package/dist/lib/diagnostics.js +1 -1
  26. package/dist/lib/fs/home-path.js +24 -0
  27. package/dist/lib/git/local-branch.js +15 -3
  28. package/dist/lib/project/resolution.js +2 -2
  29. package/dist/lib/project/setup.js +10 -7
  30. package/dist/output/patterns.js +1 -1
  31. package/dist/presenters/app.js +44 -39
  32. package/dist/presenters/branch.js +1 -1
  33. package/dist/presenters/database.js +2 -2
  34. package/dist/presenters/project.js +3 -9
  35. package/dist/shell/command-runner.js +37 -27
  36. package/dist/shell/diagnostics-output.js +2 -8
  37. package/dist/shell/help.js +30 -20
  38. package/dist/shell/runtime.js +8 -4
  39. package/dist/shell/ui.js +19 -2
  40. package/package.json +17 -3
  41. package/dist/lib/app/preview-build-settings.js +0 -385
  42. package/dist/lib/app/preview-build.js +0 -475
  43. package/dist/lib/app/preview-interaction.js +0 -5
@@ -1,59 +1,79 @@
1
1
  import { SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
2
2
  import { FileTokenStorage } from "../adapters/token-storage.js";
3
3
  import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../shell/errors.js";
4
- import { renderCommandHeader } from "../shell/ui.js";
5
- import { writeJsonEvent } from "../shell/output.js";
6
- import { canPrompt } from "../shell/runtime.js";
7
4
  import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
8
- import { requireComputeAuth } from "../lib/auth/guard.js";
9
- import { readAuthState } from "../lib/auth/auth-ops.js";
5
+ import { DEFAULT_REGION } from "../lib/app/app-interaction.js";
6
+ import { PRISMA_APP_CONFIG_FILENAME, detectLegacyBuildSettings, resolveConfiguredAppBuildSettings, resolveInferredAppBuildSettings } from "../lib/app/build-settings.js";
7
+ import { APP_BUILD_TYPES, RESOLVED_APP_BUILD_TYPES, executeAppBuild } from "../lib/app/build.js";
10
8
  import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
11
- import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
12
- import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
13
- import { DEFAULT_LOCAL_DEV_PORT, resolveLocalBuildType, runLocalApp } from "../lib/app/local-dev.js";
14
- import { formatCommandArgument } from "../shell/command-arguments.js";
9
+ import { DomainApiError, createAppProvider } from "../lib/app/app-provider.js";
10
+ import { renderCommandHeader } from "../shell/ui.js";
11
+ import { canPrompt } from "../shell/runtime.js";
15
12
  import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
13
+ import { formatCommandArgument } from "../shell/command-arguments.js";
16
14
  import { buildProjectSetupNextActions, inferTargetName, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
17
15
  import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
18
- import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
19
- import { readLocalGitBranch } from "../lib/git/local-branch.js";
20
- import { resolveOrCreatePreviewBuildSettings } from "../lib/app/preview-build-settings.js";
21
- import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
22
- import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
23
16
  import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
24
- import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress } from "../lib/app/preview-progress.js";
25
- import { PreviewDomainApiError, createPreviewAppProvider } from "../lib/app/preview-provider.js";
26
- import { enforceProductionDeployGate } from "../lib/app/production-deploy-gate.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";
27
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";
26
+ import { requireComputeAuth } from "../lib/auth/guard.js";
27
+ import { readAuthState } from "../lib/auth/auth-ops.js";
28
+ import { readLocalGitBranch } from "../lib/git/local-branch.js";
29
+ import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
30
+ import { writeJsonEvent } from "../shell/output.js";
28
31
  import { createSelectPromptPort } from "./select-prompt-port.js";
29
32
  import { requireAuthenticatedAuthState } from "./auth.js";
30
33
  import { listRealWorkspaceProjects } from "./project.js";
34
+ import { ENTRYPOINT_BUILD_TYPES, FRAMEWORKS, LOCAL_DEV_BUILD_TYPES, frameworkByKey, frameworkFromAlias, isConfigBackedBuildType } from "@prisma/compute-sdk/config";
31
35
  import { access, readFile } from "node:fs/promises";
32
36
  import path from "node:path";
33
- import open from "open";
34
37
  import { Result, matchError } from "better-result";
38
+ import open from "open";
35
39
  //#region src/controllers/app.ts
36
- const DEPLOY_FRAMEWORKS = [
37
- "nextjs",
38
- "hono",
39
- "tanstack-start",
40
- "bun"
41
- ];
42
- const TANSTACK_START_PACKAGES = ["@tanstack/react-start", "@tanstack/solid-start"];
43
40
  const FRAMEWORK_DEFAULT_HTTP_PORT = 3e3;
44
41
  const PRISMA_PROJECT_ID_ENV_VAR = "PRISMA_PROJECT_ID";
45
42
  const PRISMA_APP_ID_ENV_VAR = "PRISMA_APP_ID";
46
43
  function isRealMode(context) {
47
44
  return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
48
45
  }
49
- async function runAppBuild(context, entrypoint, requestedBuildType) {
50
- const buildType = normalizeBuildType(requestedBuildType);
51
- assertSupportedEntrypoint(buildType, entrypoint, "build");
46
+ async function runAppBuild(context, options) {
47
+ const compute = await resolveComputeTargetOrThrow(context, options?.configTarget, "build");
48
+ const merged = mergeComputeLocalInputs({
49
+ cli: {
50
+ entrypoint: options?.entrypoint,
51
+ buildType: options?.buildType
52
+ },
53
+ target: compute.target
54
+ });
55
+ const appDir = await resolveComputeAppDir(context, compute);
56
+ let buildType = normalizeBuildType(merged.buildType);
57
+ if (compute.target?.build && buildType === "auto") {
58
+ const detected = await detectDeployFramework(appDir, context.runtime.signal);
59
+ if (!detected) throw frameworkNotDetectedError(appDir);
60
+ buildType = detected.buildType;
61
+ }
62
+ assertSupportedEntrypoint(buildType, merged.entrypoint, "build");
63
+ if (compute.target?.build && buildType !== "auto") assertConfigBackedBuildSettings(buildType);
64
+ const buildSettings = compute.config && compute.target?.build && isConfigBackedBuildType(buildType) ? (await resolveConfiguredAppBuildSettings({
65
+ appPath: appDir,
66
+ buildType,
67
+ configured: compute.target.build,
68
+ configPath: compute.config.configPath,
69
+ signal: context.runtime.signal
70
+ })).settings : void 0;
52
71
  try {
53
- const { artifact, buildType: actualBuildType } = await executePreviewBuild({
54
- appPath: context.runtime.cwd,
55
- entrypoint,
72
+ const { artifact, buildType: actualBuildType } = await executeAppBuild({
73
+ appPath: appDir,
74
+ entrypoint: merged.entrypoint,
56
75
  buildType,
76
+ buildSettings,
57
77
  signal: context.runtime.signal
58
78
  });
59
79
  return {
@@ -67,21 +87,37 @@ async function runAppBuild(context, entrypoint, requestedBuildType) {
67
87
  nextSteps: ["prisma-cli app deploy"]
68
88
  };
69
89
  } catch (error) {
70
- 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");
90
+ 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");
71
91
  throw buildFailedError("Local app build failed", error);
72
92
  }
73
93
  }
74
- async function runAppRun(context, entrypoint, requestedBuildType, requestedPort) {
94
+ async function runAppRun(context, options) {
75
95
  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");
76
- const buildType = normalizeBuildType(requestedBuildType);
77
- assertSupportedEntrypoint(buildType, entrypoint, "run");
78
- const port = parseLocalPort(requestedPort);
79
- const resolvedBuildType = await requireLocalBuildType(context, buildType, "run");
96
+ const compute = await resolveComputeTargetOrThrow(context, options?.configTarget, "run");
97
+ const merged = mergeComputeLocalInputs({
98
+ cli: {
99
+ entrypoint: options?.entrypoint,
100
+ buildType: options?.buildType,
101
+ port: options?.port
102
+ },
103
+ target: compute.target
104
+ });
105
+ 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");
106
+ const appDir = await resolveComputeAppDir(context, compute);
107
+ const buildType = normalizeBuildType(merged.buildType);
108
+ assertSupportedEntrypoint(buildType, merged.entrypoint, "run");
109
+ const port = parseLocalPort(merged.port);
110
+ const framework = await resolveLocalRunFramework(context, {
111
+ requestedBuildType: buildType,
112
+ configFramework: compute.target?.framework ?? null,
113
+ appDir
114
+ });
115
+ const entrypoint = framework.buildType === "bun" ? await resolveDeployEntrypoint(appDir, framework, merged.entrypoint, context.runtime.signal) : merged.entrypoint;
80
116
  let runResult;
81
117
  try {
82
118
  runResult = await runLocalApp({
83
- appPath: context.runtime.cwd,
84
- buildType: resolvedBuildType,
119
+ appPath: appDir,
120
+ buildType: framework.buildType,
85
121
  entrypoint,
86
122
  port,
87
123
  env: context.runtime.env,
@@ -106,6 +142,106 @@ async function runAppRun(context, entrypoint, requestedBuildType, requestedPort)
106
142
  }
107
143
  async function runAppDeploy(context, appName, options) {
108
144
  ensurePreviewAppMode(context);
145
+ const loaded = await loadComputeConfig$1(context.runtime.cwd, context.runtime.signal);
146
+ if (loaded.isErr()) throw computeConfigErrorToCliError(loaded.error, "deploy");
147
+ const config = loaded.value;
148
+ const plan = planAppDeploy({
149
+ config,
150
+ requestedTarget: options?.configTarget ?? (config ? inferComputeTargetFromCwd(config, context.runtime.cwd) : void 0),
151
+ hasCreateProject: options?.createProjectName !== void 0
152
+ });
153
+ if (plan.mode === "all") return runAppDeployAll(context, config, plan.targets, appName, options);
154
+ return runSingleAppDeploy(context, appName, options, config);
155
+ }
156
+ async function runAppDeployAll(context, config, plannedTargets, appName, options) {
157
+ assertNoPerAppInputsForDeployAll(context, config, appName, options);
158
+ const deployments = [];
159
+ const warnings = [];
160
+ for (const planned of plannedTargets) {
161
+ maybeRenderDeployAllTargetHeader(context, planned);
162
+ const targetOptions = {
163
+ ...options,
164
+ configTarget: planned.targetKey,
165
+ createProjectName: planned.bindsCreateProject ? options?.createProjectName : void 0
166
+ };
167
+ try {
168
+ const single = await runSingleAppDeploy(context, void 0, targetOptions, config);
169
+ deployments.push({
170
+ target: planned.targetKey,
171
+ result: single.result
172
+ });
173
+ warnings.push(...single.warnings);
174
+ } catch (error) {
175
+ throw deployAllFailedError(error, config, planned.index, deployments);
176
+ }
177
+ }
178
+ return {
179
+ command: "app.deploy",
180
+ result: { deployments },
181
+ warnings,
182
+ nextSteps: ["prisma-cli app list-deploys <app>"]
183
+ };
184
+ }
185
+ function assertNoPerAppInputsForDeployAll(context, config, appName, options) {
186
+ const used = perAppInputsForDeployAll({
187
+ appName,
188
+ framework: options?.framework,
189
+ entrypoint: options?.entrypoint,
190
+ httpPort: options?.httpPort,
191
+ envAssignments: options?.envAssignments,
192
+ appIdEnvVar: {
193
+ name: PRISMA_APP_ID_ENV_VAR,
194
+ value: readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR)
195
+ }
196
+ });
197
+ if (used.length === 0) return;
198
+ const targets = config.targets.map((target) => target.key).join(", ");
199
+ throw usageError(`Deploying all apps does not accept ${used.join(", ")}`, `Without a target, app deploy deploys every configured app (${targets}), 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.", config.targets.map((target) => `prisma-cli app deploy ${target.key}`), "app");
200
+ }
201
+ function maybeRenderDeployAllTargetHeader(context, planned) {
202
+ if (context.flags.json || context.flags.quiet) return;
203
+ context.output.stderr.write(`${planned.index > 0 ? "\n" : ""}── ${planned.targetKey} (${planned.index + 1}/${planned.total}) ──\n\n`);
204
+ }
205
+ function deployAllFailedError(error, config, failedIndex, deployments) {
206
+ if (!(error instanceof CliError)) return error;
207
+ const failure = describeDeployAllFailure({
208
+ targetKeys: config.targets.map((target) => target.key),
209
+ failedIndex,
210
+ completed: deployments.map(({ target, result }) => ({
211
+ target,
212
+ deploymentId: result.deployment.id,
213
+ url: result.deployment.url
214
+ }))
215
+ });
216
+ const contextSentence = failure.contextLines.join(" ");
217
+ return new CliError({
218
+ code: error.code,
219
+ domain: error.domain,
220
+ summary: error.summary,
221
+ why: error.humanLines ? error.why : [error.why, contextSentence].filter(Boolean).join(" "),
222
+ fix: error.fix,
223
+ debug: error.debug,
224
+ where: error.where,
225
+ meta: {
226
+ ...error.meta,
227
+ deployAll: {
228
+ failedTarget: failure.failedTarget,
229
+ completed: failure.completed,
230
+ notAttempted: failure.notAttempted
231
+ }
232
+ },
233
+ docsUrl: error.docsUrl,
234
+ exitCode: error.exitCode,
235
+ nextSteps: error.nextSteps,
236
+ nextActions: error.nextActions,
237
+ humanLines: error.humanLines ? [
238
+ ...error.humanLines,
239
+ "",
240
+ ...failure.contextLines
241
+ ] : void 0
242
+ });
243
+ }
244
+ async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
109
245
  const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
110
246
  const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
111
247
  assertExclusiveDeployProjectInputs({
@@ -113,14 +249,27 @@ async function runAppDeploy(context, appName, options) {
113
249
  createProjectName: options?.createProjectName,
114
250
  envProjectId
115
251
  });
116
- const localPinReadResult = Boolean(envProjectId || options?.projectRef || options?.createProjectName) ? Result.ok({ kind: "missing" }) : await readLocalResolutionPin(context.runtime.cwd, context.runtime.signal);
252
+ const computeConfig = await resolveComputeTargetOrThrow(context, options?.configTarget, "deploy", { preloaded: preloadedConfig });
253
+ const merged = mergeComputeDeployInputs({
254
+ cli: {
255
+ framework: options?.framework,
256
+ entrypoint: options?.entrypoint,
257
+ httpPort: options?.httpPort,
258
+ envInputs: options?.envAssignments
259
+ },
260
+ target: computeConfig.target,
261
+ configFilename: computeConfig.config?.relativeConfigPath ?? COMPUTE_CONFIG_FILENAME$1
262
+ });
263
+ const appDir = await resolveComputeAppDir(context, computeConfig);
264
+ const projectDir = computeConfig.config?.configDir ?? context.runtime.cwd;
265
+ const localPinReadResult = Boolean(envProjectId || options?.projectRef || options?.createProjectName) ? Result.ok({ kind: "missing" }) : await readLocalResolutionPin(projectDir, context.runtime.signal);
117
266
  if (localPinReadResult.isErr()) throw localPinReadErrorToDeployError(localPinReadResult.error);
118
267
  const localPin = localPinReadResult.value;
119
268
  const branch = await resolveDeployBranch(context, options?.branchName);
120
- if (options?.httpPort) parseDeployHttpPort(options.httpPort);
269
+ if (merged.httpPort) parseDeployHttpPort(merged.httpPort.value);
121
270
  assertSupportedEntrypointForRequestedDeployShape({
122
- requestedFramework: options?.framework,
123
- entrypoint: options?.entrypoint
271
+ requestedFramework: merged.framework?.value,
272
+ entrypoint: merged.entrypoint?.value
124
273
  });
125
274
  const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
126
275
  branch,
@@ -130,27 +279,32 @@ async function runAppDeploy(context, appName, options) {
130
279
  });
131
280
  let localPinResult;
132
281
  if (target.localPinAction) {
133
- const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction);
282
+ const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction, projectDir);
134
283
  if (setupResult.isErr()) throw projectDirectoryBindingErrorToCliError(setupResult.error);
135
284
  const projectSetup = setupResult.value;
136
285
  localPinResult = projectSetup.localPin;
137
286
  maybeRenderProjectLinked(context, projectSetup.directory, projectSetup.project.name, projectSetup.localPin.path);
138
287
  }
139
288
  let framework = await resolveDeployFramework(context, {
140
- requestedFramework: options?.framework,
141
- entrypoint: options?.entrypoint
142
- });
143
- let runtime = resolveDeployRuntime(options?.httpPort, framework);
144
- assertSupportedEntrypoint(framework.buildType, options?.entrypoint, "deploy");
145
- const envVars = toOptionalEnvVars(await parseEnvInputs(context.runtime.cwd, options?.envAssignments, { commandName: "deploy" }));
289
+ requestedFramework: merged.framework?.value,
290
+ requestedFrameworkAnnotation: merged.framework?.annotation,
291
+ entrypoint: merged.entrypoint?.value,
292
+ entrypointAnnotation: merged.entrypoint?.annotation,
293
+ appDir
294
+ });
295
+ let runtime = resolveDeployRuntime(merged.httpPort?.value, merged.httpPort?.annotation, framework);
296
+ assertSupportedEntrypoint(framework.buildType, merged.entrypoint?.value, "deploy");
297
+ const envVars = toOptionalEnvVars(await parseEnvInputs(merged.envInputsFromConfig ? projectDir : context.runtime.cwd, merged.envInputs, { commandName: "deploy" }));
146
298
  const selectedApp = await resolveDeployAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
147
299
  explicitAppName: appName,
148
300
  explicitAppId: envAppId,
301
+ configAppName: merged.configAppName,
149
302
  firstDeploy: Boolean(target.localPinAction),
150
- inferName: () => inferTargetName(context.runtime.cwd, context.runtime.signal)
303
+ inferName: () => inferTargetName(appDir, context.runtime.signal)
151
304
  });
152
305
  await maybeRenderDeploySetupBlock(context, {
153
306
  includeDirectory: !target.localPinAction,
307
+ appDir,
154
308
  projectName: target.project.name,
155
309
  branchName: target.branch.name,
156
310
  appName: selectedApp.displayName
@@ -159,9 +313,9 @@ async function runAppDeploy(context, appName, options) {
159
313
  framework,
160
314
  runtime,
161
315
  firstDeploy: selectedApp.firstDeploy,
162
- explicitFramework: Boolean(options?.framework),
163
- explicitEntrypoint: Boolean(options?.entrypoint),
164
- explicitHttpPort: Boolean(options?.httpPort)
316
+ explicitFramework: Boolean(merged.framework),
317
+ explicitEntrypoint: Boolean(merged.entrypoint),
318
+ explicitHttpPort: Boolean(merged.httpPort)
165
319
  });
166
320
  framework = customized.framework;
167
321
  runtime = customized.runtime;
@@ -172,24 +326,33 @@ async function runAppDeploy(context, appName, options) {
172
326
  prod: options?.prod === true
173
327
  });
174
328
  const buildType = framework.buildType;
175
- assertSupportedEntrypoint(buildType, options?.entrypoint, "deploy");
176
- const entrypoint = await resolveDeployEntrypoint(context.runtime.cwd, framework, options?.entrypoint, context.runtime.signal);
177
- const buildSettingsResolution = await resolveOrCreatePreviewBuildSettings({
178
- appPath: context.runtime.cwd,
329
+ assertSupportedEntrypoint(buildType, merged.entrypoint?.value, "deploy");
330
+ const entrypoint = await resolveDeployEntrypoint(appDir, framework, merged.entrypoint?.value, context.runtime.signal);
331
+ if (computeConfig.target?.build) assertConfigBackedBuildSettings(buildType);
332
+ const buildSettingsResolution = computeConfig.config && computeConfig.target?.build && isConfigBackedBuildType(buildType) ? await resolveConfiguredAppBuildSettings({
333
+ appPath: appDir,
334
+ buildType,
335
+ configured: computeConfig.target.build,
336
+ configPath: computeConfig.config.configPath,
337
+ signal: context.runtime.signal
338
+ }) : await resolveInferredAppBuildSettings({
339
+ appPath: appDir,
179
340
  buildType,
180
341
  signal: context.runtime.signal
181
342
  });
343
+ const legacyWarnings = await handleLegacyBuildSettings(context, appDir, buildSettingsResolution.settings);
182
344
  maybeRenderDeployBuildSettings(context, buildSettingsResolution);
183
345
  const portMapping = parseDeployPortMapping(String(runtime.port));
184
346
  const branchDatabaseSetup = await maybeSetupBranchDatabase(context, provider, projectId, toBranchDatabaseDeployBranch(target.branch), {
185
347
  db: options?.db,
186
348
  providedEnvVars: envVars,
187
- firstProductionDeploy: productionDeployGate.firstProductionDeploy
349
+ firstProductionDeploy: productionDeployGate.firstProductionDeploy,
350
+ projectDir
188
351
  });
189
- const progressState = createPreviewDeployProgressState();
352
+ const progressState = createDeployProgressState();
190
353
  const deployStartedAt = Date.now();
191
354
  const deployResult = await provider.deployApp({
192
- cwd: context.runtime.cwd,
355
+ cwd: appDir,
193
356
  projectId,
194
357
  branchName: target.branch.name,
195
358
  appId: selectedApp.appId,
@@ -202,7 +365,7 @@ async function runAppDeploy(context, appName, options) {
202
365
  envVars,
203
366
  interaction: void 0,
204
367
  signal: context.runtime.signal,
205
- progress: createPreviewDeployProgress(context.output.stderr, context.ui, !context.flags.json && !context.flags.quiet, progressState)
368
+ progress: createDeployProgress(context.output.stderr, context.ui, !context.flags.json && !context.flags.quiet, progressState)
206
369
  }).catch((error) => {
207
370
  throw appDeployFailedError(error, progressState);
208
371
  });
@@ -252,14 +415,18 @@ async function runAppDeploy(context, appName, options) {
252
415
  durationMs: deployDurationMs,
253
416
  localPin: localPinResult
254
417
  },
255
- warnings: branchDatabaseSetup.warnings,
418
+ warnings: [...legacyWarnings, ...branchDatabaseSetup.warnings],
256
419
  nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${deployResult.deployment.id}`]
257
420
  };
258
421
  }
259
- async function runAppListDeploys(context, appName, projectRef) {
422
+ async function runAppListDeploys(context, appName, projectRef, configTarget) {
260
423
  ensurePreviewAppMode(context);
261
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app list-deploys" });
262
- const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
424
+ const compute = await resolveComputeManagementContext(context, configTarget, "list-deploys");
425
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
426
+ commandName: "app list-deploys",
427
+ projectDir: compute.projectDir
428
+ });
429
+ const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName ?? compute.configAppName);
263
430
  if (!selectedApp) return {
264
431
  command: "app.list-deploys",
265
432
  result: {
@@ -295,10 +462,14 @@ async function runAppListDeploys(context, appName, projectRef) {
295
462
  nextSteps: deployments.length > 0 ? [`prisma-cli app show-deploy ${deployments[0]?.id}`] : ["prisma-cli app deploy"]
296
463
  };
297
464
  }
298
- async function runAppShow(context, appName, projectRef) {
465
+ async function runAppShow(context, appName, projectRef, configTarget) {
299
466
  ensurePreviewAppMode(context);
300
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app show" });
301
- const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
467
+ const compute = await resolveComputeManagementContext(context, configTarget, "show");
468
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
469
+ commandName: "app show",
470
+ projectDir: compute.projectDir
471
+ });
472
+ const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName ?? compute.configAppName);
302
473
  if (!selectedApp) return {
303
474
  command: "app.show",
304
475
  result: {
@@ -373,9 +544,14 @@ async function runAppShowDeploy(context, deploymentId) {
373
544
  nextSteps: []
374
545
  };
375
546
  }
376
- async function runAppOpen(context, appName, projectRef) {
547
+ async function runAppOpen(context, appName, projectRef, configTarget) {
377
548
  ensurePreviewAppMode(context);
378
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app open" });
549
+ const compute = await resolveComputeManagementContext(context, configTarget, "open");
550
+ appName = appName ?? compute.configAppName;
551
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
552
+ commandName: "app open",
553
+ projectDir: compute.projectDir
554
+ });
379
555
  const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
380
556
  if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
381
557
  const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
@@ -539,15 +715,19 @@ async function runAppDomainWait(context, hostname, options) {
539
715
  });
540
716
  }
541
717
  }
542
- async function runAppLogs(context, appName, deploymentId, projectRef) {
718
+ async function runAppLogs(context, appName, deploymentId, projectRef, configTarget) {
543
719
  ensurePreviewAppMode(context);
544
- const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app logs" });
720
+ const compute = await resolveComputeManagementContext(context, configTarget, "logs");
721
+ appName = appName ?? compute.configAppName;
722
+ const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef, {
723
+ commandName: "app logs",
724
+ projectDir: compute.projectDir
725
+ });
545
726
  const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName);
546
727
  if (!context.flags.json && !context.flags.quiet) {
547
728
  const lines = renderCommandHeader(context.ui, {
548
729
  commandLabel: "app logs",
549
730
  description: "Streaming logs for the selected deployment.",
550
- docsPath: "docs/product/command-spec.md#prisma-cli-app-logs---app-name---deployment-id",
551
731
  rows: [
552
732
  {
553
733
  key: "project",
@@ -664,9 +844,14 @@ function writeLogRecord(context, record) {
664
844
  if (!record.text.endsWith("\n")) context.output.stdout.write("\n");
665
845
  }
666
846
  }
667
- async function runAppPromote(context, deploymentId, appName, projectRef) {
847
+ async function runAppPromote(context, deploymentId, appName, projectRef, configTarget) {
668
848
  ensurePreviewAppMode(context);
669
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app promote" });
849
+ const compute = await resolveComputeManagementContext(context, configTarget, "promote");
850
+ appName = appName ?? compute.configAppName;
851
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
852
+ commandName: "app promote",
853
+ projectDir: compute.projectDir
854
+ });
670
855
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
671
856
  const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
672
857
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
@@ -682,7 +867,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
682
867
  appId: selectedApp.id,
683
868
  deploymentId: targetDeployment.id,
684
869
  signal: context.runtime.signal,
685
- progress: createPreviewPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
870
+ progress: createPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
686
871
  }).catch((error) => {
687
872
  throw deployFailedError("Failed to promote deployment", error, ["prisma-cli app list-deploys"]);
688
873
  });
@@ -706,9 +891,14 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
706
891
  nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
707
892
  };
708
893
  }
709
- async function runAppRollback(context, appName, deploymentId, projectRef) {
894
+ async function runAppRollback(context, appName, deploymentId, projectRef, configTarget) {
710
895
  ensurePreviewAppMode(context);
711
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app rollback" });
896
+ const compute = await resolveComputeManagementContext(context, configTarget, "rollback");
897
+ appName = appName ?? compute.configAppName;
898
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
899
+ commandName: "app rollback",
900
+ projectDir: compute.projectDir
901
+ });
712
902
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
713
903
  const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
714
904
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
@@ -725,7 +915,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
725
915
  appId: selectedApp.id,
726
916
  deploymentId: targetDeployment.id,
727
917
  signal: context.runtime.signal,
728
- progress: createPreviewPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
918
+ progress: createPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
729
919
  }).catch((error) => {
730
920
  throw deployFailedError("Failed to roll back deployment", error, ["prisma-cli app list-deploys"]);
731
921
  });
@@ -750,9 +940,14 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
750
940
  nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
751
941
  };
752
942
  }
753
- async function runAppRemove(context, appName, projectRef) {
943
+ async function runAppRemove(context, appName, projectRef, configTarget) {
754
944
  ensurePreviewAppMode(context);
755
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app remove" });
945
+ const compute = await resolveComputeManagementContext(context, configTarget, "remove");
946
+ appName = appName ?? compute.configAppName;
947
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
948
+ commandName: "app remove",
949
+ projectDir: compute.projectDir
950
+ });
756
951
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
757
952
  await confirmAppRemoval(context, selectedApp);
758
953
  const removedApp = await provider.removeApp(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
@@ -776,6 +971,7 @@ async function runAppRemove(context, appName, projectRef) {
776
971
  }
777
972
  async function resolveAppDomainTarget(context, options, commandName = "app domain") {
778
973
  ensurePreviewAppMode(context);
974
+ const compute = await resolveComputeManagementContext(context, options?.configTarget, commandName.replace(/^app /, ""));
779
975
  const branch = resolveDomainBranch(options?.branchName);
780
976
  if (toBranchKind(branch.name) !== "production") throw new CliError({
781
977
  code: "BRANCH_NOT_DEPLOYABLE",
@@ -791,10 +987,11 @@ async function resolveAppDomainTarget(context, options, commandName = "app domai
791
987
  const { provider, target, projectId } = await requireProviderAndProjectContext(context, options?.projectRef, {
792
988
  branch,
793
989
  commandName,
794
- envProjectId
990
+ envProjectId,
991
+ projectDir: compute.projectDir
795
992
  });
796
993
  const selectedApp = await resolveDomainAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
797
- explicitAppName: options?.appName,
994
+ explicitAppName: options?.appName ?? compute.configAppName,
798
995
  explicitAppId: envAppId
799
996
  });
800
997
  await context.stateStore.setSelectedApp(projectId, {
@@ -910,7 +1107,7 @@ async function confirmDomainRemoval(context, target, hostname) {
910
1107
  })) throw usageError("Custom domain removal canceled", "The command was canceled before the domain was detached.", "Rerun the command and confirm removal, or pass --yes.", [`prisma-cli app domain remove ${hostname} --app ${target.app.name} --yes`], "app");
911
1108
  }
912
1109
  function domainCommandError(command, error, hostname) {
913
- if (error instanceof PreviewDomainApiError) {
1110
+ if (error instanceof DomainApiError) {
914
1111
  if (command === "add" && (error.status === 400 || error.status === 422) && isDomainDnsError(error)) return domainDnsNotConfiguredError(hostname, error);
915
1112
  if (command === "add" && error.status === 400) return new CliError({
916
1113
  code: "DOMAIN_HOSTNAME_INVALID",
@@ -1091,7 +1288,7 @@ async function resolveDeployAppSelection(context, projectId, apps, options) {
1091
1288
  };
1092
1289
  return {
1093
1290
  appName: options.explicitAppName,
1094
- region: PREVIEW_DEFAULT_REGION,
1291
+ region: DEFAULT_REGION,
1095
1292
  displayName: options.explicitAppName,
1096
1293
  annotation: "set by --app",
1097
1294
  firstDeploy: options.firstDeploy
@@ -1107,6 +1304,25 @@ async function resolveDeployAppSelection(context, projectId, apps, options) {
1107
1304
  firstDeploy: options.firstDeploy
1108
1305
  };
1109
1306
  }
1307
+ if (options.configAppName) {
1308
+ const configName = options.configAppName;
1309
+ const matches = findAppsByName(apps, configName.value);
1310
+ if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, configName.value, options.firstDeploy);
1311
+ const matched = matches[0];
1312
+ if (matched) return {
1313
+ appId: matched.id,
1314
+ displayName: matched.name,
1315
+ annotation: configName.annotation,
1316
+ firstDeploy: options.firstDeploy
1317
+ };
1318
+ return {
1319
+ appName: configName.value,
1320
+ region: DEFAULT_REGION,
1321
+ displayName: configName.value,
1322
+ annotation: configName.annotation,
1323
+ firstDeploy: options.firstDeploy
1324
+ };
1325
+ }
1110
1326
  const inferredName = await options.inferName();
1111
1327
  const matches = findAppsByName(apps, inferredName.name);
1112
1328
  if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, inferredName.name, options.firstDeploy);
@@ -1119,7 +1335,7 @@ async function resolveDeployAppSelection(context, projectId, apps, options) {
1119
1335
  };
1120
1336
  return {
1121
1337
  appName: inferredName.name,
1122
- region: PREVIEW_DEFAULT_REGION,
1338
+ region: DEFAULT_REGION,
1123
1339
  displayName: inferredName.name,
1124
1340
  annotation: inferredName.source === "package-name" ? "created from package.json" : "created from directory name",
1125
1341
  firstDeploy: options.firstDeploy
@@ -1151,7 +1367,7 @@ async function resolveAmbiguousDeployApp(context, matches, targetName, firstDepl
1151
1367
  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");
1152
1368
  if (selected === createNew) return {
1153
1369
  appName: targetName,
1154
- region: PREVIEW_DEFAULT_REGION,
1370
+ region: DEFAULT_REGION,
1155
1371
  displayName: targetName,
1156
1372
  annotation: "created from package.json",
1157
1373
  firstDeploy
@@ -1316,7 +1532,7 @@ async function requirePreviewAppProviderWithClient(context) {
1316
1532
  if (!client) throw authRequiredError(["prisma-cli auth login"]);
1317
1533
  return {
1318
1534
  client,
1319
- provider: createPreviewAppProvider(client, createPreviewLogAuthOptions(context.runtime.env, context.runtime.signal))
1535
+ provider: createAppProvider(client, createPreviewLogAuthOptions(context.runtime.env, context.runtime.signal))
1320
1536
  };
1321
1537
  }
1322
1538
  function createPreviewLogAuthOptions(env, signal) {
@@ -1363,18 +1579,24 @@ async function resolveProjectContext(context, client, explicitProject, options)
1363
1579
  workspace: authState.workspace,
1364
1580
  explicitProject,
1365
1581
  envProjectId: options?.envProjectId,
1582
+ projectDir: options?.projectDir,
1366
1583
  listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
1367
1584
  commandName: options?.commandName
1368
1585
  });
1369
1586
  if (resolvedResult.isErr()) throw projectResolutionErrorToCliError(resolvedResult.error);
1370
1587
  const resolved = resolvedResult.value;
1371
- const branch = options?.branch ?? await resolveDeployBranch(context, void 0);
1588
+ const requested = options?.branch ?? await resolveDeployBranch(context, void 0);
1589
+ const remoteBranch = options?.branch ? null : await resolveReadBranch(client, {
1590
+ projectId: resolved.project.id,
1591
+ branchName: requested.name,
1592
+ signal: context.runtime.signal
1593
+ });
1372
1594
  return {
1373
1595
  ...resolved,
1374
- branch: {
1596
+ branch: remoteBranch ?? {
1375
1597
  id: null,
1376
- name: branch.name,
1377
- kind: toBranchKind(branch.name)
1598
+ name: requested.name,
1599
+ kind: toBranchKind(requested.name)
1378
1600
  }
1379
1601
  };
1380
1602
  }
@@ -1559,22 +1781,130 @@ async function resolveDeployBranch(context, explicitBranchName) {
1559
1781
  annotation: "default"
1560
1782
  };
1561
1783
  }
1784
+ async function resolveComputeTargetOrThrow(context, configTarget, commandName, options) {
1785
+ let config;
1786
+ if (options?.preloaded !== void 0) config = options.preloaded;
1787
+ else {
1788
+ const loaded = await loadComputeConfig$1(context.runtime.cwd, context.runtime.signal);
1789
+ if (loaded.isErr()) throw computeConfigErrorToCliError(loaded.error, commandName);
1790
+ config = loaded.value;
1791
+ }
1792
+ if (!config) {
1793
+ 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");
1794
+ return {
1795
+ config: null,
1796
+ target: null
1797
+ };
1798
+ }
1799
+ const requestedTarget = configTarget ?? inferComputeTargetFromCwd(config, context.runtime.cwd);
1800
+ const selected = selectComputeDeployTarget(config, requestedTarget);
1801
+ if (selected.isErr()) {
1802
+ if (options?.targetOptional && selected.error instanceof ComputeConfigTargetRequiredError) return {
1803
+ config,
1804
+ target: null
1805
+ };
1806
+ throw computeConfigErrorToCliError(selected.error, commandName);
1807
+ }
1808
+ return {
1809
+ config,
1810
+ target: selected.value
1811
+ };
1812
+ }
1813
+ /**
1814
+ * Compute-config context for app management commands: the project directory
1815
+ * (where `.prisma/local.json` lives) and the config-selected app name, which
1816
+ * ranks below `--app` but above the remembered app selection.
1817
+ */
1818
+ async function resolveComputeManagementContext(context, configTarget, commandName) {
1819
+ const compute = await resolveComputeTargetOrThrow(context, configTarget, commandName, { targetOptional: true });
1820
+ return {
1821
+ projectDir: compute.config?.configDir ?? context.runtime.cwd,
1822
+ configAppName: compute.target?.name ?? compute.target?.key ?? void 0
1823
+ };
1824
+ }
1825
+ async function resolveComputeAppDir(context, compute) {
1826
+ if (!compute.config || !compute.target) return context.runtime.cwd;
1827
+ const appDir = computeTargetAppDir(compute.config, compute.target);
1828
+ if (!compute.target.root) return appDir;
1829
+ context.runtime.signal.throwIfAborted();
1830
+ try {
1831
+ await access(appDir);
1832
+ context.runtime.signal.throwIfAborted();
1833
+ } catch (error) {
1834
+ if (context.runtime.signal.aborted) throw error;
1835
+ throw new CliError({
1836
+ code: "COMPUTE_CONFIG_INVALID",
1837
+ domain: "app",
1838
+ summary: `App root "${compute.target.root}" does not exist`,
1839
+ why: `${compute.config.relativeConfigPath} points the selected app at "${compute.target.root}", but that directory does not exist.`,
1840
+ fix: `Fix the root path in ${compute.config.relativeConfigPath} or create the directory.`,
1841
+ where: appDir,
1842
+ meta: {
1843
+ appRoot: compute.target.root,
1844
+ appDir
1845
+ },
1846
+ exitCode: 2,
1847
+ nextSteps: ["prisma-cli app deploy"]
1848
+ });
1849
+ }
1850
+ return appDir;
1851
+ }
1852
+ /**
1853
+ * `prisma.app.json` is no longer read or written. A leftover file that
1854
+ * matches the effective settings only warns; one with custom values fails
1855
+ * with migration guidance so builds never silently change.
1856
+ */
1857
+ async function handleLegacyBuildSettings(context, appDir, effective) {
1858
+ const legacy = await detectLegacyBuildSettings({
1859
+ appPath: appDir,
1860
+ effective,
1861
+ signal: context.runtime.signal
1862
+ });
1863
+ switch (legacy.kind) {
1864
+ case "absent": return [];
1865
+ case "matching": return [`${PRISMA_APP_CONFIG_FILENAME} is no longer used and matches the resolved build settings. Delete it.`];
1866
+ case "invalid": return [`${PRISMA_APP_CONFIG_FILENAME} is no longer used and could not be parsed. Delete it.`];
1867
+ case "custom": {
1868
+ const buildBlock = [
1869
+ "build: {",
1870
+ ` command: ${legacy.buildCommand === null ? "null" : JSON.stringify(legacy.buildCommand)},`,
1871
+ ` outputDirectory: ${JSON.stringify(legacy.outputDirectory)},`,
1872
+ "}"
1873
+ ].join(" ");
1874
+ throw new CliError({
1875
+ code: "BUILD_SETTINGS_MIGRATION_REQUIRED",
1876
+ domain: "app",
1877
+ summary: `${PRISMA_APP_CONFIG_FILENAME} is no longer supported`,
1878
+ why: `${PRISMA_APP_CONFIG_FILENAME} contains custom build settings that differ from the resolved defaults, and the file is no longer read.`,
1879
+ fix: `Move the settings into prisma.compute.ts as \`${buildBlock}\` on this app, then delete ${PRISMA_APP_CONFIG_FILENAME}.`,
1880
+ where: legacy.configPath,
1881
+ meta: {
1882
+ configPath: legacy.configPath,
1883
+ buildCommand: legacy.buildCommand,
1884
+ outputDirectory: legacy.outputDirectory
1885
+ },
1886
+ exitCode: 2,
1887
+ nextSteps: ["prisma-cli app deploy"]
1888
+ });
1889
+ }
1890
+ }
1891
+ }
1562
1892
  async function resolveDeployFramework(context, options) {
1563
- if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, "set by --framework");
1893
+ if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, options.requestedFrameworkAnnotation ?? "set by --framework");
1564
1894
  if (options.entrypoint) return {
1565
1895
  key: "bun",
1566
1896
  buildType: "bun",
1567
1897
  displayName: "Bun",
1568
- annotation: "set by --entry"
1898
+ annotation: options.entrypointAnnotation ?? "set by --entry"
1569
1899
  };
1570
- const detected = await detectDeployFramework(context.runtime.cwd, context.runtime.signal);
1900
+ const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
1571
1901
  if (detected) return detected;
1572
- throw frameworkNotDetectedError(context.runtime.cwd);
1902
+ throw frameworkNotDetectedError(options.appDir);
1573
1903
  }
1574
- function resolveDeployRuntime(requestedHttpPort, framework) {
1904
+ function resolveDeployRuntime(requestedHttpPort, requestedHttpPortAnnotation, framework) {
1575
1905
  if (requestedHttpPort) return {
1576
1906
  port: parseDeployHttpPort(requestedHttpPort),
1577
- annotation: "set by --http-port"
1907
+ annotation: requestedHttpPortAnnotation ?? "set by --http-port"
1578
1908
  };
1579
1909
  return {
1580
1910
  port: FRAMEWORK_DEFAULT_HTTP_PORT,
@@ -1589,8 +1919,8 @@ async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signa
1589
1919
  if (explicitEntrypoint || framework.buildType !== "bun") return explicitEntrypoint;
1590
1920
  const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd, signal));
1591
1921
  if (packageEntrypoint) return packageEntrypoint;
1592
- if (framework.key !== "hono") return;
1593
- const defaultEntrypoint = "src/index.ts";
1922
+ const defaultEntrypoint = frameworkFromAlias(framework.key)?.defaultEntrypoint;
1923
+ if (!defaultEntrypoint) return;
1594
1924
  signal.throwIfAborted();
1595
1925
  try {
1596
1926
  await access(path.join(cwd, defaultEntrypoint));
@@ -1604,34 +1934,22 @@ async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signa
1604
1934
  }
1605
1935
  async function detectDeployFramework(cwd, signal) {
1606
1936
  const packageJson = await readBunPackageJson(cwd, signal);
1607
- const nextConfig = await detectNextConfig(cwd, signal);
1608
- if (nextConfig.exists || hasPackageDependency(packageJson, "next")) return {
1609
- key: "nextjs",
1610
- buildType: "nextjs",
1611
- displayName: "Next.js",
1612
- annotation: nextConfig.standalone ? "standalone output detected" : nextConfig.exists ? "detected from next.config" : "detected from package.json"
1613
- };
1614
- if (hasPackageDependency(packageJson, "hono")) return {
1615
- key: "hono",
1616
- buildType: "bun",
1617
- displayName: "Hono",
1618
- annotation: "detected from package.json"
1619
- };
1620
- if (hasAnyPackageDependency(packageJson, TANSTACK_START_PACKAGES)) return {
1621
- key: "tanstack-start",
1622
- buildType: "tanstack-start",
1623
- displayName: "TanStack Start",
1624
- annotation: "detected from package.json"
1625
- };
1937
+ for (const framework of FRAMEWORKS) {
1938
+ if (framework.detectConfigFiles.length === 0 && framework.detectPackages.length === 0) continue;
1939
+ const configFile = await detectFrameworkConfigFile(cwd, framework, signal);
1940
+ if (!configFile.exists && !hasAnyPackageDependency(packageJson, framework.detectPackages)) continue;
1941
+ const annotation = framework.key === "nextjs" && configFile.standalone ? "standalone output detected" : configFile.exists ? `detected from ${path.basename(configFile.path)}` : "detected from package.json";
1942
+ return {
1943
+ key: framework.key,
1944
+ buildType: framework.buildType,
1945
+ displayName: framework.displayName,
1946
+ annotation
1947
+ };
1948
+ }
1626
1949
  return null;
1627
1950
  }
1628
- async function detectNextConfig(cwd, signal) {
1629
- for (const candidate of [
1630
- "next.config.js",
1631
- "next.config.mjs",
1632
- "next.config.ts",
1633
- "next.config.mts"
1634
- ]) {
1951
+ async function detectFrameworkConfigFile(cwd, framework, signal) {
1952
+ for (const candidate of framework.detectConfigFiles) {
1635
1953
  const filePath = path.join(cwd, candidate);
1636
1954
  signal.throwIfAborted();
1637
1955
  try {
@@ -1641,7 +1959,8 @@ async function detectNextConfig(cwd, signal) {
1641
1959
  });
1642
1960
  return {
1643
1961
  exists: true,
1644
- standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content)
1962
+ standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content),
1963
+ path: filePath
1645
1964
  };
1646
1965
  } catch (error) {
1647
1966
  if (signal.aborted) throw error;
@@ -1650,7 +1969,8 @@ async function detectNextConfig(cwd, signal) {
1650
1969
  }
1651
1970
  return {
1652
1971
  exists: false,
1653
- standalone: false
1972
+ standalone: false,
1973
+ path: null
1654
1974
  };
1655
1975
  }
1656
1976
  function hasPackageDependency(packageJson, dependencyName) {
@@ -1663,48 +1983,41 @@ function hasDependency(dependencies, dependencyName) {
1663
1983
  return Boolean(dependencies && typeof dependencies === "object" && dependencyName in dependencies);
1664
1984
  }
1665
1985
  function frameworkFromUserFacingValue(value, annotation) {
1666
- switch (value.trim().toLowerCase()) {
1667
- case "next":
1668
- case "next.js":
1669
- case "nextjs": return {
1670
- key: "nextjs",
1671
- buildType: "nextjs",
1672
- displayName: "Next.js",
1673
- annotation
1674
- };
1675
- case "hono": return {
1676
- key: "hono",
1677
- buildType: "bun",
1678
- displayName: "Hono",
1679
- annotation
1680
- };
1681
- case "bun": return {
1682
- key: "bun",
1683
- buildType: "bun",
1684
- displayName: "Bun",
1685
- annotation
1686
- };
1687
- case "tanstack":
1688
- case "tanstack-start":
1689
- case "@tanstack/react-start":
1690
- case "@tanstack/solid-start": return {
1691
- key: "tanstack-start",
1692
- buildType: "tanstack-start",
1693
- displayName: "TanStack Start",
1694
- annotation
1695
- };
1696
- default: throw frameworkNotDetectedError(void 0, value);
1697
- }
1986
+ const framework = frameworkFromAlias(value);
1987
+ if (!framework) throw frameworkNotDetectedError(void 0, value);
1988
+ return {
1989
+ key: framework.key,
1990
+ buildType: framework.buildType,
1991
+ displayName: framework.displayName,
1992
+ annotation
1993
+ };
1994
+ }
1995
+ /**
1996
+ * The nuxt and astro strategies build with their framework CLI and stage
1997
+ * fixed output, so a compute config `build` block has nothing to apply to.
1998
+ * Erroring beats silently ignoring committed settings.
1999
+ */
2000
+ function assertConfigBackedBuildSettings(buildType) {
2001
+ if (isConfigBackedBuildType(buildType)) return;
2002
+ const displayName = FRAMEWORKS.find((framework) => framework.buildType === buildType)?.displayName ?? buildType;
2003
+ throw new CliError({
2004
+ code: "BUILD_SETTINGS_UNSUPPORTED",
2005
+ domain: "app",
2006
+ summary: `build settings are not supported for ${displayName} apps`,
2007
+ why: `${displayName} deploys run \`${buildType} build\` and package its output automatically.`,
2008
+ fix: "Remove the `build` block from prisma.compute.ts for this app.",
2009
+ exitCode: 2
2010
+ });
1698
2011
  }
1699
2012
  function frameworkNotDetectedError(cwd, requestedFramework) {
1700
- const supported = "Next.js, Hono, TanStack Start, Bun";
2013
+ const supported = FRAMEWORKS.map((framework) => framework.displayName).join(", ");
1701
2014
  const directory = cwd ? ` in ${formatDeployDirectory(cwd)}` : "";
1702
2015
  return new CliError({
1703
2016
  code: "FRAMEWORK_NOT_DETECTED",
1704
2017
  domain: "app",
1705
2018
  summary: requestedFramework ? `Unsupported framework "${requestedFramework}"` : `Cannot detect a supported framework${directory}`,
1706
2019
  why: `Supported Beta frameworks: ${supported}.`,
1707
- fix: "Add one of these frameworks as a dependency, pass --framework <nextjs|hono|tanstack-start|bun>, or pass --entry <path> for a Bun app.",
2020
+ 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.`,
1708
2021
  exitCode: 2,
1709
2022
  nextSteps: [
1710
2023
  "prisma-cli app deploy --framework nextjs",
@@ -1717,14 +2030,14 @@ function frameworkNotDetectedError(cwd, requestedFramework) {
1717
2030
  }
1718
2031
  async function maybeRenderDeploySetupBlock(context, details) {
1719
2032
  if (context.flags.json || context.flags.quiet) return;
1720
- const directory = formatDeployDirectory(context.runtime.cwd);
2033
+ const directory = formatAppDirectoryLabel(context.runtime.cwd, details.appDir);
1721
2034
  const prefix = details.includeDirectory ? `Deploying ${directory} to` : "Deploying to";
1722
2035
  context.output.stderr.write(`${prefix} ${details.projectName} / ${details.branchName} / ${details.appName}\n\n`);
1723
2036
  }
1724
2037
  function maybeRenderDeployBuildSettings(context, resolution) {
1725
2038
  if (context.flags.json || context.flags.quiet) return;
1726
2039
  const settings = resolution.settings;
1727
- const title = resolution.status === "created" ? `Created ${resolution.relativeConfigPath}` : `Using ${resolution.relativeConfigPath}`;
2040
+ const title = resolution.status === "config" ? `Using ${resolution.relativeConfigPath}` : "Build settings";
1728
2041
  context.output.stderr.write(`${title}\n${renderDeployOutputRows(context.ui, [{
1729
2042
  label: "Build Command",
1730
2043
  value: settings.buildCommand ?? "none",
@@ -1761,9 +2074,9 @@ async function maybeCustomizeDeploySettings(context, options) {
1761
2074
  input: context.runtime.stdin,
1762
2075
  output: context.runtime.stderr,
1763
2076
  message: `Framework (${options.framework.displayName})`,
1764
- choices: DEPLOY_FRAMEWORKS.map((framework) => ({
1765
- label: frameworkDisplayName(framework),
1766
- value: framework
2077
+ choices: FRAMEWORKS.map((framework) => ({
2078
+ label: framework.displayName,
2079
+ value: framework.key
1767
2080
  }))
1768
2081
  }), "set by you");
1769
2082
  const requestedPort = await textPrompt({
@@ -1806,14 +2119,6 @@ function maybeRenderDeploySettingsPreview(context, options) {
1806
2119
  value: `HTTP ${options.runtime.port}`
1807
2120
  }]).join("\n")}\n\n`);
1808
2121
  }
1809
- function frameworkDisplayName(framework) {
1810
- switch (framework) {
1811
- case "nextjs": return "Next.js";
1812
- case "hono": return "Hono";
1813
- case "tanstack-start": return "TanStack Start";
1814
- case "bun": return "Bun";
1815
- }
1816
- }
1817
2122
  function validateDeployHttpPortText(value) {
1818
2123
  if (!value?.trim()) return;
1819
2124
  try {
@@ -1827,6 +2132,11 @@ function formatDeployDirectory(cwd) {
1827
2132
  const basename = path.basename(cwd);
1828
2133
  return basename ? `./${basename}` : ".";
1829
2134
  }
2135
+ function formatAppDirectoryLabel(cwd, appDir) {
2136
+ if (appDir === cwd) return formatDeployDirectory(cwd);
2137
+ const relative = path.relative(cwd, appDir).split(path.sep).join("/");
2138
+ return relative.startsWith("..") ? relative : `./${relative}`;
2139
+ }
1830
2140
  async function readCurrentWorkspaceId(context) {
1831
2141
  const state = await context.stateStore.read();
1832
2142
  if (state.auth?.workspaceId) return state.auth.workspaceId;
@@ -1835,26 +2145,36 @@ async function readCurrentWorkspaceId(context) {
1835
2145
  function normalizeBuildType(requestedBuildType) {
1836
2146
  if (!requestedBuildType) return "auto";
1837
2147
  if (isPreviewBuildType(requestedBuildType)) return requestedBuildType;
1838
- 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");
2148
+ throw usageError(`Unsupported build type "${requestedBuildType}"`, `Only ${APP_BUILD_TYPES.join(", ")} are supported in the current preview.`, "Pass a supported --build-type value.", getBuildTypeExamples("build"), "app");
1839
2149
  }
1840
2150
  function isPreviewBuildType(value) {
1841
- return PREVIEW_BUILD_TYPES.includes(value);
2151
+ return APP_BUILD_TYPES.includes(value);
1842
2152
  }
1843
2153
  function getBuildTypeExamples(commandName) {
1844
- return RESOLVED_PREVIEW_BUILD_TYPES.map((buildType) => {
2154
+ return RESOLVED_APP_BUILD_TYPES.map((buildType) => {
1845
2155
  return `prisma-cli app ${commandName} --build-type ${buildType}${buildType === "bun" ? " --entry server.ts" : ""}`;
1846
2156
  });
1847
2157
  }
1848
2158
  function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
1849
- if (buildType !== "auto" && buildType !== "bun" && entrypoint) {
2159
+ if (buildType !== "auto" && !ENTRYPOINT_BUILD_TYPES.includes(buildType) && entrypoint) {
1850
2160
  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");
1851
2161
  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");
1852
2162
  }
1853
2163
  }
1854
- async function requireLocalBuildType(context, buildType, commandName) {
1855
- const resolvedBuildType = await resolveLocalBuildType(context.runtime.cwd, buildType, context.runtime.signal);
1856
- if (resolvedBuildType) return resolvedBuildType;
1857
- 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");
2164
+ /**
2165
+ * Resolves the framework for `app run` with the same detection as deploy, so
2166
+ * a repo that deploys without flags also runs without flags. Local dev server
2167
+ * support is intentionally narrower than deploy build support: only Next.js
2168
+ * and Bun/Hono have dev servers in the current preview.
2169
+ */
2170
+ async function resolveLocalRunFramework(context, options) {
2171
+ if (LOCAL_DEV_BUILD_TYPES.includes(options.requestedBuildType)) {
2172
+ if (options.configFramework && computeFrameworkToBuildType(options.configFramework) === options.requestedBuildType) return frameworkFromUserFacingValue(options.configFramework, `set by ${COMPUTE_CONFIG_FILENAME$1}`);
2173
+ return frameworkFromUserFacingValue(options.requestedBuildType, "set by --build-type");
2174
+ }
2175
+ const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
2176
+ if (detected && LOCAL_DEV_BUILD_TYPES.includes(detected.buildType)) return detected;
2177
+ 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");
1858
2178
  }
1859
2179
  function parseLocalPort(requestedPort) {
1860
2180
  if (!requestedPort) return DEFAULT_LOCAL_DEV_PORT;
@@ -2075,6 +2395,7 @@ function formatBuildTypeName(buildType) {
2075
2395
  case "nextjs": return "Next.js";
2076
2396
  case "nuxt": return "Nuxt";
2077
2397
  case "astro": return "Astro";
2398
+ case "nestjs": return "NestJS";
2078
2399
  case "tanstack-start": return "TanStack Start";
2079
2400
  case "bun": return "Bun";
2080
2401
  case "auto": return "Auto";