@prisma/cli 3.0.0-dev.99.1 → 8.0.0-rc.2

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