@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.
- package/README.md +0 -13
- package/dist/adapters/token-storage.js +311 -33
- package/dist/cli.js +7 -7
- package/dist/cli2.js +3 -3
- package/dist/commands/app/index.js +65 -52
- package/dist/commands/auth/index.js +55 -2
- package/dist/controllers/app-env.js +10 -7
- package/dist/controllers/app.js +536 -207
- package/dist/controllers/auth.js +211 -2
- package/dist/controllers/branch.js +5 -6
- package/dist/controllers/database.js +7 -5
- package/dist/controllers/project.js +33 -18
- package/dist/lib/app/app-interaction.js +5 -0
- package/dist/lib/app/{preview-provider.js → app-provider.js} +32 -28
- package/dist/lib/app/{preview-branch-database.js → branch-database-api.js} +1 -1
- package/dist/lib/app/branch-database-deploy.js +12 -60
- package/dist/lib/app/branch-database.js +1 -102
- package/dist/lib/app/build-settings.js +93 -0
- package/dist/lib/app/build.js +82 -0
- package/dist/lib/app/bun-project.js +2 -3
- package/dist/lib/app/compute-config.js +147 -0
- package/dist/lib/app/deploy-plan.js +58 -0
- package/dist/lib/app/{preview-progress.js → deploy-progress.js} +5 -5
- package/dist/lib/app/local-dev.js +3 -60
- package/dist/lib/app/production-deploy-gate.js +3 -3
- package/dist/lib/app/read-branch.js +30 -0
- package/dist/lib/auth/auth-ops.js +10 -4
- package/dist/lib/auth/guard.js +4 -1
- package/dist/lib/auth/login.js +32 -25
- package/dist/lib/diagnostics.js +1 -1
- package/dist/lib/fs/home-path.js +24 -0
- package/dist/lib/git/local-branch.js +15 -3
- package/dist/lib/project/local-pin.js +106 -26
- package/dist/lib/project/resolution.js +162 -71
- package/dist/lib/project/setup.js +65 -19
- package/dist/output/patterns.js +1 -1
- package/dist/presenters/app.js +44 -39
- package/dist/presenters/auth.js +98 -1
- package/dist/presenters/branch.js +1 -1
- package/dist/presenters/database.js +2 -2
- package/dist/presenters/project.js +3 -9
- package/dist/shell/command-meta.js +52 -2
- package/dist/shell/command-runner.js +39 -28
- package/dist/shell/diagnostics-output.js +2 -8
- package/dist/shell/errors.js +50 -1
- package/dist/shell/help.js +30 -20
- package/dist/shell/runtime.js +8 -4
- package/dist/shell/ui.js +19 -2
- package/dist/use-cases/auth.js +68 -1
- package/package.json +17 -3
- package/dist/lib/app/preview-build-settings.js +0 -385
- package/dist/lib/app/preview-build.js +0 -475
- package/dist/lib/app/preview-interaction.js +0 -5
package/dist/controllers/auth.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
8
|
-
import {
|
|
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 =
|
|
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:
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 });
|
|
@@ -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/
|
|
7
|
-
var
|
|
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 = "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
513
|
-
|
|
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 {
|
|
544
|
+
export { DomainApiError, createAppProvider };
|