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

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 (65) hide show
  1. package/README.md +18 -3
  2. package/dist/adapters/git.js +8 -3
  3. package/dist/adapters/local-state.js +12 -4
  4. package/dist/adapters/mock-api.js +81 -2
  5. package/dist/adapters/token-storage.js +63 -22
  6. package/dist/cli.js +17 -2
  7. package/dist/cli2.js +7 -3
  8. package/dist/commands/app/index.js +26 -13
  9. package/dist/commands/branch/index.js +2 -27
  10. package/dist/commands/database/index.js +159 -0
  11. package/dist/commands/env.js +8 -4
  12. package/dist/commands/project/index.js +28 -2
  13. package/dist/controllers/app-env-api.js +54 -0
  14. package/dist/controllers/app-env-file.js +181 -0
  15. package/dist/controllers/app-env.js +284 -132
  16. package/dist/controllers/app.js +493 -344
  17. package/dist/controllers/auth.js +8 -8
  18. package/dist/controllers/branch.js +78 -48
  19. package/dist/controllers/database.js +318 -0
  20. package/dist/controllers/project.js +302 -75
  21. package/dist/lib/app/branch-database-deploy.js +373 -0
  22. package/dist/lib/app/branch-database.js +316 -0
  23. package/dist/lib/app/bun-project.js +12 -5
  24. package/dist/lib/app/deploy-output.js +10 -1
  25. package/dist/lib/app/env-config.js +1 -1
  26. package/dist/lib/app/env-file.js +82 -0
  27. package/dist/lib/app/env-vars.js +28 -2
  28. package/dist/lib/app/local-dev.js +34 -18
  29. package/dist/lib/app/preview-branch-database.js +102 -0
  30. package/dist/lib/app/preview-build-settings.js +385 -0
  31. package/dist/lib/app/preview-build.js +272 -81
  32. package/dist/lib/app/preview-provider.js +163 -54
  33. package/dist/lib/app/production-deploy-gate.js +161 -0
  34. package/dist/lib/auth/auth-ops.js +69 -19
  35. package/dist/lib/auth/guard.js +3 -2
  36. package/dist/lib/auth/login.js +109 -14
  37. package/dist/lib/database/provider.js +167 -0
  38. package/dist/lib/diagnostics.js +15 -0
  39. package/dist/lib/git/local-branch.js +41 -0
  40. package/dist/lib/git/local-status.js +57 -0
  41. package/dist/lib/project/interactive-setup.js +56 -0
  42. package/dist/lib/project/local-pin.js +182 -33
  43. package/dist/lib/project/resolution.js +287 -105
  44. package/dist/lib/project/setup.js +132 -0
  45. package/dist/presenters/app-env.js +149 -14
  46. package/dist/presenters/app.js +170 -20
  47. package/dist/presenters/auth.js +19 -6
  48. package/dist/presenters/branch.js +37 -102
  49. package/dist/presenters/database.js +274 -0
  50. package/dist/presenters/project.js +100 -47
  51. package/dist/presenters/verbose-context.js +64 -0
  52. package/dist/shell/command-arguments.js +6 -0
  53. package/dist/shell/command-meta.js +139 -16
  54. package/dist/shell/command-runner.js +38 -8
  55. package/dist/shell/diagnostics-output.js +63 -0
  56. package/dist/shell/errors.js +14 -1
  57. package/dist/shell/output.js +13 -2
  58. package/dist/shell/runtime.js +3 -3
  59. package/dist/shell/ui.js +23 -1
  60. package/dist/shell/update-check.js +247 -0
  61. package/dist/use-cases/auth.js +15 -4
  62. package/dist/use-cases/branch.js +20 -68
  63. package/dist/use-cases/create-cli-gateways.js +2 -17
  64. package/dist/use-cases/project.js +2 -1
  65. package/package.json +12 -10
@@ -1,5 +1,6 @@
1
1
  import { envVarNames } from "./env-vars.js";
2
2
  import { PreviewBuildStrategy } from "./preview-build.js";
3
+ import { createBranchDatabase, createEnvironmentVariable, deleteBranchDatabase, deleteEnvironmentVariable, listEnvironmentVariables, updateEnvironmentVariable } from "./preview-branch-database.js";
3
4
  import path from "node:path";
4
5
  import { ApiError, CancelledError, ComputeClient, streamLogs } from "@prisma/compute-sdk";
5
6
  //#region src/lib/app/preview-provider.ts
@@ -19,7 +20,10 @@ function createPreviewAppProvider(client, options) {
19
20
  const sdk = new ComputeClient(client);
20
21
  return {
21
22
  async createProject(options) {
22
- const projectResult = await sdk.createProject({ name: options.name });
23
+ const projectResult = await sdk.createProject({
24
+ name: options.name,
25
+ signal: options.signal
26
+ });
23
27
  if (projectResult.isErr()) throw new Error(projectResult.error.message);
24
28
  return {
25
29
  id: projectResult.value.id,
@@ -29,17 +33,52 @@ function createPreviewAppProvider(client, options) {
29
33
  async listApps(projectId, options) {
30
34
  return listComputeServices(client, {
31
35
  projectId,
32
- branchGitName: options?.branchName
36
+ branchGitName: options?.branchName,
37
+ signal: options?.signal
38
+ });
39
+ },
40
+ async resolveBranch(projectId, options) {
41
+ const branch = await resolveOrCreateBranch(client, {
42
+ projectId,
43
+ gitName: options.branchName,
44
+ signal: options.signal
33
45
  });
46
+ return {
47
+ id: branch.id,
48
+ name: branch.gitName,
49
+ role: branch.role
50
+ };
51
+ },
52
+ async createBranchDatabase(options) {
53
+ return createBranchDatabase(client, options);
54
+ },
55
+ async deleteBranchDatabase(options) {
56
+ return deleteBranchDatabase(client, options);
57
+ },
58
+ async listEnvironmentVariables(options) {
59
+ return listEnvironmentVariables(client, options);
60
+ },
61
+ async createEnvironmentVariable(options) {
62
+ return createEnvironmentVariable(client, options);
63
+ },
64
+ async updateEnvironmentVariable(options) {
65
+ return updateEnvironmentVariable(client, options);
34
66
  },
35
- async removeApp(appId) {
36
- const appResult = await sdk.showService({ serviceId: appId });
67
+ async deleteEnvironmentVariable(options) {
68
+ return deleteEnvironmentVariable(client, options);
69
+ },
70
+ async removeApp(appId, options) {
71
+ const appResult = await sdk.showService({
72
+ serviceId: appId,
73
+ signal: options?.signal
74
+ });
37
75
  if (appResult.isErr()) throw new Error(appResult.error.message);
38
76
  const destroyResult = await sdk.destroyService({
39
77
  serviceId: appId,
40
78
  keepService: false,
41
79
  timeoutSeconds: 120,
42
- pollIntervalMs: 2e3
80
+ pollIntervalMs: 2e3,
81
+ signal: options?.signal
43
82
  });
44
83
  if (destroyResult.isErr()) throw new Error(destroyResult.error.message);
45
84
  return {
@@ -47,17 +86,18 @@ function createPreviewAppProvider(client, options) {
47
86
  name: appResult.value.name
48
87
  };
49
88
  },
50
- async listDomains(appId) {
51
- return listComputeServiceDomains(client, appId);
89
+ async listDomains(appId, options) {
90
+ return listComputeServiceDomains(client, appId, options?.signal);
52
91
  },
53
92
  async addDomain(options) {
54
93
  const result = await client.POST("/v1/compute-services/{computeServiceId}/domains", {
55
94
  params: { path: { computeServiceId: options.appId } },
56
- body: { hostname: options.hostname }
95
+ body: { hostname: options.hostname },
96
+ signal: options.signal
57
97
  });
58
98
  if (result.error || !result.data) {
59
99
  if (result.response.status === 409) {
60
- const existing = (await listComputeServiceDomains(client, options.appId)).find((domain) => sameHostname(domain.hostname, options.hostname));
100
+ const existing = (await listComputeServiceDomains(client, options.appId, options.signal)).find((domain) => sameHostname(domain.hostname, options.hostname));
61
101
  if (existing) return {
62
102
  domain: existing,
63
103
  existing: true
@@ -70,17 +110,26 @@ function createPreviewAppProvider(client, options) {
70
110
  existing: false
71
111
  };
72
112
  },
73
- async showDomain(domainId) {
74
- const result = await client.GET("/v1/domains/{domainId}", { params: { path: { domainId } } });
113
+ async showDomain(domainId, options) {
114
+ const result = await client.GET("/v1/domains/{domainId}", {
115
+ params: { path: { domainId } },
116
+ signal: options?.signal
117
+ });
75
118
  if (result.error || !result.data) throw domainApiCallError("Failed to show custom domain", result.response, result.error);
76
119
  return normalizeDomainRecord(result.data.data);
77
120
  },
78
- async removeDomain(domainId) {
79
- const result = await client.DELETE("/v1/domains/{domainId}", { params: { path: { domainId } } });
121
+ async removeDomain(domainId, options) {
122
+ const result = await client.DELETE("/v1/domains/{domainId}", {
123
+ params: { path: { domainId } },
124
+ signal: options?.signal
125
+ });
80
126
  if (result.error) throw domainApiCallError("Failed to remove custom domain", result.response, result.error);
81
127
  },
82
- async retryDomain(domainId) {
83
- const result = await client.POST("/v1/domains/{domainId}/retry", { params: { path: { domainId } } });
128
+ async retryDomain(domainId, options) {
129
+ const result = await client.POST("/v1/domains/{domainId}/retry", {
130
+ params: { path: { domainId } },
131
+ signal: options?.signal
132
+ });
84
133
  if (result.error || !result.data) throw domainApiCallError("Failed to retry custom domain", result.response, result.error);
85
134
  return normalizeDomainRecord(result.data.data);
86
135
  },
@@ -90,6 +139,7 @@ function createPreviewAppProvider(client, options) {
90
139
  versionId: options.deploymentId,
91
140
  timeoutSeconds: 120,
92
141
  pollIntervalMs: 2e3,
142
+ signal: options.signal,
93
143
  progress: options.progress
94
144
  });
95
145
  if (promoteResult.isErr()) throw new Error(promoteResult.error.message);
@@ -103,7 +153,8 @@ function createPreviewAppProvider(client, options) {
103
153
  projectId: options.projectId,
104
154
  branchName: options.branchName,
105
155
  appName: options.appName,
106
- region: options.region
156
+ region: options.region,
157
+ signal: options.signal
107
158
  }) : {
108
159
  appId: void 0,
109
160
  appName: options.appName,
@@ -113,7 +164,9 @@ function createPreviewAppProvider(client, options) {
113
164
  strategy: new PreviewBuildStrategy({
114
165
  appPath: path.resolve(options.cwd),
115
166
  entrypoint: options.entrypoint,
116
- buildType: options.buildType
167
+ buildType: options.buildType,
168
+ signal: options.signal,
169
+ buildSettings: options.buildSettings
117
170
  }),
118
171
  projectId: options.projectId,
119
172
  serviceId: resolvedApp.appId,
@@ -124,6 +177,7 @@ function createPreviewAppProvider(client, options) {
124
177
  timeoutSeconds: 120,
125
178
  pollIntervalMs: 2e3,
126
179
  interaction: options.interaction,
180
+ signal: options.signal,
127
181
  progress: options.progress
128
182
  });
129
183
  if (deployResult.isErr()) throw new Error(deployResult.error.message);
@@ -150,6 +204,7 @@ function createPreviewAppProvider(client, options) {
150
204
  envVars: options.envVars,
151
205
  timeoutSeconds: 120,
152
206
  pollIntervalMs: 2e3,
207
+ signal: options.signal,
153
208
  progress: options.progress
154
209
  });
155
210
  if (updateResult.isErr()) throw new Error(updateResult.error.message);
@@ -158,10 +213,17 @@ function createPreviewAppProvider(client, options) {
158
213
  versionId: updateResult.value.versionId,
159
214
  timeoutSeconds: 120,
160
215
  pollIntervalMs: 2e3,
216
+ signal: options.signal,
161
217
  progress: options.promoteProgress
162
218
  });
163
219
  if (promoteResult.isErr()) throw new Error(promoteResult.error.message);
164
- const [serviceResult, versionResult] = await Promise.all([sdk.showService({ serviceId: options.appId }), sdk.showVersion({ versionId: updateResult.value.versionId })]);
220
+ const [serviceResult, versionResult] = await Promise.all([sdk.showService({
221
+ serviceId: options.appId,
222
+ signal: options.signal
223
+ }), sdk.showVersion({
224
+ versionId: updateResult.value.versionId,
225
+ signal: options.signal
226
+ })]);
165
227
  if (serviceResult.isErr()) throw new Error(serviceResult.error.message);
166
228
  if (versionResult.isErr()) throw new Error(versionResult.error.message);
167
229
  return {
@@ -184,7 +246,13 @@ function createPreviewAppProvider(client, options) {
184
246
  };
185
247
  },
186
248
  async listAppEnvNames(options) {
187
- const [serviceResult, versionResult] = await Promise.all([sdk.showService({ serviceId: options.appId }), sdk.showVersion({ versionId: options.deploymentId })]);
249
+ const [serviceResult, versionResult] = await Promise.all([sdk.showService({
250
+ serviceId: options.appId,
251
+ signal: options.signal
252
+ }), sdk.showVersion({
253
+ versionId: options.deploymentId,
254
+ signal: options.signal
255
+ })]);
188
256
  if (serviceResult.isErr()) throw new Error(serviceResult.error.message);
189
257
  if (versionResult.isErr()) throw new Error(versionResult.error.message);
190
258
  return {
@@ -206,8 +274,14 @@ function createPreviewAppProvider(client, options) {
206
274
  variables: envVarNames(versionResult.value.envVars)
207
275
  };
208
276
  },
209
- async listDeployments(appId) {
210
- const [appResult, versionsResult] = await Promise.all([sdk.showService({ serviceId: appId }), sdk.listVersions({ serviceId: appId })]);
277
+ async listDeployments(appId, options) {
278
+ const [appResult, versionsResult] = await Promise.all([sdk.showService({
279
+ serviceId: appId,
280
+ signal: options?.signal
281
+ }), sdk.listVersions({
282
+ serviceId: appId,
283
+ signal: options?.signal
284
+ })]);
211
285
  if (appResult.isErr()) throw new Error(appResult.error.message);
212
286
  if (versionsResult.isErr()) throw new Error(versionsResult.error.message);
213
287
  return {
@@ -230,14 +304,17 @@ function createPreviewAppProvider(client, options) {
230
304
  }))
231
305
  };
232
306
  },
233
- async showDeployment(deploymentId) {
234
- const deploymentResult = await sdk.showVersion({ versionId: deploymentId });
307
+ async showDeployment(deploymentId, options) {
308
+ const deploymentResult = await sdk.showVersion({
309
+ versionId: deploymentId,
310
+ signal: options?.signal
311
+ });
235
312
  if (deploymentResult.isErr()) {
236
313
  if (ApiError.is(deploymentResult.error) && deploymentResult.error.statusCode === 404) return null;
237
314
  throw new Error(deploymentResult.error.message);
238
315
  }
239
316
  return {
240
- app: await findAppForDeployment(sdk, deploymentId),
317
+ app: await findAppForDeployment(sdk, deploymentId, options?.signal),
241
318
  deployment: {
242
319
  id: deploymentResult.value.id,
243
320
  status: deploymentResult.value.status,
@@ -263,22 +340,28 @@ function createPreviewAppProvider(client, options) {
263
340
  };
264
341
  }
265
342
  async function listBranches(client, options) {
266
- const result = await client.GET("/v1/projects/{projectId}/branches", { params: {
267
- path: { projectId: options.projectId },
268
- query: { gitName: options.gitName }
269
- } });
343
+ const result = await client.GET("/v1/projects/{projectId}/branches", {
344
+ params: {
345
+ path: { projectId: options.projectId },
346
+ query: { gitName: options.gitName }
347
+ },
348
+ signal: options.signal
349
+ });
270
350
  if (result.error || !result.data) throw apiCallError("Failed to list branches", result.response, result.error);
271
- return result.data.data;
351
+ return result.data.data.map((branch) => ({
352
+ id: branch.id,
353
+ gitName: branch.gitName,
354
+ isDefault: branch.isDefault,
355
+ role: branch.role
356
+ }));
272
357
  }
273
358
  async function resolveOrCreateBranch(client, options) {
274
359
  const existing = (await listBranches(client, options))[0];
275
360
  if (existing) return existing;
276
361
  const result = await client.POST("/v1/projects/{projectId}/branches", {
277
362
  params: { path: { projectId: options.projectId } },
278
- body: {
279
- gitName: options.gitName,
280
- isDefault: options.gitName === "main"
281
- }
363
+ body: { gitName: options.gitName },
364
+ signal: options.signal
282
365
  });
283
366
  if (result.error || !result.data) {
284
367
  if (result.response.status === 409) {
@@ -287,17 +370,26 @@ async function resolveOrCreateBranch(client, options) {
287
370
  }
288
371
  throw apiCallError(`Failed to create branch "${options.gitName}"`, result.response, result.error);
289
372
  }
290
- return result.data.data;
373
+ const branch = result.data.data;
374
+ return {
375
+ id: branch.id,
376
+ gitName: branch.gitName,
377
+ isDefault: branch.isDefault,
378
+ role: branch.role
379
+ };
291
380
  }
292
381
  async function listComputeServices(client, options) {
293
382
  const services = [];
294
383
  let cursor;
295
384
  while (true) {
296
- const result = await client.GET("/v1/compute-services", { params: { query: {
297
- projectId: options.projectId,
298
- branchGitName: options.branchGitName,
299
- cursor
300
- } } });
385
+ const result = await client.GET("/v1/compute-services", {
386
+ params: { query: {
387
+ projectId: options.projectId,
388
+ branchGitName: options.branchGitName,
389
+ cursor
390
+ } },
391
+ signal: options.signal
392
+ });
301
393
  if (result.error || !result.data) throw apiCallError("Failed to list apps", result.response, result.error);
302
394
  services.push(...result.data.data);
303
395
  if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
@@ -312,8 +404,11 @@ async function listComputeServices(client, options) {
312
404
  liveUrl: toAbsoluteUrl(service.serviceEndpointDomain ?? null)
313
405
  }));
314
406
  }
315
- async function listComputeServiceDomains(client, computeServiceId) {
316
- const result = await client.GET("/v1/compute-services/{computeServiceId}/domains", { params: { path: { computeServiceId } } });
407
+ async function listComputeServiceDomains(client, computeServiceId, signal) {
408
+ const result = await client.GET("/v1/compute-services/{computeServiceId}/domains", {
409
+ params: { path: { computeServiceId } },
410
+ signal
411
+ });
317
412
  if (result.error || !result.data) throw domainApiCallError("Failed to list custom domains", result.response, result.error);
318
413
  return result.data.data.map((domain) => normalizeDomainRecord(domain));
319
414
  }
@@ -355,19 +450,24 @@ function normalizeHostnameForComparison(hostname) {
355
450
  async function createBranchApp(client, options) {
356
451
  const branch = await resolveOrCreateBranch(client, {
357
452
  projectId: options.projectId,
358
- gitName: options.branchName
453
+ gitName: options.branchName,
454
+ signal: options.signal
455
+ });
456
+ const result = await client.POST("/v1/compute-services", {
457
+ body: {
458
+ projectId: options.projectId,
459
+ branchId: branch.id,
460
+ displayName: options.appName,
461
+ ...options.region ? { regionId: options.region } : {}
462
+ },
463
+ signal: options.signal
359
464
  });
360
- const result = await client.POST("/v1/compute-services", { body: {
361
- projectId: options.projectId,
362
- branchId: branch.id,
363
- displayName: options.appName,
364
- ...options.region ? { regionId: options.region } : {}
365
- } });
366
465
  if (result.error || !result.data) {
367
466
  if (result.response.status === 409) {
368
467
  const matched = (await listComputeServices(client, {
369
468
  projectId: options.projectId,
370
- branchGitName: options.branchName
469
+ branchGitName: options.branchName,
470
+ signal: options.signal
371
471
  })).find((app) => app.name === options.appName);
372
472
  if (matched) return {
373
473
  appId: matched.id,
@@ -399,14 +499,20 @@ function domainApiCallError(summary, response, error) {
399
499
  hint: error.error?.hint ?? null
400
500
  });
401
501
  }
402
- async function findAppForDeployment(sdk, deploymentId) {
403
- const projectsResult = await sdk.listProjects();
502
+ async function findAppForDeployment(sdk, deploymentId, signal) {
503
+ const projectsResult = await sdk.listProjects({ signal });
404
504
  if (projectsResult.isErr()) throw new Error(projectsResult.error.message);
405
505
  for (const project of projectsResult.value) {
406
- const servicesResult = await sdk.listServices({ projectId: project.id });
506
+ const servicesResult = await sdk.listServices({
507
+ projectId: project.id,
508
+ signal
509
+ });
407
510
  if (servicesResult.isErr()) throw new Error(servicesResult.error.message);
408
511
  for (const service of servicesResult.value) {
409
- const detailResult = await sdk.showService({ serviceId: service.id });
512
+ const detailResult = await sdk.showService({
513
+ serviceId: service.id,
514
+ signal
515
+ });
410
516
  if (detailResult.isErr()) throw new Error(detailResult.error.message);
411
517
  const app = {
412
518
  id: detailResult.value.id,
@@ -416,7 +522,10 @@ async function findAppForDeployment(sdk, deploymentId) {
416
522
  liveUrl: toAbsoluteUrl(detailResult.value.serviceEndpointDomain ?? null)
417
523
  };
418
524
  if (app.liveDeploymentId === deploymentId) return app;
419
- const versionsResult = await sdk.listVersions({ serviceId: service.id });
525
+ const versionsResult = await sdk.listVersions({
526
+ serviceId: service.id,
527
+ signal
528
+ });
420
529
  if (versionsResult.isErr()) throw new Error(versionsResult.error.message);
421
530
  if (versionsResult.value.some((version) => version.id === deploymentId)) return app;
422
531
  }
@@ -0,0 +1,161 @@
1
+ import { CliError } from "../../shell/errors.js";
2
+ import { canPrompt } from "../../shell/runtime.js";
3
+ import { confirmPrompt } from "../../shell/prompt.js";
4
+ //#region src/lib/app/production-deploy-gate.ts
5
+ async function enforceProductionDeployGate(context, provider, options) {
6
+ if (options.branchKind !== "production") return { firstProductionDeploy: false };
7
+ if (!options.appId) {
8
+ renderFirstProductionDeployLine(context, options.appName);
9
+ return { firstProductionDeploy: true };
10
+ }
11
+ const currentLiveDeployment = resolveCurrentProductionDeployment(await provider.listDeployments(options.appId).catch((error) => {
12
+ throw productionDeployInspectionFailedError(error);
13
+ }));
14
+ if (!currentLiveDeployment) {
15
+ renderFirstProductionDeployLine(context, options.appName);
16
+ return { firstProductionDeploy: true };
17
+ }
18
+ if (!options.prod) throw productionDeployRequiresFlagError();
19
+ if (context.flags.yes) {
20
+ renderProductionDeployYesLine(context);
21
+ return { firstProductionDeploy: false };
22
+ }
23
+ if (!canPrompt(context)) throw productionDeployConfirmationRequiredError(options.appName);
24
+ renderProductionDeployConfirmation(context, currentLiveDeployment);
25
+ if (!await confirmPrompt({
26
+ input: context.runtime.stdin,
27
+ output: context.output.stderr,
28
+ message: "Deploy to production?",
29
+ initialValue: false
30
+ })) throw productionDeployCancelledError();
31
+ return { firstProductionDeploy: false };
32
+ }
33
+ function resolveCurrentProductionDeployment(result) {
34
+ if (result.deployments.length === 0) return null;
35
+ if (result.app.liveDeploymentId) {
36
+ const live = result.deployments.find((deployment) => deployment.id === result.app.liveDeploymentId);
37
+ if (live) return live;
38
+ }
39
+ return result.deployments.find((deployment) => deployment.live === true) ?? result.deployments[0] ?? null;
40
+ }
41
+ function renderFirstProductionDeployLine(context, appName) {
42
+ if (context.flags.json || context.flags.quiet) return;
43
+ context.output.stderr.write(`First deploy of "${appName}" -- promoting to production.\n\n`);
44
+ }
45
+ function renderProductionDeployYesLine(context) {
46
+ if (context.flags.json || context.flags.quiet) return;
47
+ context.output.stderr.write("Deploying to production (--prod --yes).\n\n");
48
+ }
49
+ function renderProductionDeployConfirmation(context, currentLiveDeployment) {
50
+ if (context.flags.json || context.flags.quiet) return;
51
+ const lines = [
52
+ "This will deploy to production and replace the live deployment.",
53
+ "",
54
+ ` Current live: ${currentLiveDeployment.id} deployed ${formatDeploymentAge(currentLiveDeployment.createdAt)}`,
55
+ " New deploy: will be built from your local code",
56
+ ""
57
+ ];
58
+ context.output.stderr.write(`${lines.join("\n")}\n`);
59
+ }
60
+ function formatDeploymentAge(createdAt) {
61
+ const createdAtMs = Date.parse(createdAt);
62
+ if (!Number.isFinite(createdAtMs)) return createdAt;
63
+ const elapsedMs = Math.max(0, Date.now() - createdAtMs);
64
+ for (const unit of [
65
+ {
66
+ label: "day",
67
+ ms: 1440 * 60 * 1e3
68
+ },
69
+ {
70
+ label: "hour",
71
+ ms: 3600 * 1e3
72
+ },
73
+ {
74
+ label: "minute",
75
+ ms: 60 * 1e3
76
+ }
77
+ ]) if (elapsedMs >= unit.ms) {
78
+ const value = Math.floor(elapsedMs / unit.ms);
79
+ return `${value} ${unit.label}${value === 1 ? "" : "s"} ago`;
80
+ }
81
+ return "less than a minute ago";
82
+ }
83
+ function productionDeployRequiresFlagError() {
84
+ return new CliError({
85
+ code: "PROD_DEPLOY_REQUIRES_FLAG",
86
+ domain: "app",
87
+ summary: "Production deploy requires --prod",
88
+ why: "The resolved Branch is production and this App already has a production deployment.",
89
+ fix: "Re-run with --prod, or deploy from a preview Branch.",
90
+ exitCode: 2,
91
+ nextActions: [{
92
+ kind: "run-command",
93
+ journey: "deploy-app",
94
+ label: "Deploy to production",
95
+ command: "prisma-cli app deploy --prod"
96
+ }, {
97
+ kind: "run-command",
98
+ journey: "deploy-app",
99
+ label: "Create a preview branch",
100
+ commands: ["git checkout -b <branch-name>", "prisma-cli app deploy"]
101
+ }],
102
+ humanLines: [
103
+ "This would deploy to production.",
104
+ "",
105
+ "Production deploys require explicit intent. Re-run with:",
106
+ "",
107
+ " prisma app deploy --prod",
108
+ "",
109
+ "Or deploy a preview from a feature branch:",
110
+ "",
111
+ " git checkout -b <branch-name>",
112
+ " prisma app deploy"
113
+ ]
114
+ });
115
+ }
116
+ function productionDeployConfirmationRequiredError(appName) {
117
+ return new CliError({
118
+ code: "CONFIRMATION_REQUIRED",
119
+ domain: "app",
120
+ summary: "Production deploy requires confirmation in the current mode",
121
+ why: "This command cannot prompt for production deploy confirmation in the current mode.",
122
+ fix: `Pass --prod --yes to confirm deployment of "${appName}" to production.`,
123
+ exitCode: 1,
124
+ nextSteps: ["prisma-cli app deploy --prod --yes"],
125
+ nextActions: [{
126
+ kind: "run-command",
127
+ journey: "deploy-app",
128
+ label: "Deploy to production non-interactively",
129
+ command: "prisma-cli app deploy --prod --yes"
130
+ }]
131
+ });
132
+ }
133
+ function productionDeployCancelledError() {
134
+ return new CliError({
135
+ code: "CONFIRMATION_REQUIRED",
136
+ domain: "app",
137
+ summary: "Production deploy cancelled",
138
+ why: null,
139
+ fix: null,
140
+ exitCode: 0,
141
+ humanLines: ["Cancelled."]
142
+ });
143
+ }
144
+ function productionDeployInspectionFailedError(error) {
145
+ return new CliError({
146
+ code: "DEPLOY_FAILED",
147
+ domain: "app",
148
+ summary: "Failed to inspect production deployments",
149
+ why: error instanceof Error ? error.message : String(error),
150
+ fix: "Retry the command, or rerun with --trace for more detailed diagnostics.",
151
+ debug: formatDebugDetails(error),
152
+ exitCode: 1,
153
+ nextSteps: ["prisma-cli app list-deploys"]
154
+ });
155
+ }
156
+ function formatDebugDetails(error) {
157
+ if (error instanceof Error) return error.stack ?? error.message;
158
+ return typeof error === "string" ? error : null;
159
+ }
160
+ //#endregion
161
+ export { enforceProductionDeployGate };