@prisma/cli 3.0.0-beta.9 → 3.0.0-dev.101.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.
Files changed (53) hide show
  1. package/README.md +0 -13
  2. package/dist/adapters/token-storage.js +311 -33
  3. package/dist/cli.js +7 -7
  4. package/dist/cli2.js +3 -3
  5. package/dist/commands/app/index.js +65 -52
  6. package/dist/commands/auth/index.js +55 -2
  7. package/dist/controllers/app-env.js +10 -7
  8. package/dist/controllers/app.js +536 -207
  9. package/dist/controllers/auth.js +211 -2
  10. package/dist/controllers/branch.js +5 -6
  11. package/dist/controllers/database.js +7 -5
  12. package/dist/controllers/project.js +33 -18
  13. package/dist/lib/app/app-interaction.js +5 -0
  14. package/dist/lib/app/{preview-provider.js → app-provider.js} +32 -28
  15. package/dist/lib/app/{preview-branch-database.js → branch-database-api.js} +1 -1
  16. package/dist/lib/app/branch-database-deploy.js +12 -60
  17. package/dist/lib/app/branch-database.js +1 -102
  18. package/dist/lib/app/build-settings.js +93 -0
  19. package/dist/lib/app/build.js +82 -0
  20. package/dist/lib/app/bun-project.js +2 -3
  21. package/dist/lib/app/compute-config.js +147 -0
  22. package/dist/lib/app/deploy-plan.js +58 -0
  23. package/dist/lib/app/{preview-progress.js → deploy-progress.js} +5 -5
  24. package/dist/lib/app/local-dev.js +3 -60
  25. package/dist/lib/app/production-deploy-gate.js +3 -3
  26. package/dist/lib/app/read-branch.js +30 -0
  27. package/dist/lib/auth/auth-ops.js +10 -4
  28. package/dist/lib/auth/guard.js +4 -1
  29. package/dist/lib/auth/login.js +32 -25
  30. package/dist/lib/diagnostics.js +1 -1
  31. package/dist/lib/fs/home-path.js +24 -0
  32. package/dist/lib/git/local-branch.js +15 -3
  33. package/dist/lib/project/local-pin.js +106 -26
  34. package/dist/lib/project/resolution.js +162 -71
  35. package/dist/lib/project/setup.js +65 -19
  36. package/dist/output/patterns.js +1 -1
  37. package/dist/presenters/app.js +44 -39
  38. package/dist/presenters/auth.js +98 -1
  39. package/dist/presenters/branch.js +1 -1
  40. package/dist/presenters/database.js +2 -2
  41. package/dist/presenters/project.js +3 -9
  42. package/dist/shell/command-meta.js +52 -2
  43. package/dist/shell/command-runner.js +39 -28
  44. package/dist/shell/diagnostics-output.js +2 -8
  45. package/dist/shell/errors.js +50 -1
  46. package/dist/shell/help.js +30 -20
  47. package/dist/shell/runtime.js +8 -4
  48. package/dist/shell/ui.js +19 -2
  49. package/dist/use-cases/auth.js +68 -1
  50. package/package.json +17 -3
  51. package/dist/lib/app/preview-build-settings.js +0 -385
  52. package/dist/lib/app/preview-build.js +0 -475
  53. package/dist/lib/app/preview-interaction.js +0 -5
@@ -1,9 +1,12 @@
1
- import { authRequiredError, usageError } from "../shell/errors.js";
1
+ import { CLIENT_ID, SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } 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";
5
7
  import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
6
8
  import { createSelectPromptPort } from "./select-prompt-port.js";
9
+ import { createManagementApiSdk } from "@prisma/management-api-sdk";
7
10
  //#region src/controllers/auth.ts
8
11
  function isRealMode(context) {
9
12
  return !context.runtime.fixturePath && !context.runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
@@ -30,6 +33,35 @@ async function runAuthWhoAmI(context) {
30
33
  else result = await createAuthUseCases(createCliUseCaseGateways(context)).whoami();
31
34
  return createAuthSuccess("auth.whoami", result, result.authenticated ? [] : ["prisma-cli auth login"]);
32
35
  }
36
+ async function runAuthWorkspaceList(context) {
37
+ const result = isRealMode(context) ? await listRealAuthWorkspaces(context) : await createAuthUseCases(createCliUseCaseGateways(context)).listWorkspaces();
38
+ return {
39
+ command: "auth.workspace.list",
40
+ result,
41
+ warnings: [],
42
+ nextSteps: result.workspaces.length === 0 ? ["prisma-cli auth login"] : []
43
+ };
44
+ }
45
+ async function runAuthWorkspaceUse(context, workspaceRef) {
46
+ const trimmedWorkspaceRef = workspaceRef?.trim();
47
+ const selectedWorkspaceRef = trimmedWorkspaceRef ? trimmedWorkspaceRef : await selectWorkspaceSession(context);
48
+ return {
49
+ command: "auth.workspace.use",
50
+ result: isRealMode(context) ? await useRealAuthWorkspace(context, selectedWorkspaceRef) : await createAuthUseCases(createCliUseCaseGateways(context)).useWorkspace(selectedWorkspaceRef),
51
+ warnings: [],
52
+ nextSteps: ["prisma-cli auth whoami", "prisma-cli project list"]
53
+ };
54
+ }
55
+ async function runAuthWorkspaceLogout(context, workspaceRef) {
56
+ 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");
57
+ const result = isRealMode(context) ? await logoutRealAuthWorkspace(context, workspaceRef) : await createAuthUseCases(createCliUseCaseGateways(context)).logoutWorkspace(workspaceRef);
58
+ return {
59
+ command: "auth.workspace.logout",
60
+ result,
61
+ warnings: [],
62
+ nextSteps: result.activeWorkspace ? ["prisma-cli auth workspace list"] : ["prisma-cli auth workspace list", "prisma-cli auth workspace use <id>"]
63
+ };
64
+ }
33
65
  async function requireAuthenticatedAuthState(context) {
34
66
  if (isRealMode(context)) {
35
67
  const current = await readAuthState(context.runtime.env, context.runtime.signal);
@@ -44,6 +76,183 @@ async function requireAuthenticatedAuthState(context) {
44
76
  if (!canPrompt(context)) throw authRequiredError();
45
77
  return loginWithSelectionFlow(context, useCases, {});
46
78
  }
79
+ async function listRealAuthWorkspaces(context) {
80
+ const rawServiceToken = context.runtime.env[SERVICE_TOKEN_ENV_VAR];
81
+ const storage = new FileTokenStorage(context.runtime.env, context.runtime.signal);
82
+ const localWorkspaces = await hydrateLocalAuthWorkspaces(context, storage, await storage.listWorkspaces());
83
+ if (rawServiceToken !== void 0) {
84
+ const authState = await readAuthState(context.runtime.env, context.runtime.signal);
85
+ return {
86
+ authSource: authState.authenticated ? "service_token" : "none",
87
+ activeWorkspace: authState.workspace,
88
+ workspaces: [...authState.workspace ? [{
89
+ ...authState.workspace,
90
+ credentialWorkspaceId: null,
91
+ active: true,
92
+ source: "service_token",
93
+ switchable: false,
94
+ lastSeenAt: null
95
+ }] : [], ...localWorkspaces.map((workspace) => ({
96
+ ...toAuthWorkspace(workspace),
97
+ credentialWorkspaceId: workspace.credentialWorkspaceId,
98
+ active: false,
99
+ source: "oauth",
100
+ switchable: false,
101
+ lastSeenAt: workspace.lastSeenAt
102
+ }))]
103
+ };
104
+ }
105
+ const active = localWorkspaces.find((workspace) => workspace.active) ?? null;
106
+ return {
107
+ authSource: localWorkspaces.length > 0 ? "oauth" : "none",
108
+ activeWorkspace: active ? toAuthWorkspace(active) : null,
109
+ workspaces: localWorkspaces.map((workspace) => ({
110
+ ...toAuthWorkspace(workspace),
111
+ credentialWorkspaceId: workspace.credentialWorkspaceId,
112
+ active: workspace.active,
113
+ source: "oauth",
114
+ switchable: true,
115
+ lastSeenAt: workspace.lastSeenAt
116
+ }))
117
+ };
118
+ }
119
+ async function useRealAuthWorkspace(context, workspaceRef) {
120
+ if (context.runtime.env["PRISMA_SERVICE_TOKEN"] !== void 0) throw workspaceSwitchUnavailableError();
121
+ const storage = new FileTokenStorage(context.runtime.env, context.runtime.signal);
122
+ await hydrateLocalAuthWorkspaces(context, storage, await storage.listWorkspaces());
123
+ try {
124
+ const result = await storage.useWorkspace(workspaceRef);
125
+ return {
126
+ previousWorkspace: result.previous ? toAuthWorkspace(result.previous) : null,
127
+ workspace: toAuthWorkspace(result.selected)
128
+ };
129
+ } catch (error) {
130
+ if (error instanceof WorkspaceSelectionError) {
131
+ if (error.reason === "ambiguous") throw workspaceAmbiguousError(error.workspaceRef ?? workspaceRef, error.matches.map((match) => ({
132
+ id: match.id,
133
+ name: match.name,
134
+ credentialWorkspaceId: match.credentialWorkspaceId
135
+ })));
136
+ throw workspaceNotAuthenticatedError(error.workspaceRef ?? workspaceRef);
137
+ }
138
+ throw error;
139
+ }
140
+ }
141
+ async function logoutRealAuthWorkspace(context, workspaceRef) {
142
+ const storage = new FileTokenStorage(context.runtime.env, context.runtime.signal);
143
+ await hydrateLocalAuthWorkspaces(context, storage, await storage.listWorkspaces());
144
+ try {
145
+ const result = await storage.logoutWorkspace(workspaceRef);
146
+ return {
147
+ workspace: toAuthWorkspace(result.workspace),
148
+ wasActive: result.wasActive,
149
+ activeWorkspace: result.activeWorkspace ? toAuthWorkspace(result.activeWorkspace) : null
150
+ };
151
+ } catch (error) {
152
+ if (error instanceof WorkspaceSelectionError) {
153
+ if (error.reason === "ambiguous") throw workspaceAmbiguousError(error.workspaceRef ?? workspaceRef, error.matches.map((match) => ({
154
+ id: match.id,
155
+ name: match.name,
156
+ credentialWorkspaceId: match.credentialWorkspaceId
157
+ })));
158
+ throw workspaceNotAuthenticatedError(error.workspaceRef ?? workspaceRef);
159
+ }
160
+ throw error;
161
+ }
162
+ }
163
+ async function selectWorkspaceSession(context) {
164
+ const realMode = isRealMode(context);
165
+ if (realMode && context.runtime.env["PRISMA_SERVICE_TOKEN"] !== void 0) throw workspaceSwitchUnavailableError();
166
+ const workspaces = (realMode ? await listRealAuthWorkspaces(context) : await createAuthUseCases(createCliUseCaseGateways(context)).listWorkspaces()).workspaces.filter((workspace) => workspace.switchable);
167
+ 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");
168
+ if (workspaces.length === 1) return workspaces[0].id;
169
+ if (!canPrompt(context)) throw usageError("Interactive workspace selection unavailable", "auth workspace use needs an interactive terminal when no workspace is provided and 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");
170
+ return (await createSelectPromptPort(context).select({
171
+ message: "Select a workspace",
172
+ choices: workspaces.map((workspace) => ({
173
+ label: `${workspace.name} (${workspace.id})${workspace.active ? " active" : ""}`,
174
+ value: workspace
175
+ }))
176
+ })).id;
177
+ }
178
+ async function hydrateLocalAuthWorkspaces(context, storage, workspaces) {
179
+ const candidates = workspaces.filter(needsWorkspaceMetadataHydration);
180
+ if (candidates.length === 0) return workspaces;
181
+ const tokensByCredentialWorkspaceId = new Map((await storage.listWorkspaceTokens()).map((tokens) => [tokens.workspaceId, tokens]));
182
+ let nextWorkspaces = workspaces;
183
+ for (const workspace of candidates) {
184
+ const tokens = tokensByCredentialWorkspaceId.get(workspace.credentialWorkspaceId);
185
+ if (!tokens) continue;
186
+ const resolved = await resolveOAuthWorkspaceMetadata(context, tokens);
187
+ if (!resolved) continue;
188
+ await rememberResolvedWorkspaceMetadata(context, storage, tokens, resolved);
189
+ nextWorkspaces = nextWorkspaces.map((candidate) => candidate.credentialWorkspaceId === workspace.credentialWorkspaceId ? {
190
+ ...candidate,
191
+ id: resolved.id,
192
+ name: resolved.name,
193
+ lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
194
+ } : candidate);
195
+ }
196
+ return nextWorkspaces;
197
+ }
198
+ async function rememberResolvedWorkspaceMetadata(context, storage, tokens, resolved) {
199
+ try {
200
+ await storage.rememberWorkspace(tokens.workspaceId, resolved);
201
+ } catch {
202
+ context.runtime.signal?.throwIfAborted();
203
+ }
204
+ }
205
+ function needsWorkspaceMetadataHydration(workspace) {
206
+ return workspace.id === workspace.credentialWorkspaceId || workspace.name === "Unknown workspace" || workspace.name === workspace.credentialWorkspaceId;
207
+ }
208
+ async function resolveOAuthWorkspaceMetadata(context, tokens) {
209
+ const sdk = createManagementApiSdk({
210
+ clientId: CLIENT_ID,
211
+ redirectUri: "http://localhost:0/auth/callback",
212
+ tokenStorage: createSingleWorkspaceTokenStorage(new FileTokenStorage(context.runtime.env, context.runtime.signal, { activateOnSetTokens: false }), tokens),
213
+ apiBaseUrl: getApiBaseUrl(context.runtime.env)
214
+ });
215
+ try {
216
+ const { data } = await sdk.client.GET("/v1/workspaces/{id}", {
217
+ params: { path: { id: tokens.workspaceId } },
218
+ signal: context.runtime.signal
219
+ });
220
+ const id = stringOrNull(data?.data?.id) ?? tokens.workspaceId;
221
+ const name = stringOrNull(data?.data?.name) ?? id;
222
+ if (id === tokens.workspaceId && name === tokens.workspaceId) return null;
223
+ return {
224
+ id,
225
+ name
226
+ };
227
+ } catch {
228
+ context.runtime.signal?.throwIfAborted();
229
+ return null;
230
+ }
231
+ }
232
+ function createSingleWorkspaceTokenStorage(storage, initialTokens) {
233
+ let currentTokens = initialTokens;
234
+ return {
235
+ getTokens: async () => currentTokens,
236
+ setTokens: async (tokens) => {
237
+ currentTokens = tokens;
238
+ await storage.setTokens(tokens);
239
+ },
240
+ clearTokens: async () => {
241
+ const tokens = currentTokens;
242
+ currentTokens = null;
243
+ if (tokens) await storage.clearTokensIfCurrent(tokens);
244
+ }
245
+ };
246
+ }
247
+ function stringOrNull(value) {
248
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
249
+ }
250
+ function toAuthWorkspace(workspace) {
251
+ return {
252
+ id: workspace.id,
253
+ name: workspace.name
254
+ };
255
+ }
47
256
  async function loginWithSelectionFlow(context, useCases, options) {
48
257
  const selection = await resolveLoginSelection(useCases, canPrompt(context) ? createSelectPromptPort(context) : null, options);
49
258
  return useCases.login(selection);
@@ -104,4 +313,4 @@ function createAuthSuccess(command, result, nextSteps) {
104
313
  };
105
314
  }
106
315
  //#endregion
107
- export { requireAuthenticatedAuthState, runAuthLogin, runAuthLogout, runAuthWhoAmI };
316
+ export { requireAuthenticatedAuthState, runAuthLogin, runAuthLogout, runAuthWhoAmI, runAuthWorkspaceList, runAuthWorkspaceLogout, runAuthWorkspaceUse };
@@ -1,8 +1,7 @@
1
1
  import { CliError, authRequiredError, workspaceRequiredError } from "../shell/errors.js";
2
+ import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
2
3
  import { requireComputeAuth } from "../lib/auth/guard.js";
3
- import { resolveProjectTarget } from "../lib/project/resolution.js";
4
4
  import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
5
- import { createSelectPromptPort } from "./select-prompt-port.js";
6
5
  import { requireAuthenticatedAuthState } from "./auth.js";
7
6
  import { listRealWorkspaceProjects } from "./project.js";
8
7
  import { createBranchUseCases } from "../use-cases/branch.js";
@@ -30,13 +29,13 @@ async function listRealBranches(context) {
30
29
  if (!client) throw authRequiredError(["prisma-cli auth login"]);
31
30
  const workspace = authState.workspace;
32
31
  if (!workspace) throw workspaceRequiredError();
33
- const target = await resolveProjectTarget({
32
+ const targetResult = await resolveProjectTarget({
34
33
  context,
35
34
  workspace,
36
- listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
37
- prompt: createSelectPromptPort(context),
38
- remember: true
35
+ listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal)
39
36
  });
37
+ if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
38
+ const target = targetResult.value;
40
39
  const branches = await listBranches(client, target.project.id, context.runtime.signal);
41
40
  return {
42
41
  projectId: target.project.id,
@@ -1,6 +1,6 @@
1
1
  import { CliError, authRequiredError, usageError, workspaceRequiredError } from "../shell/errors.js";
2
+ import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
2
3
  import { requireComputeAuth } from "../lib/auth/guard.js";
3
- import { resolveProjectTarget } from "../lib/project/resolution.js";
4
4
  import { requireAuthenticatedAuthState } from "./auth.js";
5
5
  import { listFixtureWorkspaceProjects, listRealWorkspaceProjects } from "./project.js";
6
6
  import { createManagementDatabaseProvider, normalizeConnection, normalizeDatabase } from "../lib/database/provider.js";
@@ -155,28 +155,30 @@ async function requireDatabaseContext(context, flags, commandName) {
155
155
  if (isRealMode(context)) {
156
156
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
157
157
  if (!client) throw authRequiredError();
158
- const target = await resolveProjectTarget({
158
+ const targetResult = await resolveProjectTarget({
159
159
  context,
160
160
  workspace,
161
161
  explicitProject: flags.projectRef,
162
162
  listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
163
163
  commandName
164
164
  });
165
+ if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
165
166
  return {
166
167
  provider: createManagementDatabaseProvider(client),
167
- target
168
+ target: targetResult.value
168
169
  };
169
170
  }
170
- const target = await resolveProjectTarget({
171
+ const targetResult = await resolveProjectTarget({
171
172
  context,
172
173
  workspace,
173
174
  explicitProject: flags.projectRef,
174
175
  listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
175
176
  commandName
176
177
  });
178
+ if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
177
179
  return {
178
180
  provider: createFixtureDatabaseProvider(context),
179
- target
181
+ target: targetResult.value
180
182
  };
181
183
  }
182
184
  async function requireDatabaseProviderOnly(context) {
@@ -1,19 +1,19 @@
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
- import { requireComputeAuth } from "../lib/auth/guard.js";
5
- import { formatCommandArgument } from "../shell/command-arguments.js";
6
5
  import { readLocalResolutionPin } from "../lib/project/local-pin.js";
7
- import { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
8
- import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
6
+ import { formatCommandArgument } from "../shell/command-arguments.js";
7
+ import { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, projectResolutionErrorToCliError, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
8
+ import { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
9
+ import { requireComputeAuth } from "../lib/auth/guard.js";
9
10
  import { promptForProjectSetupChoice } from "../lib/project/interactive-setup.js";
10
- import { createPreviewAppProvider } from "../lib/app/preview-provider.js";
11
11
  import { createCliUseCaseGateways } from "../use-cases/create-cli-gateways.js";
12
12
  import { requireAuthenticatedAuthState } from "./auth.js";
13
13
  import { parseGitHubRepositoryUrl, readGitOriginRemote } from "../adapters/git.js";
14
14
  import { createProjectUseCases } from "../use-cases/project.js";
15
- import open from "open";
16
15
  import { matchError } from "better-result";
16
+ import open from "open";
17
17
  //#region src/controllers/project.ts
18
18
  const GITHUB_INSTALL_POLL_INTERVAL_MS = 2e3;
19
19
  const GITHUB_INSTALL_POLL_TIMEOUT_MS = 12e4;
@@ -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,
@@ -116,12 +116,14 @@ async function runProjectCreate(context, projectName) {
116
116
  fallbackFix: "Retry the command, or choose an existing Project with prisma-cli project link <id-or-name>."
117
117
  });
118
118
  });
119
+ const bindResult = await bindProjectToDirectory(context, workspace, {
120
+ id: created.id,
121
+ name: created.name
122
+ }, "created");
123
+ if (bindResult.isErr()) throw projectDirectoryBindingErrorToCliError(bindResult.error);
119
124
  return {
120
125
  command: "project.create",
121
- result: await bindProjectToDirectory(context, workspace, {
122
- id: created.id,
123
- name: created.name
124
- }, "created"),
126
+ result: bindResult.value,
125
127
  warnings: [],
126
128
  nextSteps: ["prisma-cli app deploy"]
127
129
  };
@@ -134,11 +136,11 @@ async function runProjectLink(context, projectRef) {
134
136
  if (isRealMode(context)) {
135
137
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
136
138
  if (!client) throw authRequiredError();
137
- provider = createPreviewAppProvider(client);
139
+ provider = createAppProvider(client);
138
140
  projects = await listRealWorkspaceProjects(client, workspace, context.runtime.signal);
139
141
  } else projects = listFixtureWorkspaceProjects(context, workspace);
140
142
  let result;
141
- if (projectRef?.trim()) result = await bindProjectToDirectory(context, workspace, toProjectSummary(resolveProjectForSetup(projectRef.trim(), projects, workspace)), "linked");
143
+ if (projectRef?.trim()) result = await requireProjectDirectoryBinding(context, workspace, toProjectSummary(resolveProjectForSetup(projectRef.trim(), projects, workspace)), "linked");
142
144
  else if (canPrompt(context) && !context.flags.yes) result = await resolveInteractiveProjectLinkSetup(context, workspace, projects, provider);
143
145
  else throw await projectLinkTargetRequiredError(context, projects);
144
146
  return {
@@ -162,7 +164,12 @@ async function resolveInteractiveProjectLinkSetup(context, workspace, projects,
162
164
  nextSteps: ["prisma-cli project link <id-or-name>", "prisma-cli project create <name>"]
163
165
  }
164
166
  });
165
- return bindProjectToDirectory(context, workspace, setup.project, setup.action);
167
+ return requireProjectDirectoryBinding(context, workspace, setup.project, setup.action);
168
+ }
169
+ async function requireProjectDirectoryBinding(context, workspace, project, action) {
170
+ const bindResult = await bindProjectToDirectory(context, workspace, project, action);
171
+ if (bindResult.isErr()) throw projectDirectoryBindingErrorToCliError(bindResult.error);
172
+ return bindResult.value;
166
173
  }
167
174
  async function createProjectForLinkSetup(provider, projectName, workspace, signal) {
168
175
  const created = await provider.createProject({
@@ -323,42 +330,50 @@ async function runGitDisconnect(context, options = {}) {
323
330
  async function resolveProjectShowInRealMode(context, workspace, explicitProject) {
324
331
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
325
332
  if (!client) throw authRequiredError();
326
- return inspectProjectBinding({
333
+ const result = await inspectProjectBinding({
327
334
  context,
328
335
  workspace,
329
336
  explicitProject,
330
337
  listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
331
338
  commandName: "project show"
332
339
  });
340
+ if (result.isErr()) throw projectResolutionErrorToCliError(result.error);
341
+ return result.value;
333
342
  }
334
343
  async function resolveRequiredProjectInRealMode(context, workspace, explicitProject, commandName) {
335
344
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
336
345
  if (!client) throw authRequiredError();
337
- return resolveProjectTarget({
346
+ const result = await resolveProjectTarget({
338
347
  context,
339
348
  workspace,
340
349
  explicitProject,
341
350
  listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
342
351
  commandName
343
352
  });
353
+ if (result.isErr()) throw projectResolutionErrorToCliError(result.error);
354
+ return result.value;
344
355
  }
345
356
  async function resolveProjectShowInFixtureMode(context, workspace, explicitProject) {
346
- return inspectProjectBinding({
357
+ const result = await inspectProjectBinding({
347
358
  context,
348
359
  workspace,
349
360
  explicitProject,
350
361
  listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
351
362
  commandName: "project show"
352
363
  });
364
+ if (result.isErr()) throw projectResolutionErrorToCliError(result.error);
365
+ return result.value;
353
366
  }
354
367
  async function resolveRequiredProjectInFixtureMode(context, workspace, explicitProject, commandName) {
355
- return resolveProjectTarget({
368
+ const result = await resolveProjectTarget({
356
369
  context,
357
370
  workspace,
358
371
  explicitProject,
359
372
  listProjects: async () => listFixtureWorkspaceProjects(context, workspace),
360
373
  commandName
361
374
  });
375
+ if (result.isErr()) throw projectResolutionErrorToCliError(result.error);
376
+ return result.value;
362
377
  }
363
378
  async function listRealWorkspaceProjects(client, workspace, signal) {
364
379
  const { data } = await client.GET("/v1/projects", { signal });
@@ -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,
@@ -509,32 +509,36 @@ async function findAppForDeployment(sdk, deploymentId, signal) {
509
509
  });
510
510
  if (servicesResult.isErr()) throw new Error(servicesResult.error.message);
511
511
  for (const service of servicesResult.value) {
512
- const detailResult = await sdk.showService({
513
- serviceId: service.id,
514
- signal
515
- });
516
- if (detailResult.isErr()) throw new Error(detailResult.error.message);
517
- const app = {
518
- id: detailResult.value.id,
519
- name: detailResult.value.name,
520
- region: detailResult.value.region ?? null,
521
- liveDeploymentId: detailResult.value.latestVersionId ?? null,
522
- liveUrl: toAbsoluteUrl(detailResult.value.serviceEndpointDomain ?? null)
523
- };
524
- if (app.liveDeploymentId === deploymentId) return app;
525
- const versionsResult = await sdk.listVersions({
526
- serviceId: service.id,
527
- signal
528
- });
529
- if (versionsResult.isErr()) throw new Error(versionsResult.error.message);
530
- if (versionsResult.value.some((version) => version.id === deploymentId)) return app;
512
+ const app = await findServiceAppForDeployment(sdk, service.id, deploymentId, signal);
513
+ if (app) return app;
531
514
  }
532
515
  }
533
516
  return null;
534
517
  }
518
+ async function findServiceAppForDeployment(sdk, serviceId, deploymentId, signal) {
519
+ const detailResult = await sdk.showService({
520
+ serviceId,
521
+ signal
522
+ });
523
+ if (detailResult.isErr()) throw new Error(detailResult.error.message);
524
+ const app = {
525
+ id: detailResult.value.id,
526
+ name: detailResult.value.name,
527
+ region: detailResult.value.region ?? null,
528
+ liveDeploymentId: detailResult.value.latestVersionId ?? null,
529
+ liveUrl: toAbsoluteUrl(detailResult.value.serviceEndpointDomain ?? null)
530
+ };
531
+ if (app.liveDeploymentId === deploymentId) return app;
532
+ const versionsResult = await sdk.listVersions({
533
+ serviceId,
534
+ signal
535
+ });
536
+ if (versionsResult.isErr()) throw new Error(versionsResult.error.message);
537
+ return versionsResult.value.some((version) => version.id === deploymentId) ? app : null;
538
+ }
535
539
  function toAbsoluteUrl(url) {
536
540
  if (!url) return null;
537
541
  return url.startsWith("https://") || url.startsWith("http://") ? url : `https://${url}`;
538
542
  }
539
543
  //#endregion
540
- 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: {