@prisma/cli 8.0.0-rc.1 → 8.0.0-rc.10-dev.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +25 -29
  2. package/dist/cli.js +14551 -16
  3. package/dist/{v8/sender.js → sender.js} +1 -1
  4. package/package.json +17 -18
  5. package/dist/adapters/git.js +0 -54
  6. package/dist/adapters/local-state.js +0 -155
  7. package/dist/auth/client.js +0 -30
  8. package/dist/auth/errors.js +0 -50
  9. package/dist/auth/guard.js +0 -39
  10. package/dist/auth/login.js +0 -471
  11. package/dist/auth/operations.js +0 -208
  12. package/dist/auth/recipient.js +0 -42
  13. package/dist/auth/token-storage.js +0 -439
  14. package/dist/auth/workspaces.js +0 -171
  15. package/dist/cli-command.js +0 -14
  16. package/dist/cli-name.js +0 -12
  17. package/dist/cli2.js +0 -136
  18. package/dist/command-arguments.js +0 -12
  19. package/dist/commands/agent/index.js +0 -60
  20. package/dist/commands/app/index.js +0 -350
  21. package/dist/commands/auth/index.js +0 -95
  22. package/dist/commands/branch/index.js +0 -27
  23. package/dist/commands/bucket/index.js +0 -123
  24. package/dist/commands/build/index.js +0 -29
  25. package/dist/commands/database/index.js +0 -249
  26. package/dist/commands/env.js +0 -99
  27. package/dist/commands/feedback/index.js +0 -20
  28. package/dist/commands/git/index.js +0 -36
  29. package/dist/commands/init/index.js +0 -33
  30. package/dist/commands/project/index.js +0 -118
  31. package/dist/commands/version/index.js +0 -18
  32. package/dist/controllers/agent-setup.js +0 -52
  33. package/dist/controllers/agent.js +0 -228
  34. package/dist/controllers/app-env-api.js +0 -55
  35. package/dist/controllers/app-env-file.js +0 -181
  36. package/dist/controllers/app-env.js +0 -502
  37. package/dist/controllers/app.js +0 -2508
  38. package/dist/controllers/auth.js +0 -124
  39. package/dist/controllers/branch.js +0 -92
  40. package/dist/controllers/bucket.js +0 -184
  41. package/dist/controllers/build.js +0 -88
  42. package/dist/controllers/database.js +0 -463
  43. package/dist/controllers/feedback.js +0 -90
  44. package/dist/controllers/init.js +0 -814
  45. package/dist/controllers/project.js +0 -892
  46. package/dist/controllers/select-prompt-port.js +0 -13
  47. package/dist/controllers/version.js +0 -12
  48. package/dist/errors.js +0 -102
  49. package/dist/lib/agent/cli-command.js +0 -20
  50. package/dist/lib/agent/constants.js +0 -12
  51. package/dist/lib/agent/package-manager.js +0 -99
  52. package/dist/lib/agent/setup-status.js +0 -83
  53. package/dist/lib/app/app-provider.js +0 -617
  54. package/dist/lib/app/branch-database-api.js +0 -102
  55. package/dist/lib/app/branch-database-deploy.js +0 -353
  56. package/dist/lib/app/branch-database.js +0 -234
  57. package/dist/lib/app/build-settings.js +0 -98
  58. package/dist/lib/app/build.js +0 -82
  59. package/dist/lib/app/bun-project.js +0 -45
  60. package/dist/lib/app/compute-config.js +0 -145
  61. package/dist/lib/app/deploy-output.js +0 -24
  62. package/dist/lib/app/deploy-plan.js +0 -59
  63. package/dist/lib/app/deploy-progress.js +0 -100
  64. package/dist/lib/app/domain-guidance.js +0 -14
  65. package/dist/lib/app/env-config.js +0 -67
  66. package/dist/lib/app/env-file.js +0 -82
  67. package/dist/lib/app/env-vars.js +0 -50
  68. package/dist/lib/app/local-dev.js +0 -109
  69. package/dist/lib/app/production-deploy-gate.js +0 -162
  70. package/dist/lib/app/read-branch.js +0 -30
  71. package/dist/lib/bucket/provider.js +0 -139
  72. package/dist/lib/database/provider.js +0 -385
  73. package/dist/lib/diagnostics.js +0 -16
  74. package/dist/lib/feedback.js +0 -15
  75. package/dist/lib/fs/home-path.js +0 -24
  76. package/dist/lib/git/local-branch.js +0 -53
  77. package/dist/lib/git/local-status.js +0 -57
  78. package/dist/lib/project/interactive-setup.js +0 -57
  79. package/dist/lib/project/local-pin.js +0 -200
  80. package/dist/lib/project/provider.js +0 -93
  81. package/dist/lib/project/resolution.js +0 -389
  82. package/dist/lib/project/setup.js +0 -138
  83. package/dist/lib/version.js +0 -56
  84. package/dist/lib/workspace-id.js +0 -18
  85. package/dist/output/patterns.js +0 -90
  86. package/dist/presenters/agent.js +0 -74
  87. package/dist/presenters/app-env.js +0 -265
  88. package/dist/presenters/app.js +0 -672
  89. package/dist/presenters/auth.js +0 -184
  90. package/dist/presenters/branch.js +0 -46
  91. package/dist/presenters/bucket.js +0 -174
  92. package/dist/presenters/database.js +0 -462
  93. package/dist/presenters/feedback.js +0 -26
  94. package/dist/presenters/init.js +0 -30
  95. package/dist/presenters/project.js +0 -268
  96. package/dist/presenters/verbose-context.js +0 -64
  97. package/dist/presenters/version.js +0 -29
  98. package/dist/shell/cli-command.js +0 -2
  99. package/dist/shell/command-meta.js +0 -923
  100. package/dist/shell/command-runner.js +0 -119
  101. package/dist/shell/diagnostics-output.js +0 -57
  102. package/dist/shell/global-flags.js +0 -37
  103. package/dist/shell/help.js +0 -90
  104. package/dist/shell/output.js +0 -144
  105. package/dist/shell/prompt.js +0 -48
  106. package/dist/shell/runtime.js +0 -38
  107. package/dist/shell/ui.js +0 -116
  108. package/dist/state-dir.js +0 -12
  109. package/dist/update-check.js +0 -248
  110. package/dist/v8/cli.js +0 -14699
@@ -1,2508 +0,0 @@
1
- import { formatCommandArgument } from "../command-arguments.js";
2
- import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../errors.js";
3
- import { renderCommandHeader } from "../shell/ui.js";
4
- import { getApiBaseUrl } from "../auth/client.js";
5
- import { sameWorkspaceId } from "../lib/workspace-id.js";
6
- import { FileTokenStorage } from "../auth/token-storage.js";
7
- import { authenticatedManagementApiClient } from "../auth/guard.js";
8
- import { readAuthState } from "../auth/operations.js";
9
- import { canPrompt } from "../shell/runtime.js";
10
- import { writeJsonEvent } from "../shell/output.js";
11
- import { APP_BUILD_TYPES, APP_BUILD_TYPE_LABELS, RESOLVED_APP_BUILD_TYPES, executeAppBuild } from "../lib/app/build.js";
12
- import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
13
- import { DomainApiError, createAppProvider } from "../lib/app/app-provider.js";
14
- import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
15
- import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
16
- import { PRISMA_APP_CONFIG_FILENAME, detectLegacyBuildSettings, resolveConfiguredAppBuildSettings, resolveInferredAppBuildSettings } from "../lib/app/build-settings.js";
17
- import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
18
- import { computeConfigErrorToCliError, computeFrameworkToBuildType, loadComputeConfig as loadComputeConfig$1, mergeComputeDeployInputs, mergeComputeLocalInputs } from "../lib/app/compute-config.js";
19
- import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
20
- import { describeDeployAllFailure, perAppInputsForDeployAll, planAppDeploy } from "../lib/app/deploy-plan.js";
21
- import { createDeployProgress, createDeployProgressState, createPromoteProgress } from "../lib/app/deploy-progress.js";
22
- import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
23
- import { DEFAULT_LOCAL_DEV_PORT, runLocalApp } from "../lib/app/local-dev.js";
24
- import { enforceProductionDeployGate } from "../lib/app/production-deploy-gate.js";
25
- import { resolveReadBranch } from "../lib/app/read-branch.js";
26
- import { readLocalGitBranch } from "../lib/git/local-branch.js";
27
- import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
28
- import { buildProjectSetupNextActions, inferTargetName, localProjectWorkspaceMismatchError, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
29
- import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
30
- import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
31
- import { maybePromptForAgentSetup } from "./agent-setup.js";
32
- import { createSelectPromptPort } from "./select-prompt-port.js";
33
- import { requireAuthenticatedAuthState } from "./auth.js";
34
- import { listRealWorkspaceProjects } from "./project.js";
35
- import path from "node:path";
36
- import { access } from "node:fs/promises";
37
- import { COMPUTE_CONFIG_FILENAME, COMPUTE_REGIONS, ComputeConfigTargetRequiredError, ENTRYPOINT_BUILD_TYPES, FRAMEWORKS, LOCAL_DEV_BUILD_TYPES, computeTargetAppDir, frameworkByKey, frameworkFromAlias, inferComputeTargetFromCwd, isConfigBackedBuildType, selectComputeDeployTarget } from "@prisma/compute-sdk/config";
38
- import { SERVICE_TOKEN_ENV_VAR } from "@prisma/cli-engine";
39
- import open from "open";
40
- import { detectComputeAppFromDirectory } from "@prisma/compute-sdk/config/directory";
41
- import { matchError } from "better-result";
42
- //#region src/controllers/app.ts
43
- const FRAMEWORK_DEFAULT_HTTP_PORT = 3e3;
44
- const PRISMA_PROJECT_ID_ENV_VAR = "PRISMA_PROJECT_ID";
45
- const PRISMA_APP_ID_ENV_VAR = "PRISMA_APP_ID";
46
- const COMPUTE_REGION_IDS = new Set(COMPUTE_REGIONS);
47
- const APP_COMMAND_PREFIX = /^app /;
48
- const TRAILING_DOT = /\.$/;
49
- const DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
50
- const CNAME_TO_PHRASE = /\bcname(?:s)?\s+to\b/;
51
- const PRISMA_BUILD_HOSTNAME = /\b((?:[a-z0-9-]+\.)+prisma\.build)\b/i;
52
- const WAIT_TIMEOUT_DURATION = /^(\d+)(ms|s|m|h)$/;
53
- const WAIT_TIMEOUT_UNIT_MULTIPLIER_MS = {
54
- ms: 1,
55
- s: 1e3,
56
- m: 60 * 1e3,
57
- h: 3600 * 1e3
58
- };
59
- const NEXTJS_MENTION = /next\.?js/i;
60
- const STANDALONE_OUTPUT_MENTION = /standalone output/i;
61
- async function runAppBuild(context, options) {
62
- const compute = await resolveComputeTargetOrThrow(context, options?.configTarget, "build");
63
- const merged = mergeComputeLocalInputs({
64
- cli: {
65
- entrypoint: options?.entrypoint,
66
- buildType: options?.buildType
67
- },
68
- target: compute.target
69
- });
70
- const appDir = await resolveComputeAppDir(context, compute);
71
- let buildType = normalizeBuildType(merged.buildType);
72
- if (compute.target?.build && buildType === "auto") {
73
- const detected = await detectDeployFramework(appDir, context.runtime.signal);
74
- if (!detected) throw frameworkNotDetectedError(appDir);
75
- buildType = detected.buildType;
76
- }
77
- assertSupportedEntrypoint(buildType, merged.entrypoint, "build");
78
- if (compute.target?.build && buildType !== "auto") assertConfigBackedBuildSettings(buildType);
79
- const buildSettings = compute.config && compute.target?.build && isConfigBackedBuildType(buildType) ? (await resolveConfiguredAppBuildSettings({
80
- appPath: appDir,
81
- buildType,
82
- configured: compute.target.build,
83
- configPath: compute.config.configPath,
84
- signal: context.runtime.signal
85
- })).settings : void 0;
86
- try {
87
- const { artifact, buildType: actualBuildType } = await executeAppBuild({
88
- appPath: appDir,
89
- entrypoint: merged.entrypoint,
90
- buildType,
91
- buildSettings,
92
- signal: context.runtime.signal
93
- });
94
- return {
95
- command: "app.build",
96
- result: {
97
- directory: artifact.directory,
98
- entrypoint: artifact.entrypoint,
99
- buildType: actualBuildType
100
- },
101
- warnings: [],
102
- nextSteps: ["prisma-cli app deploy"]
103
- };
104
- } catch (error) {
105
- if (buildType === "auto" && isAutoBuildDetectionError(error)) throw usageError("App build requires an explicit framework when detection is ambiguous", `This preview auto-detects clear project shapes for ${RESOLVED_APP_BUILD_TYPES.map(formatBuildTypeName).join(", ")}.`, "Pass a supported --build-type value, or pass --entry <path> for a Bun app.", getBuildTypeExamples("build"), "app");
106
- throw buildFailedError("Local app build failed", error);
107
- }
108
- }
109
- async function runAppRun(context, options) {
110
- if (context.flags.json) throw usageError("App run does not support --json", "This command streams the framework dev server directly and cannot return structured JSON.", "Rerun without --json to pass framework logs through directly.", ["prisma-cli app run"], "app");
111
- const compute = await resolveComputeTargetOrThrow(context, options?.configTarget, "run");
112
- const merged = mergeComputeLocalInputs({
113
- cli: {
114
- entrypoint: options?.entrypoint,
115
- buildType: options?.buildType,
116
- port: options?.port
117
- },
118
- target: compute.target
119
- });
120
- if (merged.buildTypeFromConfig && compute.target?.framework && !frameworkByKey(compute.target.framework).hasLocalDevServer) throw usageError(`App run does not support the ${compute.target?.framework} framework yet`, `${compute.config?.relativeConfigPath ?? COMPUTE_CONFIG_FILENAME} sets a framework that has no local dev server in the current preview.`, "Run the framework dev server directly, or pass --build-type nextjs or --build-type bun to override.", ["prisma-cli app run --build-type nextjs", "prisma-cli app run --build-type bun --entry server.ts"], "app");
121
- const appDir = await resolveComputeAppDir(context, compute);
122
- const buildType = normalizeBuildType(merged.buildType);
123
- assertSupportedEntrypoint(buildType, merged.entrypoint, "run");
124
- const port = parseLocalPort(merged.port);
125
- const framework = await resolveLocalRunFramework(context, {
126
- requestedBuildType: buildType,
127
- configFramework: compute.target?.framework ?? null,
128
- appDir,
129
- entrypoint: merged.entrypoint
130
- });
131
- const entrypoint = framework.buildType === "bun" ? await resolveDeployEntrypoint(appDir, framework, merged.entrypoint, context.runtime.signal) : merged.entrypoint;
132
- let runResult;
133
- try {
134
- runResult = await runLocalApp({
135
- appPath: appDir,
136
- buildType: framework.buildType,
137
- entrypoint,
138
- port,
139
- env: context.runtime.env,
140
- signal: context.runtime.signal
141
- });
142
- } catch (error) {
143
- throw runFailedError("Local app run failed", error);
144
- }
145
- if (runResult.signal === "SIGINT" || runResult.signal === "SIGTERM") throw new DOMException("Command canceled", "AbortError");
146
- else if (runResult.exitCode !== 0) throw runFailedError("Local app run failed", `The ${formatFrameworkName(runResult.framework)} process exited with code ${runResult.exitCode}.`, runResult.exitCode);
147
- return {
148
- command: "app.run",
149
- result: {
150
- framework: runResult.framework,
151
- entrypoint: runResult.entrypoint,
152
- port: runResult.port,
153
- command: runResult.command
154
- },
155
- warnings: [],
156
- nextSteps: []
157
- };
158
- }
159
- async function runAppDeploy(context, appName, options) {
160
- const loaded = await loadComputeConfig$1(context.runtime.cwd, context.runtime.signal);
161
- if (loaded.isErr()) throw computeConfigErrorToCliError(loaded.error, "deploy");
162
- const config = loaded.value;
163
- const plan = planAppDeploy({
164
- config,
165
- requestedTarget: options?.configTarget ?? (config ? inferComputeTargetFromCwd(config, context.runtime.cwd) : void 0),
166
- hasCreateProject: options?.createProjectName !== void 0
167
- });
168
- if (plan.mode === "all") return runAppDeployAll(context, config, plan.targets, appName, options);
169
- return runSingleAppDeploy(context, appName, options, config);
170
- }
171
- async function runAppDeployAll(context, config, plannedTargets, appName, options) {
172
- assertNoPerAppInputsForDeployAll(context, plannedTargets, appName, options);
173
- const deployments = [];
174
- const warnings = [];
175
- for (const planned of plannedTargets) {
176
- maybeRenderDeployAllTargetHeader(context, planned);
177
- const targetOptions = {
178
- ...options,
179
- configTarget: planned.targetKey,
180
- createProjectName: planned.bindsCreateProject ? options?.createProjectName : void 0
181
- };
182
- try {
183
- const single = await runSingleAppDeploy(context, void 0, targetOptions, config);
184
- deployments.push({
185
- target: planned.targetKey,
186
- result: single.result
187
- });
188
- warnings.push(...single.warnings);
189
- } catch (error) {
190
- throw deployAllFailedError(error, plannedTargets, planned.index, deployments);
191
- }
192
- }
193
- return {
194
- command: "app.deploy",
195
- result: { deployments },
196
- warnings,
197
- nextSteps: ["prisma-cli app list-deploys <app>"]
198
- };
199
- }
200
- function assertNoPerAppInputsForDeployAll(context, plannedTargets, appName, options) {
201
- const used = perAppInputsForDeployAll({
202
- appName,
203
- framework: options?.framework,
204
- entrypoint: options?.entrypoint,
205
- httpPort: options?.httpPort,
206
- region: options?.region,
207
- envAssignments: options?.envAssignments,
208
- appIdEnvVar: {
209
- name: PRISMA_APP_ID_ENV_VAR,
210
- value: readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR)
211
- }
212
- });
213
- if (used.length === 0) return;
214
- const targetKeys = plannedTargets.map((target) => target.targetKey);
215
- throw usageError(`Deploying all apps does not accept ${used.join(", ")}`, `Without a target, app deploy deploys every configured app (${targetKeys.join(", ")}), so per-app inputs are ambiguous.`, "Pass the app target to apply per-app inputs to one app, or remove them to deploy all apps.", targetKeys.map((target) => `prisma-cli app deploy ${target}`), "app");
216
- }
217
- function maybeRenderDeployAllTargetHeader(context, planned) {
218
- if (context.flags.json || context.flags.quiet) return;
219
- context.output.stderr.write(`${planned.index > 0 ? "\n" : ""}── ${planned.targetKey} (${planned.index + 1}/${planned.total}) ──\n\n`);
220
- }
221
- function deployAllFailedError(error, plannedTargets, failedIndex, deployments) {
222
- if (!(error instanceof CliError)) return error;
223
- const failure = describeDeployAllFailure({
224
- targetKeys: plannedTargets.map((target) => target.targetKey),
225
- failedIndex,
226
- completed: deployments.map(({ target, result }) => ({
227
- target,
228
- deploymentId: result.deployment.id,
229
- url: result.deployment.url
230
- }))
231
- });
232
- const contextSentence = failure.contextLines.join(" ");
233
- return new CliError({
234
- code: error.code,
235
- domain: error.domain,
236
- summary: error.summary,
237
- why: error.humanLines ? error.why : [error.why, contextSentence].filter(Boolean).join(" "),
238
- fix: error.fix,
239
- debug: error.debug,
240
- where: error.where,
241
- meta: {
242
- ...error.meta,
243
- deployAll: {
244
- failedTarget: failure.failedTarget,
245
- completed: failure.completed,
246
- notAttempted: failure.notAttempted
247
- }
248
- },
249
- docsUrl: error.docsUrl,
250
- exitCode: error.exitCode,
251
- nextSteps: error.nextSteps,
252
- nextActions: error.nextActions,
253
- humanLines: error.humanLines ? [
254
- ...error.humanLines,
255
- "",
256
- ...failure.contextLines
257
- ] : void 0
258
- });
259
- }
260
- async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
261
- const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
262
- const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
263
- assertExclusiveDeployProjectInputs({
264
- projectRef: options?.projectRef,
265
- createProjectName: options?.createProjectName,
266
- envProjectId
267
- });
268
- const computeConfig = await resolveComputeTargetOrThrow(context, options?.configTarget, "deploy", { preloaded: preloadedConfig });
269
- const merged = mergeComputeDeployInputs({
270
- cli: {
271
- framework: options?.framework,
272
- entrypoint: options?.entrypoint,
273
- httpPort: options?.httpPort,
274
- region: options?.region,
275
- envInputs: options?.envAssignments
276
- },
277
- target: computeConfig.target,
278
- configFilename: computeConfig.config?.relativeConfigPath ?? COMPUTE_CONFIG_FILENAME
279
- });
280
- const appDir = await resolveComputeAppDir(context, computeConfig);
281
- const projectDir = computeConfig.config?.configDir ?? context.runtime.cwd;
282
- const agentSetupWarnings = await maybePromptForAgentSetup(context, projectDir);
283
- const localPin = await readDeployLocalPin({
284
- projectDir,
285
- signal: context.runtime.signal,
286
- envProjectId,
287
- projectRef: options?.projectRef,
288
- createProjectName: options?.createProjectName
289
- });
290
- const branch = await resolveDeployBranch(context, options?.branchName);
291
- if (merged.httpPort) parseDeployHttpPort(merged.httpPort.value);
292
- const deployRegion = normalizeDeployRegionInput(merged.region);
293
- assertSupportedEntrypointForRequestedDeployShape({
294
- requestedFramework: merged.framework?.value,
295
- entrypoint: merged.entrypoint?.value
296
- });
297
- const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
298
- branch,
299
- createProjectName: options?.createProjectName,
300
- createProjectRegion: deployRegion?.value,
301
- envProjectId,
302
- localPin
303
- });
304
- const localPinResult = await bindDeployProjectDirectory(context, target, projectDir);
305
- let framework = await resolveDeployFramework(context, {
306
- requestedFramework: merged.framework?.value,
307
- requestedFrameworkAnnotation: merged.framework?.annotation,
308
- entrypoint: merged.entrypoint?.value,
309
- entrypointAnnotation: merged.entrypoint?.annotation,
310
- appDir
311
- });
312
- let runtime = resolveDeployRuntime(merged.httpPort?.value, merged.httpPort?.annotation, framework);
313
- assertSupportedEntrypoint(framework.buildType, merged.entrypoint?.value, "deploy");
314
- const envVars = toOptionalEnvVars(await parseEnvInputs(merged.envInputsFromConfig ? projectDir : context.runtime.cwd, merged.envInputs, { commandName: "deploy" }));
315
- const selectedApp = await resolveDeployAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
316
- explicitAppName: appName,
317
- explicitAppId: envAppId,
318
- configAppName: merged.configAppName,
319
- configRegion: deployRegion,
320
- firstDeploy: Boolean(target.localPinAction),
321
- inferName: () => inferTargetName(appDir, context.runtime.signal)
322
- });
323
- await maybeRenderDeploySetupBlock(context, {
324
- includeDirectory: !target.localPinAction,
325
- appDir,
326
- projectName: target.project.name,
327
- branchName: target.branch.name,
328
- appName: selectedApp.displayName
329
- });
330
- const customized = await maybeCustomizeDeploySettings(context, {
331
- framework,
332
- runtime,
333
- firstDeploy: selectedApp.firstDeploy,
334
- explicitFramework: Boolean(merged.framework),
335
- explicitEntrypoint: Boolean(merged.entrypoint),
336
- explicitHttpPort: Boolean(merged.httpPort)
337
- });
338
- framework = customized.framework;
339
- runtime = customized.runtime;
340
- const noPromote = options?.noPromote === true;
341
- const productionDeployGate = noPromote ? { firstProductionDeploy: false } : await enforceProductionDeployGate(context, provider, {
342
- appId: selectedApp.appId,
343
- appName: selectedApp.displayName,
344
- branchKind: target.branch.kind,
345
- prod: options?.prod === true
346
- });
347
- const buildType = framework.buildType;
348
- assertSupportedEntrypoint(buildType, merged.entrypoint?.value, "deploy");
349
- const entrypoint = await resolveDeployEntrypoint(appDir, framework, merged.entrypoint?.value, context.runtime.signal);
350
- const buildSettingsResolution = await resolveDeployBuildSettings({
351
- computeConfig,
352
- appDir,
353
- buildType,
354
- signal: context.runtime.signal
355
- });
356
- const legacyWarnings = await handleLegacyBuildSettings(context, appDir, buildSettingsResolution.settings);
357
- maybeRenderDeployBuildSettings(context, buildSettingsResolution);
358
- const portMapping = parseDeployPortMapping(String(runtime.port));
359
- const branchDatabaseSetup = await maybeSetupBranchDatabase(context, provider, projectId, toBranchDatabaseDeployBranch(target.branch), {
360
- db: options?.db,
361
- providedEnvVars: envVars,
362
- firstProductionDeploy: productionDeployGate.firstProductionDeploy,
363
- projectDir
364
- });
365
- const progressState = createDeployProgressState();
366
- const deployStartedAt = Date.now();
367
- const deployResult = await provider.deployApp({
368
- cwd: appDir,
369
- projectId,
370
- branchName: target.branch.name,
371
- appId: selectedApp.appId,
372
- appName: selectedApp.appName,
373
- region: selectedApp.region,
374
- entrypoint,
375
- buildType,
376
- buildSettings: buildSettingsResolution.settings,
377
- portMapping,
378
- envVars,
379
- skipPromote: noPromote,
380
- interaction: void 0,
381
- signal: context.runtime.signal,
382
- progress: createDeployProgress(context.output.stderr, context.ui, !context.flags.json && !context.flags.quiet, progressState)
383
- }).catch((error) => {
384
- throw appDeployFailedError(error, progressState);
385
- });
386
- const deployDurationMs = Date.now() - deployStartedAt;
387
- await cacheDeployedApp(context, projectId, deployResult);
388
- return {
389
- command: "app.deploy",
390
- result: {
391
- workspace: target.workspace,
392
- project: target.project,
393
- branch: toResultBranch(target.branch),
394
- resolution: target.resolution,
395
- branchDatabase: branchDatabaseSetup.result,
396
- app: {
397
- id: deployResult.app.id,
398
- name: deployResult.app.name
399
- },
400
- deployment: deployResult.deployment,
401
- promoted: deployResult.promoted,
402
- deploySettings: {
403
- config: {
404
- path: computeConfig.config?.relativeConfigPath ?? null,
405
- status: buildSettingsResolution.status
406
- },
407
- buildCommand: {
408
- value: buildSettingsResolution.settings.buildCommand,
409
- source: buildSettingsResolution.settings.buildCommandSource
410
- },
411
- outputDirectory: {
412
- value: buildSettingsResolution.settings.outputDirectory,
413
- source: buildSettingsResolution.settings.outputDirectorySource
414
- },
415
- framework: {
416
- key: framework.key,
417
- buildType,
418
- name: framework.displayName,
419
- source: framework.annotation
420
- },
421
- entrypoint: entrypoint ?? buildSettingsResolution.settings.entrypoint ?? null,
422
- httpPort: runtime.port,
423
- region: deployResult.app.region ?? selectedApp.region ?? null,
424
- regionSource: deployRegion?.annotation ?? null,
425
- envVars: envVarNames(envVars)
426
- },
427
- durationMs: deployDurationMs,
428
- localPin: localPinResult
429
- },
430
- warnings: [
431
- ...agentSetupWarnings,
432
- ...legacyWarnings,
433
- ...branchDatabaseSetup.warnings
434
- ],
435
- nextSteps: deployResult.promoted ? ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${deployResult.deployment.id}`] : [`prisma-cli app promote ${deployResult.deployment.id}`, `prisma-cli app show-deploy ${deployResult.deployment.id}`]
436
- };
437
- }
438
- async function readDeployLocalPin(options) {
439
- if (options.envProjectId || options.projectRef || options.createProjectName) return { kind: "missing" };
440
- const readResult = await readLocalResolutionPin(options.projectDir, options.signal);
441
- if (readResult.isErr()) throw localPinReadErrorToDeployError(readResult.error);
442
- return readResult.value;
443
- }
444
- async function bindDeployProjectDirectory(context, target, projectDir) {
445
- if (!target.localPinAction) return;
446
- const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction, projectDir);
447
- if (setupResult.isErr()) throw projectDirectoryBindingErrorToCliError(setupResult.error);
448
- const projectSetup = setupResult.value;
449
- maybeRenderProjectLinked(context, projectSetup.directory, projectSetup.project.name, projectSetup.localPin.path);
450
- return projectSetup.localPin;
451
- }
452
- async function cacheDeployedApp(context, projectId, deployResult) {
453
- await context.stateStore.setSelectedApp(projectId, {
454
- id: deployResult.app.id,
455
- name: deployResult.app.name
456
- });
457
- const knownLiveDeploymentId = deployResult.promoted ? deployResult.deployment.id : deployResult.app.liveDeploymentId;
458
- if (knownLiveDeploymentId) await context.stateStore.setKnownLiveDeployment(projectId, deployResult.app.id, knownLiveDeploymentId);
459
- }
460
- async function resolveDeployBuildSettings(options) {
461
- const { computeConfig, appDir, buildType, signal } = options;
462
- if (computeConfig.target?.build) assertConfigBackedBuildSettings(buildType);
463
- if (computeConfig.config && computeConfig.target?.build && isConfigBackedBuildType(buildType)) return resolveConfiguredAppBuildSettings({
464
- appPath: appDir,
465
- buildType,
466
- configured: computeConfig.target.build,
467
- configPath: computeConfig.config.configPath,
468
- signal
469
- });
470
- return resolveInferredAppBuildSettings({
471
- appPath: appDir,
472
- buildType,
473
- signal
474
- });
475
- }
476
- async function runAppListDeploys(context, appName, projectRef, configTarget) {
477
- const compute = await resolveComputeManagementContext(context, configTarget, "list-deploys");
478
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
479
- commandName: "app list-deploys",
480
- projectDir: compute.projectDir
481
- });
482
- const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName ?? compute.configAppName);
483
- if (!selectedApp) return {
484
- command: "app.list-deploys",
485
- result: {
486
- projectId,
487
- verboseContext: toAppVerboseContext(target),
488
- app: null,
489
- deployments: []
490
- },
491
- warnings: [],
492
- nextSteps: ["prisma-cli app deploy"]
493
- };
494
- const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
495
- throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app deploy"]);
496
- });
497
- const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
498
- const deployments = applyLiveDeploymentHint(deploymentsResult.deployments, currentLiveDeploymentId).slice().sort((left, right) => right.createdAt.localeCompare(left.createdAt) || right.id.localeCompare(left.id));
499
- await context.stateStore.setSelectedApp(projectId, {
500
- id: deploymentsResult.app.id,
501
- name: deploymentsResult.app.name
502
- });
503
- return {
504
- command: "app.list-deploys",
505
- result: {
506
- projectId,
507
- verboseContext: toAppVerboseContext(target),
508
- app: {
509
- id: deploymentsResult.app.id,
510
- name: deploymentsResult.app.name
511
- },
512
- deployments
513
- },
514
- warnings: [],
515
- nextSteps: deployments.length > 0 ? [`prisma-cli app show-deploy ${deployments[0]?.id}`] : ["prisma-cli app deploy"]
516
- };
517
- }
518
- async function runAppShow(context, appName, projectRef, configTarget) {
519
- const compute = await resolveComputeManagementContext(context, configTarget, "show");
520
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
521
- commandName: "app show",
522
- projectDir: compute.projectDir
523
- });
524
- const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName ?? compute.configAppName);
525
- if (!selectedApp) return {
526
- command: "app.show",
527
- result: {
528
- projectId,
529
- verboseContext: toAppVerboseContext(target),
530
- app: null,
531
- liveDeployment: null,
532
- liveUrl: null,
533
- recentDeployments: []
534
- },
535
- warnings: [],
536
- nextSteps: ["prisma-cli app deploy"]
537
- };
538
- const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
539
- throw deployFailedError("Failed to inspect app", error, ["prisma-cli app list-deploys"]);
540
- });
541
- const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
542
- const deployments = applyLiveDeploymentHint(deploymentsResult.deployments, currentLiveDeploymentId).slice().sort((left, right) => right.createdAt.localeCompare(left.createdAt) || right.id.localeCompare(left.id));
543
- const liveDeployment = currentLiveDeploymentId ? deployments.find((deployment) => deployment.id === currentLiveDeploymentId) ?? null : null;
544
- await context.stateStore.setSelectedApp(projectId, {
545
- id: deploymentsResult.app.id,
546
- name: deploymentsResult.app.name
547
- });
548
- return {
549
- command: "app.show",
550
- result: {
551
- projectId,
552
- verboseContext: toAppVerboseContext(target),
553
- app: {
554
- id: deploymentsResult.app.id,
555
- name: deploymentsResult.app.name
556
- },
557
- liveDeployment,
558
- liveUrl: deploymentsResult.app.liveUrl,
559
- recentDeployments: deployments.slice(0, 5)
560
- },
561
- warnings: [],
562
- nextSteps: buildAppShowNextSteps(deploymentsResult.app.liveUrl, liveDeployment, deployments)
563
- };
564
- }
565
- async function runAppShowDeploy(context, deploymentId) {
566
- const deployment = await (await requirePreviewAppProvider(context)).showDeployment(deploymentId, { signal: context.runtime.signal }).catch((error) => {
567
- throw deployFailedError("Failed to show deployment", error, ["prisma-cli app list-deploys"]);
568
- });
569
- if (!deployment) throw new CliError({
570
- code: "DEPLOYMENT_NOT_FOUND",
571
- domain: "app",
572
- summary: `Deployment "${deploymentId}" not found`,
573
- why: "The requested deployment does not exist or is no longer available.",
574
- fix: "Run prisma-cli app list-deploys to choose an available deployment id.",
575
- exitCode: 1,
576
- nextSteps: ["prisma-cli app list-deploys"]
577
- });
578
- const workspaceId = deployment?.app ? await readCurrentWorkspaceId(context) : null;
579
- const rememberedProject = workspaceId ? await context.stateStore.readRememberedProject(workspaceId) : null;
580
- const knownLiveDeploymentId = deployment?.app && rememberedProject ? await context.stateStore.readKnownLiveDeployment(rememberedProject.id, deployment.app.id) : null;
581
- const liveDeploymentId = deployment.app?.liveDeploymentId || knownLiveDeploymentId;
582
- return {
583
- command: "app.show-deploy",
584
- result: {
585
- app: deployment.app ? {
586
- id: deployment.app.id,
587
- name: deployment.app.name
588
- } : null,
589
- deployment: {
590
- ...deployment.deployment,
591
- live: liveDeploymentId ? deployment.deployment.id === liveDeploymentId : deployment.deployment.live
592
- }
593
- },
594
- warnings: [],
595
- nextSteps: []
596
- };
597
- }
598
- async function runAppOpen(context, appNameArg, projectRef, configTarget) {
599
- const compute = await resolveComputeManagementContext(context, configTarget, "open");
600
- const appName = appNameArg ?? compute.configAppName;
601
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
602
- commandName: "app open",
603
- projectDir: compute.projectDir
604
- });
605
- const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
606
- if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
607
- const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
608
- throw deployFailedError("Failed to resolve app URL", error, ["prisma-cli app show"]);
609
- });
610
- const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
611
- const deployments = applyLiveDeploymentHint(deploymentsResult.deployments, currentLiveDeploymentId).slice().sort((left, right) => right.createdAt.localeCompare(left.createdAt) || right.id.localeCompare(left.id));
612
- const liveDeployment = currentLiveDeploymentId ? deployments.find((deployment) => deployment.id === currentLiveDeploymentId) ?? null : null;
613
- await context.stateStore.setSelectedApp(projectId, {
614
- id: deploymentsResult.app.id,
615
- name: deploymentsResult.app.name
616
- });
617
- if (!liveDeployment) throw noDeploymentsError("No deployments available to open", `The selected app "${deploymentsResult.app.name}" does not have any deployments yet.`);
618
- if (!deploymentsResult.app.liveUrl) throw featureUnavailableError("Live URL is not available for the selected app", "Deployments exist, but the provider does not expose a stable live service URL for this app yet.", "Run prisma-cli app show to inspect the current deployment state and try again after the app reports a live URL.", ["prisma-cli app show"], "app");
619
- const shouldOpen = canPrompt(context);
620
- if (shouldOpen) {
621
- context.runtime.signal.throwIfAborted();
622
- await open(deploymentsResult.app.liveUrl);
623
- context.runtime.signal.throwIfAborted();
624
- }
625
- return {
626
- command: "app.open",
627
- result: {
628
- projectId,
629
- verboseContext: toAppVerboseContext(target),
630
- app: {
631
- id: deploymentsResult.app.id,
632
- name: deploymentsResult.app.name
633
- },
634
- url: deploymentsResult.app.liveUrl,
635
- opened: shouldOpen
636
- },
637
- warnings: [],
638
- nextSteps: ["prisma-cli app show", `prisma-cli app show-deploy ${liveDeployment.id}`]
639
- };
640
- }
641
- async function runAppDomainAdd(context, hostname, options) {
642
- const normalizedHostname = normalizeDomainHostname(hostname);
643
- const target = await resolveAppDomainTarget(context, options, `app domain add ${normalizedHostname}`);
644
- const added = await target.provider.addDomain({
645
- appId: target.app.id,
646
- hostname: normalizedHostname,
647
- signal: context.runtime.signal
648
- }).catch((error) => {
649
- throw domainCommandError("add", error, normalizedHostname);
650
- });
651
- return {
652
- command: "app.domain.add",
653
- result: {
654
- ...target.resultTarget,
655
- domain: toAppDomainSummary(added.domain),
656
- existing: added.existing
657
- },
658
- warnings: [],
659
- nextSteps: [`prisma-cli app domain wait ${normalizedHostname}`, `prisma-cli app domain show ${normalizedHostname}`]
660
- };
661
- }
662
- async function runAppDomainShow(context, hostname, options) {
663
- const normalizedHostname = normalizeDomainHostname(hostname);
664
- const target = await resolveAppDomainTarget(context, options, `app domain show ${normalizedHostname}`);
665
- const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show", context.runtime.signal);
666
- const detail = await target.provider.showDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
667
- throw domainCommandError("show", error, normalizedHostname);
668
- });
669
- return {
670
- command: "app.domain.show",
671
- result: {
672
- ...target.resultTarget,
673
- domain: toAppDomainSummary(detail)
674
- },
675
- warnings: [],
676
- nextSteps: buildDomainShowNextSteps(detail)
677
- };
678
- }
679
- async function runAppDomainRemove(context, hostname, options) {
680
- const normalizedHostname = normalizeDomainHostname(hostname);
681
- const target = await resolveAppDomainTarget(context, options, `app domain remove ${normalizedHostname}`);
682
- const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove", context.runtime.signal);
683
- await confirmDomainRemoval(context, target.resultTarget, normalizedHostname);
684
- await target.provider.removeDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
685
- throw domainCommandError("remove", error, normalizedHostname);
686
- });
687
- return {
688
- command: "app.domain.remove",
689
- result: {
690
- ...target.resultTarget,
691
- hostname: normalizedHostname,
692
- removed: true
693
- },
694
- warnings: [],
695
- nextSteps: []
696
- };
697
- }
698
- async function runAppDomainRetry(context, hostname, options) {
699
- const normalizedHostname = normalizeDomainHostname(hostname);
700
- const target = await resolveAppDomainTarget(context, options, `app domain retry ${normalizedHostname}`);
701
- const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry", context.runtime.signal);
702
- const retried = await target.provider.retryDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
703
- throw domainCommandError("retry", error, normalizedHostname);
704
- });
705
- return {
706
- command: "app.domain.retry",
707
- result: {
708
- ...target.resultTarget,
709
- domain: toAppDomainSummary(retried)
710
- },
711
- warnings: [],
712
- nextSteps: [`prisma-cli app domain wait ${normalizedHostname}`]
713
- };
714
- }
715
- async function runAppDomainWait(context, hostname, options) {
716
- const normalizedHostname = normalizeDomainHostname(hostname);
717
- const timeoutMs = parseDomainWaitTimeout(options?.timeout);
718
- const target = await resolveAppDomainTarget(context, options, `app domain wait ${normalizedHostname}`);
719
- const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait", context.runtime.signal);
720
- if (!context.flags.json && !context.flags.quiet) context.output.stderr.write([
721
- `app domain wait -> Waiting for ${normalizedHostname} to become active.`,
722
- "",
723
- `Workspace: ${target.resultTarget.workspace.name} Project: ${target.resultTarget.project.name} Branch: ${target.resultTarget.branch.name} App: ${target.resultTarget.app.name}`,
724
- ""
725
- ].join("\n"));
726
- const start = Date.now();
727
- const deadline = start + timeoutMs;
728
- const pollIntervalMs = readDomainWaitPollIntervalMs(context);
729
- let lastStatus = null;
730
- let current = domain;
731
- while (true) {
732
- emitDomainWaitStatus(context, {
733
- hostname: normalizedHostname,
734
- domainId: current.id,
735
- previousStatus: lastStatus,
736
- status: current.status,
737
- elapsedMs: Date.now() - start
738
- });
739
- lastStatus = current.status;
740
- if (current.status === "active") {
741
- if (!context.flags.json && !context.flags.quiet) context.output.stderr.write(`\n${normalizedHostname} is live at https://${normalizedHostname}\n`);
742
- return;
743
- }
744
- if (current.status === "failed") throw new CliError({
745
- code: "DOMAIN_VERIFICATION_FAILED",
746
- domain: "app",
747
- summary: `Custom domain "${normalizedHostname}" failed verification`,
748
- why: formatDomainFailureWhy(current),
749
- fix: formatDomainFailureFix(current) ?? `Run prisma-cli app domain retry ${normalizedHostname}.`,
750
- exitCode: 1,
751
- nextSteps: [`prisma-cli app domain show ${normalizedHostname}`, `prisma-cli app domain retry ${normalizedHostname}`]
752
- });
753
- if (timeoutMs === 0 || Date.now() >= deadline) throw new CliError({
754
- code: "DOMAIN_VERIFICATION_TIMEOUT",
755
- domain: "app",
756
- summary: `Timed out waiting for "${normalizedHostname}" to become active`,
757
- why: `The domain is still "${current.status}".`,
758
- fix: `Run prisma-cli app domain show ${normalizedHostname} to inspect the current status, or retry wait with a longer --timeout.`,
759
- exitCode: 1,
760
- nextSteps: [`prisma-cli app domain show ${normalizedHostname}`]
761
- });
762
- await sleep(Math.min(pollIntervalMs, Math.max(deadline - Date.now(), 0)), context.runtime.signal);
763
- current = await target.provider.showDomain(current.id, { signal: context.runtime.signal }).catch((error) => {
764
- throw domainCommandError("wait", error, normalizedHostname);
765
- });
766
- }
767
- }
768
- async function runAppLogs(context, appNameArg, deploymentId, projectRef, configTarget) {
769
- const compute = await resolveComputeManagementContext(context, configTarget, "logs");
770
- const appName = appNameArg ?? compute.configAppName;
771
- const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef, {
772
- commandName: "app logs",
773
- projectDir: compute.projectDir
774
- });
775
- const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName);
776
- if (!context.flags.json && !context.flags.quiet) {
777
- const lines = renderCommandHeader(context.ui, {
778
- commandLabel: "app logs",
779
- description: "Streaming logs for the selected deployment.",
780
- rows: [
781
- {
782
- key: "project",
783
- value: projectId
784
- },
785
- {
786
- key: "app",
787
- value: target.app.name
788
- },
789
- {
790
- key: "deployment",
791
- value: target.deployment.id
792
- }
793
- ]
794
- });
795
- if (lines.length > 0) context.output.stderr.write(`${lines.join("\n")}\n`);
796
- }
797
- await provider.streamDeploymentLogs({
798
- deploymentId: target.deployment.id,
799
- signal: context.runtime.signal,
800
- onRecord: (record) => writeLogRecord(context, record)
801
- }).catch((error) => {
802
- throw deployFailedError("Failed to stream app logs", error, [`prisma-cli app show-deploy ${target.deployment.id}`, "prisma-cli app list-deploys"]);
803
- });
804
- }
805
- async function resolveExplicitLogDeployment(context, provider, projectId, branchName, appName, deploymentId) {
806
- if (appName) {
807
- const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, branchName), appName);
808
- if (!selectedApp) throw noDeploymentsError("No deployments available to stream logs", "The resolved project does not have any deployed app yet.");
809
- const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
810
- throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
811
- });
812
- const deployment = requireDeploymentForApp(deploymentsResult.deployments, deploymentId, selectedApp.name);
813
- await context.stateStore.setSelectedApp(projectId, {
814
- id: deploymentsResult.app.id,
815
- name: deploymentsResult.app.name
816
- });
817
- return {
818
- app: deploymentsResult.app,
819
- deployment
820
- };
821
- }
822
- const shown = await provider.showDeployment(deploymentId, { signal: context.runtime.signal }).catch((error) => {
823
- throw deployFailedError("Failed to show deployment", error, ["prisma-cli app list-deploys"]);
824
- });
825
- if (!shown) throw new CliError({
826
- code: "DEPLOYMENT_NOT_FOUND",
827
- domain: "app",
828
- summary: `Deployment "${deploymentId}" not found`,
829
- why: "The requested deployment does not exist or is no longer available.",
830
- fix: "Run prisma-cli app list-deploys to choose an available deployment id.",
831
- exitCode: 1,
832
- nextSteps: ["prisma-cli app list-deploys"]
833
- });
834
- if (!shown.app) throw new CliError({
835
- code: "DEPLOYMENT_NOT_FOUND",
836
- domain: "app",
837
- summary: `Deployment "${deploymentId}" is not attached to an app`,
838
- why: "The requested deployment could be found, but its app could not be resolved.",
839
- fix: "Run prisma-cli app list-deploys to choose an available deployment id for the selected app.",
840
- exitCode: 1,
841
- nextSteps: ["prisma-cli app list-deploys"]
842
- });
843
- const resolvedProjectApp = (await listApps(context, provider, projectId, branchName)).find((app) => app.id === shown.app?.id);
844
- if (!resolvedProjectApp) throw new CliError({
845
- code: "DEPLOYMENT_NOT_FOUND",
846
- domain: "app",
847
- summary: `Deployment "${deploymentId}" not found in the resolved project`,
848
- why: "The requested deployment does not belong to an app in the resolved project.",
849
- fix: "Run prisma-cli app list-deploys to choose an available deployment id for this project.",
850
- exitCode: 1,
851
- nextSteps: ["prisma-cli app list-deploys"]
852
- });
853
- await context.stateStore.setSelectedApp(projectId, {
854
- id: resolvedProjectApp.id,
855
- name: resolvedProjectApp.name
856
- });
857
- return {
858
- app: resolvedProjectApp,
859
- deployment: shown.deployment
860
- };
861
- }
862
- async function resolveLiveLogDeployment(context, provider, projectId, branchName, appName) {
863
- const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, branchName), appName);
864
- if (!selectedApp) throw noDeploymentsError("No deployments available to stream logs", "The resolved project does not have any deployed app yet.");
865
- const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
866
- throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
867
- });
868
- const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
869
- const deployments = applyLiveDeploymentHint(deploymentsResult.deployments, currentLiveDeploymentId);
870
- const deployment = currentLiveDeploymentId ? deployments.find((candidate) => candidate.id === currentLiveDeploymentId) ?? null : null;
871
- await context.stateStore.setSelectedApp(projectId, {
872
- id: deploymentsResult.app.id,
873
- name: deploymentsResult.app.name
874
- });
875
- if (!deployment) throw noDeploymentsError("No deployments available to stream logs", `The selected app "${deploymentsResult.app.name}" does not have any deployments yet.`);
876
- return {
877
- app: deploymentsResult.app,
878
- deployment
879
- };
880
- }
881
- function writeLogRecord(context, record) {
882
- if (context.flags.json) {
883
- writeJsonEvent(context.output, {
884
- type: record.type,
885
- command: "app.logs",
886
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
887
- data: record
888
- });
889
- return;
890
- }
891
- if (record.type === "log") {
892
- context.output.stdout.write(record.text);
893
- if (!record.text.endsWith("\n")) context.output.stdout.write("\n");
894
- }
895
- }
896
- async function runAppPromote(context, deploymentId, appNameArg, projectRef, configTarget) {
897
- const compute = await resolveComputeManagementContext(context, configTarget, "promote");
898
- const appName = appNameArg ?? compute.configAppName;
899
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
900
- commandName: "app promote",
901
- projectDir: compute.projectDir
902
- });
903
- const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
904
- const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
905
- throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
906
- });
907
- const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
908
- const targetDeployment = requireDeploymentForApp(deploymentsResult.deployments, deploymentId, selectedApp.name);
909
- const targetAlreadyLive = currentLiveDeploymentId === targetDeployment.id;
910
- await context.stateStore.setSelectedApp(projectId, {
911
- id: deploymentsResult.app.id,
912
- name: deploymentsResult.app.name
913
- });
914
- if (!targetAlreadyLive) await provider.promoteDeployment({
915
- appId: selectedApp.id,
916
- deploymentId: targetDeployment.id,
917
- signal: context.runtime.signal,
918
- progress: createPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
919
- }).catch((error) => {
920
- throw deployFailedError("Failed to promote deployment", error, ["prisma-cli app list-deploys"]);
921
- });
922
- await context.stateStore.setKnownLiveDeployment(projectId, deploymentsResult.app.id, targetDeployment.id);
923
- return {
924
- command: "app.promote",
925
- result: {
926
- projectId,
927
- verboseContext: toAppVerboseContext(target),
928
- app: {
929
- id: deploymentsResult.app.id,
930
- name: deploymentsResult.app.name
931
- },
932
- deployment: {
933
- ...targetDeployment,
934
- status: "running",
935
- live: true
936
- }
937
- },
938
- warnings: targetAlreadyLive ? ["The selected deployment is already live for this app."] : [],
939
- nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
940
- };
941
- }
942
- async function runAppRollback(context, appNameArg, deploymentId, projectRef, configTarget) {
943
- const compute = await resolveComputeManagementContext(context, configTarget, "rollback");
944
- const appName = appNameArg ?? compute.configAppName;
945
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
946
- commandName: "app rollback",
947
- projectDir: compute.projectDir
948
- });
949
- const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
950
- const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
951
- throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
952
- });
953
- const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
954
- const currentLiveDeployment = currentLiveDeploymentId ? deploymentsResult.deployments.find((deployment) => deployment.id === currentLiveDeploymentId) ?? null : null;
955
- const targetDeployment = deploymentId ? requireDeploymentForApp(deploymentsResult.deployments, deploymentId, selectedApp.name) : resolveRollbackTarget(deploymentsResult.deployments, currentLiveDeploymentId);
956
- const targetAlreadyLive = currentLiveDeploymentId === targetDeployment.id;
957
- await context.stateStore.setSelectedApp(projectId, {
958
- id: deploymentsResult.app.id,
959
- name: deploymentsResult.app.name
960
- });
961
- if (!targetAlreadyLive) await provider.promoteDeployment({
962
- appId: selectedApp.id,
963
- deploymentId: targetDeployment.id,
964
- signal: context.runtime.signal,
965
- progress: createPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
966
- }).catch((error) => {
967
- throw deployFailedError("Failed to roll back deployment", error, ["prisma-cli app list-deploys"]);
968
- });
969
- await context.stateStore.setKnownLiveDeployment(projectId, deploymentsResult.app.id, targetDeployment.id);
970
- return {
971
- command: "app.rollback",
972
- result: {
973
- projectId,
974
- verboseContext: toAppVerboseContext(target),
975
- app: {
976
- id: deploymentsResult.app.id,
977
- name: deploymentsResult.app.name
978
- },
979
- deployment: {
980
- ...targetDeployment,
981
- status: "running",
982
- live: true
983
- },
984
- previousLiveDeploymentId: currentLiveDeployment?.id ?? null
985
- },
986
- warnings: targetAlreadyLive ? ["The selected deployment is already live for this app."] : [],
987
- nextSteps: ["prisma-cli app list-deploys", `prisma-cli app show-deploy ${targetDeployment.id}`]
988
- };
989
- }
990
- /**
991
- * Removes an app and every deployment it owns in the resolved branch.
992
- *
993
- * @param branchName Scopes the removal to this branch. When omitted the branch
994
- * is inferred from the local Git branch, falling back to the production branch.
995
- */
996
- async function runAppRemove(context, appNameArg, projectRef, configTarget, branchName) {
997
- const compute = await resolveComputeManagementContext(context, configTarget, "remove");
998
- const appName = appNameArg ?? compute.configAppName;
999
- if (branchName !== void 0 && branchName.trim() === "") throw usageError("The --branch value cannot be empty", "app remove scopes the removal to the given branch; an empty --branch would silently fall back to the inferred (possibly production) branch.", "Pass a non-empty branch name, e.g. --branch <branch>, or omit --branch to use the inferred branch.", ["prisma-cli app remove --app <name> --branch <branch>"], "app");
1000
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, {
1001
- branch: branchName !== void 0 ? await resolveDeployBranch(context, branchName) : void 0,
1002
- commandName: "app remove",
1003
- projectDir: compute.projectDir
1004
- });
1005
- const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
1006
- await confirmAppRemoval(context, selectedApp);
1007
- const removedApp = await provider.removeApp(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
1008
- throw removeFailedError("Failed to remove app", error, ["prisma-cli app show", "prisma-cli app list-deploys"]);
1009
- });
1010
- const warnings = await cleanupRemovedAppState(context, projectId, removedApp.id);
1011
- return {
1012
- command: "app.remove",
1013
- result: {
1014
- projectId,
1015
- verboseContext: toAppVerboseContext(target),
1016
- app: {
1017
- id: removedApp.id,
1018
- name: removedApp.name
1019
- },
1020
- removed: true
1021
- },
1022
- warnings,
1023
- nextSteps: ["prisma-cli app deploy", "prisma-cli app list-deploys"]
1024
- };
1025
- }
1026
- async function resolveAppDomainTarget(context, options, commandName = "app domain") {
1027
- const compute = await resolveComputeManagementContext(context, options?.configTarget, commandName.replace(APP_COMMAND_PREFIX, ""));
1028
- const branch = resolveDomainBranch(options?.branchName);
1029
- if (toBranchKind(branch.name) !== "production") throw new CliError({
1030
- code: "BRANCH_NOT_DEPLOYABLE",
1031
- domain: "branch",
1032
- summary: "Custom domains require the production branch",
1033
- why: `Custom domains on preview branch "${branch.name}" are not supported in Public Beta.`,
1034
- fix: "Use --branch production, or attach the domain after promoting/deploying to the production branch.",
1035
- exitCode: 2,
1036
- nextSteps: ["prisma-cli app domain add <hostname> --branch production"]
1037
- });
1038
- const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
1039
- const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
1040
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, options?.projectRef, {
1041
- branch,
1042
- commandName,
1043
- envProjectId,
1044
- projectDir: compute.projectDir
1045
- });
1046
- const selectedApp = await resolveDomainAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
1047
- explicitAppName: options?.appName ?? compute.configAppName,
1048
- explicitAppId: envAppId
1049
- });
1050
- await context.stateStore.setSelectedApp(projectId, {
1051
- id: selectedApp.id,
1052
- name: selectedApp.name
1053
- });
1054
- return {
1055
- provider,
1056
- app: selectedApp,
1057
- resultTarget: {
1058
- workspace: target.workspace,
1059
- project: target.project,
1060
- branch: toResultBranch(target.branch),
1061
- app: {
1062
- id: selectedApp.id,
1063
- name: selectedApp.name
1064
- }
1065
- }
1066
- };
1067
- }
1068
- function resolveDomainBranch(explicitBranchName) {
1069
- return {
1070
- name: explicitBranchName?.trim() || "production",
1071
- annotation: explicitBranchName ? "set by --branch" : "production default"
1072
- };
1073
- }
1074
- async function resolveDomainAppSelection(context, projectId, apps, options) {
1075
- if (options.explicitAppId) {
1076
- const matched = apps.find((app) => app.id === options.explicitAppId);
1077
- 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");
1078
- return matched;
1079
- }
1080
- const selectedApp = await resolveExistingAppSelection(context, projectId, apps, options.explicitAppName);
1081
- if (selectedApp) return selectedApp;
1082
- 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");
1083
- }
1084
- async function resolveDomainByHostname(provider, appId, hostname, command, signal) {
1085
- const matched = (await provider.listDomains(appId, { signal }).catch((error) => {
1086
- throw domainCommandError(command, error, hostname);
1087
- })).find((domain) => sameDomainHostname(domain.hostname, hostname));
1088
- if (matched) return matched;
1089
- throw domainNotFoundError(hostname);
1090
- }
1091
- function normalizeDomainHostname(hostname) {
1092
- const normalized = hostname.trim().replace(TRAILING_DOT, "").toLowerCase();
1093
- if (!isValidDomainHostname(normalized)) throw new CliError({
1094
- code: "DOMAIN_HOSTNAME_INVALID",
1095
- domain: "app",
1096
- summary: `Invalid custom domain "${hostname}"`,
1097
- why: "Custom domains must be valid hostnames without protocol, path, wildcard, or port.",
1098
- fix: "Pass a hostname like shop.acme.com.",
1099
- exitCode: 2,
1100
- nextSteps: ["prisma-cli app domain add shop.acme.com"]
1101
- });
1102
- return normalized;
1103
- }
1104
- function isValidDomainHostname(hostname) {
1105
- if (hostname.length < 1 || hostname.length > 253) return false;
1106
- if (hostname.includes("://") || hostname.includes("/") || hostname.includes(":") || hostname.startsWith("*.")) return false;
1107
- const labels = hostname.split(".");
1108
- if (labels.length < 2) return false;
1109
- return labels.every((label) => DOMAIN_LABEL.test(label));
1110
- }
1111
- function sameDomainHostname(left, right) {
1112
- return left.trim().replace(TRAILING_DOT, "").toLowerCase() === right.trim().replace(TRAILING_DOT, "").toLowerCase();
1113
- }
1114
- function toAppDomainSummary(domain) {
1115
- return {
1116
- id: domain.id,
1117
- type: domain.type,
1118
- url: domain.url,
1119
- hostname: domain.hostname,
1120
- appId: domain.appId,
1121
- status: domain.status,
1122
- foundryStatus: domain.foundryStatus,
1123
- failureReason: domain.failureReason,
1124
- failureCategory: domain.failureCategory,
1125
- certExpiresAt: domain.certExpiresAt,
1126
- createdAt: domain.createdAt,
1127
- updatedAt: domain.updatedAt,
1128
- dnsRecords: toAppDomainDnsRecords(domain)
1129
- };
1130
- }
1131
- function toAppDomainDnsRecords(domain) {
1132
- return domain.dnsRecords.map((record) => ({
1133
- type: record.type,
1134
- name: record.name,
1135
- value: record.value,
1136
- ttl: record.ttl
1137
- }));
1138
- }
1139
- function buildDomainShowNextSteps(domain) {
1140
- if (domain.status === "active") return [];
1141
- if (domain.status === "failed") return [`prisma-cli app domain retry ${domain.hostname}`];
1142
- return [`prisma-cli app domain wait ${domain.hostname}`];
1143
- }
1144
- async function confirmDomainRemoval(context, target, hostname) {
1145
- if (context.flags.yes) return;
1146
- if (!canPrompt(context)) throw new CliError({
1147
- code: "CONFIRMATION_REQUIRED",
1148
- domain: "app",
1149
- summary: "Custom domain removal requires confirmation in the current mode",
1150
- why: "This command detaches a domain and cannot prompt for confirmation in the current mode.",
1151
- fix: `Pass --yes to confirm removal of "${hostname}", or rerun prisma-cli app domain remove in an interactive TTY.`,
1152
- exitCode: 1,
1153
- nextSteps: [`prisma-cli app domain remove ${hostname} --app ${target.app.name} --yes`]
1154
- });
1155
- if (!await confirmPrompt({
1156
- input: context.runtime.stdin,
1157
- output: context.output.stderr,
1158
- signal: context.runtime.signal,
1159
- message: `Detach ${hostname} from App "${target.app.name}"?`,
1160
- initialValue: false
1161
- })) 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");
1162
- }
1163
- function domainCommandError(command, error, hostname) {
1164
- if (error instanceof DomainApiError) {
1165
- const apiError = command === "add" ? domainAddApiError(hostname, error) : domainLookupApiError(command, hostname, error);
1166
- if (apiError) return apiError;
1167
- }
1168
- return new CliError({
1169
- code: "DEPLOY_FAILED",
1170
- domain: "app",
1171
- summary: `Custom domain ${command} failed`,
1172
- why: error instanceof Error ? error.message : String(error),
1173
- fix: "Retry the command, or rerun with --trace for more detailed diagnostics.",
1174
- debug: formatDebugDetails(error),
1175
- exitCode: 1,
1176
- nextSteps: [`prisma-cli app domain show ${hostname}`]
1177
- });
1178
- }
1179
- function domainAddApiError(hostname, error) {
1180
- if ((error.status === 400 || error.status === 422) && isDomainDnsError(error)) return domainDnsNotConfiguredError(hostname, error);
1181
- if (error.status === 400) return new CliError({
1182
- code: "DOMAIN_HOSTNAME_INVALID",
1183
- domain: "app",
1184
- summary: `Invalid custom domain "${hostname}"`,
1185
- why: error.message,
1186
- fix: "Pass a valid hostname like shop.acme.com and make sure DNS can be verified.",
1187
- debug: formatDebugDetails(error),
1188
- exitCode: 2,
1189
- nextSteps: ["prisma-cli app domain add shop.acme.com"]
1190
- });
1191
- if (error.status === 429 || isDomainQuotaError(error)) return new CliError({
1192
- code: "DOMAIN_QUOTA_EXCEEDED",
1193
- domain: "app",
1194
- summary: "Custom domain quota exceeded",
1195
- why: error.message,
1196
- fix: "Remove an existing custom domain before adding another one.",
1197
- debug: formatDebugDetails(error),
1198
- exitCode: 1,
1199
- nextSteps: ["prisma-cli app domain remove <hostname>"]
1200
- });
1201
- if (error.status === 409) return domainAlreadyRegisteredError(hostname, error);
1202
- if (error.status === 422) return new CliError({
1203
- code: "NO_DEPLOYMENTS",
1204
- domain: "app",
1205
- summary: "Custom domain requires a live production deployment",
1206
- why: "The selected production app does not have a promoted version that can receive a custom domain.",
1207
- fix: "Deploy the app to the production branch, then rerun the domain command.",
1208
- debug: formatDebugDetails(error),
1209
- exitCode: 1,
1210
- nextSteps: ["prisma-cli app deploy --branch production", `prisma-cli app domain add ${hostname}`]
1211
- });
1212
- return null;
1213
- }
1214
- function domainLookupApiError(command, hostname, error) {
1215
- if (error.status === 404) return domainNotFoundError(hostname);
1216
- if (command === "retry" && error.status === 409) return new CliError({
1217
- code: "DOMAIN_RETRY_NOT_ELIGIBLE",
1218
- domain: "app",
1219
- summary: `Custom domain "${hostname}" is not eligible for retry`,
1220
- why: error.message,
1221
- fix: "Wait for the current verification or TLS step to finish, then rerun retry if the domain fails.",
1222
- debug: formatDebugDetails(error),
1223
- exitCode: 1,
1224
- nextSteps: [`prisma-cli app domain show ${hostname}`]
1225
- });
1226
- return null;
1227
- }
1228
- function isDomainQuotaError(error) {
1229
- if (error.status !== 409) return false;
1230
- const text = `${error.message} ${error.hint ?? ""}`.toLowerCase();
1231
- return text.includes("quota") || text.includes("maximum") || text.includes("limit");
1232
- }
1233
- function domainAlreadyRegisteredError(hostname, error) {
1234
- return new CliError({
1235
- code: "DOMAIN_ALREADY_REGISTERED",
1236
- domain: "app",
1237
- summary: `Custom domain "${hostname}" is already registered`,
1238
- why: error.hint ?? error.message,
1239
- fix: "Select the app that owns this hostname and remove it there, or contact support if you cannot access it.",
1240
- debug: formatDebugDetails(error),
1241
- exitCode: 1,
1242
- nextSteps: [`Select the owning app and remove ${hostname} there.`, "Contact Prisma support if you cannot access the owning app."]
1243
- });
1244
- }
1245
- function isDomainDnsError(error) {
1246
- const text = `${error.message} ${error.hint ?? ""}`.toLowerCase();
1247
- 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") || CNAME_TO_PHRASE.test(text);
1248
- }
1249
- function domainDnsNotConfiguredError(hostname, error) {
1250
- const target = extractDomainDnsTarget(error);
1251
- const record = target ? `CNAME ${hostname} -> ${target}` : null;
1252
- return new CliError({
1253
- code: "DOMAIN_DNS_NOT_CONFIGURED",
1254
- domain: "app",
1255
- summary: `DNS is not configured for "${hostname}"`,
1256
- why: error.hint ?? error.message,
1257
- 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.",
1258
- debug: formatDebugDetails(error),
1259
- exitCode: 1,
1260
- nextSteps: record ? [`add ${record}`, `prisma-cli app domain add ${hostname}`] : [`prisma-cli app domain add ${hostname} --trace`]
1261
- });
1262
- }
1263
- function extractDomainDnsTarget(error) {
1264
- const text = `${error.hint ?? ""} ${error.message}`;
1265
- return PRISMA_BUILD_HOSTNAME.exec(text)?.[1]?.toLowerCase() ?? null;
1266
- }
1267
- function domainNotFoundError(hostname) {
1268
- return new CliError({
1269
- code: "DOMAIN_NOT_FOUND",
1270
- domain: "app",
1271
- summary: `Custom domain "${hostname}" not found`,
1272
- why: "The hostname is not attached to the selected app.",
1273
- fix: "Check the hostname and selected app, or add the domain first.",
1274
- exitCode: 1,
1275
- nextSteps: [`prisma-cli app domain add ${hostname}`]
1276
- });
1277
- }
1278
- function formatDomainFailureWhy(domain) {
1279
- if (domain.failureReason) return domain.failureCategory ? `${domain.failureCategory}: ${domain.failureReason}` : domain.failureReason;
1280
- return "The platform reported a terminal failed state for this custom domain.";
1281
- }
1282
- function parseDomainWaitTimeout(value) {
1283
- if (!value) return 900 * 1e3;
1284
- const trimmed = value.trim().toLowerCase();
1285
- if (trimmed === "0") return 0;
1286
- const match = WAIT_TIMEOUT_DURATION.exec(trimmed);
1287
- 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");
1288
- return Number.parseInt(match[1], 10) * (WAIT_TIMEOUT_UNIT_MULTIPLIER_MS[match[2]] ?? 1);
1289
- }
1290
- function readDomainWaitPollIntervalMs(context) {
1291
- const raw = context.runtime.env.PRISMA_CLI_DOMAIN_WAIT_POLL_MS;
1292
- if (!raw) return 5e3;
1293
- const parsed = Number.parseInt(raw, 10);
1294
- return Number.isInteger(parsed) && parsed > 0 ? parsed : 5e3;
1295
- }
1296
- function emitDomainWaitStatus(context, event) {
1297
- if (context.flags.json) {
1298
- writeJsonEvent(context.output, {
1299
- type: "status",
1300
- command: "app.domain.wait",
1301
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1302
- data: {
1303
- hostname: event.hostname,
1304
- domainId: event.domainId,
1305
- previousStatus: event.previousStatus,
1306
- status: event.status,
1307
- elapsedMs: event.elapsedMs
1308
- }
1309
- });
1310
- return;
1311
- }
1312
- if (context.flags.quiet) return;
1313
- if (event.previousStatus === event.status) return;
1314
- const transition = event.previousStatus ? `${event.previousStatus} -> ${event.status}` : event.status;
1315
- context.output.stderr.write(` ${transition} (${formatElapsed(event.elapsedMs)})\n`);
1316
- }
1317
- function formatElapsed(milliseconds) {
1318
- const seconds = Math.max(Math.floor(milliseconds / 1e3), 0);
1319
- const minutes = Math.floor(seconds / 60);
1320
- const remainingSeconds = seconds % 60;
1321
- return `${minutes}:${String(remainingSeconds).padStart(2, "0")}`;
1322
- }
1323
- async function sleep(milliseconds, signal) {
1324
- if (milliseconds <= 0) return;
1325
- signal.throwIfAborted();
1326
- await new Promise((resolve, reject) => {
1327
- const onAbort = () => {
1328
- clearTimeout(timeout);
1329
- reject(signal.reason);
1330
- };
1331
- const timeout = setTimeout(() => {
1332
- signal.removeEventListener("abort", onAbort);
1333
- resolve();
1334
- }, milliseconds);
1335
- signal.addEventListener("abort", onAbort, { once: true });
1336
- });
1337
- }
1338
- async function resolveDeployAppSelection(context, projectId, apps, options) {
1339
- const newAppRegion = deployNewAppRegion(options.configRegion);
1340
- if (options.explicitAppName) return resolveDeployAppByName(context, apps, {
1341
- name: options.explicitAppName,
1342
- matchedAnnotation: "set by --app",
1343
- newAnnotation: "set by --app",
1344
- requestedRegion: options.configRegion,
1345
- newAppRegion,
1346
- firstDeploy: options.firstDeploy
1347
- });
1348
- if (options.explicitAppId) {
1349
- const matched = apps.find((app) => app.id === options.explicitAppId);
1350
- if (!matched) throw usageError("Selected app does not exist in the resolved project", `The app "${options.explicitAppId}" from ${PRISMA_APP_ID_ENV_VAR} could not be found in resolved project "${projectId}".`, `Unset ${PRISMA_APP_ID_ENV_VAR}, pass --app <name>, or choose an app from prisma-cli app list-deploys.`, ["prisma-cli app list-deploys"], "app");
1351
- assertDeployRegionMatchesExistingApp(matched, options.configRegion);
1352
- return {
1353
- appId: matched.id,
1354
- displayName: matched.name,
1355
- annotation: `from ${PRISMA_APP_ID_ENV_VAR}`,
1356
- firstDeploy: options.firstDeploy
1357
- };
1358
- }
1359
- if (options.configAppName) {
1360
- const configName = options.configAppName;
1361
- return resolveDeployAppByName(context, apps, {
1362
- name: configName.value,
1363
- matchedAnnotation: configName.annotation,
1364
- newAnnotation: configName.annotation,
1365
- requestedRegion: options.configRegion,
1366
- newAppRegion,
1367
- firstDeploy: options.firstDeploy
1368
- });
1369
- }
1370
- const inferredName = await options.inferName();
1371
- const newAnnotation = inferredName.source === "package-name" ? "created from package.json" : "created from directory name";
1372
- return resolveDeployAppByName(context, apps, {
1373
- name: inferredName.name,
1374
- matchedAnnotation: "existing app on this branch",
1375
- newAnnotation,
1376
- requestedRegion: options.configRegion,
1377
- newAppRegion,
1378
- firstDeploy: options.firstDeploy
1379
- });
1380
- }
1381
- async function resolveDeployAppByName(context, apps, options) {
1382
- const matches = findAppsByName(apps, options.name);
1383
- if (matches.length > 1) return resolveAmbiguousDeployApp(context, matches, options.name, options.requestedRegion, options.newAppRegion, options.firstDeploy);
1384
- const matched = matches[0];
1385
- if (matched) {
1386
- assertDeployRegionMatchesExistingApp(matched, options.requestedRegion);
1387
- return {
1388
- appId: matched.id,
1389
- displayName: matched.name,
1390
- annotation: options.matchedAnnotation,
1391
- firstDeploy: options.firstDeploy
1392
- };
1393
- }
1394
- return {
1395
- appName: options.name,
1396
- region: options.newAppRegion,
1397
- displayName: options.name,
1398
- annotation: options.newAnnotation,
1399
- firstDeploy: options.firstDeploy
1400
- };
1401
- }
1402
- function assertDeployRegionMatchesExistingApp(app, requestedRegion) {
1403
- if (requestedRegion?.annotation !== "set by --region" || !app.region || app.region === requestedRegion.value) return;
1404
- throw usageError("App already exists in another region", `The selected app "${app.name}" is in region "${app.region}", but --region requested "${requestedRegion.value}".`, "Remove --region to deploy the existing app, or pass --app <new-name> to create a new app in that region.", [`prisma-cli app deploy --app ${formatCommandArgument(app.name)}`, `prisma-cli app deploy --app <new-name> --region ${formatCommandArgument(requestedRegion.value)}`], "app");
1405
- }
1406
- async function resolveAmbiguousDeployApp(context, matches, targetName, requestedRegion, newAppRegion, firstDeploy) {
1407
- if (canPrompt(context)) {
1408
- const createNew = "__create_new_app__";
1409
- const cancel = "__cancel__";
1410
- const selected = await selectPrompt({
1411
- input: context.runtime.stdin,
1412
- output: context.runtime.stderr,
1413
- message: `Multiple apps are named "${targetName}"`,
1414
- choices: [
1415
- ...sortApps(matches).map((app) => ({
1416
- label: `${app.name} (${app.id})`,
1417
- value: app
1418
- })),
1419
- {
1420
- label: `Create a new app named "${targetName}"`,
1421
- value: createNew
1422
- },
1423
- {
1424
- label: "Cancel",
1425
- value: cancel
1426
- }
1427
- ]
1428
- });
1429
- if (selected === cancel) throw usageError("App selection canceled", "The command was canceled before an app was selected.", "Re-run the command and choose an app, or pass --app <name>.", ["prisma-cli app deploy --app <name>"], "app");
1430
- if (selected === createNew) return {
1431
- appName: targetName,
1432
- region: newAppRegion,
1433
- displayName: targetName,
1434
- annotation: "created from package.json",
1435
- firstDeploy
1436
- };
1437
- assertDeployRegionMatchesExistingApp(selected, requestedRegion);
1438
- return {
1439
- appId: selected.id,
1440
- displayName: selected.name,
1441
- annotation: "selected by you",
1442
- firstDeploy
1443
- };
1444
- }
1445
- throw new CliError({
1446
- code: "APP_AMBIGUOUS",
1447
- domain: "app",
1448
- summary: "App resolution is ambiguous",
1449
- why: `Multiple apps matched "${targetName}".`,
1450
- fix: "Pass --app <name> to choose the app explicitly.",
1451
- meta: { candidates: matches.map((app) => ({
1452
- id: app.id,
1453
- name: app.name
1454
- })) },
1455
- exitCode: 2,
1456
- nextSteps: ["prisma-cli app deploy --app <name>"]
1457
- });
1458
- }
1459
- function deployNewAppRegion(configRegion) {
1460
- return configRegion?.value;
1461
- }
1462
- async function resolveExistingAppSelection(context, projectId, apps, explicitAppName) {
1463
- if (explicitAppName) {
1464
- const matched = findAppByName(apps, explicitAppName);
1465
- if (!matched) throw usageError("Selected app does not exist in the resolved project", `The app "${explicitAppName}" could not be found in resolved project "${projectId}".`, "Pass the name of an existing app, or rerun prisma-cli app list-deploys in a TTY to choose one.", ["prisma-cli app list-deploys"], "app");
1466
- return matched;
1467
- }
1468
- const savedSelection = await context.stateStore.readSelectedApp(projectId);
1469
- if (savedSelection) {
1470
- const matched = apps.find((app) => app.id === savedSelection.id) ?? findAppByName(apps, savedSelection.name);
1471
- if (matched) return matched;
1472
- if (!canPrompt(context)) throw usageError("Saved app selection is no longer available", "The locally selected app could not be found in the resolved project.", "Pass --app <name>, or rerun prisma-cli app list-deploys in a TTY to choose an available app.", ["prisma-cli app list-deploys"], "app");
1473
- }
1474
- if (apps.length === 0) return null;
1475
- if (!canPrompt(context)) throw usageError("App selection required in non-interactive mode", "This command cannot choose an app in the current mode.", "Pass --app <name>, or rerun prisma-cli app list-deploys in a TTY to choose an app.", ["prisma-cli app list-deploys"], "app");
1476
- const selectedId = await createSelectPromptPort(context).select({
1477
- message: "Select an app",
1478
- choices: sortApps(apps).map((app) => ({
1479
- label: app.name,
1480
- value: app.id
1481
- }))
1482
- });
1483
- return apps.find((app) => app.id === selectedId) ?? null;
1484
- }
1485
- async function requireReleaseAppSelection(context, projectId, apps, explicitAppName, commandName) {
1486
- const selectedApp = await resolveExistingAppSelection(context, projectId, apps, explicitAppName);
1487
- if (selectedApp) return selectedApp;
1488
- throw usageError(`App ${commandName} requires an existing app`, "The resolved project does not have an app that can be selected for this command.", `Deploy an app first, or rerun prisma-cli app ${commandName} with --app <name> after an app exists.`, ["prisma-cli app deploy", "prisma-cli app list-deploys"], "app");
1489
- }
1490
- async function confirmAppRemoval(context, app) {
1491
- if (context.flags.yes) return;
1492
- if (!canPrompt(context)) throw new CliError({
1493
- code: "CONFIRMATION_REQUIRED",
1494
- domain: "app",
1495
- summary: "App remove requires confirmation in the current mode",
1496
- why: "This command is destructive and cannot prompt for confirmation in the current mode.",
1497
- fix: `Pass --yes to confirm removal of "${app.name}", or rerun prisma-cli app remove in an interactive TTY.`,
1498
- exitCode: 1,
1499
- nextSteps: [`prisma-cli app remove --app ${app.name} --yes`]
1500
- });
1501
- await textPrompt({
1502
- input: context.runtime.stdin,
1503
- output: context.output.stderr,
1504
- signal: context.runtime.signal,
1505
- message: `Type ${app.name} to confirm app removal`,
1506
- placeholder: app.name,
1507
- validate: (value) => value === app.name ? void 0 : `Type "${app.name}" to confirm removal.`
1508
- });
1509
- }
1510
- async function cleanupRemovedAppState(context, projectId, appId) {
1511
- const warnings = [];
1512
- try {
1513
- await context.stateStore.clearSelectedApp(projectId, appId);
1514
- } catch (error) {
1515
- warnings.push(localStateCleanupWarning("selected app", error));
1516
- }
1517
- try {
1518
- await context.stateStore.clearKnownLiveDeployment(projectId, appId);
1519
- } catch (error) {
1520
- warnings.push(localStateCleanupWarning("known live deployment", error));
1521
- }
1522
- return warnings;
1523
- }
1524
- function requireDeploymentForApp(deployments, deploymentId, appName) {
1525
- const deployment = deployments.find((candidate) => candidate.id === deploymentId);
1526
- if (deployment) return deployment;
1527
- throw new CliError({
1528
- code: "DEPLOYMENT_NOT_FOUND",
1529
- domain: "app",
1530
- summary: `Deployment "${deploymentId}" not found for app "${appName}"`,
1531
- why: "The requested deployment does not belong to the resolved app or is no longer available.",
1532
- fix: "Run prisma-cli app list-deploys to choose an available deployment id for this app.",
1533
- exitCode: 1,
1534
- nextSteps: ["prisma-cli app list-deploys"]
1535
- });
1536
- }
1537
- /**
1538
- * Resolves the app's live deployment from the app pointer, the provider's live
1539
- * flag, then locally cached state.
1540
- *
1541
- * @returns the live deployment id, or null when no authoritative signal exists.
1542
- * Callers must treat null as "not known to be live" rather than assuming the
1543
- * newest deployment is live.
1544
- */
1545
- async function resolveCurrentLiveDeploymentId(context, projectId, app, deployments) {
1546
- if (app.liveDeploymentId && deployments.some((deployment) => deployment.id === app.liveDeploymentId)) return app.liveDeploymentId;
1547
- const providerLiveDeployment = deployments.find((deployment) => deployment.live === true);
1548
- if (providerLiveDeployment) return providerLiveDeployment.id;
1549
- const knownLiveDeploymentId = await context.stateStore.readKnownLiveDeployment(projectId, app.id);
1550
- if (knownLiveDeploymentId && deployments.some((deployment) => deployment.id === knownLiveDeploymentId)) return knownLiveDeploymentId;
1551
- return null;
1552
- }
1553
- function buildAppShowNextSteps(liveUrl, liveDeployment, deployments) {
1554
- const nextSteps = [];
1555
- if (liveUrl) nextSteps.push("prisma-cli app open");
1556
- if (liveDeployment) nextSteps.push(`prisma-cli app show-deploy ${liveDeployment.id}`);
1557
- else if (deployments[0]) nextSteps.push(`prisma-cli app show-deploy ${deployments[0].id}`);
1558
- else nextSteps.push("prisma-cli app deploy");
1559
- return nextSteps;
1560
- }
1561
- function applyLiveDeploymentHint(deployments, currentLiveDeploymentId) {
1562
- if (!currentLiveDeploymentId) return deployments.map((deployment) => ({
1563
- ...deployment,
1564
- live: deployment.live ?? null
1565
- }));
1566
- return deployments.map((deployment) => ({
1567
- ...deployment,
1568
- live: deployment.id === currentLiveDeploymentId
1569
- }));
1570
- }
1571
- function resolveRollbackTarget(deployments, currentLiveDeploymentId) {
1572
- const previousDeployment = deployments.find((deployment) => deployment.id !== currentLiveDeploymentId);
1573
- if (previousDeployment) return previousDeployment;
1574
- throw new CliError({
1575
- code: "NO_PREVIOUS_DEPLOYMENT",
1576
- domain: "app",
1577
- summary: "No previous deployment available for rollback",
1578
- why: "The selected app does not have an earlier deployment to switch back to.",
1579
- fix: "Deploy a second version first, or rerun prisma-cli app rollback --to <deployment-id> for a specific earlier deployment.",
1580
- exitCode: 1,
1581
- nextSteps: ["prisma-cli app deploy", "prisma-cli app list-deploys"]
1582
- });
1583
- }
1584
- async function listApps(context, provider, projectId, branchName) {
1585
- return provider.listApps(projectId, {
1586
- branchName,
1587
- signal: context.runtime.signal
1588
- }).then(sortApps).catch((error) => {
1589
- if (isMissingProjectError(error)) throw new CliError({
1590
- code: "PROJECT_NOT_FOUND",
1591
- domain: "project",
1592
- summary: "Project not found",
1593
- why: `The resolved project "${projectId}" does not exist in the authenticated workspace or is no longer accessible.`,
1594
- fix: "Pass --project <id-or-name>, or run prisma-cli project show to inspect this directory's binding.",
1595
- exitCode: 1,
1596
- nextSteps: ["prisma-cli project show", "prisma-cli project link <id-or-name>"]
1597
- });
1598
- throw deployFailedError("Failed to list apps", error, ["prisma-cli project show"]);
1599
- });
1600
- }
1601
- async function requirePreviewAppProvider(context) {
1602
- const { provider } = await requirePreviewAppProviderWithClient(context);
1603
- return provider;
1604
- }
1605
- async function requirePreviewAppProviderWithClient(context) {
1606
- const client = await authenticatedManagementApiClient(context.runtime.env, context.runtime.signal);
1607
- if (!client) throw authRequiredError(["prisma-cli auth login"]);
1608
- return {
1609
- client,
1610
- provider: createAppProvider(client, createPreviewLogAuthOptions(context.runtime.env, context.runtime.signal))
1611
- };
1612
- }
1613
- function createPreviewLogAuthOptions(env, signal) {
1614
- const rawToken = env[SERVICE_TOKEN_ENV_VAR]?.trim();
1615
- if (rawToken) return {
1616
- baseUrl: getApiBaseUrl(env),
1617
- getToken: async () => rawToken
1618
- };
1619
- const tokenStorage = new FileTokenStorage(env, signal);
1620
- return {
1621
- baseUrl: getApiBaseUrl(env),
1622
- getToken: async () => {
1623
- const tokens = await tokenStorage.getTokens();
1624
- if (!tokens) throw new Error("Authentication token is no longer available. Run prisma-cli auth login and try again.");
1625
- return tokens.accessToken;
1626
- }
1627
- };
1628
- }
1629
- async function requireProviderAndProjectContext(context, explicitProject, options) {
1630
- const { client, provider } = await requirePreviewAppProviderWithClient(context);
1631
- const target = await resolveProjectContext(context, client, explicitProject, options);
1632
- return {
1633
- client,
1634
- provider,
1635
- target,
1636
- projectId: target.project.id
1637
- };
1638
- }
1639
- async function requireProviderAndDeployProjectContext(context, explicitProject, options) {
1640
- const { client, provider } = await requirePreviewAppProviderWithClient(context);
1641
- const target = await resolveDeployProjectContext(context, client, provider, explicitProject, options);
1642
- return {
1643
- client,
1644
- provider,
1645
- target,
1646
- projectId: target.project.id
1647
- };
1648
- }
1649
- async function resolveProjectContext(context, client, explicitProject, options) {
1650
- const workspace = (await requireAuthenticatedAuthState(context)).workspace;
1651
- if (!workspace) throw workspaceRequiredError();
1652
- const resolvedResult = await resolveProjectTarget({
1653
- context,
1654
- workspace,
1655
- explicitProject,
1656
- envProjectId: options?.envProjectId,
1657
- projectDir: options?.projectDir,
1658
- listProjects: () => listRealWorkspaceProjects(client, context.runtime.signal),
1659
- commandName: options?.commandName
1660
- });
1661
- if (resolvedResult.isErr()) throw projectResolutionErrorToCliError(resolvedResult.error);
1662
- const resolved = resolvedResult.value;
1663
- const requested = options?.branch ?? await resolveDeployBranch(context, void 0);
1664
- const remoteBranch = options?.branch ? null : await resolveReadBranch(client, {
1665
- projectId: resolved.project.id,
1666
- branchName: requested.name,
1667
- signal: context.runtime.signal
1668
- });
1669
- return {
1670
- ...resolved,
1671
- branch: remoteBranch ?? {
1672
- id: null,
1673
- name: requested.name,
1674
- kind: toBranchKind(requested.name)
1675
- }
1676
- };
1677
- }
1678
- async function resolveDeployProjectContext(context, client, provider, explicitProject, options) {
1679
- const workspace = (await requireAuthenticatedAuthState(context)).workspace;
1680
- if (!workspace) throw workspaceRequiredError();
1681
- const branch = options.branch ?? await resolveDeployBranch(context, void 0);
1682
- const projects = await listRealWorkspaceProjects(client, context.runtime.signal);
1683
- if (explicitProject) return withRemoteDeployBranch(provider, {
1684
- workspace,
1685
- project: toProjectSummary(resolveProjectForSetup(explicitProject, projects, workspace)),
1686
- resolution: {
1687
- projectSource: "explicit",
1688
- targetName: explicitProject,
1689
- targetNameSource: "explicit"
1690
- },
1691
- localPinAction: "linked"
1692
- }, branch, context.runtime.signal);
1693
- if (options.createProjectName) return withRemoteDeployBranch(provider, await resolveCreatedDeployProject(provider, options.createProjectName, workspace, context.runtime.signal, options.createProjectRegion), branch, context.runtime.signal);
1694
- if (options.envProjectId) return withRemoteDeployBranch(provider, resolveEnvDeployProject(options.envProjectId, projects, workspace), branch, context.runtime.signal);
1695
- const localPin = options.localPin;
1696
- if (localPin.kind === "present") return withRemoteDeployBranch(provider, resolveLocalPinDeployProject(localPin.pin, projects, workspace), branch, context.runtime.signal);
1697
- const platformMapping = await resolveDurablePlatformMapping();
1698
- if (platformMapping && sameWorkspaceId(platformMapping.workspace.id, workspace.id)) return withRemoteDeployBranch(provider, {
1699
- workspace,
1700
- project: toProjectSummary(platformMapping),
1701
- resolution: {
1702
- projectSource: "platform-mapping",
1703
- targetName: platformMapping.name,
1704
- targetNameSource: "platform-mapping"
1705
- }
1706
- }, branch, context.runtime.signal);
1707
- if (canPrompt(context) && !context.flags.yes) return withRemoteDeployBranch(provider, await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects, options.createProjectRegion), branch, context.runtime.signal);
1708
- throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
1709
- }
1710
- async function resolveCreatedDeployProject(provider, createProjectName, workspace, signal, createProjectRegion) {
1711
- const projectName = createProjectName.trim();
1712
- if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
1713
- return {
1714
- workspace,
1715
- project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, signal, createProjectRegion)),
1716
- resolution: {
1717
- projectSource: "created",
1718
- targetName: projectName,
1719
- targetNameSource: "explicit"
1720
- },
1721
- localPinAction: "created"
1722
- };
1723
- }
1724
- function resolveEnvDeployProject(envProjectId, projects, workspace) {
1725
- const project = projects.find((candidate) => candidate.id === envProjectId);
1726
- if (!project) throw projectNotFoundError(envProjectId, workspace);
1727
- return {
1728
- workspace,
1729
- project: toProjectSummary(project),
1730
- resolution: {
1731
- projectSource: "env",
1732
- targetName: envProjectId,
1733
- targetNameSource: "env"
1734
- }
1735
- };
1736
- }
1737
- function resolveLocalPinDeployProject(pin, projects, workspace) {
1738
- if (!sameWorkspaceId(pin.workspaceId, workspace.id)) throw localProjectWorkspaceMismatchError({
1739
- pinnedWorkspaceId: pin.workspaceId,
1740
- pinnedProjectId: pin.projectId,
1741
- activeWorkspace: workspace
1742
- });
1743
- const project = projects.find((candidate) => candidate.id === pin.projectId);
1744
- if (!project) throw localResolutionPinStaleError();
1745
- return {
1746
- workspace,
1747
- project: toProjectSummary(project),
1748
- resolution: {
1749
- projectSource: "local-pin",
1750
- targetName: project.name,
1751
- targetNameSource: "local-pin"
1752
- }
1753
- };
1754
- }
1755
- async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects, createProjectRegion) {
1756
- const setup = await promptForProjectSetupChoice({
1757
- context,
1758
- projects,
1759
- createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal, createProjectRegion),
1760
- cancel: {
1761
- why: "Deploy needs a Project before it can continue.",
1762
- fix: "Choose an existing Project or create a new one, then rerun deploy.",
1763
- nextSteps: ["prisma-cli app deploy --project <id-or-name>", "prisma-cli app deploy --create-project <name>"]
1764
- }
1765
- });
1766
- return {
1767
- workspace,
1768
- project: setup.project,
1769
- resolution: {
1770
- projectSource: setup.action === "created" ? "created" : "prompt",
1771
- targetName: setup.targetName,
1772
- targetNameSource: setup.targetNameSource
1773
- },
1774
- localPinAction: setup.action
1775
- };
1776
- }
1777
- async function createProjectForDeploySetup(provider, projectName, workspace, signal, region) {
1778
- const created = await provider.createProject({
1779
- name: projectName,
1780
- region,
1781
- signal
1782
- }).catch((error) => {
1783
- throw projectCreateFailedError(error, projectName, workspace, {
1784
- nextSteps: [
1785
- "prisma-cli project list",
1786
- "prisma-cli app deploy --project <id-or-name>",
1787
- `prisma-cli app deploy --create-project ${formatCommandArgument(projectName)}`
1788
- ],
1789
- permissionFix: "Choose an existing Project with --project, or grant the token permission to create Projects in this workspace.",
1790
- fallbackFix: "Choose an existing Project with --project, or retry after addressing the platform error above."
1791
- });
1792
- });
1793
- return {
1794
- id: created.id,
1795
- name: created.name,
1796
- ...created.defaultRegion != null ? { defaultRegion: created.defaultRegion } : {},
1797
- workspace
1798
- };
1799
- }
1800
- async function withRemoteDeployBranch(provider, target, branch, signal) {
1801
- const remoteBranch = await provider.resolveBranch(target.project.id, {
1802
- branchName: branch.name,
1803
- signal
1804
- });
1805
- return {
1806
- ...target,
1807
- branch: {
1808
- id: remoteBranch.id,
1809
- name: remoteBranch.name,
1810
- kind: remoteBranch.role
1811
- }
1812
- };
1813
- }
1814
- function toBranchKind(name) {
1815
- return name === "production" || name === "main" ? "production" : "preview";
1816
- }
1817
- function toResultBranch(branch) {
1818
- return {
1819
- id: branch.id,
1820
- name: branch.name,
1821
- kind: branch.kind
1822
- };
1823
- }
1824
- function toAppVerboseContext(target) {
1825
- return {
1826
- workspace: target.workspace,
1827
- project: target.project,
1828
- branch: target.branch,
1829
- resolution: target.resolution
1830
- };
1831
- }
1832
- function toBranchDatabaseDeployBranch(branch) {
1833
- if (!branch.id) throw new Error(`Deploy branch "${branch.name}" was not resolved remotely.`);
1834
- return {
1835
- id: branch.id,
1836
- name: branch.name,
1837
- kind: branch.kind
1838
- };
1839
- }
1840
- function assertExclusiveDeployProjectInputs(options) {
1841
- const provided = [
1842
- options.projectRef ? "--project" : null,
1843
- options.createProjectName ? "--create-project" : null,
1844
- options.envProjectId ? PRISMA_PROJECT_ID_ENV_VAR : null
1845
- ].filter((value) => Boolean(value));
1846
- if (provided.length <= 1) return;
1847
- throw usageError("Project selection is ambiguous", `${provided.join(", ")} cannot be used together.`, "Choose exactly one Project source for this deploy.", [
1848
- "prisma-cli app deploy --project <id-or-name>",
1849
- "prisma-cli app deploy --create-project <name>",
1850
- `unset ${PRISMA_PROJECT_ID_ENV_VAR}`
1851
- ], "project");
1852
- }
1853
- async function resolveDeployBranch(context, explicitBranchName) {
1854
- if (explicitBranchName) return {
1855
- name: explicitBranchName,
1856
- annotation: "set by --branch"
1857
- };
1858
- const gitBranch = await readLocalGitBranch(context.runtime.cwd, context.runtime.signal);
1859
- if (gitBranch) return {
1860
- name: gitBranch,
1861
- annotation: "from local Git branch"
1862
- };
1863
- return {
1864
- name: "main",
1865
- annotation: "default"
1866
- };
1867
- }
1868
- async function resolveComputeTargetOrThrow(context, configTarget, commandName, options) {
1869
- let config;
1870
- if (options?.preloaded !== void 0) config = options.preloaded;
1871
- else {
1872
- const loaded = await loadComputeConfig$1(context.runtime.cwd, context.runtime.signal);
1873
- if (loaded.isErr()) throw computeConfigErrorToCliError(loaded.error, commandName);
1874
- config = loaded.value;
1875
- }
1876
- if (!config) {
1877
- if (configTarget) throw usageError(`App target "${configTarget}" requires a compute config file`, `No ${COMPUTE_CONFIG_FILENAME} exists in the current directory, so there are no named app targets.`, `Create ${COMPUTE_CONFIG_FILENAME} with an apps entry named "${configTarget}", or rerun without the target argument.`, [`prisma-cli app ${commandName}`], "app");
1878
- return {
1879
- config: null,
1880
- target: null
1881
- };
1882
- }
1883
- const requestedTarget = configTarget ?? inferComputeTargetFromCwd(config, context.runtime.cwd);
1884
- const selected = selectComputeDeployTarget(config, requestedTarget);
1885
- if (selected.isErr()) {
1886
- if (options?.targetOptional && selected.error instanceof ComputeConfigTargetRequiredError) return {
1887
- config,
1888
- target: null
1889
- };
1890
- throw computeConfigErrorToCliError(selected.error, commandName);
1891
- }
1892
- return {
1893
- config,
1894
- target: selected.value
1895
- };
1896
- }
1897
- /**
1898
- * Compute-config context for app management commands: the project directory
1899
- * (where `.prisma/local.json` lives) and the config-selected app name, which
1900
- * ranks below `--app` but above the remembered app selection.
1901
- */
1902
- async function resolveComputeManagementContext(context, configTarget, commandName) {
1903
- const compute = await resolveComputeTargetOrThrow(context, configTarget, commandName, { targetOptional: true });
1904
- return {
1905
- projectDir: compute.config?.configDir ?? context.runtime.cwd,
1906
- configAppName: compute.target?.name ?? compute.target?.key ?? void 0
1907
- };
1908
- }
1909
- async function resolveComputeAppDir(context, compute) {
1910
- if (!compute.config || !compute.target) return context.runtime.cwd;
1911
- const appDir = computeTargetAppDir(compute.config, compute.target);
1912
- if (!compute.target.root) return appDir;
1913
- context.runtime.signal.throwIfAborted();
1914
- try {
1915
- await access(appDir);
1916
- context.runtime.signal.throwIfAborted();
1917
- } catch (error) {
1918
- if (context.runtime.signal.aborted) throw error;
1919
- throw new CliError({
1920
- code: "COMPUTE_CONFIG_INVALID",
1921
- domain: "app",
1922
- summary: `App root "${compute.target.root}" does not exist`,
1923
- why: `${compute.config.relativeConfigPath} points the selected app at "${compute.target.root}", but that directory does not exist.`,
1924
- fix: `Fix the root path in ${compute.config.relativeConfigPath} or create the directory.`,
1925
- where: appDir,
1926
- meta: {
1927
- appRoot: compute.target.root,
1928
- appDir
1929
- },
1930
- exitCode: 2,
1931
- nextSteps: ["prisma-cli app deploy"]
1932
- });
1933
- }
1934
- return appDir;
1935
- }
1936
- /**
1937
- * `prisma.app.json` is no longer read or written. A leftover file that
1938
- * matches the effective settings only warns; one with custom values fails
1939
- * with migration guidance so builds never silently change.
1940
- */
1941
- async function handleLegacyBuildSettings(context, appDir, effective) {
1942
- const legacy = await detectLegacyBuildSettings({
1943
- appPath: appDir,
1944
- effective,
1945
- signal: context.runtime.signal
1946
- });
1947
- switch (legacy.kind) {
1948
- case "absent": return [];
1949
- case "matching": return [`${PRISMA_APP_CONFIG_FILENAME} is no longer used and matches the resolved build settings. Delete it.`];
1950
- case "invalid": return [`${PRISMA_APP_CONFIG_FILENAME} is no longer used and could not be parsed. Delete it.`];
1951
- case "custom": {
1952
- const buildBlock = [
1953
- "build: {",
1954
- ` command: ${legacy.buildCommand === null ? "null" : JSON.stringify(legacy.buildCommand)},`,
1955
- ` outputDirectory: ${JSON.stringify(legacy.outputDirectory)},`,
1956
- "}"
1957
- ].join(" ");
1958
- throw new CliError({
1959
- code: "BUILD_SETTINGS_MIGRATION_REQUIRED",
1960
- domain: "app",
1961
- summary: `${PRISMA_APP_CONFIG_FILENAME} is no longer supported`,
1962
- why: `${PRISMA_APP_CONFIG_FILENAME} contains custom build settings that differ from the resolved defaults, and the file is no longer read.`,
1963
- fix: `Move the settings into prisma.compute.ts as \`${buildBlock}\` on this app, then delete ${PRISMA_APP_CONFIG_FILENAME}.`,
1964
- where: legacy.configPath,
1965
- meta: {
1966
- configPath: legacy.configPath,
1967
- buildCommand: legacy.buildCommand,
1968
- outputDirectory: legacy.outputDirectory
1969
- },
1970
- exitCode: 2,
1971
- nextSteps: ["prisma-cli app deploy"]
1972
- });
1973
- }
1974
- }
1975
- }
1976
- async function resolveDeployFramework(context, options) {
1977
- if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, options.requestedFrameworkAnnotation ?? "set by --framework");
1978
- if (options.entrypoint) return {
1979
- key: "bun",
1980
- buildType: "bun",
1981
- displayName: "Bun",
1982
- annotation: options.entrypointAnnotation ?? "set by --entry"
1983
- };
1984
- const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
1985
- if (detected) return detected;
1986
- throw frameworkNotDetectedError(options.appDir);
1987
- }
1988
- function resolveDeployRuntime(requestedHttpPort, requestedHttpPortAnnotation, framework) {
1989
- if (requestedHttpPort) return {
1990
- port: parseDeployHttpPort(requestedHttpPort),
1991
- annotation: requestedHttpPortAnnotation ?? "set by --http-port"
1992
- };
1993
- return {
1994
- port: FRAMEWORK_DEFAULT_HTTP_PORT,
1995
- annotation: `${framework.displayName} default`
1996
- };
1997
- }
1998
- function assertSupportedEntrypointForRequestedDeployShape(options) {
1999
- if (!options.requestedFramework) return;
2000
- assertSupportedEntrypoint(frameworkFromUserFacingValue(options.requestedFramework, "set by --framework").buildType, options.entrypoint, "deploy");
2001
- }
2002
- async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signal) {
2003
- if (explicitEntrypoint || framework.buildType !== "bun") return explicitEntrypoint;
2004
- const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd, signal));
2005
- if (packageEntrypoint) return packageEntrypoint;
2006
- const defaultEntrypoint = frameworkFromAlias(framework.key)?.defaultEntrypoint;
2007
- if (!defaultEntrypoint) return;
2008
- signal.throwIfAborted();
2009
- try {
2010
- await access(path.join(cwd, defaultEntrypoint));
2011
- signal.throwIfAborted();
2012
- return defaultEntrypoint;
2013
- } catch (error) {
2014
- if (signal.aborted) throw error;
2015
- if (error.code !== "ENOENT") throw error;
2016
- return;
2017
- }
2018
- }
2019
- async function detectDeployFramework(cwd, signal) {
2020
- const detected = await detectComputeAppFromDirectory({
2021
- appPath: cwd,
2022
- signal
2023
- });
2024
- if (!detected) return null;
2025
- let annotation = "detected from package.json";
2026
- if (detected.configFile?.standaloneOutput) annotation = "standalone output detected";
2027
- else if (detected.configFile) annotation = `detected from ${path.basename(detected.configFile.path)}`;
2028
- return {
2029
- key: detected.framework,
2030
- buildType: detected.buildType,
2031
- displayName: detected.frameworkName,
2032
- annotation
2033
- };
2034
- }
2035
- function frameworkFromUserFacingValue(value, annotation) {
2036
- const framework = frameworkFromAlias(value);
2037
- if (!framework) throw frameworkNotDetectedError(void 0, value);
2038
- return {
2039
- key: framework.key,
2040
- buildType: framework.buildType,
2041
- displayName: framework.displayName,
2042
- annotation
2043
- };
2044
- }
2045
- function assertConfigBackedBuildSettings(buildType) {
2046
- if (isConfigBackedBuildType(buildType)) return;
2047
- const displayName = FRAMEWORKS.find((framework) => framework.buildType === buildType)?.displayName ?? buildType;
2048
- throw new CliError({
2049
- code: "BUILD_SETTINGS_UNSUPPORTED",
2050
- domain: "app",
2051
- summary: `build settings are not supported for ${displayName} apps`,
2052
- why: `${displayName} deploys run \`${buildType} build\` and package its output automatically.`,
2053
- fix: "Remove the `build` block from prisma.compute.ts for this app.",
2054
- exitCode: 2
2055
- });
2056
- }
2057
- function frameworkNotDetectedError(cwd, requestedFramework) {
2058
- const supported = FRAMEWORKS.map((framework) => framework.displayName).join(", ");
2059
- const directory = cwd ? ` in ${formatDeployDirectory(cwd)}` : "";
2060
- return new CliError({
2061
- code: "FRAMEWORK_NOT_DETECTED",
2062
- domain: "app",
2063
- summary: requestedFramework ? `Unsupported framework "${requestedFramework}"` : `Cannot detect a supported framework${directory}`,
2064
- why: `Supported Beta frameworks: ${supported}.`,
2065
- fix: `Add one of these frameworks as a dependency, pass --framework <${FRAMEWORKS.map((framework) => framework.key).join("|")}>, or pass --entry <path> for a Bun app.`,
2066
- exitCode: 2,
2067
- nextSteps: [
2068
- "prisma-cli app deploy --framework nextjs",
2069
- "prisma-cli app deploy --framework hono",
2070
- "prisma-cli app deploy --framework tanstack-start",
2071
- "prisma-cli app deploy --framework bun --entry server.ts",
2072
- "prisma-cli app deploy --entry server.ts"
2073
- ]
2074
- });
2075
- }
2076
- async function maybeRenderDeploySetupBlock(context, details) {
2077
- if (context.flags.json || context.flags.quiet) return;
2078
- const directory = formatAppDirectoryLabel(context.runtime.cwd, details.appDir);
2079
- const prefix = details.includeDirectory ? `Deploying ${directory} to` : "Deploying to";
2080
- context.output.stderr.write(`${prefix} ${details.projectName} / ${details.branchName} / ${details.appName}\n\n`);
2081
- }
2082
- function maybeRenderDeployBuildSettings(context, resolution) {
2083
- if (context.flags.json || context.flags.quiet) return;
2084
- const settings = resolution.settings;
2085
- const title = resolution.status === "config" ? `Using ${resolution.relativeConfigPath}` : "Build settings";
2086
- context.output.stderr.write(`${title}\n${renderDeployOutputRows(context.ui, [
2087
- {
2088
- label: "Build Command",
2089
- value: settings.buildCommand ?? "none",
2090
- origin: settings.buildCommandSource ?? void 0
2091
- },
2092
- {
2093
- label: "Output Directory",
2094
- value: settings.outputDirectory,
2095
- origin: settings.outputDirectorySource ?? void 0
2096
- },
2097
- ...settings.entrypoint ? [{
2098
- label: "Entrypoint",
2099
- value: settings.entrypoint,
2100
- origin: settings.entrypointSource ?? void 0
2101
- }] : []
2102
- ]).join("\n")}\n\n`);
2103
- }
2104
- function maybeRenderProjectLinked(context, directory, projectName, localPinPath) {
2105
- if (context.flags.json || context.flags.quiet) return;
2106
- context.output.stderr.write(`${context.ui.success("✔")} Linked "${directory}" to Project "${projectName}"\nSaved ${localPinPath}\n\n`);
2107
- }
2108
- async function maybeCustomizeDeploySettings(context, options) {
2109
- if (!options.firstDeploy || context.flags.yes || options.explicitFramework || options.explicitEntrypoint || options.explicitHttpPort || !canPrompt(context)) return {
2110
- framework: options.framework,
2111
- runtime: options.runtime
2112
- };
2113
- maybeRenderDeploySettingsPreview(context, {
2114
- framework: options.framework,
2115
- runtime: options.runtime
2116
- });
2117
- if (!await confirmPrompt({
2118
- input: context.runtime.stdin,
2119
- output: context.runtime.stderr,
2120
- signal: context.runtime.signal,
2121
- message: "Customize build settings?",
2122
- initialValue: false
2123
- })) return {
2124
- framework: options.framework,
2125
- runtime: options.runtime
2126
- };
2127
- const framework = frameworkFromUserFacingValue(await selectPrompt({
2128
- input: context.runtime.stdin,
2129
- output: context.runtime.stderr,
2130
- signal: context.runtime.signal,
2131
- message: `Framework (${options.framework.displayName})`,
2132
- choices: FRAMEWORKS.map((framework) => ({
2133
- label: framework.displayName,
2134
- value: framework.key
2135
- }))
2136
- }), "set by you");
2137
- const requestedPort = await textPrompt({
2138
- input: context.runtime.stdin,
2139
- output: context.runtime.stderr,
2140
- signal: context.runtime.signal,
2141
- message: `HTTP port (${options.runtime.port})`,
2142
- placeholder: String(options.runtime.port),
2143
- validate: validateDeployHttpPortText
2144
- });
2145
- const runtime = {
2146
- port: requestedPort.trim() ? parseDeployHttpPort(requestedPort) : options.runtime.port,
2147
- annotation: "set by you"
2148
- };
2149
- const changedRows = [framework.key !== options.framework.key ? {
2150
- label: "Framework",
2151
- value: framework.displayName,
2152
- annotation: framework.annotation
2153
- } : null, runtime.port !== options.runtime.port ? {
2154
- label: "Runtime",
2155
- value: `HTTP ${runtime.port}`,
2156
- annotation: runtime.annotation
2157
- } : null].filter((row) => Boolean(row));
2158
- if (changedRows.length > 0 && !context.flags.quiet && !context.flags.json) context.output.stderr.write(`${renderDeployOutputRows(context.ui, changedRows.map((row) => ({
2159
- label: row.label,
2160
- value: row.value,
2161
- origin: row.annotation
2162
- }))).join("\n")}\n\n`);
2163
- return {
2164
- framework,
2165
- runtime
2166
- };
2167
- }
2168
- function maybeRenderDeploySettingsPreview(context, options) {
2169
- if (context.flags.quiet || context.flags.json) return;
2170
- context.output.stderr.write(`Detected ${options.framework.displayName}\n${renderDeploySettingsPreview(context.ui, [{
2171
- key: "framework",
2172
- value: options.framework.displayName
2173
- }, {
2174
- key: "runtime",
2175
- value: `HTTP ${options.runtime.port}`
2176
- }]).join("\n")}\n\n`);
2177
- }
2178
- function validateDeployHttpPortText(value) {
2179
- if (!value?.trim()) return;
2180
- try {
2181
- parseDeployHttpPort(value);
2182
- return;
2183
- } catch (error) {
2184
- return error instanceof CliError ? error.summary : String(error);
2185
- }
2186
- }
2187
- function formatDeployDirectory(cwd) {
2188
- const basename = path.basename(cwd);
2189
- return basename ? `./${basename}` : ".";
2190
- }
2191
- function formatAppDirectoryLabel(cwd, appDir) {
2192
- if (appDir === cwd) return formatDeployDirectory(cwd);
2193
- const relative = path.relative(cwd, appDir).split(path.sep).join("/");
2194
- return relative.startsWith("..") ? relative : `./${relative}`;
2195
- }
2196
- async function readCurrentWorkspaceId(context) {
2197
- const state = await context.stateStore.read();
2198
- if (state.auth?.workspaceId) return state.auth.workspaceId;
2199
- return (await readAuthState(context.runtime.env, context.runtime.signal)).workspace?.id ?? null;
2200
- }
2201
- function normalizeBuildType(requestedBuildType) {
2202
- if (!requestedBuildType) return "auto";
2203
- if (isPreviewBuildType(requestedBuildType)) return requestedBuildType;
2204
- throw usageError(`Unsupported build type "${requestedBuildType}"`, `Only ${APP_BUILD_TYPE_LABELS} are supported in the current preview.`, "Pass a supported --build-type value.", getBuildTypeExamples("build"), "app");
2205
- }
2206
- function isPreviewBuildType(value) {
2207
- return APP_BUILD_TYPES.includes(value);
2208
- }
2209
- function getBuildTypeExamples(commandName) {
2210
- return RESOLVED_APP_BUILD_TYPES.map((buildType) => {
2211
- return `prisma-cli app ${commandName} --build-type ${buildType}${buildType === "bun" ? " --entry server.ts" : ""}`;
2212
- });
2213
- }
2214
- function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
2215
- if (buildType !== "auto" && !ENTRYPOINT_BUILD_TYPES.includes(buildType) && entrypoint) {
2216
- 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");
2217
- 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");
2218
- }
2219
- }
2220
- /**
2221
- * Resolves the framework for `app run` with the same detection as deploy, so
2222
- * a repo that deploys without flags also runs without flags. Local dev server
2223
- * support is intentionally narrower than deploy build support: only Next.js
2224
- * and Bun/Hono have dev servers in the current preview.
2225
- */
2226
- async function resolveLocalRunFramework(context, options) {
2227
- if (options.requestedBuildType === "auto" && options.entrypoint) return frameworkFromUserFacingValue("bun", "set by --entry");
2228
- if (LOCAL_DEV_BUILD_TYPES.includes(options.requestedBuildType)) {
2229
- if (options.configFramework && computeFrameworkToBuildType(options.configFramework) === options.requestedBuildType) return frameworkFromUserFacingValue(options.configFramework, `set by ${COMPUTE_CONFIG_FILENAME}`);
2230
- return frameworkFromUserFacingValue(options.requestedBuildType, "set by --build-type");
2231
- }
2232
- const detected = await detectDeployFramework(options.appDir, context.runtime.signal);
2233
- if (detected && LOCAL_DEV_BUILD_TYPES.includes(detected.buildType)) return detected;
2234
- throw usageError("App run requires an explicit framework when detection is ambiguous", "This preview only starts local dev servers for clear Next.js or Bun project shapes.", "Pass --build-type nextjs for a Next.js app, or pass --build-type bun with --entry <path> for a Bun app.", ["prisma-cli app run --build-type nextjs", "prisma-cli app run --build-type bun --entry server.ts"], "app");
2235
- }
2236
- function parseLocalPort(requestedPort) {
2237
- if (!requestedPort) return DEFAULT_LOCAL_DEV_PORT;
2238
- const port = Number.parseInt(requestedPort, 10);
2239
- if (!Number.isInteger(port) || port <= 0 || port > 65535) throw usageError(`Invalid port "${requestedPort}"`, "Port must be an integer between 1 and 65535.", "Pass --port <number> with a valid local port value.", ["prisma-cli app run --port 3000"], "app");
2240
- return port;
2241
- }
2242
- function parseDeployPortMapping(requestedPort) {
2243
- if (!requestedPort) return;
2244
- return { http: parseDeployHttpPort(requestedPort) };
2245
- }
2246
- function parseDeployHttpPort(requestedPort) {
2247
- const port = Number.parseInt(requestedPort, 10);
2248
- if (!Number.isInteger(port) || port <= 0 || port > 65535) throw usageError(`Invalid HTTP port "${requestedPort}"`, "HTTP port must be an integer between 1 and 65535.", "Pass --http-port <number> with a valid port value.", ["prisma-cli app deploy --http-port 3000"], "app");
2249
- return port;
2250
- }
2251
- function parseDeployRegion(requestedRegion, source) {
2252
- const region = requestedRegion.trim();
2253
- if (region.length === 0) throw usageError("Invalid app region", `The app region ${source} must be a non-empty region id.`, "Pass a Prisma Compute region id.", ["prisma-cli app deploy --region eu-central-1"], "app");
2254
- if (!COMPUTE_REGION_IDS.has(region)) throw usageError("Invalid app region", `The app region ${source} must be one of: ${COMPUTE_REGIONS.join(", ")}.`, "Pass a supported Prisma Compute region id.", ["prisma-cli app deploy --region eu-central-1"], "app");
2255
- return region;
2256
- }
2257
- function normalizeDeployRegionInput(region) {
2258
- if (!region) return;
2259
- return {
2260
- ...region,
2261
- value: parseDeployRegion(region.value, region.annotation)
2262
- };
2263
- }
2264
- function deployFailedError(summary, error, nextSteps) {
2265
- return new CliError({
2266
- code: "DEPLOY_FAILED",
2267
- domain: "app",
2268
- summary,
2269
- why: error instanceof Error ? error.message : String(error),
2270
- fix: "Retry the command, or rerun with --trace for more detailed diagnostics.",
2271
- debug: formatDebugDetails(error),
2272
- exitCode: 1,
2273
- nextSteps
2274
- });
2275
- }
2276
- function describeStalledDeployPhase(progress) {
2277
- if (progress.uploadCompleted) return "The artifact uploaded, but the deployment did not start.";
2278
- if (progress.archiveReady) return "The app built locally, but the artifact did not finish uploading.";
2279
- return "The app built locally, but the deployment did not start.";
2280
- }
2281
- function localBuildFailedError(why, debug) {
2282
- const standaloneOutputFailure = isNextStandaloneOutputFailure(why);
2283
- const fix = standaloneOutputFailure ? "Add output: \"standalone\" to next.config.*, then rerun deploy." : "Inspect the build output above, fix the error, and redeploy.";
2284
- const nextSteps = standaloneOutputFailure ? ["Add output: \"standalone\" to next.config.*, then rerun prisma-cli app deploy"] : [];
2285
- const nextActions = standaloneOutputFailure ? [{
2286
- kind: "edit-file",
2287
- journey: "deploy-app",
2288
- label: "Add Next.js standalone output",
2289
- reason: "Prisma Compute needs Next.js standalone output to build a deployable server artifact."
2290
- }, {
2291
- kind: "run-command",
2292
- journey: "deploy-app",
2293
- label: "Rerun deploy",
2294
- command: "prisma-cli app deploy"
2295
- }] : [];
2296
- return new CliError({
2297
- code: "BUILD_FAILED",
2298
- domain: "app",
2299
- summary: "Build failed locally.",
2300
- why,
2301
- fix,
2302
- debug,
2303
- meta: { phase: "build" },
2304
- humanLines: [
2305
- "Build failed locally.",
2306
- "",
2307
- `✗ Built ${why}`,
2308
- "",
2309
- `Fix: ${fix}`
2310
- ],
2311
- exitCode: 1,
2312
- nextSteps,
2313
- nextActions
2314
- });
2315
- }
2316
- function appDeployFailedError(error, progress) {
2317
- const why = error instanceof Error ? error.message : String(error);
2318
- const debug = formatDebugDetails(error);
2319
- if (progress.buildStarted && !progress.buildCompleted) return localBuildFailedError(why, debug);
2320
- if (!progress.buildStarted) return deployFailedError("App deploy failed", error, ["prisma-cli app deploy"]);
2321
- const phaseHeadline = progress.containerLive ? "The deployment started, but the app is not ready yet." : "Deploy failed after the build completed.";
2322
- const recoveryLines = progress.deploymentId ? ["See what happened", `prisma-cli app logs --deployment ${progress.deploymentId}`] : ["Fix", "Retry the command, or rerun with --trace for more detailed diagnostics."];
2323
- const urlLines = progress.deploymentUrl ? [
2324
- "",
2325
- "URL",
2326
- progress.deploymentUrl
2327
- ] : [];
2328
- const humanLines = progress.containerLive ? [
2329
- phaseHeadline,
2330
- "",
2331
- "This is usually a missing env var, a failed DB connection,",
2332
- "or a crash on startup.",
2333
- "",
2334
- ...recoveryLines,
2335
- ...urlLines
2336
- ] : [
2337
- phaseHeadline,
2338
- "",
2339
- describeStalledDeployPhase(progress),
2340
- "",
2341
- ...recoveryLines
2342
- ];
2343
- return new CliError({
2344
- code: "DEPLOY_FAILED",
2345
- domain: "app",
2346
- summary: phaseHeadline,
2347
- why,
2348
- fix: progress.deploymentId ? `Inspect logs with prisma-cli app logs --deployment ${progress.deploymentId}.` : "Retry the command, or rerun with --trace for more detailed diagnostics.",
2349
- debug,
2350
- meta: {
2351
- phase: progress.containerLive ? "runtime_ready" : "deploy",
2352
- deploymentId: progress.deploymentId,
2353
- deploymentUrl: progress.deploymentUrl
2354
- },
2355
- humanLines,
2356
- exitCode: 1,
2357
- nextSteps: []
2358
- });
2359
- }
2360
- function localResolutionPinStaleError() {
2361
- return new CliError({
2362
- code: "LOCAL_STATE_STALE",
2363
- domain: "project",
2364
- summary: "Local project binding is stale",
2365
- why: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
2366
- fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}, then choose a Project explicitly.`,
2367
- meta: { pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH },
2368
- exitCode: 1,
2369
- nextSteps: [
2370
- "prisma-cli project list",
2371
- "prisma-cli project link <id-or-name>",
2372
- "prisma-cli app deploy --project <id-or-name>"
2373
- ]
2374
- });
2375
- }
2376
- function localPinReadErrorToDeployError(error) {
2377
- return matchError(error, {
2378
- LocalResolutionPinInvalidJsonError: () => localResolutionPinStaleError(),
2379
- LocalResolutionPinInvalidShapeError: () => localResolutionPinStaleError(),
2380
- LocalResolutionPinReadAbortedError: (error) => {
2381
- throw error;
2382
- },
2383
- UnhandledException: (error) => {
2384
- throw error;
2385
- }
2386
- });
2387
- }
2388
- function readDeployEnvOverride(context, name) {
2389
- const value = context.runtime.env[name]?.trim();
2390
- return value ? value : void 0;
2391
- }
2392
- function projectSetupRequiredError(projects, suggestedName) {
2393
- const createCommand = `prisma-cli app deploy --create-project ${formatCommandArgument(suggestedName.name)}`;
2394
- return new CliError({
2395
- code: "PROJECT_SETUP_REQUIRED",
2396
- domain: "project",
2397
- summary: "Choose a Project before deploying this directory",
2398
- why: "This directory is not linked to a Prisma Project, and deploy will not choose or create one implicitly.",
2399
- fix: "Choose an existing Project with --project, create one with --create-project, or rerun interactively to pick from the setup list.",
2400
- meta: {
2401
- candidates: sortProjects(projects).map((project) => ({
2402
- id: project.id,
2403
- name: project.name
2404
- })),
2405
- suggestedProjectName: suggestedName.name,
2406
- suggestedProjectNameSource: suggestedName.source,
2407
- recoveryCommands: ["prisma-cli app deploy --project <id-or-name>", createCommand]
2408
- },
2409
- exitCode: 1,
2410
- nextSteps: [
2411
- "prisma-cli project list",
2412
- "prisma-cli app deploy --project <id-or-name>",
2413
- createCommand
2414
- ],
2415
- nextActions: buildProjectSetupNextActions({
2416
- commandName: "app deploy",
2417
- createCommand,
2418
- 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."
2419
- })
2420
- });
2421
- }
2422
- function isNextStandaloneOutputFailure(message) {
2423
- return NEXTJS_MENTION.test(message) && STANDALONE_OUTPUT_MENTION.test(message);
2424
- }
2425
- function noDeploymentsError(summary, why) {
2426
- return new CliError({
2427
- code: "NO_DEPLOYMENTS",
2428
- domain: "app",
2429
- summary,
2430
- why,
2431
- fix: "Run prisma-cli app deploy first, or use prisma-cli app show to inspect the current app state.",
2432
- exitCode: 1,
2433
- nextSteps: ["prisma-cli app deploy", "prisma-cli app show"]
2434
- });
2435
- }
2436
- function buildFailedError(summary, error) {
2437
- return new CliError({
2438
- code: "BUILD_FAILED",
2439
- domain: "app",
2440
- summary,
2441
- why: error instanceof Error ? error.message : String(error),
2442
- fix: "Inspect the framework output, fix the build issue, and rerun prisma-cli app build.",
2443
- debug: formatDebugDetails(error),
2444
- exitCode: 1,
2445
- nextSteps: ["prisma-cli app build", "prisma-cli app deploy"]
2446
- });
2447
- }
2448
- function runFailedError(summary, error, exitCode = 1) {
2449
- return new CliError({
2450
- code: "RUN_FAILED",
2451
- domain: "app",
2452
- summary,
2453
- why: error instanceof Error ? error.message : String(error),
2454
- fix: "Inspect the framework output above, fix the issue, and rerun prisma-cli app run.",
2455
- exitCode,
2456
- nextSteps: ["prisma-cli app run"]
2457
- });
2458
- }
2459
- function formatFrameworkName(framework) {
2460
- return framework === "nextjs" ? "Next.js" : "Bun";
2461
- }
2462
- function isAutoBuildDetectionError(error) {
2463
- return error instanceof Error && error.message.startsWith("Entrypoint is required.");
2464
- }
2465
- function formatBuildTypeName(buildType) {
2466
- if (buildType === "auto") return "Auto";
2467
- for (let index = FRAMEWORKS.length - 1; index >= 0; index -= 1) {
2468
- const framework = FRAMEWORKS[index];
2469
- if (framework?.buildType === buildType) return framework.displayName;
2470
- }
2471
- return buildType;
2472
- }
2473
- function removeFailedError(summary, error, nextSteps) {
2474
- return new CliError({
2475
- code: "REMOVE_FAILED",
2476
- domain: "app",
2477
- summary,
2478
- why: error instanceof Error ? error.message : String(error),
2479
- fix: "Retry the command, or rerun with --trace for more detailed diagnostics.",
2480
- debug: formatDebugDetails(error),
2481
- exitCode: 1,
2482
- nextSteps
2483
- });
2484
- }
2485
- function localStateCleanupWarning(target, error) {
2486
- return `The app was removed remotely, but the local ${target} state could not be cleared: ${error instanceof Error ? error.message : String(error)}`;
2487
- }
2488
- function formatDebugDetails(error) {
2489
- if (error instanceof Error) return error.stack ?? error.message;
2490
- return typeof error === "string" ? error : null;
2491
- }
2492
- function isMissingProjectError(error) {
2493
- return error instanceof Error && error.message === "Resource Not Found";
2494
- }
2495
- function findAppByName(apps, name) {
2496
- return apps.find((app) => app.name === name);
2497
- }
2498
- function findAppsByName(apps, name) {
2499
- return apps.filter((app) => app.name === name);
2500
- }
2501
- function sortApps(apps) {
2502
- return apps.slice().sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
2503
- }
2504
- function toOptionalEnvVars(envVars) {
2505
- return Object.keys(envVars).length > 0 ? envVars : void 0;
2506
- }
2507
- //#endregion
2508
- export { detectDeployFramework, runAppBuild, runAppDeploy, runAppDomainAdd, runAppDomainRemove, runAppDomainRetry, runAppDomainShow, runAppDomainWait, runAppListDeploys, runAppLogs, runAppOpen, runAppPromote, runAppRemove, runAppRollback, runAppRun, runAppShow, runAppShowDeploy };