@prisma/cli 3.0.0-beta.0 → 3.0.0-beta.2

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.
@@ -8,11 +8,14 @@ import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
8
8
  import { requireComputeAuth } from "../lib/auth/guard.js";
9
9
  import { readAuthState } from "../lib/auth/auth-ops.js";
10
10
  import { parseEnvAssignments } from "../lib/app/env-vars.js";
11
- import { renderDeployOutputRows } from "../lib/app/deploy-output.js";
12
- import { readBunPackageJson } from "../lib/app/bun-project.js";
11
+ import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
12
+ import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
13
13
  import { DEFAULT_LOCAL_DEV_PORT, resolveLocalBuildType, runLocalApp } from "../lib/app/local-dev.js";
14
- import { inferTargetName, projectNotFoundError, resolveProjectTarget } from "../lib/project/resolution.js";
15
- import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, ensureLocalResolutionPinGitignore, readLocalResolutionPin, writeLocalResolutionPin } from "../lib/project/local-pin.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";
18
+ import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
16
19
  import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
17
20
  import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
18
21
  import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress } from "../lib/app/preview-progress.js";
@@ -28,8 +31,10 @@ import open from "open";
28
31
  const DEPLOY_FRAMEWORKS = [
29
32
  "nextjs",
30
33
  "hono",
31
- "tanstack-start"
34
+ "tanstack-start",
35
+ "bun"
32
36
  ];
37
+ const TANSTACK_START_PACKAGES = ["@tanstack/react-start", "@tanstack/solid-start"];
33
38
  const FRAMEWORK_DEFAULT_HTTP_PORT = 3e3;
34
39
  const PRISMA_PROJECT_ID_ENV_VAR = "PRISMA_PROJECT_ID";
35
40
  const PRISMA_APP_ID_ENV_VAR = "PRISMA_APP_ID";
@@ -96,67 +101,65 @@ async function runAppDeploy(context, appName, options) {
96
101
  ensurePreviewAppMode(context);
97
102
  const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
98
103
  const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
99
- const skipLocalPin = Boolean(envProjectId || envAppId);
104
+ assertExclusiveDeployProjectInputs({
105
+ projectRef: options?.projectRef,
106
+ createProjectName: options?.createProjectName,
107
+ envProjectId
108
+ });
109
+ const skipLocalPin = Boolean(envProjectId || options?.projectRef || options?.createProjectName);
100
110
  const localPin = skipLocalPin ? { kind: "missing" } : await readLocalResolutionPin(context.runtime.cwd);
101
111
  if (!skipLocalPin && localPin.kind === "invalid") throw localResolutionPinStaleError();
102
- const explicitBuildType = Boolean(options?.buildType && options.buildType !== "auto");
103
112
  const branch = await resolveDeployBranch(context, options?.branchName);
104
113
  if (options?.httpPort) parseDeployHttpPort(options.httpPort);
105
- let framework = await resolveDeployFramework(context, {
114
+ assertSupportedEntrypointForRequestedDeployShape({
106
115
  requestedFramework: options?.framework,
107
- requestedBuildType: options?.buildType,
108
- explicitBuildType
116
+ entrypoint: options?.entrypoint
109
117
  });
110
- let runtime = resolveDeployRuntime(options?.httpPort, framework);
111
- assertSupportedEntrypoint(framework.buildType, options?.entrypoint, "deploy");
112
- const envVars = toOptionalEnvVars(parseEnvAssignments(options?.envAssignments, { commandName: "deploy" }));
113
- const firstDeploy = !skipLocalPin && localPin.kind === "missing";
114
118
  const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
115
- allowCreate: true,
116
119
  branch,
120
+ createProjectName: options?.createProjectName,
117
121
  envProjectId,
118
122
  localPin
119
123
  });
124
+ let localPinResult;
125
+ if (target.localPinAction) {
126
+ const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction);
127
+ localPinResult = setupResult.localPin;
128
+ maybeRenderProjectLinked(context, setupResult.directory, setupResult.project.name, setupResult.localPin.path);
129
+ }
130
+ let framework = await resolveDeployFramework(context, {
131
+ requestedFramework: options?.framework,
132
+ entrypoint: options?.entrypoint
133
+ });
134
+ let runtime = resolveDeployRuntime(options?.httpPort, framework);
135
+ assertSupportedEntrypoint(framework.buildType, options?.entrypoint, "deploy");
136
+ const envVars = toOptionalEnvVars(parseEnvAssignments(options?.envAssignments, { commandName: "deploy" }));
120
137
  const selectedApp = await resolveDeployAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
121
138
  explicitAppName: appName,
122
139
  explicitAppId: envAppId,
123
- firstDeploy,
140
+ firstDeploy: Boolean(target.localPinAction),
124
141
  inferName: () => inferTargetName(context.runtime.cwd)
125
142
  });
126
143
  await maybeRenderDeploySetupBlock(context, {
127
- firstDeploy: selectedApp.firstDeploy,
128
- workspaceName: target.workspace.name,
144
+ includeDirectory: !target.localPinAction,
129
145
  projectName: target.project.name,
130
- projectAnnotation: annotationForProjectResolution(target.resolution),
131
146
  branchName: target.branch.name,
132
- branchAnnotation: branch.annotation,
133
- appName: selectedApp.displayName,
134
- appAnnotation: selectedApp.annotation,
135
- framework,
136
- runtime
147
+ appName: selectedApp.displayName
137
148
  });
138
149
  const customized = await maybeCustomizeDeploySettings(context, {
139
150
  framework,
140
151
  runtime,
141
152
  firstDeploy: selectedApp.firstDeploy,
142
153
  explicitFramework: Boolean(options?.framework),
143
- explicitBuildType,
154
+ explicitEntrypoint: Boolean(options?.entrypoint),
144
155
  explicitHttpPort: Boolean(options?.httpPort)
145
156
  });
146
157
  framework = customized.framework;
147
158
  runtime = customized.runtime;
148
159
  const buildType = framework.buildType;
149
160
  assertSupportedEntrypoint(buildType, options?.entrypoint, "deploy");
161
+ const entrypoint = await resolveDeployEntrypoint(context.runtime.cwd, framework, options?.entrypoint);
150
162
  const portMapping = parseDeployPortMapping(String(runtime.port));
151
- const shouldWriteLocalPin = firstDeploy && !skipLocalPin;
152
- if (shouldWriteLocalPin) {
153
- await writeLocalResolutionPin(context.runtime.cwd, {
154
- workspaceId: target.workspace.id,
155
- projectId: target.project.id
156
- });
157
- await ensureLocalResolutionPinGitignore(context.runtime.cwd);
158
- maybeRenderLocalPinBound(context, target.project.name);
159
- }
160
163
  const progressState = createPreviewDeployProgressState();
161
164
  const deployStartedAt = Date.now();
162
165
  const deployResult = await provider.deployApp({
@@ -166,7 +169,7 @@ async function runAppDeploy(context, appName, options) {
166
169
  appId: selectedApp.appId,
167
170
  appName: selectedApp.appName,
168
171
  region: selectedApp.region,
169
- entrypoint: options?.entrypoint,
172
+ entrypoint,
170
173
  buildType,
171
174
  portMapping,
172
175
  envVars,
@@ -194,10 +197,7 @@ async function runAppDeploy(context, appName, options) {
194
197
  },
195
198
  deployment: deployResult.deployment,
196
199
  durationMs: deployDurationMs,
197
- localPin: shouldWriteLocalPin ? {
198
- path: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
199
- written: true
200
- } : void 0
200
+ localPin: localPinResult
201
201
  },
202
202
  warnings: [],
203
203
  nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${deployResult.deployment.id}`]
@@ -205,7 +205,7 @@ async function runAppDeploy(context, appName, options) {
205
205
  }
206
206
  async function runAppListDeploys(context, appName, projectRef) {
207
207
  ensurePreviewAppMode(context);
208
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
208
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app list-deploys" });
209
209
  const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
210
210
  if (!selectedApp) return {
211
211
  command: "app.list-deploys",
@@ -242,7 +242,7 @@ async function runAppListDeploys(context, appName, projectRef) {
242
242
  }
243
243
  async function runAppShow(context, appName, projectRef) {
244
244
  ensurePreviewAppMode(context);
245
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
245
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app show" });
246
246
  const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
247
247
  if (!selectedApp) return {
248
248
  command: "app.show",
@@ -318,7 +318,7 @@ async function runAppShowDeploy(context, deploymentId) {
318
318
  }
319
319
  async function runAppOpen(context, appName, projectRef) {
320
320
  ensurePreviewAppMode(context);
321
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
321
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app open" });
322
322
  const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
323
323
  if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
324
324
  const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
@@ -352,7 +352,7 @@ async function runAppOpen(context, appName, projectRef) {
352
352
  }
353
353
  async function runAppDomainAdd(context, hostname, options) {
354
354
  const normalizedHostname = normalizeDomainHostname(hostname);
355
- const target = await resolveAppDomainTarget(context, options);
355
+ const target = await resolveAppDomainTarget(context, options, `app domain add ${normalizedHostname}`);
356
356
  const added = await target.provider.addDomain({
357
357
  appId: target.app.id,
358
358
  hostname: normalizedHostname
@@ -372,7 +372,7 @@ async function runAppDomainAdd(context, hostname, options) {
372
372
  }
373
373
  async function runAppDomainShow(context, hostname, options) {
374
374
  const normalizedHostname = normalizeDomainHostname(hostname);
375
- const target = await resolveAppDomainTarget(context, options);
375
+ const target = await resolveAppDomainTarget(context, options, `app domain show ${normalizedHostname}`);
376
376
  const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show");
377
377
  const detail = await target.provider.showDomain(domain.id).catch((error) => {
378
378
  throw domainCommandError("show", error, normalizedHostname);
@@ -389,7 +389,7 @@ async function runAppDomainShow(context, hostname, options) {
389
389
  }
390
390
  async function runAppDomainRemove(context, hostname, options) {
391
391
  const normalizedHostname = normalizeDomainHostname(hostname);
392
- const target = await resolveAppDomainTarget(context, options);
392
+ const target = await resolveAppDomainTarget(context, options, `app domain remove ${normalizedHostname}`);
393
393
  const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove");
394
394
  await confirmDomainRemoval(context, target.resultTarget, normalizedHostname);
395
395
  await target.provider.removeDomain(domain.id).catch((error) => {
@@ -408,7 +408,7 @@ async function runAppDomainRemove(context, hostname, options) {
408
408
  }
409
409
  async function runAppDomainRetry(context, hostname, options) {
410
410
  const normalizedHostname = normalizeDomainHostname(hostname);
411
- const target = await resolveAppDomainTarget(context, options);
411
+ const target = await resolveAppDomainTarget(context, options, `app domain retry ${normalizedHostname}`);
412
412
  const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry");
413
413
  const retried = await target.provider.retryDomain(domain.id).catch((error) => {
414
414
  throw domainCommandError("retry", error, normalizedHostname);
@@ -426,7 +426,7 @@ async function runAppDomainRetry(context, hostname, options) {
426
426
  async function runAppDomainWait(context, hostname, options) {
427
427
  const normalizedHostname = normalizeDomainHostname(hostname);
428
428
  const timeoutMs = parseDomainWaitTimeout(options?.timeout);
429
- const target = await resolveAppDomainTarget(context, options);
429
+ const target = await resolveAppDomainTarget(context, options, `app domain wait ${normalizedHostname}`);
430
430
  const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait");
431
431
  if (!context.flags.json && !context.flags.quiet) context.output.stderr.write([
432
432
  `app domain wait -> Waiting for ${normalizedHostname} to become active.`,
@@ -478,7 +478,7 @@ async function runAppDomainWait(context, hostname, options) {
478
478
  }
479
479
  async function runAppLogs(context, appName, deploymentId, projectRef) {
480
480
  ensurePreviewAppMode(context);
481
- const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef);
481
+ const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app logs" });
482
482
  const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName);
483
483
  if (!context.flags.json && !context.flags.quiet) {
484
484
  const lines = renderCommandHeader(context.ui, {
@@ -602,7 +602,7 @@ function writeLogRecord(context, record) {
602
602
  }
603
603
  async function runAppPromote(context, deploymentId, appName, projectRef) {
604
604
  ensurePreviewAppMode(context);
605
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
605
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app promote" });
606
606
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
607
607
  const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
608
608
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
@@ -642,7 +642,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
642
642
  }
643
643
  async function runAppRollback(context, appName, deploymentId, projectRef) {
644
644
  ensurePreviewAppMode(context);
645
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
645
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app rollback" });
646
646
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
647
647
  const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
648
648
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
@@ -684,7 +684,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
684
684
  }
685
685
  async function runAppRemove(context, appName, projectRef) {
686
686
  ensurePreviewAppMode(context);
687
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
687
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app remove" });
688
688
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
689
689
  await confirmAppRemoval(context, selectedApp);
690
690
  const removedApp = await provider.removeApp(selectedApp.id).catch((error) => {
@@ -705,7 +705,7 @@ async function runAppRemove(context, appName, projectRef) {
705
705
  nextSteps: ["prisma-cli app deploy", "prisma-cli app list-deploys"]
706
706
  };
707
707
  }
708
- async function resolveAppDomainTarget(context, options) {
708
+ async function resolveAppDomainTarget(context, options, commandName = "app domain") {
709
709
  ensurePreviewAppMode(context);
710
710
  const branch = resolveDomainBranch(options?.branchName);
711
711
  if (toBranchKind(branch.name) !== "production") throw new CliError({
@@ -719,14 +719,10 @@ async function resolveAppDomainTarget(context, options) {
719
719
  });
720
720
  const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
721
721
  const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
722
- const skipLocalPin = Boolean(envProjectId || envAppId);
723
- const localPin = skipLocalPin ? { kind: "missing" } : await readLocalResolutionPin(context.runtime.cwd);
724
- if (!skipLocalPin && localPin.kind === "invalid") throw localResolutionPinStaleError();
725
- const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
726
- allowCreate: false,
722
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, options?.projectRef, {
727
723
  branch,
728
- envProjectId,
729
- localPin
724
+ commandName,
725
+ envProjectId
730
726
  });
731
727
  const selectedApp = await resolveDomainAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
732
728
  explicitAppName: options?.appName,
@@ -1221,9 +1217,9 @@ async function listApps(context, provider, projectId, branchName) {
1221
1217
  domain: "project",
1222
1218
  summary: "Project not found",
1223
1219
  why: `The resolved project "${projectId}" does not exist in the authenticated workspace or is no longer accessible.`,
1224
- fix: "Pass --project <id-or-name>, or run prisma-cli project show to inspect resolution for this directory.",
1220
+ fix: "Pass --project <id-or-name>, or run prisma-cli project show to inspect this directory's binding.",
1225
1221
  exitCode: 1,
1226
- nextSteps: ["prisma-cli project show", "prisma-cli app deploy --project <id-or-name>"]
1222
+ nextSteps: ["prisma-cli project show", "prisma-cli project link <id-or-name>"]
1227
1223
  });
1228
1224
  throw deployFailedError("Failed to list apps", error, ["prisma-cli project show"]);
1229
1225
  });
@@ -1258,7 +1254,7 @@ function createPreviewLogAuthOptions(env) {
1258
1254
  }
1259
1255
  async function requireProviderAndProjectContext(context, explicitProject, options) {
1260
1256
  const { client, provider } = await requirePreviewAppProviderWithClient(context);
1261
- const target = await resolveProjectContext(context, client, provider, explicitProject, options);
1257
+ const target = await resolveProjectContext(context, client, explicitProject, options);
1262
1258
  return {
1263
1259
  client,
1264
1260
  provider,
@@ -1276,31 +1272,16 @@ async function requireProviderAndDeployProjectContext(context, explicitProject,
1276
1272
  projectId: target.project.id
1277
1273
  };
1278
1274
  }
1279
- async function resolveProjectContext(context, client, provider, explicitProject, options) {
1275
+ async function resolveProjectContext(context, client, explicitProject, options) {
1280
1276
  const authState = await requireAuthenticatedAuthState(context);
1281
1277
  if (!authState.workspace) throw workspaceRequiredError();
1282
1278
  const resolved = await resolveProjectTarget({
1283
1279
  context,
1284
1280
  workspace: authState.workspace,
1285
1281
  explicitProject,
1282
+ envProjectId: options?.envProjectId,
1286
1283
  listProjects: () => listRealWorkspaceProjects(client, authState.workspace),
1287
- createProject: options?.allowCreate ? async (name) => {
1288
- const project = await provider.createProject({ name }).catch((error) => {
1289
- throw createProjectOnFirstDeployError({
1290
- error,
1291
- inferredName: name,
1292
- workspaceName: authState.workspace.name
1293
- });
1294
- });
1295
- return {
1296
- id: project.id,
1297
- name: project.name,
1298
- workspace: authState.workspace
1299
- };
1300
- } : void 0,
1301
- allowCreate: options?.allowCreate,
1302
- prompt: createSelectPromptPort(context),
1303
- remember: true
1284
+ commandName: options?.commandName
1304
1285
  });
1305
1286
  const branch = options?.branch ?? await resolveDeployBranch(context, void 0);
1306
1287
  return {
@@ -1316,30 +1297,30 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1316
1297
  if (!workspace) throw workspaceRequiredError();
1317
1298
  const branch = options.branch ?? await resolveDeployBranch(context, void 0);
1318
1299
  const projects = await listRealWorkspaceProjects(client, workspace);
1319
- const createProject = options.allowCreate ? async (name) => {
1320
- const project = await provider.createProject({ name }).catch((error) => {
1321
- throw createProjectOnFirstDeployError({
1322
- error,
1323
- inferredName: name,
1324
- workspaceName: workspace.name
1325
- });
1326
- });
1327
- return {
1328
- id: project.id,
1329
- name: project.name,
1330
- workspace
1331
- };
1332
- } : void 0;
1333
- if (explicitProject) return withDeployBranch(await resolveProjectTarget({
1334
- context,
1300
+ if (explicitProject) return withDeployBranch({
1335
1301
  workspace,
1336
- explicitProject,
1337
- listProjects: async () => projects,
1338
- createProject,
1339
- allowCreate: options.allowCreate,
1340
- prompt: createSelectPromptPort(context),
1341
- remember: true
1342
- }), branch);
1302
+ project: toProjectSummary(resolveProjectForSetup(explicitProject, projects, workspace)),
1303
+ resolution: {
1304
+ projectSource: "explicit",
1305
+ targetName: explicitProject,
1306
+ targetNameSource: "explicit"
1307
+ },
1308
+ localPinAction: "linked"
1309
+ }, branch);
1310
+ if (options.createProjectName) {
1311
+ const projectName = options.createProjectName.trim();
1312
+ if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
1313
+ return withDeployBranch({
1314
+ workspace,
1315
+ project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace)),
1316
+ resolution: {
1317
+ projectSource: "created",
1318
+ targetName: projectName,
1319
+ targetNameSource: "explicit"
1320
+ },
1321
+ localPinAction: "created"
1322
+ }, branch);
1323
+ }
1343
1324
  if (options.envProjectId) {
1344
1325
  const project = projects.find((candidate) => candidate.id === options.envProjectId);
1345
1326
  if (!project) throw projectNotFoundError(options.envProjectId, workspace);
@@ -1368,15 +1349,58 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1368
1349
  }
1369
1350
  }, branch);
1370
1351
  }
1371
- return withDeployBranch(await resolveProjectTarget({
1352
+ const platformMapping = await resolveDurablePlatformMapping();
1353
+ if (platformMapping && platformMapping.workspace.id === workspace.id) return withDeployBranch({
1354
+ workspace,
1355
+ project: toProjectSummary(platformMapping),
1356
+ resolution: {
1357
+ projectSource: "platform-mapping",
1358
+ targetName: platformMapping.name,
1359
+ targetNameSource: "platform-mapping"
1360
+ }
1361
+ }, branch);
1362
+ if (canPrompt(context) && !context.flags.yes) return withDeployBranch(await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects), branch);
1363
+ throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd));
1364
+ }
1365
+ async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects) {
1366
+ const setup = await promptForProjectSetupChoice({
1372
1367
  context,
1368
+ projects,
1369
+ createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace),
1370
+ cancel: {
1371
+ why: "Deploy needs a Project before it can continue.",
1372
+ fix: "Choose an existing Project or create a new one, then rerun deploy.",
1373
+ nextSteps: ["prisma-cli app deploy --project <id-or-name>", "prisma-cli app deploy --create-project <name>"]
1374
+ }
1375
+ });
1376
+ return {
1373
1377
  workspace,
1374
- listProjects: async () => projects,
1375
- createProject,
1376
- allowCreate: options.allowCreate,
1377
- prompt: createSelectPromptPort(context),
1378
- remember: true
1379
- }), branch);
1378
+ project: setup.project,
1379
+ resolution: {
1380
+ projectSource: setup.action === "created" ? "created" : "prompt",
1381
+ targetName: setup.targetName,
1382
+ targetNameSource: setup.targetNameSource
1383
+ },
1384
+ localPinAction: setup.action
1385
+ };
1386
+ }
1387
+ async function createProjectForDeploySetup(provider, projectName, workspace) {
1388
+ const created = await provider.createProject({ name: projectName }).catch((error) => {
1389
+ throw projectCreateFailedError(error, projectName, workspace, {
1390
+ nextSteps: [
1391
+ "prisma-cli project list",
1392
+ "prisma-cli app deploy --project <id-or-name>",
1393
+ `prisma-cli app deploy --create-project ${formatCommandArgument(projectName)}`
1394
+ ],
1395
+ permissionFix: "Choose an existing Project with --project, or grant the token permission to create Projects in this workspace.",
1396
+ fallbackFix: "Choose an existing Project with --project, or retry after addressing the platform error above."
1397
+ });
1398
+ });
1399
+ return {
1400
+ id: created.id,
1401
+ name: created.name,
1402
+ workspace
1403
+ };
1380
1404
  }
1381
1405
  function withDeployBranch(target, branch) {
1382
1406
  return {
@@ -1387,15 +1411,22 @@ function withDeployBranch(target, branch) {
1387
1411
  }
1388
1412
  };
1389
1413
  }
1390
- function toProjectSummary(project) {
1391
- return {
1392
- id: project.id,
1393
- name: project.name
1394
- };
1395
- }
1396
1414
  function toBranchKind(name) {
1397
1415
  return name === "production" || name === "main" ? "production" : "preview";
1398
1416
  }
1417
+ function assertExclusiveDeployProjectInputs(options) {
1418
+ const provided = [
1419
+ options.projectRef ? "--project" : null,
1420
+ options.createProjectName ? "--create-project" : null,
1421
+ options.envProjectId ? PRISMA_PROJECT_ID_ENV_VAR : null
1422
+ ].filter((value) => Boolean(value));
1423
+ if (provided.length <= 1) return;
1424
+ throw usageError("Project selection is ambiguous", `${provided.join(", ")} cannot be used together.`, "Choose exactly one Project source for this deploy.", [
1425
+ "prisma-cli app deploy --project <id-or-name>",
1426
+ "prisma-cli app deploy --create-project <name>",
1427
+ `unset ${PRISMA_PROJECT_ID_ENV_VAR}`
1428
+ ], "project");
1429
+ }
1399
1430
  async function resolveDeployBranch(context, explicitBranchName) {
1400
1431
  if (explicitBranchName) return {
1401
1432
  name: explicitBranchName,
@@ -1436,15 +1467,12 @@ async function resolveGitHeadPath(gitPath) {
1436
1467
  }
1437
1468
  async function resolveDeployFramework(context, options) {
1438
1469
  if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, "set by --framework");
1439
- if (options.explicitBuildType) {
1440
- const buildType = normalizeBuildType(options.requestedBuildType);
1441
- if (buildType !== "auto") return {
1442
- key: buildType,
1443
- buildType,
1444
- displayName: formatBuildTypeName(buildType),
1445
- annotation: "set by --build-type"
1446
- };
1447
- }
1470
+ if (options.entrypoint) return {
1471
+ key: "bun",
1472
+ buildType: "bun",
1473
+ displayName: "Bun",
1474
+ annotation: "set by --entry"
1475
+ };
1448
1476
  const detected = await detectDeployFramework(context.runtime.cwd);
1449
1477
  if (detected) return detected;
1450
1478
  throw frameworkNotDetectedError(context.runtime.cwd);
@@ -1459,6 +1487,24 @@ function resolveDeployRuntime(requestedHttpPort, framework) {
1459
1487
  annotation: `${framework.displayName} default`
1460
1488
  };
1461
1489
  }
1490
+ function assertSupportedEntrypointForRequestedDeployShape(options) {
1491
+ if (!options.requestedFramework) return;
1492
+ assertSupportedEntrypoint(frameworkFromUserFacingValue(options.requestedFramework, "set by --framework").buildType, options.entrypoint, "deploy");
1493
+ }
1494
+ async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint) {
1495
+ if (explicitEntrypoint || framework.buildType !== "bun") return explicitEntrypoint;
1496
+ const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd));
1497
+ if (packageEntrypoint) return packageEntrypoint;
1498
+ if (framework.key !== "hono") return;
1499
+ const defaultEntrypoint = "src/index.ts";
1500
+ try {
1501
+ await access(path.join(cwd, defaultEntrypoint));
1502
+ return defaultEntrypoint;
1503
+ } catch (error) {
1504
+ if (error.code !== "ENOENT") throw error;
1505
+ return;
1506
+ }
1507
+ }
1462
1508
  async function detectDeployFramework(cwd) {
1463
1509
  const packageJson = await readBunPackageJson(cwd);
1464
1510
  const nextConfig = await detectNextConfig(cwd);
@@ -1474,7 +1520,7 @@ async function detectDeployFramework(cwd) {
1474
1520
  displayName: "Hono",
1475
1521
  annotation: "detected from package.json"
1476
1522
  };
1477
- if (hasPackageDependency(packageJson, "@tanstack/start")) return {
1523
+ if (hasAnyPackageDependency(packageJson, TANSTACK_START_PACKAGES)) return {
1478
1524
  key: "tanstack-start",
1479
1525
  buildType: "tanstack-start",
1480
1526
  displayName: "TanStack Start",
@@ -1487,7 +1533,8 @@ async function detectNextConfig(cwd) {
1487
1533
  "next.config.js",
1488
1534
  "next.config.mjs",
1489
1535
  "next.config.cjs",
1490
- "next.config.ts"
1536
+ "next.config.ts",
1537
+ "next.config.mts"
1491
1538
  ]) {
1492
1539
  const filePath = path.join(cwd, candidate);
1493
1540
  try {
@@ -1508,6 +1555,9 @@ async function detectNextConfig(cwd) {
1508
1555
  function hasPackageDependency(packageJson, dependencyName) {
1509
1556
  return hasDependency(packageJson?.dependencies, dependencyName) || hasDependency(packageJson?.devDependencies, dependencyName);
1510
1557
  }
1558
+ function hasAnyPackageDependency(packageJson, dependencyNames) {
1559
+ return dependencyNames.some((dependencyName) => hasPackageDependency(packageJson, dependencyName));
1560
+ }
1511
1561
  function hasDependency(dependencies, dependencyName) {
1512
1562
  return Boolean(dependencies && typeof dependencies === "object" && dependencyName in dependencies);
1513
1563
  }
@@ -1527,9 +1577,16 @@ function frameworkFromUserFacingValue(value, annotation) {
1527
1577
  displayName: "Hono",
1528
1578
  annotation
1529
1579
  };
1580
+ case "bun": return {
1581
+ key: "bun",
1582
+ buildType: "bun",
1583
+ displayName: "Bun",
1584
+ annotation
1585
+ };
1530
1586
  case "tanstack":
1531
1587
  case "tanstack-start":
1532
- case "@tanstack/start": return {
1588
+ case "@tanstack/react-start":
1589
+ case "@tanstack/solid-start": return {
1533
1590
  key: "tanstack-start",
1534
1591
  buildType: "tanstack-start",
1535
1592
  displayName: "TanStack Start",
@@ -1539,82 +1596,47 @@ function frameworkFromUserFacingValue(value, annotation) {
1539
1596
  }
1540
1597
  }
1541
1598
  function frameworkNotDetectedError(cwd, requestedFramework) {
1542
- const supported = "Next.js, Hono, TanStack Start";
1599
+ const supported = "Next.js, Hono, TanStack Start, Bun";
1543
1600
  const directory = cwd ? ` in ${formatDeployDirectory(cwd)}` : "";
1544
1601
  return new CliError({
1545
1602
  code: "FRAMEWORK_NOT_DETECTED",
1546
1603
  domain: "app",
1547
1604
  summary: requestedFramework ? `Unsupported framework "${requestedFramework}"` : `Cannot detect a supported framework${directory}`,
1548
1605
  why: `Supported Beta frameworks: ${supported}.`,
1549
- fix: "Add one of these frameworks as a dependency, or pass --framework <nextjs|hono|tanstack-start>.",
1606
+ fix: "Add one of these frameworks as a dependency, pass --framework <nextjs|hono|tanstack-start|bun>, or pass --entry <path> for a Bun app.",
1550
1607
  exitCode: 2,
1551
1608
  nextSteps: [
1552
1609
  "prisma-cli app deploy --framework nextjs",
1553
1610
  "prisma-cli app deploy --framework hono",
1554
- "prisma-cli app deploy --framework tanstack-start"
1611
+ "prisma-cli app deploy --framework tanstack-start",
1612
+ "prisma-cli app deploy --framework bun --entry server.ts",
1613
+ "prisma-cli app deploy --entry server.ts"
1555
1614
  ]
1556
1615
  });
1557
1616
  }
1558
1617
  async function maybeRenderDeploySetupBlock(context, details) {
1559
1618
  if (context.flags.json || context.flags.quiet) return;
1560
1619
  const directory = formatDeployDirectory(context.runtime.cwd);
1561
- if (!details.firstDeploy) {
1562
- context.output.stderr.write(`Deploying ${directory} to ${details.projectName} / ${details.branchName} / ${details.appName}\n\n`);
1563
- return;
1564
- }
1565
- const title = `Setting up your local directory ${formatLocalDirectory(context.runtime.cwd, context.runtime.env)}`;
1566
- const rows = details.firstDeploy ? [
1567
- {
1568
- label: "Workspace",
1569
- value: details.workspaceName
1570
- },
1571
- {
1572
- label: "Project",
1573
- value: details.projectName,
1574
- origin: details.projectAnnotation
1575
- },
1576
- {
1577
- label: "Branch",
1578
- value: details.branchName,
1579
- origin: details.branchAnnotation
1580
- },
1581
- {
1582
- label: "App",
1583
- value: details.appName,
1584
- origin: details.appAnnotation
1585
- },
1586
- {
1587
- label: "Framework",
1588
- value: details.framework.displayName,
1589
- origin: details.framework.annotation
1590
- },
1591
- {
1592
- label: "Runtime",
1593
- value: `HTTP ${details.runtime.port}`,
1594
- origin: details.runtime.annotation
1595
- }
1596
- ] : [];
1597
- const lines = [
1598
- title,
1599
- "",
1600
- ...renderDeployOutputRows(context.ui, rows),
1601
- ""
1602
- ];
1603
- context.output.stderr.write(`${lines.join("\n")}\n`);
1620
+ const prefix = details.includeDirectory ? `Deploying ${directory} to` : "Deploying to";
1621
+ context.output.stderr.write(`${prefix} ${details.projectName} / ${details.branchName} / ${details.appName}\n\n`);
1604
1622
  }
1605
- function maybeRenderLocalPinBound(context, projectName) {
1623
+ function maybeRenderProjectLinked(context, directory, projectName, localPinPath) {
1606
1624
  if (context.flags.json || context.flags.quiet) return;
1607
- context.output.stderr.write(`This directory is now linked to project ${projectName}.\n\n`);
1625
+ context.output.stderr.write(`${context.ui.success("✔")} Linked "${directory}" to Project "${projectName}"\nSaved ${localPinPath}\n\n`);
1608
1626
  }
1609
1627
  async function maybeCustomizeDeploySettings(context, options) {
1610
- if (!options.firstDeploy || context.flags.yes || options.explicitFramework || options.explicitBuildType || options.explicitHttpPort || !canPrompt(context)) return {
1628
+ if (!options.firstDeploy || context.flags.yes || options.explicitFramework || options.explicitEntrypoint || options.explicitHttpPort || !canPrompt(context)) return {
1611
1629
  framework: options.framework,
1612
1630
  runtime: options.runtime
1613
1631
  };
1632
+ maybeRenderDeploySettingsPreview(context, {
1633
+ framework: options.framework,
1634
+ runtime: options.runtime
1635
+ });
1614
1636
  if (!await confirmPrompt({
1615
1637
  input: context.runtime.stdin,
1616
1638
  output: context.runtime.stderr,
1617
- message: "Customize settings?",
1639
+ message: "Customize build settings?",
1618
1640
  initialValue: false
1619
1641
  })) return {
1620
1642
  framework: options.framework,
@@ -1659,24 +1681,22 @@ async function maybeCustomizeDeploySettings(context, options) {
1659
1681
  runtime
1660
1682
  };
1661
1683
  }
1662
- function annotationForProjectResolution(resolution) {
1663
- switch (resolution.projectSource) {
1664
- case "explicit": return "set by --project";
1665
- case "env": return `from ${PRISMA_PROJECT_ID_ENV_VAR}`;
1666
- case "local-pin": return "from local pin";
1667
- case "created": return resolution.targetNameSource === "directory-name" ? "created from directory name" : "created from package.json";
1668
- case "package-name":
1669
- case "directory-name": return "linked to existing project";
1670
- case "platform-mapping":
1671
- case "remembered-local": return "linked to existing project";
1672
- case "prompt": return "selected by you";
1673
- }
1684
+ function maybeRenderDeploySettingsPreview(context, options) {
1685
+ if (context.flags.quiet || context.flags.json) return;
1686
+ context.output.stderr.write(`Detected ${options.framework.displayName}\n${renderDeploySettingsPreview(context.ui, [{
1687
+ key: "framework",
1688
+ value: options.framework.displayName
1689
+ }, {
1690
+ key: "runtime",
1691
+ value: `HTTP ${options.runtime.port}`
1692
+ }]).join("\n")}\n\n`);
1674
1693
  }
1675
1694
  function frameworkDisplayName(framework) {
1676
1695
  switch (framework) {
1677
1696
  case "nextjs": return "Next.js";
1678
1697
  case "hono": return "Hono";
1679
1698
  case "tanstack-start": return "TanStack Start";
1699
+ case "bun": return "Bun";
1680
1700
  }
1681
1701
  }
1682
1702
  function validateDeployHttpPortText(value) {
@@ -1692,15 +1712,6 @@ function formatDeployDirectory(cwd) {
1692
1712
  const basename = path.basename(cwd);
1693
1713
  return basename ? `./${basename}` : ".";
1694
1714
  }
1695
- function formatLocalDirectory(cwd, env) {
1696
- const resolved = path.resolve(cwd);
1697
- const home = env.HOME ? path.resolve(env.HOME) : null;
1698
- if (home && (resolved === home || resolved.startsWith(`${home}${path.sep}`))) {
1699
- const relative = path.relative(home, resolved);
1700
- return relative ? `~/${relative}` : "~";
1701
- }
1702
- return resolved;
1703
- }
1704
1715
  async function readCurrentWorkspaceId(context) {
1705
1716
  const state = await context.stateStore.read();
1706
1717
  if (state.auth?.workspaceId) return state.auth.workspaceId;
@@ -1720,7 +1731,10 @@ function getBuildTypeExamples(commandName) {
1720
1731
  });
1721
1732
  }
1722
1733
  function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
1723
- if (buildType !== "auto" && buildType !== "bun" && entrypoint) 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");
1734
+ if (buildType !== "auto" && buildType !== "bun" && entrypoint) {
1735
+ 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");
1736
+ 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");
1737
+ }
1724
1738
  }
1725
1739
  async function requireLocalBuildType(context, buildType, commandName) {
1726
1740
  const resolvedBuildType = await resolveLocalBuildType(context.runtime.cwd, buildType);
@@ -1761,24 +1775,41 @@ function deployFailedError(summary, error, nextSteps) {
1761
1775
  function appDeployFailedError(error, progress) {
1762
1776
  const why = error instanceof Error ? error.message : String(error);
1763
1777
  const debug = formatDebugDetails(error);
1764
- if (progress.buildStarted && !progress.buildCompleted) return new CliError({
1765
- code: "BUILD_FAILED",
1766
- domain: "app",
1767
- summary: "Build failed locally.",
1768
- why,
1769
- fix: "Inspect the build output above, fix the error, and redeploy.",
1770
- debug,
1771
- meta: { phase: "build" },
1772
- humanLines: [
1773
- "Build failed locally.",
1774
- "",
1775
- `✗ Built ${why}`,
1776
- "",
1777
- "Fix: Inspect the build output above, fix the error, and redeploy."
1778
- ],
1779
- exitCode: 1,
1780
- nextSteps: []
1781
- });
1778
+ if (progress.buildStarted && !progress.buildCompleted) {
1779
+ const standaloneOutputFailure = isNextStandaloneOutputFailure(why);
1780
+ const fix = standaloneOutputFailure ? "Add output: \"standalone\" to next.config.*, then rerun deploy." : "Inspect the build output above, fix the error, and redeploy.";
1781
+ const nextSteps = standaloneOutputFailure ? ["Add output: \"standalone\" to next.config.*, then rerun prisma-cli app deploy"] : [];
1782
+ const nextActions = standaloneOutputFailure ? [{
1783
+ kind: "edit-file",
1784
+ journey: "deploy-app",
1785
+ label: "Add Next.js standalone output",
1786
+ reason: "Prisma Compute needs Next.js standalone output to build a deployable server artifact."
1787
+ }, {
1788
+ kind: "run-command",
1789
+ journey: "deploy-app",
1790
+ label: "Rerun deploy",
1791
+ command: "prisma-cli app deploy"
1792
+ }] : [];
1793
+ return new CliError({
1794
+ code: "BUILD_FAILED",
1795
+ domain: "app",
1796
+ summary: "Build failed locally.",
1797
+ why,
1798
+ fix,
1799
+ debug,
1800
+ meta: { phase: "build" },
1801
+ humanLines: [
1802
+ "Build failed locally.",
1803
+ "",
1804
+ `✗ Built ${why}`,
1805
+ "",
1806
+ `Fix: ${fix}`
1807
+ ],
1808
+ exitCode: 1,
1809
+ nextSteps,
1810
+ nextActions
1811
+ });
1812
+ }
1782
1813
  if (!progress.buildStarted) return deployFailedError("App deploy failed", error, ["prisma-cli app deploy"]);
1783
1814
  const phaseHeadline = progress.containerLive ? "The deployment started, but the app is not ready yet." : "Deploy failed after the build completed.";
1784
1815
  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."];
@@ -1825,61 +1856,52 @@ function localResolutionPinStaleError() {
1825
1856
  domain: "project",
1826
1857
  summary: "Local project binding is stale",
1827
1858
  why: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
1828
- fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} and re-run to re-bootstrap.`,
1859
+ fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}, then choose a Project explicitly.`,
1829
1860
  meta: { pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH },
1830
1861
  exitCode: 1,
1831
- nextSteps: ["prisma-cli app deploy"]
1862
+ nextSteps: [
1863
+ "prisma-cli project list",
1864
+ "prisma-cli project link <id-or-name>",
1865
+ "prisma-cli app deploy --project <id-or-name>"
1866
+ ]
1832
1867
  });
1833
1868
  }
1834
1869
  function readDeployEnvOverride(context, name) {
1835
1870
  const value = context.runtime.env[name]?.trim();
1836
1871
  return value ? value : void 0;
1837
1872
  }
1838
- /**
1839
- * `app deploy` falls into "create a new project on first deploy" when no
1840
- * existing project matches the package.json name (or the cwd basename as a
1841
- * fallback). When the create call fails the user often doesn't realise the
1842
- * CLI was attempting to create a project at all — they thought the deploy
1843
- * would find an existing project. Surface that context, and recommend the
1844
- * explicit `--project` flag as the unambiguous way out.
1845
- */
1846
- function createProjectOnFirstDeployError(options) {
1847
- const { error, inferredName, workspaceName } = options;
1848
- const status = extractHttpStatus(error);
1849
- const errorMessage = error instanceof Error ? error.message : String(error);
1850
- const inferredContext = `No existing project matched the package.json name \`${inferredName}\`, so the CLI attempted to create one.`;
1851
- const nextSteps = ["prisma-cli project list", "prisma-cli app deploy --project <id-or-name>"];
1852
- if (status === 401 || status === 403) return new CliError({
1853
- code: "AUTH_FORBIDDEN",
1854
- domain: "auth",
1855
- summary: "Could not create a new project for this deploy",
1856
- why: `${inferredContext} The platform rejected the create (HTTP ${status}).`,
1857
- fix: `Pass --project <id-or-name> to deploy into an existing project, or grant the service token project-create permission on workspace \`${workspaceName}\`.`,
1858
- debug: formatDebugDetails(error),
1859
- exitCode: 1,
1860
- nextSteps
1861
- });
1873
+ function projectSetupRequiredError(projects, suggestedName) {
1874
+ const createCommand = `prisma-cli app deploy --create-project ${formatCommandArgument(suggestedName.name)}`;
1862
1875
  return new CliError({
1863
- code: "DEPLOY_FAILED",
1864
- domain: "app",
1865
- summary: "Could not create a new project for this deploy",
1866
- why: `${inferredContext} ${errorMessage}`.trim(),
1867
- fix: "Pass --project <id-or-name> to deploy into an existing project, or retry after addressing the platform error above.",
1868
- debug: formatDebugDetails(error),
1876
+ code: "PROJECT_SETUP_REQUIRED",
1877
+ domain: "project",
1878
+ summary: "Choose a Project before deploying this directory",
1879
+ why: "This directory is not linked to a Prisma Project, and deploy will not choose or create one implicitly.",
1880
+ fix: "Choose an existing Project with --project, create one with --create-project, or rerun interactively to pick from the setup list.",
1881
+ meta: {
1882
+ candidates: sortProjects(projects).map((project) => ({
1883
+ id: project.id,
1884
+ name: project.name
1885
+ })),
1886
+ suggestedProjectName: suggestedName.name,
1887
+ suggestedProjectNameSource: suggestedName.source,
1888
+ recoveryCommands: ["prisma-cli app deploy --project <id-or-name>", createCommand]
1889
+ },
1869
1890
  exitCode: 1,
1870
- nextSteps
1891
+ nextSteps: [
1892
+ "prisma-cli project list",
1893
+ "prisma-cli app deploy --project <id-or-name>",
1894
+ createCommand
1895
+ ],
1896
+ nextActions: buildProjectSetupNextActions({
1897
+ commandName: "app deploy",
1898
+ createCommand,
1899
+ reason: "This directory is not linked to a Prisma Project. Ask the user which Project to use before deploying; package and directory names are setup suggestions only."
1900
+ })
1871
1901
  });
1872
1902
  }
1873
- function extractHttpStatus(error) {
1874
- if (!error || typeof error !== "object") return null;
1875
- const candidate = error;
1876
- if (typeof candidate.statusCode === "number") return candidate.statusCode;
1877
- if (typeof candidate.status === "number") return candidate.status;
1878
- if (typeof candidate.message === "string") {
1879
- const match = /\(HTTP (\d{3})\)/.exec(candidate.message);
1880
- if (match) return Number.parseInt(match[1], 10);
1881
- }
1882
- return null;
1903
+ function isNextStandaloneOutputFailure(message) {
1904
+ return /next\.?js/i.test(message) && /standalone output/i.test(message);
1883
1905
  }
1884
1906
  function noDeploymentsError(summary, why) {
1885
1907
  return new CliError({