@prisma/cli 3.0.0-dev.112.1 → 3.0.0-dev.115.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/mock-api.js +96 -0
- package/dist/adapters/token-storage.js +23 -0
- package/dist/commands/database/index.js +92 -2
- package/dist/commands/project/index.js +51 -3
- package/dist/controllers/database.js +249 -6
- package/dist/controllers/project.js +326 -3
- package/dist/lib/auth/recipient.js +42 -0
- package/dist/lib/database/provider.js +148 -1
- package/dist/lib/project/provider.js +92 -0
- package/dist/presenters/database.js +175 -1
- package/dist/presenters/project.js +90 -1
- package/dist/shell/command-meta.js +82 -0
- package/dist/shell/command-runner.js +7 -1
- package/package.json +1 -1
|
@@ -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 };
|
|
@@ -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 };
|
|
@@ -271,6 +271,36 @@ const DESCRIPTORS = [
|
|
|
271
271
|
"prisma-cli project link \"Acme Dashboard\" --json"
|
|
272
272
|
]
|
|
273
273
|
},
|
|
274
|
+
{
|
|
275
|
+
id: "project.rename",
|
|
276
|
+
path: [
|
|
277
|
+
"prisma",
|
|
278
|
+
"project",
|
|
279
|
+
"rename"
|
|
280
|
+
],
|
|
281
|
+
description: "Rename the resolved Project",
|
|
282
|
+
examples: ["prisma-cli project rename \"Acme Dashboard v2\"", "prisma-cli project rename billing-api --project proj_123"]
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
id: "project.remove",
|
|
286
|
+
path: [
|
|
287
|
+
"prisma",
|
|
288
|
+
"project",
|
|
289
|
+
"remove"
|
|
290
|
+
],
|
|
291
|
+
description: "Remove a Project permanently after exact id confirmation",
|
|
292
|
+
examples: ["prisma-cli project remove proj_123 --confirm proj_123"]
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
id: "project.transfer",
|
|
296
|
+
path: [
|
|
297
|
+
"prisma",
|
|
298
|
+
"project",
|
|
299
|
+
"transfer"
|
|
300
|
+
],
|
|
301
|
+
description: "Transfer a Project to another workspace after exact id confirmation",
|
|
302
|
+
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"]
|
|
303
|
+
},
|
|
274
304
|
{
|
|
275
305
|
id: "git.connect",
|
|
276
306
|
path: [
|
|
@@ -339,6 +369,26 @@ const DESCRIPTORS = [
|
|
|
339
369
|
description: "Create a Prisma Postgres database and print its one-time connection URL",
|
|
340
370
|
examples: ["prisma-cli database create my-db", "prisma-cli database create my-db --branch feature/foo --region eu-central-1"]
|
|
341
371
|
},
|
|
372
|
+
{
|
|
373
|
+
id: "database.usage",
|
|
374
|
+
path: [
|
|
375
|
+
"prisma",
|
|
376
|
+
"database",
|
|
377
|
+
"usage"
|
|
378
|
+
],
|
|
379
|
+
description: "Show usage metrics for a database",
|
|
380
|
+
examples: ["prisma-cli database usage db_123", "prisma-cli database usage acme-production --from 2026-06-01 --to 2026-06-30"]
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
id: "database.restore",
|
|
384
|
+
path: [
|
|
385
|
+
"prisma",
|
|
386
|
+
"database",
|
|
387
|
+
"restore"
|
|
388
|
+
],
|
|
389
|
+
description: "Restore a database from a backup after exact id confirmation",
|
|
390
|
+
examples: ["prisma-cli database restore db_123 --backup bkp_456 --confirm db_123"]
|
|
391
|
+
},
|
|
342
392
|
{
|
|
343
393
|
id: "database.remove",
|
|
344
394
|
path: [
|
|
@@ -349,6 +399,27 @@ const DESCRIPTORS = [
|
|
|
349
399
|
description: "Remove a database after exact id confirmation",
|
|
350
400
|
examples: ["prisma-cli database remove db_123 --confirm db_123"]
|
|
351
401
|
},
|
|
402
|
+
{
|
|
403
|
+
id: "database.backup",
|
|
404
|
+
path: [
|
|
405
|
+
"prisma",
|
|
406
|
+
"database",
|
|
407
|
+
"backup"
|
|
408
|
+
],
|
|
409
|
+
description: "Inspect platform-created database backups",
|
|
410
|
+
examples: ["prisma-cli database backup list db_123"]
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
id: "database.backup.list",
|
|
414
|
+
path: [
|
|
415
|
+
"prisma",
|
|
416
|
+
"database",
|
|
417
|
+
"backup",
|
|
418
|
+
"list"
|
|
419
|
+
],
|
|
420
|
+
description: "List backups for a database",
|
|
421
|
+
examples: ["prisma-cli database backup list db_123", "prisma-cli database backup list acme-production --limit 50"]
|
|
422
|
+
},
|
|
352
423
|
{
|
|
353
424
|
id: "database.connection",
|
|
354
425
|
path: [
|
|
@@ -385,6 +456,17 @@ const DESCRIPTORS = [
|
|
|
385
456
|
description: "Create a database connection and print its one-time connection URL",
|
|
386
457
|
examples: ["prisma-cli database connection create db_123", "prisma-cli database connection create db_123 --name readonly"]
|
|
387
458
|
},
|
|
459
|
+
{
|
|
460
|
+
id: "database.connection.rotate",
|
|
461
|
+
path: [
|
|
462
|
+
"prisma",
|
|
463
|
+
"database",
|
|
464
|
+
"connection",
|
|
465
|
+
"rotate"
|
|
466
|
+
],
|
|
467
|
+
description: "Rotate connection credentials and print the new one-time connection URL",
|
|
468
|
+
examples: ["prisma-cli database connection rotate conn_123 --confirm conn_123"]
|
|
469
|
+
},
|
|
388
470
|
{
|
|
389
471
|
id: "database.connection.remove",
|
|
390
472
|
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 = [
|
|
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);
|