From a2ab999ff0c4eadb41f5afba4b6313c284a988ee Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 26 Aug 2026 09:17:32 +0200 Subject: [PATCH 1/4] fix(cli): service commands raise PROJECT.NOT_FOUND for a missing project SERVICE.PROJECT_NOT_FOUND and PROJECT.NOT_FOUND described the same user-facing condition, and which code you got depended on the command you entered through. The service raise site keeps its own message and next actions (project show, project link) but now carries the project group's code, and the registry folds the services-API case into the PROJECT.NOT_FOUND entry. Co-Authored-By: Claude Fable 5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/reference/error-reference.md | 6 +----- packages/cli/src/commands/service/errors.ts | 18 +++++++----------- ...ntext.test.ts => operation-context.test.ts} | 0 packages/cli/tests/service-list.test.ts | 4 ++-- 4 files changed, 10 insertions(+), 18 deletions(-) rename packages/cli/tests/{legacy-context.test.ts => operation-context.test.ts} (100%) diff --git a/docs/reference/error-reference.md b/docs/reference/error-reference.md index 5ef29bbf..eb67b10b 100644 --- a/docs/reference/error-reference.md +++ b/docs/reference/error-reference.md @@ -384,7 +384,7 @@ The local project binding in `.prisma/local.json` is unusable: the pinned projec ### PROJECT.NOT_FOUND -An explicit project reference matched no project in the active workspace, either because it does not exist or because the credential cannot see it — raised during project resolution for any command that accepts one, including `branch list` and the `project link`/`transfer`/`delete` target lookup. The fix is to pass an id or name from `prisma project list`. Meta: none. +An explicit project reference matched no project in the active workspace, either because it does not exist or because the credential cannot see it — raised during project resolution for any command that accepts one, including `branch list` and the `project link`/`transfer`/`delete` target lookup. The fix is to pass an id or name from `prisma project list`. Service commands raise the same code one step later, when the services API answers "Resource Not Found" for a project that did resolve — the directory binding points at a project that no longer exists or is no longer accessible — and their next actions point at `project show` to inspect the binding and `project link` to fix it. Meta: none. ### PROJECT.RENAME_FAILED @@ -496,10 +496,6 @@ A `service logs` response body ended without the terminal record that closes a p The resolved service has no usable version for the command — raised by `service open` when the service has no versions, by `service logs` when it has no live version, and by `service domain add` when the API answers 422 because the production service has no promoted version that can receive a custom domain. The fix on the domain path is to promote a version on the production branch first, then add the domain again. Meta: `status`, `apiCode`, `hint` (domain-add path only; otherwise none). -### SERVICE.PROJECT_NOT_FOUND - -The project a service command resolved to does not exist in the authenticated workspace or is no longer accessible — raised when listing services answers "Resource Not Found" for the resolved project id. A service command that cannot match an explicit `--project` reference fails with the project group's own `PROJECT.NOT_FOUND` instead, because the condition is the same one whichever command met it. Next actions point at `project show` to inspect the directory binding and `project link` to fix it. Meta: none. - ### SERVICE.SELECTION_INVALID The named service could not be found among the resolved project branch's services — the match tries the stable platform id first, then the name. The fix is to pass the id or name of an existing service; the suggested command is `service list`, deliberately not `service version list`, which itself has to resolve a service and would fail the same way. Meta: none. diff --git a/packages/cli/src/commands/service/errors.ts b/packages/cli/src/commands/service/errors.ts index 48d6b671..9be0f355 100644 --- a/packages/cli/src/commands/service/errors.ts +++ b/packages/cli/src/commands/service/errors.ts @@ -70,17 +70,13 @@ export function serviceNameRequiredError(): CliStructuredError { } export function projectNotFoundError(projectId: string): CliStructuredError { - return new CliStructuredError( - "SERVICE.PROJECT_NOT_FOUND", - "Project not found", - { - why: `The resolved project "${projectId}" does not exist in the authenticated workspace or is no longer accessible.`, - nextActions: [ - runCommandAction("Inspect the directory binding", "project show"), - runCommandAction("Link a project", "project link "), - ], - }, - ); + return new CliStructuredError("PROJECT.NOT_FOUND", "Project not found", { + why: `The resolved project "${projectId}" does not exist in the authenticated workspace or is no longer accessible.`, + nextActions: [ + runCommandAction("Inspect the directory binding", "project show"), + runCommandAction("Link a project", "project link "), + ], + }); } export function deployFailedError( diff --git a/packages/cli/tests/legacy-context.test.ts b/packages/cli/tests/operation-context.test.ts similarity index 100% rename from packages/cli/tests/legacy-context.test.ts rename to packages/cli/tests/operation-context.test.ts diff --git a/packages/cli/tests/service-list.test.ts b/packages/cli/tests/service-list.test.ts index 6c8213f0..990d324e 100644 --- a/packages/cli/tests/service-list.test.ts +++ b/packages/cli/tests/service-list.test.ts @@ -174,7 +174,7 @@ describe("prisma service list", () => { }); }); - it("settles an unknown project as SERVICE.PROJECT_NOT_FOUND with exit 2", async () => { + it("settles an unknown project as PROJECT.NOT_FOUND with exit 2", async () => { const harness = await makeServiceCli({ routes: readFlowRoutes({ "GET /v1/apps": () => ({ @@ -194,7 +194,7 @@ describe("prisma service list", () => { if (frame?.kind !== "result" || frame.envelope.ok) { throw new Error("expected an errored envelope"); } - expect(frame.envelope.error.code).toBe("SERVICE.PROJECT_NOT_FOUND"); + expect(frame.envelope.error.code).toBe("PROJECT.NOT_FOUND"); }); it("fails early with the engine sign-in error when unauthenticated", async () => { From 9d5e223e6fc04b5ce6488c89b7c0ba7716d718e2 Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 26 Aug 2026 09:17:32 +0200 Subject: [PATCH 2/4] docs: sync the style guide's global-flag list with the engine The list still described the MVP plan: --trace never shipped, and --format, --log-level, --confirm, and --config were missing. The list now mirrors SHARED_FLAG_PARAMETERS in packages/cli-engine/src/execution/shared-flags.ts and names that as the source of truth. Co-Authored-By: Claude Fable 5 Signed-off-by: willbot Signed-off-by: Will Madden --- docs/product/cli-style-guide.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/product/cli-style-guide.md b/docs/product/cli-style-guide.md index 32d173dd..51149b70 100644 --- a/docs/product/cli-style-guide.md +++ b/docs/product/cli-style-guide.md @@ -149,19 +149,20 @@ Shared flag rules: - short aliases exist only for high-frequency flags - flags should mean the same thing across commands whenever possible -Shared global flags for the MVP: - -- `--json` -- `-q`, `--quiet` -- `-v`, `--verbose` -- `--trace` -- `--interactive` -- `--no-interactive` -- `-y`, `--yes` -- `--color` -- `--no-color` - -`--quiet`, `--verbose`, and `--trace` affect human output detail, not the JSON schema. +Shared global flags, defined by the engine in `SHARED_FLAG_PARAMETERS` (`packages/cli-engine/src/execution/shared-flags.ts`, the source of truth for this list): + +- `--format ` +- `--json` (shorthand for `--format json`) +- `--log-level ` +- `-v`, `--verbose` (shorthand for `--log-level verbose`) +- `-q`, `--quiet` (shorthand for `--log-level error`) +- `-y`, `--yes` (accept prompt defaults) +- `--confirm ` (grant a consent prompt non-interactively; repeatable) +- `--interactive`, `--no-interactive` +- `--color`, `--no-color` +- `--config ` + +`--log-level` and its `--verbose`/`--quiet` shorthands affect human commentary detail, not the JSON schema. ## Interactivity From f9a52f03a5ccffe90489deacd06780c8fca774a5 Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 26 Aug 2026 09:17:44 +0200 Subject: [PATCH 3/4] ci: dispatch error-reference-updated to prisma/web on registry changes prisma/web's sync-error-reference-docs.yml has listened for this repository_dispatch since it was added, but nothing ever sent it, so the hosted page could lag a registry change by up to a day until the cron fired. A push to main touching docs/reference/error-reference.md now sends the event. Needs the WEB_DISPATCH_TOKEN secret: a token allowed to send repository dispatches to prisma/web. Co-Authored-By: Claude Fable 5 Signed-off-by: willbot Signed-off-by: Will Madden --- .../workflows/notify-error-reference-docs.yml | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/notify-error-reference-docs.yml diff --git a/.github/workflows/notify-error-reference-docs.yml b/.github/workflows/notify-error-reference-docs.yml new file mode 100644 index 00000000..e75795d2 --- /dev/null +++ b/.github/workflows/notify-error-reference-docs.yml @@ -0,0 +1,39 @@ +# Tells prisma/web to regenerate the hosted error reference +# (https://docs.prisma.io/docs/cli/error-reference) as soon as the +# registry changes, instead of waiting for that repo's daily cron. +# prisma/web's sync-error-reference-docs.yml listens for the +# `error-reference-updated` repository_dispatch and re-runs the +# generator against this repo's main. +# +# WEB_DISPATCH_TOKEN is a token allowed to send repository_dispatch +# events to prisma/web (fine-grained PAT on that repo with +# "Contents: read and write", or classic `repo` scope). +name: Notify Docs of Error Reference Changes + +on: + push: + branches: [main] + paths: + - docs/reference/error-reference.md + +permissions: {} + +jobs: + dispatch: + name: Dispatch error-reference-updated to prisma/web + runs-on: ubuntu-latest + steps: + - name: Send repository_dispatch + env: + GH_TOKEN: ${{ secrets.WEB_DISPATCH_TOKEN }} + run: | + gh api repos/prisma/web/dispatches \ + --input - <<'EOF' + { + "event_type": "error-reference-updated", + "client_payload": { + "repository": "${{ github.repository }}", + "sha": "${{ github.sha }}" + } + } + EOF From bfc129d7595bd24913465293d94a3ecbc7b049fe Mon Sep 17 00:00:00 2001 From: willbot Date: Wed, 26 Aug 2026 09:17:44 +0200 Subject: [PATCH 4/4] refactor(cli): retire the legacy folder src/legacy/ held the last types named after the deleted command shell. CliRuntime and CommandContext move to controllers/context.ts, CommandSuccess moves into its only user, controllers/app-env-file.ts, and the unused CliOutput is gone. The legacyOperationContext adapter is now operationContext, and its test file follows the rename. Co-Authored-By: Claude Fable 5 Signed-off-by: willbot Signed-off-by: Will Madden --- packages/cli/src/commands/project/context.ts | 29 +++++++------- packages/cli/src/commands/project/delete.ts | 4 +- packages/cli/src/commands/project/env-add.ts | 6 +-- .../cli/src/commands/project/env-update.ts | 6 +-- packages/cli/src/commands/project/show.ts | 4 +- packages/cli/src/commands/project/transfer.ts | 4 +- packages/cli/src/controllers/app-env-file.ts | 11 +++++- packages/cli/src/controllers/app-env.ts | 2 +- .../runtime.ts => controllers/context.ts} | 6 +-- packages/cli/src/controllers/project.ts | 2 +- packages/cli/src/legacy/output.ts | 14 ------- packages/cli/tests/helpers.ts | 2 +- packages/cli/tests/operation-context.test.ts | 39 +++++++++---------- 13 files changed, 62 insertions(+), 67 deletions(-) rename packages/cli/src/{legacy/runtime.ts => controllers/context.ts} (50%) delete mode 100644 packages/cli/src/legacy/output.ts diff --git a/packages/cli/src/commands/project/context.ts b/packages/cli/src/commands/project/context.ts index 12e2f509..8b387d3c 100644 --- a/packages/cli/src/commands/project/context.ts +++ b/packages/cli/src/commands/project/context.ts @@ -1,13 +1,13 @@ /** - * Glue between the engine command context and the legacy project - * operations. The legacy resolution and env-file operations take a - * shell `CommandContext` but read only `runtime.cwd`, `runtime.env` + * Glue between the engine command context and the project controllers. + * The resolution and env-file operations take a controller + * `CommandContext` but read only `runtime.cwd`, `runtime.env` * and `runtime.signal`, so the CLI hands them exactly that. */ import path from "node:path"; import type { CommandContext } from "@prisma/cli-engine"; +import type { CommandContext as ControllerCommandContext } from "../../controllers/context"; import { listRealWorkspaceProjects } from "../../controllers/project"; -import type { CommandContext as LegacyCommandContext } from "../../legacy/runtime"; import { ensureLocalResolutionPinGitignore, LOCAL_RESOLUTION_PIN_RELATIVE_PATH, @@ -26,10 +26,10 @@ import type { ProjectSetupResult, ProjectSummary } from "../../types/project"; export type ProjectCommandContext = CommandContext; /** - * The legacy shell's `CommandContext` has many more fields than the - * three the CLI supplies, and the cast that makes the adapter compile also - * hides the day a legacy edit starts reading a fourth. Left alone that - * surfaces as `Cannot read properties of undefined`, worst case inside + * The controllers' `CommandContext` is typed to the three fields the + * CLI supplies today, but a controller edit could start reading a + * fourth. Left alone that surfaces as + * `Cannot read properties of undefined`, worst case inside * `project transfer` after the project has already moved. Refusing the * read here names the missing field at the moment it is read instead. * Probes pass through rather than throwing: symbols are how the language @@ -46,21 +46,24 @@ function refuseUnknownReads(fields: T, prefix: string): T { return Reflect.get(target, key); } throw new Error( - "the legacy-context adapter provides only runtime.cwd, " + + "the operation-context adapter provides only runtime.cwd, " + `runtime.env and runtime.signal; ${prefix}${key} was read`, ); }, }); } -export function legacyOperationContext( +export function operationContext( ctx: ProjectCommandContext, -): LegacyCommandContext { +): ControllerCommandContext { const runtime = refuseUnknownReads( { cwd: ctx.cwd, env: ctx.env, signal: ctx.signal }, "runtime.", ); - return refuseUnknownReads({ runtime }, "") as unknown as LegacyCommandContext; + return refuseUnknownReads( + { runtime }, + "", + ) as unknown as ControllerCommandContext; } export function listWorkspaceProjects( @@ -80,7 +83,7 @@ export async function resolvePinnedProject( commandName: string | undefined, ): Promise { const target = await resolveProjectTarget({ - context: legacyOperationContext(ctx), + context: operationContext(ctx), workspace, explicitProject, listProjects: () => listWorkspaceProjects(ctx), diff --git a/packages/cli/src/commands/project/delete.ts b/packages/cli/src/commands/project/delete.ts index bc8c5bba..13fb2030 100644 --- a/packages/cli/src/commands/project/delete.ts +++ b/packages/cli/src/commands/project/delete.ts @@ -15,7 +15,7 @@ import { } from "../../lib/project/setup"; import type { ProjectDeleteResult } from "../../types/project"; import { resolveActiveWorkspace } from "../resources-shared/workspace"; -import { legacyOperationContext, listWorkspaceProjects } from "./context"; +import { listWorkspaceProjects, operationContext } from "./context"; import { localPinDiagnostics } from "./presentation"; const CONSENT_QUESTION = @@ -83,7 +83,7 @@ export const projectDeleteCommand = defineCommand({ const warnings: string[] = []; const cleared = await cleanupLocalPinForProject( - legacyOperationContext(ctx), + operationContext(ctx), project.id, { onError: (message) => warnings.push(message) }, ); diff --git a/packages/cli/src/commands/project/env-add.ts b/packages/cli/src/commands/project/env-add.ts index 7f65187e..11b05a4c 100644 --- a/packages/cli/src/commands/project/env-add.ts +++ b/packages/cli/src/commands/project/env-add.ts @@ -21,7 +21,7 @@ import { runEnvAddFile } from "../../controllers/app-env-file"; import { formatScopeLabel } from "../../lib/app/env-config"; import { runCommand, userChoice } from "../../lib/app/env-errors"; import type { EnvAddResult } from "../../types/app-env"; -import { legacyOperationContext } from "./context"; +import { operationContext } from "./context"; import { branchFlag, fileFlag, @@ -92,7 +92,7 @@ export const projectEnvAddCommand = defineCommand({ ); const scope = requireEnvScope(args.flags, "add"); const input = await resolveEnvWriteInput( - legacyOperationContext(ctx), + operationContext(ctx), source, "add", ); @@ -106,7 +106,7 @@ export const projectEnvAddCommand = defineCommand({ if (input.kind === "file") { const written = await runEnvAddFile( - legacyOperationContext(ctx), + operationContext(ctx), ctx.api, projectId, resolved, diff --git a/packages/cli/src/commands/project/env-update.ts b/packages/cli/src/commands/project/env-update.ts index 803936cf..03d62be2 100644 --- a/packages/cli/src/commands/project/env-update.ts +++ b/packages/cli/src/commands/project/env-update.ts @@ -21,7 +21,7 @@ import { runEnvUpdateFile } from "../../controllers/app-env-file"; import { formatScopeLabel } from "../../lib/app/env-config"; import { runCommand, userChoice } from "../../lib/app/env-errors"; import type { EnvUpdateResult } from "../../types/app-env"; -import { legacyOperationContext } from "./context"; +import { operationContext } from "./context"; import { branchFlag, fileFlag, @@ -89,7 +89,7 @@ export const projectEnvUpdateCommand = defineCommand({ ); const scope = requireEnvScope(args.flags, "update"); const input = await resolveEnvWriteInput( - legacyOperationContext(ctx), + operationContext(ctx), source, "update", ); @@ -103,7 +103,7 @@ export const projectEnvUpdateCommand = defineCommand({ if (input.kind === "file") { const written = await runEnvUpdateFile( - legacyOperationContext(ctx), + operationContext(ctx), ctx.api, projectId, resolved, diff --git a/packages/cli/src/commands/project/show.ts b/packages/cli/src/commands/project/show.ts index 08b02abf..a1025d07 100644 --- a/packages/cli/src/commands/project/show.ts +++ b/packages/cli/src/commands/project/show.ts @@ -13,7 +13,7 @@ import { } from "../../lib/project/resolution"; import type { ProjectShowResult } from "../../types/project"; import { resolveActiveWorkspace } from "../resources-shared/workspace"; -import { legacyOperationContext, listWorkspaceProjects } from "./context"; +import { listWorkspaceProjects, operationContext } from "./context"; interface FieldRow { readonly label: string; @@ -124,7 +124,7 @@ export const projectShowCommand = defineCommand({ handler: async (args, ctx) => { const workspace = await resolveActiveWorkspace(ctx); const inspected = await inspectProjectBinding({ - context: legacyOperationContext(ctx), + context: operationContext(ctx), workspace, explicitProject: args.positionals.project, listProjects: () => listWorkspaceProjects(ctx), diff --git a/packages/cli/src/commands/project/transfer.ts b/packages/cli/src/commands/project/transfer.ts index a3954d13..33d77e2c 100644 --- a/packages/cli/src/commands/project/transfer.ts +++ b/packages/cli/src/commands/project/transfer.ts @@ -34,8 +34,8 @@ import { import type { ProjectTransferResult } from "../../types/project"; import { resolveActiveWorkspace } from "../resources-shared/workspace"; import { - legacyOperationContext, listWorkspaceProjects, + operationContext, type ProjectCommandContext, } from "./context"; import { localPinDiagnostics } from "./presentation"; @@ -255,7 +255,7 @@ export const projectTransferCommand = defineCommand({ const warnings: string[] = []; const action = await rewriteOrClearLocalPinForProject( - legacyOperationContext(ctx), + operationContext(ctx), project.id, recipient.workspaceId, { onError: (message) => warnings.push(message) }, diff --git a/packages/cli/src/controllers/app-env-file.ts b/packages/cli/src/controllers/app-env-file.ts index 68fd55e0..528c69e9 100644 --- a/packages/cli/src/controllers/app-env-file.ts +++ b/packages/cli/src/controllers/app-env-file.ts @@ -5,8 +5,6 @@ import { type NextAction, } from "@prisma/cli-engine/protocol"; import type { ManagementApiClient } from "@prisma/management-api-sdk"; -import type { CommandSuccess } from "../legacy/output"; -import type { CommandContext } from "../legacy/runtime"; import { type EnvScope, formatScopeLabel } from "../lib/app/env-config"; import { runCommand, userChoice } from "../lib/app/env-errors"; import type { EnvFileAssignment } from "../lib/app/env-file"; @@ -23,11 +21,20 @@ import { type ResolvedEnvApiScope, toMetadata, } from "./app-env-api"; +import type { CommandContext } from "./context"; export interface ResolvedEnvFileScope extends ResolvedEnvApiScope { scope: EnvScope; } +/** What an env-file controller returns: the result plus the findings it + * collected along the way, which the command turns into diagnostics. */ +export interface CommandSuccess { + command: string; + result: T; + warnings: string[]; +} + export async function runEnvAddFile( context: CommandContext, client: ManagementApiClient, diff --git a/packages/cli/src/controllers/app-env.ts b/packages/cli/src/controllers/app-env.ts index 55ba2f6f..876b910d 100644 --- a/packages/cli/src/controllers/app-env.ts +++ b/packages/cli/src/controllers/app-env.ts @@ -2,7 +2,6 @@ import { CliStructuredError } from "@prisma/cli-engine/protocol"; import type { ManagementApiClient } from "@prisma/management-api-sdk"; -import type { CommandContext } from "../legacy/runtime"; import { type EnvScope, type EnvVarRole, @@ -19,6 +18,7 @@ import { type RawEnvironmentVariable, type ResolvedEnvApiScope, } from "./app-env-api"; +import type { CommandContext } from "./context"; interface ResolvedScope extends ResolvedEnvApiScope { scope: EnvScope; diff --git a/packages/cli/src/legacy/runtime.ts b/packages/cli/src/controllers/context.ts similarity index 50% rename from packages/cli/src/legacy/runtime.ts rename to packages/cli/src/controllers/context.ts index cb4f7a82..b5f650f5 100644 --- a/packages/cli/src/legacy/runtime.ts +++ b/packages/cli/src/controllers/context.ts @@ -1,7 +1,7 @@ /** - * The context the surviving legacy operation layer takes. The commander - * shell that built it is gone, and the operations read only these three - * runtime fields; the command handlers supply them + * The context the project and env controllers take. The commander + * shell that once built it is gone; the controllers read only these + * three runtime fields, and the command handlers supply them * (`src/commands/project/context.ts`). */ export interface CliRuntime { diff --git a/packages/cli/src/controllers/project.ts b/packages/cli/src/controllers/project.ts index 69b6bcdc..4f5afa27 100644 --- a/packages/cli/src/controllers/project.ts +++ b/packages/cli/src/controllers/project.ts @@ -10,7 +10,6 @@ import type { ManagementApiClient } from "@prisma/management-api-sdk"; import { matchError } from "better-result"; import type { GitHubRepositoryReference } from "../adapters/git"; -import type { CommandContext } from "../legacy/runtime"; import type { PrismaCliPackageCommandFormatter } from "../lib/agent/cli-command"; import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, @@ -24,6 +23,7 @@ import type { GitRepositoryConnection, ProjectListResult, } from "../types/project"; +import type { CommandContext } from "./context"; export const GITHUB_INSTALL_POLL_INTERVAL_MS = 2_000; export const GITHUB_INSTALL_POLL_TIMEOUT_MS = 120_000; diff --git a/packages/cli/src/legacy/output.ts b/packages/cli/src/legacy/output.ts deleted file mode 100644 index 96a8345c..00000000 --- a/packages/cli/src/legacy/output.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { Writable } from "node:stream"; - -/** What an env-file controller returns: the result plus the findings it - * collected along the way, which the command turns into diagnostics. */ -export interface CommandSuccess { - command: string; - result: T; - warnings: string[]; -} - -export interface CliOutput { - stdout: Writable; - stderr: Writable; -} diff --git a/packages/cli/tests/helpers.ts b/packages/cli/tests/helpers.ts index c897b2f8..0666639a 100644 --- a/packages/cli/tests/helpers.ts +++ b/packages/cli/tests/helpers.ts @@ -1,7 +1,7 @@ import { mkdtemp, readFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import type { CliRuntime, CommandContext } from "../src/legacy/runtime"; +import type { CliRuntime, CommandContext } from "../src/controllers/context"; export async function createTempCwd(): Promise { return mkdtemp(path.join(os.tmpdir(), "prisma-cli-")); diff --git a/packages/cli/tests/operation-context.test.ts b/packages/cli/tests/operation-context.test.ts index 99098a5b..70beea45 100644 --- a/packages/cli/tests/operation-context.test.ts +++ b/packages/cli/tests/operation-context.test.ts @@ -1,9 +1,8 @@ /** - * The legacy-context adapter. `commands/project/context.ts` hands the legacy - * operations an object carrying three of the shell `CommandContext`'s - * fields, cast to the whole type. The structural fix belongs to S2d, - * when the legacy shell dies; until then the adapter refuses a read it - * cannot serve and names the field, so a legacy edit that starts + * The operation-context adapter. `commands/project/context.ts` hands the + * controllers an object carrying the three fields their `CommandContext` + * declares. The adapter refuses a read it + * cannot serve and names the field, so a controller edit that starts * reading a fourth fails where the mistake is rather than as * `Cannot read properties of undefined` somewhere downstream — worst * case inside `project transfer`, after the project has already moved. @@ -16,7 +15,7 @@ import os from "node:os"; import path from "node:path"; import type { CommandContext, ManagementApiClient } from "@prisma/cli-engine"; import { describe, expect, it } from "vitest"; -import { legacyOperationContext } from "../src/commands/project/context"; +import { operationContext } from "../src/commands/project/context"; import { resolveEnvWriteInput } from "../src/controllers/app-env"; import { runEnvAddFile } from "../src/controllers/app-env-file"; import { @@ -42,7 +41,7 @@ function stubContext(cwd: string): ProjectCommandContext { } async function pinnedCwd() { - const cwd = await mkdtemp(path.join(os.tmpdir(), "legacy-context-")); + const cwd = await mkdtemp(path.join(os.tmpdir(), "operation-context-")); await mkdir(path.join(cwd, ".prisma"), { recursive: true }); await writeFile( path.join(cwd, ".prisma", "local.json"), @@ -52,9 +51,9 @@ async function pinnedCwd() { return cwd; } -describe("the legacy-context adapter", () => { +describe("the operation-context adapter", () => { it("serves the three fields it declares", () => { - const context = legacyOperationContext(stubContext("/somewhere")); + const context = operationContext(stubContext("/somewhere")); expect(context.runtime.cwd).toBe("/somewhere"); expect(context.runtime.env).toEqual({ HOME: "/home/test" }); @@ -62,28 +61,28 @@ describe("the legacy-context adapter", () => { }); it("refuses a runtime field it cannot serve, and names it", () => { - const context = legacyOperationContext(stubContext("/somewhere")); + const context = operationContext(stubContext("/somewhere")); expect( () => (context.runtime as unknown as { stdout: unknown }).stdout, ).toThrow( - "the legacy-context adapter provides only runtime.cwd, runtime.env and runtime.signal; runtime.stdout was read", + "the operation-context adapter provides only runtime.cwd, runtime.env and runtime.signal; runtime.stdout was read", ); }); it("refuses a top-level field it cannot serve, and names it", () => { - const context = legacyOperationContext(stubContext("/somewhere")); + const context = operationContext(stubContext("/somewhere")); expect(() => (context as unknown as { ui: unknown }).ui).toThrow( - "the legacy-context adapter provides only runtime.cwd, runtime.env and runtime.signal; ui was read", + "the operation-context adapter provides only runtime.cwd, runtime.env and runtime.signal; ui was read", ); }); it("lets the language probe it without throwing", async () => { - const context = legacyOperationContext(stubContext("/somewhere")); + const context = operationContext(stubContext("/somewhere")); // Symbols and `then` are how the runtime inspects an object, not how - // the legacy code reads a field. Awaiting the adapter reads `then`; + // a controller reads a field. Awaiting the adapter reads `then`; // throwing there would be the very failure the trap exists to remove. expect( (context as unknown as Record)[Symbol.toStringTag], @@ -93,7 +92,7 @@ describe("the legacy-context adapter", () => { }); it("carries resolveProjectTarget", async () => { - const context = legacyOperationContext(stubContext(await pinnedCwd())); + const context = operationContext(stubContext(await pinnedCwd())); const target = await resolveProjectTarget({ context, @@ -106,7 +105,7 @@ describe("the legacy-context adapter", () => { }); it("carries cleanupLocalPinForProject", async () => { - const context = legacyOperationContext(stubContext(await pinnedCwd())); + const context = operationContext(stubContext(await pinnedCwd())); const warnings: string[] = []; const cleared = await cleanupLocalPinForProject(context, "proj_1", { @@ -118,7 +117,7 @@ describe("the legacy-context adapter", () => { }); it("carries rewriteOrClearLocalPinForProject", async () => { - const context = legacyOperationContext(stubContext(await pinnedCwd())); + const context = operationContext(stubContext(await pinnedCwd())); const warnings: string[] = []; const action = await rewriteOrClearLocalPinForProject( @@ -135,7 +134,7 @@ describe("the legacy-context adapter", () => { it("carries resolveEnvWriteInput, for both a single assignment and a file", async () => { const cwd = await pinnedCwd(); await writeFile(path.join(cwd, ".env"), "STRIPE_KEY=sk_test\n", "utf8"); - const context = legacyOperationContext(stubContext(cwd)); + const context = operationContext(stubContext(cwd)); expect( await resolveEnvWriteInput( @@ -159,7 +158,7 @@ describe("the legacy-context adapter", () => { }); it("carries runEnvAddFile", async () => { - const context = legacyOperationContext(stubContext(await pinnedCwd())); + const context = operationContext(stubContext(await pinnedCwd())); const created = { id: "env_1", key: "STRIPE_KEY",