@prisma/cli 3.0.0-beta.11 → 3.0.0-beta.13
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/token-storage.js +301 -31
- package/dist/commands/app/index.js +2 -2
- package/dist/commands/auth/index.js +67 -2
- package/dist/controllers/app-env.js +1 -1
- package/dist/controllers/app.js +41 -30
- package/dist/controllers/auth.js +140 -2
- package/dist/controllers/project.js +3 -3
- package/dist/lib/app/app-interaction.js +5 -0
- package/dist/lib/app/{preview-provider.js → app-provider.js} +9 -9
- package/dist/lib/app/{preview-branch-database.js → branch-database-api.js} +1 -1
- package/dist/lib/app/build-settings.js +93 -0
- package/dist/lib/app/build.js +80 -0
- package/dist/lib/app/bun-project.js +2 -3
- package/dist/lib/app/{preview-progress.js → deploy-progress.js} +5 -5
- package/dist/lib/app/read-branch.js +30 -0
- package/dist/lib/auth/auth-ops.js +10 -4
- package/dist/lib/auth/guard.js +4 -1
- package/dist/lib/auth/login.js +1 -1
- package/dist/lib/project/resolution.js +6 -3
- package/dist/presenters/auth.js +98 -1
- package/dist/shell/command-meta.js +59 -0
- package/dist/shell/command-runner.js +2 -1
- package/dist/shell/errors.js +50 -1
- package/dist/use-cases/auth.js +68 -1
- package/package.json +2 -2
- package/dist/lib/app/preview-build-settings.js +0 -376
- package/dist/lib/app/preview-build.js +0 -501
- package/dist/lib/app/preview-interaction.js +0 -5
|
@@ -21,13 +21,12 @@ async function readBunPackageJson(appPath, signal) {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
function readBunPackageEntrypoint(packageJson) {
|
|
24
|
-
|
|
25
|
-
if (typeof packageJson?.module === "string") return packageJson.module;
|
|
24
|
+
return typeof packageJson?.main === "string" ? packageJson.main : void 0;
|
|
26
25
|
}
|
|
27
26
|
async function resolveBunEntrypoint(appPath, explicitEntrypoint, signal) {
|
|
28
27
|
const packageJson = await readBunPackageJson(appPath, signal);
|
|
29
28
|
const candidate = explicitEntrypoint ?? readBunPackageEntrypoint(packageJson);
|
|
30
|
-
if (!candidate) throw new Error("Entrypoint is required. Pass --entry or define package.json main
|
|
29
|
+
if (!candidate) throw new Error("Entrypoint is required. Pass --entry or define package.json main.");
|
|
31
30
|
if (path.isAbsolute(candidate)) throw new Error("Entrypoint must be a relative path.");
|
|
32
31
|
const normalized = path.normalize(candidate);
|
|
33
32
|
if (normalized.startsWith("..") || path.isAbsolute(normalized) || normalized.includes(`${path.sep}..${path.sep}`)) throw new Error("Entrypoint must not escape the app directory.");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { renderDeployOutputRows } from "./deploy-output.js";
|
|
2
|
-
//#region src/lib/app/
|
|
3
|
-
function
|
|
2
|
+
//#region src/lib/app/deploy-progress.ts
|
|
3
|
+
function createDeployProgressState() {
|
|
4
4
|
return {
|
|
5
5
|
buildStarted: false,
|
|
6
6
|
buildCompleted: false,
|
|
@@ -13,7 +13,7 @@ function createPreviewDeployProgressState() {
|
|
|
13
13
|
promotedUrl: null
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
|
-
function
|
|
16
|
+
function createDeployProgress(output, ui, enabled, state = createDeployProgressState()) {
|
|
17
17
|
const write = (line) => {
|
|
18
18
|
if (!enabled) return;
|
|
19
19
|
output.write(`${line}\n`);
|
|
@@ -63,7 +63,7 @@ function createPreviewDeployProgress(output, ui, enabled, state = createPreviewD
|
|
|
63
63
|
function formatArtifactSize(byteLength) {
|
|
64
64
|
return `${(byteLength / 1024 / 1024).toFixed(1)} MB`;
|
|
65
65
|
}
|
|
66
|
-
function
|
|
66
|
+
function createPromoteProgress(output, enabled) {
|
|
67
67
|
if (!enabled) return;
|
|
68
68
|
const write = (line) => {
|
|
69
69
|
output.write(`${line}\n`);
|
|
@@ -97,4 +97,4 @@ function createPreviewPromoteProgress(output, enabled) {
|
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
99
|
//#endregion
|
|
100
|
-
export {
|
|
100
|
+
export { createDeployProgress, createDeployProgressState, createPromoteProgress };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/lib/app/read-branch.ts
|
|
2
|
+
/**
|
|
3
|
+
* Resolves the branch an app management command should read from, without ever
|
|
4
|
+
* creating one. Returns the branch whose `gitName` matches `branchName`, else
|
|
5
|
+
* the project's default branch, else null when the project has no branches.
|
|
6
|
+
*/
|
|
7
|
+
async function resolveReadBranch(client, options) {
|
|
8
|
+
const branches = [];
|
|
9
|
+
let cursor;
|
|
10
|
+
do {
|
|
11
|
+
const result = await client.GET("/v1/projects/{projectId}/branches", {
|
|
12
|
+
params: {
|
|
13
|
+
path: { projectId: options.projectId },
|
|
14
|
+
query: { cursor }
|
|
15
|
+
},
|
|
16
|
+
signal: options.signal
|
|
17
|
+
});
|
|
18
|
+
if (result.error || !result.data) throw new Error(`Failed to list branches for project ${options.projectId}: ${JSON.stringify(result.error)}`);
|
|
19
|
+
branches.push(...result.data.data);
|
|
20
|
+
cursor = result.data.pagination.hasMore ? result.data.pagination.nextCursor ?? void 0 : void 0;
|
|
21
|
+
} while (cursor);
|
|
22
|
+
const chosen = branches.find((branch) => branch.gitName === options.branchName) ?? branches.find((branch) => branch.isDefault) ?? null;
|
|
23
|
+
return chosen ? {
|
|
24
|
+
id: chosen.id,
|
|
25
|
+
name: chosen.gitName,
|
|
26
|
+
kind: chosen.role
|
|
27
|
+
} : null;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { resolveReadBranch };
|
|
@@ -26,7 +26,7 @@ function workspaceIdFromClaims(claims) {
|
|
|
26
26
|
}
|
|
27
27
|
async function performLogin(env, signal) {
|
|
28
28
|
await login({
|
|
29
|
-
tokenStorage: new FileTokenStorage(env, signal),
|
|
29
|
+
tokenStorage: new FileTokenStorage(env, signal, { activateOnSetTokens: true }),
|
|
30
30
|
env,
|
|
31
31
|
signal
|
|
32
32
|
});
|
|
@@ -38,7 +38,8 @@ async function readAuthState(env, signal) {
|
|
|
38
38
|
if (serviceToken.length === 0) throw new Error(`${SERVICE_TOKEN_ENV_VAR} is set but empty. Provide a valid token or unset the variable.`);
|
|
39
39
|
return readServiceTokenAuthState(serviceToken, env, signal);
|
|
40
40
|
}
|
|
41
|
-
const
|
|
41
|
+
const tokenStorage = new FileTokenStorage(env, signal);
|
|
42
|
+
const tokens = await tokenStorage.getTokens();
|
|
42
43
|
if (!tokens) return {
|
|
43
44
|
authenticated: false,
|
|
44
45
|
provider: null,
|
|
@@ -48,15 +49,20 @@ async function readAuthState(env, signal) {
|
|
|
48
49
|
};
|
|
49
50
|
const client = await requireComputeAuth(env, signal);
|
|
50
51
|
const currentPrincipal = await readCurrentPrincipalAuthState(client, signal);
|
|
51
|
-
if (currentPrincipal)
|
|
52
|
+
if (currentPrincipal) {
|
|
53
|
+
if (currentPrincipal.authenticated && currentPrincipal.workspace) await tokenStorage.rememberWorkspace?.(tokens.workspaceId, currentPrincipal.workspace);
|
|
54
|
+
return currentPrincipal;
|
|
55
|
+
}
|
|
52
56
|
const claims = decodeJwtPayload(tokens.accessToken);
|
|
53
|
-
|
|
57
|
+
const authState = await buildAuthState({
|
|
54
58
|
workspaceIdFromCredential: tokens.workspaceId,
|
|
55
59
|
claims,
|
|
56
60
|
env,
|
|
57
61
|
client,
|
|
58
62
|
signal
|
|
59
63
|
});
|
|
64
|
+
if (authState.authenticated && authState.workspace) await tokenStorage.rememberWorkspace?.(tokens.workspaceId, authState.workspace);
|
|
65
|
+
return authState;
|
|
60
66
|
}
|
|
61
67
|
async function readServiceTokenAuthState(token, env, signal) {
|
|
62
68
|
const client = await requireComputeAuth(env, signal);
|
package/dist/lib/auth/guard.js
CHANGED
|
@@ -22,7 +22,10 @@ async function requireComputeAuth(env = process.env, signal) {
|
|
|
22
22
|
token
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
|
-
const tokenStorage = new FileTokenStorage(env, signal
|
|
25
|
+
const tokenStorage = new FileTokenStorage(env, signal, {
|
|
26
|
+
activateOnSetTokens: false,
|
|
27
|
+
lockSetTokens: false
|
|
28
|
+
});
|
|
26
29
|
if (!await tokenStorage.getTokens()) return null;
|
|
27
30
|
return createManagementApiSdk({
|
|
28
31
|
clientId: CLIENT_ID,
|
package/dist/lib/auth/login.js
CHANGED
|
@@ -151,7 +151,7 @@ var LoginState = class {
|
|
|
151
151
|
output;
|
|
152
152
|
constructor(options) {
|
|
153
153
|
this.options = options;
|
|
154
|
-
this.tokenStorage = options.tokenStorage ?? new FileTokenStorage(options.env, options.signal);
|
|
154
|
+
this.tokenStorage = options.tokenStorage ?? new FileTokenStorage(options.env, options.signal, { activateOnSetTokens: true });
|
|
155
155
|
this.sdk = createManagementApiSdk({
|
|
156
156
|
clientId: options.clientId ?? "cmm3lndn701oo0uefvxzo0ivw",
|
|
157
157
|
redirectUri: `http://${options.hostname}:${options.port}/auth/callback`,
|
|
@@ -138,13 +138,16 @@ function localStateStaleCliError() {
|
|
|
138
138
|
nextSteps: ["prisma-cli project list", "prisma-cli project link <id-or-name>"]
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
|
+
function localProjectWorkspaceMismatchError(options) {
|
|
142
|
+
return projectResolutionErrorToCliError(new LocalProjectWorkspaceMismatchError(options));
|
|
143
|
+
}
|
|
141
144
|
function localProjectWorkspaceMismatchCliError(options) {
|
|
142
145
|
return new CliError({
|
|
143
146
|
code: "LOCAL_PROJECT_WORKSPACE_MISMATCH",
|
|
144
147
|
domain: "project",
|
|
145
148
|
summary: "Project link uses another workspace",
|
|
146
149
|
why: `${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} links this directory to project ${options.pinnedProjectId} in workspace ${options.pinnedWorkspaceId}, but your current CLI session is workspace "${options.activeWorkspace.name}" (${options.activeWorkspace.id}).`,
|
|
147
|
-
fix: "
|
|
150
|
+
fix: "Switch to the linked workspace, or relink this directory to a project in the current workspace.",
|
|
148
151
|
meta: {
|
|
149
152
|
pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
|
|
150
153
|
pinnedWorkspaceId: options.pinnedWorkspaceId,
|
|
@@ -154,7 +157,7 @@ function localProjectWorkspaceMismatchCliError(options) {
|
|
|
154
157
|
},
|
|
155
158
|
exitCode: 1,
|
|
156
159
|
nextSteps: [
|
|
157
|
-
|
|
160
|
+
`prisma-cli auth workspace use ${options.pinnedWorkspaceId}`,
|
|
158
161
|
"prisma-cli project list",
|
|
159
162
|
"prisma-cli project link <id-or-name>"
|
|
160
163
|
]
|
|
@@ -380,4 +383,4 @@ function toProjectSummary(project) {
|
|
|
380
383
|
};
|
|
381
384
|
}
|
|
382
385
|
//#endregion
|
|
383
|
-
export { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, projectAmbiguousError, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects };
|
|
386
|
+
export { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, localProjectWorkspaceMismatchError, projectAmbiguousError, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects };
|
package/dist/presenters/auth.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { formatDescriptorLabel } from "../shell/command-meta.js";
|
|
2
|
+
import { padDisplay } from "../shell/ui.js";
|
|
1
3
|
import { renderMutate, renderShow } from "../output/patterns.js";
|
|
4
|
+
import stringWidth from "string-width";
|
|
2
5
|
//#region src/presenters/auth.ts
|
|
3
6
|
function renderAuthSuccess(context, descriptor, command, result) {
|
|
4
7
|
if (command === "auth.login") {
|
|
@@ -62,11 +65,105 @@ function renderAuthSuccess(context, descriptor, command, result) {
|
|
|
62
65
|
}]
|
|
63
66
|
}, context.ui);
|
|
64
67
|
}
|
|
68
|
+
function renderAuthWorkspaceList(context, descriptor, result) {
|
|
69
|
+
const ui = context.ui;
|
|
70
|
+
const rail = ui.dim("│");
|
|
71
|
+
const lines = [
|
|
72
|
+
`${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim("Listing authenticated workspaces on this machine.")}`,
|
|
73
|
+
"",
|
|
74
|
+
`${rail} ${ui.accent("auth source:")} ${authSourceLabel(result.authSource)}`
|
|
75
|
+
];
|
|
76
|
+
const hasMixedSources = new Set(result.workspaces.map((workspace) => workspace.source)).size > 1;
|
|
77
|
+
if (result.workspaces.length === 0) {
|
|
78
|
+
lines.push(`${rail} ${ui.dim("No local OAuth workspaces found.")}`);
|
|
79
|
+
return lines;
|
|
80
|
+
}
|
|
81
|
+
const nameWidth = Math.max(4, ...result.workspaces.map((workspace) => stringWidth(workspace.name)));
|
|
82
|
+
const idWidth = Math.max(2, ...result.workspaces.map((workspace) => stringWidth(workspace.id)));
|
|
83
|
+
const sourceWidth = hasMixedSources ? Math.max(6, ...result.workspaces.map((workspace) => stringWidth(workspaceSourceLabel(workspace.source)))) : 0;
|
|
84
|
+
lines.push(rail);
|
|
85
|
+
lines.push(hasMixedSources ? `${rail} ${ui.accent(padDisplay("name", nameWidth))} ${ui.accent(padDisplay("id", idWidth))} ${ui.accent(padDisplay("source", sourceWidth))} ${ui.accent("status")}` : `${rail} ${ui.accent(padDisplay("name", nameWidth))} ${ui.accent(padDisplay("id", idWidth))} ${ui.accent("status")}`);
|
|
86
|
+
for (const workspace of result.workspaces) {
|
|
87
|
+
const status = workspace.active ? "active" : "";
|
|
88
|
+
const source = workspaceSourceLabel(workspace.source);
|
|
89
|
+
lines.push(hasMixedSources ? `${rail} ${padDisplay(workspace.name, nameWidth)} ${padDisplay(workspace.id, idWidth)} ${padDisplay(source, sourceWidth)} ${status}` : `${rail} ${padDisplay(workspace.name, nameWidth)} ${padDisplay(workspace.id, idWidth)} ${status}`);
|
|
90
|
+
}
|
|
91
|
+
return lines;
|
|
92
|
+
}
|
|
93
|
+
function serializeAuthWorkspaceList(result) {
|
|
94
|
+
return {
|
|
95
|
+
context: {
|
|
96
|
+
authSource: result.authSource,
|
|
97
|
+
activeWorkspaceId: result.activeWorkspace?.id ?? null,
|
|
98
|
+
activeWorkspaceName: result.activeWorkspace?.name ?? null
|
|
99
|
+
},
|
|
100
|
+
items: result.workspaces.map((workspace) => ({
|
|
101
|
+
id: workspace.id,
|
|
102
|
+
name: workspace.name,
|
|
103
|
+
status: workspace.active ? "active" : null,
|
|
104
|
+
source: workspace.source,
|
|
105
|
+
switchable: workspace.switchable,
|
|
106
|
+
credentialWorkspaceId: workspace.credentialWorkspaceId,
|
|
107
|
+
lastSeenAt: workspace.lastSeenAt
|
|
108
|
+
})),
|
|
109
|
+
count: result.workspaces.length
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function renderAuthWorkspaceUse(context, descriptor, result) {
|
|
113
|
+
return renderMutate({
|
|
114
|
+
title: "Switching the local CLI workspace.",
|
|
115
|
+
descriptor,
|
|
116
|
+
context: [...result.previousWorkspace ? [{
|
|
117
|
+
key: "previous",
|
|
118
|
+
value: result.previousWorkspace.name
|
|
119
|
+
}] : [], {
|
|
120
|
+
key: "workspace",
|
|
121
|
+
value: result.workspace.name
|
|
122
|
+
}],
|
|
123
|
+
operationDescription: "Applying workspace selection",
|
|
124
|
+
operationCount: 1,
|
|
125
|
+
details: ["Local OAuth workspace selection updated."]
|
|
126
|
+
}, context.ui);
|
|
127
|
+
}
|
|
128
|
+
function serializeAuthWorkspaceUse(result) {
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
function renderAuthWorkspaceLogout(context, descriptor, result) {
|
|
132
|
+
return renderMutate({
|
|
133
|
+
title: "Removing a local OAuth workspace session.",
|
|
134
|
+
descriptor,
|
|
135
|
+
context: [{
|
|
136
|
+
key: "workspace",
|
|
137
|
+
value: result.workspace.name
|
|
138
|
+
}, ...result.activeWorkspace ? [{
|
|
139
|
+
key: "active",
|
|
140
|
+
value: result.activeWorkspace.name
|
|
141
|
+
}] : [{
|
|
142
|
+
key: "active",
|
|
143
|
+
value: "none",
|
|
144
|
+
tone: "dim"
|
|
145
|
+
}]],
|
|
146
|
+
operationDescription: "Removing workspace session",
|
|
147
|
+
operationCount: 1,
|
|
148
|
+
details: [result.wasActive ? "Removed active workspace session; no replacement workspace was selected." : "Removed workspace session."]
|
|
149
|
+
}, context.ui);
|
|
150
|
+
}
|
|
151
|
+
function serializeAuthWorkspaceLogout(result) {
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
65
154
|
function providerLabel(provider) {
|
|
66
155
|
if (provider === "github") return "GitHub";
|
|
67
156
|
if (provider === "google") return "Google";
|
|
68
157
|
return "";
|
|
69
158
|
}
|
|
159
|
+
function authSourceLabel(source) {
|
|
160
|
+
if (source === "oauth") return "local OAuth";
|
|
161
|
+
if (source === "service_token") return "PRISMA_SERVICE_TOKEN";
|
|
162
|
+
return "none";
|
|
163
|
+
}
|
|
164
|
+
function workspaceSourceLabel(source) {
|
|
165
|
+
return source === "service_token" ? "service token" : "OAuth";
|
|
166
|
+
}
|
|
70
167
|
function authUserLabel(result) {
|
|
71
168
|
return result.user?.email ?? credentialUserLabel(result);
|
|
72
169
|
}
|
|
@@ -83,4 +180,4 @@ function credentialUserLabel(result) {
|
|
|
83
180
|
return null;
|
|
84
181
|
}
|
|
85
182
|
//#endregion
|
|
86
|
-
export { renderAuthSuccess };
|
|
183
|
+
export { renderAuthSuccess, renderAuthWorkspaceList, renderAuthWorkspaceLogout, renderAuthWorkspaceUse, serializeAuthWorkspaceList, serializeAuthWorkspaceLogout, serializeAuthWorkspaceUse };
|
|
@@ -50,6 +50,65 @@ const DESCRIPTORS = [
|
|
|
50
50
|
description: "Show the authenticated user and accessible workspace",
|
|
51
51
|
examples: ["prisma-cli auth whoami", "prisma-cli auth whoami --json"]
|
|
52
52
|
},
|
|
53
|
+
{
|
|
54
|
+
id: "auth.workspace",
|
|
55
|
+
path: [
|
|
56
|
+
"prisma",
|
|
57
|
+
"auth",
|
|
58
|
+
"workspace"
|
|
59
|
+
],
|
|
60
|
+
description: "Manage local authenticated workspaces",
|
|
61
|
+
examples: [
|
|
62
|
+
"prisma-cli auth workspace list",
|
|
63
|
+
"prisma-cli auth workspace select",
|
|
64
|
+
"prisma-cli auth workspace use wksp_123",
|
|
65
|
+
"prisma-cli auth workspace logout wksp_123"
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: "auth.workspace.list",
|
|
70
|
+
path: [
|
|
71
|
+
"prisma",
|
|
72
|
+
"auth",
|
|
73
|
+
"workspace",
|
|
74
|
+
"list"
|
|
75
|
+
],
|
|
76
|
+
description: "List locally authenticated workspaces",
|
|
77
|
+
examples: ["prisma-cli auth workspace list", "prisma-cli auth workspace list --json"]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "auth.workspace.use",
|
|
81
|
+
path: [
|
|
82
|
+
"prisma",
|
|
83
|
+
"auth",
|
|
84
|
+
"workspace",
|
|
85
|
+
"use"
|
|
86
|
+
],
|
|
87
|
+
description: "Switch the local CLI workspace",
|
|
88
|
+
examples: ["prisma-cli auth workspace use wksp_123", "prisma-cli auth workspace use \"Acme Inc\""]
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: "auth.workspace.select",
|
|
92
|
+
path: [
|
|
93
|
+
"prisma",
|
|
94
|
+
"auth",
|
|
95
|
+
"workspace",
|
|
96
|
+
"select"
|
|
97
|
+
],
|
|
98
|
+
description: "Interactively select the local CLI workspace",
|
|
99
|
+
examples: ["prisma-cli auth workspace select"]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "auth.workspace.logout",
|
|
103
|
+
path: [
|
|
104
|
+
"prisma",
|
|
105
|
+
"auth",
|
|
106
|
+
"workspace",
|
|
107
|
+
"logout"
|
|
108
|
+
],
|
|
109
|
+
description: "Remove one local OAuth workspace session",
|
|
110
|
+
examples: ["prisma-cli auth workspace logout wksp_123", "prisma-cli auth workspace logout \"Acme Inc\""]
|
|
111
|
+
},
|
|
53
112
|
{
|
|
54
113
|
id: "project",
|
|
55
114
|
path: ["prisma", "project"],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CliError, authRequiredError, commandCanceledError } from "./errors.js";
|
|
1
|
+
import { CliError, authConfigInvalidError, authRequiredError, commandCanceledError } from "./errors.js";
|
|
2
2
|
import { getCommandDescriptor } from "./command-meta.js";
|
|
3
3
|
import { resolveGlobalFlags } from "./global-flags.js";
|
|
4
4
|
import { createCommandContext } from "./runtime.js";
|
|
@@ -11,6 +11,7 @@ function toCliError(error, runtime) {
|
|
|
11
11
|
if (isCancellationError(error) || runtime.signal.aborted) return commandCanceledError();
|
|
12
12
|
if (error instanceof CliError) return error;
|
|
13
13
|
if (error instanceof AuthError) return authRequiredError(["prisma-cli auth login"], { debug: error.message });
|
|
14
|
+
if (error instanceof Error && error.message.startsWith("PRISMA_SERVICE_TOKEN is set but empty")) return authConfigInvalidError(error.message);
|
|
14
15
|
return null;
|
|
15
16
|
}
|
|
16
17
|
function isCancellationError(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 authConfigInvalidError(message) {
|
|
60
|
+
return new CliError({
|
|
61
|
+
code: "AUTH_CONFIG_INVALID",
|
|
62
|
+
domain: "auth",
|
|
63
|
+
summary: "Authentication configuration is invalid",
|
|
64
|
+
why: message,
|
|
65
|
+
fix: "Provide a valid PRISMA_SERVICE_TOKEN value, or unset the variable to use local OAuth login.",
|
|
66
|
+
exitCode: 1,
|
|
67
|
+
nextSteps: ["prisma-cli auth login"]
|
|
68
|
+
});
|
|
69
|
+
}
|
|
59
70
|
function commandCanceledError() {
|
|
60
71
|
return new CliError({
|
|
61
72
|
code: "COMMAND_CANCELED",
|
|
@@ -70,6 +81,44 @@ function commandCanceledError() {
|
|
|
70
81
|
function workspaceRequiredError() {
|
|
71
82
|
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");
|
|
72
83
|
}
|
|
84
|
+
function workspaceSwitchUnavailableError() {
|
|
85
|
+
return new CliError({
|
|
86
|
+
code: "WORKSPACE_SWITCH_UNAVAILABLE",
|
|
87
|
+
domain: "auth",
|
|
88
|
+
summary: "Workspace switching is unavailable",
|
|
89
|
+
why: "PRISMA_SERVICE_TOKEN is set, so authenticated commands use that token instead of local OAuth workspaces.",
|
|
90
|
+
fix: "Unset PRISMA_SERVICE_TOKEN to switch between local OAuth workspaces, or use a token for the workspace you want.",
|
|
91
|
+
exitCode: 1,
|
|
92
|
+
nextSteps: ["unset PRISMA_SERVICE_TOKEN", "prisma-cli auth workspace list"]
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function workspaceNotAuthenticatedError(workspaceRef) {
|
|
96
|
+
return new CliError({
|
|
97
|
+
code: "WORKSPACE_NOT_AUTHENTICATED",
|
|
98
|
+
domain: "auth",
|
|
99
|
+
summary: "Workspace is not authenticated",
|
|
100
|
+
why: `No stored OAuth session matched "${workspaceRef}".`,
|
|
101
|
+
fix: "Run prisma-cli auth login and authorize that workspace, then switch to it.",
|
|
102
|
+
meta: { workspaceRef },
|
|
103
|
+
exitCode: 1,
|
|
104
|
+
nextSteps: ["prisma-cli auth workspace list", "prisma-cli auth login"]
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function workspaceAmbiguousError(workspaceRef, matches) {
|
|
108
|
+
return new CliError({
|
|
109
|
+
code: "WORKSPACE_AMBIGUOUS",
|
|
110
|
+
domain: "auth",
|
|
111
|
+
summary: "Workspace name is ambiguous",
|
|
112
|
+
why: `Multiple authenticated workspaces matched "${workspaceRef}".`,
|
|
113
|
+
fix: "Run prisma-cli auth workspace list and switch by workspace id.",
|
|
114
|
+
meta: {
|
|
115
|
+
workspaceRef,
|
|
116
|
+
matches
|
|
117
|
+
},
|
|
118
|
+
exitCode: 2,
|
|
119
|
+
nextSteps: ["prisma-cli auth workspace list"]
|
|
120
|
+
});
|
|
121
|
+
}
|
|
73
122
|
function featureUnavailableError(summary, why, fix, nextSteps = [], domain = "cli") {
|
|
74
123
|
return new CliError({
|
|
75
124
|
code: "FEATURE_UNAVAILABLE",
|
|
@@ -82,4 +131,4 @@ function featureUnavailableError(summary, why, fix, nextSteps = [], domain = "cl
|
|
|
82
131
|
});
|
|
83
132
|
}
|
|
84
133
|
//#endregion
|
|
85
|
-
export { CliError, authRequiredError, commandCanceledError, featureUnavailableError, usageError, workspaceRequiredError };
|
|
134
|
+
export { CliError, authConfigInvalidError, authRequiredError, commandCanceledError, featureUnavailableError, usageError, workspaceAmbiguousError, workspaceNotAuthenticatedError, workspaceRequiredError, workspaceSwitchUnavailableError };
|
package/dist/use-cases/auth.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { usageError } from "../shell/errors.js";
|
|
1
|
+
import { authRequiredError, usageError, workspaceAmbiguousError, workspaceNotAuthenticatedError } from "../shell/errors.js";
|
|
2
2
|
//#region src/use-cases/auth.ts
|
|
3
3
|
function createAuthUseCases(dependencies) {
|
|
4
4
|
return {
|
|
@@ -15,6 +15,70 @@ function createAuthUseCases(dependencies) {
|
|
|
15
15
|
await dependencies.sessionGateway.clearAuthSession();
|
|
16
16
|
return resolveCurrentAuthState(dependencies);
|
|
17
17
|
},
|
|
18
|
+
listWorkspaces: async () => {
|
|
19
|
+
const session = await dependencies.sessionGateway.readAuthSession();
|
|
20
|
+
if (!session) return {
|
|
21
|
+
authSource: "none",
|
|
22
|
+
activeWorkspace: null,
|
|
23
|
+
workspaces: []
|
|
24
|
+
};
|
|
25
|
+
const workspaces = dependencies.identityGateway.listUserWorkspaces(session.userId);
|
|
26
|
+
return {
|
|
27
|
+
authSource: "oauth",
|
|
28
|
+
activeWorkspace: workspaces.find((workspace) => workspace.id === session.workspaceId) ?? null,
|
|
29
|
+
workspaces: workspaces.map((workspace) => ({
|
|
30
|
+
...workspace,
|
|
31
|
+
credentialWorkspaceId: workspace.id,
|
|
32
|
+
active: workspace.id === session.workspaceId,
|
|
33
|
+
source: "oauth",
|
|
34
|
+
switchable: true,
|
|
35
|
+
lastSeenAt: null
|
|
36
|
+
}))
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
useWorkspace: async (workspaceRef) => {
|
|
40
|
+
const session = await dependencies.sessionGateway.readAuthSession();
|
|
41
|
+
if (!session) throw authRequiredError(["prisma-cli auth login"]);
|
|
42
|
+
const ref = workspaceRef.trim();
|
|
43
|
+
const matches = dependencies.identityGateway.listUserWorkspaces(session.userId).filter((workspace) => workspaceMatchesRef(workspace, ref));
|
|
44
|
+
if (matches.length === 0) throw workspaceNotAuthenticatedError(workspaceRef);
|
|
45
|
+
if (matches.length > 1) throw workspaceAmbiguousError(workspaceRef, matches.map((workspace) => ({
|
|
46
|
+
id: workspace.id,
|
|
47
|
+
name: workspace.name,
|
|
48
|
+
credentialWorkspaceId: workspace.id
|
|
49
|
+
})));
|
|
50
|
+
const selected = matches[0];
|
|
51
|
+
const previousWorkspace = dependencies.identityGateway.getWorkspace(session.workspaceId) ?? null;
|
|
52
|
+
await dependencies.sessionGateway.writeAuthSession({
|
|
53
|
+
...session,
|
|
54
|
+
workspaceId: selected.id
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
previousWorkspace,
|
|
58
|
+
workspace: selected
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
logoutWorkspace: async (workspaceRef) => {
|
|
62
|
+
const session = await dependencies.sessionGateway.readAuthSession();
|
|
63
|
+
if (!session) throw workspaceNotAuthenticatedError(workspaceRef);
|
|
64
|
+
const ref = workspaceRef.trim();
|
|
65
|
+
const matches = dependencies.identityGateway.listUserWorkspaces(session.userId).filter((workspace) => workspaceMatchesRef(workspace, ref));
|
|
66
|
+
if (matches.length === 0) throw workspaceNotAuthenticatedError(workspaceRef);
|
|
67
|
+
if (matches.length > 1) throw workspaceAmbiguousError(workspaceRef, matches.map((workspace) => ({
|
|
68
|
+
id: workspace.id,
|
|
69
|
+
name: workspace.name,
|
|
70
|
+
credentialWorkspaceId: workspace.id
|
|
71
|
+
})));
|
|
72
|
+
const workspace = matches[0];
|
|
73
|
+
const wasActive = workspace.id === session.workspaceId;
|
|
74
|
+
const activeWorkspace = wasActive ? null : dependencies.identityGateway.getWorkspace(session.workspaceId) ?? null;
|
|
75
|
+
if (wasActive) await dependencies.sessionGateway.clearAuthSession();
|
|
76
|
+
return {
|
|
77
|
+
workspace,
|
|
78
|
+
wasActive,
|
|
79
|
+
activeWorkspace
|
|
80
|
+
};
|
|
81
|
+
},
|
|
18
82
|
listProviders: async () => dependencies.identityGateway.listProviders(),
|
|
19
83
|
resolveProvider: async (providerId) => {
|
|
20
84
|
const provider = dependencies.identityGateway.getProvider(providerId);
|
|
@@ -39,6 +103,9 @@ function createAuthUseCases(dependencies) {
|
|
|
39
103
|
}
|
|
40
104
|
};
|
|
41
105
|
}
|
|
106
|
+
function workspaceMatchesRef(workspace, ref) {
|
|
107
|
+
return workspace.id === ref || workspace.name.toLowerCase() === ref.toLowerCase();
|
|
108
|
+
}
|
|
42
109
|
async function resolveCurrentAuthState(dependencies) {
|
|
43
110
|
const session = await dependencies.sessionGateway.readAuthSession();
|
|
44
111
|
if (!session) return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/cli",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.13",
|
|
4
4
|
"description": "Command-line interface for the Prisma Developer Platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@clack/prompts": "^1.5.0",
|
|
47
|
-
"@prisma/compute-sdk": "^0.
|
|
47
|
+
"@prisma/compute-sdk": "^0.28.0",
|
|
48
48
|
"@prisma/credentials-store": "^7.8.0",
|
|
49
49
|
"@prisma/management-api-sdk": "^1.37.0",
|
|
50
50
|
"better-result": "^2.9.2",
|