@prisma/cli 3.0.0-beta.20 → 3.0.0-beta.22

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.
@@ -0,0 +1,92 @@
1
+ import { formatPrismaCliCommand } from "../../shell/cli-command.js";
2
+ import { CliError } from "../../shell/errors.js";
3
+ //#region src/lib/project/provider.ts
4
+ function createManagementProjectProvider(client) {
5
+ return {
6
+ async renameProject(options) {
7
+ const result = await client.PATCH("/v1/projects/{id}", {
8
+ params: { path: { id: options.projectId } },
9
+ body: { name: options.name },
10
+ signal: options.signal
11
+ });
12
+ const status = result.response?.status ?? 0;
13
+ if (status === 400 || status === 422) throw projectRenameFailedError(options.name, result.error);
14
+ if (result.error || !result.data) throw projectApiError("Failed to rename project", result.response, result.error);
15
+ const project = result.data.data;
16
+ return {
17
+ id: project.id,
18
+ name: project.name,
19
+ ...project.url ? { url: project.url } : {}
20
+ };
21
+ },
22
+ async removeProject(options) {
23
+ const result = await client.DELETE("/v1/projects/{id}", {
24
+ params: { path: { id: options.projectId } },
25
+ signal: options.signal
26
+ });
27
+ if (result.response?.status === 400) throw projectRemoveBlockedError(options.projectId, result.error);
28
+ if (result.error) throw projectApiError("Failed to remove project", result.response, result.error);
29
+ },
30
+ async transferProject(options) {
31
+ const result = await client.POST("/v1/projects/{id}/transfer", {
32
+ params: { path: { id: options.projectId } },
33
+ body: { recipientAccessToken: options.recipientAccessToken },
34
+ signal: options.signal
35
+ });
36
+ if (result.response?.status === 400) throw projectTransferRejectedError(options.projectId, result.error);
37
+ if (result.error) throw projectApiError("Failed to transfer project", result.response, result.error);
38
+ }
39
+ };
40
+ }
41
+ function projectRenameFailedError(name, error) {
42
+ return new CliError({
43
+ code: "PROJECT_RENAME_FAILED",
44
+ domain: "project",
45
+ summary: "Project rename failed",
46
+ why: error?.error?.message ?? `The platform rejected the name "${name}".`,
47
+ fix: error?.error?.hint ?? "Pass a different project name and retry the rename.",
48
+ exitCode: 1,
49
+ nextSteps: []
50
+ });
51
+ }
52
+ function projectRemoveBlockedError(projectId, error) {
53
+ return new CliError({
54
+ code: "PROJECT_REMOVE_BLOCKED",
55
+ domain: "project",
56
+ summary: "Project cannot be removed yet",
57
+ why: error?.error?.message ?? `Project "${projectId}" still has active deployments.`,
58
+ fix: "Remove the project's apps first, then retry the removal.",
59
+ exitCode: 1,
60
+ nextSteps: [formatPrismaCliCommand([
61
+ "app",
62
+ "remove",
63
+ "--app",
64
+ "<name>"
65
+ ])]
66
+ });
67
+ }
68
+ function projectTransferRejectedError(projectId, error) {
69
+ return new CliError({
70
+ code: "PROJECT_TRANSFER_REJECTED",
71
+ domain: "project",
72
+ summary: "Project transfer was rejected",
73
+ why: error?.error?.message ?? `The platform rejected the transfer of project "${projectId}", for example because the recipient token is invalid or expired.`,
74
+ fix: "Check the recipient workspace session or token and retry the transfer.",
75
+ exitCode: 1,
76
+ nextSteps: []
77
+ });
78
+ }
79
+ function projectApiError(summary, response, error) {
80
+ const status = response?.status ?? 0;
81
+ return new CliError({
82
+ code: error?.error?.code ?? "PROJECT_API_ERROR",
83
+ domain: "project",
84
+ summary,
85
+ why: error?.error?.message ?? `The Management API returned status ${status || "unknown"}.`,
86
+ fix: error?.error?.hint ?? "Re-run with --trace for the underlying API response details.",
87
+ exitCode: 1,
88
+ nextSteps: []
89
+ });
90
+ }
91
+ //#endregion
92
+ export { createManagementProjectProvider, projectRemoveBlockedError, projectRenameFailedError, projectTransferRejectedError };
@@ -1,3 +1,4 @@
1
+ import { resolvePrismaCliPackageCommandSync } from "../lib/agent/cli-command.js";
1
2
  import { renderVerboseBlock } from "../shell/ui.js";
2
3
  import { renderDeployOutputRows } from "../lib/app/deploy-output.js";
3
4
  import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
@@ -38,17 +39,31 @@ function renderAppDeploy(context, descriptor, result, options) {
38
39
  ],
39
40
  ...renderBranchDatabaseDeploySummary(context, result),
40
41
  "",
41
- ...renderDeployOutputRows(context.ui, [...result.promoted ? [] : [{
42
- label: "Promote",
43
- value: `prisma-cli app promote ${result.deployment.id}`
44
- }], {
45
- label: "Logs",
46
- value: logsCommand
47
- }]),
42
+ ...renderDeployOutputRows(context.ui, [
43
+ ...result.promoted ? [] : [{
44
+ label: "Promote",
45
+ value: `prisma-cli app promote ${result.deployment.id}`
46
+ }],
47
+ {
48
+ label: "Logs",
49
+ value: logsCommand
50
+ },
51
+ ...deployUsedComputeConfig(result) ? [] : [{
52
+ label: "Config",
53
+ value: resolvePrismaCliPackageCommandSync(context.runtime.cwd, ["init"])
54
+ }]
55
+ ]),
48
56
  ...renderDeployResolvedContextBlock(context, result),
49
57
  ...renderDeploySettingsBlock(context, result)
50
58
  ];
51
59
  }
60
+ function deployUsedComputeConfig(result) {
61
+ return result.deploySettings.config.path !== null || [
62
+ result.deploySettings.framework.source,
63
+ result.deploySettings.buildCommand.source,
64
+ result.deploySettings.outputDirectory.source
65
+ ].some((source) => source?.includes("prisma.compute"));
66
+ }
52
67
  function isAppDeployAllResult(result) {
53
68
  return "deployments" in result;
54
69
  }
@@ -236,6 +236,180 @@ function renderDatabaseConnectionRemove(context, descriptor, result) {
236
236
  function serializeDatabaseConnectionRemove(result) {
237
237
  return { connection: result.connection };
238
238
  }
239
+ function renderDatabaseUsage(context, descriptor, result) {
240
+ const lines = renderShow({
241
+ title: "Showing database usage metrics.",
242
+ descriptor,
243
+ fields: [
244
+ {
245
+ key: "project",
246
+ value: result.projectName
247
+ },
248
+ {
249
+ key: "database",
250
+ value: result.database.name
251
+ },
252
+ {
253
+ key: "id",
254
+ value: result.database.id,
255
+ tone: "dim"
256
+ },
257
+ {
258
+ key: "period",
259
+ value: `${formatUsageTimestamp(result.period.start)} to ${formatUsageTimestamp(result.period.end)}`
260
+ },
261
+ {
262
+ key: "operations",
263
+ value: `${result.metrics.operations.used} ${result.metrics.operations.unit}`
264
+ },
265
+ {
266
+ key: "storage",
267
+ value: `${result.metrics.storage.used} ${result.metrics.storage.unit}`
268
+ },
269
+ {
270
+ key: "generated",
271
+ value: formatUsageTimestamp(result.generatedAt),
272
+ tone: "dim"
273
+ }
274
+ ]
275
+ }, context.ui);
276
+ lines.push(...renderResolvedProjectContextBlock(context.ui, result.verboseContext));
277
+ return lines;
278
+ }
279
+ function serializeDatabaseUsage(result) {
280
+ return stripVerboseContext(result);
281
+ }
282
+ function renderDatabaseBackupList(context, descriptor, result) {
283
+ const ui = context.ui;
284
+ const lines = [`${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim("Listing platform-created database backups.")}`, ""];
285
+ const rail = ui.dim("│");
286
+ lines.push(`${rail} ${ui.accent("database:")} ${result.database.name}`);
287
+ if (result.retentionDays !== null) lines.push(`${rail} ${ui.accent("retention:")} ${result.retentionDays} days`);
288
+ lines.push(rail);
289
+ if (result.backups.length === 0) {
290
+ lines.push(`${rail} ${ui.dim("No backups found.")}`);
291
+ lines.push(...renderResolvedProjectContextBlock(context.ui, result.verboseContext));
292
+ return lines;
293
+ }
294
+ const rows = result.backups.map((backup) => [
295
+ backup.id,
296
+ backup.backupType,
297
+ backup.status,
298
+ formatBackupSize(backup.size),
299
+ backup.createdAt
300
+ ]);
301
+ const widths = [
302
+ Math.max(2, ...rows.map((row) => row[0].length)),
303
+ Math.max(4, ...rows.map((row) => row[1].length)),
304
+ Math.max(6, ...rows.map((row) => row[2].length)),
305
+ Math.max(4, ...rows.map((row) => row[3].length)),
306
+ Math.max(7, ...rows.map((row) => row[4].length))
307
+ ];
308
+ lines.push(`${rail} ${ui.accent(formatColumns([
309
+ "Id",
310
+ "Type",
311
+ "Status",
312
+ "Size",
313
+ "Created"
314
+ ], widths))}`);
315
+ for (const row of rows) lines.push(`${rail} ${formatColumns(row, widths)}`);
316
+ if (result.hasMore) {
317
+ lines.push(rail);
318
+ lines.push(`${rail} ${ui.dim("More backups exist; raise --limit to see them.")}`);
319
+ }
320
+ lines.push(...renderResolvedProjectContextBlock(context.ui, result.verboseContext));
321
+ return lines;
322
+ }
323
+ function serializeDatabaseBackupList(result) {
324
+ return {
325
+ ...serializeList({
326
+ context: {
327
+ project: result.projectName,
328
+ database: result.database.name
329
+ },
330
+ items: result.backups.map((backup) => ({
331
+ noun: "backup",
332
+ label: backup.id,
333
+ id: backup.id,
334
+ status: null
335
+ }))
336
+ }),
337
+ projectId: result.projectId,
338
+ database: result.database,
339
+ backups: result.backups,
340
+ retentionDays: result.retentionDays,
341
+ hasMore: result.hasMore
342
+ };
343
+ }
344
+ function renderDatabaseRestore(context, descriptor, result) {
345
+ const lines = renderMutate({
346
+ title: "Restoring database from backup.",
347
+ descriptor,
348
+ context: [
349
+ {
350
+ key: "project",
351
+ value: result.projectName
352
+ },
353
+ {
354
+ key: "database",
355
+ value: result.database.name
356
+ },
357
+ {
358
+ key: "id",
359
+ value: result.database.id,
360
+ tone: "dim"
361
+ },
362
+ {
363
+ key: "backup",
364
+ value: result.source.backupId
365
+ },
366
+ ...result.source.databaseId !== result.database.id ? [{
367
+ key: "source",
368
+ value: result.source.databaseId
369
+ }] : []
370
+ ],
371
+ operationDescription: "Restoring database",
372
+ operationCount: 1,
373
+ details: [`The restore is running; the database status is "${result.database.status ?? "recovering"}" until it completes.`, "Connections and credentials are preserved."]
374
+ }, context.ui);
375
+ lines.push(...renderResolvedProjectContextBlock(context.ui, result.verboseContext));
376
+ return lines;
377
+ }
378
+ function serializeDatabaseRestore(result) {
379
+ return stripVerboseContext(result);
380
+ }
381
+ function renderDatabaseConnectionRotateStdout(_context, _descriptor, result) {
382
+ return [result.connectionString];
383
+ }
384
+ function renderDatabaseConnectionRotate(context, _descriptor, result) {
385
+ const ui = context.ui;
386
+ const lines = [
387
+ "Rotating connection...",
388
+ renderSummaryLine(ui, "success", `Rotated credentials for ${result.database ? `"${result.database.name}"` : `connection ${result.connection.id}`}. The previous credentials no longer work.`),
389
+ " The connection URL below is shown once, so save it now."
390
+ ];
391
+ if (ui.verbose) {
392
+ lines.push("");
393
+ lines.push(...renderDatabaseConnectionRotateVerboseRows(context, result));
394
+ }
395
+ return lines;
396
+ }
397
+ function serializeDatabaseConnectionRotate(result) {
398
+ return result;
399
+ }
400
+ function renderDatabaseConnectionRotateVerboseRows(context, result) {
401
+ return renderMetadataRows([...result.database ? [["database", formatResourceWithId(context, result.database.name, result.database.id)]] : [], ["connection", formatResourceWithId(context, result.connection.name, result.connection.id)]]);
402
+ }
403
+ function formatBackupSize(size) {
404
+ if (size === null) return "unknown";
405
+ if (size < 1024) return `${size} B`;
406
+ if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KiB`;
407
+ if (size < 1024 * 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(1)} MiB`;
408
+ return `${(size / (1024 * 1024 * 1024)).toFixed(1)} GiB`;
409
+ }
410
+ function formatUsageTimestamp(value) {
411
+ return value || "unknown";
412
+ }
239
413
  function formatStatus(database) {
240
414
  return database.status ?? (database.isDefault ? "default" : "unknown");
241
415
  }
@@ -271,4 +445,4 @@ function renderMetadataRows(rows) {
271
445
  return rows.map(([key, value]) => ` ${formatColumns([key, value], widths)}`);
272
446
  }
273
447
  //#endregion
274
- export { renderDatabaseConnectionCreate, renderDatabaseConnectionCreateStdout, renderDatabaseConnectionList, renderDatabaseConnectionRemove, renderDatabaseCreate, renderDatabaseCreateStdout, renderDatabaseList, renderDatabaseRemove, renderDatabaseShow, serializeDatabaseConnectionCreate, serializeDatabaseConnectionList, serializeDatabaseConnectionRemove, serializeDatabaseCreate, serializeDatabaseList, serializeDatabaseRemove, serializeDatabaseShow };
448
+ export { renderDatabaseBackupList, renderDatabaseConnectionCreate, renderDatabaseConnectionCreateStdout, renderDatabaseConnectionList, renderDatabaseConnectionRemove, renderDatabaseConnectionRotate, renderDatabaseConnectionRotateStdout, renderDatabaseCreate, renderDatabaseCreateStdout, renderDatabaseList, renderDatabaseRemove, renderDatabaseRestore, renderDatabaseShow, renderDatabaseUsage, serializeDatabaseBackupList, serializeDatabaseConnectionCreate, serializeDatabaseConnectionList, serializeDatabaseConnectionRemove, serializeDatabaseConnectionRotate, serializeDatabaseCreate, serializeDatabaseList, serializeDatabaseRemove, serializeDatabaseRestore, serializeDatabaseShow, serializeDatabaseUsage };
@@ -0,0 +1,29 @@
1
+ import { resolvePrismaCliPackageCommandFormatterSync } from "../lib/agent/cli-command.js";
2
+ import { renderNextSteps, renderSummaryLine } from "../shell/ui.js";
3
+ //#region src/presenters/init.ts
4
+ function renderInit(context, _descriptor, result) {
5
+ const ui = context.ui;
6
+ const formatCommand = resolvePrismaCliPackageCommandFormatterSync(context.runtime.cwd);
7
+ const lines = [renderSummaryLine(ui, "success", `Wrote ${result.configPath}`)];
8
+ if (result.types.status === "installed") lines.push(renderSummaryLine(ui, "success", `Installed ${result.types.package} (config types)`));
9
+ else if ((result.types.status === "skipped" || result.types.status === "declined") && result.types.installCommand) lines.push(` ${ui.dim(`For editor types: ${result.types.installCommand}`)}`);
10
+ switch (result.link.status) {
11
+ case "linked":
12
+ lines.push(renderSummaryLine(ui, "success", `Linked "${result.directory}" to Project "${result.link.project?.name ?? ""}"`));
13
+ break;
14
+ case "already-linked": break;
15
+ case "skipped":
16
+ case "declined":
17
+ lines.push(` ${ui.dim(`Not linked to a Project yet; link with ${formatCommand(["project", "link"])}.`)}`);
18
+ break;
19
+ case "failed": break;
20
+ }
21
+ const linked = result.link.status === "linked" || result.link.status === "already-linked";
22
+ lines.push(...renderNextSteps([formatCommand(["app", "deploy"]), ...linked ? [] : [formatCommand(["project", "link"])]]));
23
+ return lines;
24
+ }
25
+ function serializeInit(result) {
26
+ return result;
27
+ }
28
+ //#endregion
29
+ export { renderInit, serializeInit };
@@ -86,6 +86,95 @@ function renderProjectSetup(context, _descriptor, result) {
86
86
  function serializeProjectSetup(result) {
87
87
  return result;
88
88
  }
89
+ function renderProjectRename(context, descriptor, result) {
90
+ return renderMutate({
91
+ title: "Renaming project.",
92
+ descriptor,
93
+ context: [
94
+ {
95
+ key: "workspace",
96
+ value: result.workspace.name
97
+ },
98
+ {
99
+ key: "project",
100
+ value: result.previousName
101
+ },
102
+ {
103
+ key: "id",
104
+ value: result.project.id,
105
+ tone: "dim"
106
+ }
107
+ ],
108
+ operationDescription: "Renaming project",
109
+ operationCount: 1,
110
+ details: [`The project is now named "${result.project.name}". Directory bindings pin the project id, so they stay valid.`]
111
+ }, context.ui);
112
+ }
113
+ function serializeProjectRename(result) {
114
+ return result;
115
+ }
116
+ function renderProjectRemove(context, descriptor, result) {
117
+ return renderMutate({
118
+ title: "Removing project.",
119
+ descriptor,
120
+ context: [
121
+ {
122
+ key: "workspace",
123
+ value: result.workspace.name
124
+ },
125
+ {
126
+ key: "project",
127
+ value: result.project.name
128
+ },
129
+ {
130
+ key: "id",
131
+ value: result.project.id,
132
+ tone: "dim"
133
+ }
134
+ ],
135
+ operationDescription: "Removing project",
136
+ operationCount: 1,
137
+ details: ["The project, its databases, and its apps were removed.", ...result.localPin.cleared ? ["This directory's local project binding was cleared."] : []]
138
+ }, context.ui);
139
+ }
140
+ function serializeProjectRemove(result) {
141
+ return result;
142
+ }
143
+ function renderProjectTransfer(context, descriptor, result) {
144
+ return renderMutate({
145
+ title: "Transferring project.",
146
+ descriptor,
147
+ context: [
148
+ {
149
+ key: "workspace",
150
+ value: result.workspace.name
151
+ },
152
+ {
153
+ key: "project",
154
+ value: result.project.name
155
+ },
156
+ {
157
+ key: "id",
158
+ value: result.project.id,
159
+ tone: "dim"
160
+ },
161
+ {
162
+ key: "recipient",
163
+ value: result.recipient.workspaceName ?? result.recipient.workspaceId ?? "workspace of the provided recipient token"
164
+ }
165
+ ],
166
+ operationDescription: "Transferring project",
167
+ operationCount: 1,
168
+ details: [
169
+ "The project now belongs to the recipient workspace; this workspace no longer has access.",
170
+ ...result.localPin.action === "rewritten" ? ["This directory's local project binding now points at the recipient workspace."] : [],
171
+ ...result.localPin.action === "cleared" ? ["This directory's local project binding was cleared."] : []
172
+ ]
173
+ }, context.ui);
174
+ }
175
+ function serializeProjectTransfer(result) {
176
+ return result;
177
+ }
89
178
  function renderGitConnect(context, descriptor, result) {
90
179
  const connection = result.repositoryConnection;
91
180
  return renderMutate({
@@ -171,4 +260,4 @@ function formatGitConnectionDetail(status) {
171
260
  }
172
261
  }
173
262
  //#endregion
174
- export { renderGitConnect, renderGitDisconnect, renderProjectList, renderProjectSetup, renderProjectShow, serializeProjectList, serializeProjectSetup, serializeProjectShow };
263
+ export { renderGitConnect, renderGitDisconnect, renderProjectList, renderProjectRemove, renderProjectRename, renderProjectSetup, renderProjectShow, renderProjectTransfer, serializeProjectList, serializeProjectRemove, serializeProjectRename, serializeProjectSetup, serializeProjectShow, serializeProjectTransfer };
@@ -20,6 +20,22 @@ const DESCRIPTORS = [
20
20
  description: "Show CLI build and environment",
21
21
  examples: ["prisma-cli version", "prisma-cli version --json"]
22
22
  },
23
+ {
24
+ id: "init",
25
+ path: ["prisma", "init"],
26
+ description: "Write a committed prisma.compute.ts for this app",
27
+ examples: (runtime) => agentCommandExamples(runtime, [
28
+ ["init"],
29
+ [
30
+ "init",
31
+ "--framework",
32
+ "hono",
33
+ "--entry",
34
+ "src/index.ts"
35
+ ],
36
+ ["init", "--no-link"]
37
+ ])
38
+ },
23
39
  {
24
40
  id: "agent",
25
41
  path: ["prisma", "agent"],
@@ -271,6 +287,36 @@ const DESCRIPTORS = [
271
287
  "prisma-cli project link \"Acme Dashboard\" --json"
272
288
  ]
273
289
  },
290
+ {
291
+ id: "project.rename",
292
+ path: [
293
+ "prisma",
294
+ "project",
295
+ "rename"
296
+ ],
297
+ description: "Rename the resolved Project",
298
+ examples: ["prisma-cli project rename \"Acme Dashboard v2\"", "prisma-cli project rename billing-api --project proj_123"]
299
+ },
300
+ {
301
+ id: "project.remove",
302
+ path: [
303
+ "prisma",
304
+ "project",
305
+ "remove"
306
+ ],
307
+ description: "Remove a Project permanently after exact id confirmation",
308
+ examples: ["prisma-cli project remove proj_123 --confirm proj_123"]
309
+ },
310
+ {
311
+ id: "project.transfer",
312
+ path: [
313
+ "prisma",
314
+ "project",
315
+ "transfer"
316
+ ],
317
+ description: "Transfer a Project to another workspace after exact id confirmation",
318
+ examples: ["prisma-cli project transfer proj_123 --to-workspace \"Prisma Labs\" --confirm proj_123", "prisma-cli project transfer proj_123 --recipient-token <token> --confirm proj_123"]
319
+ },
274
320
  {
275
321
  id: "git.connect",
276
322
  path: [
@@ -339,6 +385,26 @@ const DESCRIPTORS = [
339
385
  description: "Create a Prisma Postgres database and print its one-time connection URL",
340
386
  examples: ["prisma-cli database create my-db", "prisma-cli database create my-db --branch feature/foo --region eu-central-1"]
341
387
  },
388
+ {
389
+ id: "database.usage",
390
+ path: [
391
+ "prisma",
392
+ "database",
393
+ "usage"
394
+ ],
395
+ description: "Show usage metrics for a database",
396
+ examples: ["prisma-cli database usage db_123", "prisma-cli database usage acme-production --from 2026-06-01 --to 2026-06-30"]
397
+ },
398
+ {
399
+ id: "database.restore",
400
+ path: [
401
+ "prisma",
402
+ "database",
403
+ "restore"
404
+ ],
405
+ description: "Restore a database from a backup after exact id confirmation",
406
+ examples: ["prisma-cli database restore db_123 --backup bkp_456 --confirm db_123"]
407
+ },
342
408
  {
343
409
  id: "database.remove",
344
410
  path: [
@@ -349,6 +415,27 @@ const DESCRIPTORS = [
349
415
  description: "Remove a database after exact id confirmation",
350
416
  examples: ["prisma-cli database remove db_123 --confirm db_123"]
351
417
  },
418
+ {
419
+ id: "database.backup",
420
+ path: [
421
+ "prisma",
422
+ "database",
423
+ "backup"
424
+ ],
425
+ description: "Inspect platform-created database backups",
426
+ examples: ["prisma-cli database backup list db_123"]
427
+ },
428
+ {
429
+ id: "database.backup.list",
430
+ path: [
431
+ "prisma",
432
+ "database",
433
+ "backup",
434
+ "list"
435
+ ],
436
+ description: "List backups for a database",
437
+ examples: ["prisma-cli database backup list db_123", "prisma-cli database backup list acme-production --limit 50"]
438
+ },
352
439
  {
353
440
  id: "database.connection",
354
441
  path: [
@@ -385,6 +472,17 @@ const DESCRIPTORS = [
385
472
  description: "Create a database connection and print its one-time connection URL",
386
473
  examples: ["prisma-cli database connection create db_123", "prisma-cli database connection create db_123 --name readonly"]
387
474
  },
475
+ {
476
+ id: "database.connection.rotate",
477
+ path: [
478
+ "prisma",
479
+ "database",
480
+ "connection",
481
+ "rotate"
482
+ ],
483
+ description: "Rotate connection credentials and print the new one-time connection URL",
484
+ examples: ["prisma-cli database connection rotate conn_123 --confirm conn_123"]
485
+ },
388
486
  {
389
487
  id: "database.connection.remove",
390
488
  path: [
@@ -1,5 +1,6 @@
1
1
  import { CliError, authConfigInvalidError, authRequiredError, commandCanceledError } from "./errors.js";
2
2
  import { getCommandDescriptor } from "./command-meta.js";
3
+ import { renderSummaryLine } from "./ui.js";
3
4
  import { resolveGlobalFlags } from "./global-flags.js";
4
5
  import { createCommandContext } from "./runtime.js";
5
6
  import { collectCommandDiagnostics } from "../lib/diagnostics.js";
@@ -48,11 +49,16 @@ async function writeCommandSuccess(context, descriptor, success, presenter, dura
48
49
  return;
49
50
  }
50
51
  const rendered = presenter.renderHuman(context, descriptor, success.result);
52
+ const warningLines = success.warnings.map((warning) => renderSummaryLine(context.ui, "warning", warning));
51
53
  const diagnostics = await renderBestEffortCommandDiagnostics(context, {
52
54
  enabled: context.flags.verbose && rendered.length > 0,
53
55
  durationMs
54
56
  });
55
- const humanLines = [...rendered, ...diagnostics];
57
+ const humanLines = [
58
+ ...rendered,
59
+ ...warningLines,
60
+ ...diagnostics
61
+ ];
56
62
  if (stdout.length > 0 && humanLines.length > 0) humanLines.push("");
57
63
  writeHumanLines(context.output, humanLines);
58
64
  writeStdoutLines(context, stdout);
@@ -44,6 +44,12 @@ function usageError(summary, why, fix, nextSteps = [], domain = "cli") {
44
44
  nextSteps
45
45
  });
46
46
  }
47
+ function isUsageError(error, summary) {
48
+ return isErrorRecord(error) && error.code === "USAGE_ERROR" && (summary === void 0 || error.summary === summary);
49
+ }
50
+ function isErrorRecord(error) {
51
+ return typeof error === "object" && error !== null;
52
+ }
47
53
  function authRequiredError(nextSteps = ["prisma-cli auth login"], options = {}) {
48
54
  return new CliError({
49
55
  code: "AUTH_REQUIRED",
@@ -131,4 +137,4 @@ function featureUnavailableError(summary, why, fix, nextSteps = [], domain = "cl
131
137
  });
132
138
  }
133
139
  //#endregion
134
- export { CliError, authConfigInvalidError, authRequiredError, commandCanceledError, featureUnavailableError, usageError, workspaceAmbiguousError, workspaceNotAuthenticatedError, workspaceRequiredError, workspaceSwitchUnavailableError };
140
+ export { CliError, authConfigInvalidError, authRequiredError, commandCanceledError, featureUnavailableError, isUsageError, usageError, workspaceAmbiguousError, workspaceNotAuthenticatedError, workspaceRequiredError, workspaceSwitchUnavailableError };
@@ -1,4 +1,4 @@
1
- import { usageError } from "./errors.js";
1
+ import { isUsageError, usageError } from "./errors.js";
2
2
  import { confirm, isCancel, select, text } from "@clack/prompts";
3
3
  //#region src/shell/prompt.ts
4
4
  const PROMPT_CANCELED_SUMMARY = "Interactive prompt canceled";
@@ -40,6 +40,9 @@ async function confirmPrompt(options) {
40
40
  if (isCancel(response)) throw usageError(PROMPT_CANCELED_SUMMARY, "The command was canceled before a confirmation was made.", "Re-run the command and choose an option to continue.");
41
41
  return response;
42
42
  }
43
+ function isPromptCancelError(error) {
44
+ return isUsageError(error, PROMPT_CANCELED_SUMMARY);
45
+ }
43
46
  function disposePromptState(_input) {}
44
47
  //#endregion
45
- export { confirmPrompt, disposePromptState, selectPrompt, textPrompt };
48
+ export { confirmPrompt, disposePromptState, isPromptCancelError, selectPrompt, textPrompt };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/cli",
3
- "version": "3.0.0-beta.20",
3
+ "version": "3.0.0-beta.22",
4
4
  "description": "Command-line interface for the Prisma Developer Platform.",
5
5
  "type": "module",
6
6
  "bin": {