@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.
@@ -32,6 +32,9 @@ var MockApi = class MockApi {
32
32
  const workspaceIds = this.data.memberships.filter((membership) => membership.userId === userId).map((membership) => membership.workspaceId);
33
33
  return this.data.workspaces.filter((workspace) => workspaceIds.includes(workspace.id));
34
34
  }
35
+ listWorkspaces() {
36
+ return this.data.workspaces;
37
+ }
35
38
  getWorkspace(workspaceId) {
36
39
  return this.data.workspaces.find((workspace) => workspace.id === workspaceId);
37
40
  }
@@ -47,6 +50,36 @@ var MockApi = class MockApi {
47
50
  getProjectForWorkspace(workspaceId, projectId) {
48
51
  return this.listProjectsForWorkspace(workspaceId).find((project) => project.id === projectId);
49
52
  }
53
+ renameProject(projectId, name) {
54
+ const project = this.getProject(projectId);
55
+ if (!project) return;
56
+ project.name = name;
57
+ return project;
58
+ }
59
+ removeProject(projectId) {
60
+ const project = this.getProject(projectId);
61
+ if (!project) return { outcome: "not-found" };
62
+ if (this.data.deployments.some((deployment) => deployment.projectId === projectId)) return { outcome: "blocked" };
63
+ const removedDatabaseIds = new Set((this.data.databases ?? []).filter((database) => database.projectId === projectId).map((database) => database.id));
64
+ this.data.projects = this.data.projects.filter((candidate) => candidate.id !== projectId);
65
+ this.data.branches = this.data.branches.filter((branch) => branch.projectId !== projectId);
66
+ this.data.databases = (this.data.databases ?? []).filter((database) => database.projectId !== projectId);
67
+ this.data.databaseConnections = (this.data.databaseConnections ?? []).filter((connection) => !removedDatabaseIds.has(connection.databaseId));
68
+ return {
69
+ outcome: "removed",
70
+ project
71
+ };
72
+ }
73
+ transferProject(projectId, targetWorkspaceId) {
74
+ const project = this.getProject(projectId);
75
+ if (!project) return { outcome: "not-found" };
76
+ if (!this.getWorkspace(targetWorkspaceId)) return { outcome: "workspace-not-found" };
77
+ project.workspaceId = targetWorkspaceId;
78
+ return {
79
+ outcome: "transferred",
80
+ project
81
+ };
82
+ }
50
83
  listBranchesForProject(projectId) {
51
84
  return this.data.branches.filter((branch) => branch.projectId === projectId);
52
85
  }
@@ -131,6 +164,69 @@ var MockApi = class MockApi {
131
164
  this.data.databaseConnections = this.data.databaseConnections.filter((candidate) => candidate.id !== connectionId);
132
165
  return connection;
133
166
  }
167
+ getDatabaseUsage(databaseId, period) {
168
+ const defaults = (this.data.databaseUsage ?? []).find((record) => record.databaseId === databaseId) ?? {
169
+ databaseId,
170
+ period: {
171
+ start: "2026-06-01T00:00:00.000Z",
172
+ end: "2026-06-30T23:59:59.999Z"
173
+ },
174
+ metrics: {
175
+ operations: {
176
+ used: 0,
177
+ unit: "ops"
178
+ },
179
+ storage: {
180
+ used: 0,
181
+ unit: "GiB"
182
+ }
183
+ },
184
+ generatedAt: "2026-07-01T00:00:00.000Z"
185
+ };
186
+ return {
187
+ period: {
188
+ start: period?.from ?? defaults.period.start,
189
+ end: period?.to ?? defaults.period.end
190
+ },
191
+ metrics: defaults.metrics,
192
+ generatedAt: defaults.generatedAt
193
+ };
194
+ }
195
+ listDatabaseBackups(databaseId, limit) {
196
+ const backups = (this.data.databaseBackups ?? []).filter((backup) => backup.databaseId === databaseId);
197
+ const limited = limit === void 0 ? backups : backups.slice(0, limit);
198
+ return {
199
+ backups: limited.map((backup) => ({
200
+ id: backup.id,
201
+ backupType: backup.backupType,
202
+ status: backup.status,
203
+ size: backup.size ?? null,
204
+ createdAt: backup.createdAt
205
+ })),
206
+ retentionDays: this.data.databaseBackupRetentionDays ?? null,
207
+ hasMore: limited.length < backups.length
208
+ };
209
+ }
210
+ restoreDatabase(input) {
211
+ const target = this.getDatabase(input.targetDatabaseId);
212
+ if (!target) return { outcome: "target-not-found" };
213
+ if (!(this.data.databaseBackups ?? []).find((candidate) => candidate.id === input.backupId && candidate.databaseId === input.sourceDatabaseId)) return { outcome: "backup-not-found" };
214
+ target.status = "recovering";
215
+ return {
216
+ outcome: "restored",
217
+ database: target
218
+ };
219
+ }
220
+ rotateDatabaseConnection(connectionId) {
221
+ const connection = this.getDatabaseConnection(connectionId);
222
+ if (!connection) return;
223
+ const connectionString = `postgresql://rotated-${connection.databaseId}-${connection.id}.example.prisma.io/postgres`;
224
+ connection.connectionString = connectionString;
225
+ return {
226
+ connection,
227
+ connectionString
228
+ };
229
+ }
134
230
  };
135
231
  //#endregion
136
232
  export { MockApi };
@@ -77,6 +77,7 @@ var FileTokenStorage = class {
77
77
  this.signal?.throwIfAborted();
78
78
  try {
79
79
  const credentials = await this.readCredentialsFromDisk();
80
+ if (this.options.pinnedWorkspaceId) return findTokensForWorkspace(credentials, this.options.pinnedWorkspaceId);
80
81
  const context = await this.readAuthContext();
81
82
  if (context.state.activeWorkspaceId) return findTokensForWorkspace(credentials, context.state.activeWorkspaceId);
82
83
  const latest = findLatestValidTokens(credentials);
@@ -138,6 +139,9 @@ var FileTokenStorage = class {
138
139
  this.signal?.throwIfAborted();
139
140
  const credentials = await this.readCredentialsFromDisk();
140
141
  const context = await this.ensureMigratedAuthContext(credentials);
142
+ return this.workspacesFromState(credentials, context);
143
+ }
144
+ workspacesFromState(credentials, context) {
141
145
  return credentials.map((credential) => storedCredentialToTokens(credential)).filter((tokens) => tokens !== null).map((tokens) => {
142
146
  const cached = context.state.workspaces[tokens.workspaceId];
143
147
  const id = typeof cached?.id === "string" && cached.id.trim().length > 0 ? cached.id.trim() : tokens.workspaceId;
@@ -159,6 +163,24 @@ var FileTokenStorage = class {
159
163
  async useWorkspace(workspaceRef) {
160
164
  return this.withRefreshLock(() => this.useWorkspaceUnlocked(workspaceRef));
161
165
  }
166
+ /**
167
+ * Resolve a workspace ref (id, credential workspace id, or cached name)
168
+ * against the locally stored sessions without changing the active
169
+ * workspace. Read-only counterpart of useWorkspace: it reads the auth
170
+ * context as-is and never runs the legacy-state migration, so it writes
171
+ * nothing.
172
+ */
173
+ async resolveWorkspace(workspaceRef) {
174
+ const ref = workspaceRef.trim();
175
+ if (!ref) throw new WorkspaceSelectionError("missing");
176
+ this.signal?.throwIfAborted();
177
+ const credentials = await this.readCredentialsFromDisk();
178
+ const context = await this.readAuthContext();
179
+ const matches = this.workspacesFromState(credentials, context).filter((workspace) => workspaceMatchesRef(workspace, ref));
180
+ if (matches.length === 0) throw new WorkspaceSelectionError("not-found", ref);
181
+ if (matches.length > 1) throw new WorkspaceSelectionError("ambiguous", ref, matches);
182
+ return matches[0];
183
+ }
162
184
  async useWorkspaceUnlocked(workspaceRef) {
163
185
  const ref = workspaceRef.trim();
164
186
  if (!ref) throw new WorkspaceSelectionError("missing");
@@ -327,6 +349,7 @@ var FileTokenStorage = class {
327
349
  await this.writeAuthContext(context.state);
328
350
  }
329
351
  async maybeActivateWorkspaceId(workspaceId) {
352
+ if (this.options.pinnedWorkspaceId) return;
330
353
  const context = await this.readAuthContext();
331
354
  if (this.options.activateOnSetTokens === false && context.exists && context.state.activeWorkspaceId && context.state.activeWorkspaceId !== workspaceId) return;
332
355
  context.state.activeWorkspaceId = workspaceId;
@@ -2,8 +2,8 @@ import { attachCommandDescriptor } from "../../shell/command-meta.js";
2
2
  import { addCompactGlobalFlags, addGlobalFlags } from "../../shell/global-flags.js";
3
3
  import { configureRuntimeCommand } from "../../shell/runtime.js";
4
4
  import { runCommand } from "../../shell/command-runner.js";
5
- import { runDatabaseConnectionCreate, runDatabaseConnectionList, runDatabaseConnectionRemove, runDatabaseCreate, runDatabaseList, runDatabaseRemove, runDatabaseShow } from "../../controllers/database.js";
6
- import { renderDatabaseConnectionCreate, renderDatabaseConnectionCreateStdout, renderDatabaseConnectionList, renderDatabaseConnectionRemove, renderDatabaseCreate, renderDatabaseCreateStdout, renderDatabaseList, renderDatabaseRemove, renderDatabaseShow, serializeDatabaseConnectionCreate, serializeDatabaseConnectionList, serializeDatabaseConnectionRemove, serializeDatabaseCreate, serializeDatabaseList, serializeDatabaseRemove, serializeDatabaseShow } from "../../presenters/database.js";
5
+ import { runDatabaseBackupList, runDatabaseConnectionCreate, runDatabaseConnectionList, runDatabaseConnectionRemove, runDatabaseConnectionRotate, runDatabaseCreate, runDatabaseList, runDatabaseRemove, runDatabaseRestore, runDatabaseShow, runDatabaseUsage } from "../../controllers/database.js";
6
+ import { 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 } from "../../presenters/database.js";
7
7
  import { Command, Option } from "commander";
8
8
  //#region src/commands/database/index.ts
9
9
  function createDatabaseCommand(runtime) {
@@ -12,7 +12,10 @@ function createDatabaseCommand(runtime) {
12
12
  database.addCommand(createDatabaseListCommand(runtime));
13
13
  database.addCommand(createDatabaseShowCommand(runtime));
14
14
  database.addCommand(createDatabaseCreateCommand(runtime));
15
+ database.addCommand(createDatabaseUsageCommand(runtime));
16
+ database.addCommand(createDatabaseRestoreCommand(runtime));
15
17
  database.addCommand(createDatabaseRemoveCommand(runtime));
18
+ database.addCommand(createDatabaseBackupCommand(runtime));
16
19
  database.addCommand(createDatabaseConnectionCommand(runtime));
17
20
  return database;
18
21
  }
@@ -75,6 +78,78 @@ function createDatabaseCreateCommand(runtime) {
75
78
  });
76
79
  return command;
77
80
  }
81
+ function createDatabaseUsageCommand(runtime) {
82
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("usage"), runtime), "database.usage");
83
+ command.argument("<database>", "Database id or name").addOption(new Option("--from <iso-date>", "Start of the usage period")).addOption(new Option("--to <iso-date>", "End of the usage period"));
84
+ addProjectAndBranchOptions(command);
85
+ addGlobalFlags(command);
86
+ command.action(async (databaseRef, options) => {
87
+ const projectRef = options.project;
88
+ const branchName = options.branch;
89
+ const from = options.from;
90
+ const to = options.to;
91
+ await runCommand(runtime, "database.usage", options, (context) => runDatabaseUsage(context, databaseRef, {
92
+ projectRef,
93
+ branchName,
94
+ from,
95
+ to
96
+ }), {
97
+ renderHuman: (context, descriptor, result) => renderDatabaseUsage(context, descriptor, result),
98
+ renderJson: (result) => serializeDatabaseUsage(result)
99
+ });
100
+ });
101
+ return command;
102
+ }
103
+ function createDatabaseRestoreCommand(runtime) {
104
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("restore"), runtime), "database.restore");
105
+ command.argument("<database>", "Target database id or name").addOption(new Option("--backup <backup-id>", "Backup to restore from")).addOption(new Option("--source-database <database>", "Database the backup belongs to (defaults to the target)")).addOption(new Option("--confirm <database-id>", "Exact target database id required to restore"));
106
+ addProjectAndBranchOptions(command);
107
+ addGlobalFlags(command);
108
+ command.action(async (databaseRef, options) => {
109
+ const projectRef = options.project;
110
+ const branchName = options.branch;
111
+ const backupId = options.backup;
112
+ const sourceDatabaseRef = options.sourceDatabase;
113
+ const confirm = options.confirm;
114
+ await runCommand(runtime, "database.restore", options, (context) => runDatabaseRestore(context, databaseRef, {
115
+ projectRef,
116
+ branchName,
117
+ backupId,
118
+ sourceDatabaseRef,
119
+ confirm
120
+ }), {
121
+ renderHuman: (context, descriptor, result) => renderDatabaseRestore(context, descriptor, result),
122
+ renderJson: (result) => serializeDatabaseRestore(result)
123
+ });
124
+ });
125
+ return command;
126
+ }
127
+ function createDatabaseBackupCommand(runtime) {
128
+ const backup = attachCommandDescriptor(configureRuntimeCommand(new Command("backup"), runtime), "database.backup");
129
+ addCompactGlobalFlags(backup);
130
+ backup.addCommand(createDatabaseBackupListCommand(runtime));
131
+ return backup;
132
+ }
133
+ function createDatabaseBackupListCommand(runtime) {
134
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("list"), runtime), "database.backup.list");
135
+ command.argument("<database>", "Database id or name").addOption(new Option("--limit <n>", "Maximum number of backups to return"));
136
+ addProjectAndBranchOptions(command);
137
+ addGlobalFlags(command);
138
+ command.action(async (databaseRef, options) => {
139
+ const projectRef = options.project;
140
+ const branchName = options.branch;
141
+ const limit = options.limit;
142
+ await runCommand(runtime, "database.backup.list", options, (context) => runDatabaseBackupList(context, databaseRef, {
143
+ projectRef,
144
+ branchName,
145
+ limit
146
+ }), {
147
+ renderHuman: (context, descriptor, result) => renderDatabaseBackupList(context, descriptor, result),
148
+ renderJson: (result) => serializeDatabaseBackupList(result)
149
+ });
150
+ });
151
+ return command;
152
+ }
78
153
  function createDatabaseRemoveCommand(runtime) {
79
154
  const command = attachCommandDescriptor(configureRuntimeCommand(new Command("remove"), runtime), "database.remove");
80
155
  command.argument("<database>", "Database id or name").addOption(new Option("--confirm <database-id>", "Exact database id required to remove"));
@@ -100,9 +175,24 @@ function createDatabaseConnectionCommand(runtime) {
100
175
  addCompactGlobalFlags(connection);
101
176
  connection.addCommand(createDatabaseConnectionListCommand(runtime));
102
177
  connection.addCommand(createDatabaseConnectionCreateCommand(runtime));
178
+ connection.addCommand(createDatabaseConnectionRotateCommand(runtime));
103
179
  connection.addCommand(createDatabaseConnectionRemoveCommand(runtime));
104
180
  return connection;
105
181
  }
182
+ function createDatabaseConnectionRotateCommand(runtime) {
183
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("rotate"), runtime), "database.connection.rotate");
184
+ command.argument("<connection>", "Connection id").addOption(new Option("--confirm <connection-id>", "Exact connection id required to rotate"));
185
+ addGlobalFlags(command);
186
+ command.action(async (connectionRef, options) => {
187
+ const confirm = options.confirm;
188
+ await runCommand(runtime, "database.connection.rotate", options, (context) => runDatabaseConnectionRotate(context, connectionRef, { confirm }), {
189
+ renderStdout: (context, descriptor, result) => renderDatabaseConnectionRotateStdout(context, descriptor, result),
190
+ renderHuman: (context, descriptor, result) => renderDatabaseConnectionRotate(context, descriptor, result),
191
+ renderJson: (result) => serializeDatabaseConnectionRotate(result)
192
+ });
193
+ });
194
+ return command;
195
+ }
106
196
  function createDatabaseConnectionListCommand(runtime) {
107
197
  const command = attachCommandDescriptor(configureRuntimeCommand(new Command("list"), runtime), "database.connection.list");
108
198
  command.argument("<database>", "Database id or name");
@@ -2,10 +2,10 @@ import { attachCommandDescriptor } from "../../shell/command-meta.js";
2
2
  import { addCompactGlobalFlags, addGlobalFlags } from "../../shell/global-flags.js";
3
3
  import { configureRuntimeCommand } from "../../shell/runtime.js";
4
4
  import { runCommand } from "../../shell/command-runner.js";
5
- import { runProjectCreate, runProjectLink, runProjectList, runProjectShow } from "../../controllers/project.js";
6
- import { renderProjectList, renderProjectSetup, renderProjectShow, serializeProjectList, serializeProjectSetup, serializeProjectShow } from "../../presenters/project.js";
5
+ import { runProjectCreate, runProjectLink, runProjectList, runProjectRemove, runProjectRename, runProjectShow, runProjectTransfer } from "../../controllers/project.js";
6
+ import { renderProjectList, renderProjectRemove, renderProjectRename, renderProjectSetup, renderProjectShow, renderProjectTransfer, serializeProjectList, serializeProjectRemove, serializeProjectRename, serializeProjectSetup, serializeProjectShow, serializeProjectTransfer } from "../../presenters/project.js";
7
7
  import { createEnvCommand } from "../env.js";
8
- import { Command } from "commander";
8
+ import { Command, Option } from "commander";
9
9
  //#region src/commands/project/index.ts
10
10
  function createProjectCommand(runtime) {
11
11
  const project = attachCommandDescriptor(configureRuntimeCommand(new Command("project"), runtime), "project");
@@ -14,9 +14,57 @@ function createProjectCommand(runtime) {
14
14
  project.addCommand(createProjectShowCommand(runtime));
15
15
  project.addCommand(createProjectCreateCommand(runtime));
16
16
  project.addCommand(createProjectLinkCommand(runtime));
17
+ project.addCommand(createProjectRenameCommand(runtime));
18
+ project.addCommand(createProjectRemoveCommand(runtime));
19
+ project.addCommand(createProjectTransferCommand(runtime));
17
20
  project.addCommand(createEnvCommand(runtime));
18
21
  return project;
19
22
  }
23
+ function createProjectRenameCommand(runtime) {
24
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("rename"), runtime), "project.rename");
25
+ command.argument("<name>", "New project name").addOption(new Option("--project <id-or-name>", "Project id or name"));
26
+ addGlobalFlags(command);
27
+ command.action(async (name, options) => {
28
+ const projectRef = options.project;
29
+ await runCommand(runtime, "project.rename", options, (context) => runProjectRename(context, name, { project: projectRef }), {
30
+ renderHuman: (context, descriptor, result) => renderProjectRename(context, descriptor, result),
31
+ renderJson: (result) => serializeProjectRename(result)
32
+ });
33
+ });
34
+ return command;
35
+ }
36
+ function createProjectRemoveCommand(runtime) {
37
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("remove"), runtime), "project.remove");
38
+ command.argument("<project>", "Project id or name").addOption(new Option("--confirm <project-id>", "Exact project id required to remove"));
39
+ addGlobalFlags(command);
40
+ command.action(async (projectRef, options) => {
41
+ const confirm = options.confirm;
42
+ await runCommand(runtime, "project.remove", options, (context) => runProjectRemove(context, projectRef, { confirm }), {
43
+ renderHuman: (context, descriptor, result) => renderProjectRemove(context, descriptor, result),
44
+ renderJson: (result) => serializeProjectRemove(result)
45
+ });
46
+ });
47
+ return command;
48
+ }
49
+ function createProjectTransferCommand(runtime) {
50
+ const command = attachCommandDescriptor(configureRuntimeCommand(new Command("transfer"), runtime), "project.transfer");
51
+ command.argument("<project>", "Project id or name").addOption(new Option("--to-workspace <id-or-name>", "Locally authenticated workspace to receive the project")).addOption(new Option("--recipient-token <token>", "Access token for the receiving workspace")).addOption(new Option("--confirm <project-id>", "Exact project id required to transfer"));
52
+ addGlobalFlags(command);
53
+ command.action(async (projectRef, options) => {
54
+ const toWorkspace = options.toWorkspace;
55
+ const recipientToken = options.recipientToken;
56
+ const confirm = options.confirm;
57
+ await runCommand(runtime, "project.transfer", options, (context) => runProjectTransfer(context, projectRef, {
58
+ toWorkspace,
59
+ recipientToken,
60
+ confirm
61
+ }), {
62
+ renderHuman: (context, descriptor, result) => renderProjectTransfer(context, descriptor, result),
63
+ renderJson: (result) => serializeProjectTransfer(result)
64
+ });
65
+ });
66
+ return command;
67
+ }
20
68
  function createProjectCreateCommand(runtime) {
21
69
  const command = attachCommandDescriptor(configureRuntimeCommand(new Command("create"), runtime), "project.create");
22
70
  command.argument("<name>", "Project name");
@@ -1,3 +1,4 @@
1
+ import { resolvePrismaCliPackageCommandFormatterSync } from "../lib/agent/cli-command.js";
1
2
  import { CliError, authRequiredError, usageError, workspaceRequiredError } from "../shell/errors.js";
2
3
  import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
3
4
  import { requireComputeAuth } from "../lib/auth/guard.js";
@@ -149,6 +150,197 @@ async function runDatabaseConnectionRemove(context, connectionRef, flags) {
149
150
  nextSteps: []
150
151
  };
151
152
  }
153
+ async function runDatabaseUsage(context, databaseRef, flags) {
154
+ const formatCommand = resolvePrismaCliPackageCommandFormatterSync(context.runtime.cwd);
155
+ const from = parseUsageDate(flags.from, "--from", "start", formatCommand);
156
+ const to = parseUsageDate(flags.to, "--to", "end", formatCommand);
157
+ if (from && to && Date.parse(from) > Date.parse(to)) throw usageError("Invalid usage period", "--from must not be later than --to.", "Pass a --from date that is on or before the --to date.", [formatCommand([
158
+ "database",
159
+ "usage",
160
+ "<database>",
161
+ "--from",
162
+ "2026-06-01",
163
+ "--to",
164
+ "2026-06-30"
165
+ ])], "database");
166
+ const { provider, target } = await requireDatabaseContext(context, flags, "database usage");
167
+ const database = await resolveDatabase(provider, target, databaseRef, flags.branchName, context.runtime.signal);
168
+ const usage = await provider.getUsage(database.id, {
169
+ from,
170
+ to,
171
+ signal: context.runtime.signal
172
+ });
173
+ return {
174
+ command: "database.usage",
175
+ result: {
176
+ projectId: target.project.id,
177
+ projectName: target.project.name,
178
+ verboseContext: target,
179
+ database,
180
+ period: usage.period,
181
+ metrics: usage.metrics,
182
+ generatedAt: usage.generatedAt
183
+ },
184
+ warnings: [],
185
+ nextSteps: []
186
+ };
187
+ }
188
+ async function runDatabaseBackupList(context, databaseRef, flags) {
189
+ const limit = parseBackupLimit(flags.limit, resolvePrismaCliPackageCommandFormatterSync(context.runtime.cwd));
190
+ const { provider, target } = await requireDatabaseContext(context, flags, "database backup list");
191
+ const database = await resolveDatabase(provider, target, databaseRef, flags.branchName, context.runtime.signal);
192
+ const backups = await provider.listBackups(database.id, {
193
+ limit,
194
+ signal: context.runtime.signal
195
+ });
196
+ return {
197
+ command: "database.backup.list",
198
+ result: {
199
+ projectId: target.project.id,
200
+ projectName: target.project.name,
201
+ verboseContext: target,
202
+ database,
203
+ backups: backups.backups,
204
+ retentionDays: backups.retentionDays,
205
+ hasMore: backups.hasMore
206
+ },
207
+ warnings: [],
208
+ nextSteps: []
209
+ };
210
+ }
211
+ async function runDatabaseRestore(context, databaseRef, flags) {
212
+ const formatCommand = resolvePrismaCliPackageCommandFormatterSync(context.runtime.cwd);
213
+ const backupId = flags.backupId?.trim();
214
+ if (!backupId) throw usageError("Backup id required", "Database restore needs the backup to restore from.", `Pass --backup <backup-id> from ${formatCommand([
215
+ "database",
216
+ "backup",
217
+ "list",
218
+ "<database>"
219
+ ])}.`, [formatCommand([
220
+ "database",
221
+ "backup",
222
+ "list",
223
+ "<database>"
224
+ ])], "database");
225
+ const { provider, target } = await requireDatabaseContext(context, flags, "database restore");
226
+ const database = await resolveDatabase(provider, target, databaseRef, flags.branchName, context.runtime.signal);
227
+ const sourceDatabase = flags.sourceDatabaseRef ? await resolveDatabase(provider, target, flags.sourceDatabaseRef, flags.branchName, context.runtime.signal) : database;
228
+ const sourceDatabaseArg = sourceDatabase.id === database.id ? "" : ` --source-database ${sourceDatabase.id}`;
229
+ requireExactConfirmation({
230
+ resourceName: "database",
231
+ commandName: "database restore",
232
+ id: database.id,
233
+ confirm: flags.confirm,
234
+ summary: "Confirm database restore",
235
+ why: "Restoring immediately and irreversibly overwrites all data in the target database, so it requires the exact target database id.",
236
+ nextStep: `${formatCommand([
237
+ "database",
238
+ "restore",
239
+ database.id,
240
+ "--backup",
241
+ backupId
242
+ ])}${sourceDatabaseArg} --confirm ${database.id}`
243
+ });
244
+ const restored = await provider.restoreDatabase({
245
+ targetDatabaseId: database.id,
246
+ sourceDatabaseId: sourceDatabase.id,
247
+ backupId,
248
+ projectId: target.project.id,
249
+ signal: context.runtime.signal
250
+ });
251
+ return {
252
+ command: "database.restore",
253
+ result: {
254
+ projectId: target.project.id,
255
+ projectName: target.project.name,
256
+ verboseContext: target,
257
+ database: ensureProjectId(restored, target.project.id),
258
+ source: {
259
+ databaseId: sourceDatabase.id,
260
+ backupId
261
+ }
262
+ },
263
+ warnings: [],
264
+ nextSteps: [formatCommand([
265
+ "database",
266
+ "show",
267
+ database.id
268
+ ])]
269
+ };
270
+ }
271
+ async function runDatabaseConnectionRotate(context, connectionRef, flags) {
272
+ const formatCommand = resolvePrismaCliPackageCommandFormatterSync(context.runtime.cwd);
273
+ const connectionId = connectionRef.trim();
274
+ if (!connectionId) throw usageError("Connection id required", "Database connection rotation needs a connection id.", "Pass the connection id to rotate.", [formatCommand([
275
+ "database",
276
+ "connection",
277
+ "rotate",
278
+ "<connection-id>",
279
+ "--confirm",
280
+ "<connection-id>"
281
+ ])], "database");
282
+ requireExactConfirmation({
283
+ resourceName: "database connection",
284
+ commandName: "database connection rotate",
285
+ id: connectionId,
286
+ confirm: flags.confirm,
287
+ summary: "Confirm database connection rotation",
288
+ why: "Rotating revokes the previous credentials and breaks clients still using them, so it requires the exact connection id.",
289
+ nextStep: formatCommand([
290
+ "database",
291
+ "connection",
292
+ "rotate",
293
+ connectionId,
294
+ "--confirm",
295
+ connectionId
296
+ ])
297
+ });
298
+ const rotated = await (await requireDatabaseProviderOnly(context)).rotateConnection(connectionId, { signal: context.runtime.signal });
299
+ return {
300
+ command: "database.connection.rotate",
301
+ result: {
302
+ connection: rotated.connection,
303
+ database: rotated.database,
304
+ connectionString: rotated.connectionString
305
+ },
306
+ warnings: [],
307
+ nextSteps: []
308
+ };
309
+ }
310
+ const USAGE_DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
311
+ const USAGE_DATETIME_PATTERN = /^\d{4}-\d{2}-\d{2}T/;
312
+ function parseUsageDate(value, flagName, dayBoundary, formatCommand) {
313
+ if (value === void 0) return;
314
+ const trimmed = value.trim();
315
+ if (USAGE_DATE_ONLY_PATTERN.test(trimmed) && isValidCalendarDate(trimmed)) return dayBoundary === "start" ? `${trimmed}T00:00:00.000Z` : `${trimmed}T23:59:59.999Z`;
316
+ if (USAGE_DATETIME_PATTERN.test(trimmed) && !Number.isNaN(Date.parse(trimmed)) && isValidCalendarDate(trimmed.slice(0, 10))) return trimmed;
317
+ throw usageError("Invalid usage period", `${flagName} must be an ISO date such as 2026-06-01 or an ISO datetime such as 2026-06-01T12:00:00Z.`, `Pass an ISO date or datetime to ${flagName}.`, [formatCommand([
318
+ "database",
319
+ "usage",
320
+ "<database>",
321
+ "--from",
322
+ "2026-06-01",
323
+ "--to",
324
+ "2026-06-30"
325
+ ])], "database");
326
+ }
327
+ function isValidCalendarDate(datePart) {
328
+ const timestamp = Date.parse(`${datePart}T00:00:00.000Z`);
329
+ return !Number.isNaN(timestamp) && new Date(timestamp).toISOString().startsWith(datePart);
330
+ }
331
+ function parseBackupLimit(value, formatCommand) {
332
+ if (value === void 0) return;
333
+ const limit = Number(value.trim());
334
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) throw usageError("Invalid backup limit", "--limit must be an integer between 1 and 100.", "Pass a --limit between 1 and 100.", [formatCommand([
335
+ "database",
336
+ "backup",
337
+ "list",
338
+ "<database>",
339
+ "--limit",
340
+ "50"
341
+ ])], "database");
342
+ return limit;
343
+ }
152
344
  async function requireDatabaseContext(context, flags, commandName) {
153
345
  const workspace = (await requireAuthenticatedAuthState(context)).workspace;
154
346
  if (!workspace) throw workspaceRequiredError();
@@ -164,7 +356,7 @@ async function requireDatabaseContext(context, flags, commandName) {
164
356
  });
165
357
  if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
166
358
  return {
167
- provider: createManagementDatabaseProvider(client),
359
+ provider: createManagementDatabaseProvider(client, { formatCommand: resolvePrismaCliPackageCommandFormatterSync(context.runtime.cwd) }),
168
360
  target: targetResult.value
169
361
  };
170
362
  }
@@ -186,7 +378,7 @@ async function requireDatabaseProviderOnly(context) {
186
378
  if (isRealMode(context)) {
187
379
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
188
380
  if (!client) throw authRequiredError();
189
- return createManagementDatabaseProvider(client);
381
+ return createManagementDatabaseProvider(client, { formatCommand: resolvePrismaCliPackageCommandFormatterSync(context.runtime.cwd) });
190
382
  }
191
383
  return createFixtureDatabaseProvider(context);
192
384
  }
@@ -224,6 +416,40 @@ function createFixtureDatabaseProvider(context) {
224
416
  },
225
417
  async removeConnection(connectionId) {
226
418
  if (!context.api.removeDatabaseConnection(connectionId)) throw connectionNotFoundError(connectionId);
419
+ },
420
+ async getUsage(databaseId, options) {
421
+ if (!context.api.getDatabase(databaseId)) throw databaseNotFoundError(databaseId);
422
+ return context.api.getDatabaseUsage(databaseId, {
423
+ from: options?.from,
424
+ to: options?.to
425
+ });
426
+ },
427
+ async listBackups(databaseId, options) {
428
+ if (!context.api.getDatabase(databaseId)) throw databaseNotFoundError(databaseId);
429
+ return context.api.listDatabaseBackups(databaseId, options?.limit);
430
+ },
431
+ async restoreDatabase(options) {
432
+ const restored = context.api.restoreDatabase({
433
+ targetDatabaseId: options.targetDatabaseId,
434
+ sourceDatabaseId: options.sourceDatabaseId,
435
+ backupId: options.backupId
436
+ });
437
+ if (restored.outcome === "target-not-found") throw databaseNotFoundError(options.targetDatabaseId);
438
+ if (restored.outcome === "backup-not-found") throw backupNotFoundError(options.backupId, options.sourceDatabaseId, resolvePrismaCliPackageCommandFormatterSync(context.runtime.cwd));
439
+ return normalizeDatabase(restored.database, options.projectId);
440
+ },
441
+ async rotateConnection(connectionId) {
442
+ const rotated = context.api.rotateDatabaseConnection(connectionId);
443
+ if (!rotated) throw connectionNotFoundError(connectionId);
444
+ const database = context.api.getDatabase(rotated.connection.databaseId);
445
+ return {
446
+ connection: normalizeConnection(rotated.connection, rotated.connection.databaseId),
447
+ database: database ? {
448
+ id: database.id,
449
+ name: database.name
450
+ } : null,
451
+ connectionString: rotated.connectionString
452
+ };
227
453
  }
228
454
  };
229
455
  }
@@ -262,11 +488,11 @@ function requireExactConfirmation(options) {
262
488
  throw new CliError({
263
489
  code: "CONFIRMATION_REQUIRED",
264
490
  domain: "database",
265
- summary: `Confirm ${options.resourceName} removal`,
266
- why: `Removing this ${options.resourceName} is destructive and requires the exact id.`,
491
+ summary: options.summary ?? `Confirm ${options.resourceName} removal`,
492
+ why: options.why ?? `Removing this ${options.resourceName} is destructive and requires the exact id.`,
267
493
  fix: `Rerun with --confirm ${options.id}.`,
268
494
  exitCode: 2,
269
- nextSteps: [`prisma-cli ${options.commandName} ${options.id} --confirm ${options.id}`],
495
+ nextSteps: [options.nextStep ?? `prisma-cli ${options.commandName} ${options.id} --confirm ${options.id}`],
270
496
  meta: {
271
497
  expectedConfirm: options.id,
272
498
  receivedConfirm: options.confirm ?? null
@@ -303,6 +529,23 @@ function databaseAmbiguousError(databaseRef, matches, branchName) {
303
529
  })) }
304
530
  });
305
531
  }
532
+ function backupNotFoundError(backupId, sourceDatabaseId, formatCommand) {
533
+ const listCommand = formatCommand([
534
+ "database",
535
+ "backup",
536
+ "list",
537
+ sourceDatabaseId
538
+ ]);
539
+ return new CliError({
540
+ code: "DATABASE_BACKUP_NOT_FOUND",
541
+ domain: "database",
542
+ summary: "Database backup not found",
543
+ why: `No backup matched "${backupId}" for database "${sourceDatabaseId}".`,
544
+ fix: `Pass a backup id from ${listCommand}.`,
545
+ exitCode: 1,
546
+ nextSteps: [listCommand]
547
+ });
548
+ }
306
549
  function connectionNotFoundError(connectionId) {
307
550
  return new CliError({
308
551
  code: "DATABASE_CONNECTION_NOT_FOUND",
@@ -315,4 +558,4 @@ function connectionNotFoundError(connectionId) {
315
558
  });
316
559
  }
317
560
  //#endregion
318
- export { runDatabaseConnectionCreate, runDatabaseConnectionList, runDatabaseConnectionRemove, runDatabaseCreate, runDatabaseList, runDatabaseRemove, runDatabaseShow };
561
+ export { runDatabaseBackupList, runDatabaseConnectionCreate, runDatabaseConnectionList, runDatabaseConnectionRemove, runDatabaseConnectionRotate, runDatabaseCreate, runDatabaseList, runDatabaseRemove, runDatabaseRestore, runDatabaseShow, runDatabaseUsage };