@prisma/cli 8.0.0-rc.1 → 8.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +15115 -16
- package/dist/deploy-framework-C7bQM00A.js +23 -0
- package/dist/{v8/sender.js → sender.js} +2 -2
- package/package.json +11 -15
- package/dist/adapters/git.js +0 -54
- package/dist/adapters/local-state.js +0 -155
- package/dist/auth/client.js +0 -30
- package/dist/auth/errors.js +0 -50
- package/dist/auth/guard.js +0 -39
- package/dist/auth/login.js +0 -471
- package/dist/auth/operations.js +0 -208
- package/dist/auth/recipient.js +0 -42
- package/dist/auth/token-storage.js +0 -439
- package/dist/auth/workspaces.js +0 -171
- package/dist/cli-command.js +0 -14
- package/dist/cli-name.js +0 -12
- package/dist/cli2.js +0 -136
- package/dist/command-arguments.js +0 -12
- package/dist/commands/agent/index.js +0 -60
- package/dist/commands/app/index.js +0 -350
- package/dist/commands/auth/index.js +0 -95
- package/dist/commands/branch/index.js +0 -27
- package/dist/commands/bucket/index.js +0 -123
- package/dist/commands/build/index.js +0 -29
- package/dist/commands/database/index.js +0 -249
- package/dist/commands/env.js +0 -99
- package/dist/commands/feedback/index.js +0 -20
- package/dist/commands/git/index.js +0 -36
- package/dist/commands/init/index.js +0 -33
- package/dist/commands/project/index.js +0 -118
- package/dist/commands/version/index.js +0 -18
- package/dist/controllers/agent-setup.js +0 -52
- package/dist/controllers/agent.js +0 -228
- package/dist/controllers/app-env-api.js +0 -55
- package/dist/controllers/app-env-file.js +0 -181
- package/dist/controllers/app-env.js +0 -502
- package/dist/controllers/app.js +0 -2508
- package/dist/controllers/auth.js +0 -124
- package/dist/controllers/branch.js +0 -92
- package/dist/controllers/bucket.js +0 -184
- package/dist/controllers/build.js +0 -88
- package/dist/controllers/database.js +0 -463
- package/dist/controllers/feedback.js +0 -90
- package/dist/controllers/init.js +0 -814
- package/dist/controllers/project.js +0 -892
- package/dist/controllers/select-prompt-port.js +0 -13
- package/dist/controllers/version.js +0 -12
- package/dist/errors.js +0 -102
- package/dist/lib/agent/cli-command.js +0 -20
- package/dist/lib/agent/constants.js +0 -12
- package/dist/lib/agent/package-manager.js +0 -99
- package/dist/lib/agent/setup-status.js +0 -83
- package/dist/lib/app/app-provider.js +0 -617
- package/dist/lib/app/branch-database-api.js +0 -102
- package/dist/lib/app/branch-database-deploy.js +0 -353
- package/dist/lib/app/branch-database.js +0 -234
- package/dist/lib/app/build-settings.js +0 -98
- package/dist/lib/app/build.js +0 -82
- package/dist/lib/app/bun-project.js +0 -45
- package/dist/lib/app/compute-config.js +0 -145
- package/dist/lib/app/deploy-output.js +0 -24
- package/dist/lib/app/deploy-plan.js +0 -59
- package/dist/lib/app/deploy-progress.js +0 -100
- package/dist/lib/app/domain-guidance.js +0 -14
- package/dist/lib/app/env-config.js +0 -67
- package/dist/lib/app/env-file.js +0 -82
- package/dist/lib/app/env-vars.js +0 -50
- package/dist/lib/app/local-dev.js +0 -109
- package/dist/lib/app/production-deploy-gate.js +0 -162
- package/dist/lib/app/read-branch.js +0 -30
- package/dist/lib/bucket/provider.js +0 -139
- package/dist/lib/database/provider.js +0 -385
- package/dist/lib/diagnostics.js +0 -16
- package/dist/lib/feedback.js +0 -15
- package/dist/lib/fs/home-path.js +0 -24
- package/dist/lib/git/local-branch.js +0 -53
- package/dist/lib/git/local-status.js +0 -57
- package/dist/lib/project/interactive-setup.js +0 -57
- package/dist/lib/project/local-pin.js +0 -200
- package/dist/lib/project/provider.js +0 -93
- package/dist/lib/project/resolution.js +0 -389
- package/dist/lib/project/setup.js +0 -138
- package/dist/lib/version.js +0 -56
- package/dist/lib/workspace-id.js +0 -18
- package/dist/output/patterns.js +0 -90
- package/dist/presenters/agent.js +0 -74
- package/dist/presenters/app-env.js +0 -265
- package/dist/presenters/app.js +0 -672
- package/dist/presenters/auth.js +0 -184
- package/dist/presenters/branch.js +0 -46
- package/dist/presenters/bucket.js +0 -174
- package/dist/presenters/database.js +0 -462
- package/dist/presenters/feedback.js +0 -26
- package/dist/presenters/init.js +0 -30
- package/dist/presenters/project.js +0 -268
- package/dist/presenters/verbose-context.js +0 -64
- package/dist/presenters/version.js +0 -29
- package/dist/shell/cli-command.js +0 -2
- package/dist/shell/command-meta.js +0 -923
- package/dist/shell/command-runner.js +0 -119
- package/dist/shell/diagnostics-output.js +0 -57
- package/dist/shell/global-flags.js +0 -37
- package/dist/shell/help.js +0 -90
- package/dist/shell/output.js +0 -144
- package/dist/shell/prompt.js +0 -48
- package/dist/shell/runtime.js +0 -38
- package/dist/shell/ui.js +0 -116
- package/dist/state-dir.js +0 -12
- package/dist/update-check.js +0 -248
- package/dist/v8/cli.js +0 -14699
|
@@ -1,385 +0,0 @@
|
|
|
1
|
-
import { formatPrismaCliCommand } from "../../cli-command.js";
|
|
2
|
-
import { CliError } from "../../errors.js";
|
|
3
|
-
import "../../shell/cli-command.js";
|
|
4
|
-
//#region src/lib/database/provider.ts
|
|
5
|
-
const SUBSCRIPTION_LOOKUP_TIMEOUT_MS = 3e3;
|
|
6
|
-
function createManagementDatabaseProvider(client, options) {
|
|
7
|
-
const formatCommand = options?.formatCommand ?? ((args) => formatPrismaCliCommand(args));
|
|
8
|
-
const toDatabaseApiError = (summary, response, error, signal) => databaseApiError({
|
|
9
|
-
client,
|
|
10
|
-
workspaceId: options?.workspaceId,
|
|
11
|
-
summary,
|
|
12
|
-
response,
|
|
13
|
-
error,
|
|
14
|
-
signal
|
|
15
|
-
});
|
|
16
|
-
return {
|
|
17
|
-
async listDatabases(options) {
|
|
18
|
-
const databases = [];
|
|
19
|
-
let cursor;
|
|
20
|
-
while (true) {
|
|
21
|
-
const result = await client.GET("/v1/databases", {
|
|
22
|
-
params: { query: {
|
|
23
|
-
projectId: options.projectId,
|
|
24
|
-
branchGitName: options.branchName,
|
|
25
|
-
cursor
|
|
26
|
-
} },
|
|
27
|
-
signal: options.signal
|
|
28
|
-
});
|
|
29
|
-
if (result.error || !result.data) throw await toDatabaseApiError("Failed to list databases", result.response, result.error, options.signal);
|
|
30
|
-
databases.push(...result.data.data);
|
|
31
|
-
if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
|
|
32
|
-
cursor = result.data.pagination.nextCursor;
|
|
33
|
-
}
|
|
34
|
-
return databases.map((database) => normalizeDatabase(database, options.projectId));
|
|
35
|
-
},
|
|
36
|
-
async showDatabase(databaseId, options) {
|
|
37
|
-
const result = await client.GET("/v1/databases/{databaseId}", {
|
|
38
|
-
params: { path: { databaseId } },
|
|
39
|
-
signal: options?.signal
|
|
40
|
-
});
|
|
41
|
-
if (result.response?.status === 404 && !isPlanLimitApiError(result.error)) return null;
|
|
42
|
-
if (result.error || !result.data) throw await toDatabaseApiError("Failed to show database", result.response, result.error, options?.signal);
|
|
43
|
-
const database = result.data.data;
|
|
44
|
-
return normalizeDatabase(database, requireDatabaseProjectId(database, options?.projectId));
|
|
45
|
-
},
|
|
46
|
-
async createDatabase(options) {
|
|
47
|
-
const result = await client.POST("/v1/databases", {
|
|
48
|
-
body: {
|
|
49
|
-
projectId: options.projectId,
|
|
50
|
-
name: options.name,
|
|
51
|
-
source: { type: "empty" },
|
|
52
|
-
...options.branchName ? { branchGitName: options.branchName } : {},
|
|
53
|
-
...options.region ? { region: options.region } : {}
|
|
54
|
-
},
|
|
55
|
-
signal: options.signal
|
|
56
|
-
});
|
|
57
|
-
if (result.error || !result.data) throw await toDatabaseApiError("Failed to create database", result.response, result.error, options.signal);
|
|
58
|
-
return normalizeCreatedDatabase(result.data.data, options.projectId);
|
|
59
|
-
},
|
|
60
|
-
async removeDatabase(databaseId, options) {
|
|
61
|
-
const result = await client.DELETE("/v1/databases/{databaseId}", {
|
|
62
|
-
params: { path: { databaseId } },
|
|
63
|
-
signal: options?.signal
|
|
64
|
-
});
|
|
65
|
-
if (result.error) throw await toDatabaseApiError("Failed to remove database", result.response, result.error, options?.signal);
|
|
66
|
-
},
|
|
67
|
-
async listConnections(databaseId, options) {
|
|
68
|
-
const result = await client.GET("/v1/databases/{databaseId}/connections", {
|
|
69
|
-
params: { path: { databaseId } },
|
|
70
|
-
signal: options?.signal
|
|
71
|
-
});
|
|
72
|
-
if (result.error || !result.data) throw await toDatabaseApiError("Failed to list database connections", result.response, result.error, options?.signal);
|
|
73
|
-
return result.data.data.map((connection) => normalizeConnection(connection, databaseId));
|
|
74
|
-
},
|
|
75
|
-
async createConnection(options) {
|
|
76
|
-
const result = await client.POST("/v1/databases/{databaseId}/connections", {
|
|
77
|
-
params: { path: { databaseId: options.databaseId } },
|
|
78
|
-
body: { name: options.name },
|
|
79
|
-
signal: options.signal
|
|
80
|
-
});
|
|
81
|
-
if (result.error || !result.data) throw await toDatabaseApiError("Failed to create database connection", result.response, result.error, options.signal);
|
|
82
|
-
return normalizeCreatedConnection(result.data.data, options.databaseId);
|
|
83
|
-
},
|
|
84
|
-
async removeConnection(connectionId, options) {
|
|
85
|
-
const result = await client.DELETE("/v1/connections/{id}", {
|
|
86
|
-
params: { path: { id: connectionId } },
|
|
87
|
-
signal: options?.signal
|
|
88
|
-
});
|
|
89
|
-
if (result.error) throw await toDatabaseApiError("Failed to remove database connection", result.response, result.error, options?.signal);
|
|
90
|
-
},
|
|
91
|
-
async getUsage(databaseId, options) {
|
|
92
|
-
const result = await client.GET("/v1/databases/{databaseId}/usage", {
|
|
93
|
-
params: {
|
|
94
|
-
path: { databaseId },
|
|
95
|
-
query: {
|
|
96
|
-
...options?.from ? { startDate: options.from } : {},
|
|
97
|
-
...options?.to ? { endDate: options.to } : {}
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
|
-
signal: options?.signal
|
|
101
|
-
});
|
|
102
|
-
if (result.error || !result.data) throw await toDatabaseApiError("Failed to fetch database usage", result.response, result.error, options?.signal);
|
|
103
|
-
return normalizeUsage(result.data);
|
|
104
|
-
},
|
|
105
|
-
async listBackups(databaseId, options) {
|
|
106
|
-
const result = await client.GET("/v1/databases/{databaseId}/backups", {
|
|
107
|
-
params: {
|
|
108
|
-
path: { databaseId },
|
|
109
|
-
query: { ...options?.limit !== void 0 ? { limit: options.limit } : {} }
|
|
110
|
-
},
|
|
111
|
-
signal: options?.signal
|
|
112
|
-
});
|
|
113
|
-
if (result.response?.status === 422 && !isPlanLimitApiError(result.error)) throw backupsUnsupportedError(databaseId, result.error);
|
|
114
|
-
if (result.error || !result.data) throw await toDatabaseApiError("Failed to list database backups", result.response, result.error, options?.signal);
|
|
115
|
-
return normalizeBackupList(result.data);
|
|
116
|
-
},
|
|
117
|
-
async restoreDatabase(options) {
|
|
118
|
-
const result = await client.POST("/v1/databases/{targetDatabaseId}/restore", {
|
|
119
|
-
params: { path: { targetDatabaseId: options.targetDatabaseId } },
|
|
120
|
-
body: { source: {
|
|
121
|
-
type: "backup",
|
|
122
|
-
databaseId: options.sourceDatabaseId,
|
|
123
|
-
backupId: options.backupId
|
|
124
|
-
} },
|
|
125
|
-
signal: options.signal
|
|
126
|
-
});
|
|
127
|
-
if (result.response?.status === 409 && !isPlanLimitApiError(result.error)) throw restoreConflictError(options.targetDatabaseId, result.error, formatCommand);
|
|
128
|
-
if (result.response?.status === 404 && !isPlanLimitApiError(result.error)) throw restoreBackupNotFoundError(options, result.error, formatCommand);
|
|
129
|
-
if (result.error || !result.data) throw await toDatabaseApiError("Failed to restore database", result.response, result.error, options.signal);
|
|
130
|
-
return normalizeDatabase(result.data.data, options.projectId);
|
|
131
|
-
},
|
|
132
|
-
async rotateConnection(connectionId, options) {
|
|
133
|
-
const result = await client.POST("/v1/connections/{id}/rotate", {
|
|
134
|
-
params: { path: { id: connectionId } },
|
|
135
|
-
signal: options?.signal
|
|
136
|
-
});
|
|
137
|
-
if (result.error || !result.data) throw await toDatabaseApiError("Failed to rotate database connection", result.response, result.error, options?.signal);
|
|
138
|
-
return normalizeRotatedConnection(result.data.data);
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
function normalizeDatabase(database, fallbackProjectId) {
|
|
143
|
-
return {
|
|
144
|
-
id: database.id,
|
|
145
|
-
name: database.name,
|
|
146
|
-
projectId: database.projectId ?? fallbackProjectId,
|
|
147
|
-
branchId: database.branchId ?? database.branch?.id ?? null,
|
|
148
|
-
branchName: database.branchGitName ?? database.branchName ?? database.branch?.gitName ?? database.branch?.name ?? null,
|
|
149
|
-
region: normalizeRegion(database),
|
|
150
|
-
status: database.status ?? null,
|
|
151
|
-
isDefault: database.isDefault ?? null,
|
|
152
|
-
createdAt: database.createdAt ?? null
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
function normalizeConnection(connection, fallbackDatabaseId) {
|
|
156
|
-
return {
|
|
157
|
-
id: connection.id,
|
|
158
|
-
name: connection.name ?? connection.id,
|
|
159
|
-
databaseId: connection.databaseId ?? fallbackDatabaseId,
|
|
160
|
-
createdAt: connection.createdAt ?? null
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
function normalizeCreatedDatabase(database, fallbackProjectId) {
|
|
164
|
-
const rawConnection = database.connections?.[0];
|
|
165
|
-
if (!rawConnection) throw new CliError({
|
|
166
|
-
code: "DATABASE_CONNECTION_MISSING",
|
|
167
|
-
domain: "database",
|
|
168
|
-
summary: "Created database did not return a connection string",
|
|
169
|
-
why: "The Management API created the database but did not include the one-time connection payload.",
|
|
170
|
-
fix: "Create a connection explicitly with prisma-cli database connection create <database>.",
|
|
171
|
-
exitCode: 1,
|
|
172
|
-
nextSteps: [`prisma-cli database connection create ${database.id}`]
|
|
173
|
-
});
|
|
174
|
-
return {
|
|
175
|
-
database: normalizeDatabase(database, fallbackProjectId),
|
|
176
|
-
...normalizeCreatedConnection(rawConnection, database.id)
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
function normalizeCreatedConnection(connection, fallbackDatabaseId) {
|
|
180
|
-
const connectionString = extractConnectionString(connection);
|
|
181
|
-
if (!connectionString) throw new CliError({
|
|
182
|
-
code: "DATABASE_CONNECTION_STRING_MISSING",
|
|
183
|
-
domain: "database",
|
|
184
|
-
summary: "Created connection did not return a connection string",
|
|
185
|
-
why: "Database connection strings are one-time-view secrets, but the Management API did not include one in this create response.",
|
|
186
|
-
fix: "Create another database connection and store the returned URL immediately.",
|
|
187
|
-
exitCode: 1,
|
|
188
|
-
nextSteps: [`prisma-cli database connection create ${fallbackDatabaseId}`]
|
|
189
|
-
});
|
|
190
|
-
return {
|
|
191
|
-
connection: normalizeConnection(connection, fallbackDatabaseId),
|
|
192
|
-
connectionString
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
function normalizeRegion(database) {
|
|
196
|
-
if (typeof database.region === "string") return database.region;
|
|
197
|
-
return database.region?.id ?? database.regionId ?? null;
|
|
198
|
-
}
|
|
199
|
-
function requireDatabaseProjectId(database, fallbackProjectId) {
|
|
200
|
-
const projectId = database.projectId ?? fallbackProjectId;
|
|
201
|
-
if (projectId) return projectId;
|
|
202
|
-
throw new CliError({
|
|
203
|
-
code: "DATABASE_API_ERROR",
|
|
204
|
-
domain: "database",
|
|
205
|
-
summary: "Database response did not include a project id",
|
|
206
|
-
why: "The Management API returned database metadata without project context.",
|
|
207
|
-
fix: "Re-run with --trace for the underlying API response details.",
|
|
208
|
-
exitCode: 1,
|
|
209
|
-
nextSteps: []
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
function extractConnectionString(connection) {
|
|
213
|
-
return connection.endpoints?.pooled?.connectionString ?? connection.connectionString ?? connection.endpoints?.direct?.connectionString ?? connection.endpoints?.accelerate?.connectionString ?? null;
|
|
214
|
-
}
|
|
215
|
-
/** Absence is carried, not filled in. A `0` here reached the card, the
|
|
216
|
-
* stdout lane and the json record as though the API had measured it, and
|
|
217
|
-
* "you used nothing" is the answer a user acts on. The unit is the same
|
|
218
|
-
* problem an order of magnitude worse: `GiB` was a guess, and a value the
|
|
219
|
-
* API denominates in bytes would print against it unchanged. */
|
|
220
|
-
function normalizeUsageMetric(metric) {
|
|
221
|
-
return {
|
|
222
|
-
used: metric?.used ?? null,
|
|
223
|
-
unit: metric?.unit ?? null
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
function normalizeUsage(usage) {
|
|
227
|
-
return {
|
|
228
|
-
period: {
|
|
229
|
-
start: usage.period?.start ?? null,
|
|
230
|
-
end: usage.period?.end ?? null
|
|
231
|
-
},
|
|
232
|
-
metrics: {
|
|
233
|
-
operations: normalizeUsageMetric(usage.metrics?.operations),
|
|
234
|
-
storage: normalizeUsageMetric(usage.metrics?.storage)
|
|
235
|
-
},
|
|
236
|
-
generatedAt: usage.generatedAt ?? null
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
function normalizeBackupList(body) {
|
|
240
|
-
return {
|
|
241
|
-
backups: (body.data ?? []).map((backup) => ({
|
|
242
|
-
id: backup.id,
|
|
243
|
-
backupType: backup.backupType ?? "",
|
|
244
|
-
status: backup.status ?? "",
|
|
245
|
-
size: backup.size ?? null,
|
|
246
|
-
createdAt: backup.createdAt ?? ""
|
|
247
|
-
})),
|
|
248
|
-
retentionDays: body.meta?.backupRetentionDays ?? null,
|
|
249
|
-
hasMore: body.pagination?.hasMore ?? false
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
function normalizeRotatedConnection(connection) {
|
|
253
|
-
const connectionString = extractConnectionString(connection);
|
|
254
|
-
if (!connectionString) throw new CliError({
|
|
255
|
-
code: "DATABASE_CONNECTION_STRING_MISSING",
|
|
256
|
-
domain: "database",
|
|
257
|
-
summary: "Rotated connection did not return a connection string",
|
|
258
|
-
why: "Rotated connection strings are one-time-view secrets, but the Management API did not include one in this rotate response.",
|
|
259
|
-
fix: "Re-run the rotation, or create a replacement connection and store the returned URL immediately.",
|
|
260
|
-
exitCode: 1,
|
|
261
|
-
nextSteps: []
|
|
262
|
-
});
|
|
263
|
-
const database = connection.database?.id && connection.database?.name ? {
|
|
264
|
-
id: connection.database.id,
|
|
265
|
-
name: connection.database.name
|
|
266
|
-
} : null;
|
|
267
|
-
return {
|
|
268
|
-
connection: normalizeConnection(connection, connection.database?.id ?? connection.databaseId ?? ""),
|
|
269
|
-
database,
|
|
270
|
-
connectionString
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
function backupsUnsupportedError(databaseId, error) {
|
|
274
|
-
return new CliError({
|
|
275
|
-
code: "DATABASE_BACKUPS_UNSUPPORTED",
|
|
276
|
-
domain: "database",
|
|
277
|
-
summary: "Backups are not available for this database",
|
|
278
|
-
why: error?.error?.message ?? `The platform does not manage backups for database "${databaseId}", for example because it is a remote/BYO database.`,
|
|
279
|
-
fix: "Use your own backup tooling for externally managed databases.",
|
|
280
|
-
exitCode: 1,
|
|
281
|
-
nextSteps: []
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
function restoreBackupNotFoundError(options, error, formatCommand) {
|
|
285
|
-
const listCommand = formatCommand([
|
|
286
|
-
"database",
|
|
287
|
-
"backup",
|
|
288
|
-
"list",
|
|
289
|
-
options.sourceDatabaseId
|
|
290
|
-
]);
|
|
291
|
-
return new CliError({
|
|
292
|
-
code: "DATABASE_BACKUP_NOT_FOUND",
|
|
293
|
-
domain: "database",
|
|
294
|
-
summary: "Database backup not found",
|
|
295
|
-
why: error?.error?.message ?? `No backup matched "${options.backupId}" for database "${options.sourceDatabaseId}".`,
|
|
296
|
-
fix: `Pass a backup id from ${listCommand}.`,
|
|
297
|
-
exitCode: 1,
|
|
298
|
-
nextSteps: [listCommand]
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
function restoreConflictError(targetDatabaseId, error, formatCommand) {
|
|
302
|
-
return new CliError({
|
|
303
|
-
code: "DATABASE_RESTORE_CONFLICT",
|
|
304
|
-
domain: "database",
|
|
305
|
-
summary: "Database cannot be restored right now",
|
|
306
|
-
why: error?.error?.message ?? `Database "${targetDatabaseId}" is provisioning or already recovering.`,
|
|
307
|
-
fix: "Wait for the database to become ready, then retry the restore.",
|
|
308
|
-
exitCode: 1,
|
|
309
|
-
nextSteps: [formatCommand([
|
|
310
|
-
"database",
|
|
311
|
-
"show",
|
|
312
|
-
targetDatabaseId
|
|
313
|
-
])]
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
async function databaseApiError(options) {
|
|
317
|
-
if (isPlanLimitApiError(options.error)) return planLimitReachedError(options);
|
|
318
|
-
const status = options.response?.status ?? 0;
|
|
319
|
-
return new CliError({
|
|
320
|
-
code: options.error?.error?.code ?? "DATABASE_API_ERROR",
|
|
321
|
-
domain: "database",
|
|
322
|
-
summary: options.summary,
|
|
323
|
-
why: options.error?.error?.message ?? `The Management API returned status ${status || "unknown"}.`,
|
|
324
|
-
fix: options.error?.error?.hint ?? "Re-run with --trace for the underlying API response details.",
|
|
325
|
-
exitCode: 1,
|
|
326
|
-
nextSteps: []
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
async function planLimitReachedError(options) {
|
|
330
|
-
const subscription = options.workspaceId ? await readWorkspaceSubscription(options.client, options.workspaceId, options.signal) : null;
|
|
331
|
-
const workspaceLine = options.workspaceId ? `Workspace: ${options.workspaceId}` : "Workspace: unavailable";
|
|
332
|
-
const planName = subscription?.planName || null;
|
|
333
|
-
const usageBlocked = subscription?.usageBlocked ?? null;
|
|
334
|
-
const upgradeUrl = subscription?.upgradeUrl || null;
|
|
335
|
-
const recoveryLines = [...planName ? [`Current plan: ${planName}`] : [], upgradeUrl ? `Upgrade: ${upgradeUrl}` : "Upgrade: Open Prisma Console and upgrade the affected workspace plan."];
|
|
336
|
-
return new CliError({
|
|
337
|
-
code: "PLAN_LIMIT_REACHED",
|
|
338
|
-
domain: "database",
|
|
339
|
-
summary: "Workspace plan limit reached",
|
|
340
|
-
why: "Database operations are blocked because this workspace has used the operations included in its plan. This is a workspace plan limit, not a Prisma outage.",
|
|
341
|
-
fix: upgradeUrl ? `Upgrade the workspace plan at ${upgradeUrl}.` : "Open Prisma Console and upgrade the affected workspace plan.",
|
|
342
|
-
meta: {
|
|
343
|
-
workspaceId: options.workspaceId ?? null,
|
|
344
|
-
blockedFeature: null,
|
|
345
|
-
planName,
|
|
346
|
-
usageBlocked,
|
|
347
|
-
upgradeUrl
|
|
348
|
-
},
|
|
349
|
-
exitCode: 1,
|
|
350
|
-
nextSteps: [],
|
|
351
|
-
humanLines: [
|
|
352
|
-
"Workspace plan limit reached [PLAN_LIMIT_REACHED]",
|
|
353
|
-
"",
|
|
354
|
-
"Database operations are blocked because this workspace has used the operations included in its plan. This is a workspace plan limit, not a Prisma outage.",
|
|
355
|
-
"",
|
|
356
|
-
workspaceLine,
|
|
357
|
-
...recoveryLines
|
|
358
|
-
]
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
function isPlanLimitApiError(error) {
|
|
362
|
-
return error?.error?.code === "planLimitReached";
|
|
363
|
-
}
|
|
364
|
-
async function readWorkspaceSubscription(client, workspaceId, signal) {
|
|
365
|
-
signal?.throwIfAborted();
|
|
366
|
-
const timeoutController = new AbortController();
|
|
367
|
-
const timeout = setTimeout(() => timeoutController.abort(), SUBSCRIPTION_LOOKUP_TIMEOUT_MS);
|
|
368
|
-
const requestSignal = signal ? AbortSignal.any([signal, timeoutController.signal]) : timeoutController.signal;
|
|
369
|
-
try {
|
|
370
|
-
const result = await client.GET("/v1/workspaces/{id}/subscription", {
|
|
371
|
-
params: { path: { id: workspaceId } },
|
|
372
|
-
signal: requestSignal
|
|
373
|
-
});
|
|
374
|
-
signal?.throwIfAborted();
|
|
375
|
-
if (result.error) return null;
|
|
376
|
-
return result.data?.data ?? null;
|
|
377
|
-
} catch {
|
|
378
|
-
signal?.throwIfAborted();
|
|
379
|
-
return null;
|
|
380
|
-
} finally {
|
|
381
|
-
clearTimeout(timeout);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
//#endregion
|
|
385
|
-
export { createManagementDatabaseProvider };
|
package/dist/lib/diagnostics.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { resolveLocalStateFilePath } from "../adapters/local-state.js";
|
|
2
|
-
import { resolveStateDir } from "../state-dir.js";
|
|
3
|
-
import "../shell/runtime.js";
|
|
4
|
-
import { readLocalGitState } from "./git/local-status.js";
|
|
5
|
-
//#region src/lib/diagnostics.ts
|
|
6
|
-
async function collectCommandDiagnostics(context, options = {}) {
|
|
7
|
-
const stateDir = await resolveStateDir(context.runtime);
|
|
8
|
-
return {
|
|
9
|
-
cwd: context.runtime.cwd,
|
|
10
|
-
stateFilePath: resolveLocalStateFilePath(stateDir),
|
|
11
|
-
git: await readLocalGitState(context.runtime.cwd, context.runtime.signal),
|
|
12
|
-
durationMs: options.durationMs
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
//#endregion
|
|
16
|
-
export { collectCommandDiagnostics };
|
package/dist/lib/feedback.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
//#region src/lib/feedback.ts
|
|
2
|
-
const FEEDBACK_TIMEOUT_MS = 3e3;
|
|
3
|
-
const TIMEOUT_DETAIL = `The feedback service did not answer within ${FEEDBACK_TIMEOUT_MS / 1e3} seconds.`;
|
|
4
|
-
function isTimeout(error) {
|
|
5
|
-
return error instanceof Error && error.name === "TimeoutError";
|
|
6
|
-
}
|
|
7
|
-
function unreachableDetail(error) {
|
|
8
|
-
if (isTimeout(error)) return TIMEOUT_DETAIL;
|
|
9
|
-
return `The feedback service could not be reached${error instanceof Error && error.cause instanceof Error ? ` (${error.cause.message})` : ""}.`;
|
|
10
|
-
}
|
|
11
|
-
function unreadableBodyDetail(error) {
|
|
12
|
-
return isTimeout(error) ? TIMEOUT_DETAIL : "The feedback service response could not be read.";
|
|
13
|
-
}
|
|
14
|
-
//#endregion
|
|
15
|
-
export { FEEDBACK_TIMEOUT_MS, unreachableDetail, unreadableBodyDetail };
|
package/dist/lib/fs/home-path.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
//#region src/lib/fs/home-path.ts
|
|
3
|
-
/**
|
|
4
|
-
* Shortens a path under the user's home directory to `~/...` for display,
|
|
5
|
-
* posix-style on every platform. Falls back to the Windows home variables
|
|
6
|
-
* when `HOME` is unset (native cmd/PowerShell sessions).
|
|
7
|
-
*/
|
|
8
|
-
function shortenHomePath(value, env) {
|
|
9
|
-
const resolved = path.resolve(value);
|
|
10
|
-
const home = resolveHomeDirectory(env);
|
|
11
|
-
if (home && (resolved === home || resolved.startsWith(`${home}${path.sep}`))) {
|
|
12
|
-
const relative = path.relative(home, resolved).split(path.sep).join("/");
|
|
13
|
-
return relative ? `~/${relative}` : "~";
|
|
14
|
-
}
|
|
15
|
-
return resolved;
|
|
16
|
-
}
|
|
17
|
-
function resolveHomeDirectory(env) {
|
|
18
|
-
if (env.HOME) return path.resolve(env.HOME);
|
|
19
|
-
if (env.USERPROFILE) return path.resolve(env.USERPROFILE);
|
|
20
|
-
if (env.HOMEDRIVE && env.HOMEPATH) return path.resolve(`${env.HOMEDRIVE}${env.HOMEPATH}`);
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
//#endregion
|
|
24
|
-
export { shortenHomePath };
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { access, readFile } from "node:fs/promises";
|
|
3
|
-
//#region src/lib/git/local-branch.ts
|
|
4
|
-
/**
|
|
5
|
-
* Resolves the checked-out branch the way git does: the nearest `.git`
|
|
6
|
-
* (directory or worktree file) from `cwd` upward owns the answer, so
|
|
7
|
-
* monorepo commands run from inside a package see the repository branch.
|
|
8
|
-
* Returns null for detached HEAD or when no repository contains `cwd`.
|
|
9
|
-
*/
|
|
10
|
-
async function readLocalGitBranch(cwd, signal) {
|
|
11
|
-
for (let directory = path.resolve(cwd);;) {
|
|
12
|
-
const headPath = await resolveGitHeadPath(path.join(directory, ".git"), signal);
|
|
13
|
-
if (headPath) return readBranchFromHead(headPath, signal);
|
|
14
|
-
const parent = path.dirname(directory);
|
|
15
|
-
if (parent === directory) return null;
|
|
16
|
-
directory = parent;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
async function readBranchFromHead(headPath, signal) {
|
|
20
|
-
try {
|
|
21
|
-
const head = (await readFile(headPath, {
|
|
22
|
-
encoding: "utf8",
|
|
23
|
-
signal
|
|
24
|
-
})).trim();
|
|
25
|
-
if (head.startsWith("ref: refs/heads/")) return head.slice(16);
|
|
26
|
-
} catch (error) {
|
|
27
|
-
if (signal.aborted) throw error;
|
|
28
|
-
}
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
async function resolveGitHeadPath(gitPath, signal) {
|
|
32
|
-
signal.throwIfAborted();
|
|
33
|
-
try {
|
|
34
|
-
const raw = await readFile(gitPath, {
|
|
35
|
-
encoding: "utf8",
|
|
36
|
-
signal
|
|
37
|
-
});
|
|
38
|
-
if (raw.startsWith("gitdir:")) return path.join(path.resolve(path.dirname(gitPath), raw.slice(7).trim()), "HEAD");
|
|
39
|
-
} catch (error) {
|
|
40
|
-
if (signal.aborted) throw error;
|
|
41
|
-
}
|
|
42
|
-
signal.throwIfAborted();
|
|
43
|
-
try {
|
|
44
|
-
await access(path.join(gitPath, "HEAD"));
|
|
45
|
-
signal.throwIfAborted();
|
|
46
|
-
return path.join(gitPath, "HEAD");
|
|
47
|
-
} catch (error) {
|
|
48
|
-
if (signal.aborted) throw error;
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
//#endregion
|
|
53
|
-
export { readLocalGitBranch };
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
//#region src/lib/git/local-status.ts
|
|
3
|
-
async function readLocalGitState(cwd, signal) {
|
|
4
|
-
signal.throwIfAborted();
|
|
5
|
-
if ((await runGit(cwd, ["rev-parse", "--is-inside-work-tree"], signal))?.trim() !== "true") return null;
|
|
6
|
-
const [ref, sha, status] = await Promise.all([
|
|
7
|
-
runGit(cwd, [
|
|
8
|
-
"symbolic-ref",
|
|
9
|
-
"--quiet",
|
|
10
|
-
"--short",
|
|
11
|
-
"HEAD"
|
|
12
|
-
], signal),
|
|
13
|
-
runGit(cwd, [
|
|
14
|
-
"rev-parse",
|
|
15
|
-
"--short",
|
|
16
|
-
"HEAD"
|
|
17
|
-
], signal),
|
|
18
|
-
runGit(cwd, ["status", "--porcelain"], signal)
|
|
19
|
-
]);
|
|
20
|
-
return {
|
|
21
|
-
ref: cleanGitValue(ref),
|
|
22
|
-
sha: cleanGitValue(sha),
|
|
23
|
-
dirty: status === null ? null : status.trim().length > 0
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
function runGit(cwd, args, signal) {
|
|
27
|
-
return new Promise((resolve, reject) => {
|
|
28
|
-
signal.throwIfAborted();
|
|
29
|
-
execFile("git", args, {
|
|
30
|
-
cwd,
|
|
31
|
-
signal,
|
|
32
|
-
timeout: 2e3
|
|
33
|
-
}, (error, stdout) => {
|
|
34
|
-
if (signal.aborted) {
|
|
35
|
-
reject(error);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
if (error) {
|
|
39
|
-
resolve(null);
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
resolve(stdout);
|
|
43
|
-
}).on("error", (error) => {
|
|
44
|
-
if (signal.aborted) {
|
|
45
|
-
reject(error);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
resolve(null);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
function cleanGitValue(value) {
|
|
53
|
-
const cleaned = value?.trim();
|
|
54
|
-
return cleaned ? cleaned : null;
|
|
55
|
-
}
|
|
56
|
-
//#endregion
|
|
57
|
-
export { readLocalGitState };
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { usageError } from "../../errors.js";
|
|
2
|
-
import { selectPrompt, textPrompt } from "../../shell/prompt.js";
|
|
3
|
-
import { inferTargetName, sortProjects } from "./resolution.js";
|
|
4
|
-
import { toProjectSummary, validateProjectSetupNameText } from "./setup.js";
|
|
5
|
-
//#region src/lib/project/interactive-setup.ts
|
|
6
|
-
async function promptForProjectSetupChoice(options) {
|
|
7
|
-
const sortedProjects = sortProjects(options.projects);
|
|
8
|
-
const projectNames = sortedProjects.map((project) => project.name);
|
|
9
|
-
const duplicateNames = new Set(projectNames.filter((name, index) => projectNames.indexOf(name) !== index));
|
|
10
|
-
const choice = await selectPrompt({
|
|
11
|
-
input: options.context.runtime.stdin,
|
|
12
|
-
output: options.context.runtime.stderr,
|
|
13
|
-
message: "Which Project should this directory use?",
|
|
14
|
-
choices: [
|
|
15
|
-
{
|
|
16
|
-
label: "+ Create a new Project",
|
|
17
|
-
value: { kind: "create" }
|
|
18
|
-
},
|
|
19
|
-
...sortedProjects.map((project) => ({
|
|
20
|
-
label: duplicateNames.has(project.name) ? `${project.name} (${project.id})` : project.name,
|
|
21
|
-
value: {
|
|
22
|
-
kind: "project",
|
|
23
|
-
project
|
|
24
|
-
}
|
|
25
|
-
})),
|
|
26
|
-
{
|
|
27
|
-
label: "Cancel",
|
|
28
|
-
value: { kind: "cancel" }
|
|
29
|
-
}
|
|
30
|
-
]
|
|
31
|
-
});
|
|
32
|
-
if (choice.kind === "cancel") throw usageError("Project setup canceled", options.cancel.why, options.cancel.fix, options.cancel.nextSteps, "project");
|
|
33
|
-
if (choice.kind === "project") return {
|
|
34
|
-
project: toProjectSummary(choice.project),
|
|
35
|
-
action: "linked",
|
|
36
|
-
targetName: choice.project.name,
|
|
37
|
-
targetNameSource: "prompt"
|
|
38
|
-
};
|
|
39
|
-
const suggestedName = await inferTargetName(options.context.runtime.cwd, options.context.runtime.signal);
|
|
40
|
-
const rawName = await textPrompt({
|
|
41
|
-
input: options.context.runtime.stdin,
|
|
42
|
-
output: options.context.runtime.stderr,
|
|
43
|
-
signal: options.context.runtime.signal,
|
|
44
|
-
message: "Project name",
|
|
45
|
-
placeholder: suggestedName.name,
|
|
46
|
-
validate: (value) => validateProjectSetupNameText(value, suggestedName.name)
|
|
47
|
-
});
|
|
48
|
-
const projectName = rawName.trim() || suggestedName.name;
|
|
49
|
-
return {
|
|
50
|
-
project: toProjectSummary(await options.createProject(projectName)),
|
|
51
|
-
action: "created",
|
|
52
|
-
targetName: projectName,
|
|
53
|
-
targetNameSource: rawName.trim() ? "prompt" : suggestedName.source
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
//#endregion
|
|
57
|
-
export { promptForProjectSetupChoice };
|