@prisma/cli 3.0.0-beta.4 → 3.0.0-beta.6
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 +1 -0
- package/dist/adapters/local-state.js +1 -1
- package/dist/cli2.js +2 -3
- package/dist/commands/app/index.js +21 -11
- package/dist/commands/env.js +8 -4
- package/dist/controllers/app-env-api.js +54 -0
- package/dist/controllers/app-env-file.js +181 -0
- package/dist/controllers/app-env.js +71 -82
- package/dist/controllers/app.js +55 -4
- package/dist/controllers/branch.js +5 -0
- package/dist/lib/app/branch-database-deploy.js +323 -0
- package/dist/lib/app/branch-database.js +316 -0
- package/dist/lib/app/env-config.js +1 -1
- package/dist/lib/app/env-file.js +82 -0
- package/dist/lib/app/preview-branch-database.js +102 -0
- package/dist/lib/app/preview-provider.js +19 -0
- package/dist/lib/diagnostics.js +15 -0
- package/dist/lib/git/local-status.js +57 -0
- package/dist/lib/project/resolution.js +49 -4
- package/dist/presenters/app-env.js +137 -13
- package/dist/presenters/app.js +165 -20
- package/dist/presenters/branch.js +10 -3
- package/dist/presenters/project.js +41 -17
- package/dist/presenters/verbose-context.js +64 -0
- package/dist/shell/command-meta.js +6 -0
- package/dist/shell/command-runner.js +17 -1
- package/dist/shell/diagnostics-output.js +63 -0
- package/dist/shell/output.js +10 -1
- package/dist/shell/runtime.js +1 -1
- package/dist/shell/ui.js +20 -1
- package/package.json +4 -3
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import { padDisplay, renderNextSteps, renderSummaryLine } from "../shell/ui.js";
|
|
1
|
+
import { padDisplay, renderNextSteps, renderSummaryLine, renderVerboseBlock } from "../shell/ui.js";
|
|
2
2
|
import { formatDescriptorLabel } from "../shell/command-meta.js";
|
|
3
3
|
import { formatCommandArgument } from "../shell/command-arguments.js";
|
|
4
|
-
import {
|
|
4
|
+
import { renderMutate, renderShow, serializeList } from "../output/patterns.js";
|
|
5
|
+
import { renderResolvedProjectContextBlock } from "./verbose-context.js";
|
|
5
6
|
import path from "node:path";
|
|
7
|
+
import stringWidth from "string-width";
|
|
6
8
|
//#region src/presenters/project.ts
|
|
7
9
|
function renderProjectList(context, descriptor, result) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
emptyMessage: "No projects found."
|
|
22
|
-
}, context.ui);
|
|
10
|
+
const ui = context.ui;
|
|
11
|
+
const rail = ui.dim("│");
|
|
12
|
+
const lines = [`${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim("Listing projects for the authenticated workspace.")}`, ""];
|
|
13
|
+
lines.push(`${rail} ${ui.accent("workspace:")} ${result.workspace.name}`);
|
|
14
|
+
if (result.projects.length === 0) {
|
|
15
|
+
lines.push(`${rail} ${ui.dim("No projects found.")}`);
|
|
16
|
+
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>"]));
|
|
17
|
+
return lines;
|
|
18
|
+
}
|
|
19
|
+
const nameWidth = Math.max(4, ...result.projects.map((project) => stringWidth(project.name)));
|
|
20
|
+
lines.push(rail);
|
|
21
|
+
lines.push(`${rail} ${ui.accent(padDisplay("name", nameWidth))} ${ui.accent("id")}`);
|
|
22
|
+
for (const project of result.projects) lines.push(`${rail} ${padDisplay(project.name, nameWidth)} ${project.id}`);
|
|
23
23
|
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
24
|
return lines;
|
|
25
25
|
}
|
|
@@ -51,6 +51,25 @@ function renderProjectShow(context, descriptor, result) {
|
|
|
51
51
|
tone: "warning"
|
|
52
52
|
}]
|
|
53
53
|
}, context.ui);
|
|
54
|
+
lines.push(...renderVerboseBlock(context.ui, [
|
|
55
|
+
{
|
|
56
|
+
key: "workspace",
|
|
57
|
+
value: result.workspace.name
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
key: "workspace id",
|
|
61
|
+
value: result.workspace.id,
|
|
62
|
+
tone: "dim"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
key: "project source",
|
|
66
|
+
value: "unbound"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
key: "suggested name",
|
|
70
|
+
value: `${result.suggestedProjectName} (${result.suggestedProjectNameSource})`
|
|
71
|
+
}
|
|
72
|
+
], { title: "Resolved context" }));
|
|
54
73
|
lines.push(...renderNextSteps(["Link an existing Project you choose: prisma-cli project link <id-or-name>", `Create a new Project: prisma-cli project create ${formatCommandArgument(result.suggestedProjectName)}`]));
|
|
55
74
|
return lines;
|
|
56
75
|
}
|
|
@@ -133,6 +152,11 @@ function renderBoundProjectShow(context, descriptor, result) {
|
|
|
133
152
|
lines.push(rail);
|
|
134
153
|
lines.push(`${rail} ${ui.dim("→")} ${ui.link(result.project.url)}`);
|
|
135
154
|
}
|
|
155
|
+
lines.push(...renderResolvedProjectContextBlock(context.ui, {
|
|
156
|
+
workspace: result.workspace,
|
|
157
|
+
project: result.project,
|
|
158
|
+
resolution: result.resolution
|
|
159
|
+
}));
|
|
136
160
|
return lines;
|
|
137
161
|
}
|
|
138
162
|
function formatLocalRepoPath(cwd, env) {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { renderVerboseBlock } from "../shell/ui.js";
|
|
2
|
+
//#region src/presenters/verbose-context.ts
|
|
3
|
+
function renderResolvedProjectContextBlock(ui, context, options = {}) {
|
|
4
|
+
if (!context) return [];
|
|
5
|
+
return renderVerboseBlock(ui, [...projectResolutionRows(context), ...options.extraRows ?? []], { title: options.title ?? "Resolved context" });
|
|
6
|
+
}
|
|
7
|
+
function projectResolutionRows(context) {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
key: "workspace",
|
|
11
|
+
value: context.workspace.name
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
key: "workspace id",
|
|
15
|
+
value: context.workspace.id,
|
|
16
|
+
tone: "dim"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
key: "project",
|
|
20
|
+
value: context.project.name
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
key: "project id",
|
|
24
|
+
value: context.project.id,
|
|
25
|
+
tone: "dim"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
key: "project source",
|
|
29
|
+
value: formatProjectSource(context.resolution.projectSource)
|
|
30
|
+
},
|
|
31
|
+
...context.resolution.targetName ? [{
|
|
32
|
+
key: "target name",
|
|
33
|
+
value: formatTargetName(context.resolution)
|
|
34
|
+
}] : [],
|
|
35
|
+
...context.branch ? [{
|
|
36
|
+
key: "branch",
|
|
37
|
+
value: `${context.branch.name} (${context.branch.kind})`
|
|
38
|
+
}, ...context.branch.id ? [{
|
|
39
|
+
key: "branch id",
|
|
40
|
+
value: context.branch.id,
|
|
41
|
+
tone: "dim"
|
|
42
|
+
}] : []] : []
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
function stripVerboseContext(result) {
|
|
46
|
+
const { verboseContext: _verboseContext, ...serialized } = result;
|
|
47
|
+
return serialized;
|
|
48
|
+
}
|
|
49
|
+
function formatProjectSource(source) {
|
|
50
|
+
switch (source) {
|
|
51
|
+
case "explicit": return "--project";
|
|
52
|
+
case "env": return "environment";
|
|
53
|
+
case "local-pin": return ".prisma/local.json";
|
|
54
|
+
case "platform-mapping": return "platform mapping";
|
|
55
|
+
case "created": return "created";
|
|
56
|
+
case "prompt": return "prompt";
|
|
57
|
+
case "unbound": return "unbound";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function formatTargetName(resolution) {
|
|
61
|
+
return resolution.targetNameSource ? `${resolution.targetName} (${resolution.targetNameSource})` : String(resolution.targetName);
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { renderResolvedProjectContextBlock, stripVerboseContext };
|
|
@@ -194,6 +194,8 @@ const DESCRIPTORS = [
|
|
|
194
194
|
"prisma-cli app deploy --project proj_123",
|
|
195
195
|
"prisma-cli app deploy --create-project my-app --yes",
|
|
196
196
|
"prisma-cli app deploy --app my-app --env DATABASE_URL=postgresql://example",
|
|
197
|
+
"prisma-cli app deploy --db",
|
|
198
|
+
"prisma-cli app deploy --db --yes",
|
|
197
199
|
"prisma-cli app deploy --app my-app --framework nextjs --http-port 3000",
|
|
198
200
|
"prisma-cli app deploy --branch feat-login --framework hono",
|
|
199
201
|
"prisma-cli app deploy --prod --yes",
|
|
@@ -361,6 +363,7 @@ const DESCRIPTORS = [
|
|
|
361
363
|
examples: [
|
|
362
364
|
"prisma-cli project env list",
|
|
363
365
|
"prisma-cli project env add STRIPE_KEY=sk_test_xxx --role production",
|
|
366
|
+
"prisma-cli project env add --file .env --role preview",
|
|
364
367
|
"prisma-cli project env add DATABASE_URL=postgresql://branch --branch feature/foo",
|
|
365
368
|
"prisma-cli project env remove STRIPE_KEY --role preview"
|
|
366
369
|
]
|
|
@@ -377,7 +380,9 @@ const DESCRIPTORS = [
|
|
|
377
380
|
examples: [
|
|
378
381
|
"prisma-cli project env add STRIPE_KEY=sk_test_xxx --role production",
|
|
379
382
|
"prisma-cli project env add STRIPE_KEY=sk_test_xxx --role preview",
|
|
383
|
+
"prisma-cli project env add --file .env --role preview",
|
|
380
384
|
"prisma-cli project env add DATABASE_URL=postgresql://branch --branch feature/foo",
|
|
385
|
+
"prisma-cli project env add --file .env.local --branch feature/foo",
|
|
381
386
|
"API_URL=https://api.example prisma-cli project env add API_URL --project proj_123 --role preview"
|
|
382
387
|
]
|
|
383
388
|
},
|
|
@@ -393,6 +398,7 @@ const DESCRIPTORS = [
|
|
|
393
398
|
examples: [
|
|
394
399
|
"prisma-cli project env update STRIPE_KEY=sk_new_xxx --role production",
|
|
395
400
|
"prisma-cli project env update STRIPE_KEY=sk_new_xxx --role preview",
|
|
401
|
+
"prisma-cli project env update --file .env --role production",
|
|
396
402
|
"prisma-cli project env update DATABASE_URL=postgresql://branch --branch feature/foo"
|
|
397
403
|
]
|
|
398
404
|
},
|
|
@@ -3,6 +3,8 @@ import { cliErrorToJson, writeHumanError, writeHumanLines, writeJsonError, write
|
|
|
3
3
|
import { getCommandDescriptor } from "./command-meta.js";
|
|
4
4
|
import { resolveGlobalFlags } from "./global-flags.js";
|
|
5
5
|
import { createCommandContext } from "./runtime.js";
|
|
6
|
+
import { collectCommandDiagnostics } from "../lib/diagnostics.js";
|
|
7
|
+
import { renderCommandDiagnostics } from "./diagnostics-output.js";
|
|
6
8
|
import { AuthError } from "@prisma/management-api-sdk";
|
|
7
9
|
//#region src/shell/command-runner.ts
|
|
8
10
|
function toCliError(error, runtime) {
|
|
@@ -19,6 +21,7 @@ async function runCommand(runtime, commandName, options, handler, presenter) {
|
|
|
19
21
|
const flags = resolveGlobalFlags(runtime.argv, options);
|
|
20
22
|
const context = await createCommandContext(runtime, flags);
|
|
21
23
|
const descriptor = getCommandDescriptor(commandName);
|
|
24
|
+
const startedAt = Date.now();
|
|
22
25
|
try {
|
|
23
26
|
const success = await handler(context);
|
|
24
27
|
if (flags.json) {
|
|
@@ -29,7 +32,12 @@ async function runCommand(runtime, commandName, options, handler, presenter) {
|
|
|
29
32
|
return;
|
|
30
33
|
}
|
|
31
34
|
if (flags.quiet) return;
|
|
32
|
-
|
|
35
|
+
const rendered = presenter.renderHuman(context, descriptor, success.result);
|
|
36
|
+
const diagnostics = await renderBestEffortCommandDiagnostics(context, {
|
|
37
|
+
enabled: flags.verbose && rendered.length > 0,
|
|
38
|
+
durationMs: Date.now() - startedAt
|
|
39
|
+
});
|
|
40
|
+
writeHumanLines(context.output, [...rendered, ...diagnostics]);
|
|
33
41
|
} catch (error) {
|
|
34
42
|
const cliError = toCliError(error, runtime);
|
|
35
43
|
if (cliError) {
|
|
@@ -41,6 +49,14 @@ async function runCommand(runtime, commandName, options, handler, presenter) {
|
|
|
41
49
|
throw error;
|
|
42
50
|
}
|
|
43
51
|
}
|
|
52
|
+
async function renderBestEffortCommandDiagnostics(context, options) {
|
|
53
|
+
if (!options.enabled) return [];
|
|
54
|
+
try {
|
|
55
|
+
return renderCommandDiagnostics(context, await collectCommandDiagnostics(context, { durationMs: options.durationMs }));
|
|
56
|
+
} catch {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
44
60
|
async function runStreamingCommand(runtime, commandName, options, handler) {
|
|
45
61
|
const flags = resolveGlobalFlags(runtime.argv, options);
|
|
46
62
|
const context = await createCommandContext(runtime, flags);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { renderVerboseBlock } from "./ui.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
//#region src/shell/diagnostics-output.ts
|
|
4
|
+
function renderCommandDiagnostics(context, diagnostics, rows = [], options = {}) {
|
|
5
|
+
if (!diagnostics) return [];
|
|
6
|
+
const { env } = context.runtime;
|
|
7
|
+
const git = diagnostics.git;
|
|
8
|
+
return renderVerboseBlock(context.ui, [
|
|
9
|
+
...rows,
|
|
10
|
+
...diagnostics.durationMs === void 0 ? [] : [{
|
|
11
|
+
key: "duration",
|
|
12
|
+
value: formatDuration(diagnostics.durationMs)
|
|
13
|
+
}],
|
|
14
|
+
{
|
|
15
|
+
key: "cwd",
|
|
16
|
+
value: formatLocalPath(diagnostics.cwd, env)
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
key: "state file",
|
|
20
|
+
value: formatLocalPath(diagnostics.stateFilePath, env)
|
|
21
|
+
},
|
|
22
|
+
...git ? [
|
|
23
|
+
{
|
|
24
|
+
key: "git ref",
|
|
25
|
+
value: git.ref ?? "detached",
|
|
26
|
+
tone: git.ref ? "default" : "dim"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
key: "git sha",
|
|
30
|
+
value: git.sha ?? "unknown",
|
|
31
|
+
tone: git.sha ? "default" : "dim"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
key: "git dirty",
|
|
35
|
+
value: formatDirtyState(git.dirty),
|
|
36
|
+
tone: git.dirty ? "warning" : "dim"
|
|
37
|
+
}
|
|
38
|
+
] : [{
|
|
39
|
+
key: "git",
|
|
40
|
+
value: "not detected",
|
|
41
|
+
tone: "dim"
|
|
42
|
+
}]
|
|
43
|
+
], { title: options.title ?? "Local context" });
|
|
44
|
+
}
|
|
45
|
+
function formatLocalPath(value, env) {
|
|
46
|
+
const resolved = path.resolve(value);
|
|
47
|
+
const home = env.HOME ? path.resolve(env.HOME) : null;
|
|
48
|
+
if (home && (resolved === home || resolved.startsWith(`${home}${path.sep}`))) {
|
|
49
|
+
const relative = path.relative(home, resolved);
|
|
50
|
+
return relative ? `~/${relative}` : "~";
|
|
51
|
+
}
|
|
52
|
+
return resolved;
|
|
53
|
+
}
|
|
54
|
+
function formatDirtyState(dirty) {
|
|
55
|
+
if (dirty === null) return "unknown";
|
|
56
|
+
return dirty ? "yes" : "no";
|
|
57
|
+
}
|
|
58
|
+
function formatDuration(durationMs) {
|
|
59
|
+
if (durationMs < 1e3) return `${durationMs}ms`;
|
|
60
|
+
return `${(durationMs / 1e3).toFixed(1)}s`;
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { renderCommandDiagnostics };
|
package/dist/shell/output.js
CHANGED
|
@@ -23,6 +23,15 @@ function cliErrorToJson(error) {
|
|
|
23
23
|
docsUrl: error.docsUrl
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
+
function formatUnexpectedError(error, trace) {
|
|
27
|
+
const debug = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
28
|
+
if (trace) return `${debug}\n`;
|
|
29
|
+
return [
|
|
30
|
+
`Unexpected CLI error: ${error instanceof Error && error.message ? error.message : String(error)}`,
|
|
31
|
+
"More: Re-run with --trace for deeper diagnostics",
|
|
32
|
+
""
|
|
33
|
+
].join("\n");
|
|
34
|
+
}
|
|
26
35
|
function writeJsonError(output, command, error) {
|
|
27
36
|
output.stdout.write(`${JSON.stringify({
|
|
28
37
|
ok: false,
|
|
@@ -70,4 +79,4 @@ function writeHumanError(output, ui, error, options) {
|
|
|
70
79
|
writeHumanLines(output, lines);
|
|
71
80
|
}
|
|
72
81
|
//#endregion
|
|
73
|
-
export { cliErrorToJson, writeHumanError, writeHumanLines, writeJsonError, writeJsonEvent, writeJsonSuccess };
|
|
82
|
+
export { cliErrorToJson, formatUnexpectedError, writeHumanError, writeHumanLines, writeJsonError, writeJsonEvent, writeJsonSuccess };
|
package/dist/shell/runtime.js
CHANGED
|
@@ -48,4 +48,4 @@ function canPrompt(context) {
|
|
|
48
48
|
return Boolean(context.runtime.stdin.isTTY && context.runtime.stderr.isTTY);
|
|
49
49
|
}
|
|
50
50
|
//#endregion
|
|
51
|
-
export { canPrompt, configureRuntimeCommand, createCommandContext };
|
|
51
|
+
export { canPrompt, configureRuntimeCommand, createCommandContext, resolveStateDir };
|
package/dist/shell/ui.js
CHANGED
|
@@ -48,6 +48,17 @@ function renderNextSteps(steps) {
|
|
|
48
48
|
...steps.map((step) => `- ${step}`)
|
|
49
49
|
];
|
|
50
50
|
}
|
|
51
|
+
function renderVerboseBlock(ui, rows, options = {}) {
|
|
52
|
+
if (!ui.verbose || rows.length === 0) return [];
|
|
53
|
+
const title = options.title ?? "Details";
|
|
54
|
+
const keyWidth = Math.max(...rows.map((row) => stringWidth(`${row.key}:`)));
|
|
55
|
+
const rail = ui.dim("│");
|
|
56
|
+
return [
|
|
57
|
+
"",
|
|
58
|
+
`${ui.dim(title)}:`,
|
|
59
|
+
...rows.map((row) => `${rail} ${ui.accent(padDisplay(`${row.key}:`, keyWidth))} ${formatVerboseValue(ui, row)}`)
|
|
60
|
+
];
|
|
61
|
+
}
|
|
51
62
|
function formatColumns(columns, widths) {
|
|
52
63
|
return columns.map((value, index) => padDisplay(value, widths[index])).join(" ").trimEnd();
|
|
53
64
|
}
|
|
@@ -76,5 +87,13 @@ function formatHeaderValue(ui, row) {
|
|
|
76
87
|
if (row.tone === "link") return ui.link(value);
|
|
77
88
|
return value;
|
|
78
89
|
}
|
|
90
|
+
function formatVerboseValue(ui, row) {
|
|
91
|
+
const value = row.sensitive ? maskValue(row.value) : row.value;
|
|
92
|
+
if (row.tone === "dim") return ui.dim(value);
|
|
93
|
+
if (row.tone === "success") return ui.success(value);
|
|
94
|
+
if (row.tone === "warning") return ui.warning(value);
|
|
95
|
+
if (row.tone === "link") return ui.link(value);
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
79
98
|
//#endregion
|
|
80
|
-
export { createShellUi, formatColumns, maskValue, padDisplay, renderCommandHeader, renderNextSteps, renderSummaryLine, wrapText };
|
|
99
|
+
export { createShellUi, formatColumns, maskValue, padDisplay, renderCommandHeader, renderNextSteps, renderSummaryLine, renderVerboseBlock, wrapText };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/cli",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.6",
|
|
4
4
|
"description": "Command-line interface for the Prisma Developer Platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -36,12 +36,13 @@
|
|
|
36
36
|
"license": "Apache-2.0",
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@clack/prompts": "^1.5.0",
|
|
39
|
-
"@prisma/compute-sdk": "^0.
|
|
39
|
+
"@prisma/compute-sdk": "^0.21.0",
|
|
40
40
|
"@prisma/credentials-store": "^7.8.0",
|
|
41
|
-
"@prisma/management-api-sdk": "^1.
|
|
41
|
+
"@prisma/management-api-sdk": "^1.37.0",
|
|
42
42
|
"c12": "4.0.0-beta.5",
|
|
43
43
|
"colorette": "^2.0.20",
|
|
44
44
|
"commander": "^14.0.3",
|
|
45
|
+
"dotenv": "^17.4.2",
|
|
45
46
|
"magicast": "^0.5.3",
|
|
46
47
|
"open": "^11.0.0",
|
|
47
48
|
"string-width": "^8.2.1",
|