@prisma/cli 3.0.0-dev.125.1 → 3.0.0-dev.127.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 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
- runtime.stderr.write(formatUnexpectedError(error, runtime.argv.includes("--trace")));
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 };
@@ -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 };
@@ -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 };
@@ -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"],
@@ -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 `${debug}\n`;
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/cli",
3
- "version": "3.0.0-dev.125.1",
3
+ "version": "3.0.0-dev.127.1",
4
4
  "description": "Command-line interface for the Prisma Developer Platform.",
5
5
  "type": "module",
6
6
  "bin": {