@prisma/cli 3.0.0-dev.53.1 → 3.0.0-dev.54.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/git.js +8 -3
- package/dist/adapters/local-state.js +11 -3
- package/dist/adapters/mock-api.js +6 -2
- package/dist/adapters/token-storage.js +63 -22
- package/dist/cli.js +14 -3
- package/dist/cli2.js +1 -0
- package/dist/controllers/app-env.js +78 -46
- package/dist/controllers/app.js +111 -62
- package/dist/controllers/auth.js +8 -8
- package/dist/controllers/project.js +107 -67
- package/dist/lib/app/bun-project.js +12 -5
- package/dist/lib/app/local-dev.js +34 -18
- package/dist/lib/app/preview-build.js +90 -62
- package/dist/lib/app/preview-provider.js +118 -49
- package/dist/lib/auth/auth-ops.js +30 -21
- package/dist/lib/auth/guard.js +3 -2
- package/dist/lib/auth/login.js +31 -17
- package/dist/lib/project/interactive-setup.js +1 -1
- package/dist/lib/project/local-pin.js +27 -8
- package/dist/lib/project/resolution.js +14 -8
- package/dist/lib/project/setup.js +2 -2
- package/dist/shell/command-runner.js +9 -4
- package/dist/shell/errors.js +12 -1
- package/dist/shell/runtime.js +2 -2
- package/package.json +1 -1
|
@@ -24,20 +24,21 @@ function workspaceIdFromClaims(claims) {
|
|
|
24
24
|
const id = sub.slice(10).trim();
|
|
25
25
|
return id.length > 0 ? id : null;
|
|
26
26
|
}
|
|
27
|
-
async function performLogin(env) {
|
|
27
|
+
async function performLogin(env, signal) {
|
|
28
28
|
await login({
|
|
29
|
-
tokenStorage: new FileTokenStorage(env),
|
|
30
|
-
env
|
|
29
|
+
tokenStorage: new FileTokenStorage(env, signal),
|
|
30
|
+
env,
|
|
31
|
+
signal
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
|
-
async function readAuthState(env) {
|
|
34
|
+
async function readAuthState(env, signal) {
|
|
34
35
|
const rawServiceToken = env[SERVICE_TOKEN_ENV_VAR];
|
|
35
36
|
if (rawServiceToken !== void 0) {
|
|
36
37
|
const serviceToken = rawServiceToken.trim();
|
|
37
38
|
if (serviceToken.length === 0) throw new Error(`${SERVICE_TOKEN_ENV_VAR} is set but empty. Provide a valid token or unset the variable.`);
|
|
38
|
-
return readServiceTokenAuthState(serviceToken, env);
|
|
39
|
+
return readServiceTokenAuthState(serviceToken, env, signal);
|
|
39
40
|
}
|
|
40
|
-
const tokens = await new FileTokenStorage(env).getTokens();
|
|
41
|
+
const tokens = await new FileTokenStorage(env, signal).getTokens();
|
|
41
42
|
if (!tokens) return {
|
|
42
43
|
authenticated: false,
|
|
43
44
|
provider: null,
|
|
@@ -45,20 +46,21 @@ async function readAuthState(env) {
|
|
|
45
46
|
workspace: null,
|
|
46
47
|
credential: null
|
|
47
48
|
};
|
|
48
|
-
const client = await requireComputeAuth(env);
|
|
49
|
-
const currentPrincipal = await readCurrentPrincipalAuthState(client);
|
|
49
|
+
const client = await requireComputeAuth(env, signal);
|
|
50
|
+
const currentPrincipal = await readCurrentPrincipalAuthState(client, signal);
|
|
50
51
|
if (currentPrincipal) return currentPrincipal;
|
|
51
52
|
const claims = decodeJwtPayload(tokens.accessToken);
|
|
52
53
|
return buildAuthState({
|
|
53
54
|
workspaceIdFromCredential: tokens.workspaceId,
|
|
54
55
|
claims,
|
|
55
56
|
env,
|
|
56
|
-
client
|
|
57
|
+
client,
|
|
58
|
+
signal
|
|
57
59
|
});
|
|
58
60
|
}
|
|
59
|
-
async function readServiceTokenAuthState(token, env) {
|
|
60
|
-
const client = await requireComputeAuth(env);
|
|
61
|
-
const currentPrincipal = await readCurrentPrincipalAuthState(client);
|
|
61
|
+
async function readServiceTokenAuthState(token, env, signal) {
|
|
62
|
+
const client = await requireComputeAuth(env, signal);
|
|
63
|
+
const currentPrincipal = await readCurrentPrincipalAuthState(client, signal);
|
|
62
64
|
if (currentPrincipal) return currentPrincipal;
|
|
63
65
|
const claims = decodeJwtPayload(token);
|
|
64
66
|
const workspaceId = workspaceIdFromClaims(claims);
|
|
@@ -73,15 +75,19 @@ async function readServiceTokenAuthState(token, env) {
|
|
|
73
75
|
workspaceIdFromCredential: workspaceId,
|
|
74
76
|
claims,
|
|
75
77
|
env,
|
|
76
|
-
client
|
|
78
|
+
client,
|
|
79
|
+
signal
|
|
77
80
|
});
|
|
78
81
|
}
|
|
79
|
-
async function buildAuthState({ workspaceIdFromCredential, claims, env, client }) {
|
|
82
|
+
async function buildAuthState({ workspaceIdFromCredential, claims, env, client, signal }) {
|
|
80
83
|
let workspaceId = workspaceIdFromCredential;
|
|
81
84
|
let workspaceName = workspaceIdFromCredential;
|
|
82
|
-
client ??= await requireComputeAuth(env);
|
|
85
|
+
client ??= await requireComputeAuth(env, signal);
|
|
83
86
|
if (client) try {
|
|
84
|
-
const { data, response } = await client.GET("/v1/workspaces/{id}", {
|
|
87
|
+
const { data, response } = await client.GET("/v1/workspaces/{id}", {
|
|
88
|
+
params: { path: { id: workspaceIdFromCredential } },
|
|
89
|
+
signal
|
|
90
|
+
});
|
|
85
91
|
if (response?.status === 401) return {
|
|
86
92
|
authenticated: false,
|
|
87
93
|
provider: null,
|
|
@@ -94,7 +100,9 @@ async function buildAuthState({ workspaceIdFromCredential, claims, env, client }
|
|
|
94
100
|
workspaceName = data.data.id;
|
|
95
101
|
}
|
|
96
102
|
if (data?.data?.name) workspaceName = data.data.name;
|
|
97
|
-
} catch {
|
|
103
|
+
} catch {
|
|
104
|
+
signal?.throwIfAborted();
|
|
105
|
+
}
|
|
98
106
|
const email = emailFromClaims(claims);
|
|
99
107
|
return {
|
|
100
108
|
authenticated: true,
|
|
@@ -107,10 +115,10 @@ async function buildAuthState({ workspaceIdFromCredential, claims, env, client }
|
|
|
107
115
|
credential: null
|
|
108
116
|
};
|
|
109
117
|
}
|
|
110
|
-
async function readCurrentPrincipalAuthState(client) {
|
|
118
|
+
async function readCurrentPrincipalAuthState(client, signal) {
|
|
111
119
|
if (!client) return null;
|
|
112
120
|
try {
|
|
113
|
-
const { data, response } = await client.GET("/v1/me");
|
|
121
|
+
const { data, response } = await client.GET("/v1/me", { signal });
|
|
114
122
|
if (response?.status === 401) return {
|
|
115
123
|
authenticated: false,
|
|
116
124
|
provider: null,
|
|
@@ -133,11 +141,12 @@ async function readCurrentPrincipalAuthState(client) {
|
|
|
133
141
|
credential: principal.credential
|
|
134
142
|
};
|
|
135
143
|
} catch {
|
|
144
|
+
signal?.throwIfAborted();
|
|
136
145
|
return null;
|
|
137
146
|
}
|
|
138
147
|
}
|
|
139
|
-
async function performLogout(env) {
|
|
140
|
-
await new FileTokenStorage(env).clearTokens();
|
|
148
|
+
async function performLogout(env, signal) {
|
|
149
|
+
await new FileTokenStorage(env, signal).clearTokens();
|
|
141
150
|
}
|
|
142
151
|
//#endregion
|
|
143
152
|
export { performLogin, performLogout, readAuthState };
|
package/dist/lib/auth/guard.js
CHANGED
|
@@ -11,7 +11,8 @@ import { createManagementApiClient, createManagementApiSdk } from "@prisma/manag
|
|
|
11
11
|
*
|
|
12
12
|
* Returns null if not authenticated.
|
|
13
13
|
*/
|
|
14
|
-
async function requireComputeAuth(env = process.env) {
|
|
14
|
+
async function requireComputeAuth(env = process.env, signal) {
|
|
15
|
+
signal?.throwIfAborted();
|
|
15
16
|
const rawToken = env[SERVICE_TOKEN_ENV_VAR];
|
|
16
17
|
if (rawToken !== void 0) {
|
|
17
18
|
const token = rawToken.trim();
|
|
@@ -21,7 +22,7 @@ async function requireComputeAuth(env = process.env) {
|
|
|
21
22
|
token
|
|
22
23
|
});
|
|
23
24
|
}
|
|
24
|
-
const tokenStorage = new FileTokenStorage(env);
|
|
25
|
+
const tokenStorage = new FileTokenStorage(env, signal);
|
|
25
26
|
if (!await tokenStorage.getTokens()) return null;
|
|
26
27
|
return createManagementApiSdk({
|
|
27
28
|
clientId: CLIENT_ID,
|
package/dist/lib/auth/login.js
CHANGED
|
@@ -34,6 +34,7 @@ async function login(options = {}) {
|
|
|
34
34
|
authBaseUrl: options.authBaseUrl,
|
|
35
35
|
openUrl: options.openUrl,
|
|
36
36
|
env: options.env,
|
|
37
|
+
signal: options.signal,
|
|
37
38
|
output
|
|
38
39
|
});
|
|
39
40
|
let completed = false;
|
|
@@ -48,6 +49,14 @@ async function login(options = {}) {
|
|
|
48
49
|
return completion;
|
|
49
50
|
};
|
|
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
|
+
};
|
|
51
60
|
server.on("request", async (req, res) => {
|
|
52
61
|
const url = new URL(`http://${state.host}${req.url}`);
|
|
53
62
|
if (url.pathname !== "/auth/callback") {
|
|
@@ -63,29 +72,27 @@ async function login(options = {}) {
|
|
|
63
72
|
}
|
|
64
73
|
try {
|
|
65
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);
|
|
66
79
|
} catch (error) {
|
|
67
80
|
res.statusCode = 400;
|
|
68
81
|
const message = error instanceof Error ? error.message : String(error);
|
|
69
82
|
res.end(message);
|
|
70
|
-
reject(error);
|
|
83
|
+
settle(() => reject(error));
|
|
71
84
|
return;
|
|
72
85
|
}
|
|
73
|
-
const workspaceName = await state.resolveWorkspaceName();
|
|
74
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
75
|
-
res.end(renderSuccessPage(workspaceName));
|
|
76
|
-
resolve();
|
|
77
86
|
});
|
|
78
87
|
});
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
await Promise.race([httpResult, pasteResult]);
|
|
88
|
-
} else await httpResult;
|
|
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]);
|
|
89
96
|
} finally {
|
|
90
97
|
pasteAbort.abort();
|
|
91
98
|
if (server.listening) await new Promise((resolve) => server.close(() => resolve()));
|
|
@@ -137,7 +144,7 @@ var LoginState = class {
|
|
|
137
144
|
output;
|
|
138
145
|
constructor(options) {
|
|
139
146
|
this.options = options;
|
|
140
|
-
this.tokenStorage = options.tokenStorage ?? new FileTokenStorage(options.env);
|
|
147
|
+
this.tokenStorage = options.tokenStorage ?? new FileTokenStorage(options.env, options.signal);
|
|
141
148
|
this.sdk = createManagementApiSdk({
|
|
142
149
|
clientId: options.clientId ?? "cmm3lndn701oo0uefvxzo0ivw",
|
|
143
150
|
redirectUri: `http://${options.hostname}:${options.port}/auth/callback`,
|
|
@@ -149,6 +156,7 @@ var LoginState = class {
|
|
|
149
156
|
this.output = options.output;
|
|
150
157
|
}
|
|
151
158
|
async openLoginPage(interactive) {
|
|
159
|
+
this.options.signal?.throwIfAborted();
|
|
152
160
|
const { url, state, verifier } = await this.sdk.getLoginUrl({
|
|
153
161
|
scope: "workspace:admin offline_access",
|
|
154
162
|
additionalParams: {
|
|
@@ -159,12 +167,14 @@ var LoginState = class {
|
|
|
159
167
|
});
|
|
160
168
|
this.latestState = state;
|
|
161
169
|
this.latestVerifier = verifier;
|
|
170
|
+
this.options.signal?.throwIfAborted();
|
|
162
171
|
if (interactive) this.printLoginInstructions(url);
|
|
163
172
|
try {
|
|
164
173
|
await this.openUrl(url);
|
|
165
174
|
} catch (error) {
|
|
166
175
|
if (!interactive) throw error;
|
|
167
176
|
}
|
|
177
|
+
this.options.signal?.throwIfAborted();
|
|
168
178
|
}
|
|
169
179
|
printLoginInstructions(url) {
|
|
170
180
|
const output = this.output;
|
|
@@ -196,10 +206,14 @@ var LoginState = class {
|
|
|
196
206
|
try {
|
|
197
207
|
const tokens = await this.tokenStorage.getTokens();
|
|
198
208
|
if (!tokens?.workspaceId) return null;
|
|
199
|
-
const { data } = await this.sdk.client.GET("/v1/workspaces/{id}", {
|
|
209
|
+
const { data } = await this.sdk.client.GET("/v1/workspaces/{id}", {
|
|
210
|
+
params: { path: { id: tokens.workspaceId } },
|
|
211
|
+
signal: this.options.signal
|
|
212
|
+
});
|
|
200
213
|
const name = data?.data?.name;
|
|
201
214
|
return typeof name === "string" && name.trim().length > 0 ? name.trim() : null;
|
|
202
215
|
} catch {
|
|
216
|
+
this.options.signal?.throwIfAborted();
|
|
203
217
|
return null;
|
|
204
218
|
}
|
|
205
219
|
}
|
|
@@ -36,7 +36,7 @@ async function promptForProjectSetupChoice(options) {
|
|
|
36
36
|
targetName: choice.project.name,
|
|
37
37
|
targetNameSource: "prompt"
|
|
38
38
|
};
|
|
39
|
-
const suggestedName = await inferTargetName(options.context.runtime.cwd);
|
|
39
|
+
const suggestedName = await inferTargetName(options.context.runtime.cwd, options.context.runtime.signal);
|
|
40
40
|
const rawName = await textPrompt({
|
|
41
41
|
input: options.context.runtime.stdin,
|
|
42
42
|
output: options.context.runtime.stderr,
|
|
@@ -2,9 +2,13 @@ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
//#region src/lib/project/local-pin.ts
|
|
4
4
|
const LOCAL_RESOLUTION_PIN_RELATIVE_PATH = ".prisma/local.json";
|
|
5
|
-
async function readLocalResolutionPin(cwd) {
|
|
5
|
+
async function readLocalResolutionPin(cwd, signal) {
|
|
6
|
+
signal?.throwIfAborted();
|
|
6
7
|
try {
|
|
7
|
-
const raw = await readFile(path.join(cwd, LOCAL_RESOLUTION_PIN_RELATIVE_PATH),
|
|
8
|
+
const raw = await readFile(path.join(cwd, LOCAL_RESOLUTION_PIN_RELATIVE_PATH), {
|
|
9
|
+
encoding: "utf8",
|
|
10
|
+
signal
|
|
11
|
+
});
|
|
8
12
|
const parsed = JSON.parse(raw);
|
|
9
13
|
if (!isLocalResolutionPin(parsed)) return { kind: "invalid" };
|
|
10
14
|
return {
|
|
@@ -17,28 +21,43 @@ async function readLocalResolutionPin(cwd) {
|
|
|
17
21
|
throw error;
|
|
18
22
|
}
|
|
19
23
|
}
|
|
20
|
-
async function writeLocalResolutionPin(cwd, pin) {
|
|
24
|
+
async function writeLocalResolutionPin(cwd, pin, signal) {
|
|
21
25
|
const prismaDir = path.join(cwd, ".prisma");
|
|
26
|
+
signal?.throwIfAborted();
|
|
22
27
|
await mkdir(prismaDir, { recursive: true });
|
|
23
28
|
const pinPath = path.join(cwd, LOCAL_RESOLUTION_PIN_RELATIVE_PATH);
|
|
24
29
|
const tmpPath = path.join(prismaDir, `local.${process.pid}.${Date.now()}.tmp`);
|
|
25
|
-
await writeFile(tmpPath, `${JSON.stringify(pin, null, 2)}\n`,
|
|
30
|
+
await writeFile(tmpPath, `${JSON.stringify(pin, null, 2)}\n`, {
|
|
31
|
+
encoding: "utf8",
|
|
32
|
+
signal
|
|
33
|
+
});
|
|
34
|
+
signal?.throwIfAborted();
|
|
26
35
|
await rename(tmpPath, pinPath);
|
|
27
36
|
}
|
|
28
|
-
async function ensureLocalResolutionPinGitignore(cwd) {
|
|
37
|
+
async function ensureLocalResolutionPinGitignore(cwd, signal) {
|
|
29
38
|
const gitignorePath = path.join(cwd, ".gitignore");
|
|
30
39
|
let existing = null;
|
|
40
|
+
signal?.throwIfAborted();
|
|
31
41
|
try {
|
|
32
|
-
existing = await readFile(gitignorePath,
|
|
42
|
+
existing = await readFile(gitignorePath, {
|
|
43
|
+
encoding: "utf8",
|
|
44
|
+
signal
|
|
45
|
+
});
|
|
33
46
|
} catch (error) {
|
|
34
47
|
if (error.code !== "ENOENT") throw error;
|
|
35
48
|
}
|
|
36
49
|
if (existing === null) {
|
|
37
|
-
await writeFile(gitignorePath, ".prisma/\n",
|
|
50
|
+
await writeFile(gitignorePath, ".prisma/\n", {
|
|
51
|
+
encoding: "utf8",
|
|
52
|
+
signal
|
|
53
|
+
});
|
|
38
54
|
return;
|
|
39
55
|
}
|
|
40
56
|
if (existing.split(/\r?\n/).map((line) => line.trim()).some((line) => line === ".prisma/" || line === ".prisma/local.json")) return;
|
|
41
|
-
await writeFile(gitignorePath, existing.endsWith("\n") ? `${existing}.prisma/\n` : `${existing}\n.prisma/\n`,
|
|
57
|
+
await writeFile(gitignorePath, existing.endsWith("\n") ? `${existing}.prisma/\n` : `${existing}\n.prisma/\n`, {
|
|
58
|
+
encoding: "utf8",
|
|
59
|
+
signal
|
|
60
|
+
});
|
|
42
61
|
}
|
|
43
62
|
function isLocalResolutionPin(value) {
|
|
44
63
|
if (!value || typeof value !== "object") return false;
|
|
@@ -11,7 +11,8 @@ async function resolveProjectTarget(options) {
|
|
|
11
11
|
throw await projectSetupRequiredError({
|
|
12
12
|
cwd: options.context.runtime.cwd,
|
|
13
13
|
projects,
|
|
14
|
-
commandName: options.commandName
|
|
14
|
+
commandName: options.commandName,
|
|
15
|
+
signal: options.context.runtime.signal
|
|
15
16
|
});
|
|
16
17
|
}
|
|
17
18
|
async function inspectProjectBinding(options) {
|
|
@@ -26,7 +27,8 @@ async function inspectProjectBinding(options) {
|
|
|
26
27
|
...await buildProjectSetupSuggestion({
|
|
27
28
|
cwd: options.context.runtime.cwd,
|
|
28
29
|
projects,
|
|
29
|
-
commandName: options.commandName ?? "project show"
|
|
30
|
+
commandName: options.commandName ?? "project show",
|
|
31
|
+
signal: options.context.runtime.signal
|
|
30
32
|
})
|
|
31
33
|
};
|
|
32
34
|
}
|
|
@@ -72,7 +74,7 @@ function localStateStaleError() {
|
|
|
72
74
|
});
|
|
73
75
|
}
|
|
74
76
|
async function buildProjectSetupSuggestion(options) {
|
|
75
|
-
const suggestedName = await inferTargetName(options.cwd);
|
|
77
|
+
const suggestedName = await inferTargetName(options.cwd, options.signal);
|
|
76
78
|
const candidates = sortProjects(options.projects.filter((project) => projectMatchesSuggestedName(project, suggestedName.name))).map(toProjectSummary);
|
|
77
79
|
return {
|
|
78
80
|
suggestedProjectName: suggestedName.name,
|
|
@@ -131,9 +133,13 @@ function buildProjectSetupNextActions(options = {}) {
|
|
|
131
133
|
});
|
|
132
134
|
return actions;
|
|
133
135
|
}
|
|
134
|
-
async function readPackageName(cwd) {
|
|
136
|
+
async function readPackageName(cwd, signal) {
|
|
137
|
+
signal?.throwIfAborted();
|
|
135
138
|
try {
|
|
136
|
-
const raw = await readFile(path.join(cwd, "package.json"),
|
|
139
|
+
const raw = await readFile(path.join(cwd, "package.json"), {
|
|
140
|
+
encoding: "utf8",
|
|
141
|
+
signal
|
|
142
|
+
});
|
|
137
143
|
const parsed = JSON.parse(raw);
|
|
138
144
|
if (!parsed || typeof parsed !== "object") return null;
|
|
139
145
|
const packageName = "name" in parsed ? parsed.name : null;
|
|
@@ -144,8 +150,8 @@ async function readPackageName(cwd) {
|
|
|
144
150
|
throw error;
|
|
145
151
|
}
|
|
146
152
|
}
|
|
147
|
-
async function inferTargetName(cwd) {
|
|
148
|
-
const packageName = await readPackageName(cwd);
|
|
153
|
+
async function inferTargetName(cwd, signal) {
|
|
154
|
+
const packageName = await readPackageName(cwd, signal);
|
|
149
155
|
if (packageName && isValidInferredTargetName(packageName)) return {
|
|
150
156
|
name: packageName,
|
|
151
157
|
source: "package-name"
|
|
@@ -186,7 +192,7 @@ async function resolveBoundProjectTarget(options, projects, settings) {
|
|
|
186
192
|
targetNameSource: "env"
|
|
187
193
|
});
|
|
188
194
|
}
|
|
189
|
-
const localPin = await readLocalResolutionPin(options.context.runtime.cwd);
|
|
195
|
+
const localPin = await readLocalResolutionPin(options.context.runtime.cwd, options.context.runtime.signal);
|
|
190
196
|
if (localPin.kind === "invalid") throw localStateStaleError();
|
|
191
197
|
if (localPin.kind === "present") {
|
|
192
198
|
if (localPin.pin.workspaceId !== options.workspace.id) throw localStateStaleError();
|
|
@@ -20,8 +20,8 @@ async function bindProjectToDirectory(context, workspace, project, action) {
|
|
|
20
20
|
await writeLocalResolutionPin(context.runtime.cwd, {
|
|
21
21
|
workspaceId: workspace.id,
|
|
22
22
|
projectId: project.id
|
|
23
|
-
});
|
|
24
|
-
await ensureLocalResolutionPinGitignore(context.runtime.cwd);
|
|
23
|
+
}, context.runtime.signal);
|
|
24
|
+
await ensureLocalResolutionPinGitignore(context.runtime.cwd, context.runtime.signal);
|
|
25
25
|
return {
|
|
26
26
|
workspace,
|
|
27
27
|
project,
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
import { CliError, authRequiredError } from "./errors.js";
|
|
1
|
+
import { CliError, authRequiredError, commandCanceledError } from "./errors.js";
|
|
2
2
|
import { cliErrorToJson, writeHumanError, writeHumanLines, writeJsonError, writeJsonEvent, writeJsonSuccess } from "./output.js";
|
|
3
3
|
import { getCommandDescriptor } from "./command-meta.js";
|
|
4
4
|
import { resolveGlobalFlags } from "./global-flags.js";
|
|
5
5
|
import { createCommandContext } from "./runtime.js";
|
|
6
6
|
import { AuthError } from "@prisma/management-api-sdk";
|
|
7
7
|
//#region src/shell/command-runner.ts
|
|
8
|
-
function toCliError(error) {
|
|
8
|
+
function toCliError(error, runtime) {
|
|
9
|
+
if (isCancellationError(error) || runtime.signal.aborted) return commandCanceledError();
|
|
9
10
|
if (error instanceof CliError) return error;
|
|
10
11
|
if (error instanceof AuthError) return authRequiredError(["prisma-cli auth login"], { debug: error.message });
|
|
11
12
|
return null;
|
|
12
13
|
}
|
|
14
|
+
function isCancellationError(error) {
|
|
15
|
+
if (!(error instanceof Error)) return false;
|
|
16
|
+
return error.name === "AbortError" || error.name === "CancelledError";
|
|
17
|
+
}
|
|
13
18
|
async function runCommand(runtime, commandName, options, handler, presenter) {
|
|
14
19
|
const flags = resolveGlobalFlags(runtime.argv, options);
|
|
15
20
|
const context = await createCommandContext(runtime, flags);
|
|
@@ -26,7 +31,7 @@ async function runCommand(runtime, commandName, options, handler, presenter) {
|
|
|
26
31
|
if (flags.quiet) return;
|
|
27
32
|
writeHumanLines(context.output, presenter.renderHuman(context, descriptor, success.result));
|
|
28
33
|
} catch (error) {
|
|
29
|
-
const cliError = toCliError(error);
|
|
34
|
+
const cliError = toCliError(error, runtime);
|
|
30
35
|
if (cliError) {
|
|
31
36
|
if (flags.json) writeJsonError(context.output, commandName, cliError);
|
|
32
37
|
else writeHumanError(context.output, context.ui, cliError, { trace: flags.trace });
|
|
@@ -51,7 +56,7 @@ async function runStreamingCommand(runtime, commandName, options, handler) {
|
|
|
51
56
|
nextActions: []
|
|
52
57
|
});
|
|
53
58
|
} catch (error) {
|
|
54
|
-
const cliError = toCliError(error);
|
|
59
|
+
const cliError = toCliError(error, runtime);
|
|
55
60
|
if (cliError) {
|
|
56
61
|
if (flags.json) writeJsonEvent(context.output, {
|
|
57
62
|
type: "error",
|
package/dist/shell/errors.js
CHANGED
|
@@ -56,6 +56,17 @@ function authRequiredError(nextSteps = ["prisma-cli auth login"], options = {})
|
|
|
56
56
|
nextSteps
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
|
+
function commandCanceledError() {
|
|
60
|
+
return new CliError({
|
|
61
|
+
code: "COMMAND_CANCELED",
|
|
62
|
+
domain: "cli",
|
|
63
|
+
summary: "Command canceled",
|
|
64
|
+
why: null,
|
|
65
|
+
fix: null,
|
|
66
|
+
exitCode: 130,
|
|
67
|
+
humanLines: ["Command canceled [COMMAND_CANCELED]"]
|
|
68
|
+
});
|
|
69
|
+
}
|
|
59
70
|
function workspaceRequiredError() {
|
|
60
71
|
return usageError("Workspace required", "This command needs an active workspace, but the authenticated session does not have one.", "Run prisma-cli auth login and choose a workspace.", ["prisma-cli auth login"], "auth");
|
|
61
72
|
}
|
|
@@ -71,4 +82,4 @@ function featureUnavailableError(summary, why, fix, nextSteps = [], domain = "cl
|
|
|
71
82
|
});
|
|
72
83
|
}
|
|
73
84
|
//#endregion
|
|
74
|
-
export { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError };
|
|
85
|
+
export { CliError, authRequiredError, commandCanceledError, featureUnavailableError, usageError, workspaceRequiredError };
|
package/dist/shell/runtime.js
CHANGED
|
@@ -22,13 +22,13 @@ async function createCommandContext(runtime, flags) {
|
|
|
22
22
|
const fixturePath = runtime.fixturePath ?? runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
|
|
23
23
|
const stateDir = resolveStateDir(runtime);
|
|
24
24
|
let loadedApi;
|
|
25
|
-
if (fixturePath) loadedApi = await MockApi.load(fixturePath);
|
|
25
|
+
if (fixturePath) loadedApi = await MockApi.load(fixturePath, runtime.signal);
|
|
26
26
|
return {
|
|
27
27
|
get api() {
|
|
28
28
|
if (!loadedApi) throw new Error("context.api accessed in real mode. Set runtime.fixturePath or PRISMA_CLI_MOCK_FIXTURE_PATH to use fixture mode.");
|
|
29
29
|
return loadedApi;
|
|
30
30
|
},
|
|
31
|
-
stateStore: new LocalStateStore(stateDir),
|
|
31
|
+
stateStore: new LocalStateStore(stateDir, runtime.signal),
|
|
32
32
|
output: {
|
|
33
33
|
stdout: runtime.stdout,
|
|
34
34
|
stderr: runtime.stderr
|