@prisma/cli 3.0.0-beta.1 → 3.0.0-beta.11
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 -3
- package/dist/adapters/git.js +8 -3
- package/dist/adapters/local-state.js +12 -4
- package/dist/adapters/mock-api.js +81 -2
- package/dist/adapters/token-storage.js +64 -23
- package/dist/cli.js +18 -3
- package/dist/cli2.js +9 -5
- package/dist/commands/app/index.js +84 -59
- package/dist/commands/branch/index.js +2 -27
- package/dist/commands/database/index.js +159 -0
- 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 +286 -131
- package/dist/controllers/app.js +699 -244
- package/dist/controllers/auth.js +8 -8
- package/dist/controllers/branch.js +78 -48
- package/dist/controllers/database.js +318 -0
- package/dist/controllers/project.js +156 -85
- package/dist/lib/app/branch-database-deploy.js +325 -0
- package/dist/lib/app/branch-database.js +215 -0
- package/dist/lib/app/bun-project.js +12 -5
- package/dist/lib/app/compute-config.js +147 -0
- package/dist/lib/app/deploy-output.js +10 -1
- package/dist/lib/app/deploy-plan.js +58 -0
- 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/preview-branch-database.js +102 -0
- package/dist/lib/app/preview-build-settings.js +376 -0
- package/dist/lib/app/preview-build.js +314 -97
- package/dist/lib/app/preview-provider.js +178 -65
- package/dist/lib/app/production-deploy-gate.js +161 -0
- package/dist/lib/auth/auth-ops.js +30 -21
- package/dist/lib/auth/guard.js +3 -2
- package/dist/lib/auth/login.js +116 -14
- 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 +1 -1
- package/dist/lib/project/local-pin.js +182 -33
- package/dist/lib/project/resolution.js +204 -50
- package/dist/lib/project/setup.js +66 -19
- package/dist/output/patterns.js +1 -1
- package/dist/presenters/app-env.js +149 -14
- package/dist/presenters/app.js +178 -23
- package/dist/presenters/branch.js +37 -102
- package/dist/presenters/database.js +274 -0
- package/dist/presenters/project.js +57 -39
- package/dist/presenters/verbose-context.js +64 -0
- package/dist/shell/command-meta.js +103 -13
- package/dist/shell/command-runner.js +57 -19
- package/dist/shell/diagnostics-output.js +57 -0
- package/dist/shell/errors.js +12 -1
- package/dist/shell/help.js +30 -20
- package/dist/shell/output.js +10 -1
- package/dist/shell/runtime.js +11 -7
- package/dist/shell/ui.js +42 -3
- package/dist/shell/update-check.js +247 -0
- package/dist/use-cases/branch.js +20 -68
- package/dist/use-cases/create-cli-gateways.js +2 -17
- package/dist/use-cases/project.js +2 -1
- package/package.json +26 -10
|
@@ -69,8 +69,18 @@ const DESCRIPTORS = [
|
|
|
69
69
|
{
|
|
70
70
|
id: "branch",
|
|
71
71
|
path: ["prisma", "branch"],
|
|
72
|
-
description: "View your
|
|
73
|
-
examples: ["prisma-cli branch list"
|
|
72
|
+
description: "View your Platform branches",
|
|
73
|
+
examples: ["prisma-cli branch list"]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: "database",
|
|
77
|
+
path: ["prisma", "database"],
|
|
78
|
+
description: "Manage Prisma Postgres databases for a project",
|
|
79
|
+
examples: [
|
|
80
|
+
"prisma-cli database list",
|
|
81
|
+
"prisma-cli database create my-db",
|
|
82
|
+
"prisma-cli database connection create db_123"
|
|
83
|
+
]
|
|
74
84
|
},
|
|
75
85
|
{
|
|
76
86
|
id: "git",
|
|
@@ -153,28 +163,99 @@ const DESCRIPTORS = [
|
|
|
153
163
|
"branch",
|
|
154
164
|
"list"
|
|
155
165
|
],
|
|
156
|
-
description: "List
|
|
166
|
+
description: "List Platform branches for the resolved project",
|
|
157
167
|
examples: ["prisma-cli branch list", "prisma-cli branch list --json"]
|
|
158
168
|
},
|
|
159
169
|
{
|
|
160
|
-
id: "
|
|
170
|
+
id: "database.list",
|
|
161
171
|
path: [
|
|
162
172
|
"prisma",
|
|
163
|
-
"
|
|
173
|
+
"database",
|
|
174
|
+
"list"
|
|
175
|
+
],
|
|
176
|
+
description: "List Prisma Postgres databases for the resolved project",
|
|
177
|
+
examples: [
|
|
178
|
+
"prisma-cli database list",
|
|
179
|
+
"prisma-cli database list --branch feature/foo",
|
|
180
|
+
"prisma-cli database list --json"
|
|
181
|
+
]
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
id: "database.show",
|
|
185
|
+
path: [
|
|
186
|
+
"prisma",
|
|
187
|
+
"database",
|
|
164
188
|
"show"
|
|
165
189
|
],
|
|
166
|
-
description: "Show
|
|
167
|
-
examples: ["prisma-cli
|
|
190
|
+
description: "Show database metadata without secret values",
|
|
191
|
+
examples: ["prisma-cli database show db_123", "prisma-cli database show acme-preview --branch preview --json"]
|
|
168
192
|
},
|
|
169
193
|
{
|
|
170
|
-
id: "
|
|
194
|
+
id: "database.create",
|
|
171
195
|
path: [
|
|
172
196
|
"prisma",
|
|
173
|
-
"
|
|
174
|
-
"
|
|
197
|
+
"database",
|
|
198
|
+
"create"
|
|
199
|
+
],
|
|
200
|
+
description: "Create a Prisma Postgres database and print its one-time connection URL",
|
|
201
|
+
examples: ["prisma-cli database create my-db", "prisma-cli database create my-db --branch feature/foo --region eu-central-1"]
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: "database.remove",
|
|
205
|
+
path: [
|
|
206
|
+
"prisma",
|
|
207
|
+
"database",
|
|
208
|
+
"remove"
|
|
175
209
|
],
|
|
176
|
-
description: "
|
|
177
|
-
examples: ["prisma-cli
|
|
210
|
+
description: "Remove a database after exact id confirmation",
|
|
211
|
+
examples: ["prisma-cli database remove db_123 --confirm db_123"]
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: "database.connection",
|
|
215
|
+
path: [
|
|
216
|
+
"prisma",
|
|
217
|
+
"database",
|
|
218
|
+
"connection"
|
|
219
|
+
],
|
|
220
|
+
description: "Manage one-time-view database connection strings",
|
|
221
|
+
examples: [
|
|
222
|
+
"prisma-cli database connection list db_123",
|
|
223
|
+
"prisma-cli database connection create db_123",
|
|
224
|
+
"prisma-cli database connection remove conn_123 --confirm conn_123"
|
|
225
|
+
]
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
id: "database.connection.list",
|
|
229
|
+
path: [
|
|
230
|
+
"prisma",
|
|
231
|
+
"database",
|
|
232
|
+
"connection",
|
|
233
|
+
"list"
|
|
234
|
+
],
|
|
235
|
+
description: "List database connection metadata without secret values",
|
|
236
|
+
examples: ["prisma-cli database connection list db_123", "prisma-cli database connection list acme-preview --branch preview --json"]
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
id: "database.connection.create",
|
|
240
|
+
path: [
|
|
241
|
+
"prisma",
|
|
242
|
+
"database",
|
|
243
|
+
"connection",
|
|
244
|
+
"create"
|
|
245
|
+
],
|
|
246
|
+
description: "Create a database connection and print its one-time connection URL",
|
|
247
|
+
examples: ["prisma-cli database connection create db_123", "prisma-cli database connection create db_123 --name readonly"]
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: "database.connection.remove",
|
|
251
|
+
path: [
|
|
252
|
+
"prisma",
|
|
253
|
+
"database",
|
|
254
|
+
"connection",
|
|
255
|
+
"remove"
|
|
256
|
+
],
|
|
257
|
+
description: "Remove a database connection after exact id confirmation",
|
|
258
|
+
examples: ["prisma-cli database connection remove conn_123 --confirm conn_123"]
|
|
178
259
|
},
|
|
179
260
|
{
|
|
180
261
|
id: "app.build",
|
|
@@ -214,8 +295,12 @@ const DESCRIPTORS = [
|
|
|
214
295
|
"prisma-cli app deploy --project proj_123",
|
|
215
296
|
"prisma-cli app deploy --create-project my-app --yes",
|
|
216
297
|
"prisma-cli app deploy --app my-app --env DATABASE_URL=postgresql://example",
|
|
298
|
+
"prisma-cli app deploy --env .env",
|
|
299
|
+
"prisma-cli app deploy --db",
|
|
300
|
+
"prisma-cli app deploy --db --yes",
|
|
217
301
|
"prisma-cli app deploy --app my-app --framework nextjs --http-port 3000",
|
|
218
302
|
"prisma-cli app deploy --branch feat-login --framework hono",
|
|
303
|
+
"prisma-cli app deploy --prod --yes",
|
|
219
304
|
"pnpm dlx skills@latest add prisma/prisma-cli/skills#cli-v<cli-version> --all",
|
|
220
305
|
"prisma-cli app deploy --framework bun --entry src/server.ts"
|
|
221
306
|
]
|
|
@@ -378,8 +463,9 @@ const DESCRIPTORS = [
|
|
|
378
463
|
],
|
|
379
464
|
description: "Manage environment variables for the active project",
|
|
380
465
|
examples: [
|
|
381
|
-
"prisma-cli project env list
|
|
466
|
+
"prisma-cli project env list",
|
|
382
467
|
"prisma-cli project env add STRIPE_KEY=sk_test_xxx --role production",
|
|
468
|
+
"prisma-cli project env add --file .env --role preview",
|
|
383
469
|
"prisma-cli project env add DATABASE_URL=postgresql://branch --branch feature/foo",
|
|
384
470
|
"prisma-cli project env remove STRIPE_KEY --role preview"
|
|
385
471
|
]
|
|
@@ -396,7 +482,9 @@ const DESCRIPTORS = [
|
|
|
396
482
|
examples: [
|
|
397
483
|
"prisma-cli project env add STRIPE_KEY=sk_test_xxx --role production",
|
|
398
484
|
"prisma-cli project env add STRIPE_KEY=sk_test_xxx --role preview",
|
|
485
|
+
"prisma-cli project env add --file .env --role preview",
|
|
399
486
|
"prisma-cli project env add DATABASE_URL=postgresql://branch --branch feature/foo",
|
|
487
|
+
"prisma-cli project env add --file .env.local --branch feature/foo",
|
|
400
488
|
"API_URL=https://api.example prisma-cli project env add API_URL --project proj_123 --role preview"
|
|
401
489
|
]
|
|
402
490
|
},
|
|
@@ -412,6 +500,7 @@ const DESCRIPTORS = [
|
|
|
412
500
|
examples: [
|
|
413
501
|
"prisma-cli project env update STRIPE_KEY=sk_new_xxx --role production",
|
|
414
502
|
"prisma-cli project env update STRIPE_KEY=sk_new_xxx --role preview",
|
|
503
|
+
"prisma-cli project env update --file .env --role production",
|
|
415
504
|
"prisma-cli project env update DATABASE_URL=postgresql://branch --branch feature/foo"
|
|
416
505
|
]
|
|
417
506
|
},
|
|
@@ -425,6 +514,7 @@ const DESCRIPTORS = [
|
|
|
425
514
|
],
|
|
426
515
|
description: "List environment variable metadata for a scope (no values).",
|
|
427
516
|
examples: [
|
|
517
|
+
"prisma-cli project env list",
|
|
428
518
|
"prisma-cli project env list --role production",
|
|
429
519
|
"prisma-cli project env list --role preview",
|
|
430
520
|
"prisma-cli project env list --branch feature/foo"
|
|
@@ -1,41 +1,79 @@
|
|
|
1
|
-
import { CliError, authRequiredError } from "./errors.js";
|
|
2
|
-
import { cliErrorToJson, writeHumanError, writeHumanLines, writeJsonError, writeJsonEvent, writeJsonSuccess } from "./output.js";
|
|
1
|
+
import { CliError, authRequiredError, commandCanceledError } from "./errors.js";
|
|
3
2
|
import { getCommandDescriptor } from "./command-meta.js";
|
|
4
3
|
import { resolveGlobalFlags } from "./global-flags.js";
|
|
5
4
|
import { createCommandContext } from "./runtime.js";
|
|
5
|
+
import { cliErrorToJson, writeHumanError, writeHumanLines, writeJsonError, writeJsonEvent, writeJsonSuccess } from "./output.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
|
-
function toCliError(error) {
|
|
10
|
+
function toCliError(error, runtime) {
|
|
11
|
+
if (isCancellationError(error) || runtime.signal.aborted) return commandCanceledError();
|
|
9
12
|
if (error instanceof CliError) return error;
|
|
10
13
|
if (error instanceof AuthError) return authRequiredError(["prisma-cli auth login"], { debug: error.message });
|
|
11
14
|
return null;
|
|
12
15
|
}
|
|
16
|
+
function isCancellationError(error) {
|
|
17
|
+
if (!(error instanceof Error)) return false;
|
|
18
|
+
return error.name === "AbortError" || error.name === "CancelledError";
|
|
19
|
+
}
|
|
13
20
|
async function runCommand(runtime, commandName, options, handler, presenter) {
|
|
14
|
-
const
|
|
15
|
-
const context = await createCommandContext(runtime, flags);
|
|
21
|
+
const context = await createCommandContext(runtime, resolveGlobalFlags(runtime.argv, options));
|
|
16
22
|
const descriptor = getCommandDescriptor(commandName);
|
|
23
|
+
const startedAt = Date.now();
|
|
17
24
|
try {
|
|
18
|
-
|
|
19
|
-
if (flags.json) {
|
|
20
|
-
writeJsonSuccess(context.output, {
|
|
21
|
-
...success,
|
|
22
|
-
result: presenter.renderJson ? presenter.renderJson(success.result) : success.result
|
|
23
|
-
});
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
if (flags.quiet) return;
|
|
27
|
-
writeHumanLines(context.output, presenter.renderHuman(context, descriptor, success.result));
|
|
25
|
+
await writeCommandSuccess(context, descriptor, await handler(context), presenter, Date.now() - startedAt);
|
|
28
26
|
} catch (error) {
|
|
29
|
-
const cliError = toCliError(error);
|
|
27
|
+
const cliError = toCliError(error, runtime);
|
|
30
28
|
if (cliError) {
|
|
31
|
-
|
|
32
|
-
else writeHumanError(context.output, context.ui, cliError, { trace: flags.trace });
|
|
29
|
+
writeCommandError(context, commandName, cliError);
|
|
33
30
|
process.exitCode = cliError.exitCode;
|
|
34
31
|
return;
|
|
35
32
|
}
|
|
36
33
|
throw error;
|
|
37
34
|
}
|
|
38
35
|
}
|
|
36
|
+
async function writeCommandSuccess(context, descriptor, success, presenter, durationMs) {
|
|
37
|
+
if (context.flags.json) {
|
|
38
|
+
writeJsonSuccess(context.output, {
|
|
39
|
+
...success,
|
|
40
|
+
result: presenter.renderJson ? presenter.renderJson(success.result) : success.result
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const stdout = presenter.renderStdout?.(context, descriptor, success.result) ?? [];
|
|
45
|
+
if (context.flags.quiet) {
|
|
46
|
+
writeStdoutLines(context, stdout);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const rendered = presenter.renderHuman(context, descriptor, success.result);
|
|
50
|
+
const diagnostics = await renderBestEffortCommandDiagnostics(context, {
|
|
51
|
+
enabled: context.flags.verbose && rendered.length > 0,
|
|
52
|
+
durationMs
|
|
53
|
+
});
|
|
54
|
+
const humanLines = [...rendered, ...diagnostics];
|
|
55
|
+
if (stdout.length > 0 && humanLines.length > 0) humanLines.push("");
|
|
56
|
+
writeHumanLines(context.output, humanLines);
|
|
57
|
+
writeStdoutLines(context, stdout);
|
|
58
|
+
}
|
|
59
|
+
function writeCommandError(context, commandName, cliError) {
|
|
60
|
+
if (context.flags.json) {
|
|
61
|
+
writeJsonError(context.output, commandName, cliError);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
writeHumanError(context.output, context.ui, cliError, { trace: context.flags.trace });
|
|
65
|
+
}
|
|
66
|
+
function writeStdoutLines(context, lines) {
|
|
67
|
+
if (lines.length > 0) context.output.stdout.write(`${lines.join("\n")}\n`);
|
|
68
|
+
}
|
|
69
|
+
async function renderBestEffortCommandDiagnostics(context, options) {
|
|
70
|
+
if (!options.enabled) return [];
|
|
71
|
+
try {
|
|
72
|
+
return renderCommandDiagnostics(context, await collectCommandDiagnostics(context, { durationMs: options.durationMs }));
|
|
73
|
+
} catch {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
39
77
|
async function runStreamingCommand(runtime, commandName, options, handler) {
|
|
40
78
|
const flags = resolveGlobalFlags(runtime.argv, options);
|
|
41
79
|
const context = await createCommandContext(runtime, flags);
|
|
@@ -51,7 +89,7 @@ async function runStreamingCommand(runtime, commandName, options, handler) {
|
|
|
51
89
|
nextActions: []
|
|
52
90
|
});
|
|
53
91
|
} catch (error) {
|
|
54
|
-
const cliError = toCliError(error);
|
|
92
|
+
const cliError = toCliError(error, runtime);
|
|
55
93
|
if (cliError) {
|
|
56
94
|
if (flags.json) writeJsonEvent(context.output, {
|
|
57
95
|
type: "error",
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { renderVerboseBlock } from "./ui.js";
|
|
2
|
+
import { shortenHomePath } from "../lib/fs/home-path.js";
|
|
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
|
+
return shortenHomePath(value, env);
|
|
47
|
+
}
|
|
48
|
+
function formatDirtyState(dirty) {
|
|
49
|
+
if (dirty === null) return "unknown";
|
|
50
|
+
return dirty ? "yes" : "no";
|
|
51
|
+
}
|
|
52
|
+
function formatDuration(durationMs) {
|
|
53
|
+
if (durationMs < 1e3) return `${durationMs}ms`;
|
|
54
|
+
return `${(durationMs / 1e3).toFixed(1)}s`;
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { renderCommandDiagnostics };
|
package/dist/shell/errors.js
CHANGED
|
@@ -56,6 +56,17 @@ function authRequiredError(nextSteps = ["prisma-cli auth login"], options = {})
|
|
|
56
56
|
nextSteps
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
|
+
function commandCanceledError() {
|
|
60
|
+
return new CliError({
|
|
61
|
+
code: "COMMAND_CANCELED",
|
|
62
|
+
domain: "cli",
|
|
63
|
+
summary: "Command canceled",
|
|
64
|
+
why: null,
|
|
65
|
+
fix: null,
|
|
66
|
+
exitCode: 130,
|
|
67
|
+
humanLines: ["Command canceled [COMMAND_CANCELED]"]
|
|
68
|
+
});
|
|
69
|
+
}
|
|
59
70
|
function workspaceRequiredError() {
|
|
60
71
|
return usageError("Workspace required", "This command needs an active workspace, but the authenticated session does not have one.", "Run prisma-cli auth login and choose a workspace.", ["prisma-cli auth login"], "auth");
|
|
61
72
|
}
|
|
@@ -71,4 +82,4 @@ function featureUnavailableError(summary, why, fix, nextSteps = [], domain = "cl
|
|
|
71
82
|
});
|
|
72
83
|
}
|
|
73
84
|
//#endregion
|
|
74
|
-
export { CliError, authRequiredError, featureUnavailableError, usageError, workspaceRequiredError };
|
|
85
|
+
export { CliError, authRequiredError, commandCanceledError, featureUnavailableError, usageError, workspaceRequiredError };
|
package/dist/shell/help.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createShellUi, padDisplay, wrapText } from "./ui.js";
|
|
2
1
|
import { formatDescriptorLabel, getDescriptorForCommand } from "./command-meta.js";
|
|
3
2
|
import { COMPACT_GLOBAL_OPTION_FLAGS, resolveGlobalFlags } from "./global-flags.js";
|
|
3
|
+
import { createShellUi, padDisplay, wrapText } from "./ui.js";
|
|
4
4
|
//#region src/shell/help.ts
|
|
5
5
|
function renderHelp(command, runtime) {
|
|
6
6
|
const descriptor = getDescriptorForCommand(command);
|
|
@@ -10,28 +10,38 @@ function renderHelp(command, runtime) {
|
|
|
10
10
|
const visibleCommands = command.commands.filter((candidate) => candidate.name() !== "help" && !candidate.hidden);
|
|
11
11
|
const visibleOptions = command.options.filter((candidate) => !candidate.hidden);
|
|
12
12
|
if (visibleCommands.length > 0) lines.push(...renderCommandRows(rail, ui, visibleCommands));
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
if (visibleOptions.length > 0) {
|
|
19
|
-
if (visibleCommands.length > 0) lines.push(`${rail}`);
|
|
20
|
-
if (visibleCommands.length > 0 && visibleOptions.every((option) => COMPACT_GLOBAL_OPTION_FLAGS.includes(option.flags))) lines.push(`${rail} Global options:`);
|
|
21
|
-
lines.push(...renderOptionRows(rail, ui, visibleOptions));
|
|
22
|
-
}
|
|
23
|
-
if (descriptor.examples && descriptor.examples.length > 0) {
|
|
24
|
-
lines.push(`${rail}`);
|
|
25
|
-
lines.push(`${rail} Examples:`);
|
|
26
|
-
for (const example of descriptor.examples) lines.push(`${rail} $ ${example}`);
|
|
27
|
-
}
|
|
28
|
-
if (descriptor.docsPath) {
|
|
29
|
-
lines.push(`${rail}`);
|
|
30
|
-
lines.push(`${rail} ${ui.accent(padDisplay("Read more", 16))} ${ui.link(descriptor.docsPath)}`);
|
|
31
|
-
}
|
|
13
|
+
lines.push(...renderLongDescription(rail, ui, descriptor.longDescription));
|
|
14
|
+
lines.push(...renderVisibleOptions(rail, ui, visibleCommands, visibleOptions));
|
|
15
|
+
lines.push(...renderExamples(rail, descriptor.examples));
|
|
16
|
+
lines.push(...renderDocsPath(rail, ui, descriptor.docsPath));
|
|
32
17
|
lines.push("");
|
|
33
18
|
return `${lines.join("\n")}`;
|
|
34
19
|
}
|
|
20
|
+
function renderLongDescription(rail, ui, longDescription) {
|
|
21
|
+
if (!longDescription) return [];
|
|
22
|
+
return [`${rail}`, ...wrapText(longDescription, Math.max(ui.width - 3, 40)).map((line) => `${rail} ${line}`)];
|
|
23
|
+
}
|
|
24
|
+
function renderVisibleOptions(rail, ui, visibleCommands, visibleOptions) {
|
|
25
|
+
if (visibleOptions.length === 0) return [];
|
|
26
|
+
const lines = visibleCommands.length > 0 ? [`${rail}`] : [];
|
|
27
|
+
if (shouldLabelGlobalOptions(visibleCommands, visibleOptions)) lines.push(`${rail} Global options:`);
|
|
28
|
+
return [...lines, ...renderOptionRows(rail, ui, visibleOptions)];
|
|
29
|
+
}
|
|
30
|
+
function shouldLabelGlobalOptions(visibleCommands, visibleOptions) {
|
|
31
|
+
return visibleCommands.length > 0 && visibleOptions.every((option) => COMPACT_GLOBAL_OPTION_FLAGS.includes(option.flags));
|
|
32
|
+
}
|
|
33
|
+
function renderExamples(rail, examples) {
|
|
34
|
+
if (!examples || examples.length === 0) return [];
|
|
35
|
+
return [
|
|
36
|
+
`${rail}`,
|
|
37
|
+
`${rail} Examples:`,
|
|
38
|
+
...examples.map((example) => `${rail} $ ${example}`)
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
function renderDocsPath(rail, ui, docsPath) {
|
|
42
|
+
if (!docsPath) return [];
|
|
43
|
+
return [`${rail}`, `${rail} ${ui.accent(padDisplay("Read more", 16))} ${ui.link(docsPath)}`];
|
|
44
|
+
}
|
|
35
45
|
function renderCommandRows(rail, ui, commands) {
|
|
36
46
|
return renderAlignedRows(rail, ui, commands.map((command) => {
|
|
37
47
|
const descriptor = getDescriptorForCommand(command);
|
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
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { createShellUi } from "./ui.js";
|
|
2
1
|
import { LocalStateStore } from "../adapters/local-state.js";
|
|
3
2
|
import { MockApi } from "../adapters/mock-api.js";
|
|
3
|
+
import { createShellUi } from "./ui.js";
|
|
4
4
|
import { renderHelp } from "./help.js";
|
|
5
|
+
import { findComputeConfigDir } from "@prisma/compute-sdk/config";
|
|
5
6
|
import path from "node:path";
|
|
6
7
|
//#region src/shell/runtime.ts
|
|
7
8
|
const DEFAULT_STATE_DIR_NAME = path.join(".prisma", "cli");
|
|
@@ -20,15 +21,15 @@ function configureRuntimeCommand(command, runtime) {
|
|
|
20
21
|
}
|
|
21
22
|
async function createCommandContext(runtime, flags) {
|
|
22
23
|
const fixturePath = runtime.fixturePath ?? runtime.env.PRISMA_CLI_MOCK_FIXTURE_PATH;
|
|
23
|
-
const stateDir = resolveStateDir(runtime);
|
|
24
|
+
const stateDir = await resolveStateDir(runtime);
|
|
24
25
|
let loadedApi;
|
|
25
|
-
if (fixturePath) loadedApi = await MockApi.load(fixturePath);
|
|
26
|
+
if (fixturePath) loadedApi = await MockApi.load(fixturePath, runtime.signal);
|
|
26
27
|
return {
|
|
27
28
|
get api() {
|
|
28
29
|
if (!loadedApi) throw new Error("context.api accessed in real mode. Set runtime.fixturePath or PRISMA_CLI_MOCK_FIXTURE_PATH to use fixture mode.");
|
|
29
30
|
return loadedApi;
|
|
30
31
|
},
|
|
31
|
-
stateStore: new LocalStateStore(stateDir),
|
|
32
|
+
stateStore: new LocalStateStore(stateDir, runtime.signal),
|
|
32
33
|
output: {
|
|
33
34
|
stdout: runtime.stdout,
|
|
34
35
|
stderr: runtime.stderr
|
|
@@ -38,8 +39,11 @@ async function createCommandContext(runtime, flags) {
|
|
|
38
39
|
ui: createShellUi(runtime, flags)
|
|
39
40
|
};
|
|
40
41
|
}
|
|
41
|
-
function resolveStateDir(runtime) {
|
|
42
|
-
|
|
42
|
+
async function resolveStateDir(runtime) {
|
|
43
|
+
const explicitStateDir = runtime.stateDir ?? runtime.env.PRISMA_CLI_STATE_DIR;
|
|
44
|
+
if (explicitStateDir) return explicitStateDir;
|
|
45
|
+
const projectDir = await findComputeConfigDir(runtime.cwd, runtime.signal);
|
|
46
|
+
return path.join(projectDir ?? runtime.cwd, DEFAULT_STATE_DIR_NAME);
|
|
43
47
|
}
|
|
44
48
|
function canPrompt(context) {
|
|
45
49
|
if (context.flags.json) return false;
|
|
@@ -48,4 +52,4 @@ function canPrompt(context) {
|
|
|
48
52
|
return Boolean(context.runtime.stdin.isTTY && context.runtime.stderr.isTTY);
|
|
49
53
|
}
|
|
50
54
|
//#endregion
|
|
51
|
-
export { canPrompt, configureRuntimeCommand, createCommandContext };
|
|
55
|
+
export { canPrompt, configureRuntimeCommand, createCommandContext, resolveStateDir };
|
package/dist/shell/ui.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { createColors } from "colorette";
|
|
1
2
|
import stringWidth from "string-width";
|
|
2
3
|
import stripAnsi from "strip-ansi";
|
|
3
4
|
import wrapAnsi from "wrap-ansi";
|
|
4
|
-
import { createColors } from "colorette";
|
|
5
5
|
//#region src/shell/ui.ts
|
|
6
|
+
const URL_CREDENTIALS_PATTERN = /:\/\/[^:@/\s]+:[^@/\s]+@/g;
|
|
6
7
|
const DEFAULT_WIDTH = 80;
|
|
7
8
|
function createShellUi(runtime, flags) {
|
|
8
9
|
const isTTY = Boolean(runtime.stderr.isTTY);
|
|
@@ -48,6 +49,20 @@ function renderNextSteps(steps) {
|
|
|
48
49
|
...steps.map((step) => `- ${step}`)
|
|
49
50
|
];
|
|
50
51
|
}
|
|
52
|
+
function renderVerboseBlock(ui, rows, options = {}) {
|
|
53
|
+
if (!ui.verbose || rows.length === 0) return [];
|
|
54
|
+
const title = options.title ?? "Details";
|
|
55
|
+
const keyWidth = Math.max(...rows.map((row) => stringWidth(`${row.key}:`)));
|
|
56
|
+
const rail = ui.dim("│");
|
|
57
|
+
return [
|
|
58
|
+
"",
|
|
59
|
+
`${ui.dim(title)}:`,
|
|
60
|
+
...rows.map((row) => `${rail} ${ui.accent(padDisplay(`${row.key}:`, keyWidth))} ${formatVerboseValue(ui, row)}`)
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
function formatColumns(columns, widths) {
|
|
64
|
+
return columns.map((value, index) => padDisplay(value, widths[index])).join(" ").trimEnd();
|
|
65
|
+
}
|
|
51
66
|
function wrapText(text, width, indent = "") {
|
|
52
67
|
return wrapAnsi(text, width, {
|
|
53
68
|
hard: false,
|
|
@@ -59,7 +74,23 @@ function padDisplay(text, width) {
|
|
|
59
74
|
return `${text}${" ".repeat(padding)}`;
|
|
60
75
|
}
|
|
61
76
|
function maskValue(value) {
|
|
62
|
-
return value
|
|
77
|
+
return maskEmailLocalParts(value).replace(URL_CREDENTIALS_PATTERN, "://****:****@");
|
|
78
|
+
}
|
|
79
|
+
function maskEmailLocalParts(value) {
|
|
80
|
+
let masked = "";
|
|
81
|
+
let segmentStart = 0;
|
|
82
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
83
|
+
if (value[index] !== "@") continue;
|
|
84
|
+
let localStart = index;
|
|
85
|
+
while (localStart > segmentStart && isEmailLocalPartChar(value[localStart - 1])) localStart -= 1;
|
|
86
|
+
if (localStart === index) continue;
|
|
87
|
+
masked += `${value.slice(segmentStart, localStart)}****@`;
|
|
88
|
+
segmentStart = index + 1;
|
|
89
|
+
}
|
|
90
|
+
return masked + value.slice(segmentStart);
|
|
91
|
+
}
|
|
92
|
+
function isEmailLocalPartChar(char) {
|
|
93
|
+
return char >= "A" && char <= "Z" || char >= "a" && char <= "z" || char >= "0" && char <= "9" || char === "!" || char === "#" || char === "$" || char === "." || char === "&" || char === "'" || char === "*" || char === "%" || char === "+" || char === "-" || char === "/" || char === "=" || char === "?" || char === "^" || char === "_" || char === "`" || char === "{" || char === "|" || char === "}" || char === "~";
|
|
63
94
|
}
|
|
64
95
|
function resolveColorEnabled(runtime, flags, isTTY) {
|
|
65
96
|
if (flags.color === true) return true;
|
|
@@ -73,5 +104,13 @@ function formatHeaderValue(ui, row) {
|
|
|
73
104
|
if (row.tone === "link") return ui.link(value);
|
|
74
105
|
return value;
|
|
75
106
|
}
|
|
107
|
+
function formatVerboseValue(ui, row) {
|
|
108
|
+
const value = row.sensitive ? maskValue(row.value) : row.value;
|
|
109
|
+
if (row.tone === "dim") return ui.dim(value);
|
|
110
|
+
if (row.tone === "success") return ui.success(value);
|
|
111
|
+
if (row.tone === "warning") return ui.warning(value);
|
|
112
|
+
if (row.tone === "link") return ui.link(value);
|
|
113
|
+
return value;
|
|
114
|
+
}
|
|
76
115
|
//#endregion
|
|
77
|
-
export { createShellUi, maskValue, padDisplay, renderCommandHeader, renderNextSteps, renderSummaryLine, wrapText };
|
|
116
|
+
export { createShellUi, formatColumns, maskValue, padDisplay, renderCommandHeader, renderNextSteps, renderSummaryLine, renderVerboseBlock, wrapText };
|