@prisma/cli 3.0.0-beta.2 → 3.0.0-beta.20
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 +5 -16
- package/dist/adapters/git.js +8 -3
- package/dist/adapters/local-state.js +26 -7
- package/dist/adapters/mock-api.js +81 -2
- package/dist/adapters/token-storage.js +344 -25
- package/dist/cli.js +18 -3
- package/dist/cli2.js +12 -4
- package/dist/commands/agent/index.js +60 -0
- package/dist/commands/app/index.js +90 -61
- package/dist/commands/auth/index.js +55 -2
- package/dist/commands/branch/index.js +2 -27
- package/dist/commands/build/index.js +29 -0
- package/dist/commands/database/index.js +159 -0
- package/dist/commands/env.js +8 -4
- package/dist/commands/git/index.js +1 -1
- package/dist/commands/project/index.js +1 -1
- package/dist/controllers/agent.js +228 -0
- package/dist/controllers/app-env-api.js +54 -0
- package/dist/controllers/app-env-file.js +181 -0
- package/dist/controllers/app-env.js +286 -131
- package/dist/controllers/app.js +849 -309
- package/dist/controllers/auth.js +255 -11
- package/dist/controllers/branch.js +78 -48
- package/dist/controllers/build.js +88 -0
- package/dist/controllers/database.js +318 -0
- package/dist/controllers/project.js +156 -87
- package/dist/controllers/select-prompt-port.js +1 -0
- package/dist/lib/agent/cli-command.js +17 -0
- package/dist/lib/agent/constants.js +12 -0
- package/dist/lib/agent/package-manager.js +99 -0
- package/dist/lib/agent/setup-status.js +83 -0
- package/dist/lib/app/app-interaction.js +5 -0
- package/dist/lib/app/{preview-provider.js → app-provider.js} +220 -104
- package/dist/lib/app/branch-database-api.js +102 -0
- package/dist/lib/app/branch-database-deploy.js +326 -0
- package/dist/lib/app/branch-database.js +216 -0
- package/dist/lib/app/build-settings.js +93 -0
- package/dist/lib/app/build.js +82 -0
- package/dist/lib/app/bun-project.js +15 -9
- package/dist/lib/app/compute-config.js +145 -0
- package/dist/lib/app/deploy-plan.js +59 -0
- package/dist/lib/app/{preview-progress.js → deploy-progress.js} +12 -12
- package/dist/lib/app/env-config.js +1 -1
- package/dist/lib/app/env-file.js +82 -0
- package/dist/lib/app/env-vars.js +28 -2
- package/dist/lib/app/local-dev.js +8 -49
- package/dist/lib/app/production-deploy-gate.js +162 -0
- package/dist/lib/app/read-branch.js +30 -0
- package/dist/lib/auth/auth-ops.js +38 -23
- package/dist/lib/auth/guard.js +6 -2
- package/dist/lib/auth/login.js +117 -15
- package/dist/lib/database/provider.js +167 -0
- package/dist/lib/diagnostics.js +15 -0
- package/dist/lib/fs/home-path.js +24 -0
- package/dist/lib/git/local-branch.js +53 -0
- package/dist/lib/git/local-status.js +57 -0
- package/dist/lib/project/interactive-setup.js +2 -1
- package/dist/lib/project/local-pin.js +183 -34
- package/dist/lib/project/resolution.js +206 -50
- package/dist/lib/project/setup.js +64 -18
- package/dist/output/patterns.js +1 -1
- package/dist/presenters/agent.js +74 -0
- package/dist/presenters/app-env.js +149 -14
- package/dist/presenters/app.js +187 -26
- package/dist/presenters/auth.js +99 -2
- package/dist/presenters/branch.js +37 -102
- package/dist/presenters/database.js +274 -0
- package/dist/presenters/project.js +44 -26
- package/dist/presenters/verbose-context.js +64 -0
- package/dist/shell/cli-command.js +12 -0
- package/dist/shell/command-arguments.js +7 -1
- package/dist/shell/command-meta.js +243 -15
- package/dist/shell/command-runner.js +60 -21
- package/dist/shell/diagnostics-output.js +57 -0
- package/dist/shell/errors.js +61 -1
- package/dist/shell/help.js +31 -20
- package/dist/shell/output.js +10 -1
- package/dist/shell/prompt.js +7 -3
- package/dist/shell/runtime.js +10 -6
- package/dist/shell/ui.js +42 -3
- package/dist/shell/update-check.js +247 -0
- package/dist/use-cases/auth.js +68 -1
- package/dist/use-cases/branch.js +20 -68
- package/dist/use-cases/create-cli-gateways.js +2 -17
- package/package.json +27 -10
- package/dist/lib/app/preview-build.js +0 -284
- package/dist/lib/app/preview-interaction.js +0 -5
|
@@ -1,36 +1,100 @@
|
|
|
1
|
-
import { CliError } from "../../shell/errors.js";
|
|
2
1
|
import { formatCommandArgument } from "../../shell/command-arguments.js";
|
|
2
|
+
import { CliError } from "../../shell/errors.js";
|
|
3
3
|
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "./local-pin.js";
|
|
4
|
-
import { readFile } from "node:fs/promises";
|
|
5
4
|
import path from "node:path";
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import { Result, TaggedError, matchError } from "better-result";
|
|
6
7
|
//#region src/lib/project/resolution.ts
|
|
8
|
+
var ProjectNotFoundError = class extends TaggedError("ProjectNotFoundError")() {
|
|
9
|
+
constructor(projectRef, workspace) {
|
|
10
|
+
super({
|
|
11
|
+
message: `Project "${projectRef}" was not found in workspace "${workspace.name}".`,
|
|
12
|
+
projectRef,
|
|
13
|
+
workspace
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var ProjectAmbiguousError = class extends TaggedError("ProjectAmbiguousError")() {
|
|
18
|
+
constructor(projectRef, matches) {
|
|
19
|
+
super({
|
|
20
|
+
message: projectRef ? `Multiple projects matched "${projectRef}".` : "Multiple projects matched the current directory context.",
|
|
21
|
+
projectRef,
|
|
22
|
+
matches
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var LocalStateStaleError = class extends TaggedError("LocalStateStaleError")() {
|
|
27
|
+
constructor() {
|
|
28
|
+
super({
|
|
29
|
+
message: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
|
|
30
|
+
pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var LocalProjectWorkspaceMismatchError = class extends TaggedError("LocalProjectWorkspaceMismatchError")() {
|
|
35
|
+
constructor(options) {
|
|
36
|
+
super({
|
|
37
|
+
message: `${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} links this directory to project ${options.pinnedProjectId} in workspace ${options.pinnedWorkspaceId}, but the active workspace is "${options.activeWorkspace.name}" (${options.activeWorkspace.id}).`,
|
|
38
|
+
pinnedWorkspaceId: options.pinnedWorkspaceId,
|
|
39
|
+
pinnedProjectId: options.pinnedProjectId,
|
|
40
|
+
activeWorkspace: options.activeWorkspace
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var ProjectSetupRequiredError = class extends TaggedError("ProjectSetupRequiredError")() {
|
|
45
|
+
constructor(options) {
|
|
46
|
+
const commandLabel = options.commandName ? `prisma-cli ${options.commandName}` : "this command";
|
|
47
|
+
super({
|
|
48
|
+
message: `This directory is not linked to a Prisma Project, and ${commandLabel} will not choose one from package or directory names.`,
|
|
49
|
+
commandName: options.commandName,
|
|
50
|
+
suggestion: options.suggestion
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
};
|
|
7
54
|
async function resolveProjectTarget(options) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
55
|
+
return Result.gen(async function* () {
|
|
56
|
+
const localPin = yield* Result.await(readImplicitLocalPin(options, { allowEnvProjectId: true }));
|
|
57
|
+
const projects = await options.listProjects();
|
|
58
|
+
const target = yield* Result.await(resolveBoundProjectTarget(options, projects, {
|
|
59
|
+
allowEnvProjectId: true,
|
|
60
|
+
localPin
|
|
61
|
+
}));
|
|
62
|
+
if (target) return Result.ok(target);
|
|
63
|
+
return Result.err(await projectSetupRequiredError({
|
|
64
|
+
cwd: options.context.runtime.cwd,
|
|
65
|
+
projects,
|
|
66
|
+
commandName: options.commandName,
|
|
67
|
+
signal: options.context.runtime.signal
|
|
68
|
+
}));
|
|
15
69
|
});
|
|
16
70
|
}
|
|
17
71
|
async function inspectProjectBinding(options) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
72
|
+
return Result.gen(async function* () {
|
|
73
|
+
const localPin = yield* Result.await(readImplicitLocalPin(options, { allowEnvProjectId: false }));
|
|
74
|
+
const projects = await options.listProjects();
|
|
75
|
+
const target = yield* Result.await(resolveBoundProjectTarget(options, projects, {
|
|
76
|
+
allowEnvProjectId: false,
|
|
77
|
+
localPin
|
|
78
|
+
}));
|
|
79
|
+
if (target) return Result.ok(target);
|
|
80
|
+
return Result.ok({
|
|
81
|
+
workspace: options.workspace,
|
|
82
|
+
project: null,
|
|
83
|
+
localBinding: { status: "not-linked" },
|
|
84
|
+
resolution: { projectSource: "unbound" },
|
|
85
|
+
...await buildProjectSetupSuggestion({
|
|
86
|
+
cwd: options.context.runtime.cwd,
|
|
87
|
+
projects,
|
|
88
|
+
commandName: options.commandName ?? "project show",
|
|
89
|
+
signal: options.context.runtime.signal
|
|
90
|
+
})
|
|
91
|
+
});
|
|
92
|
+
});
|
|
32
93
|
}
|
|
33
94
|
function projectNotFoundError(projectRef, workspace) {
|
|
95
|
+
return projectResolutionErrorToCliError(new ProjectNotFoundError(projectRef, workspace));
|
|
96
|
+
}
|
|
97
|
+
function projectNotFoundCliError(projectRef, workspace) {
|
|
34
98
|
return new CliError({
|
|
35
99
|
code: "PROJECT_NOT_FOUND",
|
|
36
100
|
domain: "project",
|
|
@@ -42,6 +106,9 @@ function projectNotFoundError(projectRef, workspace) {
|
|
|
42
106
|
});
|
|
43
107
|
}
|
|
44
108
|
function projectAmbiguousError(projectRef, matches) {
|
|
109
|
+
return projectResolutionErrorToCliError(new ProjectAmbiguousError(projectRef, matches));
|
|
110
|
+
}
|
|
111
|
+
function projectAmbiguousCliError(projectRef, matches) {
|
|
45
112
|
const firstMatch = matches[0];
|
|
46
113
|
const nextSteps = ["prisma-cli project list"];
|
|
47
114
|
if (firstMatch) nextSteps.push(`prisma-cli app deploy --project ${firstMatch.id}`);
|
|
@@ -59,7 +126,7 @@ function projectAmbiguousError(projectRef, matches) {
|
|
|
59
126
|
nextSteps
|
|
60
127
|
});
|
|
61
128
|
}
|
|
62
|
-
function
|
|
129
|
+
function localStateStaleCliError() {
|
|
63
130
|
return new CliError({
|
|
64
131
|
code: "LOCAL_STATE_STALE",
|
|
65
132
|
domain: "project",
|
|
@@ -71,8 +138,58 @@ function localStateStaleError() {
|
|
|
71
138
|
nextSteps: ["prisma-cli project list", "prisma-cli project link <id-or-name>"]
|
|
72
139
|
});
|
|
73
140
|
}
|
|
141
|
+
function localProjectWorkspaceMismatchError(options) {
|
|
142
|
+
return projectResolutionErrorToCliError(new LocalProjectWorkspaceMismatchError(options));
|
|
143
|
+
}
|
|
144
|
+
function localProjectWorkspaceMismatchCliError(options) {
|
|
145
|
+
return new CliError({
|
|
146
|
+
code: "LOCAL_PROJECT_WORKSPACE_MISMATCH",
|
|
147
|
+
domain: "project",
|
|
148
|
+
summary: "Project link uses another workspace",
|
|
149
|
+
why: `${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} links this directory to project ${options.pinnedProjectId} in workspace ${options.pinnedWorkspaceId}, but your current CLI session is workspace "${options.activeWorkspace.name}" (${options.activeWorkspace.id}).`,
|
|
150
|
+
fix: "Switch to the linked workspace, or relink this directory to a project in the current workspace.",
|
|
151
|
+
meta: {
|
|
152
|
+
pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
|
|
153
|
+
pinnedWorkspaceId: options.pinnedWorkspaceId,
|
|
154
|
+
pinnedProjectId: options.pinnedProjectId,
|
|
155
|
+
activeWorkspaceId: options.activeWorkspace.id,
|
|
156
|
+
activeWorkspaceName: options.activeWorkspace.name
|
|
157
|
+
},
|
|
158
|
+
exitCode: 1,
|
|
159
|
+
nextSteps: [
|
|
160
|
+
`prisma-cli auth workspace use ${options.pinnedWorkspaceId}`,
|
|
161
|
+
"prisma-cli project list",
|
|
162
|
+
"prisma-cli project link <id-or-name>"
|
|
163
|
+
]
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Converts expected project-resolution variants to command-boundary CliErrors.
|
|
168
|
+
* `LocalResolutionPinReadAbortedError` and `UnhandledException` intentionally
|
|
169
|
+
* propagate as exceptions; callers such as `resolveProjectShowInRealMode`
|
|
170
|
+
* throw this helper's result, so passthrough variants should keep bubbling.
|
|
171
|
+
*/
|
|
172
|
+
function projectResolutionErrorToCliError(error) {
|
|
173
|
+
return matchError(error, {
|
|
174
|
+
ProjectNotFoundError: (error) => projectNotFoundCliError(error.projectRef, error.workspace),
|
|
175
|
+
ProjectAmbiguousError: (error) => projectAmbiguousCliError(error.projectRef, error.matches),
|
|
176
|
+
ProjectSetupRequiredError: (error) => projectSetupRequiredCliError(error),
|
|
177
|
+
LocalStateStaleError: () => localStateStaleCliError(),
|
|
178
|
+
LocalProjectWorkspaceMismatchError: (error) => localProjectWorkspaceMismatchCliError({
|
|
179
|
+
pinnedWorkspaceId: error.pinnedWorkspaceId,
|
|
180
|
+
pinnedProjectId: error.pinnedProjectId,
|
|
181
|
+
activeWorkspace: error.activeWorkspace
|
|
182
|
+
}),
|
|
183
|
+
LocalResolutionPinReadAbortedError: (error) => {
|
|
184
|
+
throw error;
|
|
185
|
+
},
|
|
186
|
+
UnhandledException: (error) => {
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
74
191
|
async function buildProjectSetupSuggestion(options) {
|
|
75
|
-
const suggestedName = await inferTargetName(options.cwd);
|
|
192
|
+
const suggestedName = await inferTargetName(options.cwd, options.signal);
|
|
76
193
|
const candidates = sortProjects(options.projects.filter((project) => projectMatchesSuggestedName(project, suggestedName.name))).map(toProjectSummary);
|
|
77
194
|
return {
|
|
78
195
|
suggestedProjectName: suggestedName.name,
|
|
@@ -83,17 +200,24 @@ async function buildProjectSetupSuggestion(options) {
|
|
|
83
200
|
}
|
|
84
201
|
async function projectSetupRequiredError(options) {
|
|
85
202
|
const suggestion = await buildProjectSetupSuggestion(options);
|
|
203
|
+
return new ProjectSetupRequiredError({
|
|
204
|
+
commandName: options.commandName,
|
|
205
|
+
suggestion
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
function projectSetupRequiredCliError(error) {
|
|
209
|
+
const suggestion = error.suggestion;
|
|
86
210
|
return new CliError({
|
|
87
211
|
code: "PROJECT_SETUP_REQUIRED",
|
|
88
212
|
domain: "project",
|
|
89
213
|
summary: "Choose a Project before running this command",
|
|
90
|
-
why:
|
|
214
|
+
why: error.message,
|
|
91
215
|
fix: "Link the directory to an existing Project, or pass --project <id-or-name> for this command.",
|
|
92
216
|
meta: { ...suggestion },
|
|
93
217
|
exitCode: 1,
|
|
94
218
|
nextSteps: ["prisma-cli project list", ...suggestion.recoveryCommands],
|
|
95
219
|
nextActions: buildProjectSetupNextActions({
|
|
96
|
-
commandName:
|
|
220
|
+
commandName: error.commandName,
|
|
97
221
|
suggestedProjectName: suggestion.suggestedProjectName
|
|
98
222
|
})
|
|
99
223
|
});
|
|
@@ -131,9 +255,13 @@ function buildProjectSetupNextActions(options = {}) {
|
|
|
131
255
|
});
|
|
132
256
|
return actions;
|
|
133
257
|
}
|
|
134
|
-
async function readPackageName(cwd) {
|
|
258
|
+
async function readPackageName(cwd, signal) {
|
|
259
|
+
signal?.throwIfAborted();
|
|
135
260
|
try {
|
|
136
|
-
const raw = await readFile(path.join(cwd, "package.json"),
|
|
261
|
+
const raw = await readFile(path.join(cwd, "package.json"), {
|
|
262
|
+
encoding: "utf8",
|
|
263
|
+
signal
|
|
264
|
+
});
|
|
137
265
|
const parsed = JSON.parse(raw);
|
|
138
266
|
if (!parsed || typeof parsed !== "object") return null;
|
|
139
267
|
const packageName = "name" in parsed ? parsed.name : null;
|
|
@@ -144,8 +272,8 @@ async function readPackageName(cwd) {
|
|
|
144
272
|
throw error;
|
|
145
273
|
}
|
|
146
274
|
}
|
|
147
|
-
async function inferTargetName(cwd) {
|
|
148
|
-
const packageName = await readPackageName(cwd);
|
|
275
|
+
async function inferTargetName(cwd, signal) {
|
|
276
|
+
const packageName = await readPackageName(cwd, signal);
|
|
149
277
|
if (packageName && isValidInferredTargetName(packageName)) return {
|
|
150
278
|
name: packageName,
|
|
151
279
|
source: "package-name"
|
|
@@ -163,9 +291,9 @@ function sortProjects(projects) {
|
|
|
163
291
|
}
|
|
164
292
|
function resolveExplicitProject(projectRef, projects, workspace) {
|
|
165
293
|
const matches = projects.filter((project) => project.id === projectRef || project.name === projectRef);
|
|
166
|
-
if (matches.length === 1) return matches[0];
|
|
167
|
-
if (matches.length > 1)
|
|
168
|
-
|
|
294
|
+
if (matches.length === 1) return Result.ok(matches[0]);
|
|
295
|
+
if (matches.length > 1) return Result.err(new ProjectAmbiguousError(projectRef, matches));
|
|
296
|
+
return Result.err(new ProjectNotFoundError(projectRef, workspace));
|
|
169
297
|
}
|
|
170
298
|
function projectMatchesSuggestedName(project, suggestedName) {
|
|
171
299
|
return project.id === suggestedName || project.name === suggestedName || project.slug === suggestedName;
|
|
@@ -174,35 +302,63 @@ async function resolveDurablePlatformMapping() {
|
|
|
174
302
|
return null;
|
|
175
303
|
}
|
|
176
304
|
async function resolveBoundProjectTarget(options, projects, settings) {
|
|
177
|
-
if (options.explicitProject)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
305
|
+
if (options.explicitProject) {
|
|
306
|
+
const projectResult = resolveExplicitProject(options.explicitProject, projects, options.workspace);
|
|
307
|
+
if (projectResult.isErr()) return Result.err(projectResult.error);
|
|
308
|
+
return Result.ok(resolvedTarget(options.workspace, projectResult.value, "explicit", {
|
|
309
|
+
targetName: options.explicitProject,
|
|
310
|
+
targetNameSource: "explicit"
|
|
311
|
+
}));
|
|
312
|
+
}
|
|
181
313
|
if (settings.allowEnvProjectId && options.envProjectId) {
|
|
182
314
|
const project = projects.find((candidate) => candidate.id === options.envProjectId);
|
|
183
|
-
if (!project)
|
|
184
|
-
return resolvedTarget(options.workspace, project, "env", {
|
|
315
|
+
if (!project) return Result.err(new ProjectNotFoundError(options.envProjectId, options.workspace));
|
|
316
|
+
return Result.ok(resolvedTarget(options.workspace, project, "env", {
|
|
185
317
|
targetName: options.envProjectId,
|
|
186
318
|
targetNameSource: "env"
|
|
187
|
-
});
|
|
319
|
+
}));
|
|
188
320
|
}
|
|
189
|
-
const localPin =
|
|
190
|
-
if (localPin
|
|
321
|
+
const localPin = settings.localPin;
|
|
322
|
+
if (!localPin) return Result.ok(null);
|
|
191
323
|
if (localPin.kind === "present") {
|
|
192
|
-
if (localPin.pin.workspaceId !== options.workspace.id)
|
|
324
|
+
if (localPin.pin.workspaceId !== options.workspace.id) return Result.err(new LocalProjectWorkspaceMismatchError({
|
|
325
|
+
pinnedWorkspaceId: localPin.pin.workspaceId,
|
|
326
|
+
pinnedProjectId: localPin.pin.projectId,
|
|
327
|
+
activeWorkspace: options.workspace
|
|
328
|
+
}));
|
|
193
329
|
const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
|
|
194
|
-
if (!project)
|
|
195
|
-
return resolvedTarget(options.workspace, project, "local-pin", {
|
|
330
|
+
if (!project) return Result.err(new LocalStateStaleError());
|
|
331
|
+
return Result.ok(resolvedTarget(options.workspace, project, "local-pin", {
|
|
196
332
|
targetName: project.name,
|
|
197
333
|
targetNameSource: "local-pin"
|
|
198
|
-
});
|
|
334
|
+
}));
|
|
199
335
|
}
|
|
200
336
|
const platformMapping = await resolveDurablePlatformMapping();
|
|
201
|
-
if (platformMapping && platformMapping.workspace.id === options.workspace.id) return resolvedTarget(options.workspace, platformMapping, "platform-mapping", {
|
|
337
|
+
if (platformMapping && platformMapping.workspace.id === options.workspace.id) return Result.ok(resolvedTarget(options.workspace, platformMapping, "platform-mapping", {
|
|
202
338
|
targetName: platformMapping.name,
|
|
203
339
|
targetNameSource: "platform-mapping"
|
|
340
|
+
}));
|
|
341
|
+
return Result.ok(null);
|
|
342
|
+
}
|
|
343
|
+
async function readImplicitLocalPin(options, settings) {
|
|
344
|
+
if (options.explicitProject || settings.allowEnvProjectId && options.envProjectId) return Result.ok(null);
|
|
345
|
+
const localPinResult = await readLocalResolutionPin(options.projectDir ?? options.context.runtime.cwd, options.context.runtime.signal);
|
|
346
|
+
if (localPinResult.isErr()) return Result.err(localPinReadErrorToProjectError(localPinResult.error));
|
|
347
|
+
const localPin = localPinResult.value;
|
|
348
|
+
if (localPin.kind === "present" && localPin.pin.workspaceId !== options.workspace.id) return Result.err(new LocalProjectWorkspaceMismatchError({
|
|
349
|
+
pinnedWorkspaceId: localPin.pin.workspaceId,
|
|
350
|
+
pinnedProjectId: localPin.pin.projectId,
|
|
351
|
+
activeWorkspace: options.workspace
|
|
352
|
+
}));
|
|
353
|
+
return Result.ok(localPin);
|
|
354
|
+
}
|
|
355
|
+
function localPinReadErrorToProjectError(error) {
|
|
356
|
+
return matchError(error, {
|
|
357
|
+
LocalResolutionPinInvalidJsonError: () => new LocalStateStaleError(),
|
|
358
|
+
LocalResolutionPinInvalidShapeError: () => new LocalStateStaleError(),
|
|
359
|
+
LocalResolutionPinReadAbortedError: (error) => error,
|
|
360
|
+
UnhandledException: (error) => error
|
|
204
361
|
});
|
|
205
|
-
return null;
|
|
206
362
|
}
|
|
207
363
|
function resolvedTarget(workspace, project, projectSource, resolutionDetails) {
|
|
208
364
|
return {
|
|
@@ -227,4 +383,4 @@ function toProjectSummary(project) {
|
|
|
227
383
|
};
|
|
228
384
|
}
|
|
229
385
|
//#endregion
|
|
230
|
-
export { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, projectAmbiguousError, projectNotFoundError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects };
|
|
386
|
+
export { buildProjectSetupNextActions, inferTargetName, inspectProjectBinding, localProjectWorkspaceMismatchError, projectAmbiguousError, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects };
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { CliError, usageError } from "../../shell/errors.js";
|
|
2
1
|
import "../../shell/command-arguments.js";
|
|
2
|
+
import { CliError, usageError } from "../../shell/errors.js";
|
|
3
|
+
import { shortenHomePath } from "../fs/home-path.js";
|
|
3
4
|
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, ensureLocalResolutionPinGitignore, writeLocalResolutionPin } from "./local-pin.js";
|
|
4
5
|
import { projectAmbiguousError, projectNotFoundError } from "./resolution.js";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { Result, matchError } from "better-result";
|
|
5
8
|
//#region src/lib/project/setup.ts
|
|
6
9
|
function isValidProjectSetupName(projectName) {
|
|
7
10
|
return projectName.trim().length > 0;
|
|
@@ -16,22 +19,64 @@ function resolveProjectForSetup(projectRef, projects, workspace) {
|
|
|
16
19
|
if (matches.length > 1) throw projectAmbiguousError(projectRef, matches);
|
|
17
20
|
throw projectNotFoundError(projectRef, workspace);
|
|
18
21
|
}
|
|
19
|
-
async function bindProjectToDirectory(context, workspace, project, action) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
async function bindProjectToDirectory(context, workspace, project, action, directory = context.runtime.cwd) {
|
|
23
|
+
return Result.gen(async function* () {
|
|
24
|
+
yield* Result.await(writeLocalResolutionPin(directory, {
|
|
25
|
+
workspaceId: workspace.id,
|
|
26
|
+
projectId: project.id
|
|
27
|
+
}, context.runtime.signal));
|
|
28
|
+
yield* Result.await(ensureLocalResolutionPinGitignore(directory, context.runtime.signal));
|
|
29
|
+
return Result.ok({
|
|
30
|
+
workspace,
|
|
31
|
+
project,
|
|
32
|
+
directory: formatSetupDirectory(directory, context),
|
|
33
|
+
localPin: {
|
|
34
|
+
path: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
|
|
35
|
+
written: true
|
|
36
|
+
},
|
|
37
|
+
action
|
|
38
|
+
});
|
|
23
39
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
localPin: {
|
|
30
|
-
path: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
|
|
31
|
-
written: true
|
|
40
|
+
}
|
|
41
|
+
function projectDirectoryBindingErrorToCliError(error) {
|
|
42
|
+
return matchError(error, {
|
|
43
|
+
LocalResolutionPinSerializationError: (error) => {
|
|
44
|
+
throw error;
|
|
32
45
|
},
|
|
33
|
-
|
|
34
|
-
|
|
46
|
+
LocalResolutionPinWriteAbortedError: (error) => {
|
|
47
|
+
throw error;
|
|
48
|
+
},
|
|
49
|
+
LocalResolutionPinWriteFailedError: (error) => localStateWriteFailedError(error, {
|
|
50
|
+
why: `The CLI could not write ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}.`,
|
|
51
|
+
meta: {
|
|
52
|
+
pinPath: error.pinPath,
|
|
53
|
+
operation: error.operation
|
|
54
|
+
}
|
|
55
|
+
}),
|
|
56
|
+
LocalResolutionPinGitignoreUpdateAbortedError: (error) => {
|
|
57
|
+
throw error;
|
|
58
|
+
},
|
|
59
|
+
LocalResolutionPinGitignoreUpdateFailedError: (error) => localStateWriteFailedError(error, {
|
|
60
|
+
why: "The CLI could not update .gitignore to keep local Project binding state out of git.",
|
|
61
|
+
meta: {
|
|
62
|
+
gitignorePath: error.gitignorePath,
|
|
63
|
+
operation: error.operation
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function localStateWriteFailedError(error, options) {
|
|
69
|
+
return new CliError({
|
|
70
|
+
code: "LOCAL_STATE_WRITE_FAILED",
|
|
71
|
+
domain: "project",
|
|
72
|
+
summary: "Could not save local Project binding",
|
|
73
|
+
why: options.why,
|
|
74
|
+
fix: "Check that this directory is writable and that .prisma/local.json and .gitignore are not blocked by directories or permissions, then retry.",
|
|
75
|
+
debug: formatDebugDetails(error.cause),
|
|
76
|
+
meta: options.meta,
|
|
77
|
+
exitCode: 1,
|
|
78
|
+
nextSteps: ["prisma-cli project link <id-or-name>", "prisma-cli app deploy --project <id-or-name>"]
|
|
79
|
+
});
|
|
35
80
|
}
|
|
36
81
|
function toProjectSummary(project) {
|
|
37
82
|
return {
|
|
@@ -66,8 +111,9 @@ function projectCreateFailedError(error, projectName, workspace, options) {
|
|
|
66
111
|
nextSteps: options.nextSteps
|
|
67
112
|
});
|
|
68
113
|
}
|
|
69
|
-
function formatSetupDirectory(
|
|
70
|
-
|
|
114
|
+
function formatSetupDirectory(directory, context) {
|
|
115
|
+
if (path.resolve(directory) !== path.resolve(context.runtime.cwd)) return shortenHomePath(directory, context.runtime.env);
|
|
116
|
+
const basename = directory.split(/[\\/]/).filter(Boolean).pop();
|
|
71
117
|
return basename ? `./${basename}` : ".";
|
|
72
118
|
}
|
|
73
119
|
function extractHttpStatus(error) {
|
|
@@ -86,4 +132,4 @@ function formatDebugDetails(error) {
|
|
|
86
132
|
return typeof error === "string" ? error : null;
|
|
87
133
|
}
|
|
88
134
|
//#endregion
|
|
89
|
-
export { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary, validateProjectSetupNameText };
|
|
135
|
+
export { bindProjectToDirectory, isValidProjectSetupName, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary, validateProjectSetupNameText };
|
package/dist/output/patterns.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { maskValue, padDisplay, renderSummaryLine } from "../shell/ui.js";
|
|
2
1
|
import { formatDescriptorLabel } from "../shell/command-meta.js";
|
|
2
|
+
import { maskValue, padDisplay, renderSummaryLine } from "../shell/ui.js";
|
|
3
3
|
import stringWidth from "string-width";
|
|
4
4
|
//#region src/output/patterns.ts
|
|
5
5
|
function renderList(input, ui) {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { formatShellCommand } from "../shell/command-arguments.js";
|
|
2
|
+
import { formatDescriptorLabel } from "../shell/command-meta.js";
|
|
3
|
+
import { renderSummaryLine } from "../shell/ui.js";
|
|
4
|
+
//#region src/presenters/agent.ts
|
|
5
|
+
function renderAgentInstall(context, descriptor, result) {
|
|
6
|
+
const ui = context.ui;
|
|
7
|
+
const rail = ui.dim("│");
|
|
8
|
+
return [
|
|
9
|
+
renderSummaryLine(ui, "success", `${formatDescriptorLabel(descriptor)} → ${operationSummary(result)} Prisma skills.`),
|
|
10
|
+
"",
|
|
11
|
+
`${rail} ${ui.accent("skills:")} ${result.skills.status.replace("-", " ")}`,
|
|
12
|
+
`${rail} ${ui.dim(formatShellCommand(result.skills.command))}`
|
|
13
|
+
];
|
|
14
|
+
}
|
|
15
|
+
function renderAgentStatus(context, descriptor, result) {
|
|
16
|
+
const ui = context.ui;
|
|
17
|
+
const rail = ui.dim("│");
|
|
18
|
+
return [
|
|
19
|
+
`${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim(`Checking ${result.statusScope} Prisma skills.`)}`,
|
|
20
|
+
"",
|
|
21
|
+
`${rail} ${ui.accent("skills:")} ${result.skillsInstalled ? "installed" : "not found"}`,
|
|
22
|
+
...renderInstalledSkills(context, result.skills),
|
|
23
|
+
`${rail}`,
|
|
24
|
+
`${rail} ${ui.accent("source:")} ${formatStatusSource(result)}`,
|
|
25
|
+
`${rail} ${ui.dim(formatShellCommand(result.skillsListCommand))}`,
|
|
26
|
+
...renderProjectStatusDetails(context, result)
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
function serializeAgentInstall(result) {
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
function serializeAgentStatus(result) {
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
function formatSetupPromptStatus(result) {
|
|
36
|
+
if (result.skillsInstalled) return "not needed";
|
|
37
|
+
if (result.promptDismissedAt) return `dismissed ${result.promptDismissedAt}`;
|
|
38
|
+
return "active";
|
|
39
|
+
}
|
|
40
|
+
function formatStatusSource(result) {
|
|
41
|
+
if (result.statusSource === "skills-cli") return result.statusScope === "global" ? "skills list -g --json" : "skills list --json";
|
|
42
|
+
if (result.statusSource === "skills-lock") return result.skillsLockPath;
|
|
43
|
+
return "unavailable";
|
|
44
|
+
}
|
|
45
|
+
function renderProjectStatusDetails(context, result) {
|
|
46
|
+
if (result.statusScope !== "project") return [];
|
|
47
|
+
const ui = context.ui;
|
|
48
|
+
const rail = ui.dim("│");
|
|
49
|
+
return [
|
|
50
|
+
`${rail}`,
|
|
51
|
+
`${rail} ${ui.accent("skills lock:")} ${result.skillsLockInstalled ? "installed" : "not found"}`,
|
|
52
|
+
`${rail} ${ui.dim(result.skillsLockPath)}`,
|
|
53
|
+
`${rail}`,
|
|
54
|
+
`${rail} ${ui.accent("setup prompt:")} ${formatSetupPromptStatus(result)}`
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
function renderInstalledSkills(context, skills) {
|
|
58
|
+
const ui = context.ui;
|
|
59
|
+
const rail = ui.dim("│");
|
|
60
|
+
if (skills.length === 0) return [`${rail} ${ui.dim("No Prisma skills reported.")}`];
|
|
61
|
+
return skills.map((skill) => {
|
|
62
|
+
const agents = skill.agents.length > 0 ? skill.agents.join(", ") : "no agents reported";
|
|
63
|
+
return `${rail} ${skill.name} ${ui.dim(`${skill.scope}; ${agents}`)}`;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function operationSummary(result) {
|
|
67
|
+
if (result.skills.status === "would-install") return "Would install";
|
|
68
|
+
return operationLabel(result.operation);
|
|
69
|
+
}
|
|
70
|
+
function operationLabel(operation) {
|
|
71
|
+
return operation === "update" ? "Updated" : "Installed";
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { renderAgentInstall, renderAgentStatus, serializeAgentInstall, serializeAgentStatus };
|