@prisma/cli 8.0.0-rc.1 → 8.0.0-rc.2-dev.49
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 +15410 -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
package/dist/auth/login.js
DELETED
|
@@ -1,471 +0,0 @@
|
|
|
1
|
-
import { getApiBaseUrl } from "./client.js";
|
|
2
|
-
import { FileTokenStorage } from "./token-storage.js";
|
|
3
|
-
import { AuthError, createManagementApiSdk } from "@prisma/management-api-sdk";
|
|
4
|
-
import events from "node:events";
|
|
5
|
-
import http from "node:http";
|
|
6
|
-
import readline from "node:readline/promises";
|
|
7
|
-
import open from "open";
|
|
8
|
-
//#region src/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
|
-
onVerificationUrl: options.onVerificationUrl,
|
|
37
|
-
env: options.env,
|
|
38
|
-
signal: options.signal,
|
|
39
|
-
output
|
|
40
|
-
});
|
|
41
|
-
let completed = false;
|
|
42
|
-
let completion;
|
|
43
|
-
const completeOnce = (url) => {
|
|
44
|
-
if (!completion) completion = state.handleCallback(url).then(() => {
|
|
45
|
-
completed = true;
|
|
46
|
-
}, (error) => {
|
|
47
|
-
completion = void 0;
|
|
48
|
-
throw error;
|
|
49
|
-
});
|
|
50
|
-
return completion;
|
|
51
|
-
};
|
|
52
|
-
const httpResult = new Promise((resolve, reject) => {
|
|
53
|
-
const onAbort = () => {
|
|
54
|
-
reject(options.signal?.reason);
|
|
55
|
-
};
|
|
56
|
-
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
57
|
-
const settle = (callback) => {
|
|
58
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
59
|
-
callback();
|
|
60
|
-
};
|
|
61
|
-
server.on("request", async (req, res) => {
|
|
62
|
-
const url = new URL(`http://${state.host}${req.url}`);
|
|
63
|
-
if (url.pathname !== "/auth/callback") {
|
|
64
|
-
res.statusCode = 404;
|
|
65
|
-
res.end("Not found");
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
if (completed) {
|
|
69
|
-
const workspaceName = await state.resolveWorkspaceName();
|
|
70
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
71
|
-
res.end(renderSuccessPage(workspaceName));
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
try {
|
|
75
|
-
await completeOnce(url);
|
|
76
|
-
const workspaceName = await state.resolveWorkspaceName();
|
|
77
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
78
|
-
res.end(renderSuccessPage(workspaceName));
|
|
79
|
-
settle(resolve);
|
|
80
|
-
} catch (error) {
|
|
81
|
-
res.statusCode = 400;
|
|
82
|
-
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
83
|
-
res.end("Sign-in could not be completed. Return to your terminal.");
|
|
84
|
-
settle(() => reject(error));
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
options.signal?.throwIfAborted();
|
|
90
|
-
const callbackResult = interactive ? Promise.race([httpResult, consumePastedCallback({
|
|
91
|
-
input,
|
|
92
|
-
output,
|
|
93
|
-
signal: pasteAbort.signal,
|
|
94
|
-
complete: completeOnce
|
|
95
|
-
})]) : httpResult;
|
|
96
|
-
await Promise.all([state.openLoginPage(interactive), callbackResult]);
|
|
97
|
-
} finally {
|
|
98
|
-
pasteAbort.abort();
|
|
99
|
-
if (server.listening) await new Promise((resolve) => server.close(() => resolve()));
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
async function consumePastedCallback(options) {
|
|
103
|
-
if (!options.input.isTTY) return;
|
|
104
|
-
const rl = readline.createInterface({
|
|
105
|
-
input: options.input,
|
|
106
|
-
output: options.output
|
|
107
|
-
});
|
|
108
|
-
try {
|
|
109
|
-
for (;;) {
|
|
110
|
-
const url = await readPastedCallbackUrl(rl, options);
|
|
111
|
-
if (url === null) return;
|
|
112
|
-
if (url === void 0) continue;
|
|
113
|
-
if (await tryCompletePastedCallback(url, options)) return;
|
|
114
|
-
}
|
|
115
|
-
} finally {
|
|
116
|
-
rl.close();
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
async function readPastedCallbackUrl(rl, options) {
|
|
120
|
-
let answer;
|
|
121
|
-
try {
|
|
122
|
-
answer = await rl.question("Paste the callback URL here: ", { signal: options.signal });
|
|
123
|
-
} catch (error) {
|
|
124
|
-
if (error?.name === "AbortError") return null;
|
|
125
|
-
throw error;
|
|
126
|
-
}
|
|
127
|
-
const trimmed = answer.trim().replace(/^["']|["']$/g, "");
|
|
128
|
-
try {
|
|
129
|
-
if (!trimmed) throw new Error("empty input");
|
|
130
|
-
return new URL(trimmed);
|
|
131
|
-
} catch {
|
|
132
|
-
options.output.write("That didn't look like a URL. Paste the full localhost callback URL and try again.\n");
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
async function tryCompletePastedCallback(url, options) {
|
|
137
|
-
try {
|
|
138
|
-
await options.complete(url);
|
|
139
|
-
return true;
|
|
140
|
-
} catch (error) {
|
|
141
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
142
|
-
options.output.write(`Sign-in didn't complete (${message}). Paste the callback URL to try again.\n`);
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
var LoginState = class {
|
|
147
|
-
latestVerifier;
|
|
148
|
-
latestState;
|
|
149
|
-
sdk;
|
|
150
|
-
openUrl;
|
|
151
|
-
tokenStorage;
|
|
152
|
-
output;
|
|
153
|
-
constructor(options) {
|
|
154
|
-
this.options = options;
|
|
155
|
-
this.tokenStorage = options.tokenStorage ?? new FileTokenStorage(options.env, options.signal, { activateOnSetTokens: true });
|
|
156
|
-
this.sdk = createManagementApiSdk({
|
|
157
|
-
clientId: options.clientId ?? "cmm3lndn701oo0uefvxzo0ivw",
|
|
158
|
-
redirectUri: `http://${options.hostname}:${options.port}/auth/callback`,
|
|
159
|
-
tokenStorage: this.tokenStorage,
|
|
160
|
-
apiBaseUrl: options.apiBaseUrl ?? getApiBaseUrl(options.env),
|
|
161
|
-
authBaseUrl: options.authBaseUrl
|
|
162
|
-
});
|
|
163
|
-
this.openUrl = options.openUrl ?? open;
|
|
164
|
-
this.output = options.output;
|
|
165
|
-
}
|
|
166
|
-
async openLoginPage(interactive) {
|
|
167
|
-
this.options.signal?.throwIfAborted();
|
|
168
|
-
const { url, state, verifier } = await this.sdk.getLoginUrl({
|
|
169
|
-
scope: "workspace:admin offline_access",
|
|
170
|
-
additionalParams: {
|
|
171
|
-
utm_source: "prisma-cli",
|
|
172
|
-
utm_medium: "command-login",
|
|
173
|
-
utm_campaign: "prisma-cli"
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
this.latestState = state;
|
|
177
|
-
this.latestVerifier = verifier;
|
|
178
|
-
try {
|
|
179
|
-
this.options.onVerificationUrl?.(url);
|
|
180
|
-
} catch {}
|
|
181
|
-
this.options.signal?.throwIfAborted();
|
|
182
|
-
if (interactive) this.printLoginInstructions(url);
|
|
183
|
-
try {
|
|
184
|
-
await this.openUrl(url);
|
|
185
|
-
} catch (error) {
|
|
186
|
-
if (!interactive) throw error;
|
|
187
|
-
}
|
|
188
|
-
this.options.signal?.throwIfAborted();
|
|
189
|
-
}
|
|
190
|
-
printLoginInstructions(url) {
|
|
191
|
-
const output = this.output;
|
|
192
|
-
if (!output) return;
|
|
193
|
-
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`);
|
|
194
|
-
}
|
|
195
|
-
async handleCallback(url) {
|
|
196
|
-
if (url.pathname !== "/auth/callback") throw new AuthError$1("Not a callback URL");
|
|
197
|
-
const params = url.searchParams;
|
|
198
|
-
const error = params.get("error");
|
|
199
|
-
if (error) {
|
|
200
|
-
const desc = params.get("error_description");
|
|
201
|
-
throw new AuthError$1(desc ? `${error}: ${desc}` : error);
|
|
202
|
-
}
|
|
203
|
-
if (!this.latestVerifier) throw new AuthError$1("No verifier found");
|
|
204
|
-
if (!this.latestState) throw new AuthError$1("No state found");
|
|
205
|
-
try {
|
|
206
|
-
await this.sdk.handleCallback({
|
|
207
|
-
callbackUrl: url,
|
|
208
|
-
verifier: this.latestVerifier,
|
|
209
|
-
expectedState: this.latestState
|
|
210
|
-
});
|
|
211
|
-
} catch (error) {
|
|
212
|
-
if (error instanceof AuthError) throw new AuthError$1(error.message);
|
|
213
|
-
throw new AuthError$1(error instanceof Error ? error.message : "Unknown error during login");
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
async resolveWorkspaceName() {
|
|
217
|
-
try {
|
|
218
|
-
const tokens = await this.tokenStorage.getTokens();
|
|
219
|
-
if (!tokens?.workspaceId) return null;
|
|
220
|
-
const { data } = await this.sdk.client.GET("/v1/workspaces/{id}", {
|
|
221
|
-
params: { path: { id: tokens.workspaceId } },
|
|
222
|
-
signal: this.options.signal
|
|
223
|
-
});
|
|
224
|
-
const name = data?.data?.name;
|
|
225
|
-
return typeof name === "string" && name.trim().length > 0 ? name.trim() : null;
|
|
226
|
-
} catch {
|
|
227
|
-
this.options.signal?.throwIfAborted();
|
|
228
|
-
return null;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
get host() {
|
|
232
|
-
return `${this.options.hostname}:${this.options.port}`;
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
function renderSuccessPage(workspaceName) {
|
|
236
|
-
return `<!doctype html>
|
|
237
|
-
<html lang="en">
|
|
238
|
-
<head>
|
|
239
|
-
<meta charset="utf-8">
|
|
240
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
241
|
-
<title>Prisma Developer Platform</title>
|
|
242
|
-
<style>
|
|
243
|
-
:root {
|
|
244
|
-
color-scheme: light dark;
|
|
245
|
-
--background: #ffffff;
|
|
246
|
-
--foreground: #1f2430;
|
|
247
|
-
--muted: #4f5665;
|
|
248
|
-
--mark-color: #050812;
|
|
249
|
-
--surface: #f6f7fb;
|
|
250
|
-
--border: #e4e7ee;
|
|
251
|
-
--success: #15803d;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
@media (prefers-color-scheme: dark) {
|
|
255
|
-
:root {
|
|
256
|
-
--background: #050812;
|
|
257
|
-
--foreground: #f6f7fb;
|
|
258
|
-
--muted: #c5cad6;
|
|
259
|
-
--mark-color: #ffffff;
|
|
260
|
-
--surface: #0d1322;
|
|
261
|
-
--border: #232a3d;
|
|
262
|
-
--success: #4ade80;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
* {
|
|
267
|
-
box-sizing: border-box;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
body {
|
|
271
|
-
min-height: 100vh;
|
|
272
|
-
margin: 0;
|
|
273
|
-
background: var(--background);
|
|
274
|
-
color: var(--foreground);
|
|
275
|
-
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
276
|
-
display: grid;
|
|
277
|
-
grid-template-rows: 128px 1fr;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
.mark {
|
|
281
|
-
align-self: end;
|
|
282
|
-
justify-self: center;
|
|
283
|
-
width: 36px;
|
|
284
|
-
height: 36px;
|
|
285
|
-
color: var(--mark-color);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
.mark path {
|
|
289
|
-
fill: currentColor !important;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
main {
|
|
293
|
-
align-self: center;
|
|
294
|
-
justify-self: center;
|
|
295
|
-
width: min(520px, calc(100vw - 48px));
|
|
296
|
-
margin-top: -128px;
|
|
297
|
-
text-align: center;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
h1 {
|
|
301
|
-
margin: 0 0 12px;
|
|
302
|
-
font-size: 26px;
|
|
303
|
-
line-height: 1.2;
|
|
304
|
-
font-weight: 700;
|
|
305
|
-
letter-spacing: 0;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
p {
|
|
309
|
-
margin: 0 auto;
|
|
310
|
-
max-width: 480px;
|
|
311
|
-
color: var(--muted);
|
|
312
|
-
font-size: 15px;
|
|
313
|
-
line-height: 1.55;
|
|
314
|
-
letter-spacing: 0;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
.skills {
|
|
318
|
-
margin-top: 40px;
|
|
319
|
-
padding-top: 28px;
|
|
320
|
-
border-top: 1px solid var(--border);
|
|
321
|
-
text-align: left;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
.skills-lead {
|
|
325
|
-
display: flex;
|
|
326
|
-
align-items: center;
|
|
327
|
-
gap: 8px;
|
|
328
|
-
margin: 0 0 12px;
|
|
329
|
-
font-size: 15px;
|
|
330
|
-
color: var(--foreground);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
.skills-lead svg {
|
|
334
|
-
flex: none;
|
|
335
|
-
color: var(--muted);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
.command {
|
|
339
|
-
display: flex;
|
|
340
|
-
align-items: center;
|
|
341
|
-
gap: 8px;
|
|
342
|
-
padding: 10px 8px 10px 16px;
|
|
343
|
-
background: var(--surface);
|
|
344
|
-
border: 1px solid var(--border);
|
|
345
|
-
border-radius: 8px;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
.command code {
|
|
349
|
-
flex: 1;
|
|
350
|
-
overflow-x: auto;
|
|
351
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
352
|
-
font-size: 13.5px;
|
|
353
|
-
white-space: nowrap;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
.command .prompt {
|
|
357
|
-
color: var(--muted);
|
|
358
|
-
user-select: none;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
.copy {
|
|
362
|
-
flex: none;
|
|
363
|
-
display: grid;
|
|
364
|
-
place-items: center;
|
|
365
|
-
padding: 6px;
|
|
366
|
-
border: 0;
|
|
367
|
-
border-radius: 6px;
|
|
368
|
-
background: transparent;
|
|
369
|
-
color: var(--muted);
|
|
370
|
-
cursor: pointer;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
.copy:hover {
|
|
374
|
-
background: var(--border);
|
|
375
|
-
color: var(--foreground);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
.copy .icon-check,
|
|
379
|
-
.copy.copied .icon-copy {
|
|
380
|
-
display: none;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
.copy.copied .icon-check {
|
|
384
|
-
display: block;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
.copy.copied,
|
|
388
|
-
.copy.copied:hover {
|
|
389
|
-
color: var(--success);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
.visually-hidden {
|
|
393
|
-
position: absolute;
|
|
394
|
-
width: 1px;
|
|
395
|
-
height: 1px;
|
|
396
|
-
margin: -1px;
|
|
397
|
-
padding: 0;
|
|
398
|
-
overflow: hidden;
|
|
399
|
-
clip: rect(0 0 0 0);
|
|
400
|
-
white-space: nowrap;
|
|
401
|
-
border: 0;
|
|
402
|
-
}
|
|
403
|
-
</style>
|
|
404
|
-
</head>
|
|
405
|
-
<body>
|
|
406
|
-
<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>
|
|
407
|
-
<main>
|
|
408
|
-
<h1>You're all set.</h1>
|
|
409
|
-
<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>
|
|
410
|
-
<section class="skills">
|
|
411
|
-
<div class="skills-lead">
|
|
412
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/><path d="M20 3v4"/><path d="M22 5h-4"/></svg>
|
|
413
|
-
Using an AI coding agent? Add the Prisma skills:
|
|
414
|
-
</div>
|
|
415
|
-
<div class="command">
|
|
416
|
-
<code><span class="prompt">$ </span><span class="command-text">npx skills add prisma/skills</span></code>
|
|
417
|
-
<button class="copy" type="button" aria-label="Copy command to clipboard">
|
|
418
|
-
<svg class="icon-copy" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
|
|
419
|
-
<svg class="icon-check" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>
|
|
420
|
-
</button>
|
|
421
|
-
</div>
|
|
422
|
-
<span class="visually-hidden skills-status" role="status"></span>
|
|
423
|
-
</section>
|
|
424
|
-
</main>
|
|
425
|
-
<script>
|
|
426
|
-
(() => {
|
|
427
|
-
const command = "npx skills add prisma/skills";
|
|
428
|
-
const button = document.querySelector(".copy");
|
|
429
|
-
const status = document.querySelector(".skills-status");
|
|
430
|
-
let timer;
|
|
431
|
-
button.addEventListener("click", async () => {
|
|
432
|
-
try {
|
|
433
|
-
await navigator.clipboard.writeText(command);
|
|
434
|
-
} catch {
|
|
435
|
-
// Clipboard access denied: select the command so Cmd/Ctrl+C works.
|
|
436
|
-
const range = document.createRange();
|
|
437
|
-
range.selectNodeContents(document.querySelector(".command-text"));
|
|
438
|
-
const selection = window.getSelection();
|
|
439
|
-
selection.removeAllRanges();
|
|
440
|
-
selection.addRange(range);
|
|
441
|
-
status.textContent =
|
|
442
|
-
"Copying failed. The command is selected; press Ctrl+C or Cmd+C to copy it.";
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
button.classList.add("copied");
|
|
446
|
-
status.textContent = "Command copied to clipboard.";
|
|
447
|
-
clearTimeout(timer);
|
|
448
|
-
timer = setTimeout(() => {
|
|
449
|
-
button.classList.remove("copied");
|
|
450
|
-
status.textContent = "";
|
|
451
|
-
}, 2000);
|
|
452
|
-
});
|
|
453
|
-
})();
|
|
454
|
-
<\/script>
|
|
455
|
-
</body>
|
|
456
|
-
</html>`;
|
|
457
|
-
}
|
|
458
|
-
function escapeHtml(value) {
|
|
459
|
-
return value.replace(/[&<>"']/g, (char) => {
|
|
460
|
-
switch (char) {
|
|
461
|
-
case "&": return "&";
|
|
462
|
-
case "<": return "<";
|
|
463
|
-
case ">": return ">";
|
|
464
|
-
case "\"": return """;
|
|
465
|
-
case "'": return "'";
|
|
466
|
-
default: return char;
|
|
467
|
-
}
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
//#endregion
|
|
471
|
-
export { AuthError$1 as AuthError, login };
|
package/dist/auth/operations.js
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
import { FileTokenStorage } from "./token-storage.js";
|
|
2
|
-
import { authenticatedManagementApiClient } from "./guard.js";
|
|
3
|
-
import { AuthError, login } from "./login.js";
|
|
4
|
-
import { SERVICE_TOKEN_ENV_VAR, claimedExpiresAt, credentialWorkspaceId } from "@prisma/cli-engine";
|
|
5
|
-
//#region src/auth/operations.ts
|
|
6
|
-
const WORKSPACE_SUB_PREFIX = "workspace:";
|
|
7
|
-
/** Thrown when PRISMA_SERVICE_TOKEN is set to an empty/blank value. */
|
|
8
|
-
var EmptyServiceTokenError = class extends Error {
|
|
9
|
-
code = "EMPTY_SERVICE_TOKEN";
|
|
10
|
-
constructor() {
|
|
11
|
-
super(`${SERVICE_TOKEN_ENV_VAR} is set but empty. Provide a valid token or unset the variable.`);
|
|
12
|
-
this.name = "EmptyServiceTokenError";
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
/** Matches by the code discriminator, not instanceof, so the check
|
|
16
|
-
* survives duplicate module instances. */
|
|
17
|
-
function isEmptyServiceTokenError(error) {
|
|
18
|
-
return typeof error === "object" && error !== null && error.code === "EMPTY_SERVICE_TOKEN";
|
|
19
|
-
}
|
|
20
|
-
function decodeJwtPayload(token) {
|
|
21
|
-
try {
|
|
22
|
-
const payload = token.split(".")[1];
|
|
23
|
-
if (!payload) return {};
|
|
24
|
-
return JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
|
|
25
|
-
} catch {
|
|
26
|
-
return {};
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function emailFromClaims(claims) {
|
|
30
|
-
const email = claims.email;
|
|
31
|
-
return typeof email === "string" && email.trim().length > 0 ? email.trim() : null;
|
|
32
|
-
}
|
|
33
|
-
function workspaceIdFromClaims(claims) {
|
|
34
|
-
const sub = claims.sub;
|
|
35
|
-
if (typeof sub !== "string") return null;
|
|
36
|
-
if (!sub.startsWith(WORKSPACE_SUB_PREFIX)) return null;
|
|
37
|
-
const id = sub.slice(10).trim();
|
|
38
|
-
return id.length > 0 ? id : null;
|
|
39
|
-
}
|
|
40
|
-
/** Holds the tokens the SDK writes at callback time and nothing else:
|
|
41
|
-
* minting and custody stay separate, so a login never writes through
|
|
42
|
-
* the credential manager. */
|
|
43
|
-
var ThrowawayTokenStorage = class {
|
|
44
|
-
tokens = null;
|
|
45
|
-
async getTokens() {
|
|
46
|
-
return this.tokens;
|
|
47
|
-
}
|
|
48
|
-
async setTokens(tokens) {
|
|
49
|
-
this.tokens = tokens;
|
|
50
|
-
}
|
|
51
|
-
async clearTokens() {
|
|
52
|
-
this.tokens = null;
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
/** Runs the browser consent flow and RETURNS the minted credential.
|
|
56
|
-
* Storing it is the caller's job. */
|
|
57
|
-
async function performLogin(env, signal, options) {
|
|
58
|
-
const tokenStorage = new ThrowawayTokenStorage();
|
|
59
|
-
await login({
|
|
60
|
-
tokenStorage,
|
|
61
|
-
env,
|
|
62
|
-
signal,
|
|
63
|
-
onVerificationUrl: options?.onVerificationUrl
|
|
64
|
-
});
|
|
65
|
-
const tokens = tokenStorage.tokens;
|
|
66
|
-
if (!tokens) throw new AuthError("Sign-in finished without producing a credential.");
|
|
67
|
-
return {
|
|
68
|
-
token: tokens.accessToken,
|
|
69
|
-
refreshToken: tokens.refreshToken,
|
|
70
|
-
expiresAt: claimedExpiresAt(tokens.accessToken)
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
/** Writes a minted credential into the legacy store, which the legacy
|
|
74
|
-
* shell still reads. Dies with the legacy shell. */
|
|
75
|
-
async function storeLegacyCredential(env, credential, signal) {
|
|
76
|
-
const workspaceId = credentialWorkspaceId(credential.token);
|
|
77
|
-
if (workspaceId === void 0) return;
|
|
78
|
-
await new FileTokenStorage(env, signal, { activateOnSetTokens: true }).setTokens({
|
|
79
|
-
workspaceId,
|
|
80
|
-
accessToken: credential.token,
|
|
81
|
-
...credential.refreshToken === void 0 ? {} : { refreshToken: credential.refreshToken }
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
async function readAuthState(env, signal) {
|
|
85
|
-
const rawServiceToken = env[SERVICE_TOKEN_ENV_VAR];
|
|
86
|
-
if (rawServiceToken !== void 0) {
|
|
87
|
-
const serviceToken = rawServiceToken.trim();
|
|
88
|
-
if (serviceToken.length === 0) throw new EmptyServiceTokenError();
|
|
89
|
-
return readServiceTokenAuthState(serviceToken, env, signal);
|
|
90
|
-
}
|
|
91
|
-
const tokenStorage = new FileTokenStorage(env, signal);
|
|
92
|
-
const tokens = await tokenStorage.getTokens();
|
|
93
|
-
if (!tokens) return {
|
|
94
|
-
authenticated: false,
|
|
95
|
-
provider: null,
|
|
96
|
-
user: null,
|
|
97
|
-
workspace: null,
|
|
98
|
-
credential: null
|
|
99
|
-
};
|
|
100
|
-
const client = await authenticatedManagementApiClient(env, signal);
|
|
101
|
-
const currentPrincipal = await readCurrentPrincipalAuthState(client, signal);
|
|
102
|
-
if (currentPrincipal) {
|
|
103
|
-
if (currentPrincipal.authenticated && currentPrincipal.workspace) await tokenStorage.rememberWorkspace?.(tokens.workspaceId, currentPrincipal.workspace);
|
|
104
|
-
return currentPrincipal;
|
|
105
|
-
}
|
|
106
|
-
const claims = decodeJwtPayload(tokens.accessToken);
|
|
107
|
-
const authState = await buildAuthState({
|
|
108
|
-
workspaceIdFromCredential: tokens.workspaceId,
|
|
109
|
-
claims,
|
|
110
|
-
env,
|
|
111
|
-
client,
|
|
112
|
-
signal
|
|
113
|
-
});
|
|
114
|
-
if (authState.authenticated && authState.workspace) await tokenStorage.rememberWorkspace?.(tokens.workspaceId, authState.workspace);
|
|
115
|
-
return authState;
|
|
116
|
-
}
|
|
117
|
-
async function readServiceTokenAuthState(token, env, signal) {
|
|
118
|
-
const client = await authenticatedManagementApiClient(env, signal);
|
|
119
|
-
const currentPrincipal = await readCurrentPrincipalAuthState(client, signal);
|
|
120
|
-
if (currentPrincipal) return currentPrincipal;
|
|
121
|
-
const claims = decodeJwtPayload(token);
|
|
122
|
-
const workspaceId = workspaceIdFromClaims(claims);
|
|
123
|
-
if (!workspaceId) return {
|
|
124
|
-
authenticated: false,
|
|
125
|
-
provider: null,
|
|
126
|
-
user: null,
|
|
127
|
-
workspace: null,
|
|
128
|
-
credential: null
|
|
129
|
-
};
|
|
130
|
-
return buildAuthState({
|
|
131
|
-
workspaceIdFromCredential: workspaceId,
|
|
132
|
-
claims,
|
|
133
|
-
env,
|
|
134
|
-
client,
|
|
135
|
-
signal
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
async function buildAuthState({ workspaceIdFromCredential, claims, env, client, signal }) {
|
|
139
|
-
let workspaceId = workspaceIdFromCredential;
|
|
140
|
-
let workspaceName = workspaceIdFromCredential;
|
|
141
|
-
client ??= await authenticatedManagementApiClient(env, signal);
|
|
142
|
-
if (client) try {
|
|
143
|
-
const { data, response } = await client.GET("/v1/workspaces/{id}", {
|
|
144
|
-
params: { path: { id: workspaceIdFromCredential } },
|
|
145
|
-
signal
|
|
146
|
-
});
|
|
147
|
-
if (response?.status === 401) return {
|
|
148
|
-
authenticated: false,
|
|
149
|
-
provider: null,
|
|
150
|
-
user: null,
|
|
151
|
-
workspace: null,
|
|
152
|
-
credential: null
|
|
153
|
-
};
|
|
154
|
-
if (data?.data?.id) {
|
|
155
|
-
workspaceId = data.data.id;
|
|
156
|
-
workspaceName = data.data.id;
|
|
157
|
-
}
|
|
158
|
-
if (data?.data?.name) workspaceName = data.data.name;
|
|
159
|
-
} catch {
|
|
160
|
-
signal?.throwIfAborted();
|
|
161
|
-
}
|
|
162
|
-
const email = emailFromClaims(claims);
|
|
163
|
-
return {
|
|
164
|
-
authenticated: true,
|
|
165
|
-
provider: null,
|
|
166
|
-
user: email ? { email } : null,
|
|
167
|
-
workspace: {
|
|
168
|
-
id: workspaceId,
|
|
169
|
-
name: workspaceName
|
|
170
|
-
},
|
|
171
|
-
credential: null
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
async function readCurrentPrincipalAuthState(client, signal) {
|
|
175
|
-
if (!client) return null;
|
|
176
|
-
try {
|
|
177
|
-
const { data, response } = await client.GET("/v1/me", { signal });
|
|
178
|
-
if (response?.status === 401) return {
|
|
179
|
-
authenticated: false,
|
|
180
|
-
provider: null,
|
|
181
|
-
user: null,
|
|
182
|
-
workspace: null,
|
|
183
|
-
credential: null
|
|
184
|
-
};
|
|
185
|
-
const principal = data?.data;
|
|
186
|
-
if (!principal) return null;
|
|
187
|
-
if (!principal.credential) return null;
|
|
188
|
-
return {
|
|
189
|
-
authenticated: true,
|
|
190
|
-
provider: null,
|
|
191
|
-
user: principal.user ? {
|
|
192
|
-
id: principal.user.id,
|
|
193
|
-
email: principal.user.email,
|
|
194
|
-
name: principal.user.name
|
|
195
|
-
} : null,
|
|
196
|
-
workspace: principal.workspace,
|
|
197
|
-
credential: principal.credential
|
|
198
|
-
};
|
|
199
|
-
} catch {
|
|
200
|
-
signal?.throwIfAborted();
|
|
201
|
-
return null;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
async function performLogout(env, signal) {
|
|
205
|
-
await new FileTokenStorage(env, signal).clearTokens();
|
|
206
|
-
}
|
|
207
|
-
//#endregion
|
|
208
|
-
export { isEmptyServiceTokenError, performLogin, performLogout, readAuthState, storeLegacyCredential };
|