@prisma/cli 3.0.0-dev.99.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/README.md +3 -1
- package/dist/cli.js +15115 -16
- package/dist/deploy-framework-C7bQM00A.js +23 -0
- package/dist/payload-B88Qvzxk-CrtTXSOe.js +34 -0
- package/dist/sender.js +192 -0
- package/package.json +22 -18
- package/dist/adapters/git.js +0 -54
- package/dist/adapters/local-state.js +0 -144
- package/dist/adapters/mock-api.js +0 -136
- package/dist/adapters/token-storage.js +0 -418
- package/dist/cli2.js +0 -119
- package/dist/commands/app/index.js +0 -345
- package/dist/commands/auth/index.js +0 -96
- package/dist/commands/branch/index.js +0 -27
- package/dist/commands/database/index.js +0 -159
- package/dist/commands/env.js +0 -99
- package/dist/commands/git/index.js +0 -36
- package/dist/commands/project/index.js +0 -69
- package/dist/commands/version/index.js +0 -18
- package/dist/controllers/app-env-api.js +0 -54
- package/dist/controllers/app-env-file.js +0 -181
- package/dist/controllers/app-env.js +0 -502
- package/dist/controllers/app.js +0 -2443
- package/dist/controllers/auth.js +0 -316
- package/dist/controllers/branch.js +0 -103
- package/dist/controllers/database.js +0 -318
- package/dist/controllers/project.js +0 -731
- package/dist/controllers/select-prompt-port.js +0 -12
- package/dist/controllers/version.js +0 -12
- package/dist/lib/app/app-interaction.js +0 -5
- package/dist/lib/app/app-provider.js +0 -544
- package/dist/lib/app/branch-database-api.js +0 -102
- package/dist/lib/app/branch-database-deploy.js +0 -325
- package/dist/lib/app/branch-database.js +0 -215
- package/dist/lib/app/build-settings.js +0 -93
- package/dist/lib/app/build.js +0 -80
- package/dist/lib/app/bun-project.js +0 -45
- package/dist/lib/app/compute-config.js +0 -147
- package/dist/lib/app/deploy-output.js +0 -24
- package/dist/lib/app/deploy-plan.js +0 -58
- 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 -161
- package/dist/lib/app/read-branch.js +0 -30
- package/dist/lib/auth/auth-ops.js +0 -158
- package/dist/lib/auth/client.js +0 -22
- package/dist/lib/auth/guard.js +0 -38
- package/dist/lib/auth/login.js +0 -330
- package/dist/lib/database/provider.js +0 -167
- package/dist/lib/diagnostics.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 -56
- package/dist/lib/project/local-pin.js +0 -200
- package/dist/lib/project/resolution.js +0 -386
- package/dist/lib/project/setup.js +0 -135
- package/dist/lib/version.js +0 -55
- package/dist/output/patterns.js +0 -90
- package/dist/presenters/app-env.js +0 -265
- package/dist/presenters/app.js +0 -646
- package/dist/presenters/auth.js +0 -183
- package/dist/presenters/branch.js +0 -46
- package/dist/presenters/database.js +0 -274
- package/dist/presenters/project.js +0 -174
- package/dist/presenters/verbose-context.js +0 -64
- package/dist/presenters/version.js +0 -29
- package/dist/shell/command-arguments.js +0 -6
- package/dist/shell/command-meta.js +0 -622
- package/dist/shell/command-runner.js +0 -112
- package/dist/shell/diagnostics-output.js +0 -57
- package/dist/shell/errors.js +0 -134
- package/dist/shell/global-flags.js +0 -37
- package/dist/shell/help.js +0 -89
- package/dist/shell/output.js +0 -82
- package/dist/shell/prompt.js +0 -41
- package/dist/shell/runtime.js +0 -55
- package/dist/shell/ui.js +0 -116
- package/dist/shell/update-check.js +0 -247
- package/dist/use-cases/auth.js +0 -145
- package/dist/use-cases/branch.js +0 -47
- package/dist/use-cases/create-cli-gateways.js +0 -68
- package/dist/use-cases/project.js +0 -30
package/dist/lib/auth/login.js
DELETED
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
import { getApiBaseUrl } from "./client.js";
|
|
2
|
-
import { FileTokenStorage } from "../../adapters/token-storage.js";
|
|
3
|
-
import open from "open";
|
|
4
|
-
import { AuthError, createManagementApiSdk } from "@prisma/management-api-sdk";
|
|
5
|
-
import events from "node:events";
|
|
6
|
-
import http from "node:http";
|
|
7
|
-
import readline from "node:readline/promises";
|
|
8
|
-
//#region src/lib/auth/login.ts
|
|
9
|
-
var AuthError$1 = class extends Error {
|
|
10
|
-
constructor(message) {
|
|
11
|
-
super(message);
|
|
12
|
-
this.name = "AuthError";
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
async function login(options = {}) {
|
|
16
|
-
const hostname = options.hostname ?? "localhost";
|
|
17
|
-
const port = options.port ?? 0;
|
|
18
|
-
const input = options.input ?? process.stdin;
|
|
19
|
-
const output = options.output ?? process.stderr;
|
|
20
|
-
const interactive = input.isTTY === true;
|
|
21
|
-
const server = http.createServer();
|
|
22
|
-
server.listen({
|
|
23
|
-
host: hostname,
|
|
24
|
-
port
|
|
25
|
-
});
|
|
26
|
-
const pasteAbort = new AbortController();
|
|
27
|
-
try {
|
|
28
|
-
const state = new LoginState({
|
|
29
|
-
hostname,
|
|
30
|
-
port: (await events.once(server, "listening").then(() => server.address())).port,
|
|
31
|
-
tokenStorage: options.tokenStorage,
|
|
32
|
-
clientId: options.clientId,
|
|
33
|
-
apiBaseUrl: options.apiBaseUrl,
|
|
34
|
-
authBaseUrl: options.authBaseUrl,
|
|
35
|
-
openUrl: options.openUrl,
|
|
36
|
-
env: options.env,
|
|
37
|
-
signal: options.signal,
|
|
38
|
-
output
|
|
39
|
-
});
|
|
40
|
-
let completed = false;
|
|
41
|
-
let completion;
|
|
42
|
-
const completeOnce = (url) => {
|
|
43
|
-
if (!completion) completion = state.handleCallback(url).then(() => {
|
|
44
|
-
completed = true;
|
|
45
|
-
}, (error) => {
|
|
46
|
-
completion = void 0;
|
|
47
|
-
throw error;
|
|
48
|
-
});
|
|
49
|
-
return completion;
|
|
50
|
-
};
|
|
51
|
-
const httpResult = new Promise((resolve, reject) => {
|
|
52
|
-
const onAbort = () => {
|
|
53
|
-
reject(options.signal?.reason);
|
|
54
|
-
};
|
|
55
|
-
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
56
|
-
const settle = (callback) => {
|
|
57
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
58
|
-
callback();
|
|
59
|
-
};
|
|
60
|
-
server.on("request", async (req, res) => {
|
|
61
|
-
const url = new URL(`http://${state.host}${req.url}`);
|
|
62
|
-
if (url.pathname !== "/auth/callback") {
|
|
63
|
-
res.statusCode = 404;
|
|
64
|
-
res.end("Not found");
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
if (completed) {
|
|
68
|
-
const workspaceName = await state.resolveWorkspaceName();
|
|
69
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
70
|
-
res.end(renderSuccessPage(workspaceName));
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
try {
|
|
74
|
-
await completeOnce(url);
|
|
75
|
-
const workspaceName = await state.resolveWorkspaceName();
|
|
76
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
77
|
-
res.end(renderSuccessPage(workspaceName));
|
|
78
|
-
settle(resolve);
|
|
79
|
-
} catch (error) {
|
|
80
|
-
res.statusCode = 400;
|
|
81
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
82
|
-
res.end(message);
|
|
83
|
-
settle(() => reject(error));
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
options.signal?.throwIfAborted();
|
|
89
|
-
const callbackResult = interactive ? Promise.race([httpResult, consumePastedCallback({
|
|
90
|
-
input,
|
|
91
|
-
output,
|
|
92
|
-
signal: pasteAbort.signal,
|
|
93
|
-
complete: completeOnce
|
|
94
|
-
})]) : httpResult;
|
|
95
|
-
await Promise.all([state.openLoginPage(interactive), callbackResult]);
|
|
96
|
-
} finally {
|
|
97
|
-
pasteAbort.abort();
|
|
98
|
-
if (server.listening) await new Promise((resolve) => server.close(() => resolve()));
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
async function consumePastedCallback(options) {
|
|
102
|
-
if (!options.input.isTTY) return;
|
|
103
|
-
const rl = readline.createInterface({
|
|
104
|
-
input: options.input,
|
|
105
|
-
output: options.output
|
|
106
|
-
});
|
|
107
|
-
try {
|
|
108
|
-
for (;;) {
|
|
109
|
-
const url = await readPastedCallbackUrl(rl, options);
|
|
110
|
-
if (url === null) return;
|
|
111
|
-
if (url === void 0) continue;
|
|
112
|
-
if (await tryCompletePastedCallback(url, options)) return;
|
|
113
|
-
}
|
|
114
|
-
} finally {
|
|
115
|
-
rl.close();
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
async function readPastedCallbackUrl(rl, options) {
|
|
119
|
-
let answer;
|
|
120
|
-
try {
|
|
121
|
-
answer = await rl.question("Paste the callback URL here: ", { signal: options.signal });
|
|
122
|
-
} catch (error) {
|
|
123
|
-
if (error?.name === "AbortError") return null;
|
|
124
|
-
throw error;
|
|
125
|
-
}
|
|
126
|
-
const trimmed = answer.trim().replace(/^["']|["']$/g, "");
|
|
127
|
-
try {
|
|
128
|
-
if (!trimmed) throw new Error("empty input");
|
|
129
|
-
return new URL(trimmed);
|
|
130
|
-
} catch {
|
|
131
|
-
options.output.write("That didn't look like a URL. Paste the full localhost callback URL and try again.\n");
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
async function tryCompletePastedCallback(url, options) {
|
|
136
|
-
try {
|
|
137
|
-
await options.complete(url);
|
|
138
|
-
return true;
|
|
139
|
-
} catch (error) {
|
|
140
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
141
|
-
options.output.write(`Sign-in didn't complete (${message}). Paste the callback URL to try again.\n`);
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
var LoginState = class {
|
|
146
|
-
latestVerifier;
|
|
147
|
-
latestState;
|
|
148
|
-
sdk;
|
|
149
|
-
openUrl;
|
|
150
|
-
tokenStorage;
|
|
151
|
-
output;
|
|
152
|
-
constructor(options) {
|
|
153
|
-
this.options = options;
|
|
154
|
-
this.tokenStorage = options.tokenStorage ?? new FileTokenStorage(options.env, options.signal, { activateOnSetTokens: true });
|
|
155
|
-
this.sdk = createManagementApiSdk({
|
|
156
|
-
clientId: options.clientId ?? "cmm3lndn701oo0uefvxzo0ivw",
|
|
157
|
-
redirectUri: `http://${options.hostname}:${options.port}/auth/callback`,
|
|
158
|
-
tokenStorage: this.tokenStorage,
|
|
159
|
-
apiBaseUrl: options.apiBaseUrl ?? getApiBaseUrl(options.env),
|
|
160
|
-
authBaseUrl: options.authBaseUrl
|
|
161
|
-
});
|
|
162
|
-
this.openUrl = options.openUrl ?? open;
|
|
163
|
-
this.output = options.output;
|
|
164
|
-
}
|
|
165
|
-
async openLoginPage(interactive) {
|
|
166
|
-
this.options.signal?.throwIfAborted();
|
|
167
|
-
const { url, state, verifier } = await this.sdk.getLoginUrl({
|
|
168
|
-
scope: "workspace:admin offline_access",
|
|
169
|
-
additionalParams: {
|
|
170
|
-
utm_source: "prisma-cli",
|
|
171
|
-
utm_medium: "command-login",
|
|
172
|
-
utm_campaign: "prisma-cli"
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
this.latestState = state;
|
|
176
|
-
this.latestVerifier = verifier;
|
|
177
|
-
this.options.signal?.throwIfAborted();
|
|
178
|
-
if (interactive) this.printLoginInstructions(url);
|
|
179
|
-
try {
|
|
180
|
-
await this.openUrl(url);
|
|
181
|
-
} catch (error) {
|
|
182
|
-
if (!interactive) throw error;
|
|
183
|
-
}
|
|
184
|
-
this.options.signal?.throwIfAborted();
|
|
185
|
-
}
|
|
186
|
-
printLoginInstructions(url) {
|
|
187
|
-
const output = this.output;
|
|
188
|
-
if (!output) return;
|
|
189
|
-
output.write(`\nOpen this URL to sign in: ${url}\n\nIf the browser opens on another machine, finish sign-in there. When it\nredirects to localhost, copy the full localhost URL from the address bar\nand paste it here.\n\n`);
|
|
190
|
-
}
|
|
191
|
-
async handleCallback(url) {
|
|
192
|
-
if (url.pathname !== "/auth/callback") throw new AuthError$1("Not a callback URL");
|
|
193
|
-
const params = url.searchParams;
|
|
194
|
-
const error = params.get("error");
|
|
195
|
-
if (error) {
|
|
196
|
-
const desc = params.get("error_description");
|
|
197
|
-
throw new AuthError$1(desc ? `${error}: ${desc}` : error);
|
|
198
|
-
}
|
|
199
|
-
if (!this.latestVerifier) throw new AuthError$1("No verifier found");
|
|
200
|
-
if (!this.latestState) throw new AuthError$1("No state found");
|
|
201
|
-
try {
|
|
202
|
-
await this.sdk.handleCallback({
|
|
203
|
-
callbackUrl: url,
|
|
204
|
-
verifier: this.latestVerifier,
|
|
205
|
-
expectedState: this.latestState
|
|
206
|
-
});
|
|
207
|
-
} catch (error) {
|
|
208
|
-
if (error instanceof AuthError) throw new AuthError$1(error.message);
|
|
209
|
-
throw new AuthError$1(error instanceof Error ? error.message : "Unknown error during login");
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
async resolveWorkspaceName() {
|
|
213
|
-
try {
|
|
214
|
-
const tokens = await this.tokenStorage.getTokens();
|
|
215
|
-
if (!tokens?.workspaceId) return null;
|
|
216
|
-
const { data } = await this.sdk.client.GET("/v1/workspaces/{id}", {
|
|
217
|
-
params: { path: { id: tokens.workspaceId } },
|
|
218
|
-
signal: this.options.signal
|
|
219
|
-
});
|
|
220
|
-
const name = data?.data?.name;
|
|
221
|
-
return typeof name === "string" && name.trim().length > 0 ? name.trim() : null;
|
|
222
|
-
} catch {
|
|
223
|
-
this.options.signal?.throwIfAborted();
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
get host() {
|
|
228
|
-
return `${this.options.hostname}:${this.options.port}`;
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
function renderSuccessPage(workspaceName) {
|
|
232
|
-
return `<!doctype html>
|
|
233
|
-
<html lang="en">
|
|
234
|
-
<head>
|
|
235
|
-
<meta charset="utf-8">
|
|
236
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
237
|
-
<title>Prisma Developer Platform</title>
|
|
238
|
-
<style>
|
|
239
|
-
:root {
|
|
240
|
-
color-scheme: light dark;
|
|
241
|
-
--background: #ffffff;
|
|
242
|
-
--foreground: #1f2430;
|
|
243
|
-
--muted: #4f5665;
|
|
244
|
-
--mark-color: #050812;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
@media (prefers-color-scheme: dark) {
|
|
248
|
-
:root {
|
|
249
|
-
--background: #050812;
|
|
250
|
-
--foreground: #f6f7fb;
|
|
251
|
-
--muted: #c5cad6;
|
|
252
|
-
--mark-color: #ffffff;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
* {
|
|
257
|
-
box-sizing: border-box;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
body {
|
|
261
|
-
min-height: 100vh;
|
|
262
|
-
margin: 0;
|
|
263
|
-
background: var(--background);
|
|
264
|
-
color: var(--foreground);
|
|
265
|
-
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
266
|
-
display: grid;
|
|
267
|
-
grid-template-rows: 128px 1fr;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
.mark {
|
|
271
|
-
align-self: end;
|
|
272
|
-
justify-self: center;
|
|
273
|
-
width: 36px;
|
|
274
|
-
height: 36px;
|
|
275
|
-
color: var(--mark-color);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
.mark path {
|
|
279
|
-
fill: currentColor !important;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
main {
|
|
283
|
-
align-self: center;
|
|
284
|
-
justify-self: center;
|
|
285
|
-
width: min(520px, calc(100vw - 48px));
|
|
286
|
-
margin-top: -128px;
|
|
287
|
-
text-align: center;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
h1 {
|
|
291
|
-
margin: 0 0 12px;
|
|
292
|
-
font-size: 26px;
|
|
293
|
-
line-height: 1.2;
|
|
294
|
-
font-weight: 700;
|
|
295
|
-
letter-spacing: 0;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
p {
|
|
299
|
-
margin: 0 auto;
|
|
300
|
-
max-width: 480px;
|
|
301
|
-
color: var(--muted);
|
|
302
|
-
font-size: 15px;
|
|
303
|
-
line-height: 1.55;
|
|
304
|
-
letter-spacing: 0;
|
|
305
|
-
}
|
|
306
|
-
</style>
|
|
307
|
-
</head>
|
|
308
|
-
<body>
|
|
309
|
-
<svg class="mark" width="36" height="36" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M25.21,24.21,12.739,27.928a.525.525,0,0,1-.667-.606L16.528,5.811a.43.43,0,0,1,.809-.094l8.249,17.661A.6.6,0,0,1,25.21,24.21Zm2.139-.878L17.8,2.883h0A1.531,1.531,0,0,0,16.491,2a1.513,1.513,0,0,0-1.4.729L4.736,19.648a1.592,1.592,0,0,0,.018,1.7l5.064,7.909a1.628,1.628,0,0,0,1.83.678l14.7-4.383a1.6,1.6,0,0,0,1-2.218Z" style="fill:#0c344b;fill-rule:evenodd"/></svg>
|
|
310
|
-
<main>
|
|
311
|
-
<h1>You're all set.</h1>
|
|
312
|
-
<p>${workspaceName ? `Your terminal is now connected to your ${escapeHtml(workspaceName)} workspace. Head back to your terminal to continue.` : "Your terminal is now connected to your Prisma workspace. Head back to your terminal to continue."}</p>
|
|
313
|
-
</main>
|
|
314
|
-
</body>
|
|
315
|
-
</html>`;
|
|
316
|
-
}
|
|
317
|
-
function escapeHtml(value) {
|
|
318
|
-
return value.replace(/[&<>"']/g, (char) => {
|
|
319
|
-
switch (char) {
|
|
320
|
-
case "&": return "&";
|
|
321
|
-
case "<": return "<";
|
|
322
|
-
case ">": return ">";
|
|
323
|
-
case "\"": return """;
|
|
324
|
-
case "'": return "'";
|
|
325
|
-
default: return char;
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
//#endregion
|
|
330
|
-
export { login };
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import { CliError } from "../../shell/errors.js";
|
|
2
|
-
//#region src/lib/database/provider.ts
|
|
3
|
-
function createManagementDatabaseProvider(client) {
|
|
4
|
-
return {
|
|
5
|
-
async listDatabases(options) {
|
|
6
|
-
const databases = [];
|
|
7
|
-
let cursor;
|
|
8
|
-
while (true) {
|
|
9
|
-
const result = await client.GET("/v1/databases", {
|
|
10
|
-
params: { query: {
|
|
11
|
-
projectId: options.projectId,
|
|
12
|
-
branchGitName: options.branchName,
|
|
13
|
-
cursor
|
|
14
|
-
} },
|
|
15
|
-
signal: options.signal
|
|
16
|
-
});
|
|
17
|
-
if (result.error || !result.data) throw databaseApiError("Failed to list databases", result.response, result.error);
|
|
18
|
-
databases.push(...result.data.data);
|
|
19
|
-
if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
|
|
20
|
-
cursor = result.data.pagination.nextCursor;
|
|
21
|
-
}
|
|
22
|
-
return databases.map((database) => normalizeDatabase(database, options.projectId));
|
|
23
|
-
},
|
|
24
|
-
async showDatabase(databaseId, options) {
|
|
25
|
-
const result = await client.GET("/v1/databases/{databaseId}", {
|
|
26
|
-
params: { path: { databaseId } },
|
|
27
|
-
signal: options?.signal
|
|
28
|
-
});
|
|
29
|
-
if (result.response?.status === 404) return null;
|
|
30
|
-
if (result.error || !result.data) throw databaseApiError("Failed to show database", result.response, result.error);
|
|
31
|
-
const database = result.data.data;
|
|
32
|
-
return normalizeDatabase(database, requireDatabaseProjectId(database, options?.projectId));
|
|
33
|
-
},
|
|
34
|
-
async createDatabase(options) {
|
|
35
|
-
const result = await client.POST("/v1/databases", {
|
|
36
|
-
body: {
|
|
37
|
-
projectId: options.projectId,
|
|
38
|
-
name: options.name,
|
|
39
|
-
source: { type: "empty" },
|
|
40
|
-
...options.branchName ? { branchGitName: options.branchName } : {},
|
|
41
|
-
...options.region ? { region: options.region } : {}
|
|
42
|
-
},
|
|
43
|
-
signal: options.signal
|
|
44
|
-
});
|
|
45
|
-
if (result.error || !result.data) throw databaseApiError("Failed to create database", result.response, result.error);
|
|
46
|
-
return normalizeCreatedDatabase(result.data.data, options.projectId);
|
|
47
|
-
},
|
|
48
|
-
async removeDatabase(databaseId, options) {
|
|
49
|
-
const result = await client.DELETE("/v1/databases/{databaseId}", {
|
|
50
|
-
params: { path: { databaseId } },
|
|
51
|
-
signal: options?.signal
|
|
52
|
-
});
|
|
53
|
-
if (result.error) throw databaseApiError("Failed to remove database", result.response, result.error);
|
|
54
|
-
},
|
|
55
|
-
async listConnections(databaseId, options) {
|
|
56
|
-
const result = await client.GET("/v1/databases/{databaseId}/connections", {
|
|
57
|
-
params: { path: { databaseId } },
|
|
58
|
-
signal: options?.signal
|
|
59
|
-
});
|
|
60
|
-
if (result.error || !result.data) throw databaseApiError("Failed to list database connections", result.response, result.error);
|
|
61
|
-
return result.data.data.map((connection) => normalizeConnection(connection, databaseId));
|
|
62
|
-
},
|
|
63
|
-
async createConnection(options) {
|
|
64
|
-
const result = await client.POST("/v1/databases/{databaseId}/connections", {
|
|
65
|
-
params: { path: { databaseId: options.databaseId } },
|
|
66
|
-
body: { name: options.name },
|
|
67
|
-
signal: options.signal
|
|
68
|
-
});
|
|
69
|
-
if (result.error || !result.data) throw databaseApiError("Failed to create database connection", result.response, result.error);
|
|
70
|
-
return normalizeCreatedConnection(result.data.data, options.databaseId);
|
|
71
|
-
},
|
|
72
|
-
async removeConnection(connectionId, options) {
|
|
73
|
-
const result = await client.DELETE("/v1/connections/{id}", {
|
|
74
|
-
params: { path: { id: connectionId } },
|
|
75
|
-
signal: options?.signal
|
|
76
|
-
});
|
|
77
|
-
if (result.error) throw databaseApiError("Failed to remove database connection", result.response, result.error);
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
function normalizeDatabase(database, fallbackProjectId) {
|
|
82
|
-
return {
|
|
83
|
-
id: database.id,
|
|
84
|
-
name: database.name,
|
|
85
|
-
projectId: database.projectId ?? fallbackProjectId,
|
|
86
|
-
branchId: database.branchId ?? database.branch?.id ?? null,
|
|
87
|
-
branchName: database.branchGitName ?? database.branchName ?? database.branch?.gitName ?? database.branch?.name ?? null,
|
|
88
|
-
region: normalizeRegion(database),
|
|
89
|
-
status: database.status ?? null,
|
|
90
|
-
isDefault: database.isDefault ?? null,
|
|
91
|
-
createdAt: database.createdAt ?? null
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
function normalizeConnection(connection, fallbackDatabaseId) {
|
|
95
|
-
return {
|
|
96
|
-
id: connection.id,
|
|
97
|
-
name: connection.name ?? connection.id,
|
|
98
|
-
databaseId: connection.databaseId ?? fallbackDatabaseId,
|
|
99
|
-
createdAt: connection.createdAt ?? null
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
function normalizeCreatedDatabase(database, fallbackProjectId) {
|
|
103
|
-
const rawConnection = database.connections?.[0];
|
|
104
|
-
if (!rawConnection) throw new CliError({
|
|
105
|
-
code: "DATABASE_CONNECTION_MISSING",
|
|
106
|
-
domain: "database",
|
|
107
|
-
summary: "Created database did not return a connection string",
|
|
108
|
-
why: "The Management API created the database but did not include the one-time connection payload.",
|
|
109
|
-
fix: "Create a connection explicitly with prisma-cli database connection create <database>.",
|
|
110
|
-
exitCode: 1,
|
|
111
|
-
nextSteps: [`prisma-cli database connection create ${database.id}`]
|
|
112
|
-
});
|
|
113
|
-
return {
|
|
114
|
-
database: normalizeDatabase(database, fallbackProjectId),
|
|
115
|
-
...normalizeCreatedConnection(rawConnection, database.id)
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
function normalizeCreatedConnection(connection, fallbackDatabaseId) {
|
|
119
|
-
const connectionString = extractConnectionString(connection);
|
|
120
|
-
if (!connectionString) throw new CliError({
|
|
121
|
-
code: "DATABASE_CONNECTION_STRING_MISSING",
|
|
122
|
-
domain: "database",
|
|
123
|
-
summary: "Created connection did not return a connection string",
|
|
124
|
-
why: "Database connection strings are one-time-view secrets, but the Management API did not include one in this create response.",
|
|
125
|
-
fix: "Create another database connection and store the returned URL immediately.",
|
|
126
|
-
exitCode: 1,
|
|
127
|
-
nextSteps: [`prisma-cli database connection create ${fallbackDatabaseId}`]
|
|
128
|
-
});
|
|
129
|
-
return {
|
|
130
|
-
connection: normalizeConnection(connection, fallbackDatabaseId),
|
|
131
|
-
connectionString
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
function normalizeRegion(database) {
|
|
135
|
-
if (typeof database.region === "string") return database.region;
|
|
136
|
-
return database.region?.id ?? database.regionId ?? null;
|
|
137
|
-
}
|
|
138
|
-
function requireDatabaseProjectId(database, fallbackProjectId) {
|
|
139
|
-
const projectId = database.projectId ?? fallbackProjectId;
|
|
140
|
-
if (projectId) return projectId;
|
|
141
|
-
throw new CliError({
|
|
142
|
-
code: "DATABASE_API_ERROR",
|
|
143
|
-
domain: "database",
|
|
144
|
-
summary: "Database response did not include a project id",
|
|
145
|
-
why: "The Management API returned database metadata without project context.",
|
|
146
|
-
fix: "Re-run with --trace for the underlying API response details.",
|
|
147
|
-
exitCode: 1,
|
|
148
|
-
nextSteps: []
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
function extractConnectionString(connection) {
|
|
152
|
-
return connection.endpoints?.pooled?.connectionString ?? connection.connectionString ?? connection.endpoints?.direct?.connectionString ?? connection.endpoints?.accelerate?.connectionString ?? null;
|
|
153
|
-
}
|
|
154
|
-
function databaseApiError(summary, response, error) {
|
|
155
|
-
const status = response?.status ?? 0;
|
|
156
|
-
return new CliError({
|
|
157
|
-
code: error?.error?.code ?? "DATABASE_API_ERROR",
|
|
158
|
-
domain: "database",
|
|
159
|
-
summary,
|
|
160
|
-
why: error?.error?.message ?? `The Management API returned status ${status || "unknown"}.`,
|
|
161
|
-
fix: error?.error?.hint ?? "Re-run with --trace for the underlying API response details.",
|
|
162
|
-
exitCode: 1,
|
|
163
|
-
nextSteps: []
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
//#endregion
|
|
167
|
-
export { createManagementDatabaseProvider, normalizeConnection, normalizeDatabase };
|
package/dist/lib/diagnostics.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { resolveLocalStateFilePath } from "../adapters/local-state.js";
|
|
2
|
-
import { resolveStateDir } from "../shell/runtime.js";
|
|
3
|
-
import { readLocalGitState } from "./git/local-status.js";
|
|
4
|
-
//#region src/lib/diagnostics.ts
|
|
5
|
-
async function collectCommandDiagnostics(context, options = {}) {
|
|
6
|
-
const stateDir = await resolveStateDir(context.runtime);
|
|
7
|
-
return {
|
|
8
|
-
cwd: context.runtime.cwd,
|
|
9
|
-
stateFilePath: resolveLocalStateFilePath(stateDir),
|
|
10
|
-
git: await readLocalGitState(context.runtime.cwd, context.runtime.signal),
|
|
11
|
-
durationMs: options.durationMs
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
//#endregion
|
|
15
|
-
export { collectCommandDiagnostics };
|
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 { access, readFile } from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
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 };
|