Skip to content

Commit ee7c648

Browse files
authored
Merge pull request #80 from prisma/better-result-project-resolution-errors
Adopt typed result errors for project resolution
2 parents 872f7c4 + ad22e9f commit ee7c648

9 files changed

Lines changed: 307 additions & 106 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Better Result Phase 3
2+
3+
Scope tangent worth pursuing later: Package typecheck now runs after installing dependencies, but it reports unrelated existing errors in `branch-database.ts`, test helper typings, project real-mode mock types, and script module declarations. I did not fix these because phase 3 is limited to project resolution domain errors.

.agents/projects/better-result-error-handling.plan.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ None.
6969

7070
### Phase 3: Project Resolution Domain Errors
7171

72-
**Status:** ☐ Not started
72+
**Status:** ✓ Complete; full package typecheck blocked by unrelated existing errors
7373

7474
**Goal:** Convert project target resolution failures from thrown `CliError` helpers to typed project-domain failures.
7575

@@ -87,10 +87,10 @@ None.
8787

8888
**Acceptance Criteria:**
8989

90-
- Migrated project resolution APIs expose typed results rather than throwing expected `CliError` instances.
91-
- Existing project-related structured error codes and recovery guidance remain stable.
92-
- `pnpm --filter @prisma/cli exec tsc -p tsconfig.json` passes.
93-
- Project resolution and project show/list tests pass.
90+
- [x] Migrated project resolution APIs expose typed results rather than throwing expected `CliError` instances.
91+
- [x] Existing project-related structured error codes and recovery guidance remain stable.
92+
- [ ] `pnpm --filter @prisma/cli exec tsc -p tsconfig.json` passes. Blocked by unrelated existing errors in database/test typing surfaces.
93+
- [x] Project resolution and project show/list tests pass.
9494

9595
### Phase 4: Project Setup Validation And Creation Errors
9696

packages/cli/src/controllers/app-env.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { readLocalGitBranch } from "../lib/git/local-branch";
1313
import { authRequiredError, CliError, usageError, workspaceRequiredError } from "../shell/errors";
1414
import type { CommandSuccess } from "../shell/output";
1515
import type { CommandContext } from "../shell/runtime";
16-
import { resolveProjectTarget } from "../lib/project/resolution";
16+
import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution";
1717
import type {
1818
EnvAddResult,
1919
EnvListTarget,
@@ -416,13 +416,17 @@ async function requireClientAndProject(
416416
throw workspaceRequiredError();
417417
}
418418

419-
const target = await resolveProjectTarget({
419+
const targetResult = await resolveProjectTarget({
420420
context,
421421
workspace: authState.workspace,
422422
explicitProject,
423423
listProjects: () => listRealWorkspaceProjects(client, authState.workspace!, context.runtime.signal),
424424
commandName,
425425
});
426+
if (targetResult.isErr()) {
427+
throw projectResolutionErrorToCliError(targetResult.error);
428+
}
429+
const target = targetResult.value;
426430

427431
return {
428432
client,

packages/cli/src/controllers/app.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import {
5252
buildProjectSetupNextActions,
5353
inferTargetName,
5454
projectNotFoundError,
55+
projectResolutionErrorToCliError,
5556
resolveDurablePlatformMapping,
5657
resolveProjectTarget,
5758
type InferredTargetName,
@@ -2330,14 +2331,18 @@ async function resolveProjectContext(
23302331
throw workspaceRequiredError();
23312332
}
23322333

2333-
const resolved = await resolveProjectTarget({
2334+
const resolvedResult = await resolveProjectTarget({
23342335
context,
23352336
workspace: authState.workspace,
23362337
explicitProject,
23372338
envProjectId: options?.envProjectId,
23382339
listProjects: () => listRealWorkspaceProjects(client, authState.workspace!, context.runtime.signal),
23392340
commandName: options?.commandName,
23402341
});
2342+
if (resolvedResult.isErr()) {
2343+
throw projectResolutionErrorToCliError(resolvedResult.error);
2344+
}
2345+
const resolved = resolvedResult.value;
23412346
const branch = options?.branch ?? await resolveDeployBranch(context, undefined);
23422347

23432348
return {

packages/cli/src/controllers/branch.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ import type { BranchListResult, BranchRole, BranchSummary } from "../types/branc
77
import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways";
88
import { createBranchUseCases } from "../use-cases/branch";
99
import { requireComputeAuth } from "../lib/auth/guard";
10-
import { resolveProjectTarget } from "../lib/project/resolution";
10+
import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution";
1111
import { requireAuthenticatedAuthState } from "./auth";
1212
import { listRealWorkspaceProjects } from "./project";
13-
import { createSelectPromptPort } from "./select-prompt-port";
1413

1514
function isRealMode(context: CommandContext): boolean {
1615
return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
@@ -55,13 +54,15 @@ async function listRealBranches(context: CommandContext): Promise<BranchListResu
5554
throw workspaceRequiredError();
5655
}
5756

58-
const target = await resolveProjectTarget({
57+
const targetResult = await resolveProjectTarget({
5958
context,
6059
workspace,
6160
listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
62-
prompt: createSelectPromptPort(context),
63-
remember: true,
6461
});
62+
if (targetResult.isErr()) {
63+
throw projectResolutionErrorToCliError(targetResult.error);
64+
}
65+
const target = targetResult.value;
6566

6667
const branches = await listBranches(client, target.project.id, context.runtime.signal);
6768

packages/cli/src/controllers/database.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
normalizeDatabase,
88
type DatabaseProvider,
99
} from "../lib/database/provider";
10-
import { resolveProjectTarget, type ResolvedProjectTarget } from "../lib/project/resolution";
10+
import { projectResolutionErrorToCliError, resolveProjectTarget, type ResolvedProjectTarget } from "../lib/project/resolution";
1111
import { authRequiredError, CliError, usageError, workspaceRequiredError } from "../shell/errors";
1212
import type { CommandSuccess } from "../shell/output";
1313
import type { CommandContext } from "../shell/runtime";
@@ -277,31 +277,37 @@ async function requireDatabaseContext(
277277
throw authRequiredError();
278278
}
279279

280-
const target = await resolveProjectTarget({
280+
const targetResult = await resolveProjectTarget({
281281
context,
282282
workspace,
283283
explicitProject: flags.projectRef,
284284
listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
285285
commandName,
286286
});
287+
if (targetResult.isErr()) {
288+
throw projectResolutionErrorToCliError(targetResult.error);
289+
}
287290

288291
return {
289292
provider: createManagementDatabaseProvider(client),
290-
target,
293+
target: targetResult.value,
291294
};
292295
}
293296

294-
const target = await resolveProjectTarget({
297+
const targetResult = await resolveProjectTarget({
295298
context,
296299
workspace,
297300
explicitProject: flags.projectRef,
298301
listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
299302
commandName,
300303
});
304+
if (targetResult.isErr()) {
305+
throw projectResolutionErrorToCliError(targetResult.error);
306+
}
301307

302308
return {
303309
provider: createFixtureDatabaseProvider(context),
304-
target,
310+
target: targetResult.value,
305311
};
306312
}
307313

packages/cli/src/controllers/project.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
buildProjectSetupNextActions,
1313
inferTargetName,
1414
inspectProjectBinding,
15+
projectResolutionErrorToCliError,
1516
resolveProjectTarget,
1617
sortProjects,
1718
type ProjectCandidate,
@@ -563,13 +564,17 @@ async function resolveProjectShowInRealMode(
563564
throw authRequiredError();
564565
}
565566

566-
return inspectProjectBinding({
567+
const result = await inspectProjectBinding({
567568
context,
568569
workspace,
569570
explicitProject,
570571
listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
571572
commandName: "project show",
572573
});
574+
if (result.isErr()) {
575+
throw projectResolutionErrorToCliError(result.error);
576+
}
577+
return result.value;
573578
}
574579

575580
async function resolveRequiredProjectInRealMode(
@@ -583,27 +588,35 @@ async function resolveRequiredProjectInRealMode(
583588
throw authRequiredError();
584589
}
585590

586-
return resolveProjectTarget({
591+
const result = await resolveProjectTarget({
587592
context,
588593
workspace,
589594
explicitProject,
590595
listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
591596
commandName,
592597
});
598+
if (result.isErr()) {
599+
throw projectResolutionErrorToCliError(result.error);
600+
}
601+
return result.value;
593602
}
594603

595604
async function resolveProjectShowInFixtureMode(
596605
context: CommandContext,
597606
workspace: AuthWorkspace,
598607
explicitProject: string | undefined,
599608
): Promise<ProjectShowResult> {
600-
return inspectProjectBinding({
609+
const result = await inspectProjectBinding({
601610
context,
602611
workspace,
603612
explicitProject,
604613
listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
605614
commandName: "project show",
606615
});
616+
if (result.isErr()) {
617+
throw projectResolutionErrorToCliError(result.error);
618+
}
619+
return result.value;
607620
}
608621

609622
async function resolveRequiredProjectInFixtureMode(
@@ -612,13 +625,17 @@ async function resolveRequiredProjectInFixtureMode(
612625
explicitProject: string | undefined,
613626
commandName: string,
614627
): Promise<ResolvedProjectTarget> {
615-
return resolveProjectTarget({
628+
const result = await resolveProjectTarget({
616629
context,
617630
workspace,
618631
explicitProject,
619632
listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
620633
commandName,
621634
});
635+
if (result.isErr()) {
636+
throw projectResolutionErrorToCliError(result.error);
637+
}
638+
return result.value;
622639
}
623640

624641
export async function listRealWorkspaceProjects(

0 commit comments

Comments
 (0)