@prisma/cli 3.0.0-dev.75.1 → 3.0.0-dev.78.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.
@@ -2,7 +2,7 @@ import { CliError, authRequiredError, usageError, workspaceRequiredError } from
2
2
  import { requireComputeAuth } from "../lib/auth/guard.js";
3
3
  import { formatScopeLabel, parseKeyValuePositional, resolveEnvScope } from "../lib/app/env-config.js";
4
4
  import { readEnvFileAssignments } from "../lib/app/env-file.js";
5
- import { resolveProjectTarget } from "../lib/project/resolution.js";
5
+ import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
6
6
  import { readLocalGitBranch } from "../lib/git/local-branch.js";
7
7
  import { requireAuthenticatedAuthState } from "./auth.js";
8
8
  import { listRealWorkspaceProjects } from "./project.js";
@@ -205,13 +205,15 @@ async function requireClientAndProject(context, explicitProject, commandName) {
205
205
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
206
206
  if (!client) throw authRequiredError(["prisma-cli auth login"]);
207
207
  if (!authState.workspace) throw workspaceRequiredError();
208
- const target = await resolveProjectTarget({
208
+ const targetResult = await resolveProjectTarget({
209
209
  context,
210
210
  workspace: authState.workspace,
211
211
  explicitProject,
212
212
  listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
213
213
  commandName
214
214
  });
215
+ if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
216
+ const target = targetResult.value;
215
217
  return {
216
218
  client,
217
219
  projectId: target.project.id,
@@ -13,8 +13,8 @@ import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-pro
13
13
  import { DEFAULT_LOCAL_DEV_PORT, resolveLocalBuildType, runLocalApp } from "../lib/app/local-dev.js";
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
- import { buildProjectSetupNextActions, inferTargetName, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
17
- import { bindProjectToDirectory, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
16
+ import { buildProjectSetupNextActions, inferTargetName, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.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,
@@ -1356,7 +1358,7 @@ async function requireProviderAndDeployProjectContext(context, explicitProject,
1356
1358
  async function resolveProjectContext(context, client, explicitProject, options) {
1357
1359
  const authState = await requireAuthenticatedAuthState(context);
1358
1360
  if (!authState.workspace) throw workspaceRequiredError();
1359
- const resolved = await resolveProjectTarget({
1361
+ const resolvedResult = await resolveProjectTarget({
1360
1362
  context,
1361
1363
  workspace: authState.workspace,
1362
1364
  explicitProject,
@@ -1364,6 +1366,8 @@ async function resolveProjectContext(context, client, explicitProject, options)
1364
1366
  listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
1365
1367
  commandName: options?.commandName
1366
1368
  });
1369
+ if (resolvedResult.isErr()) throw projectResolutionErrorToCliError(resolvedResult.error);
1370
+ const resolved = resolvedResult.value;
1367
1371
  const branch = options?.branch ?? await resolveDeployBranch(context, void 0);
1368
1372
  return {
1369
1373
  ...resolved,
@@ -1,8 +1,7 @@
1
1
  import { CliError, authRequiredError, workspaceRequiredError } from "../shell/errors.js";
2
2
  import { requireComputeAuth } from "../lib/auth/guard.js";
3
- import { resolveProjectTarget } from "../lib/project/resolution.js";
3
+ import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
4
4
  import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
5
- import { createSelectPromptPort } from "./select-prompt-port.js";
6
5
  import { requireAuthenticatedAuthState } from "./auth.js";
7
6
  import { listRealWorkspaceProjects } from "./project.js";
8
7
  import { createBranchUseCases } from "../use-cases/branch.js";
@@ -30,13 +29,13 @@ async function listRealBranches(context) {
30
29
  if (!client) throw authRequiredError(["prisma-cli auth login"]);
31
30
  const workspace = authState.workspace;
32
31
  if (!workspace) throw workspaceRequiredError();
33
- const target = await resolveProjectTarget({
32
+ const targetResult = await resolveProjectTarget({
34
33
  context,
35
34
  workspace,
36
- listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
37
- prompt: createSelectPromptPort(context),
38
- remember: true
35
+ listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal)
39
36
  });
37
+ if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
38
+ const target = targetResult.value;
40
39
  const branches = await listBranches(client, target.project.id, context.runtime.signal);
41
40
  return {
42
41
  projectId: target.project.id,
@@ -1,6 +1,6 @@
1
1
  import { CliError, authRequiredError, usageError, workspaceRequiredError } from "../shell/errors.js";
2
2
  import { requireComputeAuth } from "../lib/auth/guard.js";
3
- import { resolveProjectTarget } from "../lib/project/resolution.js";
3
+ import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
4
4
  import { requireAuthenticatedAuthState } from "./auth.js";
5
5
  import { listFixtureWorkspaceProjects, listRealWorkspaceProjects } from "./project.js";
6
6
  import { createManagementDatabaseProvider, normalizeConnection, normalizeDatabase } from "../lib/database/provider.js";
@@ -155,28 +155,30 @@ async function requireDatabaseContext(context, flags, commandName) {
155
155
  if (isRealMode(context)) {
156
156
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
157
157
  if (!client) throw authRequiredError();
158
- const target = await resolveProjectTarget({
158
+ const targetResult = await resolveProjectTarget({
159
159
  context,
160
160
  workspace,
161
161
  explicitProject: flags.projectRef,
162
162
  listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
163
163
  commandName
164
164
  });
165
+ if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
165
166
  return {
166
167
  provider: createManagementDatabaseProvider(client),
167
- target
168
+ target: targetResult.value
168
169
  };
169
170
  }
170
- const target = await resolveProjectTarget({
171
+ const targetResult = await resolveProjectTarget({
171
172
  context,
172
173
  workspace,
173
174
  explicitProject: flags.projectRef,
174
175
  listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
175
176
  commandName
176
177
  });
178
+ if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
177
179
  return {
178
180
  provider: createFixtureDatabaseProvider(context),
179
- target
181
+ target: targetResult.value
180
182
  };
181
183
  }
182
184
  async function requireDatabaseProviderOnly(context) {
@@ -4,8 +4,8 @@ import { canPrompt } from "../shell/runtime.js";
4
4
  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
- import { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
8
- import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
7
+ import { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, projectResolutionErrorToCliError, resolveProjectTarget, sortProjects } from "../lib/project/resolution.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({
@@ -323,42 +330,50 @@ async function runGitDisconnect(context, options = {}) {
323
330
  async function resolveProjectShowInRealMode(context, workspace, explicitProject) {
324
331
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
325
332
  if (!client) throw authRequiredError();
326
- return inspectProjectBinding({
333
+ const result = await inspectProjectBinding({
327
334
  context,
328
335
  workspace,
329
336
  explicitProject,
330
337
  listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
331
338
  commandName: "project show"
332
339
  });
340
+ if (result.isErr()) throw projectResolutionErrorToCliError(result.error);
341
+ return result.value;
333
342
  }
334
343
  async function resolveRequiredProjectInRealMode(context, workspace, explicitProject, commandName) {
335
344
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
336
345
  if (!client) throw authRequiredError();
337
- return resolveProjectTarget({
346
+ const result = await resolveProjectTarget({
338
347
  context,
339
348
  workspace,
340
349
  explicitProject,
341
350
  listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
342
351
  commandName
343
352
  });
353
+ if (result.isErr()) throw projectResolutionErrorToCliError(result.error);
354
+ return result.value;
344
355
  }
345
356
  async function resolveProjectShowInFixtureMode(context, workspace, explicitProject) {
346
- return inspectProjectBinding({
357
+ const result = await inspectProjectBinding({
347
358
  context,
348
359
  workspace,
349
360
  explicitProject,
350
361
  listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
351
362
  commandName: "project show"
352
363
  });
364
+ if (result.isErr()) throw projectResolutionErrorToCliError(result.error);
365
+ return result.value;
353
366
  }
354
367
  async function resolveRequiredProjectInFixtureMode(context, workspace, explicitProject, commandName) {
355
- return resolveProjectTarget({
368
+ const result = await resolveProjectTarget({
356
369
  context,
357
370
  workspace,
358
371
  explicitProject,
359
372
  listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
360
373
  commandName
361
374
  });
375
+ if (result.isErr()) throw projectResolutionErrorToCliError(result.error);
376
+ return result.value;
362
377
  }
363
378
  async function listRealWorkspaceProjects(client, workspace, signal) {
364
379
  const { data } = await client.GET("/v1/projects", { signal });
@@ -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) {
@@ -3,45 +3,98 @@ import { formatCommandArgument } from "../../shell/command-arguments.js";
3
3
  import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "./local-pin.js";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import path from "node:path";
6
- import { matchError } from "better-result";
6
+ import { Result, TaggedError, matchError } from "better-result";
7
7
  //#region src/lib/project/resolution.ts
8
+ var ProjectNotFoundError = class extends TaggedError("ProjectNotFoundError")() {
9
+ constructor(projectRef, workspace) {
10
+ super({
11
+ message: `Project "${projectRef}" was not found in workspace "${workspace.name}".`,
12
+ projectRef,
13
+ workspace
14
+ });
15
+ }
16
+ };
17
+ var ProjectAmbiguousError = class extends TaggedError("ProjectAmbiguousError")() {
18
+ constructor(projectRef, matches) {
19
+ super({
20
+ message: projectRef ? `Multiple projects matched "${projectRef}".` : "Multiple projects matched the current directory context.",
21
+ projectRef,
22
+ matches
23
+ });
24
+ }
25
+ };
26
+ var LocalStateStaleError = class extends TaggedError("LocalStateStaleError")() {
27
+ constructor() {
28
+ super({
29
+ message: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
30
+ pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
31
+ });
32
+ }
33
+ };
34
+ var LocalProjectWorkspaceMismatchError = class extends TaggedError("LocalProjectWorkspaceMismatchError")() {
35
+ constructor(options) {
36
+ super({
37
+ message: `${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} links this directory to project ${options.pinnedProjectId} in workspace ${options.pinnedWorkspaceId}, but the active workspace is "${options.activeWorkspace.name}" (${options.activeWorkspace.id}).`,
38
+ pinnedWorkspaceId: options.pinnedWorkspaceId,
39
+ pinnedProjectId: options.pinnedProjectId,
40
+ activeWorkspace: options.activeWorkspace
41
+ });
42
+ }
43
+ };
44
+ var ProjectSetupRequiredError = class extends TaggedError("ProjectSetupRequiredError")() {
45
+ constructor(options) {
46
+ const commandLabel = options.commandName ? `prisma-cli ${options.commandName}` : "this command";
47
+ super({
48
+ message: `This directory is not linked to a Prisma Project, and ${commandLabel} will not choose one from package or directory names.`,
49
+ commandName: options.commandName,
50
+ suggestion: options.suggestion
51
+ });
52
+ }
53
+ };
8
54
  async function resolveProjectTarget(options) {
9
- const localPin = await readImplicitLocalPin(options, { allowEnvProjectId: true });
10
- const projects = await options.listProjects();
11
- const target = await resolveBoundProjectTarget(options, projects, {
12
- allowEnvProjectId: true,
13
- localPin
14
- });
15
- if (target) return target;
16
- throw await projectSetupRequiredError({
17
- cwd: options.context.runtime.cwd,
18
- projects,
19
- commandName: options.commandName,
20
- signal: options.context.runtime.signal
55
+ return Result.gen(async function* () {
56
+ const localPin = yield* Result.await(readImplicitLocalPin(options, { allowEnvProjectId: true }));
57
+ const projects = await options.listProjects();
58
+ const target = yield* Result.await(resolveBoundProjectTarget(options, projects, {
59
+ allowEnvProjectId: true,
60
+ localPin
61
+ }));
62
+ if (target) return Result.ok(target);
63
+ return Result.err(await projectSetupRequiredError({
64
+ cwd: options.context.runtime.cwd,
65
+ projects,
66
+ commandName: options.commandName,
67
+ signal: options.context.runtime.signal
68
+ }));
21
69
  });
22
70
  }
23
71
  async function inspectProjectBinding(options) {
24
- const localPin = await readImplicitLocalPin(options, { allowEnvProjectId: false });
25
- const projects = await options.listProjects();
26
- const target = await resolveBoundProjectTarget(options, projects, {
27
- allowEnvProjectId: false,
28
- localPin
72
+ return Result.gen(async function* () {
73
+ const localPin = yield* Result.await(readImplicitLocalPin(options, { allowEnvProjectId: false }));
74
+ const projects = await options.listProjects();
75
+ const target = yield* Result.await(resolveBoundProjectTarget(options, projects, {
76
+ allowEnvProjectId: false,
77
+ localPin
78
+ }));
79
+ if (target) return Result.ok(target);
80
+ return Result.ok({
81
+ workspace: options.workspace,
82
+ project: null,
83
+ localBinding: { status: "not-linked" },
84
+ resolution: { projectSource: "unbound" },
85
+ ...await buildProjectSetupSuggestion({
86
+ cwd: options.context.runtime.cwd,
87
+ projects,
88
+ commandName: options.commandName ?? "project show",
89
+ signal: options.context.runtime.signal
90
+ })
91
+ });
29
92
  });
30
- if (target) return target;
31
- return {
32
- workspace: options.workspace,
33
- project: null,
34
- localBinding: { status: "not-linked" },
35
- resolution: { projectSource: "unbound" },
36
- ...await buildProjectSetupSuggestion({
37
- cwd: options.context.runtime.cwd,
38
- projects,
39
- commandName: options.commandName ?? "project show",
40
- signal: options.context.runtime.signal
41
- })
42
- };
43
93
  }
44
94
  function projectNotFoundError(projectRef, workspace) {
95
+ return projectResolutionErrorToCliError(new ProjectNotFoundError(projectRef, workspace));
96
+ }
97
+ function projectNotFoundCliError(projectRef, workspace) {
45
98
  return new CliError({
46
99
  code: "PROJECT_NOT_FOUND",
47
100
  domain: "project",
@@ -53,6 +106,9 @@ function projectNotFoundError(projectRef, workspace) {
53
106
  });
54
107
  }
55
108
  function projectAmbiguousError(projectRef, matches) {
109
+ return projectResolutionErrorToCliError(new ProjectAmbiguousError(projectRef, matches));
110
+ }
111
+ function projectAmbiguousCliError(projectRef, matches) {
56
112
  const firstMatch = matches[0];
57
113
  const nextSteps = ["prisma-cli project list"];
58
114
  if (firstMatch) nextSteps.push(`prisma-cli app deploy --project ${firstMatch.id}`);
@@ -70,7 +126,7 @@ function projectAmbiguousError(projectRef, matches) {
70
126
  nextSteps
71
127
  });
72
128
  }
73
- function localStateStaleError() {
129
+ function localStateStaleCliError() {
74
130
  return new CliError({
75
131
  code: "LOCAL_STATE_STALE",
76
132
  domain: "project",
@@ -82,7 +138,7 @@ function localStateStaleError() {
82
138
  nextSteps: ["prisma-cli project list", "prisma-cli project link <id-or-name>"]
83
139
  });
84
140
  }
85
- function localProjectWorkspaceMismatchError(options) {
141
+ function localProjectWorkspaceMismatchCliError(options) {
86
142
  return new CliError({
87
143
  code: "LOCAL_PROJECT_WORKSPACE_MISMATCH",
88
144
  domain: "project",
@@ -104,6 +160,31 @@ function localProjectWorkspaceMismatchError(options) {
104
160
  ]
105
161
  });
106
162
  }
163
+ /**
164
+ * Converts expected project-resolution variants to command-boundary CliErrors.
165
+ * `LocalResolutionPinReadAbortedError` and `UnhandledException` intentionally
166
+ * propagate as exceptions; callers such as `resolveProjectShowInRealMode`
167
+ * throw this helper's result, so passthrough variants should keep bubbling.
168
+ */
169
+ function projectResolutionErrorToCliError(error) {
170
+ return matchError(error, {
171
+ ProjectNotFoundError: (error) => projectNotFoundCliError(error.projectRef, error.workspace),
172
+ ProjectAmbiguousError: (error) => projectAmbiguousCliError(error.projectRef, error.matches),
173
+ ProjectSetupRequiredError: (error) => projectSetupRequiredCliError(error),
174
+ LocalStateStaleError: () => localStateStaleCliError(),
175
+ LocalProjectWorkspaceMismatchError: (error) => localProjectWorkspaceMismatchCliError({
176
+ pinnedWorkspaceId: error.pinnedWorkspaceId,
177
+ pinnedProjectId: error.pinnedProjectId,
178
+ activeWorkspace: error.activeWorkspace
179
+ }),
180
+ LocalResolutionPinReadAbortedError: (error) => {
181
+ throw error;
182
+ },
183
+ UnhandledException: (error) => {
184
+ throw error;
185
+ }
186
+ });
187
+ }
107
188
  async function buildProjectSetupSuggestion(options) {
108
189
  const suggestedName = await inferTargetName(options.cwd, options.signal);
109
190
  const candidates = sortProjects(options.projects.filter((project) => projectMatchesSuggestedName(project, suggestedName.name))).map(toProjectSummary);
@@ -116,17 +197,24 @@ async function buildProjectSetupSuggestion(options) {
116
197
  }
117
198
  async function projectSetupRequiredError(options) {
118
199
  const suggestion = await buildProjectSetupSuggestion(options);
200
+ return new ProjectSetupRequiredError({
201
+ commandName: options.commandName,
202
+ suggestion
203
+ });
204
+ }
205
+ function projectSetupRequiredCliError(error) {
206
+ const suggestion = error.suggestion;
119
207
  return new CliError({
120
208
  code: "PROJECT_SETUP_REQUIRED",
121
209
  domain: "project",
122
210
  summary: "Choose a Project before running this command",
123
- why: `This directory is not linked to a Prisma Project, and ${options.commandName ? `prisma-cli ${options.commandName}` : "this command"} will not choose one from package or directory names.`,
211
+ why: error.message,
124
212
  fix: "Link the directory to an existing Project, or pass --project <id-or-name> for this command.",
125
213
  meta: { ...suggestion },
126
214
  exitCode: 1,
127
215
  nextSteps: ["prisma-cli project list", ...suggestion.recoveryCommands],
128
216
  nextActions: buildProjectSetupNextActions({
129
- commandName: options.commandName,
217
+ commandName: error.commandName,
130
218
  suggestedProjectName: suggestion.suggestedProjectName
131
219
  })
132
220
  });
@@ -200,9 +288,9 @@ function sortProjects(projects) {
200
288
  }
201
289
  function resolveExplicitProject(projectRef, projects, workspace) {
202
290
  const matches = projects.filter((project) => project.id === projectRef || project.name === projectRef);
203
- if (matches.length === 1) return matches[0];
204
- if (matches.length > 1) throw projectAmbiguousError(projectRef, matches);
205
- throw projectNotFoundError(projectRef, workspace);
291
+ if (matches.length === 1) return Result.ok(matches[0]);
292
+ if (matches.length > 1) return Result.err(new ProjectAmbiguousError(projectRef, matches));
293
+ return Result.err(new ProjectNotFoundError(projectRef, workspace));
206
294
  }
207
295
  function projectMatchesSuggestedName(project, suggestedName) {
208
296
  return project.id === suggestedName || project.name === suggestedName || project.slug === suggestedName;
@@ -211,62 +299,62 @@ async function resolveDurablePlatformMapping() {
211
299
  return null;
212
300
  }
213
301
  async function resolveBoundProjectTarget(options, projects, settings) {
214
- if (options.explicitProject) return resolvedTarget(options.workspace, resolveExplicitProject(options.explicitProject, projects, options.workspace), "explicit", {
215
- targetName: options.explicitProject,
216
- targetNameSource: "explicit"
217
- });
302
+ if (options.explicitProject) {
303
+ const projectResult = resolveExplicitProject(options.explicitProject, projects, options.workspace);
304
+ if (projectResult.isErr()) return Result.err(projectResult.error);
305
+ return Result.ok(resolvedTarget(options.workspace, projectResult.value, "explicit", {
306
+ targetName: options.explicitProject,
307
+ targetNameSource: "explicit"
308
+ }));
309
+ }
218
310
  if (settings.allowEnvProjectId && options.envProjectId) {
219
311
  const project = projects.find((candidate) => candidate.id === options.envProjectId);
220
- if (!project) throw projectNotFoundError(options.envProjectId, options.workspace);
221
- return resolvedTarget(options.workspace, project, "env", {
312
+ if (!project) return Result.err(new ProjectNotFoundError(options.envProjectId, options.workspace));
313
+ return Result.ok(resolvedTarget(options.workspace, project, "env", {
222
314
  targetName: options.envProjectId,
223
315
  targetNameSource: "env"
224
- });
316
+ }));
225
317
  }
226
318
  const localPin = settings.localPin;
227
- if (!localPin) return null;
319
+ if (!localPin) return Result.ok(null);
228
320
  if (localPin.kind === "present") {
229
- if (localPin.pin.workspaceId !== options.workspace.id) throw localProjectWorkspaceMismatchError({
321
+ if (localPin.pin.workspaceId !== options.workspace.id) return Result.err(new LocalProjectWorkspaceMismatchError({
230
322
  pinnedWorkspaceId: localPin.pin.workspaceId,
231
323
  pinnedProjectId: localPin.pin.projectId,
232
324
  activeWorkspace: options.workspace
233
- });
325
+ }));
234
326
  const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
235
- if (!project) throw localStateStaleError();
236
- return resolvedTarget(options.workspace, project, "local-pin", {
327
+ if (!project) return Result.err(new LocalStateStaleError());
328
+ return Result.ok(resolvedTarget(options.workspace, project, "local-pin", {
237
329
  targetName: project.name,
238
330
  targetNameSource: "local-pin"
239
- });
331
+ }));
240
332
  }
241
333
  const platformMapping = await resolveDurablePlatformMapping();
242
- if (platformMapping && platformMapping.workspace.id === options.workspace.id) return resolvedTarget(options.workspace, platformMapping, "platform-mapping", {
334
+ if (platformMapping && platformMapping.workspace.id === options.workspace.id) return Result.ok(resolvedTarget(options.workspace, platformMapping, "platform-mapping", {
243
335
  targetName: platformMapping.name,
244
336
  targetNameSource: "platform-mapping"
245
- });
246
- return null;
337
+ }));
338
+ return Result.ok(null);
247
339
  }
248
340
  async function readImplicitLocalPin(options, settings) {
249
- if (options.explicitProject || settings.allowEnvProjectId && options.envProjectId) return null;
341
+ if (options.explicitProject || settings.allowEnvProjectId && options.envProjectId) return Result.ok(null);
250
342
  const localPinResult = await readLocalResolutionPin(options.context.runtime.cwd, options.context.runtime.signal);
251
- if (localPinResult.isErr()) throw localPinReadErrorToProjectError(localPinResult.error);
343
+ if (localPinResult.isErr()) return Result.err(localPinReadErrorToProjectError(localPinResult.error));
252
344
  const localPin = localPinResult.value;
253
- if (localPin.kind === "present" && localPin.pin.workspaceId !== options.workspace.id) throw localProjectWorkspaceMismatchError({
345
+ if (localPin.kind === "present" && localPin.pin.workspaceId !== options.workspace.id) return Result.err(new LocalProjectWorkspaceMismatchError({
254
346
  pinnedWorkspaceId: localPin.pin.workspaceId,
255
347
  pinnedProjectId: localPin.pin.projectId,
256
348
  activeWorkspace: options.workspace
257
- });
258
- return localPin;
349
+ }));
350
+ return Result.ok(localPin);
259
351
  }
260
352
  function localPinReadErrorToProjectError(error) {
261
353
  return matchError(error, {
262
- LocalResolutionPinInvalidJsonError: () => localStateStaleError(),
263
- LocalResolutionPinInvalidShapeError: () => localStateStaleError(),
264
- LocalResolutionPinReadAbortedError: (error) => {
265
- throw error;
266
- },
267
- UnhandledException: (error) => {
268
- throw error;
269
- }
354
+ LocalResolutionPinInvalidJsonError: () => new LocalStateStaleError(),
355
+ LocalResolutionPinInvalidShapeError: () => new LocalStateStaleError(),
356
+ LocalResolutionPinReadAbortedError: (error) => error,
357
+ UnhandledException: (error) => error
270
358
  });
271
359
  }
272
360
  function resolvedTarget(workspace, project, projectSource, resolutionDetails) {
@@ -292,4 +380,4 @@ function toProjectSummary(project) {
292
380
  };
293
381
  }
294
382
  //#endregion
295
- export { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, projectAmbiguousError, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects };
383
+ export { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, projectAmbiguousError, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects };
@@ -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.78.1",
4
4
  "description": "Command-line interface for the Prisma Developer Platform.",
5
5
  "type": "module",
6
6
  "bin": {