@prisma/cli 3.0.0-dev.125.1 → 3.0.0-dev.129.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/dist/cli2.js +13 -3
- package/dist/commands/feedback/index.js +20 -0
- package/dist/commands/project/index.js +3 -2
- package/dist/controllers/app.js +11 -8
- package/dist/controllers/feedback.js +86 -0
- package/dist/controllers/project.js +5 -2
- package/dist/lib/app/app-provider.js +3 -1
- package/dist/lib/project/resolution.js +2 -1
- package/dist/lib/project/setup.js +2 -1
- package/dist/presenters/app.js +5 -0
- package/dist/presenters/feedback.js +26 -0
- package/dist/presenters/project.js +7 -2
- package/dist/shell/command-meta.js +7 -0
- package/dist/shell/output.js +65 -3
- package/dist/use-cases/project.js +2 -1
- package/package.json +1 -1
- package/dist/lib/app/app-interaction.js +0 -5
package/dist/cli2.js
CHANGED
|
@@ -3,7 +3,7 @@ import { attachCommandDescriptor } from "./shell/command-meta.js";
|
|
|
3
3
|
import { createShellUi } from "./shell/ui.js";
|
|
4
4
|
import { addCompactGlobalFlags } from "./shell/global-flags.js";
|
|
5
5
|
import { configureRuntimeCommand, createCommandContext } from "./shell/runtime.js";
|
|
6
|
-
import { formatUnexpectedError, writeHumanError, writeJsonError, writeJsonSuccess } from "./shell/output.js";
|
|
6
|
+
import { formatUnexpectedError, unexpectedErrorFeedbackCommand, writeHumanError, writeJsonError, writeJsonSuccess, writeJsonUnexpectedError } from "./shell/output.js";
|
|
7
7
|
import { createAgentCommand } from "./commands/agent/index.js";
|
|
8
8
|
import "./shell/prompt.js";
|
|
9
9
|
import { createAppCommand } from "./commands/app/index.js";
|
|
@@ -11,10 +11,11 @@ import { createAuthCommand } from "./commands/auth/index.js";
|
|
|
11
11
|
import { createBranchCommand } from "./commands/branch/index.js";
|
|
12
12
|
import { createBuildCommand } from "./commands/build/index.js";
|
|
13
13
|
import { createDatabaseCommand } from "./commands/database/index.js";
|
|
14
|
+
import { getCliName, getCliVersion } from "./lib/version.js";
|
|
15
|
+
import { createFeedbackCommand } from "./commands/feedback/index.js";
|
|
14
16
|
import { createGitCommand } from "./commands/git/index.js";
|
|
15
17
|
import { createInitCommand } from "./commands/init/index.js";
|
|
16
18
|
import { createProjectCommand } from "./commands/project/index.js";
|
|
17
|
-
import { getCliName, getCliVersion } from "./lib/version.js";
|
|
18
19
|
import { runVersion } from "./controllers/version.js";
|
|
19
20
|
import { createVersionCommand } from "./commands/version/index.js";
|
|
20
21
|
import { maybeWriteCachedUpdateNotification } from "./shell/update-check.js";
|
|
@@ -37,7 +38,15 @@ async function runCli(options = {}) {
|
|
|
37
38
|
return process.exitCode ?? 0;
|
|
38
39
|
} catch (error) {
|
|
39
40
|
if (error instanceof CommanderError) return error.code === "commander.helpDisplayed" ? 0 : 2;
|
|
40
|
-
|
|
41
|
+
if (runtime.argv.includes("--json")) {
|
|
42
|
+
writeJsonUnexpectedError({
|
|
43
|
+
stdout: runtime.stdout,
|
|
44
|
+
stderr: runtime.stderr
|
|
45
|
+
}, runtime.argv, error);
|
|
46
|
+
return 1;
|
|
47
|
+
}
|
|
48
|
+
const quiet = runtime.argv.includes("--quiet") || runtime.argv.includes("-q");
|
|
49
|
+
runtime.stderr.write(formatUnexpectedError(error, runtime.argv.includes("--trace"), quiet ? void 0 : unexpectedErrorFeedbackCommand(runtime.argv, error)));
|
|
41
50
|
return 1;
|
|
42
51
|
} finally {
|
|
43
52
|
runtime.stdin;
|
|
@@ -50,6 +59,7 @@ function createProgram(runtime) {
|
|
|
50
59
|
program.name("prisma").showSuggestionAfterError();
|
|
51
60
|
program.addCommand(createVersionCommand(runtime));
|
|
52
61
|
program.addCommand(createInitCommand(runtime));
|
|
62
|
+
program.addCommand(createFeedbackCommand(runtime));
|
|
53
63
|
program.addCommand(createAgentCommand(runtime));
|
|
54
64
|
program.addCommand(createAuthCommand(runtime));
|
|
55
65
|
program.addCommand(createProjectCommand(runtime));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { attachCommandDescriptor } from "../../shell/command-meta.js";
|
|
2
|
+
import { addGlobalFlags } from "../../shell/global-flags.js";
|
|
3
|
+
import { configureRuntimeCommand } from "../../shell/runtime.js";
|
|
4
|
+
import { runCommand } from "../../shell/command-runner.js";
|
|
5
|
+
import { runFeedback } from "../../controllers/feedback.js";
|
|
6
|
+
import { renderFeedbackSuccess } from "../../presenters/feedback.js";
|
|
7
|
+
import { Command, Option } from "commander";
|
|
8
|
+
//#region src/commands/feedback/index.ts
|
|
9
|
+
function createFeedbackCommand(runtime) {
|
|
10
|
+
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("feedback"), runtime), "feedback");
|
|
11
|
+
command.argument("<message>", "Feedback text (up to 4000 characters)").addOption(new Option("--email <address>", "Contact email if you want a reply; feedback is anonymous without it"));
|
|
12
|
+
addGlobalFlags(command);
|
|
13
|
+
command.action(async (message, options) => {
|
|
14
|
+
const flags = options;
|
|
15
|
+
await runCommand(runtime, "feedback", options, (context) => runFeedback(context, message, { email: flags.email }), { renderHuman: (context, descriptor, result) => renderFeedbackSuccess(context, descriptor, result) });
|
|
16
|
+
});
|
|
17
|
+
return command;
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { createFeedbackCommand };
|
|
@@ -67,10 +67,11 @@ function createProjectTransferCommand(runtime) {
|
|
|
67
67
|
}
|
|
68
68
|
function createProjectCreateCommand(runtime) {
|
|
69
69
|
const command = attachCommandDescriptor(configureRuntimeCommand(new Command("create"), runtime), "project.create");
|
|
70
|
-
command.argument("<name>", "Project name");
|
|
70
|
+
command.argument("<name>", "Project name").addOption(new Option("--region <region>", "Prisma Compute region id"));
|
|
71
71
|
addGlobalFlags(command);
|
|
72
72
|
command.action(async (name, options) => {
|
|
73
|
-
|
|
73
|
+
const region = options.region;
|
|
74
|
+
await runCommand(runtime, "project.create", options, (context) => runProjectCreate(context, String(name), { region }), {
|
|
74
75
|
renderHuman: (context, descriptor, result) => renderProjectSetup(context, descriptor, result),
|
|
75
76
|
renderJson: (result) => serializeProjectSetup(result)
|
|
76
77
|
});
|
package/dist/controllers/app.js
CHANGED
|
@@ -5,12 +5,11 @@ import { canPrompt } from "../shell/runtime.js";
|
|
|
5
5
|
import { writeJsonEvent } from "../shell/output.js";
|
|
6
6
|
import { SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "../lib/auth/client.js";
|
|
7
7
|
import { FileTokenStorage } from "../adapters/token-storage.js";
|
|
8
|
-
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
9
|
-
import "../lib/app/app-interaction.js";
|
|
10
8
|
import { PRISMA_APP_CONFIG_FILENAME, detectLegacyBuildSettings, resolveConfiguredAppBuildSettings, resolveInferredAppBuildSettings } from "../lib/app/build-settings.js";
|
|
11
9
|
import { APP_BUILD_TYPES, APP_BUILD_TYPE_LABELS, RESOLVED_APP_BUILD_TYPES, executeAppBuild } from "../lib/app/build.js";
|
|
12
10
|
import { envVarNames, parseEnvInputs } from "../lib/app/env-vars.js";
|
|
13
11
|
import { DomainApiError, createAppProvider } from "../lib/app/app-provider.js";
|
|
12
|
+
import { confirmPrompt, selectPrompt, textPrompt } from "../shell/prompt.js";
|
|
14
13
|
import { LOCAL_RESOLUTION_PIN_RELATIVE_PATH, readLocalResolutionPin } from "../lib/project/local-pin.js";
|
|
15
14
|
import { buildProjectSetupNextActions, inferTargetName, localProjectWorkspaceMismatchError, projectNotFoundError, projectResolutionErrorToCliError, resolveDurablePlatformMapping, resolveProjectTarget, sortProjects } from "../lib/project/resolution.js";
|
|
16
15
|
import { bindProjectToDirectory, projectCreateFailedError, projectDirectoryBindingErrorToCliError, projectSetupNameRequiredError, resolveProjectForSetup, toProjectSummary } from "../lib/project/setup.js";
|
|
@@ -281,6 +280,7 @@ async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
|
|
|
281
280
|
const { provider, target, projectId } = await requireProviderAndDeployProjectContext(context, options?.projectRef, {
|
|
282
281
|
branch,
|
|
283
282
|
createProjectName: options?.createProjectName,
|
|
283
|
+
createProjectRegion: deployRegion?.value,
|
|
284
284
|
envProjectId,
|
|
285
285
|
localPin
|
|
286
286
|
});
|
|
@@ -416,6 +416,7 @@ async function runSingleAppDeploy(context, appName, options, preloadedConfig) {
|
|
|
416
416
|
entrypoint: entrypoint ?? buildSettingsResolution.settings.entrypoint ?? null,
|
|
417
417
|
httpPort: runtime.port,
|
|
418
418
|
region: deployResult.app.region ?? selectedApp.region ?? null,
|
|
419
|
+
regionSource: deployRegion?.annotation ?? null,
|
|
419
420
|
envVars: envVarNames(envVars)
|
|
420
421
|
},
|
|
421
422
|
durationMs: deployDurationMs,
|
|
@@ -1424,7 +1425,7 @@ async function resolveAmbiguousDeployApp(context, matches, targetName, requested
|
|
|
1424
1425
|
});
|
|
1425
1426
|
}
|
|
1426
1427
|
function deployNewAppRegion(configRegion) {
|
|
1427
|
-
return configRegion?.value
|
|
1428
|
+
return configRegion?.value;
|
|
1428
1429
|
}
|
|
1429
1430
|
async function resolveExistingAppSelection(context, projectId, apps, explicitAppName) {
|
|
1430
1431
|
if (explicitAppName) {
|
|
@@ -1654,7 +1655,7 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1654
1655
|
if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
|
|
1655
1656
|
return withRemoteDeployBranch(provider, {
|
|
1656
1657
|
workspace,
|
|
1657
|
-
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal)),
|
|
1658
|
+
project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal, options.createProjectRegion)),
|
|
1658
1659
|
resolution: {
|
|
1659
1660
|
projectSource: "created",
|
|
1660
1661
|
targetName: projectName,
|
|
@@ -1705,14 +1706,14 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
|
|
|
1705
1706
|
targetNameSource: "platform-mapping"
|
|
1706
1707
|
}
|
|
1707
1708
|
}, branch, context.runtime.signal);
|
|
1708
|
-
if (canPrompt(context) && !context.flags.yes) return withRemoteDeployBranch(provider, await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects), branch, context.runtime.signal);
|
|
1709
|
+
if (canPrompt(context) && !context.flags.yes) return withRemoteDeployBranch(provider, await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects, options.createProjectRegion), branch, context.runtime.signal);
|
|
1709
1710
|
throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
|
|
1710
1711
|
}
|
|
1711
|
-
async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects) {
|
|
1712
|
+
async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects, createProjectRegion) {
|
|
1712
1713
|
const setup = await promptForProjectSetupChoice({
|
|
1713
1714
|
context,
|
|
1714
1715
|
projects,
|
|
1715
|
-
createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal),
|
|
1716
|
+
createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal, createProjectRegion),
|
|
1716
1717
|
cancel: {
|
|
1717
1718
|
why: "Deploy needs a Project before it can continue.",
|
|
1718
1719
|
fix: "Choose an existing Project or create a new one, then rerun deploy.",
|
|
@@ -1730,9 +1731,10 @@ async function resolveInteractiveDeployProjectSetup(context, provider, workspace
|
|
|
1730
1731
|
localPinAction: setup.action
|
|
1731
1732
|
};
|
|
1732
1733
|
}
|
|
1733
|
-
async function createProjectForDeploySetup(provider, projectName, workspace, signal) {
|
|
1734
|
+
async function createProjectForDeploySetup(provider, projectName, workspace, signal, region) {
|
|
1734
1735
|
const created = await provider.createProject({
|
|
1735
1736
|
name: projectName,
|
|
1737
|
+
region,
|
|
1736
1738
|
signal
|
|
1737
1739
|
}).catch((error) => {
|
|
1738
1740
|
throw projectCreateFailedError(error, projectName, workspace, {
|
|
@@ -1748,6 +1750,7 @@ async function createProjectForDeploySetup(provider, projectName, workspace, sig
|
|
|
1748
1750
|
return {
|
|
1749
1751
|
id: created.id,
|
|
1750
1752
|
name: created.name,
|
|
1753
|
+
...created.defaultRegion != null ? { defaultRegion: created.defaultRegion } : {},
|
|
1751
1754
|
workspace
|
|
1752
1755
|
};
|
|
1753
1756
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { CliError, usageError } from "../shell/errors.js";
|
|
2
|
+
import { getCliVersion } from "../lib/version.js";
|
|
3
|
+
//#region src/controllers/feedback.ts
|
|
4
|
+
const DEFAULT_FEEDBACK_ENDPOINT = "https://hiieirp2pwqnjvq9axzyg6d0.fra.prisma.build/feedback";
|
|
5
|
+
const FEEDBACK_TIMEOUT_MS = 3e3;
|
|
6
|
+
const MAX_MESSAGE_LENGTH = 4e3;
|
|
7
|
+
const MAX_EMAIL_LENGTH = 320;
|
|
8
|
+
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
9
|
+
async function runFeedback(context, messageArg, flags) {
|
|
10
|
+
const message = messageArg.trim();
|
|
11
|
+
if (!message) throw usageError("Feedback message required", "The message argument is empty.", "Pass a non-empty message.", ["prisma-cli feedback \"the deploy flow is great\""]);
|
|
12
|
+
if (message.length > MAX_MESSAGE_LENGTH) throw usageError("Feedback message too long", `The message is ${message.length} characters; the limit is ${MAX_MESSAGE_LENGTH}.`, "Shorten the message.");
|
|
13
|
+
const email = flags.email?.trim();
|
|
14
|
+
if (email !== void 0 && (email.length > MAX_EMAIL_LENGTH || !EMAIL_PATTERN.test(email))) throw usageError("Invalid email", `"${flags.email}" is not a valid email address of at most ${MAX_EMAIL_LENGTH} characters.`, "Pass a valid address with --email, or drop the flag to stay anonymous.", ["prisma-cli feedback \"please add X\" --email you@example.com"]);
|
|
15
|
+
const feedbackContext = {
|
|
16
|
+
cliVersion: getCliVersion(),
|
|
17
|
+
nodeVersion: process.version,
|
|
18
|
+
platform: process.platform,
|
|
19
|
+
arch: process.arch
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
command: "feedback",
|
|
23
|
+
result: {
|
|
24
|
+
id: await postFeedback(context, context.runtime.env.PRISMA_CLI_FEEDBACK_URL || DEFAULT_FEEDBACK_ENDPOINT, {
|
|
25
|
+
message,
|
|
26
|
+
...email ? { email } : {},
|
|
27
|
+
meta: { ...feedbackContext }
|
|
28
|
+
}),
|
|
29
|
+
email: email ?? null,
|
|
30
|
+
context: feedbackContext
|
|
31
|
+
},
|
|
32
|
+
warnings: [],
|
|
33
|
+
nextSteps: []
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async function postFeedback(context, endpoint, body) {
|
|
37
|
+
let response;
|
|
38
|
+
try {
|
|
39
|
+
response = await fetch(endpoint, {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers: {
|
|
42
|
+
"content-type": "application/json",
|
|
43
|
+
"user-agent": `prisma-cli/${getCliVersion()}`
|
|
44
|
+
},
|
|
45
|
+
body: JSON.stringify(body),
|
|
46
|
+
signal: AbortSignal.any([context.runtime.signal, AbortSignal.timeout(FEEDBACK_TIMEOUT_MS)])
|
|
47
|
+
});
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (context.runtime.signal.aborted) throw error;
|
|
50
|
+
throw feedbackSendFailed(error instanceof Error && error.name === "TimeoutError" ? TIMEOUT_DETAIL : `The feedback service could not be reached${error instanceof Error && error.cause instanceof Error ? ` (${error.cause.message})` : ""}.`);
|
|
51
|
+
}
|
|
52
|
+
if (!response.ok) throw feedbackSendFailed(`The feedback service responded with HTTP ${response.status}${await readServiceError(context, response)}.`);
|
|
53
|
+
let payload;
|
|
54
|
+
try {
|
|
55
|
+
payload = await response.json();
|
|
56
|
+
} catch (error) {
|
|
57
|
+
if (context.runtime.signal.aborted) throw error;
|
|
58
|
+
if (!(error instanceof SyntaxError)) throw feedbackSendFailed(error instanceof Error && error.name === "TimeoutError" ? TIMEOUT_DETAIL : "The feedback service response could not be read.");
|
|
59
|
+
payload = null;
|
|
60
|
+
}
|
|
61
|
+
return typeof payload?.id === "string" ? payload.id : null;
|
|
62
|
+
}
|
|
63
|
+
const TIMEOUT_DETAIL = `The feedback service did not answer within ${FEEDBACK_TIMEOUT_MS / 1e3} seconds.`;
|
|
64
|
+
async function readServiceError(context, response) {
|
|
65
|
+
let payload;
|
|
66
|
+
try {
|
|
67
|
+
payload = await response.json();
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (context.runtime.signal.aborted) throw error;
|
|
70
|
+
return "";
|
|
71
|
+
}
|
|
72
|
+
return typeof payload?.error?.message === "string" ? ` (${payload.error.message})` : "";
|
|
73
|
+
}
|
|
74
|
+
function feedbackSendFailed(detail) {
|
|
75
|
+
return new CliError({
|
|
76
|
+
code: "FEEDBACK_SEND_FAILED",
|
|
77
|
+
domain: "cli",
|
|
78
|
+
summary: "Feedback could not be delivered",
|
|
79
|
+
why: detail,
|
|
80
|
+
fix: "Check your network and rerun the command.",
|
|
81
|
+
exitCode: 1,
|
|
82
|
+
nextSteps: []
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { runFeedback };
|
|
@@ -104,7 +104,7 @@ async function runProjectShow(context, explicitProject) {
|
|
|
104
104
|
}) : []
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
|
-
async function runProjectCreate(context, projectName) {
|
|
107
|
+
async function runProjectCreate(context, projectName, options) {
|
|
108
108
|
const workspace = (await requireAuthenticatedAuthState(context)).workspace;
|
|
109
109
|
if (!workspace) throw workspaceRequiredError();
|
|
110
110
|
if (!isValidProjectSetupName(projectName)) throw projectSetupNameRequiredError("project create");
|
|
@@ -115,6 +115,7 @@ async function runProjectCreate(context, projectName) {
|
|
|
115
115
|
const name = projectName.trim();
|
|
116
116
|
const created = await provider.createProject({
|
|
117
117
|
name,
|
|
118
|
+
region: options?.region,
|
|
118
119
|
signal: context.runtime.signal
|
|
119
120
|
}).catch((error) => {
|
|
120
121
|
throw projectCreateFailedError(error, name, workspace, {
|
|
@@ -125,7 +126,8 @@ async function runProjectCreate(context, projectName) {
|
|
|
125
126
|
});
|
|
126
127
|
const bindResult = await bindProjectToDirectory(context, workspace, {
|
|
127
128
|
id: created.id,
|
|
128
|
-
name: created.name
|
|
129
|
+
name: created.name,
|
|
130
|
+
...created.defaultRegion != null ? { defaultRegion: created.defaultRegion } : {}
|
|
129
131
|
}, "created");
|
|
130
132
|
if (bindResult.isErr()) throw projectDirectoryBindingErrorToCliError(bindResult.error);
|
|
131
133
|
return {
|
|
@@ -704,6 +706,7 @@ async function listRealWorkspaceProjects(client, workspace, signal) {
|
|
|
704
706
|
id: project.id,
|
|
705
707
|
name: project.name,
|
|
706
708
|
..."url" in project && typeof project.url === "string" ? { url: project.url } : {},
|
|
709
|
+
..."defaultRegion" in project ? { defaultRegion: project.defaultRegion } : {},
|
|
707
710
|
slug: "slug" in project && typeof project.slug === "string" ? project.slug : null,
|
|
708
711
|
workspace: {
|
|
709
712
|
id: project.workspace.id,
|
|
@@ -22,12 +22,14 @@ function createAppProvider(client, options) {
|
|
|
22
22
|
async createProject(options) {
|
|
23
23
|
const projectResult = await sdk.createProject({
|
|
24
24
|
name: options.name,
|
|
25
|
+
region: options.region,
|
|
25
26
|
signal: options.signal
|
|
26
27
|
});
|
|
27
28
|
if (projectResult.isErr()) throw new Error(projectResult.error.message);
|
|
28
29
|
return {
|
|
29
30
|
id: projectResult.value.id,
|
|
30
|
-
name: projectResult.value.name
|
|
31
|
+
name: projectResult.value.name,
|
|
32
|
+
defaultRegion: projectResult.value.defaultRegion
|
|
31
33
|
};
|
|
32
34
|
},
|
|
33
35
|
async listApps(projectId, options) {
|
|
@@ -379,7 +379,8 @@ function toProjectSummary(project) {
|
|
|
379
379
|
return {
|
|
380
380
|
id: project.id,
|
|
381
381
|
name: project.name,
|
|
382
|
-
...project.url ? { url: project.url } : {}
|
|
382
|
+
...project.url ? { url: project.url } : {},
|
|
383
|
+
...project.defaultRegion != null ? { defaultRegion: project.defaultRegion } : {}
|
|
383
384
|
};
|
|
384
385
|
}
|
|
385
386
|
//#endregion
|
|
@@ -82,7 +82,8 @@ function toProjectSummary(project) {
|
|
|
82
82
|
return {
|
|
83
83
|
id: project.id,
|
|
84
84
|
name: project.name,
|
|
85
|
-
...project.url ? { url: project.url } : {}
|
|
85
|
+
...project.url ? { url: project.url } : {},
|
|
86
|
+
...project.defaultRegion != null ? { defaultRegion: project.defaultRegion } : {}
|
|
86
87
|
};
|
|
87
88
|
}
|
|
88
89
|
function projectSetupNameRequiredError(command) {
|
package/dist/presenters/app.js
CHANGED
|
@@ -48,6 +48,11 @@ function renderAppDeploy(context, descriptor, result, options) {
|
|
|
48
48
|
label: "Logs",
|
|
49
49
|
value: logsCommand
|
|
50
50
|
},
|
|
51
|
+
...result.deploySettings.region && result.deploySettings.regionSource === null ? [{
|
|
52
|
+
label: "Region",
|
|
53
|
+
value: result.deploySettings.region,
|
|
54
|
+
origin: result.project.defaultRegion != null ? "project default" : "platform default — pass --region to choose"
|
|
55
|
+
}] : [],
|
|
51
56
|
...deployUsedComputeConfig(result) ? [] : [{
|
|
52
57
|
label: "Config",
|
|
53
58
|
value: resolvePrismaCliPackageCommandSync(context.runtime.cwd, ["init"])
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { renderShow } from "../output/patterns.js";
|
|
2
|
+
//#region src/presenters/feedback.ts
|
|
3
|
+
function renderFeedbackSuccess(context, descriptor, result) {
|
|
4
|
+
return renderShow({
|
|
5
|
+
title: "Feedback sent. Thank you!",
|
|
6
|
+
descriptor,
|
|
7
|
+
fields: [
|
|
8
|
+
...result.id ? [{
|
|
9
|
+
key: "id",
|
|
10
|
+
value: result.id
|
|
11
|
+
}] : [],
|
|
12
|
+
{
|
|
13
|
+
key: "sent as",
|
|
14
|
+
value: result.email ?? "anonymous",
|
|
15
|
+
tone: result.email ? "default" : "dim"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
key: "included",
|
|
19
|
+
value: `CLI ${result.context.cliVersion}, node ${result.context.nodeVersion}, ${result.context.platform} ${result.context.arch}`,
|
|
20
|
+
tone: "dim"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}, context.ui);
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { renderFeedbackSuccess };
|
|
@@ -17,9 +17,13 @@ function renderProjectList(context, descriptor, result) {
|
|
|
17
17
|
return lines;
|
|
18
18
|
}
|
|
19
19
|
const nameWidth = Math.max(4, ...result.projects.map((project) => stringWidth(project.name)));
|
|
20
|
+
const idWidth = Math.max(2, ...result.projects.map((project) => project.id.length));
|
|
20
21
|
lines.push(rail);
|
|
21
|
-
lines.push(`${rail} ${ui.accent(padDisplay("name", nameWidth))} ${ui.accent("id")}`);
|
|
22
|
-
for (const project of result.projects)
|
|
22
|
+
lines.push(`${rail} ${ui.accent(padDisplay("name", nameWidth))} ${ui.accent(padDisplay("id", idWidth))} ${ui.accent("region")}`);
|
|
23
|
+
for (const project of result.projects) {
|
|
24
|
+
const region = project.defaultRegion ? project.defaultRegion : ui.dim("none");
|
|
25
|
+
lines.push(`${rail} ${padDisplay(project.name, nameWidth)} ${padDisplay(project.id, idWidth)} ${region}`);
|
|
26
|
+
}
|
|
23
27
|
if (result.localBinding?.status === "not-linked" || result.localBinding?.status === "invalid") lines.push(...renderNextSteps(["Link an existing Project you choose: prisma-cli project link <id-or-name>", "Create a new Project: prisma-cli project create <name>"]));
|
|
24
28
|
return lines;
|
|
25
29
|
}
|
|
@@ -241,6 +245,7 @@ function renderBoundProjectShow(context, descriptor, result) {
|
|
|
241
245
|
lines.push(rail);
|
|
242
246
|
lines.push(`${rail} ${ui.dim("→")} ${ui.link(result.project.url)}`);
|
|
243
247
|
}
|
|
248
|
+
if (result.project.defaultRegion) lines.push(`${rail} ${ui.accent(padDisplay("region", keyWidth))} ${ui.dim(result.project.defaultRegion)}`);
|
|
244
249
|
lines.push(...renderResolvedProjectContextBlock(context.ui, {
|
|
245
250
|
workspace: result.workspace,
|
|
246
251
|
project: result.project,
|
|
@@ -20,6 +20,13 @@ const DESCRIPTORS = [
|
|
|
20
20
|
description: "Show CLI build and environment",
|
|
21
21
|
examples: ["prisma-cli version", "prisma-cli version --json"]
|
|
22
22
|
},
|
|
23
|
+
{
|
|
24
|
+
id: "feedback",
|
|
25
|
+
path: ["prisma", "feedback"],
|
|
26
|
+
description: "Send feedback to the Prisma CLI team",
|
|
27
|
+
longDescription: "Anonymous unless --email is passed. Every submission includes the CLI version, node version, and OS platform/arch, and nothing else.",
|
|
28
|
+
examples: ["prisma-cli feedback \"the deploy flow is great\"", "prisma-cli feedback \"please add X\" --email you@example.com"]
|
|
29
|
+
},
|
|
23
30
|
{
|
|
24
31
|
id: "init",
|
|
25
32
|
path: ["prisma", "init"],
|
package/dist/shell/output.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatCommandArgument } from "./command-arguments.js";
|
|
1
2
|
import { renderNextSteps, renderSummaryLine } from "./ui.js";
|
|
2
3
|
//#region src/shell/output.ts
|
|
3
4
|
function writeJsonSuccess(output, success) {
|
|
@@ -23,15 +24,76 @@ function cliErrorToJson(error) {
|
|
|
23
24
|
docsUrl: error.docsUrl
|
|
24
25
|
};
|
|
25
26
|
}
|
|
26
|
-
function formatUnexpectedError(error, trace) {
|
|
27
|
+
function formatUnexpectedError(error, trace, feedbackCommand) {
|
|
28
|
+
const feedbackLine = feedbackCommand ? `Tell us what happened: ${feedbackCommand}` : null;
|
|
27
29
|
const debug = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
28
|
-
if (trace) return
|
|
30
|
+
if (trace) return [
|
|
31
|
+
debug,
|
|
32
|
+
...feedbackLine ? [feedbackLine] : [],
|
|
33
|
+
""
|
|
34
|
+
].join("\n");
|
|
29
35
|
return [
|
|
30
36
|
`Unexpected CLI error: ${error instanceof Error && error.message ? error.message : String(error)}`,
|
|
31
37
|
"More: Re-run with --trace for deeper diagnostics",
|
|
38
|
+
...feedbackLine ? [feedbackLine] : [],
|
|
32
39
|
""
|
|
33
40
|
].join("\n");
|
|
34
41
|
}
|
|
42
|
+
/** Command groups whose second argv token names the subcommand. */
|
|
43
|
+
const COMMAND_GROUPS = new Set([
|
|
44
|
+
"agent",
|
|
45
|
+
"auth",
|
|
46
|
+
"project",
|
|
47
|
+
"git",
|
|
48
|
+
"branch",
|
|
49
|
+
"build",
|
|
50
|
+
"database",
|
|
51
|
+
"app"
|
|
52
|
+
]);
|
|
53
|
+
/** Best-effort `command` label for a crash that predates envelope assembly. */
|
|
54
|
+
function unexpectedErrorCommandLabel(argv) {
|
|
55
|
+
const [group, action] = argv.filter((arg) => !arg.startsWith("-"));
|
|
56
|
+
if (!group) return "unknown";
|
|
57
|
+
return action && COMMAND_GROUPS.has(group) ? `${group}.${action}` : group;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Pre-filled feedback command for a crash, so the report arrives carrying the
|
|
61
|
+
* failing command and the error's first line.
|
|
62
|
+
*/
|
|
63
|
+
function unexpectedErrorFeedbackCommand(argv, error) {
|
|
64
|
+
return `prisma-cli feedback ${formatCommandArgument(`${unexpectedErrorCommandLabel(argv).replace(".", " ")} crashed: ${(error instanceof Error && error.message ? error.message : String(error)).split("\n")[0]}`.slice(0, 200))}`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A crash must not break the `--json` contract: agents still get a structured
|
|
68
|
+
* envelope, with a recover action pointing at the feedback command.
|
|
69
|
+
*/
|
|
70
|
+
function writeJsonUnexpectedError(output, argv, error) {
|
|
71
|
+
const feedbackCommand = unexpectedErrorFeedbackCommand(argv, error);
|
|
72
|
+
output.stdout.write(`${JSON.stringify({
|
|
73
|
+
ok: false,
|
|
74
|
+
command: unexpectedErrorCommandLabel(argv),
|
|
75
|
+
error: {
|
|
76
|
+
code: "UNEXPECTED_ERROR",
|
|
77
|
+
domain: "cli",
|
|
78
|
+
severity: "error",
|
|
79
|
+
summary: "Unexpected CLI error",
|
|
80
|
+
why: error instanceof Error && error.message ? error.message : String(error),
|
|
81
|
+
fix: "Re-run with --trace for deeper diagnostics, and report the crash.",
|
|
82
|
+
where: null,
|
|
83
|
+
meta: {},
|
|
84
|
+
docsUrl: null
|
|
85
|
+
},
|
|
86
|
+
warnings: [],
|
|
87
|
+
nextSteps: [feedbackCommand],
|
|
88
|
+
nextActions: [{
|
|
89
|
+
kind: "run-command",
|
|
90
|
+
journey: "recover",
|
|
91
|
+
label: "Report this crash to the Prisma team",
|
|
92
|
+
command: feedbackCommand,
|
|
93
|
+
reason: "This looks like a CLI bug; the pre-filled report carries the failing command and error."
|
|
94
|
+
}]
|
|
95
|
+
}, null, 2)}\n`);
|
|
96
|
+
}
|
|
35
97
|
function writeJsonError(output, command, error) {
|
|
36
98
|
output.stdout.write(`${JSON.stringify({
|
|
37
99
|
ok: false,
|
|
@@ -79,4 +141,4 @@ function writeHumanError(output, ui, error, options) {
|
|
|
79
141
|
writeHumanLines(output, lines);
|
|
80
142
|
}
|
|
81
143
|
//#endregion
|
|
82
|
-
export { cliErrorToJson, formatUnexpectedError, writeHumanError, writeHumanLines, writeJsonError, writeJsonEvent, writeJsonSuccess };
|
|
144
|
+
export { cliErrorToJson, formatUnexpectedError, unexpectedErrorFeedbackCommand, writeHumanError, writeHumanLines, writeJsonError, writeJsonEvent, writeJsonSuccess, writeJsonUnexpectedError };
|
|
@@ -23,7 +23,8 @@ function toProjectSummary(project) {
|
|
|
23
23
|
return {
|
|
24
24
|
id: project.id,
|
|
25
25
|
name: project.name,
|
|
26
|
-
...project.url ? { url: project.url } : {}
|
|
26
|
+
...project.url ? { url: project.url } : {},
|
|
27
|
+
...project.defaultRegion != null ? { defaultRegion: project.defaultRegion } : {}
|
|
27
28
|
};
|
|
28
29
|
}
|
|
29
30
|
//#endregion
|
package/package.json
CHANGED