@prisma/cli 3.0.0-dev.75.1 → 3.0.0-dev.77.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.
@@ -14,7 +14,7 @@ import { DEFAULT_LOCAL_DEV_PORT, resolveLocalBuildType, runLocalApp } from "../l
14
14
  import { formatCommandArgument } from "../shell/command-arguments.js";
15
15
  import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
16
16
  import { buildProjectSetupNextActions, inferTargetName, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
17
- import { bindProjectToDirectory, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
17
+ import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
18
18
  import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
19
19
  import { readLocalGitBranch } from "../lib/git/local-branch.js";
20
20
  import { resolveOrCreatePreviewBuildSettings } from "../lib/app/preview-build-settings.js";
@@ -131,8 +131,10 @@ async function runAppDeploy(context, appName, options) {
131
131
  let localPinResult;
132
132
  if (target.localPinAction) {
133
133
  const setupResult = await bindProjectToDirectory(context, target.workspace, target.project, target.localPinAction);
134
- localPinResult = setupResult.localPin;
135
- maybeRenderProjectLinked(context, setupResult.directory, setupResult.project.name, setupResult.localPin.path);
134
+ if (setupResult.isErr()) throw projectDirectoryBindingErrorToCliError(setupResult.error);
135
+ const projectSetup = setupResult.value;
136
+ localPinResult = projectSetup.localPin;
137
+ maybeRenderProjectLinked(context, projectSetup.directory, projectSetup.project.name, projectSetup.localPin.path);
136
138
  }
137
139
  let framework = await resolveDeployFramework(context, {
138
140
  requestedFramework: options?.framework,
@@ -5,7 +5,7 @@ import { requireComputeAuth } from "../lib/auth/guard.js";
5
5
  import { formatCommandArgument } from "../shell/command-arguments.js";
6
6
  import { readLocalResolutionPin } from "../lib/project/local-pin.js";
7
7
  import { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
8
- import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
8
+ import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
9
9
  import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
10
10
  import { createPreviewAppProvider } from "../lib/app/preview-provider.js";
11
11
  import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
@@ -116,12 +116,14 @@ async function runProjectCreate(context, projectName) {
116
116
  fallbackFix: "Retry the command, or choose an existing Project with prisma-cli project link <id-or-name>."
117
117
  });
118
118
  });
119
+ const bindResult = await bindProjectToDirectory(context, workspace, {
120
+ id: created.id,
121
+ name: created.name
122
+ }, "created");
123
+ if (bindResult.isErr()) throw projectDirectoryBindingErrorToCliError(bindResult.error);
119
124
  return {
120
125
  command: "project.create",
121
- result: await bindProjectToDirectory(context, workspace, {
122
- id: created.id,
123
- name: created.name
124
- }, "created"),
126
+ result: bindResult.value,
125
127
  warnings: [],
126
128
  nextSteps: ["prisma-cli app deploy"]
127
129
  };
@@ -138,7 +140,7 @@ async function runProjectLink(context, projectRef) {
138
140
  projects = await listRealWorkspaceProjects(client, workspace, context.runtime.signal);
139
141
  } else projects = listFixtureWorkspaceProjects(context, workspace);
140
142
  let result;
141
- if (projectRef?.trim()) result = await bindProjectToDirectory(context, workspace, toProjectSummary(resolveProjectForSetup(projectRef.trim(), projects, workspace)), "linked");
143
+ if (projectRef?.trim()) result = await requireProjectDirectoryBinding(context, workspace, toProjectSummary(resolveProjectForSetup(projectRef.trim(), projects, workspace)), "linked");
142
144
  else if (canPrompt(context) && !context.flags.yes) result = await resolveInteractiveProjectLinkSetup(context, workspace, projects, provider);
143
145
  else throw await projectLinkTargetRequiredError(context, projects);
144
146
  return {
@@ -162,7 +164,12 @@ async function resolveInteractiveProjectLinkSetup(context, workspace, projects,
162
164
  nextSteps: ["prisma-cli project link <id-or-name>", "prisma-cli project create <name>"]
163
165
  }
164
166
  });
165
- return bindProjectToDirectory(context, workspace, setup.project, setup.action);
167
+ return requireProjectDirectoryBinding(context, workspace, setup.project, setup.action);
168
+ }
169
+ async function requireProjectDirectoryBinding(context, workspace, project, action) {
170
+ const bindResult = await bindProjectToDirectory(context, workspace, project, action);
171
+ if (bindResult.isErr()) throw projectDirectoryBindingErrorToCliError(bindResult.error);
172
+ return bindResult.value;
166
173
  }
167
174
  async function createProjectForLinkSetup(provider, projectName, workspace, signal) {
168
175
  const created = await provider.createProject({
@@ -29,6 +29,53 @@ var LocalResolutionPinReadAbortedError = class extends TaggedError("LocalResolut
29
29
  });
30
30
  }
31
31
  };
32
+ var LocalResolutionPinSerializationError = class extends TaggedError("LocalResolutionPinSerializationError")() {
33
+ constructor(cause) {
34
+ super({
35
+ message: `Could not serialize ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}.`,
36
+ cause,
37
+ pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
38
+ });
39
+ }
40
+ };
41
+ var LocalResolutionPinWriteAbortedError = class extends TaggedError("LocalResolutionPinWriteAbortedError")() {
42
+ constructor(cause) {
43
+ super({
44
+ message: `Writing ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} was aborted.`,
45
+ cause,
46
+ pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
47
+ });
48
+ }
49
+ };
50
+ var LocalResolutionPinWriteFailedError = class extends TaggedError("LocalResolutionPinWriteFailedError")() {
51
+ constructor(operation, cause) {
52
+ super({
53
+ message: `Could not write ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}.`,
54
+ cause,
55
+ operation,
56
+ pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
57
+ });
58
+ }
59
+ };
60
+ var LocalResolutionPinGitignoreUpdateAbortedError = class extends TaggedError("LocalResolutionPinGitignoreUpdateAbortedError")() {
61
+ constructor(cause) {
62
+ super({
63
+ message: "Updating .gitignore for the local Project binding was aborted.",
64
+ cause,
65
+ gitignorePath: ".gitignore"
66
+ });
67
+ }
68
+ };
69
+ var LocalResolutionPinGitignoreUpdateFailedError = class extends TaggedError("LocalResolutionPinGitignoreUpdateFailedError")() {
70
+ constructor(operation, cause) {
71
+ super({
72
+ message: "Could not update .gitignore for the local Project binding.",
73
+ cause,
74
+ operation,
75
+ gitignorePath: ".gitignore"
76
+ });
77
+ }
78
+ };
32
79
  async function readLocalResolutionPin(cwd, signal) {
33
80
  return Result.gen(async function* () {
34
81
  yield* ensureLocalResolutionPinReadNotAborted(signal);
@@ -72,41 +119,74 @@ function parseLocalResolutionPin(raw) {
72
119
  });
73
120
  }
74
121
  async function writeLocalResolutionPin(cwd, pin, signal) {
75
- const prismaDir = path.join(cwd, ".prisma");
76
- signal?.throwIfAborted();
77
- await mkdir(prismaDir, { recursive: true });
78
- const pinPath = path.join(cwd, LOCAL_RESOLUTION_PIN_RELATIVE_PATH);
79
- const tmpPath = path.join(prismaDir, `local.${process.pid}.${Date.now()}.tmp`);
80
- await writeFile(tmpPath, `${JSON.stringify(pin, null, 2)}\n`, {
81
- encoding: "utf8",
82
- signal
122
+ return Result.gen(async function* () {
123
+ const prismaDir = path.join(cwd, ".prisma");
124
+ yield* ensureLocalResolutionPinWriteNotAborted(signal);
125
+ yield* Result.await(writeLocalResolutionPinBoundary(() => mkdir(prismaDir, { recursive: true }), "create-directory", signal));
126
+ const pinPath = path.join(cwd, LOCAL_RESOLUTION_PIN_RELATIVE_PATH);
127
+ const tmpPath = path.join(prismaDir, `local.${process.pid}.${Date.now()}.tmp`);
128
+ const serialized = yield* serializeLocalResolutionPin(pin);
129
+ yield* Result.await(writeLocalResolutionPinBoundary(() => writeFile(tmpPath, serialized, {
130
+ encoding: "utf8",
131
+ signal
132
+ }), "write-temp-file", signal));
133
+ yield* ensureLocalResolutionPinWriteNotAborted(signal);
134
+ yield* Result.await(writeLocalResolutionPinBoundary(() => rename(tmpPath, pinPath), "rename-temp-file", signal));
135
+ return Result.ok(void 0);
83
136
  });
84
- signal?.throwIfAborted();
85
- await rename(tmpPath, pinPath);
86
137
  }
87
138
  async function ensureLocalResolutionPinGitignore(cwd, signal) {
88
139
  const gitignorePath = path.join(cwd, ".gitignore");
89
140
  let existing = null;
90
- signal?.throwIfAborted();
91
- try {
92
- existing = await readFile(gitignorePath, {
141
+ const notAborted = ensureLocalResolutionPinGitignoreUpdateNotAborted(signal);
142
+ if (notAborted.isErr()) return Result.err(notAborted.error);
143
+ const existingResult = await Result.tryPromise({
144
+ try: () => readFile(gitignorePath, {
93
145
  encoding: "utf8",
94
146
  signal
95
- });
96
- } catch (error) {
97
- if (error.code !== "ENOENT") throw error;
98
- }
99
- if (existing === null) {
100
- await writeFile(gitignorePath, ".prisma/\n", {
147
+ }),
148
+ catch: (cause) => signal?.aborted ? new LocalResolutionPinGitignoreUpdateAbortedError(cause) : new LocalResolutionPinGitignoreUpdateFailedError("read", cause)
149
+ });
150
+ if (existingResult.isErr()) if (existingResult.error instanceof LocalResolutionPinGitignoreUpdateFailedError && existingResult.error.cause.code === "ENOENT") existing = null;
151
+ else return Result.err(existingResult.error);
152
+ else existing = existingResult.value;
153
+ if (existing === null) return writeLocalResolutionPinGitignore(gitignorePath, ".prisma/\n", signal);
154
+ if (existing.split(/\r?\n/).map((line) => line.trim()).some((line) => line === ".prisma/" || line === ".prisma/local.json")) return Result.ok(void 0);
155
+ return writeLocalResolutionPinGitignore(gitignorePath, existing.endsWith("\n") ? `${existing}.prisma/\n` : `${existing}\n.prisma/\n`, signal);
156
+ }
157
+ function ensureLocalResolutionPinWriteNotAborted(signal) {
158
+ return Result.try({
159
+ try: () => signal?.throwIfAborted(),
160
+ catch: (cause) => new LocalResolutionPinWriteAbortedError(cause)
161
+ });
162
+ }
163
+ function serializeLocalResolutionPin(pin) {
164
+ return Result.try({
165
+ try: () => `${JSON.stringify(pin, null, 2)}\n`,
166
+ catch: (cause) => new LocalResolutionPinSerializationError(cause)
167
+ });
168
+ }
169
+ function writeLocalResolutionPinBoundary(run, operation, signal) {
170
+ return Result.tryPromise({
171
+ try: async () => {
172
+ await run();
173
+ },
174
+ catch: (cause) => signal?.aborted ? new LocalResolutionPinWriteAbortedError(cause) : new LocalResolutionPinWriteFailedError(operation, cause)
175
+ });
176
+ }
177
+ function ensureLocalResolutionPinGitignoreUpdateNotAborted(signal) {
178
+ return Result.try({
179
+ try: () => signal?.throwIfAborted(),
180
+ catch: (cause) => new LocalResolutionPinGitignoreUpdateAbortedError(cause)
181
+ });
182
+ }
183
+ function writeLocalResolutionPinGitignore(gitignorePath, contents, signal) {
184
+ return Result.tryPromise({
185
+ try: () => writeFile(gitignorePath, contents, {
101
186
  encoding: "utf8",
102
187
  signal
103
- });
104
- return;
105
- }
106
- if (existing.split(/\r?\n/).map((line) => line.trim()).some((line) => line === ".prisma/" || line === ".prisma/local.json")) return;
107
- await writeFile(gitignorePath, existing.endsWith("\n") ? `${existing}.prisma/\n` : `${existing}\n.prisma/\n`, {
108
- encoding: "utf8",
109
- signal
188
+ }),
189
+ catch: (cause) => signal?.aborted ? new LocalResolutionPinGitignoreUpdateAbortedError(cause) : new LocalResolutionPinGitignoreUpdateFailedError("write", cause)
110
190
  });
111
191
  }
112
192
  function isLocalResolutionPin(value) {
@@ -2,6 +2,7 @@ import { CliError, usageError } from "../../shell/errors.js";
2
2
  import "../../shell/command-arguments.js";
3
3
  import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, ensureLocalResolutionPinGitignore, writeLocalResolutionPin } from "./local-pin.js";
4
4
  import { projectAmbiguousError, projectNotFoundError } from "./resolution.js";
5
+ import { Result, matchError } from "better-result";
5
6
  //#region src/lib/project/setup.ts
6
7
  function isValidProjectSetupName(projectName) {
7
8
  return projectName.trim().length > 0;
@@ -17,21 +18,63 @@ function resolveProjectForSetup(projectRef, projects, workspace) {
17
18
  throw projectNotFoundError(projectRef, workspace);
18
19
  }
19
20
  async function bindProjectToDirectory(context, workspace, project, action) {
20
- await writeLocalResolutionPin(context.runtime.cwd, {
21
- workspaceId: workspace.id,
22
- projectId: project.id
23
- }, context.runtime.signal);
24
- await ensureLocalResolutionPinGitignore(context.runtime.cwd, context.runtime.signal);
25
- return {
26
- workspace,
27
- project,
28
- directory: formatSetupDirectory(context.runtime.cwd),
29
- localPin: {
30
- path: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
31
- written: true
21
+ return Result.gen(async function* () {
22
+ yield* Result.await(writeLocalResolutionPin(context.runtime.cwd, {
23
+ workspaceId: workspace.id,
24
+ projectId: project.id
25
+ }, context.runtime.signal));
26
+ yield* Result.await(ensureLocalResolutionPinGitignore(context.runtime.cwd, context.runtime.signal));
27
+ return Result.ok({
28
+ workspace,
29
+ project,
30
+ directory: formatSetupDirectory(context.runtime.cwd),
31
+ localPin: {
32
+ path: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
33
+ written: true
34
+ },
35
+ action
36
+ });
37
+ });
38
+ }
39
+ function projectDirectoryBindingErrorToCliError(error) {
40
+ return matchError(error, {
41
+ LocalResolutionPinSerializationError: (error) => {
42
+ throw error;
32
43
  },
33
- action
34
- };
44
+ LocalResolutionPinWriteAbortedError: (error) => {
45
+ throw error;
46
+ },
47
+ LocalResolutionPinWriteFailedError: (error) => localStateWriteFailedError(error, {
48
+ why: `The CLI could not write ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}.`,
49
+ meta: {
50
+ pinPath: error.pinPath,
51
+ operation: error.operation
52
+ }
53
+ }),
54
+ LocalResolutionPinGitignoreUpdateAbortedError: (error) => {
55
+ throw error;
56
+ },
57
+ LocalResolutionPinGitignoreUpdateFailedError: (error) => localStateWriteFailedError(error, {
58
+ why: "The CLI could not update .gitignore to keep local Project binding state out of git.",
59
+ meta: {
60
+ gitignorePath: error.gitignorePath,
61
+ operation: error.operation
62
+ }
63
+ })
64
+ });
65
+ }
66
+ function localStateWriteFailedError(error, options) {
67
+ return new CliError({
68
+ code: "LOCAL_STATE_WRITE_FAILED",
69
+ domain: "project",
70
+ summary: "Could not save local Project binding",
71
+ why: options.why,
72
+ fix: "Check that this directory is writable and that .prisma/local.json and .gitignore are not blocked by directories or permissions, then retry.",
73
+ debug: formatDebugDetails(error.cause),
74
+ meta: options.meta,
75
+ exitCode: 1,
76
+ nextSteps: ["prisma-cli project link <id-or-name>", "prisma-cli app deploy --project <id-or-name>"]
77
+ });
35
78
  }
36
79
  function toProjectSummary(project) {
37
80
  return {
@@ -86,4 +129,4 @@ function formatDebugDetails(error) {
86
129
  return typeof error === "string" ? error : null;
87
130
  }
88
131
  //#endregion
89
- export { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary, validateProjectSetupNameText };
132
+ export { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary, validateProjectSetupNameText };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/cli",
3
- "version": "3.0.0-dev.75.1",
3
+ "version": "3.0.0-dev.77.1",
4
4
  "description": "Command-line interface for the Prisma Developer Platform.",
5
5
  "type": "module",
6
6
  "bin": {