@prisma/cli 3.0.0-dev.39.1 → 3.0.0-dev.41.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.
@@ -62,13 +62,13 @@ function createDeployCommand(runtime) {
62
62
  command.addOption(new Option("--app <name>", "App name")).addOption(new Option("--project <id-or-name>", "Project id or name")).addOption(new Option("--create-project <name>", "Create and link a new Project before deploying")).addOption(new Option("--branch <name>", "Branch name")).addOption(new Option("--framework <name>", "Framework to deploy").choices([
63
63
  "nextjs",
64
64
  "hono",
65
- "tanstack-start"
66
- ])).addOption(new Option("--entry <path>", "Entrypoint path for Bun or auto deploys")).addOption(new Option("--build-type <type>", "Legacy deploy build type").choices([...PREVIEW_BUILD_TYPES]).default("auto").hideHelp()).addOption(new Option("--http-port <port>", "HTTP port override for the deployed app")).addOption(new Option("--env <name=value>", "Environment variable").argParser(collectRepeatableValues));
65
+ "tanstack-start",
66
+ "bun"
67
+ ])).addOption(new Option("--entry <path>", "Entrypoint path for Bun deploys")).addOption(new Option("--http-port <port>", "HTTP port override for the deployed app")).addOption(new Option("--env <name=value>", "Environment variable").argParser(collectRepeatableValues));
67
68
  addGlobalFlags(command);
68
69
  command.action(async (options) => {
69
70
  const appName = options.app;
70
71
  const entry = options.entry;
71
- const buildType = options.buildType;
72
72
  const branchName = options.branch;
73
73
  const framework = options.framework;
74
74
  const httpPort = options.httpPort;
@@ -80,7 +80,6 @@ function createDeployCommand(runtime) {
80
80
  createProjectName,
81
81
  branchName,
82
82
  entrypoint: entry,
83
- buildType,
84
83
  framework,
85
84
  httpPort,
86
85
  envAssignments
@@ -1,7 +1,6 @@
1
1
  import { CliError, authRequiredError, usageError, workspaceRequiredError } from "../shell/errors.js";
2
2
  import { requireComputeAuth } from "../lib/auth/guard.js";
3
3
  import { resolveProjectTarget } from "../lib/project/resolution.js";
4
- import { createSelectPromptPort } from "./select-prompt-port.js";
5
4
  import { requireAuthenticatedAuthState } from "./auth.js";
6
5
  import { listRealWorkspaceProjects } from "./project.js";
7
6
  import { formatScopeLabel, parseKeyValuePositional, resolveEnvScope } from "../lib/app/env-config.js";
@@ -19,7 +18,7 @@ async function runEnvAdd(context, rawAssignment, flags) {
19
18
  command: "add"
20
19
  });
21
20
  if (!scope) throw usageError(`prisma-cli project env add requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [`prisma-cli project env add ${key}=${value} --role production`], "app");
22
- const { client, projectId } = await requireClientAndProject(context, flags.projectRef);
21
+ const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env add");
23
22
  const resolved = await resolveScopeToApi(client, projectId, scope, { createBranchIfMissing: true });
24
23
  if (await findVariableByNaturalKey(client, projectId, key, resolved)) throw new CliError({
25
24
  code: "ENV_VARIABLE_ALREADY_EXISTS",
@@ -70,7 +69,7 @@ async function runEnvUpdate(context, rawAssignment, flags) {
70
69
  command: "update"
71
70
  });
72
71
  if (!scope) throw usageError(`prisma-cli project env update requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [`prisma-cli project env update ${key}=${value} --role production`], "app");
73
- const { client, projectId } = await requireClientAndProject(context, flags.projectRef);
72
+ const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env update");
74
73
  const resolved = await resolveScopeToApi(client, projectId, scope, { createBranchIfMissing: false });
75
74
  const existing = await findVariableByNaturalKey(client, projectId, key, resolved);
76
75
  if (!existing) throw new CliError({
@@ -103,7 +102,7 @@ async function runEnvList(context, flags) {
103
102
  requireExplicit: false,
104
103
  command: "list"
105
104
  }) ?? defaultRoleScope();
106
- const { client, projectId } = await requireClientAndProject(context, flags.projectRef);
105
+ const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env list");
107
106
  const resolved = await resolveScopeToApi(client, projectId, scope, { createBranchIfMissing: false });
108
107
  const variables = await listVariables(client, projectId, resolved);
109
108
  return {
@@ -124,7 +123,7 @@ async function runEnvRemove(context, key, flags) {
124
123
  command: "remove"
125
124
  });
126
125
  if (!scope) throw usageError("prisma-cli project env remove requires --role or --branch", "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [`prisma-cli project env remove ${key} --role production`], "app");
127
- const { client, projectId } = await requireClientAndProject(context, flags.projectRef);
126
+ const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env remove");
128
127
  const resolved = await resolveScopeToApi(client, projectId, scope, { createBranchIfMissing: false });
129
128
  const existing = await findVariableByNaturalKey(client, projectId, key, resolved);
130
129
  if (!existing) throw new CliError({
@@ -149,7 +148,7 @@ async function runEnvRemove(context, key, flags) {
149
148
  nextSteps: []
150
149
  };
151
150
  }
152
- async function requireClientAndProject(context, explicitProject) {
151
+ async function requireClientAndProject(context, explicitProject, commandName) {
153
152
  const authState = await requireAuthenticatedAuthState(context);
154
153
  const client = await requireComputeAuth(context.runtime.env);
155
154
  if (!client) throw authRequiredError(["prisma-cli auth login"]);
@@ -161,8 +160,7 @@ async function requireClientAndProject(context, explicitProject) {
161
160
  workspace: authState.workspace,
162
161
  explicitProject,
163
162
  listProjects: () => listRealWorkspaceProjects(client, authState.workspace),
164
- prompt: createSelectPromptPort(context),
165
- remember: true
163
+ commandName
166
164
  })).project.id
167
165
  };
168
166
  }
@@ -9,10 +9,10 @@ import { requireComputeAuth } from "../lib/auth/guard.js";
9
9
  import { readAuthState } from "../lib/auth/auth-ops.js";
10
10
  import { parseEnvAssignments } from "../lib/app/env-vars.js";
11
11
  import { renderDeployOutputRows } from "../lib/app/deploy-output.js";
12
- import { readBunPackageJson } from "../lib/app/bun-project.js";
12
+ import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
13
13
  import { DEFAULT_LOCAL_DEV_PORT, resolveLocalBuildType, runLocalApp } from "../lib/app/local-dev.js";
14
- import { inferTargetName, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
15
14
  import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
15
+ import { inferTargetName, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
16
16
  import { bindProjectToDirectory, formatCommandArgument, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
17
17
  import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
18
18
  import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
@@ -29,8 +29,10 @@ import open from "open";
29
29
  const DEPLOY_FRAMEWORKS = [
30
30
  "nextjs",
31
31
  "hono",
32
- "tanstack-start"
32
+ "tanstack-start",
33
+ "bun"
33
34
  ];
35
+ const TANSTACK_START_PACKAGES = ["@tanstack/react-start", "@tanstack/solid-start"];
34
36
  const FRAMEWORK_DEFAULT_HTTP_PORT = 3e3;
35
37
  const PRISMA_PROJECT_ID_ENV_VAR = "PRISMA_PROJECT_ID";
36
38
  const PRISMA_APP_ID_ENV_VAR = "PRISMA_APP_ID";
@@ -105,15 +107,12 @@ async function runAppDeploy(context, appName, options) {
105
107
  const skipLocalPin = Boolean(envProjectId || options?.projectRef || options?.createProjectName);
106
108
  const localPin = skipLocalPin ? { kind: "missing" } : await readLocalResolutionPin(context.runtime.cwd);
107
109
  if (!skipLocalPin && localPin.kind === "invalid") throw localResolutionPinStaleError();
108
- const explicitBuildType = Boolean(options?.buildType && options.buildType !== "auto");
110
+ const branch = await resolveDeployBranch(context, options?.branchName);
109
111
  if (options?.httpPort) parseDeployHttpPort(options.httpPort);
110
112
  assertSupportedEntrypointForRequestedDeployShape({
111
113
  requestedFramework: options?.framework,
112
- requestedBuildType: options?.buildType,
113
- explicitBuildType,
114
114
  entrypoint: options?.entrypoint
115
115
  });
116
- const branch = await resolveDeployBranch(context, options?.branchName);
117
116
  const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
118
117
  branch,
119
118
  createProjectName: options?.createProjectName,
@@ -128,8 +127,7 @@ async function runAppDeploy(context, appName, options) {
128
127
  }
129
128
  let framework = await resolveDeployFramework(context, {
130
129
  requestedFramework: options?.framework,
131
- requestedBuildType: options?.buildType,
132
- explicitBuildType
130
+ entrypoint: options?.entrypoint
133
131
  });
134
132
  let runtime = resolveDeployRuntime(options?.httpPort, framework);
135
133
  assertSupportedEntrypoint(framework.buildType, options?.entrypoint, "deploy");
@@ -151,13 +149,14 @@ async function runAppDeploy(context, appName, options) {
151
149
  runtime,
152
150
  firstDeploy: selectedApp.firstDeploy,
153
151
  explicitFramework: Boolean(options?.framework),
154
- explicitBuildType,
152
+ explicitEntrypoint: Boolean(options?.entrypoint),
155
153
  explicitHttpPort: Boolean(options?.httpPort)
156
154
  });
157
155
  framework = customized.framework;
158
156
  runtime = customized.runtime;
159
157
  const buildType = framework.buildType;
160
158
  assertSupportedEntrypoint(buildType, options?.entrypoint, "deploy");
159
+ const entrypoint = await resolveDeployEntrypoint(context.runtime.cwd, framework, options?.entrypoint);
161
160
  const portMapping = parseDeployPortMapping(String(runtime.port));
162
161
  const progressState = createPreviewDeployProgressState();
163
162
  const deployStartedAt = Date.now();
@@ -168,7 +167,7 @@ async function runAppDeploy(context, appName, options) {
168
167
  appId: selectedApp.appId,
169
168
  appName: selectedApp.appName,
170
169
  region: selectedApp.region,
171
- entrypoint: options?.entrypoint,
170
+ entrypoint,
172
171
  buildType,
173
172
  portMapping,
174
173
  envVars,
@@ -204,7 +203,7 @@ async function runAppDeploy(context, appName, options) {
204
203
  }
205
204
  async function runAppListDeploys(context, appName, projectRef) {
206
205
  ensurePreviewAppMode(context);
207
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
206
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app list-deploys" });
208
207
  const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
209
208
  if (!selectedApp) return {
210
209
  command: "app.list-deploys",
@@ -241,7 +240,7 @@ async function runAppListDeploys(context, appName, projectRef) {
241
240
  }
242
241
  async function runAppShow(context, appName, projectRef) {
243
242
  ensurePreviewAppMode(context);
244
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
243
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app show" });
245
244
  const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
246
245
  if (!selectedApp) return {
247
246
  command: "app.show",
@@ -317,7 +316,7 @@ async function runAppShowDeploy(context, deploymentId) {
317
316
  }
318
317
  async function runAppOpen(context, appName, projectRef) {
319
318
  ensurePreviewAppMode(context);
320
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
319
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app open" });
321
320
  const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
322
321
  if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
323
322
  const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
@@ -351,7 +350,7 @@ async function runAppOpen(context, appName, projectRef) {
351
350
  }
352
351
  async function runAppDomainAdd(context, hostname, options) {
353
352
  const normalizedHostname = normalizeDomainHostname(hostname);
354
- const target = await resolveAppDomainTarget(context, options);
353
+ const target = await resolveAppDomainTarget(context, options, `app domain add ${normalizedHostname}`);
355
354
  const added = await target.provider.addDomain({
356
355
  appId: target.app.id,
357
356
  hostname: normalizedHostname
@@ -371,7 +370,7 @@ async function runAppDomainAdd(context, hostname, options) {
371
370
  }
372
371
  async function runAppDomainShow(context, hostname, options) {
373
372
  const normalizedHostname = normalizeDomainHostname(hostname);
374
- const target = await resolveAppDomainTarget(context, options);
373
+ const target = await resolveAppDomainTarget(context, options, `app domain show ${normalizedHostname}`);
375
374
  const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show");
376
375
  const detail = await target.provider.showDomain(domain.id).catch((error) => {
377
376
  throw domainCommandError("show", error, normalizedHostname);
@@ -388,7 +387,7 @@ async function runAppDomainShow(context, hostname, options) {
388
387
  }
389
388
  async function runAppDomainRemove(context, hostname, options) {
390
389
  const normalizedHostname = normalizeDomainHostname(hostname);
391
- const target = await resolveAppDomainTarget(context, options);
390
+ const target = await resolveAppDomainTarget(context, options, `app domain remove ${normalizedHostname}`);
392
391
  const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove");
393
392
  await confirmDomainRemoval(context, target.resultTarget, normalizedHostname);
394
393
  await target.provider.removeDomain(domain.id).catch((error) => {
@@ -407,7 +406,7 @@ async function runAppDomainRemove(context, hostname, options) {
407
406
  }
408
407
  async function runAppDomainRetry(context, hostname, options) {
409
408
  const normalizedHostname = normalizeDomainHostname(hostname);
410
- const target = await resolveAppDomainTarget(context, options);
409
+ const target = await resolveAppDomainTarget(context, options, `app domain retry ${normalizedHostname}`);
411
410
  const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry");
412
411
  const retried = await target.provider.retryDomain(domain.id).catch((error) => {
413
412
  throw domainCommandError("retry", error, normalizedHostname);
@@ -425,7 +424,7 @@ async function runAppDomainRetry(context, hostname, options) {
425
424
  async function runAppDomainWait(context, hostname, options) {
426
425
  const normalizedHostname = normalizeDomainHostname(hostname);
427
426
  const timeoutMs = parseDomainWaitTimeout(options?.timeout);
428
- const target = await resolveAppDomainTarget(context, options);
427
+ const target = await resolveAppDomainTarget(context, options, `app domain wait ${normalizedHostname}`);
429
428
  const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait");
430
429
  if (!context.flags.json && !context.flags.quiet) context.output.stderr.write([
431
430
  `app domain wait -> Waiting for ${normalizedHostname} to become active.`,
@@ -477,7 +476,7 @@ async function runAppDomainWait(context, hostname, options) {
477
476
  }
478
477
  async function runAppLogs(context, appName, deploymentId, projectRef) {
479
478
  ensurePreviewAppMode(context);
480
- const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef);
479
+ const { provider, target: resolvedTarget, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app logs" });
481
480
  const target = deploymentId ? await resolveExplicitLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName, deploymentId) : await resolveLiveLogDeployment(context, provider, projectId, resolvedTarget.branch.name, appName);
482
481
  if (!context.flags.json && !context.flags.quiet) {
483
482
  const lines = renderCommandHeader(context.ui, {
@@ -601,7 +600,7 @@ function writeLogRecord(context, record) {
601
600
  }
602
601
  async function runAppPromote(context, deploymentId, appName, projectRef) {
603
602
  ensurePreviewAppMode(context);
604
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
603
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app promote" });
605
604
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
606
605
  const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
607
606
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
@@ -641,7 +640,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
641
640
  }
642
641
  async function runAppRollback(context, appName, deploymentId, projectRef) {
643
642
  ensurePreviewAppMode(context);
644
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
643
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app rollback" });
645
644
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
646
645
  const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
647
646
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
@@ -683,7 +682,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
683
682
  }
684
683
  async function runAppRemove(context, appName, projectRef) {
685
684
  ensurePreviewAppMode(context);
686
- const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef);
685
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app remove" });
687
686
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
688
687
  await confirmAppRemoval(context, selectedApp);
689
688
  const removedApp = await provider.removeApp(selectedApp.id).catch((error) => {
@@ -704,7 +703,7 @@ async function runAppRemove(context, appName, projectRef) {
704
703
  nextSteps: ["prisma-cli app deploy", "prisma-cli app list-deploys"]
705
704
  };
706
705
  }
707
- async function resolveAppDomainTarget(context, options) {
706
+ async function resolveAppDomainTarget(context, options, commandName = "app domain") {
708
707
  ensurePreviewAppMode(context);
709
708
  const branch = resolveDomainBranch(options?.branchName);
710
709
  if (toBranchKind(branch.name) !== "production") throw new CliError({
@@ -718,13 +717,10 @@ async function resolveAppDomainTarget(context, options) {
718
717
  });
719
718
  const envProjectId = readDeployEnvOverride(context, PRISMA_PROJECT_ID_ENV_VAR);
720
719
  const envAppId = readDeployEnvOverride(context, PRISMA_APP_ID_ENV_VAR);
721
- const skipLocalPin = Boolean(envProjectId);
722
- const localPin = skipLocalPin ? { kind: "missing" } : await readLocalResolutionPin(context.runtime.cwd);
723
- if (!skipLocalPin && localPin.kind === "invalid") throw localResolutionPinStaleError();
724
- const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
720
+ const { provider, target, projectId } = await requireProviderAndProjectContext(context, options?.projectRef, {
725
721
  branch,
726
- envProjectId,
727
- localPin
722
+ commandName,
723
+ envProjectId
728
724
  });
729
725
  const selectedApp = await resolveDomainAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), {
730
726
  explicitAppName: options?.appName,
@@ -1219,9 +1215,9 @@ async function listApps(context, provider, projectId, branchName) {
1219
1215
  domain: "project",
1220
1216
  summary: "Project not found",
1221
1217
  why: `The resolved project "${projectId}" does not exist in the authenticated workspace or is no longer accessible.`,
1222
- fix: "Pass --project <id-or-name>, or run prisma-cli project show to inspect resolution for this directory.",
1218
+ fix: "Pass --project <id-or-name>, or run prisma-cli project show to inspect this directory's binding.",
1223
1219
  exitCode: 1,
1224
- nextSteps: ["prisma-cli project show", "prisma-cli app deploy --project <id-or-name>"]
1220
+ nextSteps: ["prisma-cli project show", "prisma-cli project link <id-or-name>"]
1225
1221
  });
1226
1222
  throw deployFailedError("Failed to list apps", error, ["prisma-cli project show"]);
1227
1223
  });
@@ -1281,8 +1277,9 @@ async function resolveProjectContext(context, client, explicitProject, options)
1281
1277
  context,
1282
1278
  workspace: authState.workspace,
1283
1279
  explicitProject,
1280
+ envProjectId: options?.envProjectId,
1284
1281
  listProjects: () => listRealWorkspaceProjects(client, authState.workspace),
1285
- remember: true
1282
+ commandName: options?.commandName
1286
1283
  });
1287
1284
  const branch = options?.branch ?? await resolveDeployBranch(context, void 0);
1288
1285
  return {
@@ -1505,15 +1502,12 @@ async function resolveGitHeadPath(gitPath) {
1505
1502
  }
1506
1503
  async function resolveDeployFramework(context, options) {
1507
1504
  if (options.requestedFramework) return frameworkFromUserFacingValue(options.requestedFramework, "set by --framework");
1508
- if (options.explicitBuildType) {
1509
- const buildType = normalizeBuildType(options.requestedBuildType);
1510
- if (buildType !== "auto") return {
1511
- key: buildType,
1512
- buildType,
1513
- displayName: formatBuildTypeName(buildType),
1514
- annotation: "set by --build-type"
1515
- };
1516
- }
1505
+ if (options.entrypoint) return {
1506
+ key: "bun",
1507
+ buildType: "bun",
1508
+ displayName: "Bun",
1509
+ annotation: "set by --entry"
1510
+ };
1517
1511
  const detected = await detectDeployFramework(context.runtime.cwd);
1518
1512
  if (detected) return detected;
1519
1513
  throw frameworkNotDetectedError(context.runtime.cwd);
@@ -1529,12 +1523,22 @@ function resolveDeployRuntime(requestedHttpPort, framework) {
1529
1523
  };
1530
1524
  }
1531
1525
  function assertSupportedEntrypointForRequestedDeployShape(options) {
1532
- if (options.requestedFramework) {
1533
- assertSupportedEntrypoint(frameworkFromUserFacingValue(options.requestedFramework, "set by --framework").buildType, options.entrypoint, "deploy");
1526
+ if (!options.requestedFramework) return;
1527
+ assertSupportedEntrypoint(frameworkFromUserFacingValue(options.requestedFramework, "set by --framework").buildType, options.entrypoint, "deploy");
1528
+ }
1529
+ async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint) {
1530
+ if (explicitEntrypoint || framework.buildType !== "bun") return explicitEntrypoint;
1531
+ const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd));
1532
+ if (packageEntrypoint) return packageEntrypoint;
1533
+ if (framework.key !== "hono") return;
1534
+ const defaultEntrypoint = "src/index.ts";
1535
+ try {
1536
+ await access(path.join(cwd, defaultEntrypoint));
1537
+ return defaultEntrypoint;
1538
+ } catch (error) {
1539
+ if (error.code !== "ENOENT") throw error;
1534
1540
  return;
1535
1541
  }
1536
- if (!options.explicitBuildType) return;
1537
- assertSupportedEntrypoint(normalizeBuildType(options.requestedBuildType), options.entrypoint, "deploy");
1538
1542
  }
1539
1543
  async function detectDeployFramework(cwd) {
1540
1544
  const packageJson = await readBunPackageJson(cwd);
@@ -1551,7 +1555,7 @@ async function detectDeployFramework(cwd) {
1551
1555
  displayName: "Hono",
1552
1556
  annotation: "detected from package.json"
1553
1557
  };
1554
- if (hasPackageDependency(packageJson, "@tanstack/start")) return {
1558
+ if (hasAnyPackageDependency(packageJson, TANSTACK_START_PACKAGES)) return {
1555
1559
  key: "tanstack-start",
1556
1560
  buildType: "tanstack-start",
1557
1561
  displayName: "TanStack Start",
@@ -1564,7 +1568,8 @@ async function detectNextConfig(cwd) {
1564
1568
  "next.config.js",
1565
1569
  "next.config.mjs",
1566
1570
  "next.config.cjs",
1567
- "next.config.ts"
1571
+ "next.config.ts",
1572
+ "next.config.mts"
1568
1573
  ]) {
1569
1574
  const filePath = path.join(cwd, candidate);
1570
1575
  try {
@@ -1585,6 +1590,9 @@ async function detectNextConfig(cwd) {
1585
1590
  function hasPackageDependency(packageJson, dependencyName) {
1586
1591
  return hasDependency(packageJson?.dependencies, dependencyName) || hasDependency(packageJson?.devDependencies, dependencyName);
1587
1592
  }
1593
+ function hasAnyPackageDependency(packageJson, dependencyNames) {
1594
+ return dependencyNames.some((dependencyName) => hasPackageDependency(packageJson, dependencyName));
1595
+ }
1588
1596
  function hasDependency(dependencies, dependencyName) {
1589
1597
  return Boolean(dependencies && typeof dependencies === "object" && dependencyName in dependencies);
1590
1598
  }
@@ -1604,9 +1612,16 @@ function frameworkFromUserFacingValue(value, annotation) {
1604
1612
  displayName: "Hono",
1605
1613
  annotation
1606
1614
  };
1615
+ case "bun": return {
1616
+ key: "bun",
1617
+ buildType: "bun",
1618
+ displayName: "Bun",
1619
+ annotation
1620
+ };
1607
1621
  case "tanstack":
1608
1622
  case "tanstack-start":
1609
- case "@tanstack/start": return {
1623
+ case "@tanstack/react-start":
1624
+ case "@tanstack/solid-start": return {
1610
1625
  key: "tanstack-start",
1611
1626
  buildType: "tanstack-start",
1612
1627
  displayName: "TanStack Start",
@@ -1616,19 +1631,21 @@ function frameworkFromUserFacingValue(value, annotation) {
1616
1631
  }
1617
1632
  }
1618
1633
  function frameworkNotDetectedError(cwd, requestedFramework) {
1619
- const supported = "Next.js, Hono, TanStack Start";
1634
+ const supported = "Next.js, Hono, TanStack Start, Bun";
1620
1635
  const directory = cwd ? ` in ${formatDeployDirectory(cwd)}` : "";
1621
1636
  return new CliError({
1622
1637
  code: "FRAMEWORK_NOT_DETECTED",
1623
1638
  domain: "app",
1624
1639
  summary: requestedFramework ? `Unsupported framework "${requestedFramework}"` : `Cannot detect a supported framework${directory}`,
1625
1640
  why: `Supported Beta frameworks: ${supported}.`,
1626
- fix: "Add one of these frameworks as a dependency, or pass --framework <nextjs|hono|tanstack-start>.",
1641
+ fix: "Add one of these frameworks as a dependency, pass --framework <nextjs|hono|tanstack-start|bun>, or pass --entry <path> for a Bun app.",
1627
1642
  exitCode: 2,
1628
1643
  nextSteps: [
1629
1644
  "prisma-cli app deploy --framework nextjs",
1630
1645
  "prisma-cli app deploy --framework hono",
1631
- "prisma-cli app deploy --framework tanstack-start"
1646
+ "prisma-cli app deploy --framework tanstack-start",
1647
+ "prisma-cli app deploy --framework bun --entry server.ts",
1648
+ "prisma-cli app deploy --entry server.ts"
1632
1649
  ]
1633
1650
  });
1634
1651
  }
@@ -1643,7 +1660,7 @@ function maybeRenderProjectLinked(context, directory, projectName, localPinPath)
1643
1660
  context.output.stderr.write(`${context.ui.success("✔")} Linked "${directory}" to Project "${projectName}"\nSaved ${localPinPath}\n\n`);
1644
1661
  }
1645
1662
  async function maybeCustomizeDeploySettings(context, options) {
1646
- if (!options.firstDeploy || context.flags.yes || options.explicitFramework || options.explicitBuildType || options.explicitHttpPort || !canPrompt(context)) return {
1663
+ if (!options.firstDeploy || context.flags.yes || options.explicitFramework || options.explicitEntrypoint || options.explicitHttpPort || !canPrompt(context)) return {
1647
1664
  framework: options.framework,
1648
1665
  runtime: options.runtime
1649
1666
  };
@@ -1700,6 +1717,7 @@ function frameworkDisplayName(framework) {
1700
1717
  case "nextjs": return "Next.js";
1701
1718
  case "hono": return "Hono";
1702
1719
  case "tanstack-start": return "TanStack Start";
1720
+ case "bun": return "Bun";
1703
1721
  }
1704
1722
  }
1705
1723
  function validateDeployHttpPortText(value) {
@@ -1734,7 +1752,10 @@ function getBuildTypeExamples(commandName) {
1734
1752
  });
1735
1753
  }
1736
1754
  function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
1737
- if (buildType !== "auto" && buildType !== "bun" && entrypoint) throw usageError(`App ${commandName} does not accept --entry with --build-type ${buildType}`, `${formatBuildTypeName(buildType)} apps do not use an entrypoint flag in the current preview.`, `Remove --entry, or rerun prisma-cli app ${commandName} with --build-type bun when you want to target a Bun entrypoint directly.`, [`prisma-cli app ${commandName} --build-type ${buildType}`, `prisma-cli app ${commandName} --build-type bun --entry server.ts`], "app");
1755
+ if (buildType !== "auto" && buildType !== "bun" && entrypoint) {
1756
+ if (commandName === "deploy") throw usageError(`App deploy does not accept --entry with ${formatBuildTypeName(buildType)}`, `${formatBuildTypeName(buildType)} apps derive their runtime entrypoint from build output.`, "Remove --entry, or use --framework bun when you want to target a Bun entrypoint directly.", [`prisma-cli app deploy --framework ${buildType}`, "prisma-cli app deploy --framework bun --entry server.ts"], "app");
1757
+ throw usageError(`App ${commandName} does not accept --entry with --build-type ${buildType}`, `${formatBuildTypeName(buildType)} apps do not use an entrypoint flag in the current preview.`, `Remove --entry, or rerun prisma-cli app ${commandName} with --build-type bun when you want to target a Bun entrypoint directly.`, [`prisma-cli app ${commandName} --build-type ${buildType}`, `prisma-cli app ${commandName} --build-type bun --entry server.ts`], "app");
1758
+ }
1738
1759
  }
1739
1760
  async function requireLocalBuildType(context, buildType, commandName) {
1740
1761
  const resolvedBuildType = await resolveLocalBuildType(context.runtime.cwd, buildType);
@@ -1839,10 +1860,14 @@ function localResolutionPinStaleError() {
1839
1860
  domain: "project",
1840
1861
  summary: "Local project binding is stale",
1841
1862
  why: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
1842
- fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} and re-run to re-bootstrap.`,
1863
+ fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}, then choose a Project explicitly.`,
1843
1864
  meta: { pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH },
1844
1865
  exitCode: 1,
1845
- nextSteps: ["prisma-cli app deploy"]
1866
+ nextSteps: [
1867
+ "prisma-cli project list",
1868
+ "prisma-cli project link <id-or-name>",
1869
+ "prisma-cli app deploy --project <id-or-name>"
1870
+ ]
1846
1871
  });
1847
1872
  }
1848
1873
  function readDeployEnvOverride(context, name) {
@@ -2,8 +2,8 @@ import { CliError, authRequiredError, featureUnavailableError, usageError, works
2
2
  import { renderSummaryLine } from "../shell/ui.js";
3
3
  import { canPrompt } from "../shell/runtime.js";
4
4
  import { requireComputeAuth } from "../lib/auth/guard.js";
5
- import { resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
6
- import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup } from "../lib/project/setup.js";
5
+ import { inspectProjectBinding, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
6
+ import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
7
7
  import { createPreviewAppProvider } from "../lib/app/preview-provider.js";
8
8
  import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
9
9
  import { requireAuthenticatedAuthState } from "./auth.js";
@@ -94,7 +94,7 @@ async function runGitConnect(context, gitUrl, options = {}) {
94
94
  if (isRealMode(context)) {
95
95
  const client = await requireComputeAuth(context.runtime.env);
96
96
  if (!client) throw authRequiredError();
97
- const target = await resolveProjectShowInRealMode(context, workspace, options.project);
97
+ const target = await resolveRequiredProjectInRealMode(context, workspace, options.project, "git connect");
98
98
  const repository = await resolveRepositoryForConnect(context, gitUrl);
99
99
  const api = client;
100
100
  const existing = await readFirstSourceRepository(api, target.project.id);
@@ -129,7 +129,7 @@ async function runGitConnect(context, gitUrl, options = {}) {
129
129
  nextSteps: []
130
130
  };
131
131
  }
132
- const target = await resolveProjectShowInFixtureMode(context, workspace, options.project);
132
+ const target = await resolveRequiredProjectInFixtureMode(context, workspace, options.project, "git connect");
133
133
  const repository = await resolveRepositoryForConnect(context, gitUrl);
134
134
  const existingConnection = await context.stateStore.readRepositoryConnection(target.project.id);
135
135
  if (existingConnection) {
@@ -162,7 +162,7 @@ async function runGitDisconnect(context, options = {}) {
162
162
  if (isRealMode(context)) {
163
163
  const client = await requireComputeAuth(context.runtime.env);
164
164
  if (!client) throw authRequiredError();
165
- const target = await resolveProjectShowInRealMode(context, workspace, options.project);
165
+ const target = await resolveRequiredProjectInRealMode(context, workspace, options.project, "git disconnect");
166
166
  const api = client;
167
167
  const existing = await readFirstSourceRepository(api, target.project.id);
168
168
  if (!existing) throw repoNotConnectedError();
@@ -178,7 +178,7 @@ async function runGitDisconnect(context, options = {}) {
178
178
  nextSteps: []
179
179
  };
180
180
  }
181
- const target = await resolveProjectShowInFixtureMode(context, workspace, options.project);
181
+ const target = await resolveRequiredProjectInFixtureMode(context, workspace, options.project, "git disconnect");
182
182
  const existingConnection = await context.stateStore.readRepositoryConnection(target.project.id);
183
183
  if (!existingConnection) throw repoNotConnectedError();
184
184
  await context.stateStore.clearRepositoryConnection(target.project.id);
@@ -193,6 +193,17 @@ async function runGitDisconnect(context, options = {}) {
193
193
  };
194
194
  }
195
195
  async function resolveProjectShowInRealMode(context, workspace, explicitProject) {
196
+ const client = await requireComputeAuth(context.runtime.env);
197
+ if (!client) throw authRequiredError();
198
+ return inspectProjectBinding({
199
+ context,
200
+ workspace,
201
+ explicitProject,
202
+ listProjects: () => listRealWorkspaceProjects(client, workspace),
203
+ commandName: "project show"
204
+ });
205
+ }
206
+ async function resolveRequiredProjectInRealMode(context, workspace, explicitProject, commandName) {
196
207
  const client = await requireComputeAuth(context.runtime.env);
197
208
  if (!client) throw authRequiredError();
198
209
  return resolveProjectTarget({
@@ -200,7 +211,7 @@ async function resolveProjectShowInRealMode(context, workspace, explicitProject)
200
211
  workspace,
201
212
  explicitProject,
202
213
  listProjects: () => listRealWorkspaceProjects(client, workspace),
203
- remember: false
214
+ commandName
204
215
  });
205
216
  }
206
217
  async function listRealProjectsForLink(context, workspace) {
@@ -209,12 +220,21 @@ async function listRealProjectsForLink(context, workspace) {
209
220
  return listRealWorkspaceProjects(client, workspace);
210
221
  }
211
222
  async function resolveProjectShowInFixtureMode(context, workspace, explicitProject) {
223
+ return inspectProjectBinding({
224
+ context,
225
+ workspace,
226
+ explicitProject,
227
+ listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
228
+ commandName: "project show"
229
+ });
230
+ }
231
+ async function resolveRequiredProjectInFixtureMode(context, workspace, explicitProject, commandName) {
212
232
  return resolveProjectTarget({
213
233
  context,
214
234
  workspace,
215
235
  explicitProject,
216
236
  listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
217
- remember: false
237
+ commandName
218
238
  });
219
239
  }
220
240
  async function listRealWorkspaceProjects(client, workspace) {
@@ -539,11 +559,5 @@ function repoConnectionFixForStatus(status) {
539
559
  if (status === 422) return "Make sure the GitHub App installation has access to this repository.";
540
560
  return "Re-run with --trace for the underlying API response details.";
541
561
  }
542
- function toProjectSummary(project) {
543
- return {
544
- id: project.id,
545
- name: project.name
546
- };
547
- }
548
562
  //#endregion
549
563
  export { listRealWorkspaceProjects, runGitConnect, runGitDisconnect, runProjectCreate, runProjectLink, runProjectList, runProjectShow };
@@ -1,73 +1,31 @@
1
1
  import { CliError } from "../../shell/errors.js";
2
- import { canPrompt } from "../../shell/runtime.js";
2
+ import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "./local-pin.js";
3
3
  import { readFile } from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  //#region src/lib/project/resolution.ts
6
6
  async function resolveProjectTarget(options) {
7
7
  const projects = await options.listProjects();
8
- const inferredName = await inferTargetName(options.context.runtime.cwd);
9
- if (options.explicitProject) return rememberIfRequested(options, resolveExplicitProject(options.explicitProject, projects, options.workspace), "explicit", {
10
- targetName: options.explicitProject,
11
- targetNameSource: "explicit"
8
+ const target = await resolveBoundProjectTarget(options, projects, { allowEnvProjectId: true });
9
+ if (target) return target;
10
+ throw await projectSetupRequiredError({
11
+ cwd: options.context.runtime.cwd,
12
+ projects,
13
+ commandName: options.commandName
12
14
  });
13
- const platformMapping = await resolveDurablePlatformMapping();
14
- if (platformMapping) return rememberIfRequested(options, platformMapping, "platform-mapping");
15
- let staleRemembered = false;
16
- if (!options.allowCreate) {
17
- const rememberedResult = await resolveRememberedProject(options, projects);
18
- if (rememberedResult.target) return rememberedResult.target;
19
- staleRemembered = rememberedResult.stale;
20
- }
21
- const packageName = inferredName.source === "package-name" ? inferredName.name : null;
22
- if (packageName) {
23
- const matches = projects.filter((project) => projectMatchesPackageName(project, packageName));
24
- if (matches.length === 1) return rememberIfRequested(options, matches[0], "package-name", {
25
- targetName: packageName,
26
- targetNameSource: "package-name"
27
- });
28
- if (matches.length > 1) return resolveAmbiguousProject(options, matches, packageName, "package-name");
29
- }
30
- if (options.allowCreate && options.createProject) {
31
- if (inferredName.name) {
32
- const existing = projects.filter((project) => projectMatchesPackageName(project, inferredName.name));
33
- if (existing.length === 1) return rememberIfRequested(options, existing[0], inferredName.source, {
34
- targetName: inferredName.name,
35
- targetNameSource: inferredName.source
36
- });
37
- if (existing.length > 1) return resolveAmbiguousProject(options, existing, inferredName.name, inferredName.source);
38
- return rememberIfRequested(options, await options.createProject(inferredName.name), "created", {
39
- targetName: inferredName.name,
40
- targetNameSource: inferredName.source
41
- });
42
- }
43
- }
44
- if (options.prompt && canPrompt(options.context) && projects.length > 0) return rememberIfRequested(options, await options.prompt.select({
45
- message: "Select a project",
46
- choices: sortProjects(projects).map((project) => ({
47
- label: `${project.name} (${project.id})`,
48
- value: project
49
- }))
50
- }), "prompt");
51
- if (staleRemembered && projects.length > 1) throw localStateStaleError();
52
- throw projectUnresolvedError();
53
- }
54
- async function resolveRememberedProject(options, projects) {
55
- const remembered = await options.context.stateStore.readRememberedProject(options.workspace.id);
56
- if (!remembered) return {
57
- target: null,
58
- stale: false
59
- };
60
- const matched = projects.find((project) => project.id === remembered.id);
61
- if (!matched) return {
62
- target: null,
63
- stale: true
64
- };
15
+ }
16
+ async function inspectProjectBinding(options) {
17
+ const projects = await options.listProjects();
18
+ const target = await resolveBoundProjectTarget(options, projects, { allowEnvProjectId: false });
19
+ if (target) return target;
65
20
  return {
66
- target: await rememberIfRequested(options, matched, "remembered-local", {
67
- targetName: remembered.name,
68
- targetNameSource: "remembered-local"
69
- }),
70
- stale: false
21
+ workspace: options.workspace,
22
+ project: null,
23
+ resolution: { projectSource: "unbound" },
24
+ ...await buildProjectSetupSuggestion({
25
+ cwd: options.context.runtime.cwd,
26
+ projects,
27
+ commandName: options.commandName ?? "project show"
28
+ })
71
29
  };
72
30
  }
73
31
  function projectNotFoundError(projectRef, workspace) {
@@ -99,26 +57,39 @@ function projectAmbiguousError(projectRef, matches) {
99
57
  nextSteps
100
58
  });
101
59
  }
102
- function projectUnresolvedError() {
60
+ function localStateStaleError() {
103
61
  return new CliError({
104
- code: "PROJECT_UNRESOLVED",
62
+ code: "LOCAL_STATE_STALE",
105
63
  domain: "project",
106
- summary: "No project is resolved for this directory",
107
- why: "No project could be resolved from explicit input, platform mappings, remembered local context, or package metadata.",
108
- fix: "Pass --project <id-or-name> on the command that needs a project, or add a package.json name that matches an accessible project.",
64
+ summary: "Local project binding is stale",
65
+ why: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
66
+ fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}, then choose a Project explicitly.`,
67
+ meta: { pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH },
109
68
  exitCode: 1,
110
- nextSteps: ["prisma-cli project list", "prisma-cli project show --project <id-or-name>"]
69
+ nextSteps: ["prisma-cli project list", "prisma-cli project link <id-or-name>"]
111
70
  });
112
71
  }
113
- function localStateStaleError() {
72
+ async function buildProjectSetupSuggestion(options) {
73
+ const suggestedName = await inferTargetName(options.cwd);
74
+ const candidates = sortProjects(options.projects.filter((project) => projectMatchesSuggestedName(project, suggestedName.name))).map(toProjectSummary);
75
+ return {
76
+ suggestedProjectName: suggestedName.name,
77
+ suggestedProjectNameSource: suggestedName.source,
78
+ candidates,
79
+ recoveryCommands: buildProjectRecoveryCommands(options.commandName)
80
+ };
81
+ }
82
+ async function projectSetupRequiredError(options) {
83
+ const suggestion = await buildProjectSetupSuggestion(options);
114
84
  return new CliError({
115
- code: "LOCAL_STATE_STALE",
85
+ code: "PROJECT_SETUP_REQUIRED",
116
86
  domain: "project",
117
- summary: "Remembered project context is stale",
118
- why: "The remembered project is no longer available in the selected workspace, and automatic resolution would be ambiguous.",
119
- fix: "Pass --project <id-or-name> to choose the project explicitly.",
87
+ summary: "Choose a Project before running this command",
88
+ 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.`,
89
+ fix: "Link the directory to an existing Project, or pass --project <id-or-name> for this command.",
90
+ meta: { ...suggestion },
120
91
  exitCode: 1,
121
- nextSteps: ["prisma-cli project list"]
92
+ nextSteps: ["prisma-cli project list", ...suggestion.recoveryCommands]
122
93
  });
123
94
  }
124
95
  async function readPackageName(cwd) {
@@ -157,33 +128,46 @@ function resolveExplicitProject(projectRef, projects, workspace) {
157
128
  if (matches.length > 1) throw projectAmbiguousError(projectRef, matches);
158
129
  throw projectNotFoundError(projectRef, workspace);
159
130
  }
160
- function resolveAmbiguousProject(options, matches, projectRef, targetNameSource) {
161
- if (options.prompt && canPrompt(options.context)) return options.prompt.select({
162
- message: "Select a project",
163
- choices: sortProjects(matches).map((project) => ({
164
- label: `${project.name} (${project.id})`,
165
- value: project
166
- }))
167
- }).then((selected) => rememberIfRequested(options, selected, "prompt", {
168
- targetName: projectRef,
169
- targetNameSource
170
- }));
171
- throw projectAmbiguousError(projectRef, matches);
172
- }
173
- function projectMatchesPackageName(project, packageName) {
174
- return project.id === packageName || project.name === packageName || project.slug === packageName;
131
+ function projectMatchesSuggestedName(project, suggestedName) {
132
+ return project.id === suggestedName || project.name === suggestedName || project.slug === suggestedName;
175
133
  }
176
134
  async function resolveDurablePlatformMapping() {
177
135
  return null;
178
136
  }
179
- async function rememberIfRequested(options, project, projectSource, resolutionDetails) {
180
- if (options.remember) await options.context.stateStore.setRememberedProject({
181
- id: project.id,
182
- name: project.name,
183
- workspaceId: options.workspace.id
137
+ async function resolveBoundProjectTarget(options, projects, settings) {
138
+ if (options.explicitProject) return resolvedTarget(options.workspace, resolveExplicitProject(options.explicitProject, projects, options.workspace), "explicit", {
139
+ targetName: options.explicitProject,
140
+ targetNameSource: "explicit"
184
141
  });
142
+ if (settings.allowEnvProjectId && options.envProjectId) {
143
+ const project = projects.find((candidate) => candidate.id === options.envProjectId);
144
+ if (!project) throw projectNotFoundError(options.envProjectId, options.workspace);
145
+ return resolvedTarget(options.workspace, project, "env", {
146
+ targetName: options.envProjectId,
147
+ targetNameSource: "env"
148
+ });
149
+ }
150
+ const localPin = await readLocalResolutionPin(options.context.runtime.cwd);
151
+ if (localPin.kind === "invalid") throw localStateStaleError();
152
+ if (localPin.kind === "present") {
153
+ if (localPin.pin.workspaceId !== options.workspace.id) throw localStateStaleError();
154
+ const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
155
+ if (!project) throw localStateStaleError();
156
+ return resolvedTarget(options.workspace, project, "local-pin", {
157
+ targetName: project.name,
158
+ targetNameSource: "local-pin"
159
+ });
160
+ }
161
+ const platformMapping = await resolveDurablePlatformMapping();
162
+ if (platformMapping && platformMapping.workspace.id === options.workspace.id) return resolvedTarget(options.workspace, platformMapping, "platform-mapping", {
163
+ targetName: platformMapping.name,
164
+ targetNameSource: "platform-mapping"
165
+ });
166
+ return null;
167
+ }
168
+ function resolvedTarget(workspace, project, projectSource, resolutionDetails) {
185
169
  return {
186
- workspace: options.workspace,
170
+ workspace,
187
171
  project: toProjectSummary(project),
188
172
  resolution: {
189
173
  projectSource,
@@ -191,6 +175,11 @@ async function rememberIfRequested(options, project, projectSource, resolutionDe
191
175
  }
192
176
  };
193
177
  }
178
+ function buildProjectRecoveryCommands(commandName) {
179
+ const commands = ["prisma-cli project link <id-or-name>"];
180
+ if (commandName) commands.push(`prisma-cli ${commandName} --project <id-or-name>`);
181
+ return commands;
182
+ }
194
183
  function toProjectSummary(project) {
195
184
  return {
196
185
  id: project.id,
@@ -198,4 +187,4 @@ function toProjectSummary(project) {
198
187
  };
199
188
  }
200
189
  //#endregion
201
- export { inferTargetName, projectAmbiguousError, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects };
190
+ export { inferTargetName, inspectProjectBinding, projectAmbiguousError, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects };
@@ -1,6 +1,6 @@
1
1
  import { CliError, usageError } from "../../shell/errors.js";
2
- import { projectAmbiguousError, projectNotFoundError } from "./resolution.js";
3
2
  import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, ensureLocalResolutionPinGitignore, writeLocalResolutionPin } from "./local-pin.js";
3
+ import { projectAmbiguousError, projectNotFoundError } from "./resolution.js";
4
4
  //#region src/lib/project/setup.ts
5
5
  function isValidProjectSetupName(projectName) {
6
6
  return projectName.trim().length > 0;
@@ -30,8 +30,35 @@ function serializeProjectList(result) {
30
30
  });
31
31
  }
32
32
  function renderProjectShow(context, descriptor, result) {
33
+ if (result.project === null) return renderShow({
34
+ title: "No Project linked to this directory.",
35
+ descriptor,
36
+ fields: [
37
+ {
38
+ key: "workspace",
39
+ value: result.workspace.name
40
+ },
41
+ {
42
+ key: "project",
43
+ value: "unbound",
44
+ tone: "warning"
45
+ },
46
+ {
47
+ key: "suggested",
48
+ value: `${result.suggestedProjectName} (${formatSuggestionSource(result.suggestedProjectNameSource)})`
49
+ },
50
+ {
51
+ key: "match",
52
+ value: formatCandidateList(result.candidates)
53
+ },
54
+ {
55
+ key: "next",
56
+ value: result.recoveryCommands[0] ?? "prisma-cli project link <id-or-name>"
57
+ }
58
+ ]
59
+ }, context.ui);
33
60
  return renderShow({
34
- title: "Showing the project Prisma resolves for this directory.",
61
+ title: "Showing this directory's Project binding.",
35
62
  descriptor,
36
63
  fields: [
37
64
  {
@@ -117,13 +144,21 @@ function formatProjectSource(source) {
117
144
  case "env": return "environment";
118
145
  case "local-pin": return "local pin";
119
146
  case "platform-mapping": return "platform mapping";
120
- case "remembered-local": return "remembered local context";
121
- case "package-name": return "package name";
122
- case "directory-name": return "directory name";
123
147
  case "created": return "created";
124
148
  case "prompt": return "prompt";
149
+ case "unbound": return "unbound";
125
150
  }
126
151
  }
152
+ function formatSuggestionSource(source) {
153
+ switch (source) {
154
+ case "package-name": return "package name";
155
+ case "directory-name": return "directory name";
156
+ }
157
+ }
158
+ function formatCandidateList(candidates) {
159
+ if (candidates.length === 0) return "none";
160
+ return candidates.map((project) => project.name).join(", ");
161
+ }
127
162
  function formatGitConnectionDetail(status) {
128
163
  switch (status) {
129
164
  case "active": return "GitHub branch automation is active for this project.";
@@ -95,7 +95,7 @@ const DESCRIPTORS = [
95
95
  "project",
96
96
  "show"
97
97
  ],
98
- description: "Show which project is active for this directory",
98
+ description: "Show this directory's Project binding",
99
99
  examples: ["prisma-cli project show", "prisma-cli project show --project proj_123 --json"]
100
100
  },
101
101
  {
@@ -212,7 +212,8 @@ const DESCRIPTORS = [
212
212
  "prisma-cli app deploy --app my-app --env DATABASE_URL=postgresql://example",
213
213
  "prisma-cli app deploy --app my-app --framework nextjs --http-port 3000",
214
214
  "prisma-cli app deploy --branch feat-login --framework hono",
215
- "pnpm dlx skills@latest add prisma/prisma-cli/skills#cli-v<cli-version> --all"
215
+ "pnpm dlx skills@latest add prisma/prisma-cli/skills#cli-v<cli-version> --all",
216
+ "prisma-cli app deploy --framework bun --entry src/server.ts"
216
217
  ]
217
218
  },
218
219
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/cli",
3
- "version": "3.0.0-dev.39.1",
3
+ "version": "3.0.0-dev.41.1",
4
4
  "description": "Command-line interface for the Prisma Developer Platform.",
5
5
  "type": "module",
6
6
  "bin": {