@prisma/cli 3.0.0-alpha.9 → 3.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,14 +9,18 @@ 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
11
  import { renderDeployOutputRows } from "../lib/app/deploy-output.js";
12
- import { readBunPackageJson } from "../lib/app/bun-project.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
- import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress, createPreviewUpdateEnvProgress } from "../lib/app/preview-progress.js";
19
- import { createPreviewAppProvider } from "../lib/app/preview-provider.js";
21
+ import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress } from "../lib/app/preview-progress.js";
22
+ import { PreviewDomainApiError, createPreviewAppProvider } from "../lib/app/preview-provider.js";
23
+ import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
20
24
  import { createSelectPromptPort } from "./select-prompt-port.js";
21
25
  import { requireAuthenticatedAuthState } from "./auth.js";
22
26
  import { listRealWorkspaceProjects } from "./project.js";
@@ -27,8 +31,10 @@ import open from "open";
27
31
  const DEPLOY_FRAMEWORKS = [
28
32
  "nextjs",
29
33
  "hono",
30
- "tanstack-start"
34
+ "tanstack-start",
35
+ "bun"
31
36
  ];
37
+ const TANSTACK_START_PACKAGES = ["@tanstack/react-start", "@tanstack/solid-start"];
32
38
  const FRAMEWORK_DEFAULT_HTTP_PORT = 3e3;
33
39
  const PRISMA_PROJECT_ID_ENV_VAR = "PRISMA_PROJECT_ID";
34
40
  const PRISMA_APP_ID_ENV_VAR = "PRISMA_APP_ID";
@@ -95,67 +101,65 @@ async function runAppDeploy(context, appName, options) {
95
101
  ensurePreviewAppMode(context);
96
102
  const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
97
103
  const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
98
- 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);
99
110
  const localPin = skipLocalPin ? { kind: "missing" } : await readLocalResolutionPin(context.runtime.cwd);
100
111
  if (!skipLocalPin && localPin.kind === "invalid") throw localResolutionPinStaleError();
101
- const explicitBuildType = Boolean(options?.buildType && options.buildType !== "auto");
102
112
  const branch = await resolveDeployBranch(context, options?.branchName);
103
113
  if (options?.httpPort) parseDeployHttpPort(options.httpPort);
104
- let framework = await resolveDeployFramework(context, {
114
+ assertSupportedEntrypointForRequestedDeployShape({
105
115
  requestedFramework: options?.framework,
106
- requestedBuildType: options?.buildType,
107
- explicitBuildType
116
+ entrypoint: options?.entrypoint
108
117
  });
109
- let runtime = resolveDeployRuntime(options?.httpPort, framework);
110
- assertSupportedEntrypoint(framework.buildType, options?.entrypoint, "deploy");
111
- const envVars = toOptionalEnvVars(parseEnvAssignments(options?.envAssignments, { commandName: "deploy" }));
112
- const firstDeploy = !skipLocalPin && localPin.kind === "missing";
113
118
  const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
114
- allowCreate: true,
115
119
  branch,
120
+ createProjectName: options?.createProjectName,
116
121
  envProjectId,
117
122
  localPin
118
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" }));
119
137
  const selectedApp = await resolveDeployAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
120
138
  explicitAppName: appName,
121
139
  explicitAppId: envAppId,
122
- firstDeploy,
140
+ firstDeploy: Boolean(target.localPinAction),
123
141
  inferName: () => inferTargetName(context.runtime.cwd)
124
142
  });
125
143
  await maybeRenderDeploySetupBlock(context, {
126
- firstDeploy: selectedApp.firstDeploy,
127
- workspaceName: target.workspace.name,
144
+ includeDirectory: !target.localPinAction,
128
145
  projectName: target.project.name,
129
- projectAnnotation: annotationForProjectResolution(target.resolution),
130
146
  branchName: target.branch.name,
131
- branchAnnotation: branch.annotation,
132
- appName: selectedApp.displayName,
133
- appAnnotation: selectedApp.annotation,
134
- framework,
135
- runtime
147
+ appName: selectedApp.displayName
136
148
  });
137
149
  const customized = await maybeCustomizeDeploySettings(context, {
138
150
  framework,
139
151
  runtime,
140
152
  firstDeploy: selectedApp.firstDeploy,
141
153
  explicitFramework: Boolean(options?.framework),
142
- explicitBuildType,
154
+ explicitEntrypoint: Boolean(options?.entrypoint),
143
155
  explicitHttpPort: Boolean(options?.httpPort)
144
156
  });
145
157
  framework = customized.framework;
146
158
  runtime = customized.runtime;
147
159
  const buildType = framework.buildType;
148
160
  assertSupportedEntrypoint(buildType, options?.entrypoint, "deploy");
161
+ const entrypoint = await resolveDeployEntrypoint(context.runtime.cwd, framework, options?.entrypoint);
149
162
  const portMapping = parseDeployPortMapping(String(runtime.port));
150
- const shouldWriteLocalPin = firstDeploy && !skipLocalPin;
151
- if (shouldWriteLocalPin) {
152
- await writeLocalResolutionPin(context.runtime.cwd, {
153
- workspaceId: target.workspace.id,
154
- projectId: target.project.id
155
- });
156
- await ensureLocalResolutionPinGitignore(context.runtime.cwd);
157
- maybeRenderLocalPinBound(context, target.project.name);
158
- }
159
163
  const progressState = createPreviewDeployProgressState();
160
164
  const deployStartedAt = Date.now();
161
165
  const deployResult = await provider.deployApp({
@@ -165,7 +169,7 @@ async function runAppDeploy(context, appName, options) {
165
169
  appId: selectedApp.appId,
166
170
  appName: selectedApp.appName,
167
171
  region: selectedApp.region,
168
- entrypoint: options?.entrypoint,
172
+ entrypoint,
169
173
  buildType,
170
174
  portMapping,
171
175
  envVars,
@@ -193,148 +197,15 @@ async function runAppDeploy(context, appName, options) {
193
197
  },
194
198
  deployment: deployResult.deployment,
195
199
  durationMs: deployDurationMs,
196
- localPin: shouldWriteLocalPin ? {
197
- path: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
198
- written: true
199
- } : void 0
200
+ localPin: localPinResult
200
201
  },
201
202
  warnings: [],
202
203
  nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${deployResult.deployment.id}`]
203
204
  };
204
205
  }
205
- async function runAppUpdateEnv(context, appName, envAssignments, projectRef) {
206
- ensurePreviewAppMode(context);
207
- emitLegacyEnvDeprecationWarning(context, "app update-env", "project env add");
208
- const envVars = parseEnvAssignments(envAssignments, {
209
- commandName: "update-env",
210
- requireAtLeastOne: true
211
- });
212
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
213
- const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
214
- if (!selectedApp) throw noDeploymentsError("No deployments available to update environment variables", "The resolved project does not have any deployed app yet.");
215
- const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
216
- throw deployFailedError("Failed to inspect app deployments", error, ["prisma-cli app list-deploys"]);
217
- });
218
- if (deploymentsResult.deployments.length === 0) throw noDeploymentsError("No deployments available to update environment variables", `The selected app "${deploymentsResult.app.name}" does not have any deployments yet.`);
219
- const updateResult = await provider.updateAppEnv({
220
- appId: deploymentsResult.app.id,
221
- envVars,
222
- progress: createPreviewUpdateEnvProgress(context.output.stderr, !context.flags.json && !context.flags.quiet),
223
- promoteProgress: createPreviewPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
224
- }).catch((error) => {
225
- throw deployFailedError("Failed to update app environment variables", error, ["prisma-cli app list-env"]);
226
- });
227
- await context.stateStore.setSelectedApp(projectId, {
228
- id: updateResult.app.id,
229
- name: updateResult.app.name
230
- });
231
- await context.stateStore.setKnownLiveDeployment(projectId, updateResult.app.id, updateResult.deployment.id);
232
- return {
233
- command: "app.update-env",
234
- result: {
235
- projectId: updateResult.projectId,
236
- app: {
237
- id: updateResult.app.id,
238
- name: updateResult.app.name
239
- },
240
- deployment: updateResult.deployment,
241
- variables: updateResult.variables
242
- },
243
- warnings: [],
244
- nextSteps: ["prisma-cli app list-env", `prisma-cli app show-deploy ${updateResult.deployment.id}`]
245
- };
246
- }
247
- async function runAppListEnv(context, appName, projectRef) {
248
- ensurePreviewAppMode(context);
249
- emitLegacyEnvDeprecationWarning(context, "app list-env", "project env list");
250
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
251
- const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
252
- if (!selectedApp) return {
253
- command: "app.list-env",
254
- result: {
255
- projectId,
256
- app: null,
257
- deployment: null,
258
- variables: []
259
- },
260
- warnings: [],
261
- nextSteps: ["prisma-cli app deploy"]
262
- };
263
- const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
264
- throw deployFailedError("Failed to inspect app deployments", error, ["prisma-cli app list-deploys"]);
265
- });
266
- const knownLiveDeploymentId = await context.stateStore.readKnownLiveDeployment(projectId, deploymentsResult.app.id);
267
- const missingKnownLiveDeploymentId = knownLiveDeploymentId && !deploymentsResult.deployments.some((candidate) => candidate.id === knownLiveDeploymentId) ? knownLiveDeploymentId : null;
268
- const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
269
- const deployments = applyLiveDeploymentHint(deploymentsResult.deployments, currentLiveDeploymentId).slice().sort((left, right) => right.createdAt.localeCompare(left.createdAt) || right.id.localeCompare(left.id));
270
- const deployment = currentLiveDeploymentId ? deployments.find((candidate) => candidate.id === currentLiveDeploymentId) ?? null : null;
271
- await context.stateStore.setSelectedApp(projectId, {
272
- id: deploymentsResult.app.id,
273
- name: deploymentsResult.app.name
274
- });
275
- if (missingKnownLiveDeploymentId) {
276
- const envResult = await provider.listAppEnvNames({
277
- appId: deploymentsResult.app.id,
278
- deploymentId: missingKnownLiveDeploymentId
279
- }).catch((error) => {
280
- throw deployFailedError("Failed to inspect app environment variables", error, ["prisma-cli app list-deploys"]);
281
- });
282
- return {
283
- command: "app.list-env",
284
- result: {
285
- projectId,
286
- app: {
287
- id: envResult.app.id,
288
- name: envResult.app.name
289
- },
290
- deployment: envResult.deployment,
291
- variables: envResult.variables
292
- },
293
- warnings: [],
294
- nextSteps: [`prisma-cli app show-deploy ${envResult.deployment.id}`]
295
- };
296
- }
297
- if (!deployment) return {
298
- command: "app.list-env",
299
- result: {
300
- projectId,
301
- app: {
302
- id: deploymentsResult.app.id,
303
- name: deploymentsResult.app.name
304
- },
305
- deployment: null,
306
- variables: []
307
- },
308
- warnings: [],
309
- nextSteps: ["prisma-cli app deploy"]
310
- };
311
- const envResult = await provider.listAppEnvNames({
312
- appId: deploymentsResult.app.id,
313
- deploymentId: deployment.id
314
- }).catch((error) => {
315
- throw deployFailedError("Failed to inspect app environment variables", error, ["prisma-cli app list-deploys"]);
316
- });
317
- return {
318
- command: "app.list-env",
319
- result: {
320
- projectId,
321
- app: {
322
- id: envResult.app.id,
323
- name: envResult.app.name
324
- },
325
- deployment: {
326
- ...deployment,
327
- live: deployment.live ?? envResult.deployment.live
328
- },
329
- variables: envResult.variables
330
- },
331
- warnings: [],
332
- nextSteps: deployment.id ? [`prisma-cli app show-deploy ${deployment.id}`] : ["prisma-cli app deploy"]
333
- };
334
- }
335
206
  async function runAppListDeploys(context, appName, projectRef) {
336
207
  ensurePreviewAppMode(context);
337
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
208
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app list-deploys" });
338
209
  const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
339
210
  if (!selectedApp) return {
340
211
  command: "app.list-deploys",
@@ -371,7 +242,7 @@ async function runAppListDeploys(context, appName, projectRef) {
371
242
  }
372
243
  async function runAppShow(context, appName, projectRef) {
373
244
  ensurePreviewAppMode(context);
374
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
245
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app show" });
375
246
  const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
376
247
  if (!selectedApp) return {
377
248
  command: "app.show",
@@ -447,7 +318,7 @@ async function runAppShowDeploy(context, deploymentId) {
447
318
  }
448
319
  async function runAppOpen(context, appName, projectRef) {
449
320
  ensurePreviewAppMode(context);
450
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
321
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app open" });
451
322
  const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
452
323
  if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
453
324
  const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
@@ -479,9 +350,135 @@ async function runAppOpen(context, appName, projectRef) {
479
350
  nextSteps: ["prisma-cli app show", `prisma-cli app show-deploy ${liveDeployment.id}`]
480
351
  };
481
352
  }
353
+ async function runAppDomainAdd(context, hostname, options) {
354
+ const normalizedHostname = normalizeDomainHostname(hostname);
355
+ const target = await resolveAppDomainTarget(context, options, `app domain add ${normalizedHostname}`);
356
+ const added = await target.provider.addDomain({
357
+ appId: target.app.id,
358
+ hostname: normalizedHostname
359
+ }).catch((error) => {
360
+ throw domainCommandError("add", error, normalizedHostname);
361
+ });
362
+ return {
363
+ command: "app.domain.add",
364
+ result: {
365
+ ...target.resultTarget,
366
+ domain: toAppDomainSummary(added.domain),
367
+ existing: added.existing
368
+ },
369
+ warnings: [],
370
+ nextSteps: [`prisma-cli app domain wait ${normalizedHostname}`, `prisma-cli app domain show ${normalizedHostname}`]
371
+ };
372
+ }
373
+ async function runAppDomainShow(context, hostname, options) {
374
+ const normalizedHostname = normalizeDomainHostname(hostname);
375
+ const target = await resolveAppDomainTarget(context, options, `app domain show ${normalizedHostname}`);
376
+ const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show");
377
+ const detail = await target.provider.showDomain(domain.id).catch((error) => {
378
+ throw domainCommandError("show", error, normalizedHostname);
379
+ });
380
+ return {
381
+ command: "app.domain.show",
382
+ result: {
383
+ ...target.resultTarget,
384
+ domain: toAppDomainSummary(detail)
385
+ },
386
+ warnings: [],
387
+ nextSteps: buildDomainShowNextSteps(detail)
388
+ };
389
+ }
390
+ async function runAppDomainRemove(context, hostname, options) {
391
+ const normalizedHostname = normalizeDomainHostname(hostname);
392
+ const target = await resolveAppDomainTarget(context, options, `app domain remove ${normalizedHostname}`);
393
+ const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove");
394
+ await confirmDomainRemoval(context, target.resultTarget, normalizedHostname);
395
+ await target.provider.removeDomain(domain.id).catch((error) => {
396
+ throw domainCommandError("remove", error, normalizedHostname);
397
+ });
398
+ return {
399
+ command: "app.domain.remove",
400
+ result: {
401
+ ...target.resultTarget,
402
+ hostname: normalizedHostname,
403
+ removed: true
404
+ },
405
+ warnings: [],
406
+ nextSteps: []
407
+ };
408
+ }
409
+ async function runAppDomainRetry(context, hostname, options) {
410
+ const normalizedHostname = normalizeDomainHostname(hostname);
411
+ const target = await resolveAppDomainTarget(context, options, `app domain retry ${normalizedHostname}`);
412
+ const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry");
413
+ const retried = await target.provider.retryDomain(domain.id).catch((error) => {
414
+ throw domainCommandError("retry", error, normalizedHostname);
415
+ });
416
+ return {
417
+ command: "app.domain.retry",
418
+ result: {
419
+ ...target.resultTarget,
420
+ domain: toAppDomainSummary(retried)
421
+ },
422
+ warnings: [],
423
+ nextSteps: [`prisma-cli app domain wait ${normalizedHostname}`]
424
+ };
425
+ }
426
+ async function runAppDomainWait(context, hostname, options) {
427
+ const normalizedHostname = normalizeDomainHostname(hostname);
428
+ const timeoutMs = parseDomainWaitTimeout(options?.timeout);
429
+ const target = await resolveAppDomainTarget(context, options, `app domain wait ${normalizedHostname}`);
430
+ const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait");
431
+ if (!context.flags.json && !context.flags.quiet) context.output.stderr.write([
432
+ `app domain wait -> Waiting for ${normalizedHostname} to become active.`,
433
+ "",
434
+ `Workspace: ${target.resultTarget.workspace.name} Project: ${target.resultTarget.project.name} Branch: ${target.resultTarget.branch.name} App: ${target.resultTarget.app.name}`,
435
+ ""
436
+ ].join("\n"));
437
+ const start = Date.now();
438
+ const deadline = start + timeoutMs;
439
+ const pollIntervalMs = readDomainWaitPollIntervalMs(context);
440
+ let lastStatus = null;
441
+ let current = domain;
442
+ while (true) {
443
+ emitDomainWaitStatus(context, {
444
+ hostname: normalizedHostname,
445
+ domainId: current.id,
446
+ previousStatus: lastStatus,
447
+ status: current.status,
448
+ elapsedMs: Date.now() - start
449
+ });
450
+ lastStatus = current.status;
451
+ if (current.status === "active") {
452
+ if (!context.flags.json && !context.flags.quiet) context.output.stderr.write(`\n${normalizedHostname} is live at https://${normalizedHostname}\n`);
453
+ return;
454
+ }
455
+ if (current.status === "failed") throw new CliError({
456
+ code: "DOMAIN_VERIFICATION_FAILED",
457
+ domain: "app",
458
+ summary: `Custom domain "${normalizedHostname}" failed verification`,
459
+ why: formatDomainFailureWhy(current),
460
+ fix: formatDomainFailureFix(current) ?? `Run prisma-cli app domain retry ${normalizedHostname}.`,
461
+ exitCode: 1,
462
+ nextSteps: [`prisma-cli app domain show ${normalizedHostname}`, `prisma-cli app domain retry ${normalizedHostname}`]
463
+ });
464
+ if (timeoutMs === 0 || Date.now() >= deadline) throw new CliError({
465
+ code: "DOMAIN_VERIFICATION_TIMEOUT",
466
+ domain: "app",
467
+ summary: `Timed out waiting for "${normalizedHostname}" to become active`,
468
+ why: `The domain is still "${current.status}".`,
469
+ fix: `Run prisma-cli app domain show ${normalizedHostname} to inspect the current status, or retry wait with a longer --timeout.`,
470
+ exitCode: 1,
471
+ nextSteps: [`prisma-cli app domain show ${normalizedHostname}`]
472
+ });
473
+ await sleep(Math.min(pollIntervalMs, Math.max(deadline - Date.now(), 0)));
474
+ current = await target.provider.showDomain(current.id).catch((error) => {
475
+ throw domainCommandError("wait", error, normalizedHostname);
476
+ });
477
+ }
478
+ }
482
479
  async function runAppLogs(context, appName, deploymentId, projectRef) {
483
480
  ensurePreviewAppMode(context);
484
- const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef);
481
+ const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app logs" });
485
482
  const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName);
486
483
  if (!context.flags.json && !context.flags.quiet) {
487
484
  const lines = renderCommandHeader(context.ui, {
@@ -605,7 +602,7 @@ function writeLogRecord(context, record) {
605
602
  }
606
603
  async function runAppPromote(context, deploymentId, appName, projectRef) {
607
604
  ensurePreviewAppMode(context);
608
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
605
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app promote" });
609
606
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
610
607
  const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
611
608
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
@@ -645,7 +642,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
645
642
  }
646
643
  async function runAppRollback(context, appName, deploymentId, projectRef) {
647
644
  ensurePreviewAppMode(context);
648
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
645
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app rollback" });
649
646
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
650
647
  const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
651
648
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
@@ -687,7 +684,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
687
684
  }
688
685
  async function runAppRemove(context, appName, projectRef) {
689
686
  ensurePreviewAppMode(context);
690
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
687
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app remove" });
691
688
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
692
689
  await confirmAppRemoval(context, selectedApp);
693
690
  const removedApp = await provider.removeApp(selectedApp.id).catch((error) => {
@@ -708,6 +705,299 @@ async function runAppRemove(context, appName, projectRef) {
708
705
  nextSteps: ["prisma-cli app deploy", "prisma-cli app list-deploys"]
709
706
  };
710
707
  }
708
+ async function resolveAppDomainTarget(context, options, commandName = "app domain") {
709
+ ensurePreviewAppMode(context);
710
+ const branch = resolveDomainBranch(options?.branchName);
711
+ if (toBranchKind(branch.name) !== "production") throw new CliError({
712
+ code: "BRANCH_NOT_DEPLOYABLE",
713
+ domain: "branch",
714
+ summary: "Custom domains require the production branch",
715
+ why: `Custom domains on preview branch "${branch.name}" are not supported in Public Beta.`,
716
+ fix: "Use --branch production, or attach the domain after promoting/deploying to the production branch.",
717
+ exitCode: 2,
718
+ nextSteps: ["prisma-cli app domain add <hostname> --branch production"]
719
+ });
720
+ const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
721
+ const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
722
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, options?.projectRef, {
723
+ branch,
724
+ commandName,
725
+ envProjectId
726
+ });
727
+ const selectedApp = await resolveDomainAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
728
+ explicitAppName: options?.appName,
729
+ explicitAppId: envAppId
730
+ });
731
+ await context.stateStore.setSelectedApp(projectId, {
732
+ id: selectedApp.id,
733
+ name: selectedApp.name
734
+ });
735
+ return {
736
+ provider,
737
+ app: selectedApp,
738
+ resultTarget: {
739
+ workspace: target.workspace,
740
+ project: target.project,
741
+ branch: target.branch,
742
+ app: {
743
+ id: selectedApp.id,
744
+ name: selectedApp.name
745
+ }
746
+ }
747
+ };
748
+ }
749
+ function resolveDomainBranch(explicitBranchName) {
750
+ return {
751
+ name: explicitBranchName?.trim() || "production",
752
+ annotation: explicitBranchName ? "set by --branch" : "production default"
753
+ };
754
+ }
755
+ async function resolveDomainAppSelection(context, projectId, apps, options) {
756
+ if (options.explicitAppId) {
757
+ const matched = apps.find((app) => app.id === options.explicitAppId);
758
+ if (!matched) throw usageError("Selected app does not exist in the resolved production branch", `The app "${options.explicitAppId}" from ${PRISMA_APP_ID_ENV_VAR} could not be found in resolved project "${projectId}".`, `Unset ${PRISMA_APP_ID_ENV_VAR}, pass --app <name>, or deploy the app on the production branch.`, ["prisma-cli app deploy --branch production"], "app");
759
+ return matched;
760
+ }
761
+ const selectedApp = await resolveExistingAppSelection(context, projectId, apps, options.explicitAppName);
762
+ if (selectedApp) return selectedApp;
763
+ throw usageError("Custom domain requires an existing app on the production branch", "The resolved production branch does not have an app that can receive a custom domain.", "Deploy or promote an app to production first, then rerun the domain command.", ["prisma-cli app deploy --branch production", "prisma-cli app show"], "app");
764
+ }
765
+ async function resolveDomainByHostname(provider, appId, hostname, command) {
766
+ const matched = (await provider.listDomains(appId).catch((error) => {
767
+ throw domainCommandError(command, error, hostname);
768
+ })).find((domain) => sameDomainHostname(domain.hostname, hostname));
769
+ if (matched) return matched;
770
+ throw domainNotFoundError(hostname);
771
+ }
772
+ function normalizeDomainHostname(hostname) {
773
+ const normalized = hostname.trim().replace(/\.$/, "").toLowerCase();
774
+ if (!isValidDomainHostname(normalized)) throw new CliError({
775
+ code: "DOMAIN_HOSTNAME_INVALID",
776
+ domain: "app",
777
+ summary: `Invalid custom domain "${hostname}"`,
778
+ why: "Custom domains must be valid hostnames without protocol, path, wildcard, or port.",
779
+ fix: "Pass a hostname like shop.acme.com.",
780
+ exitCode: 2,
781
+ nextSteps: ["prisma-cli app domain add shop.acme.com"]
782
+ });
783
+ return normalized;
784
+ }
785
+ function isValidDomainHostname(hostname) {
786
+ if (hostname.length < 1 || hostname.length > 253) return false;
787
+ if (hostname.includes("://") || hostname.includes("/") || hostname.includes(":") || hostname.startsWith("*.")) return false;
788
+ const labels = hostname.split(".");
789
+ if (labels.length < 2) return false;
790
+ return labels.every((label) => /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(label));
791
+ }
792
+ function sameDomainHostname(left, right) {
793
+ return left.trim().replace(/\.$/, "").toLowerCase() === right.trim().replace(/\.$/, "").toLowerCase();
794
+ }
795
+ function toAppDomainSummary(domain) {
796
+ return {
797
+ id: domain.id,
798
+ type: domain.type,
799
+ url: domain.url,
800
+ hostname: domain.hostname,
801
+ computeServiceId: domain.computeServiceId,
802
+ status: domain.status,
803
+ foundryStatus: domain.foundryStatus,
804
+ failureReason: domain.failureReason,
805
+ failureCategory: domain.failureCategory,
806
+ certExpiresAt: domain.certExpiresAt,
807
+ createdAt: domain.createdAt,
808
+ updatedAt: domain.updatedAt,
809
+ dnsRecords: toAppDomainDnsRecords(domain)
810
+ };
811
+ }
812
+ function toAppDomainDnsRecords(domain) {
813
+ return domain.dnsRecords.map((record) => ({
814
+ type: record.type,
815
+ name: record.name,
816
+ value: record.value,
817
+ ttl: record.ttl
818
+ }));
819
+ }
820
+ function buildDomainShowNextSteps(domain) {
821
+ if (domain.status === "active") return [];
822
+ if (domain.status === "failed") return [`prisma-cli app domain retry ${domain.hostname}`];
823
+ return [`prisma-cli app domain wait ${domain.hostname}`];
824
+ }
825
+ async function confirmDomainRemoval(context, target, hostname) {
826
+ if (context.flags.yes) return;
827
+ if (!canPrompt(context)) throw new CliError({
828
+ code: "CONFIRMATION_REQUIRED",
829
+ domain: "app",
830
+ summary: "Custom domain removal requires confirmation in the current mode",
831
+ why: "This command detaches a domain and cannot prompt for confirmation in the current mode.",
832
+ fix: `Pass --yes to confirm removal of "${hostname}", or rerun prisma-cli app domain remove in an interactive TTY.`,
833
+ exitCode: 1,
834
+ nextSteps: [`prisma-cli app domain remove ${hostname} --app ${target.app.name} --yes`]
835
+ });
836
+ if (!await confirmPrompt({
837
+ input: context.runtime.stdin,
838
+ output: context.output.stderr,
839
+ message: `Detach ${hostname} from App "${target.app.name}"?`,
840
+ initialValue: false
841
+ })) 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");
842
+ }
843
+ function domainCommandError(command, error, hostname) {
844
+ if (error instanceof PreviewDomainApiError) {
845
+ if (command === "add" && (error.status === 400 || error.status === 422) && isDomainDnsError(error)) return domainDnsNotConfiguredError(hostname, error);
846
+ if (command === "add" && error.status === 400) return new CliError({
847
+ code: "DOMAIN_HOSTNAME_INVALID",
848
+ domain: "app",
849
+ summary: `Invalid custom domain "${hostname}"`,
850
+ why: error.message,
851
+ fix: "Pass a valid hostname like shop.acme.com and make sure DNS can be verified.",
852
+ debug: formatDebugDetails(error),
853
+ exitCode: 2,
854
+ nextSteps: ["prisma-cli app domain add shop.acme.com"]
855
+ });
856
+ if (command === "add" && (error.status === 429 || isDomainQuotaError(error))) return new CliError({
857
+ code: "DOMAIN_QUOTA_EXCEEDED",
858
+ domain: "app",
859
+ summary: "Custom domain quota exceeded",
860
+ why: error.message,
861
+ fix: "Remove an existing custom domain before adding another one.",
862
+ debug: formatDebugDetails(error),
863
+ exitCode: 1,
864
+ nextSteps: ["prisma-cli app domain remove <hostname>"]
865
+ });
866
+ if (command === "add" && error.status === 409) return domainAlreadyRegisteredError(hostname, error);
867
+ if (command === "add" && error.status === 422) return new CliError({
868
+ code: "NO_DEPLOYMENTS",
869
+ domain: "app",
870
+ summary: "Custom domain requires a live production deployment",
871
+ why: "The selected production app does not have a promoted version that can receive a custom domain.",
872
+ fix: "Deploy the app to the production branch, then rerun the domain command.",
873
+ debug: formatDebugDetails(error),
874
+ exitCode: 1,
875
+ nextSteps: ["prisma-cli app deploy --branch production", `prisma-cli app domain add ${hostname}`]
876
+ });
877
+ if ((command === "show" || command === "remove" || command === "retry" || command === "wait") && error.status === 404) return domainNotFoundError(hostname);
878
+ if (command === "retry" && error.status === 409) return new CliError({
879
+ code: "DOMAIN_RETRY_NOT_ELIGIBLE",
880
+ domain: "app",
881
+ summary: `Custom domain "${hostname}" is not eligible for retry`,
882
+ why: error.message,
883
+ fix: "Wait for the current verification or TLS step to finish, then rerun retry if the domain fails.",
884
+ debug: formatDebugDetails(error),
885
+ exitCode: 1,
886
+ nextSteps: [`prisma-cli app domain show ${hostname}`]
887
+ });
888
+ }
889
+ return new CliError({
890
+ code: "DEPLOY_FAILED",
891
+ domain: "app",
892
+ summary: `Custom domain ${command} failed`,
893
+ why: error instanceof Error ? error.message : String(error),
894
+ fix: "Retry the command, or rerun with --trace for more detailed diagnostics.",
895
+ debug: formatDebugDetails(error),
896
+ exitCode: 1,
897
+ nextSteps: [`prisma-cli app domain show ${hostname}`]
898
+ });
899
+ }
900
+ function isDomainQuotaError(error) {
901
+ if (error.status !== 409) return false;
902
+ const text = `${error.message} ${error.hint ?? ""}`.toLowerCase();
903
+ return text.includes("quota") || text.includes("maximum") || text.includes("limit");
904
+ }
905
+ function domainAlreadyRegisteredError(hostname, error) {
906
+ return new CliError({
907
+ code: "DOMAIN_ALREADY_REGISTERED",
908
+ domain: "app",
909
+ summary: `Custom domain "${hostname}" is already registered`,
910
+ why: error.hint ?? error.message,
911
+ fix: "Select the app that owns this hostname and remove it there, or contact support if you cannot access it.",
912
+ debug: formatDebugDetails(error),
913
+ exitCode: 1,
914
+ nextSteps: [`Select the owning app and remove ${hostname} there.`, "Contact Prisma support if you cannot access the owning app."]
915
+ });
916
+ }
917
+ function isDomainDnsError(error) {
918
+ const text = `${error.message} ${error.hint ?? ""}`.toLowerCase();
919
+ return text.includes("dns is not configured") || text.includes("dns verification failed") || text.includes("no cname") || text.includes("cname record") || text.includes("no a/aaaa") || /\bcname(?:s)?\s+to\b/.test(text);
920
+ }
921
+ function domainDnsNotConfiguredError(hostname, error) {
922
+ const target = extractDomainDnsTarget(error);
923
+ const record = target ? `CNAME ${hostname} -> ${target}` : null;
924
+ return new CliError({
925
+ code: "DOMAIN_DNS_NOT_CONFIGURED",
926
+ domain: "app",
927
+ summary: `DNS is not configured for "${hostname}"`,
928
+ why: error.hint ?? error.message,
929
+ fix: record ? `Add ${record} at your DNS provider, then rerun the domain command.` : "The platform did not return the required DNS target. Re-run with --trace for the underlying API response details.",
930
+ debug: formatDebugDetails(error),
931
+ exitCode: 1,
932
+ nextSteps: record ? [`add ${record}`, `prisma-cli app domain add ${hostname}`] : [`prisma-cli app domain add ${hostname} --trace`]
933
+ });
934
+ }
935
+ function extractDomainDnsTarget(error) {
936
+ const text = `${error.hint ?? ""} ${error.message}`;
937
+ return /\b((?:[a-z0-9-]+\.)+prisma\.build)\b/i.exec(text)?.[1]?.toLowerCase() ?? null;
938
+ }
939
+ function domainNotFoundError(hostname) {
940
+ return new CliError({
941
+ code: "DOMAIN_NOT_FOUND",
942
+ domain: "app",
943
+ summary: `Custom domain "${hostname}" not found`,
944
+ why: "The hostname is not attached to the selected app.",
945
+ fix: "Check the hostname and selected app, or add the domain first.",
946
+ exitCode: 1,
947
+ nextSteps: [`prisma-cli app domain add ${hostname}`]
948
+ });
949
+ }
950
+ function formatDomainFailureWhy(domain) {
951
+ if (domain.failureReason) return domain.failureCategory ? `${domain.failureCategory}: ${domain.failureReason}` : domain.failureReason;
952
+ return "The platform reported a terminal failed state for this custom domain.";
953
+ }
954
+ function parseDomainWaitTimeout(value) {
955
+ if (!value) return 900 * 1e3;
956
+ const trimmed = value.trim().toLowerCase();
957
+ if (trimmed === "0") return 0;
958
+ const match = /^(\d+)(ms|s|m|h)$/.exec(trimmed);
959
+ if (!match) throw usageError(`Invalid timeout "${value}"`, "Timeout must be a duration such as 0, 30s, 15m, or 1h.", "Pass --timeout 15m, or --timeout 0 to poll once.", ["prisma-cli app domain wait shop.acme.com --timeout 15m"], "app");
960
+ const amount = Number.parseInt(match[1], 10);
961
+ const unit = match[2];
962
+ return amount * (unit === "h" ? 3600 * 1e3 : unit === "m" ? 60 * 1e3 : unit === "s" ? 1e3 : 1);
963
+ }
964
+ function readDomainWaitPollIntervalMs(context) {
965
+ const raw = context.runtime.env.PRISMA_CLI_DOMAIN_WAIT_POLL_MS;
966
+ if (!raw) return 5e3;
967
+ const parsed = Number.parseInt(raw, 10);
968
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : 5e3;
969
+ }
970
+ function emitDomainWaitStatus(context, event) {
971
+ if (context.flags.json) {
972
+ writeJsonEvent(context.output, {
973
+ type: "status",
974
+ command: "app.domain.wait",
975
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
976
+ data: {
977
+ hostname: event.hostname,
978
+ domainId: event.domainId,
979
+ previousStatus: event.previousStatus,
980
+ status: event.status,
981
+ elapsedMs: event.elapsedMs
982
+ }
983
+ });
984
+ return;
985
+ }
986
+ if (context.flags.quiet) return;
987
+ if (event.previousStatus === event.status) return;
988
+ const transition = event.previousStatus ? `${event.previousStatus} -> ${event.status}` : event.status;
989
+ context.output.stderr.write(` ${transition} (${formatElapsed(event.elapsedMs)})\n`);
990
+ }
991
+ function formatElapsed(milliseconds) {
992
+ const seconds = Math.max(Math.floor(milliseconds / 1e3), 0);
993
+ const minutes = Math.floor(seconds / 60);
994
+ const remainingSeconds = seconds % 60;
995
+ return `${minutes}:${String(remainingSeconds).padStart(2, "0")}`;
996
+ }
997
+ async function sleep(milliseconds) {
998
+ if (milliseconds <= 0) return;
999
+ await new Promise((resolve) => setTimeout(resolve, milliseconds));
1000
+ }
711
1001
  async function resolveDeployAppSelection(context, projectId, apps, options) {
712
1002
  if (options.explicitAppName) {
713
1003
  const matches = findAppsByName(apps, options.explicitAppName);
@@ -927,9 +1217,9 @@ async function listApps(context, provider, projectId, branchName) {
927
1217
  domain: "project",
928
1218
  summary: "Project not found",
929
1219
  why: `The resolved project "${projectId}" does not exist in the authenticated workspace or is no longer accessible.`,
930
- 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.",
931
1221
  exitCode: 1,
932
- 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>"]
933
1223
  });
934
1224
  throw deployFailedError("Failed to list apps", error, ["prisma-cli project show"]);
935
1225
  });
@@ -964,7 +1254,7 @@ function createPreviewLogAuthOptions(env) {
964
1254
  }
965
1255
  async function requireProviderAndProjectContext(context, explicitProject, options) {
966
1256
  const { client, provider } = await requirePreviewAppProviderWithClient(context);
967
- const target = await resolveProjectContext(context, client, provider, explicitProject, options);
1257
+ const target = await resolveProjectContext(context, client, explicitProject, options);
968
1258
  return {
969
1259
  client,
970
1260
  provider,
@@ -982,31 +1272,16 @@ async function requireProviderAndDeployProjectContext(context, explicitProject,
982
1272
  projectId: target.project.id
983
1273
  };
984
1274
  }
985
- async function resolveProjectContext(context, client, provider, explicitProject, options) {
1275
+ async function resolveProjectContext(context, client, explicitProject, options) {
986
1276
  const authState = await requireAuthenticatedAuthState(context);
987
1277
  if (!authState.workspace) throw workspaceRequiredError();
988
1278
  const resolved = await resolveProjectTarget({
989
1279
  context,
990
1280
  workspace: authState.workspace,
991
1281
  explicitProject,
1282
+ envProjectId: options?.envProjectId,
992
1283
  listProjects: () => listRealWorkspaceProjects(client, authState.workspace),
993
- createProject: options?.allowCreate ? async (name) => {
994
- const project = await provider.createProject({ name }).catch((error) => {
995
- throw createProjectOnFirstDeployError({
996
- error,
997
- inferredName: name,
998
- workspaceName: authState.workspace.name
999
- });
1000
- });
1001
- return {
1002
- id: project.id,
1003
- name: project.name,
1004
- workspace: authState.workspace
1005
- };
1006
- } : void 0,
1007
- allowCreate: options?.allowCreate,
1008
- prompt: createSelectPromptPort(context),
1009
- remember: true
1284
+ commandName: options?.commandName
1010
1285
  });
1011
1286
  const branch = options?.branch ?? await resolveDeployBranch(context, void 0);
1012
1287
  return {
@@ -1022,30 +1297,30 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1022
1297
  if (!workspace) throw workspaceRequiredError();
1023
1298
  const branch = options.branch ?? await resolveDeployBranch(context, void 0);
1024
1299
  const projects = await listRealWorkspaceProjects(client, workspace);
1025
- const createProject = options.allowCreate ? async (name) => {
1026
- const project = await provider.createProject({ name }).catch((error) => {
1027
- throw createProjectOnFirstDeployError({
1028
- error,
1029
- inferredName: name,
1030
- workspaceName: workspace.name
1031
- });
1032
- });
1033
- return {
1034
- id: project.id,
1035
- name: project.name,
1036
- workspace
1037
- };
1038
- } : void 0;
1039
- if (explicitProject) return withDeployBranch(await resolveProjectTarget({
1040
- context,
1300
+ if (explicitProject) return withDeployBranch({
1041
1301
  workspace,
1042
- explicitProject,
1043
- listProjects: async () => projects,
1044
- createProject,
1045
- allowCreate: options.allowCreate,
1046
- prompt: createSelectPromptPort(context),
1047
- remember: true
1048
- }), 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
+ }
1049
1324
  if (options.envProjectId) {
1050
1325
  const project = projects.find((candidate) => candidate.id === options.envProjectId);
1051
1326
  if (!project) throw projectNotFoundError(options.envProjectId, workspace);
@@ -1074,15 +1349,58 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1074
1349
  }
1075
1350
  }, branch);
1076
1351
  }
1077
- 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({
1078
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 {
1079
1377
  workspace,
1080
- listProjects: async () => projects,
1081
- createProject,
1082
- allowCreate: options.allowCreate,
1083
- prompt: createSelectPromptPort(context),
1084
- remember: true
1085
- }), 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
+ };
1086
1404
  }
1087
1405
  function withDeployBranch(target, branch) {
1088
1406
  return {
@@ -1093,15 +1411,22 @@ function withDeployBranch(target, branch) {
1093
1411
  }
1094
1412
  };
1095
1413
  }
1096
- function toProjectSummary(project) {
1097
- return {
1098
- id: project.id,
1099
- name: project.name
1100
- };
1101
- }
1102
1414
  function toBranchKind(name) {
1103
1415
  return name === "production" || name === "main" ? "production" : "preview";
1104
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
+ }
1105
1430
  async function resolveDeployBranch(context, explicitBranchName) {
1106
1431
  if (explicitBranchName) return {
1107
1432
  name: explicitBranchName,
@@ -1142,15 +1467,12 @@ async function resolveGitHeadPath(gitPath) {
1142
1467
  }
1143
1468
  async function resolveDeployFramework(context, options) {
1144
1469
  if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, "set by --framework");
1145
- if (options.explicitBuildType) {
1146
- const buildType = normalizeBuildType(options.requestedBuildType);
1147
- if (buildType !== "auto") return {
1148
- key: buildType,
1149
- buildType,
1150
- displayName: formatBuildTypeName(buildType),
1151
- annotation: "set by --build-type"
1152
- };
1153
- }
1470
+ if (options.entrypoint) return {
1471
+ key: "bun",
1472
+ buildType: "bun",
1473
+ displayName: "Bun",
1474
+ annotation: "set by --entry"
1475
+ };
1154
1476
  const detected = await detectDeployFramework(context.runtime.cwd);
1155
1477
  if (detected) return detected;
1156
1478
  throw frameworkNotDetectedError(context.runtime.cwd);
@@ -1165,6 +1487,24 @@ function resolveDeployRuntime(requestedHttpPort, framework) {
1165
1487
  annotation: `${framework.displayName} default`
1166
1488
  };
1167
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
+ }
1168
1508
  async function detectDeployFramework(cwd) {
1169
1509
  const packageJson = await readBunPackageJson(cwd);
1170
1510
  const nextConfig = await detectNextConfig(cwd);
@@ -1180,7 +1520,7 @@ async function detectDeployFramework(cwd) {
1180
1520
  displayName: "Hono",
1181
1521
  annotation: "detected from package.json"
1182
1522
  };
1183
- if (hasPackageDependency(packageJson, "@tanstack/start")) return {
1523
+ if (hasAnyPackageDependency(packageJson, TANSTACK_START_PACKAGES)) return {
1184
1524
  key: "tanstack-start",
1185
1525
  buildType: "tanstack-start",
1186
1526
  displayName: "TanStack Start",
@@ -1193,7 +1533,8 @@ async function detectNextConfig(cwd) {
1193
1533
  "next.config.js",
1194
1534
  "next.config.mjs",
1195
1535
  "next.config.cjs",
1196
- "next.config.ts"
1536
+ "next.config.ts",
1537
+ "next.config.mts"
1197
1538
  ]) {
1198
1539
  const filePath = path.join(cwd, candidate);
1199
1540
  try {
@@ -1214,6 +1555,9 @@ async function detectNextConfig(cwd) {
1214
1555
  function hasPackageDependency(packageJson, dependencyName) {
1215
1556
  return hasDependency(packageJson?.dependencies, dependencyName) || hasDependency(packageJson?.devDependencies, dependencyName);
1216
1557
  }
1558
+ function hasAnyPackageDependency(packageJson, dependencyNames) {
1559
+ return dependencyNames.some((dependencyName) => hasPackageDependency(packageJson, dependencyName));
1560
+ }
1217
1561
  function hasDependency(dependencies, dependencyName) {
1218
1562
  return Boolean(dependencies && typeof dependencies === "object" && dependencyName in dependencies);
1219
1563
  }
@@ -1233,9 +1577,16 @@ function frameworkFromUserFacingValue(value, annotation) {
1233
1577
  displayName: "Hono",
1234
1578
  annotation
1235
1579
  };
1580
+ case "bun": return {
1581
+ key: "bun",
1582
+ buildType: "bun",
1583
+ displayName: "Bun",
1584
+ annotation
1585
+ };
1236
1586
  case "tanstack":
1237
1587
  case "tanstack-start":
1238
- case "@tanstack/start": return {
1588
+ case "@tanstack/react-start":
1589
+ case "@tanstack/solid-start": return {
1239
1590
  key: "tanstack-start",
1240
1591
  buildType: "tanstack-start",
1241
1592
  displayName: "TanStack Start",
@@ -1245,75 +1596,36 @@ function frameworkFromUserFacingValue(value, annotation) {
1245
1596
  }
1246
1597
  }
1247
1598
  function frameworkNotDetectedError(cwd, requestedFramework) {
1248
- const supported = "Next.js, Hono, TanStack Start";
1599
+ const supported = "Next.js, Hono, TanStack Start, Bun";
1249
1600
  const directory = cwd ? ` in ${formatDeployDirectory(cwd)}` : "";
1250
1601
  return new CliError({
1251
1602
  code: "FRAMEWORK_NOT_DETECTED",
1252
1603
  domain: "app",
1253
1604
  summary: requestedFramework ? `Unsupported framework "${requestedFramework}"` : `Cannot detect a supported framework${directory}`,
1254
1605
  why: `Supported Beta frameworks: ${supported}.`,
1255
- 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.",
1256
1607
  exitCode: 2,
1257
1608
  nextSteps: [
1258
1609
  "prisma-cli app deploy --framework nextjs",
1259
1610
  "prisma-cli app deploy --framework hono",
1260
- "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"
1261
1614
  ]
1262
1615
  });
1263
1616
  }
1264
1617
  async function maybeRenderDeploySetupBlock(context, details) {
1265
1618
  if (context.flags.json || context.flags.quiet) return;
1266
1619
  const directory = formatDeployDirectory(context.runtime.cwd);
1267
- if (!details.firstDeploy) {
1268
- context.output.stderr.write(`Deploying ${directory} to ${details.projectName} / ${details.branchName} / ${details.appName}\n\n`);
1269
- return;
1270
- }
1271
- const title = `Setting up your local directory ${formatLocalDirectory(context.runtime.cwd, context.runtime.env)}`;
1272
- const rows = details.firstDeploy ? [
1273
- {
1274
- label: "Workspace",
1275
- value: details.workspaceName
1276
- },
1277
- {
1278
- label: "Project",
1279
- value: details.projectName,
1280
- origin: details.projectAnnotation
1281
- },
1282
- {
1283
- label: "Branch",
1284
- value: details.branchName,
1285
- origin: details.branchAnnotation
1286
- },
1287
- {
1288
- label: "App",
1289
- value: details.appName,
1290
- origin: details.appAnnotation
1291
- },
1292
- {
1293
- label: "Framework",
1294
- value: details.framework.displayName,
1295
- origin: details.framework.annotation
1296
- },
1297
- {
1298
- label: "Runtime",
1299
- value: `HTTP ${details.runtime.port}`,
1300
- origin: details.runtime.annotation
1301
- }
1302
- ] : [];
1303
- const lines = [
1304
- title,
1305
- "",
1306
- ...renderDeployOutputRows(context.ui, rows),
1307
- ""
1308
- ];
1309
- 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`);
1310
1622
  }
1311
- function maybeRenderLocalPinBound(context, projectName) {
1623
+ function maybeRenderProjectLinked(context, directory, projectName, localPinPath) {
1312
1624
  if (context.flags.json || context.flags.quiet) return;
1313
- 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`);
1314
1626
  }
1315
1627
  async function maybeCustomizeDeploySettings(context, options) {
1316
- 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 {
1317
1629
  framework: options.framework,
1318
1630
  runtime: options.runtime
1319
1631
  };
@@ -1365,24 +1677,12 @@ async function maybeCustomizeDeploySettings(context, options) {
1365
1677
  runtime
1366
1678
  };
1367
1679
  }
1368
- function annotationForProjectResolution(resolution) {
1369
- switch (resolution.projectSource) {
1370
- case "explicit": return "set by --project";
1371
- case "env": return `from ${PRISMA_PROJECT_ID_ENV_VAR}`;
1372
- case "local-pin": return "from local pin";
1373
- case "created": return resolution.targetNameSource === "directory-name" ? "created from directory name" : "created from package.json";
1374
- case "package-name":
1375
- case "directory-name": return "linked to existing project";
1376
- case "platform-mapping":
1377
- case "remembered-local": return "linked to existing project";
1378
- case "prompt": return "selected by you";
1379
- }
1380
- }
1381
1680
  function frameworkDisplayName(framework) {
1382
1681
  switch (framework) {
1383
1682
  case "nextjs": return "Next.js";
1384
1683
  case "hono": return "Hono";
1385
1684
  case "tanstack-start": return "TanStack Start";
1685
+ case "bun": return "Bun";
1386
1686
  }
1387
1687
  }
1388
1688
  function validateDeployHttpPortText(value) {
@@ -1398,15 +1698,6 @@ function formatDeployDirectory(cwd) {
1398
1698
  const basename = path.basename(cwd);
1399
1699
  return basename ? `./${basename}` : ".";
1400
1700
  }
1401
- function formatLocalDirectory(cwd, env) {
1402
- const resolved = path.resolve(cwd);
1403
- const home = env.HOME ? path.resolve(env.HOME) : null;
1404
- if (home && (resolved === home || resolved.startsWith(`${home}${path.sep}`))) {
1405
- const relative = path.relative(home, resolved);
1406
- return relative ? `~/${relative}` : "~";
1407
- }
1408
- return resolved;
1409
- }
1410
1701
  async function readCurrentWorkspaceId(context) {
1411
1702
  const state = await context.stateStore.read();
1412
1703
  if (state.auth?.workspaceId) return state.auth.workspaceId;
@@ -1426,7 +1717,10 @@ function getBuildTypeExamples(commandName) {
1426
1717
  });
1427
1718
  }
1428
1719
  function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
1429
- 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");
1720
+ if (buildType !== "auto" && buildType !== "bun" && entrypoint) {
1721
+ 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");
1722
+ 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");
1723
+ }
1430
1724
  }
1431
1725
  async function requireLocalBuildType(context, buildType, commandName) {
1432
1726
  const resolvedBuildType = await resolveLocalBuildType(context.runtime.cwd, buildType);
@@ -1467,24 +1761,41 @@ function deployFailedError(summary, error, nextSteps) {
1467
1761
  function appDeployFailedError(error, progress) {
1468
1762
  const why = error instanceof Error ? error.message : String(error);
1469
1763
  const debug = formatDebugDetails(error);
1470
- if (progress.buildStarted && !progress.buildCompleted) return new CliError({
1471
- code: "BUILD_FAILED",
1472
- domain: "app",
1473
- summary: "Build failed locally.",
1474
- why,
1475
- fix: "Inspect the build output above, fix the error, and redeploy.",
1476
- debug,
1477
- meta: { phase: "build" },
1478
- humanLines: [
1479
- "Build failed locally.",
1480
- "",
1481
- `✗ Built ${why}`,
1482
- "",
1483
- "Fix: Inspect the build output above, fix the error, and redeploy."
1484
- ],
1485
- exitCode: 1,
1486
- nextSteps: []
1487
- });
1764
+ if (progress.buildStarted && !progress.buildCompleted) {
1765
+ const standaloneOutputFailure = isNextStandaloneOutputFailure(why);
1766
+ const fix = standaloneOutputFailure ? "Add output: \"standalone\" to next.config.*, then rerun deploy." : "Inspect the build output above, fix the error, and redeploy.";
1767
+ const nextSteps = standaloneOutputFailure ? ["Add output: \"standalone\" to next.config.*, then rerun prisma-cli app deploy"] : [];
1768
+ const nextActions = standaloneOutputFailure ? [{
1769
+ kind: "edit-file",
1770
+ journey: "deploy-app",
1771
+ label: "Add Next.js standalone output",
1772
+ reason: "Prisma Compute needs Next.js standalone output to build a deployable server artifact."
1773
+ }, {
1774
+ kind: "run-command",
1775
+ journey: "deploy-app",
1776
+ label: "Rerun deploy",
1777
+ command: "prisma-cli app deploy"
1778
+ }] : [];
1779
+ return new CliError({
1780
+ code: "BUILD_FAILED",
1781
+ domain: "app",
1782
+ summary: "Build failed locally.",
1783
+ why,
1784
+ fix,
1785
+ debug,
1786
+ meta: { phase: "build" },
1787
+ humanLines: [
1788
+ "Build failed locally.",
1789
+ "",
1790
+ `✗ Built ${why}`,
1791
+ "",
1792
+ `Fix: ${fix}`
1793
+ ],
1794
+ exitCode: 1,
1795
+ nextSteps,
1796
+ nextActions
1797
+ });
1798
+ }
1488
1799
  if (!progress.buildStarted) return deployFailedError("App deploy failed", error, ["prisma-cli app deploy"]);
1489
1800
  const phaseHeadline = progress.containerLive ? "The deployment started, but the app is not ready yet." : "Deploy failed after the build completed.";
1490
1801
  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."];
@@ -1531,61 +1842,52 @@ function localResolutionPinStaleError() {
1531
1842
  domain: "project",
1532
1843
  summary: "Local project binding is stale",
1533
1844
  why: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
1534
- fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} and re-run to re-bootstrap.`,
1845
+ fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}, then choose a Project explicitly.`,
1535
1846
  meta: { pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH },
1536
1847
  exitCode: 1,
1537
- nextSteps: ["prisma-cli app deploy"]
1848
+ nextSteps: [
1849
+ "prisma-cli project list",
1850
+ "prisma-cli project link <id-or-name>",
1851
+ "prisma-cli app deploy --project <id-or-name>"
1852
+ ]
1538
1853
  });
1539
1854
  }
1540
1855
  function readDeployEnvOverride(context, name) {
1541
1856
  const value = context.runtime.env[name]?.trim();
1542
1857
  return value ? value : void 0;
1543
1858
  }
1544
- /**
1545
- * `app deploy` falls into "create a new project on first deploy" when no
1546
- * existing project matches the package.json name (or the cwd basename as a
1547
- * fallback). When the create call fails the user often doesn't realise the
1548
- * CLI was attempting to create a project at all — they thought the deploy
1549
- * would find an existing project. Surface that context, and recommend the
1550
- * explicit `--project` flag as the unambiguous way out.
1551
- */
1552
- function createProjectOnFirstDeployError(options) {
1553
- const { error, inferredName, workspaceName } = options;
1554
- const status = extractHttpStatus(error);
1555
- const errorMessage = error instanceof Error ? error.message : String(error);
1556
- const inferredContext = `No existing project matched the package.json name \`${inferredName}\`, so the CLI attempted to create one.`;
1557
- const nextSteps = ["prisma-cli project list", "prisma-cli app deploy --project <id-or-name>"];
1558
- if (status === 401 || status === 403) return new CliError({
1559
- code: "AUTH_FORBIDDEN",
1560
- domain: "auth",
1561
- summary: "Could not create a new project for this deploy",
1562
- why: `${inferredContext} The platform rejected the create (HTTP ${status}).`,
1563
- fix: `Pass --project <id-or-name> to deploy into an existing project, or grant the service token project-create permission on workspace \`${workspaceName}\`.`,
1564
- debug: formatDebugDetails(error),
1565
- exitCode: 1,
1566
- nextSteps
1567
- });
1859
+ function projectSetupRequiredError(projects, suggestedName) {
1860
+ const createCommand = `prisma-cli app deploy --create-project ${formatCommandArgument(suggestedName.name)}`;
1568
1861
  return new CliError({
1569
- code: "DEPLOY_FAILED",
1570
- domain: "app",
1571
- summary: "Could not create a new project for this deploy",
1572
- why: `${inferredContext} ${errorMessage}`.trim(),
1573
- fix: "Pass --project <id-or-name> to deploy into an existing project, or retry after addressing the platform error above.",
1574
- debug: formatDebugDetails(error),
1862
+ code: "PROJECT_SETUP_REQUIRED",
1863
+ domain: "project",
1864
+ summary: "Choose a Project before deploying this directory",
1865
+ why: "This directory is not linked to a Prisma Project, and deploy will not choose or create one implicitly.",
1866
+ fix: "Choose an existing Project with --project, create one with --create-project, or rerun interactively to pick from the setup list.",
1867
+ meta: {
1868
+ candidates: sortProjects(projects).map((project) => ({
1869
+ id: project.id,
1870
+ name: project.name
1871
+ })),
1872
+ suggestedProjectName: suggestedName.name,
1873
+ suggestedProjectNameSource: suggestedName.source,
1874
+ recoveryCommands: ["prisma-cli app deploy --project <id-or-name>", createCommand]
1875
+ },
1575
1876
  exitCode: 1,
1576
- nextSteps
1877
+ nextSteps: [
1878
+ "prisma-cli project list",
1879
+ "prisma-cli app deploy --project <id-or-name>",
1880
+ createCommand
1881
+ ],
1882
+ nextActions: buildProjectSetupNextActions({
1883
+ commandName: "app deploy",
1884
+ createCommand,
1885
+ 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."
1886
+ })
1577
1887
  });
1578
1888
  }
1579
- function extractHttpStatus(error) {
1580
- if (!error || typeof error !== "object") return null;
1581
- const candidate = error;
1582
- if (typeof candidate.statusCode === "number") return candidate.statusCode;
1583
- if (typeof candidate.status === "number") return candidate.status;
1584
- if (typeof candidate.message === "string") {
1585
- const match = /\(HTTP (\d{3})\)/.exec(candidate.message);
1586
- if (match) return Number.parseInt(match[1], 10);
1587
- }
1588
- return null;
1889
+ function isNextStandaloneOutputFailure(message) {
1890
+ return /next\.?js/i.test(message) && /standalone output/i.test(message);
1589
1891
  }
1590
1892
  function noDeploymentsError(summary, why) {
1591
1893
  return new CliError({
@@ -1671,21 +1973,5 @@ function sortApps(apps) {
1671
1973
  function toOptionalEnvVars(envVars) {
1672
1974
  return Object.keys(envVars).length > 0 ? envVars : void 0;
1673
1975
  }
1674
- /**
1675
- * Emits a deprecation banner to stderr when the legacy single-shot
1676
- * env-var commands are invoked. The banner is suppressed in --json
1677
- * mode so machine consumers keep their JSON channel clean; --json
1678
- * users discover the deprecation via release notes and the new
1679
- * `prisma-cli project env` namespace's output anyway.
1680
- *
1681
- * Removal of these legacy commands is deliberately scoped out of the
1682
- * Public Beta — see the Compute Beta plan, sub-track 3B.1, where the
1683
- * Terminal team picks an explicit removal milestone.
1684
- */
1685
- function emitLegacyEnvDeprecationWarning(context, legacyCommand, replacement) {
1686
- if (context.flags.json) return;
1687
- const message = `[deprecation] \`prisma-cli ${legacyCommand}\` is deprecated. Use \`prisma-cli ${replacement}\` instead.`;
1688
- context.runtime.stderr.write(`${message}\n`);
1689
- }
1690
1976
  //#endregion
1691
- export { runAppBuild, runAppDeploy, runAppListDeploys, runAppListEnv, runAppLogs, runAppOpen, runAppPromote, runAppRemove, runAppRollback, runAppRun, runAppShow, runAppShowDeploy, runAppUpdateEnv };
1977
+ export { runAppBuild, runAppDeploy, runAppDomainAdd, runAppDomainRemove, runAppDomainRetry, runAppDomainShow, runAppDomainWait, runAppListDeploys, runAppLogs, runAppOpen, runAppPromote, runAppRemove, runAppRollback, runAppRun, runAppShow, runAppShowDeploy };