@prisma/cli 3.0.0-beta.11 → 3.0.0-beta.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,26 +2,27 @@ import { SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
2
2
  import { FileTokenStorage } from "../adapters/token-storage.js";
3
3
  import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../shell/errors.js";
4
4
  import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
5
+ import { DEFAULT_REGION } from "../lib/app/app-interaction.js";
6
+ import { PRISMA_APP_CONFIG_FILENAME, detectLegacyBuildSettings, resolveConfiguredAppBuildSettings, resolveInferredAppBuildSettings } from "../lib/app/build-settings.js";
7
+ import { APP_BUILD_TYPES, RESOLVED_APP_BUILD_TYPES, executeAppBuild } from "../lib/app/build.js";
8
+ import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
9
+ import { DomainApiError, createAppProvider } from "../lib/app/app-provider.js";
5
10
  import { renderCommandHeader } from "../shell/ui.js";
6
11
  import { canPrompt } from "../shell/runtime.js";
7
12
  import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
8
13
  import { formatCommandArgument } from "../shell/command-arguments.js";
9
- import { buildProjectSetupNextActions, inferTargetName, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
14
+ import { buildProjectSetupNextActions, inferTargetName, localProjectWorkspaceMismatchError, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
10
15
  import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
11
16
  import { maybeSetupBranchDatabase } from "../lib/app/branch-database-deploy.js";
12
17
  import { readBunPackageEntrypoint, readBunPackageJson } from "../lib/app/bun-project.js";
13
18
  import { COMPUTE_CONFIG_FILENAME as COMPUTE_CONFIG_FILENAME$1, ComputeConfigTargetRequiredError, computeConfigErrorToCliError, computeFrameworkToBuildType, computeTargetAppDir, inferComputeTargetFromCwd, loadComputeConfig as loadComputeConfig$1, mergeComputeDeployInputs, mergeComputeLocalInputs, selectComputeDeployTarget } from "../lib/app/compute-config.js";
14
19
  import { renderDeployOutputRows, renderDeploySettingsPreview } from "../lib/app/deploy-output.js";
15
20
  import { describeDeployAllFailure, perAppInputsForDeployAll, planAppDeploy } from "../lib/app/deploy-plan.js";
21
+ import { createDeployProgress, createDeployProgressState, createPromoteProgress } from "../lib/app/deploy-progress.js";
16
22
  import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
17
- import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
18
23
  import { DEFAULT_LOCAL_DEV_PORT, runLocalApp } from "../lib/app/local-dev.js";
19
- import { PRISMA_APP_CONFIG_FILENAME, detectLegacyBuildSettings, resolveConfiguredPreviewBuildSettings, resolveInferredPreviewBuildSettings } from "../lib/app/preview-build-settings.js";
20
- import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild } from "../lib/app/preview-build.js";
21
- import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
22
- import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress } from "../lib/app/preview-progress.js";
23
- import { PreviewDomainApiError, createPreviewAppProvider } from "../lib/app/preview-provider.js";
24
24
  import { enforceProductionDeployGate } from "../lib/app/production-deploy-gate.js";
25
+ import { resolveReadBranch } from "../lib/app/read-branch.js";
25
26
  import { requireComputeAuth } from "../lib/auth/guard.js";
26
27
  import { readAuthState } from "../lib/auth/auth-ops.js";
27
28
  import { readLocalGitBranch } from "../lib/git/local-branch.js";
@@ -60,7 +61,7 @@ async function runAppBuild(context, options) {
60
61
  }
61
62
  assertSupportedEntrypoint(buildType, merged.entrypoint, "build");
62
63
  if (compute.target?.build && buildType !== "auto") assertConfigBackedBuildSettings(buildType);
63
- const buildSettings = compute.config && compute.target?.build && isConfigBackedBuildType(buildType) ? (await resolveConfiguredPreviewBuildSettings({
64
+ const buildSettings = compute.config && compute.target?.build && isConfigBackedBuildType(buildType) ? (await resolveConfiguredAppBuildSettings({
64
65
  appPath: appDir,
65
66
  buildType,
66
67
  configured: compute.target.build,
@@ -68,7 +69,7 @@ async function runAppBuild(context, options) {
68
69
  signal: context.runtime.signal
69
70
  })).settings : void 0;
70
71
  try {
71
- const { artifact, buildType: actualBuildType } = await executePreviewBuild({
72
+ const { artifact, buildType: actualBuildType } = await executeAppBuild({
72
73
  appPath: appDir,
73
74
  entrypoint: merged.entrypoint,
74
75
  buildType,
@@ -86,7 +87,7 @@ async function runAppBuild(context, options) {
86
87
  nextSteps: ["prisma-cli app deploy"]
87
88
  };
88
89
  } catch (error) {
89
- if (buildType === "auto" && isAutoBuildDetectionError(error)) throw usageError("App build requires an explicit framework when detection is ambiguous", `This preview auto-detects clear project shapes for ${RESOLVED_PREVIEW_BUILD_TYPES.map(formatBuildTypeName).join(", ")}.`, "Pass a supported --build-type value, or pass --entry <path> for a Bun app.", getBuildTypeExamples("build"), "app");
90
+ if (buildType === "auto" && isAutoBuildDetectionError(error)) throw usageError("App build requires an explicit framework when detection is ambiguous", `This preview auto-detects clear project shapes for ${RESOLVED_APP_BUILD_TYPES.map(formatBuildTypeName).join(", ")}.`, "Pass a supported --build-type value, or pass --entry <path> for a Bun app.", getBuildTypeExamples("build"), "app");
90
91
  throw buildFailedError("Local app build failed", error);
91
92
  }
92
93
  }
@@ -328,13 +329,13 @@ async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
328
329
  assertSupportedEntrypoint(buildType, merged.entrypoint?.value, "deploy");
329
330
  const entrypoint = await resolveDeployEntrypoint(appDir, framework, merged.entrypoint?.value, context.runtime.signal);
330
331
  if (computeConfig.target?.build) assertConfigBackedBuildSettings(buildType);
331
- const buildSettingsResolution = computeConfig.config && computeConfig.target?.build && isConfigBackedBuildType(buildType) ? await resolveConfiguredPreviewBuildSettings({
332
+ const buildSettingsResolution = computeConfig.config && computeConfig.target?.build && isConfigBackedBuildType(buildType) ? await resolveConfiguredAppBuildSettings({
332
333
  appPath: appDir,
333
334
  buildType,
334
335
  configured: computeConfig.target.build,
335
336
  configPath: computeConfig.config.configPath,
336
337
  signal: context.runtime.signal
337
- }) : await resolveInferredPreviewBuildSettings({
338
+ }) : await resolveInferredAppBuildSettings({
338
339
  appPath: appDir,
339
340
  buildType,
340
341
  signal: context.runtime.signal
@@ -348,7 +349,7 @@ async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
348
349
  firstProductionDeploy: productionDeployGate.firstProductionDeploy,
349
350
  projectDir
350
351
  });
351
- const progressState = createPreviewDeployProgressState();
352
+ const progressState = createDeployProgressState();
352
353
  const deployStartedAt = Date.now();
353
354
  const deployResult = await provider.deployApp({
354
355
  cwd: appDir,
@@ -364,7 +365,7 @@ async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
364
365
  envVars,
365
366
  interaction: void 0,
366
367
  signal: context.runtime.signal,
367
- progress: createPreviewDeployProgress(context.output.stderr, context.ui, !context.flags.json && !context.flags.quiet, progressState)
368
+ progress: createDeployProgress(context.output.stderr, context.ui, !context.flags.json && !context.flags.quiet, progressState)
368
369
  }).catch((error) => {
369
370
  throw appDeployFailedError(error, progressState);
370
371
  });
@@ -866,7 +867,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef, configT
866
867
  appId: selectedApp.id,
867
868
  deploymentId: targetDeployment.id,
868
869
  signal: context.runtime.signal,
869
- progress: createPreviewPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
870
+ progress: createPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
870
871
  }).catch((error) => {
871
872
  throw deployFailedError("Failed to promote deployment", error, ["prisma-cli app list-deploys"]);
872
873
  });
@@ -914,7 +915,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef, config
914
915
  appId: selectedApp.id,
915
916
  deploymentId: targetDeployment.id,
916
917
  signal: context.runtime.signal,
917
- progress: createPreviewPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
918
+ progress: createPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
918
919
  }).catch((error) => {
919
920
  throw deployFailedError("Failed to roll back deployment", error, ["prisma-cli app list-deploys"]);
920
921
  });
@@ -1106,7 +1107,7 @@ async function confirmDomainRemoval(context, target, hostname) {
1106
1107
  })) throw usageError("Custom domain removal canceled", "The command was canceled before the domain was detached.", "Rerun the command and confirm removal, or pass --yes.", [`prisma-cli app domain remove ${hostname} --app ${target.app.name} --yes`], "app");
1107
1108
  }
1108
1109
  function domainCommandError(command, error, hostname) {
1109
- if (error instanceof PreviewDomainApiError) {
1110
+ if (error instanceof DomainApiError) {
1110
1111
  if (command === "add" && (error.status === 400 || error.status === 422) && isDomainDnsError(error)) return domainDnsNotConfiguredError(hostname, error);
1111
1112
  if (command === "add" && error.status === 400) return new CliError({
1112
1113
  code: "DOMAIN_HOSTNAME_INVALID",
@@ -1287,7 +1288,7 @@ async function resolveDeployAppSelection(context, projectId, apps, options) {
1287
1288
  };
1288
1289
  return {
1289
1290
  appName: options.explicitAppName,
1290
- region: PREVIEW_DEFAULT_REGION,
1291
+ region: DEFAULT_REGION,
1291
1292
  displayName: options.explicitAppName,
1292
1293
  annotation: "set by --app",
1293
1294
  firstDeploy: options.firstDeploy
@@ -1316,7 +1317,7 @@ async function resolveDeployAppSelection(context, projectId, apps, options) {
1316
1317
  };
1317
1318
  return {
1318
1319
  appName: configName.value,
1319
- region: PREVIEW_DEFAULT_REGION,
1320
+ region: DEFAULT_REGION,
1320
1321
  displayName: configName.value,
1321
1322
  annotation: configName.annotation,
1322
1323
  firstDeploy: options.firstDeploy
@@ -1334,7 +1335,7 @@ async function resolveDeployAppSelection(context, projectId, apps, options) {
1334
1335
  };
1335
1336
  return {
1336
1337
  appName: inferredName.name,
1337
- region: PREVIEW_DEFAULT_REGION,
1338
+ region: DEFAULT_REGION,
1338
1339
  displayName: inferredName.name,
1339
1340
  annotation: inferredName.source === "package-name" ? "created from package.json" : "created from directory name",
1340
1341
  firstDeploy: options.firstDeploy
@@ -1366,7 +1367,7 @@ async function resolveAmbiguousDeployApp(context, matches, targetName, firstDepl
1366
1367
  if (selected === cancel) throw usageError("App selection canceled", "The command was canceled before an app was selected.", "Re-run the command and choose an app, or pass --app <name>.", ["prisma-cli app deploy --app <name>"], "app");
1367
1368
  if (selected === createNew) return {
1368
1369
  appName: targetName,
1369
- region: PREVIEW_DEFAULT_REGION,
1370
+ region: DEFAULT_REGION,
1370
1371
  displayName: targetName,
1371
1372
  annotation: "created from package.json",
1372
1373
  firstDeploy
@@ -1531,7 +1532,7 @@ async function requirePreviewAppProviderWithClient(context) {
1531
1532
  if (!client) throw authRequiredError(["prisma-cli auth login"]);
1532
1533
  return {
1533
1534
  client,
1534
- provider: createPreviewAppProvider(client, createPreviewLogAuthOptions(context.runtime.env, context.runtime.signal))
1535
+ provider: createAppProvider(client, createPreviewLogAuthOptions(context.runtime.env, context.runtime.signal))
1535
1536
  };
1536
1537
  }
1537
1538
  function createPreviewLogAuthOptions(env, signal) {
@@ -1584,13 +1585,18 @@ async function resolveProjectContext(context, client, explicitProject, options)
1584
1585
  });
1585
1586
  if (resolvedResult.isErr()) throw projectResolutionErrorToCliError(resolvedResult.error);
1586
1587
  const resolved = resolvedResult.value;
1587
- const branch = options?.branch ?? await resolveDeployBranch(context, void 0);
1588
+ const requested = options?.branch ?? await resolveDeployBranch(context, void 0);
1589
+ const remoteBranch = options?.branch ? null : await resolveReadBranch(client, {
1590
+ projectId: resolved.project.id,
1591
+ branchName: requested.name,
1592
+ signal: context.runtime.signal
1593
+ });
1588
1594
  return {
1589
1595
  ...resolved,
1590
- branch: {
1596
+ branch: remoteBranch ?? {
1591
1597
  id: null,
1592
- name: branch.name,
1593
- kind: toBranchKind(branch.name)
1598
+ name: requested.name,
1599
+ kind: toBranchKind(requested.name)
1594
1600
  }
1595
1601
  };
1596
1602
  }
@@ -1638,7 +1644,11 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1638
1644
  }
1639
1645
  const localPin = options.localPin;
1640
1646
  if (localPin.kind === "present") {
1641
- if (localPin.pin.workspaceId !== workspace.id) throw localResolutionPinStaleError();
1647
+ if (localPin.pin.workspaceId !== workspace.id) throw localProjectWorkspaceMismatchError({
1648
+ pinnedWorkspaceId: localPin.pin.workspaceId,
1649
+ pinnedProjectId: localPin.pin.projectId,
1650
+ activeWorkspace: workspace
1651
+ });
1642
1652
  const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
1643
1653
  if (!project) throw localResolutionPinStaleError();
1644
1654
  return withRemoteDeployBranch(provider, {
@@ -2139,13 +2149,13 @@ async function readCurrentWorkspaceId(context) {
2139
2149
  function normalizeBuildType(requestedBuildType) {
2140
2150
  if (!requestedBuildType) return "auto";
2141
2151
  if (isPreviewBuildType(requestedBuildType)) return requestedBuildType;
2142
- throw usageError(`Unsupported build type "${requestedBuildType}"`, `Only ${PREVIEW_BUILD_TYPES.join(", ")} are supported in the current preview.`, "Pass a supported --build-type value.", getBuildTypeExamples("build"), "app");
2152
+ throw usageError(`Unsupported build type "${requestedBuildType}"`, `Only ${APP_BUILD_TYPES.join(", ")} are supported in the current preview.`, "Pass a supported --build-type value.", getBuildTypeExamples("build"), "app");
2143
2153
  }
2144
2154
  function isPreviewBuildType(value) {
2145
- return PREVIEW_BUILD_TYPES.includes(value);
2155
+ return APP_BUILD_TYPES.includes(value);
2146
2156
  }
2147
2157
  function getBuildTypeExamples(commandName) {
2148
- return RESOLVED_PREVIEW_BUILD_TYPES.map((buildType) => {
2158
+ return RESOLVED_APP_BUILD_TYPES.map((buildType) => {
2149
2159
  return `prisma-cli app ${commandName} --build-type ${buildType}${buildType === "bun" ? " --entry server.ts" : ""}`;
2150
2160
  });
2151
2161
  }
@@ -2389,6 +2399,7 @@ function formatBuildTypeName(buildType) {
2389
2399
  case "nextjs": return "Next.js";
2390
2400
  case "nuxt": return "Nuxt";
2391
2401
  case "astro": return "Astro";
2402
+ case "nestjs": return "NestJS";
2392
2403
  case "tanstack-start": return "TanStack Start";
2393
2404
  case "bun": return "Bun";
2394
2405
  case "auto": return "Auto";
@@ -1,4 +1,6 @@
1
- import { authRequiredError, usageError } from "../shell/errors.js";
1
+ import { SERVICE_TOKEN_ENV_VAR } from "../lib/auth/client.js";
2
+ import { FileTokenStorage, WorkspaceSelectionError } from "../adapters/token-storage.js";
3
+ import { authRequiredError, usageError, workspaceAmbiguousError, workspaceNotAuthenticatedError, workspaceSwitchUnavailableError } from "../shell/errors.js";
2
4
  import { canPrompt } from "../shell/runtime.js";
3
5
  import { performLogin, performLogout, readAuthState } from "../lib/auth/auth-ops.js";
4
6
  import { createAuthUseCases } from "../use-cases/auth.js";
@@ -30,6 +32,40 @@ async function runAuthWhoAmI(context) {
30
32
  else result = await createAuthUseCases(createCliUseCaseGateways(context)).whoami();
31
33
  return createAuthSuccess("auth.whoami", result, result.authenticated ? [] : ["prisma-cli auth login"]);
32
34
  }
35
+ async function runAuthWorkspaceList(context) {
36
+ const result = isRealMode(context) ? await listRealAuthWorkspaces(context) : await createAuthUseCases(createCliUseCaseGateways(context)).listWorkspaces();
37
+ return {
38
+ command: "auth.workspace.list",
39
+ result,
40
+ warnings: [],
41
+ nextSteps: result.workspaces.length === 0 ? ["prisma-cli auth login"] : []
42
+ };
43
+ }
44
+ async function runAuthWorkspaceUse(context, workspaceRef) {
45
+ if (!workspaceRef?.trim()) throw usageError("Workspace required", "auth workspace use needs a workspace id or cached workspace name.", "Pass a workspace from prisma-cli auth workspace list.", ["prisma-cli auth workspace list"], "auth");
46
+ return {
47
+ command: "auth.workspace.use",
48
+ result: isRealMode(context) ? await useRealAuthWorkspace(context, workspaceRef) : await createAuthUseCases(createCliUseCaseGateways(context)).useWorkspace(workspaceRef),
49
+ warnings: [],
50
+ nextSteps: ["prisma-cli auth whoami", "prisma-cli project list"]
51
+ };
52
+ }
53
+ async function runAuthWorkspaceSelect(context) {
54
+ return {
55
+ ...await runAuthWorkspaceUse(context, await selectWorkspaceSession(context)),
56
+ command: "auth.workspace.select"
57
+ };
58
+ }
59
+ async function runAuthWorkspaceLogout(context, workspaceRef) {
60
+ if (!workspaceRef?.trim()) throw usageError("Workspace required", "auth workspace logout needs a workspace id or cached workspace name.", "Pass a workspace from prisma-cli auth workspace list.", ["prisma-cli auth workspace list"], "auth");
61
+ const result = isRealMode(context) ? await logoutRealAuthWorkspace(context, workspaceRef) : await createAuthUseCases(createCliUseCaseGateways(context)).logoutWorkspace(workspaceRef);
62
+ return {
63
+ command: "auth.workspace.logout",
64
+ result,
65
+ warnings: [],
66
+ nextSteps: result.activeWorkspace ? ["prisma-cli auth workspace list"] : ["prisma-cli auth workspace list", "prisma-cli auth workspace use <id>"]
67
+ };
68
+ }
33
69
  async function requireAuthenticatedAuthState(context) {
34
70
  if (isRealMode(context)) {
35
71
  const current = await readAuthState(context.runtime.env, context.runtime.signal);
@@ -44,6 +80,108 @@ async function requireAuthenticatedAuthState(context) {
44
80
  if (!canPrompt(context)) throw authRequiredError();
45
81
  return loginWithSelectionFlow(context, useCases, {});
46
82
  }
83
+ async function listRealAuthWorkspaces(context) {
84
+ const rawServiceToken = context.runtime.env[SERVICE_TOKEN_ENV_VAR];
85
+ const localWorkspaces = await new FileTokenStorage(context.runtime.env, context.runtime.signal).listWorkspaces();
86
+ if (rawServiceToken !== void 0) {
87
+ const authState = await readAuthState(context.runtime.env, context.runtime.signal);
88
+ return {
89
+ authSource: authState.authenticated ? "service_token" : "none",
90
+ activeWorkspace: authState.workspace,
91
+ workspaces: [...authState.workspace ? [{
92
+ ...authState.workspace,
93
+ credentialWorkspaceId: null,
94
+ active: true,
95
+ source: "service_token",
96
+ switchable: false,
97
+ lastSeenAt: null
98
+ }] : [], ...localWorkspaces.map((workspace) => ({
99
+ ...toAuthWorkspace(workspace),
100
+ credentialWorkspaceId: workspace.credentialWorkspaceId,
101
+ active: false,
102
+ source: "oauth",
103
+ switchable: false,
104
+ lastSeenAt: workspace.lastSeenAt
105
+ }))]
106
+ };
107
+ }
108
+ const active = localWorkspaces.find((workspace) => workspace.active) ?? null;
109
+ return {
110
+ authSource: localWorkspaces.length > 0 ? "oauth" : "none",
111
+ activeWorkspace: active ? toAuthWorkspace(active) : null,
112
+ workspaces: localWorkspaces.map((workspace) => ({
113
+ ...toAuthWorkspace(workspace),
114
+ credentialWorkspaceId: workspace.credentialWorkspaceId,
115
+ active: workspace.active,
116
+ source: "oauth",
117
+ switchable: true,
118
+ lastSeenAt: workspace.lastSeenAt
119
+ }))
120
+ };
121
+ }
122
+ async function useRealAuthWorkspace(context, workspaceRef) {
123
+ if (context.runtime.env["PRISMA_SERVICE_TOKEN"] !== void 0) throw workspaceSwitchUnavailableError();
124
+ const storage = new FileTokenStorage(context.runtime.env, context.runtime.signal);
125
+ try {
126
+ const result = await storage.useWorkspace(workspaceRef);
127
+ return {
128
+ previousWorkspace: result.previous ? toAuthWorkspace(result.previous) : null,
129
+ workspace: toAuthWorkspace(result.selected)
130
+ };
131
+ } catch (error) {
132
+ if (error instanceof WorkspaceSelectionError) {
133
+ if (error.reason === "ambiguous") throw workspaceAmbiguousError(error.workspaceRef ?? workspaceRef, error.matches.map((match) => ({
134
+ id: match.id,
135
+ name: match.name,
136
+ credentialWorkspaceId: match.credentialWorkspaceId
137
+ })));
138
+ throw workspaceNotAuthenticatedError(error.workspaceRef ?? workspaceRef);
139
+ }
140
+ throw error;
141
+ }
142
+ }
143
+ async function logoutRealAuthWorkspace(context, workspaceRef) {
144
+ const storage = new FileTokenStorage(context.runtime.env, context.runtime.signal);
145
+ try {
146
+ const result = await storage.logoutWorkspace(workspaceRef);
147
+ return {
148
+ workspace: toAuthWorkspace(result.workspace),
149
+ wasActive: result.wasActive,
150
+ activeWorkspace: result.activeWorkspace ? toAuthWorkspace(result.activeWorkspace) : null
151
+ };
152
+ } catch (error) {
153
+ if (error instanceof WorkspaceSelectionError) {
154
+ if (error.reason === "ambiguous") throw workspaceAmbiguousError(error.workspaceRef ?? workspaceRef, error.matches.map((match) => ({
155
+ id: match.id,
156
+ name: match.name,
157
+ credentialWorkspaceId: match.credentialWorkspaceId
158
+ })));
159
+ throw workspaceNotAuthenticatedError(error.workspaceRef ?? workspaceRef);
160
+ }
161
+ throw error;
162
+ }
163
+ }
164
+ async function selectWorkspaceSession(context) {
165
+ const realMode = isRealMode(context);
166
+ if (realMode && context.runtime.env["PRISMA_SERVICE_TOKEN"] !== void 0) throw workspaceSwitchUnavailableError();
167
+ const workspaces = (realMode ? await listRealAuthWorkspaces(context) : await createAuthUseCases(createCliUseCaseGateways(context)).listWorkspaces()).workspaces.filter((workspace) => workspace.switchable);
168
+ if (workspaces.length === 0) throw usageError("No authenticated workspaces", "There are no local OAuth workspace sessions to select.", "Run prisma-cli auth login and authorize a workspace.", ["prisma-cli auth login"], "auth");
169
+ if (workspaces.length === 1) return workspaces[0].id;
170
+ if (!canPrompt(context)) throw usageError("Interactive workspace selection unavailable", "auth workspace select needs an interactive terminal when more than one workspace is available.", "Run prisma-cli auth workspace use <id-or-name> with a workspace from prisma-cli auth workspace list.", ["prisma-cli auth workspace list"], "auth");
171
+ return (await createSelectPromptPort(context).select({
172
+ message: "Select a workspace",
173
+ choices: workspaces.map((workspace) => ({
174
+ label: `${workspace.name} (${workspace.id})${workspace.active ? " active" : ""}`,
175
+ value: workspace
176
+ }))
177
+ })).id;
178
+ }
179
+ function toAuthWorkspace(workspace) {
180
+ return {
181
+ id: workspace.id,
182
+ name: workspace.name
183
+ };
184
+ }
47
185
  async function loginWithSelectionFlow(context, useCases, options) {
48
186
  const selection = await resolveLoginSelection(useCases, canPrompt(context) ? createSelectPromptPort(context) : null, options);
49
187
  return useCases.login(selection);
@@ -104,4 +242,4 @@ function createAuthSuccess(command, result, nextSteps) {
104
242
  };
105
243
  }
106
244
  //#endregion
107
- export { requireAuthenticatedAuthState, runAuthLogin, runAuthLogout, runAuthWhoAmI };
245
+ export { requireAuthenticatedAuthState, runAuthLogin, runAuthLogout, runAuthWhoAmI, runAuthWorkspaceList, runAuthWorkspaceLogout, runAuthWorkspaceSelect, runAuthWorkspaceUse };
@@ -1,11 +1,11 @@
1
1
  import { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError } from "../shell/errors.js";
2
+ import { createAppProvider } from "../lib/app/app-provider.js";
2
3
  import { renderSummaryLine } from "../shell/ui.js";
3
4
  import { canPrompt } from "../shell/runtime.js";
4
5
  import { readLocalResolutionPin } from "../lib/project/local-pin.js";
5
6
  import { formatCommandArgument } from "../shell/command-arguments.js";
6
7
  import { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, projectResolutionErrorToCliError, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
7
8
  import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
8
- import { createPreviewAppProvider } from "../lib/app/preview-provider.js";
9
9
  import { requireComputeAuth } from "../lib/auth/guard.js";
10
10
  import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
11
11
  import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
@@ -104,7 +104,7 @@ async function runProjectCreate(context, projectName) {
104
104
  if (!isRealMode(context)) throw featureUnavailableError("Project create is not available in fixture mode", "Creating Projects requires live platform integration.", "Rerun without fixture mode enabled to create a Project.", ["prisma-cli auth login"], "project");
105
105
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
106
106
  if (!client) throw authRequiredError();
107
- const provider = createPreviewAppProvider(client);
107
+ const provider = createAppProvider(client);
108
108
  const name = projectName.trim();
109
109
  const created = await provider.createProject({
110
110
  name,
@@ -136,7 +136,7 @@ async function runProjectLink(context, projectRef) {
136
136
  if (isRealMode(context)) {
137
137
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
138
138
  if (!client) throw authRequiredError();
139
- provider = createPreviewAppProvider(client);
139
+ provider = createAppProvider(client);
140
140
  projects = await listRealWorkspaceProjects(client, workspace, context.runtime.signal);
141
141
  } else projects = listFixtureWorkspaceProjects(context, workspace);
142
142
  let result;
@@ -0,0 +1,5 @@
1
+ import "../../shell/prompt.js";
2
+ //#region src/lib/app/app-interaction.ts
3
+ const DEFAULT_REGION = "eu-central-1";
4
+ //#endregion
5
+ export { DEFAULT_REGION };
@@ -1,22 +1,22 @@
1
+ import { createBranchDatabase, createEnvironmentVariable, deleteBranchDatabase, deleteEnvironmentVariable, listEnvironmentVariables, updateEnvironmentVariable } from "./branch-database-api.js";
2
+ import { AppBuildStrategy } from "./build.js";
1
3
  import { envVarNames } from "./env-vars.js";
2
- import { PreviewBuildStrategy } from "./preview-build.js";
3
- import { createBranchDatabase, createEnvironmentVariable, deleteBranchDatabase, deleteEnvironmentVariable, listEnvironmentVariables, updateEnvironmentVariable } from "./preview-branch-database.js";
4
4
  import path from "node:path";
5
5
  import { ApiError, CancelledError, ComputeClient, streamLogs } from "@prisma/compute-sdk";
6
- //#region src/lib/app/preview-provider.ts
7
- var PreviewDomainApiError = class extends Error {
6
+ //#region src/lib/app/app-provider.ts
7
+ var DomainApiError = class extends Error {
8
8
  status;
9
9
  code;
10
10
  hint;
11
11
  constructor(options) {
12
12
  super(`${options.summary}: ${options.message}${options.hint ? ` ${options.hint}` : ""}`);
13
- this.name = "PreviewDomainApiError";
13
+ this.name = "DomainApiError";
14
14
  this.status = options.status;
15
15
  this.code = options.code ?? null;
16
16
  this.hint = options.hint ?? null;
17
17
  }
18
18
  };
19
- function createPreviewAppProvider(client, options) {
19
+ function createAppProvider(client, options) {
20
20
  const sdk = new ComputeClient(client);
21
21
  return {
22
22
  async createProject(options) {
@@ -161,7 +161,7 @@ function createPreviewAppProvider(client, options) {
161
161
  region: options.region
162
162
  };
163
163
  const deployResult = await sdk.deploy({
164
- strategy: new PreviewBuildStrategy({
164
+ strategy: new AppBuildStrategy({
165
165
  appPath: path.resolve(options.cwd),
166
166
  entrypoint: options.entrypoint,
167
167
  buildType: options.buildType,
@@ -491,7 +491,7 @@ function apiCallError(summary, response, error) {
491
491
  return /* @__PURE__ */ new Error(`${summary}: ${message}${hint}`);
492
492
  }
493
493
  function domainApiCallError(summary, response, error) {
494
- return new PreviewDomainApiError({
494
+ return new DomainApiError({
495
495
  summary,
496
496
  status: response.status,
497
497
  code: error.error?.code ?? null,
@@ -541,4 +541,4 @@ function toAbsoluteUrl(url) {
541
541
  return url.startsWith("https://") || url.startsWith("http://") ? url : `https://${url}`;
542
542
  }
543
543
  //#endregion
544
- export { PreviewDomainApiError, createPreviewAppProvider };
544
+ export { DomainApiError, createAppProvider };
@@ -1,4 +1,4 @@
1
- //#region src/lib/app/preview-branch-database.ts
1
+ //#region src/lib/app/branch-database-api.ts
2
2
  async function createBranchDatabase(client, options) {
3
3
  const result = await client.POST("/v1/databases", {
4
4
  body: {
@@ -0,0 +1,93 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { resolveBuildSettings, resolveConfiguredBuildSettings } from "@prisma/compute-sdk";
4
+ //#region src/lib/app/build-settings.ts
5
+ /** Legacy build-settings file: no longer read or written, only detected for migration. */
6
+ const PRISMA_APP_CONFIG_FILENAME = "prisma.app.json";
7
+ /**
8
+ * Detects a leftover `prisma.app.json`. The file is no longer used: one that
9
+ * matches the effective settings is reported for deletion, one with custom
10
+ * values must be migrated to the compute config so builds never silently
11
+ * change.
12
+ */
13
+ async function detectLegacyBuildSettings(options) {
14
+ const configPath = path.join(options.appPath, PRISMA_APP_CONFIG_FILENAME);
15
+ let content;
16
+ try {
17
+ options.signal?.throwIfAborted();
18
+ content = await readFile(configPath, {
19
+ encoding: "utf8",
20
+ signal: options.signal
21
+ });
22
+ } catch (error) {
23
+ if (options.signal?.aborted) throw error;
24
+ return { kind: "absent" };
25
+ }
26
+ let legacy;
27
+ try {
28
+ const parsed = JSON.parse(content);
29
+ const buildCommand = parsed.buildCommand === null || typeof parsed.buildCommand === "string" ? typeof parsed.buildCommand === "string" ? parsed.buildCommand.trim() || null : null : void 0;
30
+ const outputDirectory = typeof parsed.outputDirectory === "string" ? normalizeRelativePath(parsed.outputDirectory) : void 0;
31
+ if (buildCommand === void 0 || !outputDirectory) return {
32
+ kind: "invalid",
33
+ configPath
34
+ };
35
+ legacy = {
36
+ buildCommand,
37
+ outputDirectory
38
+ };
39
+ } catch {
40
+ return {
41
+ kind: "invalid",
42
+ configPath
43
+ };
44
+ }
45
+ return legacy.buildCommand === options.effective.buildCommand && legacy.outputDirectory === options.effective.outputDirectory ? {
46
+ kind: "matching",
47
+ configPath
48
+ } : {
49
+ kind: "custom",
50
+ configPath,
51
+ ...legacy
52
+ };
53
+ }
54
+ /** Resolves build settings purely from framework inference; nothing is read or written. */
55
+ async function resolveInferredAppBuildSettings(options) {
56
+ return {
57
+ status: "inferred",
58
+ configPath: null,
59
+ relativeConfigPath: null,
60
+ settings: await resolveBuildSettings(options)
61
+ };
62
+ }
63
+ /**
64
+ * Resolves build settings when the compute config owns them: configured
65
+ * fields win, omitted fields fall back to framework defaults.
66
+ */
67
+ async function resolveConfiguredAppBuildSettings(options) {
68
+ const configFilename = path.basename(options.configPath);
69
+ const settings = await resolveConfiguredBuildSettings({
70
+ appPath: options.appPath,
71
+ buildType: options.buildType,
72
+ configured: options.configured,
73
+ source: `set by ${configFilename}`,
74
+ signal: options.signal
75
+ });
76
+ return {
77
+ status: "config",
78
+ configPath: options.configPath,
79
+ relativeConfigPath: configFilename,
80
+ settings
81
+ };
82
+ }
83
+ function normalizeRelativePath(value) {
84
+ const raw = value.trim().replace(/\\/g, "/");
85
+ if (raw.length === 0 || raw.split("/").includes("..")) return;
86
+ if (/^[A-Za-z]:/.test(raw)) return;
87
+ const normalized = path.posix.normalize(raw);
88
+ const segments = normalized.split("/");
89
+ if (path.win32.isAbsolute(value) || path.posix.isAbsolute(normalized) || segments.includes("..")) return;
90
+ return normalized === "." ? "." : normalized;
91
+ }
92
+ //#endregion
93
+ export { PRISMA_APP_CONFIG_FILENAME, detectLegacyBuildSettings, resolveConfiguredAppBuildSettings, resolveInferredAppBuildSettings };
@@ -0,0 +1,80 @@
1
+ import "./build-settings.js";
2
+ import "node:fs/promises";
3
+ import "node:path";
4
+ import { normalizeArtifactSymlinks, resolveBuildStrategy } from "@prisma/compute-sdk";
5
+ //#region src/lib/app/build.ts
6
+ const APP_BUILD_TYPES = [
7
+ "auto",
8
+ "bun",
9
+ "nextjs",
10
+ "nuxt",
11
+ "astro",
12
+ "nestjs",
13
+ "tanstack-start"
14
+ ];
15
+ const RESOLVED_APP_BUILD_TYPES = APP_BUILD_TYPES.filter((buildType) => buildType !== "auto");
16
+ var AppBuildStrategy = class {
17
+ #appPath;
18
+ #entrypoint;
19
+ #buildType;
20
+ #signal;
21
+ #buildSettings;
22
+ constructor(options) {
23
+ this.#appPath = options.appPath;
24
+ this.#entrypoint = options.entrypoint;
25
+ this.#buildType = options.buildType ?? "auto";
26
+ this.#signal = options.signal;
27
+ this.#buildSettings = options.buildSettings;
28
+ }
29
+ async canBuild(signal = this.#signal) {
30
+ const { strategy } = await resolveAppBuildStrategy({
31
+ appPath: this.#appPath,
32
+ entrypoint: this.#entrypoint,
33
+ buildType: this.#buildType,
34
+ signal,
35
+ buildSettings: this.#buildSettings
36
+ });
37
+ return strategy.canBuild(signal);
38
+ }
39
+ async execute(signal = this.#signal) {
40
+ const { artifact } = await executeAppBuild({
41
+ appPath: this.#appPath,
42
+ entrypoint: this.#entrypoint,
43
+ buildType: this.#buildType,
44
+ signal,
45
+ buildSettings: this.#buildSettings
46
+ });
47
+ return artifact;
48
+ }
49
+ };
50
+ async function executeAppBuild(options) {
51
+ const { strategy, buildType } = await resolveAppBuildStrategy({
52
+ appPath: options.appPath,
53
+ entrypoint: options.entrypoint,
54
+ buildType: options.buildType ?? "auto",
55
+ signal: options.signal,
56
+ buildSettings: options.buildSettings
57
+ });
58
+ const artifact = await strategy.execute(options.signal);
59
+ try {
60
+ await normalizeArtifactSymlinks(artifact.directory, options.appPath, options.signal);
61
+ return {
62
+ artifact,
63
+ buildType
64
+ };
65
+ } catch (error) {
66
+ await artifact.cleanup?.().catch(() => void 0);
67
+ throw error;
68
+ }
69
+ }
70
+ async function resolveAppBuildStrategy(options) {
71
+ return resolveBuildStrategy({
72
+ appPath: options.appPath,
73
+ buildType: options.buildType,
74
+ entrypoint: options.entrypoint,
75
+ buildSettings: options.buildSettings,
76
+ signal: options.signal
77
+ });
78
+ }
79
+ //#endregion
80
+ export { APP_BUILD_TYPES, AppBuildStrategy, RESOLVED_APP_BUILD_TYPES, executeAppBuild };