@prisma/cli 3.0.0-dev.93.1 → 3.0.0-dev.97.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.
@@ -11,6 +11,7 @@ const EMPTY_AUTH_CONTEXT = {
11
11
  activeWorkspaceId: null,
12
12
  workspaces: {}
13
13
  };
14
+ const UNKNOWN_WORKSPACE_NAME = "Unknown workspace";
14
15
  function getAuthContextFilePath(authFilePath) {
15
16
  const extension = path.extname(authFilePath);
16
17
  if (!extension) return `${authFilePath}.context.json`;
@@ -140,7 +141,7 @@ var FileTokenStorage = class {
140
141
  return credentials.map((credential) => storedCredentialToTokens(credential)).filter((tokens) => tokens !== null).map((tokens) => {
141
142
  const cached = context.state.workspaces[tokens.workspaceId];
142
143
  const id = typeof cached?.id === "string" && cached.id.trim().length > 0 ? cached.id.trim() : tokens.workspaceId;
143
- const name = typeof cached?.name === "string" && cached.name.trim().length > 0 ? cached.name.trim() : id;
144
+ const name = typeof cached?.name === "string" && cached.name.trim().length > 0 ? workspaceDisplayName(cached.name.trim(), tokens.workspaceId) : UNKNOWN_WORKSPACE_NAME;
144
145
  const lastSeenAt = typeof cached?.lastSeenAt === "string" && cached.lastSeenAt.trim().length > 0 ? cached.lastSeenAt.trim() : null;
145
146
  return {
146
147
  id,
@@ -151,6 +152,10 @@ var FileTokenStorage = class {
151
152
  };
152
153
  });
153
154
  }
155
+ async listWorkspaceTokens() {
156
+ this.signal?.throwIfAborted();
157
+ return (await this.readCredentialsFromDisk()).map((credential) => storedCredentialToTokens(credential)).filter((tokens) => tokens !== null);
158
+ }
154
159
  async useWorkspace(workspaceRef) {
155
160
  return this.withRefreshLock(() => this.useWorkspaceUnlocked(workspaceRef));
156
161
  }
@@ -406,5 +411,8 @@ function workspaceMatchesRef(workspace, ref) {
406
411
  function stripWorkspacePrefix(value) {
407
412
  return value.startsWith("wksp_") ? value.slice(5) : value;
408
413
  }
414
+ function workspaceDisplayName(name, credentialWorkspaceId) {
415
+ return name === credentialWorkspaceId ? UNKNOWN_WORKSPACE_NAME : name;
416
+ }
409
417
  //#endregion
410
418
  export { FileTokenStorage, WorkspaceSelectionError };
@@ -1,7 +1,7 @@
1
1
  import { attachCommandDescriptor } from "../../shell/command-meta.js";
2
2
  import { addCompactGlobalFlags, addGlobalFlags } from "../../shell/global-flags.js";
3
3
  import { configureRuntimeCommand } from "../../shell/runtime.js";
4
- import { runAuthLogin, runAuthLogout, runAuthWhoAmI, runAuthWorkspaceList, runAuthWorkspaceLogout, runAuthWorkspaceSelect, runAuthWorkspaceUse } from "../../controllers/auth.js";
4
+ import { runAuthLogin, runAuthLogout, runAuthWhoAmI, runAuthWorkspaceList, runAuthWorkspaceLogout, runAuthWorkspaceUse } from "../../controllers/auth.js";
5
5
  import { runCommand } from "../../shell/command-runner.js";
6
6
  import { renderAuthSuccess, renderAuthWorkspaceList, renderAuthWorkspaceLogout, renderAuthWorkspaceUse, serializeAuthWorkspaceList, serializeAuthWorkspaceLogout, serializeAuthWorkspaceUse } from "../../presenters/auth.js";
7
7
  import { Command, Option } from "commander";
@@ -54,7 +54,6 @@ function createAuthWorkspaceCommand(runtime) {
54
54
  addCompactGlobalFlags(workspace);
55
55
  workspace.addCommand(createAuthWorkspaceListCommand(runtime));
56
56
  workspace.addCommand(createAuthWorkspaceUseCommand(runtime));
57
- workspace.addCommand(createAuthWorkspaceSelectCommand(runtime));
58
57
  workspace.addCommand(createAuthWorkspaceLogoutCommand(runtime));
59
58
  return workspace;
60
59
  }
@@ -69,17 +68,6 @@ function createAuthWorkspaceListCommand(runtime) {
69
68
  });
70
69
  return command;
71
70
  }
72
- function createAuthWorkspaceSelectCommand(runtime) {
73
- const command = attachCommandDescriptor(configureRuntimeCommand(new Command("select"), runtime), "auth.workspace.select");
74
- addGlobalFlags(command);
75
- command.action(async (options) => {
76
- await runCommand(runtime, "auth.workspace.select", options, (context) => runAuthWorkspaceSelect(context), {
77
- renderHuman: (context, descriptor, result) => renderAuthWorkspaceUse(context, descriptor, result),
78
- renderJson: (result) => serializeAuthWorkspaceUse(result)
79
- });
80
- });
81
- return command;
82
- }
83
71
  function createAuthWorkspaceLogoutCommand(runtime) {
84
72
  const command = attachCommandDescriptor(configureRuntimeCommand(new Command("logout"), runtime), "auth.workspace.logout");
85
73
  command.argument("<id-or-name>", "Workspace id or exact name");
@@ -94,7 +82,7 @@ function createAuthWorkspaceLogoutCommand(runtime) {
94
82
  }
95
83
  function createAuthWorkspaceUseCommand(runtime) {
96
84
  const command = attachCommandDescriptor(configureRuntimeCommand(new Command("use"), runtime), "auth.workspace.use");
97
- command.argument("<id-or-name>", "Workspace id or exact name");
85
+ command.argument("[id-or-name]", "Workspace id or exact name");
98
86
  addGlobalFlags(command);
99
87
  command.action(async (workspaceRef, options) => {
100
88
  await runCommand(runtime, "auth.workspace.use", options, (context) => runAuthWorkspaceUse(context, typeof workspaceRef === "string" ? workspaceRef : void 0), {
@@ -1,4 +1,4 @@
1
- import { SERVICE_TOKEN_ENV_VAR } from "../lib/auth/client.js";
1
+ import { CLIENT_ID, SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
2
2
  import { FileTokenStorage, WorkspaceSelectionError } from "../adapters/token-storage.js";
3
3
  import { authRequiredError, usageError, workspaceAmbiguousError, workspaceNotAuthenticatedError, workspaceSwitchUnavailableError } from "../shell/errors.js";
4
4
  import { canPrompt } from "../shell/runtime.js";
@@ -6,6 +6,7 @@ import { performLogin, performLogout, readAuthState } from "../lib/auth/auth-ops
6
6
  import { createAuthUseCases } from "../use-cases/auth.js";
7
7
  import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
8
8
  import { createSelectPromptPort } from "./select-prompt-port.js";
9
+ import { createManagementApiSdk } from "@prisma/management-api-sdk";
9
10
  //#region src/controllers/auth.ts
10
11
  function isRealMode(context) {
11
12
  return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
@@ -42,20 +43,15 @@ async function runAuthWorkspaceList(context) {
42
43
  };
43
44
  }
44
45
  async function runAuthWorkspaceUse(context, workspaceRef) {
45
- if (!workspaceRef?.trim()) throw usageError("Workspace required", "auth workspace use needs a workspace id or cached workspace name.", "Pass a workspace from prisma-cli auth workspace list.", ["prisma-cli auth workspace list"], "auth");
46
+ const trimmedWorkspaceRef = workspaceRef?.trim();
47
+ const selectedWorkspaceRef = trimmedWorkspaceRef ? trimmedWorkspaceRef : await selectWorkspaceSession(context);
46
48
  return {
47
49
  command: "auth.workspace.use",
48
- result: isRealMode(context) ? await useRealAuthWorkspace(context, workspaceRef) : await createAuthUseCases(createCliUseCaseGateways(context)).useWorkspace(workspaceRef),
50
+ result: isRealMode(context) ? await useRealAuthWorkspace(context, selectedWorkspaceRef) : await createAuthUseCases(createCliUseCaseGateways(context)).useWorkspace(selectedWorkspaceRef),
49
51
  warnings: [],
50
52
  nextSteps: ["prisma-cli auth whoami", "prisma-cli project list"]
51
53
  };
52
54
  }
53
- async function runAuthWorkspaceSelect(context) {
54
- return {
55
- ...await runAuthWorkspaceUse(context, await selectWorkspaceSession(context)),
56
- command: "auth.workspace.select"
57
- };
58
- }
59
55
  async function runAuthWorkspaceLogout(context, workspaceRef) {
60
56
  if (!workspaceRef?.trim()) throw usageError("Workspace required", "auth workspace logout needs a workspace id or cached workspace name.", "Pass a workspace from prisma-cli auth workspace list.", ["prisma-cli auth workspace list"], "auth");
61
57
  const result = isRealMode(context) ? await logoutRealAuthWorkspace(context, workspaceRef) : await createAuthUseCases(createCliUseCaseGateways(context)).logoutWorkspace(workspaceRef);
@@ -82,7 +78,8 @@ async function requireAuthenticatedAuthState(context) {
82
78
  }
83
79
  async function listRealAuthWorkspaces(context) {
84
80
  const rawServiceToken = context.runtime.env[SERVICE_TOKEN_ENV_VAR];
85
- const localWorkspaces = await new FileTokenStorage(context.runtime.env, context.runtime.signal).listWorkspaces();
81
+ const storage = new FileTokenStorage(context.runtime.env, context.runtime.signal);
82
+ const localWorkspaces = await hydrateLocalAuthWorkspaces(context, storage, await storage.listWorkspaces());
86
83
  if (rawServiceToken !== void 0) {
87
84
  const authState = await readAuthState(context.runtime.env, context.runtime.signal);
88
85
  return {
@@ -122,6 +119,7 @@ async function listRealAuthWorkspaces(context) {
122
119
  async function useRealAuthWorkspace(context, workspaceRef) {
123
120
  if (context.runtime.env["PRISMA_SERVICE_TOKEN"] !== void 0) throw workspaceSwitchUnavailableError();
124
121
  const storage = new FileTokenStorage(context.runtime.env, context.runtime.signal);
122
+ await hydrateLocalAuthWorkspaces(context, storage, await storage.listWorkspaces());
125
123
  try {
126
124
  const result = await storage.useWorkspace(workspaceRef);
127
125
  return {
@@ -142,6 +140,7 @@ async function useRealAuthWorkspace(context, workspaceRef) {
142
140
  }
143
141
  async function logoutRealAuthWorkspace(context, workspaceRef) {
144
142
  const storage = new FileTokenStorage(context.runtime.env, context.runtime.signal);
143
+ await hydrateLocalAuthWorkspaces(context, storage, await storage.listWorkspaces());
145
144
  try {
146
145
  const result = await storage.logoutWorkspace(workspaceRef);
147
146
  return {
@@ -167,7 +166,7 @@ async function selectWorkspaceSession(context) {
167
166
  const workspaces = (realMode ? await listRealAuthWorkspaces(context) : await createAuthUseCases(createCliUseCaseGateways(context)).listWorkspaces()).workspaces.filter((workspace) => workspace.switchable);
168
167
  if (workspaces.length === 0) throw usageError("No authenticated workspaces", "There are no local OAuth workspace sessions to select.", "Run prisma-cli auth login and authorize a workspace.", ["prisma-cli auth login"], "auth");
169
168
  if (workspaces.length === 1) return workspaces[0].id;
170
- if (!canPrompt(context)) throw usageError("Interactive workspace selection unavailable", "auth workspace select needs an interactive terminal when more than one workspace is available.", "Run prisma-cli auth workspace use <id-or-name> with a workspace from prisma-cli auth workspace list.", ["prisma-cli auth workspace list"], "auth");
169
+ if (!canPrompt(context)) throw usageError("Interactive workspace selection unavailable", "auth workspace use needs an interactive terminal when no workspace is provided and more than one workspace is available.", "Run prisma-cli auth workspace use <id-or-name> with a workspace from prisma-cli auth workspace list.", ["prisma-cli auth workspace list"], "auth");
171
170
  return (await createSelectPromptPort(context).select({
172
171
  message: "Select a workspace",
173
172
  choices: workspaces.map((workspace) => ({
@@ -176,6 +175,78 @@ async function selectWorkspaceSession(context) {
176
175
  }))
177
176
  })).id;
178
177
  }
178
+ async function hydrateLocalAuthWorkspaces(context, storage, workspaces) {
179
+ const candidates = workspaces.filter(needsWorkspaceMetadataHydration);
180
+ if (candidates.length === 0) return workspaces;
181
+ const tokensByCredentialWorkspaceId = new Map((await storage.listWorkspaceTokens()).map((tokens) => [tokens.workspaceId, tokens]));
182
+ let nextWorkspaces = workspaces;
183
+ for (const workspace of candidates) {
184
+ const tokens = tokensByCredentialWorkspaceId.get(workspace.credentialWorkspaceId);
185
+ if (!tokens) continue;
186
+ const resolved = await resolveOAuthWorkspaceMetadata(context, tokens);
187
+ if (!resolved) continue;
188
+ await rememberResolvedWorkspaceMetadata(context, storage, tokens, resolved);
189
+ nextWorkspaces = nextWorkspaces.map((candidate) => candidate.credentialWorkspaceId === workspace.credentialWorkspaceId ? {
190
+ ...candidate,
191
+ id: resolved.id,
192
+ name: resolved.name,
193
+ lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
194
+ } : candidate);
195
+ }
196
+ return nextWorkspaces;
197
+ }
198
+ async function rememberResolvedWorkspaceMetadata(context, storage, tokens, resolved) {
199
+ try {
200
+ await storage.rememberWorkspace(tokens.workspaceId, resolved);
201
+ } catch {
202
+ context.runtime.signal?.throwIfAborted();
203
+ }
204
+ }
205
+ function needsWorkspaceMetadataHydration(workspace) {
206
+ return workspace.id === workspace.credentialWorkspaceId || workspace.name === "Unknown workspace" || workspace.name === workspace.credentialWorkspaceId;
207
+ }
208
+ async function resolveOAuthWorkspaceMetadata(context, tokens) {
209
+ const sdk = createManagementApiSdk({
210
+ clientId: CLIENT_ID,
211
+ redirectUri: "http://localhost:0/auth/callback",
212
+ tokenStorage: createSingleWorkspaceTokenStorage(new FileTokenStorage(context.runtime.env, context.runtime.signal, { activateOnSetTokens: false }), tokens),
213
+ apiBaseUrl: getApiBaseUrl(context.runtime.env)
214
+ });
215
+ try {
216
+ const { data } = await sdk.client.GET("/v1/workspaces/{id}", {
217
+ params: { path: { id: tokens.workspaceId } },
218
+ signal: context.runtime.signal
219
+ });
220
+ const id = stringOrNull(data?.data?.id) ?? tokens.workspaceId;
221
+ const name = stringOrNull(data?.data?.name) ?? id;
222
+ if (id === tokens.workspaceId && name === tokens.workspaceId) return null;
223
+ return {
224
+ id,
225
+ name
226
+ };
227
+ } catch {
228
+ context.runtime.signal?.throwIfAborted();
229
+ return null;
230
+ }
231
+ }
232
+ function createSingleWorkspaceTokenStorage(storage, initialTokens) {
233
+ let currentTokens = initialTokens;
234
+ return {
235
+ getTokens: async () => currentTokens,
236
+ setTokens: async (tokens) => {
237
+ currentTokens = tokens;
238
+ await storage.setTokens(tokens);
239
+ },
240
+ clearTokens: async () => {
241
+ const tokens = currentTokens;
242
+ currentTokens = null;
243
+ if (tokens) await storage.clearTokensIfCurrent(tokens);
244
+ }
245
+ };
246
+ }
247
+ function stringOrNull(value) {
248
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
249
+ }
179
250
  function toAuthWorkspace(workspace) {
180
251
  return {
181
252
  id: workspace.id,
@@ -242,4 +313,4 @@ function createAuthSuccess(command, result, nextSteps) {
242
313
  };
243
314
  }
244
315
  //#endregion
245
- export { requireAuthenticatedAuthState, runAuthLogin, runAuthLogout, runAuthWhoAmI, runAuthWorkspaceList, runAuthWorkspaceLogout, runAuthWorkspaceSelect, runAuthWorkspaceUse };
316
+ export { requireAuthenticatedAuthState, runAuthLogin, runAuthLogout, runAuthWhoAmI, runAuthWorkspaceList, runAuthWorkspaceLogout, runAuthWorkspaceUse };
@@ -60,7 +60,7 @@ const DESCRIPTORS = [
60
60
  description: "Manage local authenticated workspaces",
61
61
  examples: [
62
62
  "prisma-cli auth workspace list",
63
- "prisma-cli auth workspace select",
63
+ "prisma-cli auth workspace use",
64
64
  "prisma-cli auth workspace use wksp_123",
65
65
  "prisma-cli auth workspace logout wksp_123"
66
66
  ]
@@ -85,18 +85,11 @@ const DESCRIPTORS = [
85
85
  "use"
86
86
  ],
87
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"]
88
+ examples: [
89
+ "prisma-cli auth workspace use",
90
+ "prisma-cli auth workspace use wksp_123",
91
+ "prisma-cli auth workspace use \"Acme Inc\""
92
+ ]
100
93
  },
101
94
  {
102
95
  id: "auth.workspace.logout",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/cli",
3
- "version": "3.0.0-dev.93.1",
3
+ "version": "3.0.0-dev.97.1",
4
4
  "description": "Command-line interface for the Prisma Developer Platform.",
5
5
  "type": "module",
6
6
  "bin": {