@prisma/cli 3.0.0-alpha.10 → 3.0.0-alpha.11

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.
@@ -1,5 +1,8 @@
1
1
  import { getAuthFilePath } from "../lib/auth/client.js";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
2
4
  import { CredentialsStore } from "@prisma/credentials-store";
5
+ import { randomUUID } from "node:crypto";
3
6
  //#region src/adapters/token-storage.ts
4
7
  function findLatestValidTokens(allCredentials) {
5
8
  for (let i = allCredentials.length - 1; i >= 0; i -= 1) {
@@ -14,10 +17,19 @@ function findLatestValidTokens(allCredentials) {
14
17
  }
15
18
  return null;
16
19
  }
20
+ function tokensEqual(a, b) {
21
+ return a?.workspaceId === b?.workspaceId && a?.accessToken === b?.accessToken && a?.refreshToken === b?.refreshToken;
22
+ }
23
+ function sleep(ms) {
24
+ return new Promise((resolve) => setTimeout(resolve, ms));
25
+ }
17
26
  var FileTokenStorage = class {
18
27
  credentialsStore;
28
+ lockFilePath;
19
29
  constructor(env = process.env) {
20
- this.credentialsStore = new CredentialsStore(getAuthFilePath(env));
30
+ const authFilePath = getAuthFilePath(env);
31
+ this.credentialsStore = new CredentialsStore(authFilePath);
32
+ this.lockFilePath = `${authFilePath}.lock`;
21
33
  }
22
34
  async getTokens() {
23
35
  try {
@@ -38,6 +50,50 @@ var FileTokenStorage = class {
38
50
  if (!tokens) return;
39
51
  await this.credentialsStore.deleteCredentials(tokens.workspaceId);
40
52
  }
53
+ async clearTokensIfCurrent(tokens) {
54
+ if (!tokensEqual(await this.getTokens(), tokens)) return;
55
+ await this.clearTokens();
56
+ }
57
+ async withRefreshLock(fn) {
58
+ const lockId = await this.acquireRefreshLock();
59
+ try {
60
+ return await fn();
61
+ } finally {
62
+ await this.releaseRefreshLock(lockId);
63
+ }
64
+ }
65
+ async acquireRefreshLock() {
66
+ const lockId = randomUUID();
67
+ await fs.mkdir(path.dirname(this.lockFilePath), { recursive: true });
68
+ while (true) try {
69
+ const handle = await fs.open(this.lockFilePath, "wx");
70
+ try {
71
+ await handle.writeFile(lockId, "utf8");
72
+ } finally {
73
+ await handle.close();
74
+ }
75
+ return lockId;
76
+ } catch (error) {
77
+ if (error.code !== "EEXIST") throw error;
78
+ const staleLockId = await this.getStaleRefreshLockId();
79
+ if (staleLockId) {
80
+ await this.releaseRefreshLock(staleLockId);
81
+ continue;
82
+ }
83
+ await sleep(100);
84
+ }
85
+ }
86
+ async getStaleRefreshLockId() {
87
+ const lockId = await fs.readFile(this.lockFilePath, "utf8").catch(() => null);
88
+ if (lockId === null) return null;
89
+ const stats = await fs.stat(this.lockFilePath).catch(() => null);
90
+ if (!stats) return null;
91
+ return Date.now() - stats.mtimeMs > 3e4 ? lockId : null;
92
+ }
93
+ async releaseRefreshLock(lockId) {
94
+ if (await fs.readFile(this.lockFilePath, "utf8").catch(() => null) !== lockId) return;
95
+ await fs.unlink(this.lockFilePath).catch(() => {});
96
+ }
41
97
  };
42
98
  //#endregion
43
99
  export { FileTokenStorage };
@@ -1,9 +1,15 @@
1
- import { CliError } from "./errors.js";
1
+ import { CliError, authRequiredError } 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
+ import { AuthError } from "@prisma/management-api-sdk";
6
7
  //#region src/shell/command-runner.ts
8
+ function toCliError(error) {
9
+ if (error instanceof CliError) return error;
10
+ if (error instanceof AuthError) return authRequiredError(["prisma-cli auth login"], { debug: error.message });
11
+ return null;
12
+ }
7
13
  async function runCommand(runtime, commandName, options, handler, presenter) {
8
14
  const flags = resolveGlobalFlags(runtime.argv, options);
9
15
  const context = await createCommandContext(runtime, flags);
@@ -20,10 +26,11 @@ async function runCommand(runtime, commandName, options, handler, presenter) {
20
26
  if (flags.quiet) return;
21
27
  writeHumanLines(context.output, presenter.renderHuman(context, descriptor, success.result));
22
28
  } catch (error) {
23
- if (error instanceof CliError) {
24
- if (flags.json) writeJsonError(context.output, commandName, error);
25
- else writeHumanError(context.output, context.ui, error, { trace: flags.trace });
26
- process.exitCode = error.exitCode;
29
+ const cliError = toCliError(error);
30
+ if (cliError) {
31
+ if (flags.json) writeJsonError(context.output, commandName, cliError);
32
+ else writeHumanError(context.output, context.ui, cliError, { trace: flags.trace });
33
+ process.exitCode = cliError.exitCode;
27
34
  return;
28
35
  }
29
36
  throw error;
@@ -43,17 +50,18 @@ async function runStreamingCommand(runtime, commandName, options, handler) {
43
50
  nextSteps: []
44
51
  });
45
52
  } catch (error) {
46
- if (error instanceof CliError) {
53
+ const cliError = toCliError(error);
54
+ if (cliError) {
47
55
  if (flags.json) writeJsonEvent(context.output, {
48
56
  type: "error",
49
57
  command: commandName,
50
58
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
51
- error: cliErrorToJson(error),
59
+ error: cliErrorToJson(cliError),
52
60
  warnings: [],
53
- nextSteps: error.nextSteps
61
+ nextSteps: cliError.nextSteps
54
62
  });
55
- else writeHumanError(context.output, context.ui, error, { trace: flags.trace });
56
- process.exitCode = error.exitCode;
63
+ else writeHumanError(context.output, context.ui, cliError, { trace: flags.trace });
64
+ process.exitCode = cliError.exitCode;
57
65
  return;
58
66
  }
59
67
  throw error;
@@ -42,13 +42,14 @@ function usageError(summary, why, fix, nextSteps = [], domain = "cli") {
42
42
  nextSteps
43
43
  });
44
44
  }
45
- function authRequiredError(nextSteps = ["prisma-cli auth login"]) {
45
+ function authRequiredError(nextSteps = ["prisma-cli auth login"], options = {}) {
46
46
  return new CliError({
47
47
  code: "AUTH_REQUIRED",
48
48
  domain: "auth",
49
49
  summary: "Authentication required",
50
50
  why: "This command needs an authenticated session.",
51
51
  fix: "Run prisma-cli auth login, or rerun the command in a TTY to sign in interactively.",
52
+ debug: options.debug,
52
53
  exitCode: 1,
53
54
  nextSteps
54
55
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/cli",
3
- "version": "3.0.0-alpha.10",
3
+ "version": "3.0.0-alpha.11",
4
4
  "description": "Preview of the unified Prisma CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -39,7 +39,7 @@
39
39
  "@prisma/compute-sdk": "^0.18.0",
40
40
  "c12": "4.0.0-beta.4",
41
41
  "@prisma/credentials-store": "^7.7.0",
42
- "@prisma/management-api-sdk": "^1.27.0",
42
+ "@prisma/management-api-sdk": "^1.32.1",
43
43
  "colorette": "^2.0.20",
44
44
  "commander": "^12.1.0",
45
45
  "magicast": "^0.3.5",