@prisma/cli 8.0.0-rc.10-dev.83 → 8.0.0-rc.10-dev.85
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/cli.js +1212 -1723
- package/package.json +9 -9
package/dist/cli.js
CHANGED
|
@@ -61,11 +61,15 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
61
61
|
* unified binary's name.
|
|
62
62
|
*/
|
|
63
63
|
const CLI_NAME = "prisma";
|
|
64
|
-
/** The CLI docs
|
|
65
|
-
*
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
/** The unified CLI's docs section (also the update-check fallback
|
|
65
|
+
* instruction URL). */
|
|
66
|
+
const CLI_DOCS_URL = "https://www.prisma.io/docs/cli";
|
|
67
|
+
/**
|
|
68
|
+
* Base URL for structured-error documentation links. The engine composes
|
|
69
|
+
* each diagnostic's docsUrl as base + code; every code is documented at
|
|
70
|
+
* this page (registry: docs/reference/error-reference.md).
|
|
71
|
+
*/
|
|
72
|
+
const DOCS_ERRORS_BASE_URL = "https://www.prisma.io/docs/cli/error-reference/";
|
|
69
73
|
//#endregion
|
|
70
74
|
//#region src/auth/client.ts
|
|
71
75
|
const CLIENT_ID = "cmm3lndn701oo0uefvxzo0ivw";
|
|
@@ -2460,7 +2464,7 @@ function loginWorkspaceUnknownError() {
|
|
|
2460
2464
|
}]
|
|
2461
2465
|
});
|
|
2462
2466
|
}
|
|
2463
|
-
function nextActionsFor$
|
|
2467
|
+
function nextActionsFor$1(agentSetupTipCommand) {
|
|
2464
2468
|
return [
|
|
2465
2469
|
{
|
|
2466
2470
|
kind: "run-command",
|
|
@@ -2511,7 +2515,7 @@ function presentationsFor$2(spec, result) {
|
|
|
2511
2515
|
}]
|
|
2512
2516
|
],
|
|
2513
2517
|
stdout: () => rows.map((row) => `${row.label}: ${row.value}`),
|
|
2514
|
-
next: () => nextActionsFor$
|
|
2518
|
+
next: () => nextActionsFor$1(spec.agentSetupTipCommand)
|
|
2515
2519
|
};
|
|
2516
2520
|
}
|
|
2517
2521
|
const authLoginCommand = defineCommand({
|
|
@@ -2966,65 +2970,6 @@ async function promptForSession(stored, select) {
|
|
|
2966
2970
|
return requireSession(stored.sessions, workspaceId);
|
|
2967
2971
|
}
|
|
2968
2972
|
//#endregion
|
|
2969
|
-
//#region src/errors.ts
|
|
2970
|
-
var CliError = class extends Error {
|
|
2971
|
-
code;
|
|
2972
|
-
domain;
|
|
2973
|
-
severity;
|
|
2974
|
-
summary;
|
|
2975
|
-
why;
|
|
2976
|
-
fix;
|
|
2977
|
-
debug;
|
|
2978
|
-
where;
|
|
2979
|
-
meta;
|
|
2980
|
-
docsUrl;
|
|
2981
|
-
exitCode;
|
|
2982
|
-
nextSteps;
|
|
2983
|
-
nextActions;
|
|
2984
|
-
humanLines;
|
|
2985
|
-
constructor(options) {
|
|
2986
|
-
super(options.summary);
|
|
2987
|
-
this.name = "CliError";
|
|
2988
|
-
this.code = options.code;
|
|
2989
|
-
this.domain = options.domain;
|
|
2990
|
-
this.severity = "error";
|
|
2991
|
-
this.summary = options.summary;
|
|
2992
|
-
this.why = options.why;
|
|
2993
|
-
this.fix = options.fix;
|
|
2994
|
-
this.debug = options.debug ?? null;
|
|
2995
|
-
this.where = options.where ?? null;
|
|
2996
|
-
this.meta = options.meta ?? {};
|
|
2997
|
-
this.docsUrl = options.docsUrl ?? null;
|
|
2998
|
-
this.exitCode = options.exitCode ?? 1;
|
|
2999
|
-
this.nextSteps = options.nextSteps ?? [];
|
|
3000
|
-
this.nextActions = options.nextActions ?? [];
|
|
3001
|
-
this.humanLines = options.humanLines && options.humanLines.length > 0 ? [...options.humanLines] : null;
|
|
3002
|
-
}
|
|
3003
|
-
};
|
|
3004
|
-
function usageError(summary, why, fix, nextSteps = [], domain = "cli") {
|
|
3005
|
-
return new CliError({
|
|
3006
|
-
code: "USAGE_ERROR",
|
|
3007
|
-
domain,
|
|
3008
|
-
summary,
|
|
3009
|
-
why,
|
|
3010
|
-
fix,
|
|
3011
|
-
exitCode: 2,
|
|
3012
|
-
nextSteps
|
|
3013
|
-
});
|
|
3014
|
-
}
|
|
3015
|
-
function authRequiredError(nextSteps = ["prisma auth login"], options = {}) {
|
|
3016
|
-
return new CliError({
|
|
3017
|
-
code: "AUTH_REQUIRED",
|
|
3018
|
-
domain: "auth",
|
|
3019
|
-
summary: "Authentication required",
|
|
3020
|
-
why: "This command needs an authenticated session.",
|
|
3021
|
-
fix: "Run prisma auth login, or rerun the command in a TTY to sign in interactively.",
|
|
3022
|
-
debug: options.debug,
|
|
3023
|
-
exitCode: 1,
|
|
3024
|
-
nextSteps
|
|
3025
|
-
});
|
|
3026
|
-
}
|
|
3027
|
-
//#endregion
|
|
3028
2973
|
//#region src/controllers/branch.ts
|
|
3029
2974
|
function sortBranches(branches) {
|
|
3030
2975
|
return branches.slice().sort((left, right) => {
|
|
@@ -3067,14 +3012,17 @@ function toBranchSummary(branch) {
|
|
|
3067
3012
|
}
|
|
3068
3013
|
function branchApiError(summary, response, error) {
|
|
3069
3014
|
const status = response?.status ?? 0;
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
domain: "branch",
|
|
3073
|
-
summary,
|
|
3015
|
+
const apiCode = error?.error?.code;
|
|
3016
|
+
return new CliStructuredError("BRANCH.API_ERROR", summary, {
|
|
3074
3017
|
why: error?.error?.message ?? `The Management API returned status ${status || "unknown"}.`,
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3018
|
+
...status || apiCode !== void 0 ? { meta: {
|
|
3019
|
+
...status ? { status } : {},
|
|
3020
|
+
...apiCode !== void 0 ? { apiCode } : {}
|
|
3021
|
+
} } : {},
|
|
3022
|
+
nextActions: [{
|
|
3023
|
+
kind: "user-choice",
|
|
3024
|
+
label: error?.error?.hint ?? "Re-run with --log-level verbose for the underlying API response details."
|
|
3025
|
+
}]
|
|
3078
3026
|
});
|
|
3079
3027
|
}
|
|
3080
3028
|
//#endregion
|
|
@@ -3342,54 +3290,50 @@ function createManagementProjectProvider(client) {
|
|
|
3342
3290
|
}
|
|
3343
3291
|
};
|
|
3344
3292
|
}
|
|
3293
|
+
function userChoice$6(label) {
|
|
3294
|
+
return {
|
|
3295
|
+
kind: "user-choice",
|
|
3296
|
+
label
|
|
3297
|
+
};
|
|
3298
|
+
}
|
|
3345
3299
|
function projectRenameFailedError(name, error) {
|
|
3346
|
-
return new
|
|
3347
|
-
code: "PROJECT_RENAME_FAILED",
|
|
3348
|
-
domain: "project",
|
|
3349
|
-
summary: "Project rename failed",
|
|
3300
|
+
return new CliStructuredError("PROJECT.RENAME_FAILED", "Project rename failed", {
|
|
3350
3301
|
why: error?.error?.message ?? `The platform rejected the name "${name}".`,
|
|
3351
|
-
|
|
3352
|
-
exitCode: 1,
|
|
3353
|
-
nextSteps: []
|
|
3302
|
+
nextActions: [userChoice$6(error?.error?.hint ?? "Pass a different project name and retry the rename.")]
|
|
3354
3303
|
});
|
|
3355
3304
|
}
|
|
3356
3305
|
function projectDeleteBlockedError(projectId, error) {
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3306
|
+
const deleteServicesCommand = formatPrismaCliCommand([
|
|
3307
|
+
"service",
|
|
3308
|
+
"delete",
|
|
3309
|
+
"--service",
|
|
3310
|
+
"<name>"
|
|
3311
|
+
]);
|
|
3312
|
+
return new CliStructuredError("PROJECT.DELETE_BLOCKED", "Project cannot be deleted yet", {
|
|
3361
3313
|
why: error?.error?.message ?? `Project "${projectId}" still has active deployments.`,
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
"--service",
|
|
3368
|
-
"<name>"
|
|
3369
|
-
])]
|
|
3314
|
+
nextActions: [userChoice$6("Delete the project's services first, then retry the deletion."), {
|
|
3315
|
+
kind: "run-command",
|
|
3316
|
+
label: deleteServicesCommand,
|
|
3317
|
+
command: deleteServicesCommand
|
|
3318
|
+
}]
|
|
3370
3319
|
});
|
|
3371
3320
|
}
|
|
3372
3321
|
function projectTransferRejectedError(projectId, error) {
|
|
3373
|
-
return new
|
|
3374
|
-
code: "PROJECT_TRANSFER_REJECTED",
|
|
3375
|
-
domain: "project",
|
|
3376
|
-
summary: "Project transfer was rejected",
|
|
3322
|
+
return new CliStructuredError("PROJECT.TRANSFER_REJECTED", "Project transfer was rejected", {
|
|
3377
3323
|
why: error?.error?.message ?? `The platform rejected the transfer of project "${projectId}", for example because the recipient token is invalid or expired.`,
|
|
3378
|
-
|
|
3379
|
-
exitCode: 1,
|
|
3380
|
-
nextSteps: []
|
|
3324
|
+
nextActions: [userChoice$6("Check the recipient workspace session or token and retry the transfer.")]
|
|
3381
3325
|
});
|
|
3382
3326
|
}
|
|
3383
3327
|
function projectApiError(summary, response, error) {
|
|
3384
3328
|
const status = response?.status ?? 0;
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
domain: "project",
|
|
3388
|
-
summary,
|
|
3329
|
+
const apiCode = error?.error?.code;
|
|
3330
|
+
return new CliStructuredError("PROJECT.API_ERROR", summary, {
|
|
3389
3331
|
why: error?.error?.message ?? `The Management API returned status ${status || "unknown"}.`,
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3332
|
+
...apiCode !== void 0 || status ? { meta: {
|
|
3333
|
+
...status ? { status } : {},
|
|
3334
|
+
...apiCode !== void 0 ? { apiCode } : {}
|
|
3335
|
+
} } : {},
|
|
3336
|
+
nextActions: [userChoice$6(error?.error?.hint ?? "Re-run with --log-level verbose for the underlying API response details.")]
|
|
3393
3337
|
});
|
|
3394
3338
|
}
|
|
3395
3339
|
//#endregion
|
|
@@ -3479,60 +3423,59 @@ async function inspectProjectBinding(options) {
|
|
|
3479
3423
|
});
|
|
3480
3424
|
});
|
|
3481
3425
|
}
|
|
3426
|
+
function runCommand$5(command, reason) {
|
|
3427
|
+
return {
|
|
3428
|
+
kind: "run-command",
|
|
3429
|
+
label: command,
|
|
3430
|
+
command,
|
|
3431
|
+
...reason === void 0 ? {} : { reason }
|
|
3432
|
+
};
|
|
3433
|
+
}
|
|
3434
|
+
function userChoice$5(label) {
|
|
3435
|
+
return {
|
|
3436
|
+
kind: "user-choice",
|
|
3437
|
+
label
|
|
3438
|
+
};
|
|
3439
|
+
}
|
|
3482
3440
|
function projectNotFoundError$1(projectRef, workspace) {
|
|
3483
|
-
return
|
|
3441
|
+
return projectResolutionErrorToStructured(new ProjectNotFoundError(projectRef, workspace));
|
|
3484
3442
|
}
|
|
3485
|
-
function
|
|
3486
|
-
return new
|
|
3487
|
-
code: "PROJECT_NOT_FOUND",
|
|
3488
|
-
domain: "project",
|
|
3489
|
-
summary: "Project not found",
|
|
3443
|
+
function projectNotFoundStructuredError(projectRef, workspace) {
|
|
3444
|
+
return new CliStructuredError("PROJECT.NOT_FOUND", "Project not found", {
|
|
3490
3445
|
why: `The project "${projectRef}" does not exist in workspace "${workspace.name}" or is not accessible.`,
|
|
3491
|
-
|
|
3492
|
-
exitCode: 1,
|
|
3493
|
-
nextSteps: ["prisma project list"]
|
|
3446
|
+
nextActions: [userChoice$5("Pass a project id or name from prisma project list."), runCommand$5("prisma project list")]
|
|
3494
3447
|
});
|
|
3495
3448
|
}
|
|
3496
3449
|
function projectAmbiguousError(projectRef, matches) {
|
|
3497
|
-
return
|
|
3450
|
+
return projectResolutionErrorToStructured(new ProjectAmbiguousError(projectRef, matches));
|
|
3498
3451
|
}
|
|
3499
|
-
function
|
|
3452
|
+
function projectAmbiguousStructuredError(projectRef, matches) {
|
|
3500
3453
|
const firstMatch = matches[0];
|
|
3501
|
-
const
|
|
3502
|
-
if (firstMatch)
|
|
3503
|
-
return new
|
|
3504
|
-
code: "PROJECT_AMBIGUOUS",
|
|
3505
|
-
domain: "project",
|
|
3506
|
-
summary: "Project resolution is ambiguous",
|
|
3454
|
+
const nextActions = [userChoice$5("Pass --project <id-or-name> to choose the project explicitly."), runCommand$5("prisma project list")];
|
|
3455
|
+
if (firstMatch) nextActions.push(runCommand$5(`prisma project link ${firstMatch.id}`));
|
|
3456
|
+
return new CliStructuredError("PROJECT.AMBIGUOUS", "Project resolution is ambiguous", {
|
|
3507
3457
|
why: projectRef ? `Multiple projects matched "${projectRef}".` : "Multiple projects matched the current directory context.",
|
|
3508
|
-
fix: "Pass --project <id-or-name> to choose the project explicitly.",
|
|
3509
3458
|
meta: { matches: matches.map((project) => ({
|
|
3510
3459
|
id: project.id,
|
|
3511
3460
|
name: project.name
|
|
3512
3461
|
})) },
|
|
3513
|
-
|
|
3514
|
-
nextSteps
|
|
3462
|
+
nextActions
|
|
3515
3463
|
});
|
|
3516
3464
|
}
|
|
3517
|
-
function
|
|
3518
|
-
return new
|
|
3519
|
-
code: "LOCAL_STATE_STALE",
|
|
3520
|
-
domain: "project",
|
|
3521
|
-
summary: "Local project binding is stale",
|
|
3465
|
+
function localStateStaleStructuredError() {
|
|
3466
|
+
return new CliStructuredError("PROJECT.LOCAL_STATE_STALE", "Local project binding is stale", {
|
|
3522
3467
|
why: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
|
|
3523
|
-
fix: `Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}, then choose a Project explicitly.`,
|
|
3524
3468
|
meta: { pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH },
|
|
3525
|
-
|
|
3526
|
-
|
|
3469
|
+
nextActions: [
|
|
3470
|
+
userChoice$5(`Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}, then choose a Project explicitly.`),
|
|
3471
|
+
runCommand$5("prisma project list"),
|
|
3472
|
+
runCommand$5("prisma project link <id-or-name>")
|
|
3473
|
+
]
|
|
3527
3474
|
});
|
|
3528
3475
|
}
|
|
3529
|
-
function
|
|
3530
|
-
return new
|
|
3531
|
-
code: "LOCAL_PROJECT_WORKSPACE_MISMATCH",
|
|
3532
|
-
domain: "project",
|
|
3533
|
-
summary: "Project link uses another workspace",
|
|
3476
|
+
function localProjectWorkspaceMismatchStructuredError(options) {
|
|
3477
|
+
return new CliStructuredError("PROJECT.LOCAL_WORKSPACE_MISMATCH", "Project link uses another workspace", {
|
|
3534
3478
|
why: `${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} links this directory to project ${options.pinnedProjectId} in workspace ${options.pinnedWorkspaceId}, but your current CLI session is workspace "${options.activeWorkspace.name}" (${options.activeWorkspace.id}).`,
|
|
3535
|
-
fix: "Switch to the linked workspace, or relink this directory to a project in the current workspace.",
|
|
3536
3479
|
meta: {
|
|
3537
3480
|
pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
|
|
3538
3481
|
pinnedWorkspaceId: options.pinnedWorkspaceId,
|
|
@@ -3540,27 +3483,29 @@ function localProjectWorkspaceMismatchCliError(options) {
|
|
|
3540
3483
|
activeWorkspaceId: options.activeWorkspace.id,
|
|
3541
3484
|
activeWorkspaceName: options.activeWorkspace.name
|
|
3542
3485
|
},
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
`prisma auth workspace use ${options.pinnedWorkspaceId}
|
|
3546
|
-
"prisma project list",
|
|
3547
|
-
"prisma project link <id-or-name>"
|
|
3486
|
+
nextActions: [
|
|
3487
|
+
userChoice$5("Switch to the linked workspace, or relink this directory to a project in the current workspace."),
|
|
3488
|
+
runCommand$5(`prisma auth workspace use ${options.pinnedWorkspaceId}`),
|
|
3489
|
+
runCommand$5("prisma project list"),
|
|
3490
|
+
runCommand$5("prisma project link <id-or-name>")
|
|
3548
3491
|
]
|
|
3549
3492
|
});
|
|
3550
3493
|
}
|
|
3551
3494
|
/**
|
|
3552
|
-
* Converts expected project-resolution variants to
|
|
3495
|
+
* Converts expected project-resolution variants to the structured errors
|
|
3496
|
+
* a command boundary raises — the codes here are the registered PROJECT.*
|
|
3497
|
+
* codes, assigned at origin.
|
|
3553
3498
|
* `LocalResolutionPinReadAbortedError` and `UnhandledException` intentionally
|
|
3554
3499
|
* propagate as exceptions; callers such as `resolveProjectShowInRealMode`
|
|
3555
3500
|
* throw this helper's result, so passthrough variants should keep bubbling.
|
|
3556
3501
|
*/
|
|
3557
|
-
function
|
|
3502
|
+
function projectResolutionErrorToStructured(error) {
|
|
3558
3503
|
return matchError(error, {
|
|
3559
|
-
ProjectNotFoundError: (error) =>
|
|
3560
|
-
ProjectAmbiguousError: (error) =>
|
|
3561
|
-
ProjectSetupRequiredError: (error) =>
|
|
3562
|
-
LocalStateStaleError: () =>
|
|
3563
|
-
LocalProjectWorkspaceMismatchError: (error) =>
|
|
3504
|
+
ProjectNotFoundError: (error) => projectNotFoundStructuredError(error.projectRef, error.workspace),
|
|
3505
|
+
ProjectAmbiguousError: (error) => projectAmbiguousStructuredError(error.projectRef, error.matches),
|
|
3506
|
+
ProjectSetupRequiredError: (error) => projectSetupRequiredStructuredError(error),
|
|
3507
|
+
LocalStateStaleError: () => localStateStaleStructuredError(),
|
|
3508
|
+
LocalProjectWorkspaceMismatchError: (error) => localProjectWorkspaceMismatchStructuredError({
|
|
3564
3509
|
pinnedWorkspaceId: error.pinnedWorkspaceId,
|
|
3565
3510
|
pinnedProjectId: error.pinnedProjectId,
|
|
3566
3511
|
activeWorkspace: error.activeWorkspace
|
|
@@ -3590,17 +3535,11 @@ async function projectSetupRequiredError(options) {
|
|
|
3590
3535
|
suggestion
|
|
3591
3536
|
});
|
|
3592
3537
|
}
|
|
3593
|
-
function
|
|
3538
|
+
function projectSetupRequiredStructuredError(error) {
|
|
3594
3539
|
const suggestion = error.suggestion;
|
|
3595
|
-
return new
|
|
3596
|
-
code: "PROJECT_SETUP_REQUIRED",
|
|
3597
|
-
domain: "project",
|
|
3598
|
-
summary: "Choose a Project before running this command",
|
|
3540
|
+
return new CliStructuredError("PROJECT.SETUP_REQUIRED", "Choose a Project before running this command", {
|
|
3599
3541
|
why: error.message,
|
|
3600
|
-
fix: "Link the directory to an existing Project, or pass --project <id-or-name> for this command.",
|
|
3601
3542
|
meta: { ...suggestion },
|
|
3602
|
-
exitCode: 1,
|
|
3603
|
-
nextSteps: ["prisma project list", ...suggestion.recoveryCommands],
|
|
3604
3543
|
nextActions: buildProjectSetupNextActions({
|
|
3605
3544
|
commandName: error.commandName,
|
|
3606
3545
|
suggestedProjectName: suggestion.suggestedProjectName
|
|
@@ -3613,7 +3552,6 @@ function buildProjectSetupNextActions(options = {}) {
|
|
|
3613
3552
|
const retryCommand = options.retryCommand ?? recoveryCommands[1];
|
|
3614
3553
|
const actions = [{
|
|
3615
3554
|
kind: "user-choice",
|
|
3616
|
-
journey: "project-setup",
|
|
3617
3555
|
label: "Ask the user whether to link an existing Project or create a new one",
|
|
3618
3556
|
commands: [
|
|
3619
3557
|
"prisma project list",
|
|
@@ -3623,7 +3561,6 @@ function buildProjectSetupNextActions(options = {}) {
|
|
|
3623
3561
|
reason: options.reason ?? "This directory is not linked to a Prisma Project. Package and directory names are suggestions only, not a safe Project selection."
|
|
3624
3562
|
}, {
|
|
3625
3563
|
kind: "run-command",
|
|
3626
|
-
journey: "project-setup",
|
|
3627
3564
|
label: "Link the chosen Project",
|
|
3628
3565
|
command: linkCommand,
|
|
3629
3566
|
reason: "Linking writes the durable local Project binding for this directory."
|
|
@@ -3631,14 +3568,12 @@ function buildProjectSetupNextActions(options = {}) {
|
|
|
3631
3568
|
const createCommand = options.createCommand ?? (options.suggestedProjectName ? `prisma project create ${formatCommandArgument(options.suggestedProjectName)}` : void 0);
|
|
3632
3569
|
if (createCommand) actions.push({
|
|
3633
3570
|
kind: "run-command",
|
|
3634
|
-
journey: "project-setup",
|
|
3635
3571
|
label: "Create and link a new Project",
|
|
3636
3572
|
command: createCommand,
|
|
3637
3573
|
reason: "Use this when the user wants a new Prisma Project instead of an existing one."
|
|
3638
3574
|
});
|
|
3639
3575
|
if (options.commandName) actions.push({
|
|
3640
3576
|
kind: "run-command",
|
|
3641
|
-
journey: "recover",
|
|
3642
3577
|
label: "Retry with an explicit Project",
|
|
3643
3578
|
command: retryCommand ?? `prisma ${options.commandName} --project <id-or-name>`
|
|
3644
3579
|
});
|
|
@@ -3770,6 +3705,28 @@ function toProjectSummary$1(project) {
|
|
|
3770
3705
|
//#region src/controllers/project.ts
|
|
3771
3706
|
const GITHUB_INSTALL_POLL_INTERVAL_MS = 2e3;
|
|
3772
3707
|
const GITHUB_INSTALL_POLL_TIMEOUT_MS = 12e4;
|
|
3708
|
+
function runCommand$4(command) {
|
|
3709
|
+
return {
|
|
3710
|
+
kind: "run-command",
|
|
3711
|
+
label: command,
|
|
3712
|
+
command
|
|
3713
|
+
};
|
|
3714
|
+
}
|
|
3715
|
+
function userChoice$4(label) {
|
|
3716
|
+
return {
|
|
3717
|
+
kind: "user-choice",
|
|
3718
|
+
label
|
|
3719
|
+
};
|
|
3720
|
+
}
|
|
3721
|
+
/** A URL is not a command: putting one in `command` tells a consumer to
|
|
3722
|
+
* execute it. */
|
|
3723
|
+
function openUrl(url) {
|
|
3724
|
+
return {
|
|
3725
|
+
kind: "open-url",
|
|
3726
|
+
label: url,
|
|
3727
|
+
url
|
|
3728
|
+
};
|
|
3729
|
+
}
|
|
3773
3730
|
async function readProjectListLocalBinding(cwd, projects, signal) {
|
|
3774
3731
|
const pinResult = await readLocalResolutionPin(cwd, signal);
|
|
3775
3732
|
if (pinResult.isErr()) return localPinReadErrorToInvalidLocalBinding(pinResult.error);
|
|
@@ -3790,37 +3747,31 @@ function localPinReadErrorToInvalidLocalBinding(error) {
|
|
|
3790
3747
|
});
|
|
3791
3748
|
}
|
|
3792
3749
|
function transferRecipientRequiredError(formatCommand) {
|
|
3793
|
-
return new
|
|
3794
|
-
code: "TRANSFER_RECIPIENT_REQUIRED",
|
|
3795
|
-
domain: "project",
|
|
3796
|
-
summary: "Transfer recipient required",
|
|
3750
|
+
return new CliStructuredError("PROJECT.TRANSFER_RECIPIENT_REQUIRED", "Transfer recipient required", {
|
|
3797
3751
|
why: "Project transfer needs the receiving workspace.",
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3752
|
+
nextActions: [
|
|
3753
|
+
userChoice$4("Pass --to-workspace <id-or-name> for a locally authenticated workspace, or --recipient-token <token> for a cross-account transfer."),
|
|
3754
|
+
runCommand$4(formatCommand([
|
|
3755
|
+
"auth",
|
|
3756
|
+
"workspace",
|
|
3757
|
+
"list"
|
|
3758
|
+
])),
|
|
3759
|
+
runCommand$4(formatCommand([
|
|
3760
|
+
"project",
|
|
3761
|
+
"transfer",
|
|
3762
|
+
"<project>",
|
|
3763
|
+
"--to-workspace",
|
|
3764
|
+
"<id-or-name>",
|
|
3765
|
+
"--confirm",
|
|
3766
|
+
"<project-id>"
|
|
3767
|
+
]))
|
|
3768
|
+
]
|
|
3813
3769
|
});
|
|
3814
3770
|
}
|
|
3815
3771
|
function transferRecipientUnavailableError(formatCommand) {
|
|
3816
|
-
return new
|
|
3817
|
-
code: "TRANSFER_RECIPIENT_UNAVAILABLE",
|
|
3818
|
-
domain: "project",
|
|
3819
|
-
summary: "Local workspace sessions are unavailable",
|
|
3772
|
+
return new CliStructuredError("PROJECT.TRANSFER_RECIPIENT_UNAVAILABLE", "Local workspace sessions are unavailable", {
|
|
3820
3773
|
why: `--to-workspace resolves locally stored OAuth sessions, but ${SERVICE_TOKEN_ENV_VAR} is set and service-token mode does not read them.`,
|
|
3821
|
-
|
|
3822
|
-
exitCode: 1,
|
|
3823
|
-
nextSteps: [formatCommand([
|
|
3774
|
+
nextActions: [userChoice$4("Pass --recipient-token <token> with an access token for the receiving workspace, or unset the service token."), runCommand$4(formatCommand([
|
|
3824
3775
|
"project",
|
|
3825
3776
|
"transfer",
|
|
3826
3777
|
"<project>",
|
|
@@ -3828,7 +3779,7 @@ function transferRecipientUnavailableError(formatCommand) {
|
|
|
3828
3779
|
"<token>",
|
|
3829
3780
|
"--confirm",
|
|
3830
3781
|
"<project-id>"
|
|
3831
|
-
])]
|
|
3782
|
+
]))]
|
|
3832
3783
|
});
|
|
3833
3784
|
}
|
|
3834
3785
|
async function cleanupLocalPinForProject(context, projectId, hooks) {
|
|
@@ -3966,8 +3917,8 @@ async function findRepositoryInInstallationIfAvailable(api, installationId, repo
|
|
|
3966
3917
|
}
|
|
3967
3918
|
}
|
|
3968
3919
|
function isUnavailableScmInstallationError(error) {
|
|
3969
|
-
if (!(error
|
|
3970
|
-
return error.meta
|
|
3920
|
+
if (!CliStructuredError.is(error) || error.code !== "GIT.REPO_CONNECTION_FAILED") return false;
|
|
3921
|
+
return error.meta?.status === 404 || error.meta?.status === 422;
|
|
3971
3922
|
}
|
|
3972
3923
|
async function createGitHubInstallIntent(api, workspaceId, signal) {
|
|
3973
3924
|
const { data, error, response } = await api.POST("/v1/scm-installations/install-intents", {
|
|
@@ -4020,69 +3971,50 @@ function toRepositoryConnection(record) {
|
|
|
4020
3971
|
};
|
|
4021
3972
|
}
|
|
4022
3973
|
function unsupportedRepositoryProviderError() {
|
|
4023
|
-
return new
|
|
4024
|
-
code: "REPO_PROVIDER_UNSUPPORTED",
|
|
4025
|
-
domain: "project",
|
|
4026
|
-
summary: "Repository provider is not supported",
|
|
3974
|
+
return new CliStructuredError("GIT.REPO_PROVIDER_UNSUPPORTED", "Repository provider is not supported", {
|
|
4027
3975
|
why: "Repository connection supports GitHub repository URLs only.",
|
|
4028
|
-
|
|
4029
|
-
exitCode: 2,
|
|
4030
|
-
nextSteps: ["prisma git connect git@github.com:owner/repo.git"]
|
|
3976
|
+
nextActions: [userChoice$4("Pass a GitHub repository URL such as git@github.com:prisma/prisma-cli.git."), runCommand$4("prisma git connect git@github.com:owner/repo.git")]
|
|
4031
3977
|
});
|
|
4032
3978
|
}
|
|
4033
3979
|
function repoNotConnectedError() {
|
|
4034
|
-
return new
|
|
4035
|
-
code: "REPO_NOT_CONNECTED",
|
|
4036
|
-
domain: "project",
|
|
4037
|
-
summary: "No GitHub repository connected",
|
|
3980
|
+
return new CliStructuredError("GIT.REPO_NOT_CONNECTED", "No GitHub repository connected", {
|
|
4038
3981
|
why: "The resolved project does not have an active GitHub repository connection.",
|
|
4039
|
-
|
|
4040
|
-
exitCode: 1,
|
|
4041
|
-
nextSteps: ["prisma git connect"]
|
|
3982
|
+
nextActions: [userChoice$4("Run prisma git connect before disconnecting."), runCommand$4("prisma git connect")]
|
|
4042
3983
|
});
|
|
4043
3984
|
}
|
|
4044
|
-
function repoInstallationRequiredError(repository, installUrl
|
|
4045
|
-
return new
|
|
4046
|
-
code: "REPO_INSTALLATION_REQUIRED",
|
|
4047
|
-
domain: "project",
|
|
4048
|
-
summary: "GitHub App installation required",
|
|
3985
|
+
function repoInstallationRequiredError(repository, installUrl) {
|
|
3986
|
+
return new CliStructuredError("GIT.REPO_INSTALLATION_REQUIRED", "GitHub App installation required", {
|
|
4049
3987
|
why: `The selected workspace does not have a GitHub App installation that can be used to link ${repository.fullName}.`,
|
|
4050
|
-
fix: opened ? "Finish installing the GitHub App in the browser, then rerun prisma git connect." : "Open the GitHub App installation URL, approve access, then rerun prisma git connect.",
|
|
4051
3988
|
meta: {
|
|
4052
3989
|
repository: repository.fullName,
|
|
4053
|
-
installUrl
|
|
4054
|
-
opened
|
|
3990
|
+
installUrl
|
|
4055
3991
|
},
|
|
4056
|
-
|
|
4057
|
-
|
|
3992
|
+
nextActions: [
|
|
3993
|
+
userChoice$4("Finish installing the GitHub App in the browser, then rerun prisma git connect."),
|
|
3994
|
+
openUrl(installUrl),
|
|
3995
|
+
runCommand$4(`prisma git connect ${repository.url}`)
|
|
3996
|
+
]
|
|
4058
3997
|
});
|
|
4059
3998
|
}
|
|
4060
|
-
function repoNotAccessibleError(repository, installUrl
|
|
4061
|
-
return new
|
|
4062
|
-
code: "REPO_NOT_ACCESSIBLE",
|
|
4063
|
-
domain: "project",
|
|
4064
|
-
summary: "GitHub repository is not accessible",
|
|
3999
|
+
function repoNotAccessibleError(repository, installUrl) {
|
|
4000
|
+
return new CliStructuredError("GIT.REPO_NOT_ACCESSIBLE", "GitHub repository is not accessible", {
|
|
4065
4001
|
why: `The GitHub App installations connected to this workspace do not expose ${repository.fullName}.`,
|
|
4066
|
-
fix: "Open the GitHub App installation URL, grant access to this repository, then rerun prisma git connect.",
|
|
4067
4002
|
meta: {
|
|
4068
4003
|
repository: repository.fullName,
|
|
4069
|
-
installUrl
|
|
4070
|
-
opened
|
|
4004
|
+
installUrl
|
|
4071
4005
|
},
|
|
4072
|
-
|
|
4073
|
-
|
|
4006
|
+
nextActions: [
|
|
4007
|
+
userChoice$4("Open the GitHub App installation URL, grant access to this repository, then rerun prisma git connect."),
|
|
4008
|
+
openUrl(installUrl),
|
|
4009
|
+
runCommand$4(`prisma git connect ${repository.url}`)
|
|
4010
|
+
]
|
|
4074
4011
|
});
|
|
4075
4012
|
}
|
|
4076
4013
|
function repoAlreadyConnectedError(repositoryFullName) {
|
|
4077
|
-
return new
|
|
4078
|
-
code: "REPO_ALREADY_CONNECTED",
|
|
4079
|
-
domain: "project",
|
|
4080
|
-
summary: "Project already has a GitHub repository connected",
|
|
4014
|
+
return new CliStructuredError("GIT.REPO_ALREADY_CONNECTED", "Project already has a GitHub repository connected", {
|
|
4081
4015
|
why: `The resolved project is already connected to ${repositoryFullName}.`,
|
|
4082
|
-
fix: "Disconnect the existing repository before connecting a different one.",
|
|
4083
4016
|
meta: { repository: repositoryFullName },
|
|
4084
|
-
|
|
4085
|
-
nextSteps: ["prisma git disconnect"]
|
|
4017
|
+
nextActions: [userChoice$4("Disconnect the existing repository before connecting a different one."), runCommand$4("prisma git disconnect")]
|
|
4086
4018
|
});
|
|
4087
4019
|
}
|
|
4088
4020
|
function repositoryFullNamesMatch(left, right) {
|
|
@@ -4093,26 +4025,22 @@ function repoConnectionApiError(summary, response, error) {
|
|
|
4093
4025
|
const apiCode = error?.error?.code;
|
|
4094
4026
|
const apiMessage = error?.error?.message;
|
|
4095
4027
|
const apiHint = error?.error?.hint;
|
|
4096
|
-
|
|
4097
|
-
return new
|
|
4098
|
-
|
|
4099
|
-
domain: "project",
|
|
4100
|
-
summary,
|
|
4101
|
-
why: apiMessage ?? `The Management API returned status ${status || "unknown"}.`,
|
|
4102
|
-
fix: apiHint ?? repoConnectionFixForStatus(status),
|
|
4028
|
+
const unauthorized = status === 401 || status === 403;
|
|
4029
|
+
return new CliStructuredError("GIT.REPO_CONNECTION_FAILED", summary, {
|
|
4030
|
+
why: apiMessage ?? (unauthorized ? `The Management API rejected the request as unauthorized (HTTP ${status}).` : `The Management API returned status ${status || "unknown"}.`),
|
|
4103
4031
|
meta: {
|
|
4104
4032
|
status,
|
|
4105
4033
|
...apiCode ? { apiCode } : {}
|
|
4106
4034
|
},
|
|
4107
|
-
|
|
4108
|
-
nextSteps: ["prisma project show"]
|
|
4035
|
+
nextActions: [userChoice$4(apiHint ?? repoConnectionFixForStatus(status)), runCommand$4(unauthorized ? "prisma auth login" : "prisma project show")]
|
|
4109
4036
|
});
|
|
4110
4037
|
}
|
|
4111
4038
|
function repoConnectionFixForStatus(status) {
|
|
4039
|
+
if (status === 401 || status === 403) return "Sign in again with prisma auth login, then rerun the command.";
|
|
4112
4040
|
if (status === 404) return "Install the GitHub App for this workspace, then rerun prisma git connect.";
|
|
4113
4041
|
if (status === 409) return "This project or repository is already linked. Disconnect the old link first, then try again.";
|
|
4114
4042
|
if (status === 422) return "Make sure the GitHub App installation has access to this repository.";
|
|
4115
|
-
return "Re-run with --
|
|
4043
|
+
return "Re-run with --log-level verbose for the underlying API response details.";
|
|
4116
4044
|
}
|
|
4117
4045
|
//#endregion
|
|
4118
4046
|
//#region src/lib/project/setup.ts
|
|
@@ -4126,7 +4054,7 @@ function resolveProjectForSetup(projectRef, projects, workspace) {
|
|
|
4126
4054
|
if (match !== void 0) return match;
|
|
4127
4055
|
throw projectNotFoundError$1(projectRef, workspace);
|
|
4128
4056
|
}
|
|
4129
|
-
function
|
|
4057
|
+
function projectDirectoryBindingErrorToStructured(error) {
|
|
4130
4058
|
return matchError(error, {
|
|
4131
4059
|
LocalResolutionPinSerializationError: (error) => {
|
|
4132
4060
|
throw error;
|
|
@@ -4154,16 +4082,18 @@ function projectDirectoryBindingErrorToCliError(error) {
|
|
|
4154
4082
|
});
|
|
4155
4083
|
}
|
|
4156
4084
|
function localStateWriteFailedError(error, options) {
|
|
4157
|
-
return new
|
|
4158
|
-
code: "LOCAL_STATE_WRITE_FAILED",
|
|
4159
|
-
domain: "project",
|
|
4160
|
-
summary: "Could not save local Project binding",
|
|
4085
|
+
return new CliStructuredError("PROJECT.LOCAL_STATE_WRITE_FAILED", "Could not save local Project binding", {
|
|
4161
4086
|
why: options.why,
|
|
4162
|
-
fix: "Check that this directory is writable and that .prisma/local.json and .gitignore are not blocked by directories or permissions, then retry.",
|
|
4163
|
-
debug: formatDebugDetails(error.cause),
|
|
4164
4087
|
meta: options.meta,
|
|
4165
|
-
|
|
4166
|
-
|
|
4088
|
+
cause: error.cause,
|
|
4089
|
+
nextActions: [{
|
|
4090
|
+
kind: "user-choice",
|
|
4091
|
+
label: "Check that this directory is writable and that .prisma/local.json and .gitignore are not blocked by directories or permissions, then retry."
|
|
4092
|
+
}, {
|
|
4093
|
+
kind: "run-command",
|
|
4094
|
+
label: "prisma project link <id-or-name>",
|
|
4095
|
+
command: "prisma project link <id-or-name>"
|
|
4096
|
+
}]
|
|
4167
4097
|
});
|
|
4168
4098
|
}
|
|
4169
4099
|
function toProjectSummary(project) {
|
|
@@ -4175,29 +4105,35 @@ function toProjectSummary(project) {
|
|
|
4175
4105
|
};
|
|
4176
4106
|
}
|
|
4177
4107
|
function projectSetupNameRequiredError(command) {
|
|
4178
|
-
|
|
4108
|
+
const example = `prisma ${command} my-app`;
|
|
4109
|
+
return new CliStructuredError("PROJECT.USAGE_ERROR", "Project create requires a name", {
|
|
4110
|
+
why: "The project name must be a non-empty value.",
|
|
4111
|
+
nextActions: [{
|
|
4112
|
+
kind: "user-choice",
|
|
4113
|
+
label: "Pass a Project name explicitly."
|
|
4114
|
+
}, {
|
|
4115
|
+
kind: "run-command",
|
|
4116
|
+
label: example,
|
|
4117
|
+
command: example
|
|
4118
|
+
}]
|
|
4119
|
+
});
|
|
4179
4120
|
}
|
|
4180
4121
|
function projectCreateFailedError(error, projectName, workspace, options) {
|
|
4181
4122
|
const status = extractHttpStatus(error);
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
});
|
|
4192
|
-
return new
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
why: error instanceof Error ? error.message : String(error),
|
|
4197
|
-
fix: options.fallbackFix,
|
|
4198
|
-
debug: formatDebugDetails(error),
|
|
4199
|
-
exitCode: 1,
|
|
4200
|
-
nextSteps: options.nextSteps
|
|
4123
|
+
const permissionRejection = status === 401 || status === 403;
|
|
4124
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4125
|
+
const nextActions = [{
|
|
4126
|
+
kind: "user-choice",
|
|
4127
|
+
label: permissionRejection ? options.permissionFix : options.fallbackFix
|
|
4128
|
+
}, ...options.nextSteps.map((step) => ({
|
|
4129
|
+
kind: "run-command",
|
|
4130
|
+
label: step,
|
|
4131
|
+
command: step
|
|
4132
|
+
}))];
|
|
4133
|
+
return new CliStructuredError("PROJECT.CREATE_FAILED", `Could not create Project "${projectName}"`, {
|
|
4134
|
+
why: permissionRejection ? `The platform rejected the Project create in workspace "${workspace.name}" (HTTP ${status}).` : message,
|
|
4135
|
+
cause: error,
|
|
4136
|
+
nextActions
|
|
4201
4137
|
});
|
|
4202
4138
|
}
|
|
4203
4139
|
const HTTP_STATUS_IN_MESSAGE = /\(HTTP (\d{3})\)/;
|
|
@@ -4212,10 +4148,6 @@ function extractHttpStatus(error) {
|
|
|
4212
4148
|
}
|
|
4213
4149
|
return null;
|
|
4214
4150
|
}
|
|
4215
|
-
function formatDebugDetails(error) {
|
|
4216
|
-
if (error instanceof Error) return error.stack ?? error.message;
|
|
4217
|
-
return typeof error === "string" ? error : null;
|
|
4218
|
-
}
|
|
4219
4151
|
//#endregion
|
|
4220
4152
|
//#region src/commands/project/context.ts
|
|
4221
4153
|
/**
|
|
@@ -4265,7 +4197,7 @@ async function resolvePinnedProject(ctx, workspace, explicitProject, commandName
|
|
|
4265
4197
|
listProjects: () => listWorkspaceProjects$1(ctx),
|
|
4266
4198
|
commandName
|
|
4267
4199
|
});
|
|
4268
|
-
if (target.isErr()) throw
|
|
4200
|
+
if (target.isErr()) throw projectResolutionErrorToStructured(target.error);
|
|
4269
4201
|
return target.value;
|
|
4270
4202
|
}
|
|
4271
4203
|
/** Writes `.prisma/local.json` for this directory and keeps it out of
|
|
@@ -4275,9 +4207,9 @@ async function bindDirectoryToProject(ctx, workspace, project, action) {
|
|
|
4275
4207
|
workspaceId: workspace.id,
|
|
4276
4208
|
projectId: project.id
|
|
4277
4209
|
}, ctx.signal);
|
|
4278
|
-
if (written.isErr()) throw
|
|
4210
|
+
if (written.isErr()) throw projectDirectoryBindingErrorToStructured(written.error);
|
|
4279
4211
|
const ignored = await ensureLocalResolutionPinGitignore(ctx.cwd, ctx.signal);
|
|
4280
|
-
if (ignored.isErr()) throw
|
|
4212
|
+
if (ignored.isErr()) throw projectDirectoryBindingErrorToStructured(ignored.error);
|
|
4281
4213
|
return {
|
|
4282
4214
|
workspace,
|
|
4283
4215
|
project,
|
|
@@ -4313,129 +4245,6 @@ async function resolveActiveWorkspace(ctx) {
|
|
|
4313
4245
|
};
|
|
4314
4246
|
}
|
|
4315
4247
|
//#endregion
|
|
4316
|
-
//#region src/commands/project/errors.ts
|
|
4317
|
-
const PROJECT_CODE_MAP = {
|
|
4318
|
-
USAGE_ERROR: "PROJECT.USAGE_ERROR",
|
|
4319
|
-
PROJECT_NOT_FOUND: "PROJECT.NOT_FOUND",
|
|
4320
|
-
PROJECT_AMBIGUOUS: "PROJECT.AMBIGUOUS",
|
|
4321
|
-
PROJECT_SETUP_REQUIRED: "PROJECT.SETUP_REQUIRED",
|
|
4322
|
-
LOCAL_STATE_STALE: "PROJECT.LOCAL_STATE_STALE",
|
|
4323
|
-
LOCAL_PROJECT_WORKSPACE_MISMATCH: "PROJECT.LOCAL_WORKSPACE_MISMATCH",
|
|
4324
|
-
LOCAL_STATE_WRITE_FAILED: "PROJECT.LOCAL_STATE_WRITE_FAILED",
|
|
4325
|
-
PROJECT_CREATE_FAILED: "PROJECT.CREATE_FAILED",
|
|
4326
|
-
PROJECT_RENAME_FAILED: "PROJECT.RENAME_FAILED",
|
|
4327
|
-
PROJECT_DELETE_BLOCKED: "PROJECT.DELETE_BLOCKED",
|
|
4328
|
-
PROJECT_TRANSFER_REJECTED: "PROJECT.TRANSFER_REJECTED",
|
|
4329
|
-
TRANSFER_RECIPIENT_REQUIRED: "PROJECT.TRANSFER_RECIPIENT_REQUIRED",
|
|
4330
|
-
TRANSFER_RECIPIENT_UNAVAILABLE: "PROJECT.TRANSFER_RECIPIENT_UNAVAILABLE",
|
|
4331
|
-
CONFIRMATION_REQUIRED: "PROJECT.CONFIRMATION_REQUIRED",
|
|
4332
|
-
PROJECT_LINK_TARGET_REQUIRED: "PROJECT.LINK_TARGET_REQUIRED",
|
|
4333
|
-
ENV_VARIABLE_ALREADY_EXISTS: "PROJECT.ENV_VARIABLE_ALREADY_EXISTS",
|
|
4334
|
-
ENV_VARIABLE_NOT_FOUND: "PROJECT.ENV_VARIABLE_NOT_FOUND",
|
|
4335
|
-
ENV_BRANCH_NOT_FOUND: "PROJECT.ENV_BRANCH_NOT_FOUND",
|
|
4336
|
-
ENV_BRANCH_SCOPE_IS_PRODUCTION: "PROJECT.ENV_BRANCH_SCOPE_IS_PRODUCTION",
|
|
4337
|
-
ENV_BRANCH_CREATE_REQUIRES_DEFAULT_BRANCH: "PROJECT.ENV_BRANCH_CREATE_REQUIRES_DEFAULT_BRANCH",
|
|
4338
|
-
ENV_FILE_APPLY_FAILED: "PROJECT.ENV_FILE_APPLY_FAILED",
|
|
4339
|
-
ENV_API_ERROR: "PROJECT.ENV_API_ERROR",
|
|
4340
|
-
PROJECT_API_ERROR: "PROJECT.API_ERROR"
|
|
4341
|
-
};
|
|
4342
|
-
const PACKAGE_RUNNER_PREFIX = /^\S+(?: -y)? @prisma\/cli@\S+ /;
|
|
4343
|
-
const COMMENT_PREFIX$1 = /^#\s*/;
|
|
4344
|
-
/** Ported command strings already name this binary; what still needs
|
|
4345
|
-
* porting is the package-runner spelling the legacy formatter emitted
|
|
4346
|
-
* (`npx -y @prisma/cli@next auth login`), which becomes a plain
|
|
4347
|
-
* invocation. Anything else is passed through untouched. */
|
|
4348
|
-
function portCommandString(command) {
|
|
4349
|
-
if (command.startsWith(`prisma `)) return command;
|
|
4350
|
-
return command.replace(PACKAGE_RUNNER_PREFIX, `${CLI_NAME} `);
|
|
4351
|
-
}
|
|
4352
|
-
const STALE_INTERACTIVE_SIGN_IN$4 = /, or rerun the command in a TTY to sign in interactively\./g;
|
|
4353
|
-
const TRACE_FLAG$4 = /--trace/g;
|
|
4354
|
-
/** `--trace` is gone in this CLI; the log level replaces it. Interactive
|
|
4355
|
-
* sign-in is gone too (R-S2b-2), so the legacy offer to rerun in a TTY
|
|
4356
|
-
* describes something this CLI cannot do; `auth login` is the whole remedy. */
|
|
4357
|
-
function portFixText$4(fix) {
|
|
4358
|
-
return fix.replace(TRACE_FLAG$4, "--log-level verbose").replace(STALE_INTERACTIVE_SIGN_IN$4, ".");
|
|
4359
|
-
}
|
|
4360
|
-
/** A `#`-comment line in the legacy nextSteps is not an action: it
|
|
4361
|
-
* explains the command that follows it, so it becomes that action's
|
|
4362
|
-
* `reason`. */
|
|
4363
|
-
function runCommandActions$1(nextSteps) {
|
|
4364
|
-
const actions = [];
|
|
4365
|
-
let reason;
|
|
4366
|
-
for (const step of nextSteps) {
|
|
4367
|
-
if (step.startsWith("#")) {
|
|
4368
|
-
reason = step.replace(COMMENT_PREFIX$1, "");
|
|
4369
|
-
continue;
|
|
4370
|
-
}
|
|
4371
|
-
const command = portCommandString(step);
|
|
4372
|
-
actions.push({
|
|
4373
|
-
kind: "run-command",
|
|
4374
|
-
label: command,
|
|
4375
|
-
command,
|
|
4376
|
-
...reason === void 0 ? {} : { reason }
|
|
4377
|
-
});
|
|
4378
|
-
reason = void 0;
|
|
4379
|
-
}
|
|
4380
|
-
return actions;
|
|
4381
|
-
}
|
|
4382
|
-
function nextActionsFor$4(error) {
|
|
4383
|
-
return [...error.fix ? [{
|
|
4384
|
-
kind: "user-choice",
|
|
4385
|
-
label: portFixText$4(error.fix)
|
|
4386
|
-
}] : [], ...runCommandActions$1(error.nextSteps)];
|
|
4387
|
-
}
|
|
4388
|
-
function mapProjectOperationError(error) {
|
|
4389
|
-
if (!(error instanceof CliError)) return null;
|
|
4390
|
-
return new CliStructuredError(PROJECT_CODE_MAP[error.code] ?? `PROJECT.${error.code}`, error.summary, {
|
|
4391
|
-
why: error.why ?? void 0,
|
|
4392
|
-
meta: Object.keys(error.meta).length > 0 ? error.meta : void 0,
|
|
4393
|
-
nextActions: nextActionsFor$4(error)
|
|
4394
|
-
});
|
|
4395
|
-
}
|
|
4396
|
-
//#endregion
|
|
4397
|
-
//#region src/commands/branch/errors.ts
|
|
4398
|
-
const BRANCH_CODE_MAP = { BRANCH_API_ERROR: "BRANCH.API_ERROR" };
|
|
4399
|
-
/** The project-resolution codes `branch list` can raise; they keep the
|
|
4400
|
-
* project group's dotted codes and copy. */
|
|
4401
|
-
const PROJECT_CODES$3 = new Set([
|
|
4402
|
-
"PROJECT_NOT_FOUND",
|
|
4403
|
-
"PROJECT_AMBIGUOUS",
|
|
4404
|
-
"PROJECT_SETUP_REQUIRED",
|
|
4405
|
-
"LOCAL_STATE_STALE",
|
|
4406
|
-
"LOCAL_PROJECT_WORKSPACE_MISMATCH"
|
|
4407
|
-
]);
|
|
4408
|
-
const STALE_INTERACTIVE_SIGN_IN$3 = /, or rerun the command in a TTY to sign in interactively\./g;
|
|
4409
|
-
const TRACE_FLAG$3 = /--trace/g;
|
|
4410
|
-
/** `--trace` is gone in this CLI; the log level replaces it. Interactive
|
|
4411
|
-
* sign-in is gone too (R-S2b-2), so the legacy offer to rerun in a TTY
|
|
4412
|
-
* describes something this CLI cannot do; `auth login` is the whole remedy. */
|
|
4413
|
-
function portFixText$3(fix) {
|
|
4414
|
-
return fix.replace(TRACE_FLAG$3, "--log-level verbose").replace(STALE_INTERACTIVE_SIGN_IN$3, ".");
|
|
4415
|
-
}
|
|
4416
|
-
function nextActionsFor$3(error) {
|
|
4417
|
-
return [...error.fix ? [{
|
|
4418
|
-
kind: "user-choice",
|
|
4419
|
-
label: portFixText$3(error.fix)
|
|
4420
|
-
}] : [], ...error.nextSteps.map((step) => {
|
|
4421
|
-
const command = portCommandString(step);
|
|
4422
|
-
return {
|
|
4423
|
-
kind: "run-command",
|
|
4424
|
-
label: command,
|
|
4425
|
-
command
|
|
4426
|
-
};
|
|
4427
|
-
})];
|
|
4428
|
-
}
|
|
4429
|
-
function mapBranchOperationError(error) {
|
|
4430
|
-
if (!(error instanceof CliError)) return null;
|
|
4431
|
-
if (PROJECT_CODES$3.has(error.code)) return mapProjectOperationError(error);
|
|
4432
|
-
return new CliStructuredError(BRANCH_CODE_MAP[error.code] ?? `BRANCH.${error.code}`, error.summary, {
|
|
4433
|
-
why: error.why ?? void 0,
|
|
4434
|
-
meta: Object.keys(error.meta).length > 0 ? error.meta : void 0,
|
|
4435
|
-
nextActions: nextActionsFor$3(error)
|
|
4436
|
-
});
|
|
4437
|
-
}
|
|
4438
|
-
//#endregion
|
|
4439
4248
|
//#region src/commands/branch/list.ts
|
|
4440
4249
|
/** The `branch list` command. */
|
|
4441
4250
|
const TITLE$12 = "Listing branches for the resolved project.";
|
|
@@ -4489,20 +4298,14 @@ const branchListCommand = defineCommand({
|
|
|
4489
4298
|
}) } },
|
|
4490
4299
|
needs: { credentials: true },
|
|
4491
4300
|
handler: async (args, ctx) => {
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
return ok(ctx.present({ data: result }, listPresentations$8(result)));
|
|
4501
|
-
} catch (error) {
|
|
4502
|
-
const mapped = mapBranchOperationError(error);
|
|
4503
|
-
if (mapped) return notOk(mapped);
|
|
4504
|
-
throw error;
|
|
4505
|
-
}
|
|
4301
|
+
const target = await resolvePinnedProject(ctx, await resolveActiveWorkspace(ctx), args.flags.project, "branch list");
|
|
4302
|
+
const branches = await listBranches$1(ctx.api, target.project.id, ctx.signal);
|
|
4303
|
+
const result = {
|
|
4304
|
+
projectId: target.project.id,
|
|
4305
|
+
projectName: target.project.name,
|
|
4306
|
+
branches: sortBranches(branches.map(toBranchSummary))
|
|
4307
|
+
};
|
|
4308
|
+
return ok(ctx.present({ data: result }, listPresentations$8(result)));
|
|
4506
4309
|
}
|
|
4507
4310
|
});
|
|
4508
4311
|
//#endregion
|
|
@@ -4583,15 +4386,7 @@ function createManagementBucketProvider(client) {
|
|
|
4583
4386
|
const accessKeyId = raw.accessKeyId;
|
|
4584
4387
|
const endpoint = raw.endpoint;
|
|
4585
4388
|
const bucketName = raw.bucketName;
|
|
4586
|
-
if (!secretAccessKey || !accessKeyId || !endpoint || !bucketName) throw
|
|
4587
|
-
code: "BUCKET_KEY_SECRET_MISSING",
|
|
4588
|
-
domain: "bucket",
|
|
4589
|
-
summary: "Created bucket key did not return credentials",
|
|
4590
|
-
why: "Bucket key credentials are one-time-view secrets, but the Management API did not include them in this create response.",
|
|
4591
|
-
fix: "Create another bucket key and store the returned credentials immediately.",
|
|
4592
|
-
exitCode: 1,
|
|
4593
|
-
nextSteps: [`prisma bucket key create ${options.bucketId}`]
|
|
4594
|
-
});
|
|
4389
|
+
if (!secretAccessKey || !accessKeyId || !endpoint || !bucketName) throw bucketKeySecretMissingError(options.bucketId);
|
|
4595
4390
|
return {
|
|
4596
4391
|
key: normalizeKey(raw),
|
|
4597
4392
|
secretAccessKey,
|
|
@@ -4630,21 +4425,66 @@ function normalizeKey(raw) {
|
|
|
4630
4425
|
createdAt: raw.createdAt
|
|
4631
4426
|
};
|
|
4632
4427
|
}
|
|
4428
|
+
const VERBOSE_LOG_FIX$1 = "Re-run with --log-level verbose for the underlying API response details.";
|
|
4429
|
+
function userChoice$3(label) {
|
|
4430
|
+
return {
|
|
4431
|
+
kind: "user-choice",
|
|
4432
|
+
label
|
|
4433
|
+
};
|
|
4434
|
+
}
|
|
4435
|
+
function runCommand$3(command) {
|
|
4436
|
+
return {
|
|
4437
|
+
kind: "run-command",
|
|
4438
|
+
label: command,
|
|
4439
|
+
command
|
|
4440
|
+
};
|
|
4441
|
+
}
|
|
4442
|
+
function bucketKeySecretMissingError(bucketId) {
|
|
4443
|
+
return new CliStructuredError("BUCKET.KEY_SECRET_MISSING", "Created bucket key did not return credentials", {
|
|
4444
|
+
why: "Bucket key credentials are one-time-view secrets, but the Management API did not include them in this create response.",
|
|
4445
|
+
nextActions: [userChoice$3("Create another bucket key and store the returned credentials immediately."), runCommand$3(`${CLI_NAME} bucket key create ${bucketId}`)]
|
|
4446
|
+
});
|
|
4447
|
+
}
|
|
4448
|
+
/** A 401 or 403 is the API refusing the caller, not a bucket problem. */
|
|
4449
|
+
function isRejectedCaller$1(status) {
|
|
4450
|
+
return status === 401 || status === 403;
|
|
4451
|
+
}
|
|
4452
|
+
function apiErrorWhy$1(status, message) {
|
|
4453
|
+
if (!isRejectedCaller$1(status)) return message ?? `The Management API returned status ${status || "unknown"}.`;
|
|
4454
|
+
const rejection = `The Management API rejected the request as ${status === 401 ? "unauthorized" : "forbidden"}.`;
|
|
4455
|
+
return message ? `${rejection} ${message}` : rejection;
|
|
4456
|
+
}
|
|
4457
|
+
function apiErrorMeta$1(status, apiCode) {
|
|
4458
|
+
if (!status && apiCode === void 0) return;
|
|
4459
|
+
return {
|
|
4460
|
+
...status ? { status } : {},
|
|
4461
|
+
...apiCode === void 0 ? {} : { apiCode }
|
|
4462
|
+
};
|
|
4463
|
+
}
|
|
4464
|
+
function apiErrorActions$1(status, hint) {
|
|
4465
|
+
if (!isRejectedCaller$1(status)) return [userChoice$3(hint ?? VERBOSE_LOG_FIX$1)];
|
|
4466
|
+
return [userChoice$3(hint ?? `Sign in again with prisma auth login, then retry the command.`), runCommand$3(`${CLI_NAME} auth login`)];
|
|
4467
|
+
}
|
|
4468
|
+
/**
|
|
4469
|
+
* Every bucket Management API failure lands on the one registered code.
|
|
4470
|
+
* The response's own error code is data, not an identity: it travels in
|
|
4471
|
+
* `meta.apiCode` beside `meta.status` so a consumer can still branch on
|
|
4472
|
+
* it without the CLI minting a code it never registered.
|
|
4473
|
+
*/
|
|
4633
4474
|
function bucketApiError(summary, response, error) {
|
|
4634
4475
|
const status = response?.status ?? 0;
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
fix: error?.error?.hint ?? "Re-run with --trace for the underlying API response details.",
|
|
4641
|
-
exitCode: 1,
|
|
4642
|
-
nextSteps: []
|
|
4476
|
+
const meta = apiErrorMeta$1(status, error?.error?.code);
|
|
4477
|
+
return new CliStructuredError("BUCKET.API_ERROR", summary, {
|
|
4478
|
+
why: apiErrorWhy$1(status, error?.error?.message),
|
|
4479
|
+
...meta === void 0 ? {} : { meta },
|
|
4480
|
+
nextActions: apiErrorActions$1(status, error?.error?.hint)
|
|
4643
4481
|
});
|
|
4644
4482
|
}
|
|
4645
4483
|
//#endregion
|
|
4646
4484
|
//#region src/commands/bucket/context.ts
|
|
4647
4485
|
/** Workspace, project and provider for the `bucket *` commands. */
|
|
4486
|
+
/** Where a caller who is missing a bucket id finds one. */
|
|
4487
|
+
const LIST_BUCKETS_COMMAND = `${CLI_NAME} bucket list`;
|
|
4648
4488
|
const projectFlag$3 = flag.string({
|
|
4649
4489
|
brief: "Project id or name",
|
|
4650
4490
|
placeholder: "id-or-name"
|
|
@@ -4675,52 +4515,6 @@ function resolveBucketProviderOnly(ctx) {
|
|
|
4675
4515
|
return createManagementBucketProvider(ctx.api);
|
|
4676
4516
|
}
|
|
4677
4517
|
//#endregion
|
|
4678
|
-
//#region src/commands/bucket/errors.ts
|
|
4679
|
-
const BUCKET_CODE_MAP = {
|
|
4680
|
-
USAGE_ERROR: "BUCKET.USAGE_ERROR",
|
|
4681
|
-
BUCKET_KEY_SECRET_MISSING: "BUCKET.KEY_SECRET_MISSING",
|
|
4682
|
-
BUCKET_API_ERROR: "BUCKET.API_ERROR"
|
|
4683
|
-
};
|
|
4684
|
-
/** The project-resolution codes `bucket list` and `bucket create` can
|
|
4685
|
-
* raise; they keep the project group's dotted codes and copy. */
|
|
4686
|
-
const PROJECT_CODES$2 = new Set([
|
|
4687
|
-
"PROJECT_NOT_FOUND",
|
|
4688
|
-
"PROJECT_AMBIGUOUS",
|
|
4689
|
-
"PROJECT_SETUP_REQUIRED",
|
|
4690
|
-
"LOCAL_STATE_STALE",
|
|
4691
|
-
"LOCAL_PROJECT_WORKSPACE_MISMATCH"
|
|
4692
|
-
]);
|
|
4693
|
-
const STALE_INTERACTIVE_SIGN_IN$2 = /, or rerun the command in a TTY to sign in interactively\./g;
|
|
4694
|
-
const TRACE_FLAG$2 = /--trace/g;
|
|
4695
|
-
/** `--trace` is gone in this CLI; the log level replaces it. Interactive
|
|
4696
|
-
* sign-in is gone too (R-S2b-2), so the legacy offer to rerun in a TTY
|
|
4697
|
-
* describes something this CLI cannot do; `auth login` is the whole remedy. */
|
|
4698
|
-
function portFixText$2(fix) {
|
|
4699
|
-
return fix.replace(TRACE_FLAG$2, "--log-level verbose").replace(STALE_INTERACTIVE_SIGN_IN$2, ".");
|
|
4700
|
-
}
|
|
4701
|
-
function nextActionsFor$2(error) {
|
|
4702
|
-
return [...error.fix ? [{
|
|
4703
|
-
kind: "user-choice",
|
|
4704
|
-
label: portFixText$2(error.fix)
|
|
4705
|
-
}] : [], ...error.nextSteps.map((step) => {
|
|
4706
|
-
const command = portCommandString(step);
|
|
4707
|
-
return {
|
|
4708
|
-
kind: "run-command",
|
|
4709
|
-
label: command,
|
|
4710
|
-
command
|
|
4711
|
-
};
|
|
4712
|
-
})];
|
|
4713
|
-
}
|
|
4714
|
-
function mapBucketOperationError(error) {
|
|
4715
|
-
if (!(error instanceof CliError)) return null;
|
|
4716
|
-
if (PROJECT_CODES$2.has(error.code)) return mapProjectOperationError(error);
|
|
4717
|
-
return new CliStructuredError(BUCKET_CODE_MAP[error.code] ?? `BUCKET.${error.code}`, error.summary, {
|
|
4718
|
-
why: error.why ?? void 0,
|
|
4719
|
-
meta: Object.keys(error.meta).length > 0 ? error.meta : void 0,
|
|
4720
|
-
nextActions: nextActionsFor$2(error)
|
|
4721
|
-
});
|
|
4722
|
-
}
|
|
4723
|
-
//#endregion
|
|
4724
4518
|
//#region src/commands/bucket/presentation.ts
|
|
4725
4519
|
/** Legacy `formatBucketTarget`. */
|
|
4726
4520
|
function bucketTargetLabel(projectName, branchId) {
|
|
@@ -4777,34 +4571,28 @@ const bucketCreateCommand = defineCommand({
|
|
|
4777
4571
|
},
|
|
4778
4572
|
needs: { credentials: true },
|
|
4779
4573
|
handler: async (args, ctx) => {
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
}));
|
|
4803
|
-
} catch (error) {
|
|
4804
|
-
const mapped = mapBucketOperationError(error);
|
|
4805
|
-
if (mapped) return notOk(mapped);
|
|
4806
|
-
throw error;
|
|
4807
|
-
}
|
|
4574
|
+
const { provider, projectId, projectName } = await resolveBucketContext(ctx, args.flags, "bucket create");
|
|
4575
|
+
const bucket = await provider.createBucket({
|
|
4576
|
+
projectId,
|
|
4577
|
+
name: args.flags.name?.trim() || void 0,
|
|
4578
|
+
branchGitName: args.flags.branch,
|
|
4579
|
+
signal: ctx.signal
|
|
4580
|
+
});
|
|
4581
|
+
const result = {
|
|
4582
|
+
projectId,
|
|
4583
|
+
projectName,
|
|
4584
|
+
bucket
|
|
4585
|
+
};
|
|
4586
|
+
return ok(ctx.present({ data: result }, {
|
|
4587
|
+
human: () => [{
|
|
4588
|
+
kind: "summary",
|
|
4589
|
+
status: "ok",
|
|
4590
|
+
text: `Created bucket "${bucket.name}" in ${bucketTargetLabel(projectName, bucket.branchId)}.`
|
|
4591
|
+
}],
|
|
4592
|
+
stdout: () => [],
|
|
4593
|
+
json: () => result,
|
|
4594
|
+
next: () => []
|
|
4595
|
+
}));
|
|
4808
4596
|
}
|
|
4809
4597
|
});
|
|
4810
4598
|
//#endregion
|
|
@@ -4844,18 +4632,22 @@ const bucketDeleteCommand = defineCommand({
|
|
|
4844
4632
|
},
|
|
4845
4633
|
needs: { credentials: true },
|
|
4846
4634
|
handler: async (args, ctx) => {
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
}
|
|
4635
|
+
const bucketId = args.positionals.bucketId.trim();
|
|
4636
|
+
if (!bucketId) throw new CliStructuredError("BUCKET.USAGE_ERROR", "Bucket id required", {
|
|
4637
|
+
why: "Bucket deletion needs a bucket id.",
|
|
4638
|
+
nextActions: [{
|
|
4639
|
+
kind: "user-choice",
|
|
4640
|
+
label: "Pass the bucket id to delete."
|
|
4641
|
+
}, {
|
|
4642
|
+
kind: "run-command",
|
|
4643
|
+
label: LIST_BUCKETS_COMMAND,
|
|
4644
|
+
command: LIST_BUCKETS_COMMAND
|
|
4645
|
+
}]
|
|
4646
|
+
});
|
|
4647
|
+
await ctx.prompt.consent(CONSENT_QUESTION$6, { token: bucketId });
|
|
4648
|
+
await resolveBucketProviderOnly(ctx).deleteBucket(bucketId, { signal: ctx.signal });
|
|
4649
|
+
const result = { bucket: { id: bucketId } };
|
|
4650
|
+
return ok(ctx.present({ data: result }, deletePresentations$5(result)));
|
|
4859
4651
|
}
|
|
4860
4652
|
});
|
|
4861
4653
|
//#endregion
|
|
@@ -4936,24 +4728,28 @@ const bucketKeyCreateCommand = defineCommand({
|
|
|
4936
4728
|
},
|
|
4937
4729
|
needs: { credentials: true },
|
|
4938
4730
|
handler: async (args, ctx) => {
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4731
|
+
const bucketId = args.positionals.bucketId.trim();
|
|
4732
|
+
if (!bucketId) throw new CliStructuredError("BUCKET.USAGE_ERROR", "Bucket id required", {
|
|
4733
|
+
why: "Bucket key creation needs a bucket id.",
|
|
4734
|
+
nextActions: [{
|
|
4735
|
+
kind: "user-choice",
|
|
4736
|
+
label: "Pass the bucket id."
|
|
4737
|
+
}, {
|
|
4738
|
+
kind: "run-command",
|
|
4739
|
+
label: LIST_BUCKETS_COMMAND,
|
|
4740
|
+
command: LIST_BUCKETS_COMMAND
|
|
4741
|
+
}]
|
|
4742
|
+
});
|
|
4743
|
+
const result = {
|
|
4744
|
+
bucketId,
|
|
4745
|
+
...await resolveBucketProviderOnly(ctx).createKey({
|
|
4943
4746
|
bucketId,
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
};
|
|
4951
|
-
return ok(ctx.present({ data: result }, createPresentations$1(result)));
|
|
4952
|
-
} catch (error) {
|
|
4953
|
-
const mapped = mapBucketOperationError(error);
|
|
4954
|
-
if (mapped) return notOk(mapped);
|
|
4955
|
-
throw error;
|
|
4956
|
-
}
|
|
4747
|
+
name: args.flags.name?.trim() || void 0,
|
|
4748
|
+
role: resolveKeyRole(args.flags.role),
|
|
4749
|
+
signal: ctx.signal
|
|
4750
|
+
})
|
|
4751
|
+
};
|
|
4752
|
+
return ok(ctx.present({ data: result }, createPresentations$1(result)));
|
|
4957
4753
|
}
|
|
4958
4754
|
});
|
|
4959
4755
|
//#endregion
|
|
@@ -4998,18 +4794,25 @@ const bucketKeyDeleteCommand = defineCommand({
|
|
|
4998
4794
|
},
|
|
4999
4795
|
needs: { credentials: true },
|
|
5000
4796
|
handler: async (args, ctx) => {
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
4797
|
+
const bucketId = args.positionals.bucketId.trim();
|
|
4798
|
+
const keyId = args.positionals.keyId.trim();
|
|
4799
|
+
if (!bucketId || !keyId) {
|
|
4800
|
+
const listKeysCommand = `${CLI_NAME} bucket key list <bucketId>`;
|
|
4801
|
+
throw new CliStructuredError("BUCKET.USAGE_ERROR", "Bucket id and key id required", {
|
|
4802
|
+
why: "Bucket key deletion needs both a bucket id and a key id.",
|
|
4803
|
+
nextActions: [{
|
|
4804
|
+
kind: "user-choice",
|
|
4805
|
+
label: "Pass the bucket id and key id."
|
|
4806
|
+
}, {
|
|
4807
|
+
kind: "run-command",
|
|
4808
|
+
label: listKeysCommand,
|
|
4809
|
+
command: listKeysCommand
|
|
4810
|
+
}]
|
|
4811
|
+
});
|
|
5012
4812
|
}
|
|
4813
|
+
await resolveBucketProviderOnly(ctx).deleteKey(bucketId, keyId, { signal: ctx.signal });
|
|
4814
|
+
const result = { key: { id: keyId } };
|
|
4815
|
+
return ok(ctx.present({ data: result }, deletePresentations$4(result)));
|
|
5013
4816
|
}
|
|
5014
4817
|
});
|
|
5015
4818
|
//#endregion
|
|
@@ -5108,19 +4911,23 @@ const bucketKeyListCommand = defineCommand({
|
|
|
5108
4911
|
},
|
|
5109
4912
|
needs: { credentials: true },
|
|
5110
4913
|
handler: async (args, ctx) => {
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
}
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
4914
|
+
const bucketId = args.positionals.bucketId.trim();
|
|
4915
|
+
if (!bucketId) throw new CliStructuredError("BUCKET.USAGE_ERROR", "Bucket id required", {
|
|
4916
|
+
why: "Bucket key listing needs a bucket id.",
|
|
4917
|
+
nextActions: [{
|
|
4918
|
+
kind: "user-choice",
|
|
4919
|
+
label: "Pass the bucket id."
|
|
4920
|
+
}, {
|
|
4921
|
+
kind: "run-command",
|
|
4922
|
+
label: LIST_BUCKETS_COMMAND,
|
|
4923
|
+
command: LIST_BUCKETS_COMMAND
|
|
4924
|
+
}]
|
|
4925
|
+
});
|
|
4926
|
+
const result = {
|
|
4927
|
+
bucketId,
|
|
4928
|
+
keys: await resolveBucketProviderOnly(ctx).listKeys(bucketId, { signal: ctx.signal })
|
|
4929
|
+
};
|
|
4930
|
+
return ok(ctx.present({ data: result }, listPresentations$7(result)));
|
|
5124
4931
|
}
|
|
5125
4932
|
});
|
|
5126
4933
|
//#endregion
|
|
@@ -5183,25 +4990,19 @@ const bucketListCommand = defineCommand({
|
|
|
5183
4990
|
},
|
|
5184
4991
|
needs: { credentials: true },
|
|
5185
4992
|
handler: async (args, ctx) => {
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
return ok(ctx.present({ data: result }, listPresentations$6(result)));
|
|
5200
|
-
} catch (error) {
|
|
5201
|
-
const mapped = mapBucketOperationError(error);
|
|
5202
|
-
if (mapped) return notOk(mapped);
|
|
5203
|
-
throw error;
|
|
5204
|
-
}
|
|
4993
|
+
const { provider, projectId, projectName } = await resolveBucketContext(ctx, args.flags, "bucket list");
|
|
4994
|
+
const buckets = await provider.listBuckets({
|
|
4995
|
+
projectId,
|
|
4996
|
+
branchName: args.flags.branch,
|
|
4997
|
+
signal: ctx.signal
|
|
4998
|
+
});
|
|
4999
|
+
const result = {
|
|
5000
|
+
projectId,
|
|
5001
|
+
projectName,
|
|
5002
|
+
branchName: args.flags.branch ?? null,
|
|
5003
|
+
buckets
|
|
5004
|
+
};
|
|
5005
|
+
return ok(ctx.present({ data: result }, listPresentations$6(result)));
|
|
5205
5006
|
}
|
|
5206
5007
|
});
|
|
5207
5008
|
//#endregion
|
|
@@ -5495,84 +5296,13 @@ async function resolveGitContext(ctx, explicitProject, commandName) {
|
|
|
5495
5296
|
}
|
|
5496
5297
|
//#endregion
|
|
5497
5298
|
//#region src/commands/git/errors.ts
|
|
5498
|
-
const GIT_CODE_MAP = {
|
|
5499
|
-
USAGE_ERROR: "GIT.USAGE_ERROR",
|
|
5500
|
-
REPO_PROVIDER_UNSUPPORTED: "GIT.REPO_PROVIDER_UNSUPPORTED",
|
|
5501
|
-
REPO_ALREADY_CONNECTED: "GIT.REPO_ALREADY_CONNECTED",
|
|
5502
|
-
REPO_INSTALLATION_REQUIRED: "GIT.REPO_INSTALLATION_REQUIRED",
|
|
5503
|
-
REPO_NOT_ACCESSIBLE: "GIT.REPO_NOT_ACCESSIBLE",
|
|
5504
|
-
REPO_NOT_CONNECTED: "GIT.REPO_NOT_CONNECTED",
|
|
5505
|
-
REPO_CONNECTION_FAILED: "GIT.REPO_CONNECTION_FAILED"
|
|
5506
|
-
};
|
|
5507
|
-
/** The project-resolution codes the git commands can raise; they keep
|
|
5508
|
-
* the project group's dotted codes and copy. */
|
|
5509
|
-
const PROJECT_CODES$1 = new Set([
|
|
5510
|
-
"PROJECT_NOT_FOUND",
|
|
5511
|
-
"PROJECT_AMBIGUOUS",
|
|
5512
|
-
"PROJECT_SETUP_REQUIRED",
|
|
5513
|
-
"LOCAL_STATE_STALE",
|
|
5514
|
-
"LOCAL_PROJECT_WORKSPACE_MISMATCH"
|
|
5515
|
-
]);
|
|
5516
|
-
const STALE_INTERACTIVE_SIGN_IN$1 = /, or rerun the command in a TTY to sign in interactively\./g;
|
|
5517
|
-
const TRACE_FLAG$1 = /--trace/g;
|
|
5518
|
-
/** `--trace` is gone in this CLI; the log level replaces it. Interactive
|
|
5519
|
-
* sign-in is gone too (R-S2b-2), so the legacy offer to rerun in a TTY
|
|
5520
|
-
* describes something this CLI cannot do; `auth login` is the whole remedy. */
|
|
5521
|
-
function portFixText$1(fix) {
|
|
5522
|
-
return fix.replace(TRACE_FLAG$1, "--log-level verbose").replace(STALE_INTERACTIVE_SIGN_IN$1, ".");
|
|
5523
|
-
}
|
|
5524
|
-
/** The install-required and not-accessible errors put the raw install
|
|
5525
|
-
* URL in their nextSteps beside real commands. A URL is not a command,
|
|
5526
|
-
* so it becomes an `open-url` action. */
|
|
5527
|
-
function nextStepAction(step) {
|
|
5528
|
-
if (step.startsWith("https://") || step.startsWith("http://")) return {
|
|
5529
|
-
kind: "open-url",
|
|
5530
|
-
label: step,
|
|
5531
|
-
url: step
|
|
5532
|
-
};
|
|
5533
|
-
const command = portCommandString(step);
|
|
5534
|
-
return {
|
|
5535
|
-
kind: "run-command",
|
|
5536
|
-
label: command,
|
|
5537
|
-
command
|
|
5538
|
-
};
|
|
5539
|
-
}
|
|
5540
|
-
function nextActionsFor$1(error) {
|
|
5541
|
-
return [...error.fix ? [{
|
|
5542
|
-
kind: "user-choice",
|
|
5543
|
-
label: portFixText$1(error.fix)
|
|
5544
|
-
}] : [], ...error.nextSteps.map(nextStepAction)];
|
|
5545
|
-
}
|
|
5546
|
-
/** The legacy errors branch their fix text on whether a browser was
|
|
5547
|
-
* opened. The engine's browser wait always shows the URL, so the
|
|
5548
|
-
* opened branch is the one that describes what this CLI does. */
|
|
5549
|
-
const BROWSER_OPENED = true;
|
|
5550
5299
|
/**
|
|
5551
|
-
* The install wait's two terminal outcomes
|
|
5552
|
-
*
|
|
5553
|
-
*
|
|
5554
|
-
* the structured error is assembled from their fields with the meta
|
|
5555
|
-
* d3 §3.8 pins.
|
|
5300
|
+
* The install wait's two terminal outcomes: the workspace has an
|
|
5301
|
+
* inspectable installation that simply does not expose the repository,
|
|
5302
|
+
* or it has none at all.
|
|
5556
5303
|
*/
|
|
5557
5304
|
function installWaitFailedError(repository, installUrl, inspectableInstallationCount) {
|
|
5558
|
-
|
|
5559
|
-
return new CliStructuredError(GIT_CODE_MAP[legacy.code], legacy.summary, {
|
|
5560
|
-
why: legacy.why ?? void 0,
|
|
5561
|
-
meta: {
|
|
5562
|
-
repository: repository.fullName,
|
|
5563
|
-
installUrl
|
|
5564
|
-
},
|
|
5565
|
-
nextActions: nextActionsFor$1(legacy)
|
|
5566
|
-
});
|
|
5567
|
-
}
|
|
5568
|
-
function mapGitOperationError(error) {
|
|
5569
|
-
if (!(error instanceof CliError)) return null;
|
|
5570
|
-
if (PROJECT_CODES$1.has(error.code)) return mapProjectOperationError(error);
|
|
5571
|
-
return new CliStructuredError(GIT_CODE_MAP[error.code] ?? `GIT.${error.code}`, error.summary, {
|
|
5572
|
-
why: error.why ?? void 0,
|
|
5573
|
-
meta: Object.keys(error.meta).length > 0 ? error.meta : void 0,
|
|
5574
|
-
nextActions: nextActionsFor$1(error)
|
|
5575
|
-
});
|
|
5305
|
+
return inspectableInstallationCount > 0 ? repoNotAccessibleError(repository, installUrl) : repoInstallationRequiredError(repository, installUrl);
|
|
5576
5306
|
}
|
|
5577
5307
|
//#endregion
|
|
5578
5308
|
//#region src/commands/git/connect.ts
|
|
@@ -5671,43 +5401,50 @@ const gitConnectCommand = defineCommand({
|
|
|
5671
5401
|
},
|
|
5672
5402
|
needs: { credentials: true },
|
|
5673
5403
|
handler: async (args, ctx) => {
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
}
|
|
5688
|
-
return ok(ctx.present({ data: idempotent }, connectPresentations(idempotent)));
|
|
5689
|
-
}
|
|
5690
|
-
const installed = await resolveInstalledRepository(ctx, api, target.workspace.id, repository);
|
|
5691
|
-
const { data, error, response } = await api.POST("/v1/source-repositories", {
|
|
5692
|
-
body: {
|
|
5693
|
-
projectId: target.project.id,
|
|
5694
|
-
provider: "github",
|
|
5695
|
-
providerRepositoryId: installed.repository.id,
|
|
5696
|
-
installationId: installed.installation.id
|
|
5697
|
-
},
|
|
5698
|
-
signal: ctx.signal
|
|
5404
|
+
const { api, target } = await resolveGitContext(ctx, args.flags.project, "git connect");
|
|
5405
|
+
const remoteUrl = args.positionals.gitUrl ?? await readGitOriginRemote(ctx.cwd, ctx.signal);
|
|
5406
|
+
if (!remoteUrl) {
|
|
5407
|
+
const example = `${CLI_NAME} git connect git@github.com:prisma/prisma-cli.git`;
|
|
5408
|
+
throw new CliStructuredError("GIT.USAGE_ERROR", "Repository connection requires a GitHub repository URL", {
|
|
5409
|
+
why: "No git-url was provided and the local repo does not have an origin remote.",
|
|
5410
|
+
nextActions: [{
|
|
5411
|
+
kind: "user-choice",
|
|
5412
|
+
label: `Pass a GitHub repository URL, or add a GitHub origin remote and rerun ${CLI_NAME} git connect.`
|
|
5413
|
+
}, {
|
|
5414
|
+
kind: "run-command",
|
|
5415
|
+
label: example,
|
|
5416
|
+
command: example
|
|
5417
|
+
}]
|
|
5699
5418
|
});
|
|
5700
|
-
|
|
5701
|
-
|
|
5419
|
+
}
|
|
5420
|
+
const repository = parseGitHubRepositoryUrl(remoteUrl);
|
|
5421
|
+
if (!repository) throw unsupportedRepositoryProviderError();
|
|
5422
|
+
const existing = await readFirstSourceRepository(api, target.project.id, ctx.signal);
|
|
5423
|
+
if (existing) {
|
|
5424
|
+
const existingConnection = toRepositoryConnection(existing);
|
|
5425
|
+
if (!repositoryFullNamesMatch(existingConnection.repository.fullName, repository.fullName)) throw repoAlreadyConnectedError(existingConnection.repository.fullName);
|
|
5426
|
+
const idempotent = {
|
|
5702
5427
|
...target,
|
|
5703
|
-
repositoryConnection:
|
|
5428
|
+
repositoryConnection: existingConnection
|
|
5704
5429
|
};
|
|
5705
|
-
return ok(ctx.present({ data:
|
|
5706
|
-
} catch (error) {
|
|
5707
|
-
const mapped = mapGitOperationError(error);
|
|
5708
|
-
if (mapped) return notOk(mapped);
|
|
5709
|
-
throw error;
|
|
5430
|
+
return ok(ctx.present({ data: idempotent }, connectPresentations(idempotent)));
|
|
5710
5431
|
}
|
|
5432
|
+
const installed = await resolveInstalledRepository(ctx, api, target.workspace.id, repository);
|
|
5433
|
+
const { data, error, response } = await api.POST("/v1/source-repositories", {
|
|
5434
|
+
body: {
|
|
5435
|
+
projectId: target.project.id,
|
|
5436
|
+
provider: "github",
|
|
5437
|
+
providerRepositoryId: installed.repository.id,
|
|
5438
|
+
installationId: installed.installation.id
|
|
5439
|
+
},
|
|
5440
|
+
signal: ctx.signal
|
|
5441
|
+
});
|
|
5442
|
+
if (error || !data) throw repoConnectionApiError("Failed to connect GitHub repository", response, error);
|
|
5443
|
+
const result = {
|
|
5444
|
+
...target,
|
|
5445
|
+
repositoryConnection: toRepositoryConnection(data.data)
|
|
5446
|
+
};
|
|
5447
|
+
return ok(ctx.present({ data: result }, connectPresentations(result)));
|
|
5711
5448
|
}
|
|
5712
5449
|
});
|
|
5713
5450
|
//#endregion
|
|
@@ -5756,25 +5493,19 @@ const gitDisconnectCommand = defineCommand({
|
|
|
5756
5493
|
},
|
|
5757
5494
|
needs: { credentials: true },
|
|
5758
5495
|
handler: async (args, ctx) => {
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
return ok(ctx.present({ data: result }, disconnectPresentations(result)));
|
|
5773
|
-
} catch (error) {
|
|
5774
|
-
const mapped = mapGitOperationError(error);
|
|
5775
|
-
if (mapped) return notOk(mapped);
|
|
5776
|
-
throw error;
|
|
5777
|
-
}
|
|
5496
|
+
const { api, target } = await resolveGitContext(ctx, args.flags.project, "git disconnect");
|
|
5497
|
+
const existing = await readFirstSourceRepository(api, target.project.id, ctx.signal);
|
|
5498
|
+
if (!existing) throw repoNotConnectedError();
|
|
5499
|
+
const { error, response } = await api.DELETE("/v1/source-repositories/{id}", {
|
|
5500
|
+
params: { path: { id: existing.id } },
|
|
5501
|
+
signal: ctx.signal
|
|
5502
|
+
});
|
|
5503
|
+
if (error) throw repoConnectionApiError("Failed to disconnect GitHub repository", response, error);
|
|
5504
|
+
const result = {
|
|
5505
|
+
...target,
|
|
5506
|
+
repositoryConnection: toRepositoryConnection(existing)
|
|
5507
|
+
};
|
|
5508
|
+
return ok(ctx.present({ data: result }, disconnectPresentations(result)));
|
|
5778
5509
|
}
|
|
5779
5510
|
});
|
|
5780
5511
|
//#endregion
|
|
@@ -6581,41 +6312,51 @@ const initCommand = defineCommand({
|
|
|
6581
6312
|
//#region src/controllers/database.ts
|
|
6582
6313
|
const USAGE_DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
|
6583
6314
|
const USAGE_DATETIME_PATTERN = /^\d{4}-\d{2}-\d{2}T/;
|
|
6584
|
-
|
|
6315
|
+
const LIST_DATABASES_COMMAND = `${CLI_NAME} postgres list`;
|
|
6316
|
+
/** The corrected `postgres usage` form both period errors point at. */
|
|
6317
|
+
const USAGE_PERIOD_EXAMPLE_COMMAND = `${CLI_NAME} postgres usage <database> --from 2026-06-01 --to 2026-06-30`;
|
|
6318
|
+
function userChoice$2(label) {
|
|
6319
|
+
return {
|
|
6320
|
+
kind: "user-choice",
|
|
6321
|
+
label
|
|
6322
|
+
};
|
|
6323
|
+
}
|
|
6324
|
+
function runCommand$2(command) {
|
|
6325
|
+
return {
|
|
6326
|
+
kind: "run-command",
|
|
6327
|
+
label: command,
|
|
6328
|
+
command
|
|
6329
|
+
};
|
|
6330
|
+
}
|
|
6331
|
+
function parseUsageDate(value, flagName, dayBoundary) {
|
|
6585
6332
|
if (value === void 0) return;
|
|
6586
6333
|
const trimmed = value.trim();
|
|
6587
6334
|
if (USAGE_DATE_ONLY_PATTERN.test(trimmed) && isValidCalendarDate(trimmed)) return dayBoundary === "start" ? `${trimmed}T00:00:00.000Z` : `${trimmed}T23:59:59.999Z`;
|
|
6588
6335
|
if (USAGE_DATETIME_PATTERN.test(trimmed) && !Number.isNaN(Date.parse(trimmed)) && isValidCalendarDate(trimmed.slice(0, 10))) return trimmed;
|
|
6589
|
-
throw
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
"--from",
|
|
6594
|
-
"2026-06-01",
|
|
6595
|
-
"--to",
|
|
6596
|
-
"2026-06-30"
|
|
6597
|
-
])], "database");
|
|
6336
|
+
throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Invalid usage period", {
|
|
6337
|
+
why: `${flagName} must be an ISO date such as 2026-06-01 or an ISO datetime such as 2026-06-01T12:00:00Z.`,
|
|
6338
|
+
nextActions: [userChoice$2(`Pass an ISO date or datetime to ${flagName}.`), runCommand$2(USAGE_PERIOD_EXAMPLE_COMMAND)]
|
|
6339
|
+
});
|
|
6598
6340
|
}
|
|
6599
6341
|
function isValidCalendarDate(datePart) {
|
|
6600
6342
|
const timestamp = Date.parse(`${datePart}T00:00:00.000Z`);
|
|
6601
6343
|
return !Number.isNaN(timestamp) && new Date(timestamp).toISOString().startsWith(datePart);
|
|
6602
6344
|
}
|
|
6603
|
-
function parseBackupLimit(value
|
|
6345
|
+
function parseBackupLimit(value) {
|
|
6604
6346
|
if (value === void 0) return;
|
|
6605
6347
|
const limit = Number(value.trim());
|
|
6606
|
-
if (!Number.isInteger(limit) || limit < 1 || limit > 100) throw
|
|
6607
|
-
"
|
|
6608
|
-
"
|
|
6609
|
-
|
|
6610
|
-
"<database>",
|
|
6611
|
-
"--limit",
|
|
6612
|
-
"50"
|
|
6613
|
-
])], "database");
|
|
6348
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Invalid backup limit", {
|
|
6349
|
+
why: "--limit must be an integer between 1 and 100.",
|
|
6350
|
+
nextActions: [userChoice$2("Pass a --limit between 1 and 100."), runCommand$2(`${CLI_NAME} postgres backup list <database> --limit 50`)]
|
|
6351
|
+
});
|
|
6614
6352
|
return limit;
|
|
6615
6353
|
}
|
|
6616
6354
|
async function resolveDatabase(provider, target, databaseRef, branchName, signal) {
|
|
6617
6355
|
const ref = databaseRef.trim();
|
|
6618
|
-
if (!ref) throw
|
|
6356
|
+
if (!ref) throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Database id or name required", {
|
|
6357
|
+
why: "This command needs a database id or name.",
|
|
6358
|
+
nextActions: [userChoice$2("Pass a database id or name."), runCommand$2(LIST_DATABASES_COMMAND)]
|
|
6359
|
+
});
|
|
6619
6360
|
const databases = await provider.listDatabases({
|
|
6620
6361
|
projectId: target.project.id,
|
|
6621
6362
|
branchName,
|
|
@@ -6634,14 +6375,9 @@ async function resolveDatabase(provider, target, databaseRef, branchName, signal
|
|
|
6634
6375
|
return ensureProjectId(shown, target.project.id);
|
|
6635
6376
|
}
|
|
6636
6377
|
function databaseRemovedDuringResolutionError(database, projectName) {
|
|
6637
|
-
return new
|
|
6638
|
-
code: "DATABASE_NOT_FOUND",
|
|
6639
|
-
domain: "database",
|
|
6640
|
-
summary: "Database not found",
|
|
6378
|
+
return new CliStructuredError("POSTGRES.NOT_FOUND", "Database not found", {
|
|
6641
6379
|
why: `"${database.name}" (${database.id}) was listed for project "${projectName}", but reading it returned 404. It was most likely removed while this command was running.`,
|
|
6642
|
-
|
|
6643
|
-
exitCode: 1,
|
|
6644
|
-
nextSteps: ["prisma database list"]
|
|
6380
|
+
nextActions: [userChoice$2("Re-run the command, or list the project's databases to see what is there now."), runCommand$2(LIST_DATABASES_COMMAND)]
|
|
6645
6381
|
});
|
|
6646
6382
|
}
|
|
6647
6383
|
function ensureProjectId(database, projectId) {
|
|
@@ -6662,30 +6398,20 @@ function defaultConnectionName() {
|
|
|
6662
6398
|
return `cli-${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "").slice(0, 17)}-${randomBytes(2).toString("hex")}`;
|
|
6663
6399
|
}
|
|
6664
6400
|
function databaseNotFoundError(databaseRef, projectName, branchName) {
|
|
6665
|
-
return new
|
|
6666
|
-
code: "DATABASE_NOT_FOUND",
|
|
6667
|
-
domain: "database",
|
|
6668
|
-
summary: "Database not found",
|
|
6401
|
+
return new CliStructuredError("POSTGRES.NOT_FOUND", "Database not found", {
|
|
6669
6402
|
why: `No database matched "${databaseRef}"${projectName ? ` in project "${projectName}"${branchName ? ` on branch "${branchName}"` : ""}` : ""}.`,
|
|
6670
|
-
|
|
6671
|
-
exitCode: 1,
|
|
6672
|
-
nextSteps: ["prisma database list"]
|
|
6403
|
+
nextActions: [userChoice$2(`Pass a database id or name from ${LIST_DATABASES_COMMAND}.`), runCommand$2(LIST_DATABASES_COMMAND)]
|
|
6673
6404
|
});
|
|
6674
6405
|
}
|
|
6675
6406
|
function databaseAmbiguousError(databaseRef, matches, branchName) {
|
|
6676
|
-
return new
|
|
6677
|
-
code: "DATABASE_AMBIGUOUS",
|
|
6678
|
-
domain: "database",
|
|
6679
|
-
summary: "Database resolution is ambiguous",
|
|
6407
|
+
return new CliStructuredError("POSTGRES.AMBIGUOUS", "Database resolution is ambiguous", {
|
|
6680
6408
|
why: branchName ? `Multiple databases matched "${databaseRef}" on branch "${branchName}".` : `Multiple databases matched "${databaseRef}".`,
|
|
6681
|
-
fix: "Pass the database id, or pass --branch <git-name> to narrow the match.",
|
|
6682
|
-
exitCode: 1,
|
|
6683
|
-
nextSteps: ["prisma database list"],
|
|
6684
6409
|
meta: { matches: matches.map((database) => ({
|
|
6685
6410
|
id: database.id,
|
|
6686
6411
|
name: database.name,
|
|
6687
6412
|
branchName: database.branchName
|
|
6688
|
-
})) }
|
|
6413
|
+
})) },
|
|
6414
|
+
nextActions: [userChoice$2("Pass the database id, or pass --branch <git-name> to narrow the match."), runCommand$2(LIST_DATABASES_COMMAND)]
|
|
6689
6415
|
});
|
|
6690
6416
|
}
|
|
6691
6417
|
//#endregion
|
|
@@ -6752,8 +6478,21 @@ function serializeDatabaseBackupList(result) {
|
|
|
6752
6478
|
//#endregion
|
|
6753
6479
|
//#region src/lib/database/provider.ts
|
|
6754
6480
|
const SUBSCRIPTION_LOOKUP_TIMEOUT_MS = 3e3;
|
|
6481
|
+
const VERBOSE_LOG_FIX = "Re-run with --log-level verbose for the underlying API response details.";
|
|
6482
|
+
function userChoice$1(label) {
|
|
6483
|
+
return {
|
|
6484
|
+
kind: "user-choice",
|
|
6485
|
+
label
|
|
6486
|
+
};
|
|
6487
|
+
}
|
|
6488
|
+
function runCommand$1(command) {
|
|
6489
|
+
return {
|
|
6490
|
+
kind: "run-command",
|
|
6491
|
+
label: command,
|
|
6492
|
+
command
|
|
6493
|
+
};
|
|
6494
|
+
}
|
|
6755
6495
|
function createManagementDatabaseProvider(client, options) {
|
|
6756
|
-
const formatCommand = options?.formatCommand ?? ((args) => formatPrismaCliCommand(args));
|
|
6757
6496
|
const toDatabaseApiError = (summary, response, error, signal) => databaseApiError({
|
|
6758
6497
|
client,
|
|
6759
6498
|
workspaceId: options?.workspaceId,
|
|
@@ -6873,8 +6612,8 @@ function createManagementDatabaseProvider(client, options) {
|
|
|
6873
6612
|
} },
|
|
6874
6613
|
signal: options.signal
|
|
6875
6614
|
});
|
|
6876
|
-
if (result.response?.status === 409 && !isPlanLimitApiError(result.error)) throw restoreConflictError(options.targetDatabaseId, result.error
|
|
6877
|
-
if (result.response?.status === 404 && !isPlanLimitApiError(result.error)) throw restoreBackupNotFoundError(options, result.error
|
|
6615
|
+
if (result.response?.status === 409 && !isPlanLimitApiError(result.error)) throw restoreConflictError(options.targetDatabaseId, result.error);
|
|
6616
|
+
if (result.response?.status === 404 && !isPlanLimitApiError(result.error)) throw restoreBackupNotFoundError(options, result.error);
|
|
6878
6617
|
if (result.error || !result.data) throw await toDatabaseApiError("Failed to restore database", result.response, result.error, options.signal);
|
|
6879
6618
|
return normalizeDatabase(result.data.data, options.projectId);
|
|
6880
6619
|
},
|
|
@@ -6911,14 +6650,9 @@ function normalizeConnection(connection, fallbackDatabaseId) {
|
|
|
6911
6650
|
}
|
|
6912
6651
|
function normalizeCreatedDatabase(database, fallbackProjectId) {
|
|
6913
6652
|
const rawConnection = database.connections?.[0];
|
|
6914
|
-
if (!rawConnection) throw new
|
|
6915
|
-
code: "DATABASE_CONNECTION_MISSING",
|
|
6916
|
-
domain: "database",
|
|
6917
|
-
summary: "Created database did not return a connection string",
|
|
6653
|
+
if (!rawConnection) throw new CliStructuredError("POSTGRES.CONNECTION_MISSING", "Created database did not return a connection string", {
|
|
6918
6654
|
why: "The Management API created the database but did not include the one-time connection payload.",
|
|
6919
|
-
|
|
6920
|
-
exitCode: 1,
|
|
6921
|
-
nextSteps: [`prisma database connection create ${database.id}`]
|
|
6655
|
+
nextActions: [userChoice$1(`Create a connection explicitly with ${CLI_NAME} postgres connection create <database>.`), runCommand$1(`${CLI_NAME} postgres connection create ${database.id}`)]
|
|
6922
6656
|
});
|
|
6923
6657
|
return {
|
|
6924
6658
|
database: normalizeDatabase(database, fallbackProjectId),
|
|
@@ -6927,14 +6661,9 @@ function normalizeCreatedDatabase(database, fallbackProjectId) {
|
|
|
6927
6661
|
}
|
|
6928
6662
|
function normalizeCreatedConnection(connection, fallbackDatabaseId) {
|
|
6929
6663
|
const connectionString = extractConnectionString(connection);
|
|
6930
|
-
if (!connectionString) throw new
|
|
6931
|
-
code: "DATABASE_CONNECTION_STRING_MISSING",
|
|
6932
|
-
domain: "database",
|
|
6933
|
-
summary: "Created connection did not return a connection string",
|
|
6664
|
+
if (!connectionString) throw new CliStructuredError("POSTGRES.CONNECTION_STRING_MISSING", "Created connection did not return a connection string", {
|
|
6934
6665
|
why: "Database connection strings are one-time-view secrets, but the Management API did not include one in this create response.",
|
|
6935
|
-
|
|
6936
|
-
exitCode: 1,
|
|
6937
|
-
nextSteps: [`prisma database connection create ${fallbackDatabaseId}`]
|
|
6666
|
+
nextActions: [userChoice$1("Create another database connection and store the returned URL immediately."), runCommand$1(`${CLI_NAME} postgres connection create ${fallbackDatabaseId}`)]
|
|
6938
6667
|
});
|
|
6939
6668
|
return {
|
|
6940
6669
|
connection: normalizeConnection(connection, fallbackDatabaseId),
|
|
@@ -6948,14 +6677,9 @@ function normalizeRegion(database) {
|
|
|
6948
6677
|
function requireDatabaseProjectId(database, fallbackProjectId) {
|
|
6949
6678
|
const projectId = database.projectId ?? fallbackProjectId;
|
|
6950
6679
|
if (projectId) return projectId;
|
|
6951
|
-
throw new
|
|
6952
|
-
code: "DATABASE_API_ERROR",
|
|
6953
|
-
domain: "database",
|
|
6954
|
-
summary: "Database response did not include a project id",
|
|
6680
|
+
throw new CliStructuredError("POSTGRES.API_ERROR", "Database response did not include a project id", {
|
|
6955
6681
|
why: "The Management API returned database metadata without project context.",
|
|
6956
|
-
|
|
6957
|
-
exitCode: 1,
|
|
6958
|
-
nextSteps: []
|
|
6682
|
+
nextActions: [userChoice$1(VERBOSE_LOG_FIX)]
|
|
6959
6683
|
});
|
|
6960
6684
|
}
|
|
6961
6685
|
function extractConnectionString(connection) {
|
|
@@ -7000,14 +6724,9 @@ function normalizeBackupList(body) {
|
|
|
7000
6724
|
}
|
|
7001
6725
|
function normalizeRotatedConnection(connection) {
|
|
7002
6726
|
const connectionString = extractConnectionString(connection);
|
|
7003
|
-
if (!connectionString) throw new
|
|
7004
|
-
code: "DATABASE_CONNECTION_STRING_MISSING",
|
|
7005
|
-
domain: "database",
|
|
7006
|
-
summary: "Rotated connection did not return a connection string",
|
|
6727
|
+
if (!connectionString) throw new CliStructuredError("POSTGRES.CONNECTION_STRING_MISSING", "Rotated connection did not return a connection string", {
|
|
7007
6728
|
why: "Rotated connection strings are one-time-view secrets, but the Management API did not include one in this rotate response.",
|
|
7008
|
-
|
|
7009
|
-
exitCode: 1,
|
|
7010
|
-
nextSteps: []
|
|
6729
|
+
nextActions: [userChoice$1("Re-run the rotation, or create a replacement connection and store the returned URL immediately.")]
|
|
7011
6730
|
});
|
|
7012
6731
|
const database = connection.database?.id && connection.database?.name ? {
|
|
7013
6732
|
id: connection.database.id,
|
|
@@ -7020,74 +6739,68 @@ function normalizeRotatedConnection(connection) {
|
|
|
7020
6739
|
};
|
|
7021
6740
|
}
|
|
7022
6741
|
function backupsUnsupportedError(databaseId, error) {
|
|
7023
|
-
return new
|
|
7024
|
-
code: "DATABASE_BACKUPS_UNSUPPORTED",
|
|
7025
|
-
domain: "database",
|
|
7026
|
-
summary: "Backups are not available for this database",
|
|
6742
|
+
return new CliStructuredError("POSTGRES.BACKUPS_UNSUPPORTED", "Backups are not available for this database", {
|
|
7027
6743
|
why: error?.error?.message ?? `The platform does not manage backups for database "${databaseId}", for example because it is a remote/BYO database.`,
|
|
7028
|
-
|
|
7029
|
-
exitCode: 1,
|
|
7030
|
-
nextSteps: []
|
|
6744
|
+
nextActions: [userChoice$1("Use your own backup tooling for externally managed databases.")]
|
|
7031
6745
|
});
|
|
7032
6746
|
}
|
|
7033
|
-
function restoreBackupNotFoundError(options, error
|
|
7034
|
-
const listCommand =
|
|
7035
|
-
|
|
7036
|
-
"backup",
|
|
7037
|
-
"list",
|
|
7038
|
-
options.sourceDatabaseId
|
|
7039
|
-
]);
|
|
7040
|
-
return new CliError({
|
|
7041
|
-
code: "DATABASE_BACKUP_NOT_FOUND",
|
|
7042
|
-
domain: "database",
|
|
7043
|
-
summary: "Database backup not found",
|
|
6747
|
+
function restoreBackupNotFoundError(options, error) {
|
|
6748
|
+
const listCommand = `${CLI_NAME} postgres backup list ${options.sourceDatabaseId}`;
|
|
6749
|
+
return new CliStructuredError("POSTGRES.BACKUP_NOT_FOUND", "Database backup not found", {
|
|
7044
6750
|
why: error?.error?.message ?? `No backup matched "${options.backupId}" for database "${options.sourceDatabaseId}".`,
|
|
7045
|
-
|
|
7046
|
-
exitCode: 1,
|
|
7047
|
-
nextSteps: [listCommand]
|
|
6751
|
+
nextActions: [userChoice$1(`Pass a backup id from ${listCommand}.`), runCommand$1(listCommand)]
|
|
7048
6752
|
});
|
|
7049
6753
|
}
|
|
7050
|
-
function restoreConflictError(targetDatabaseId, error
|
|
7051
|
-
return new
|
|
7052
|
-
code: "DATABASE_RESTORE_CONFLICT",
|
|
7053
|
-
domain: "database",
|
|
7054
|
-
summary: "Database cannot be restored right now",
|
|
6754
|
+
function restoreConflictError(targetDatabaseId, error) {
|
|
6755
|
+
return new CliStructuredError("POSTGRES.RESTORE_CONFLICT", "Database cannot be restored right now", {
|
|
7055
6756
|
why: error?.error?.message ?? `Database "${targetDatabaseId}" is provisioning or already recovering.`,
|
|
7056
|
-
|
|
7057
|
-
exitCode: 1,
|
|
7058
|
-
nextSteps: [formatCommand([
|
|
7059
|
-
"database",
|
|
7060
|
-
"show",
|
|
7061
|
-
targetDatabaseId
|
|
7062
|
-
])]
|
|
6757
|
+
nextActions: [userChoice$1("Wait for the database to become ready, then retry the restore."), runCommand$1(`${CLI_NAME} postgres show ${targetDatabaseId}`)]
|
|
7063
6758
|
});
|
|
7064
6759
|
}
|
|
6760
|
+
/** A 401 or 403 is the API refusing the caller, not a database problem. */
|
|
6761
|
+
function isRejectedCaller(status) {
|
|
6762
|
+
return status === 401 || status === 403;
|
|
6763
|
+
}
|
|
6764
|
+
function apiErrorWhy(status, message) {
|
|
6765
|
+
if (!isRejectedCaller(status)) return message ?? `The Management API returned status ${status || "unknown"}.`;
|
|
6766
|
+
const rejection = `The Management API rejected the request as ${status === 401 ? "unauthorized" : "forbidden"}.`;
|
|
6767
|
+
return message ? `${rejection} ${message}` : rejection;
|
|
6768
|
+
}
|
|
6769
|
+
function apiErrorMeta(status, apiCode) {
|
|
6770
|
+
if (!status && apiCode === void 0) return;
|
|
6771
|
+
return {
|
|
6772
|
+
...status ? { status } : {},
|
|
6773
|
+
...apiCode === void 0 ? {} : { apiCode }
|
|
6774
|
+
};
|
|
6775
|
+
}
|
|
6776
|
+
function apiErrorActions(status, hint) {
|
|
6777
|
+
if (!isRejectedCaller(status)) return [userChoice$1(hint ?? VERBOSE_LOG_FIX)];
|
|
6778
|
+
return [userChoice$1(hint ?? `Sign in again with prisma auth login, then retry the command.`), runCommand$1(`${CLI_NAME} auth login`)];
|
|
6779
|
+
}
|
|
6780
|
+
/**
|
|
6781
|
+
* Every database Management API failure that is not a plan limit lands
|
|
6782
|
+
* on the one registered code. The response's own error code is data, not
|
|
6783
|
+
* an identity: it travels in `meta.apiCode` beside `meta.status` so a
|
|
6784
|
+
* consumer can still branch on it without the CLI minting a code it
|
|
6785
|
+
* never registered.
|
|
6786
|
+
*/
|
|
7065
6787
|
async function databaseApiError(options) {
|
|
7066
6788
|
if (isPlanLimitApiError(options.error)) return planLimitReachedError(options);
|
|
7067
6789
|
const status = options.response?.status ?? 0;
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
fix: options.error?.error?.hint ?? "Re-run with --trace for the underlying API response details.",
|
|
7074
|
-
exitCode: 1,
|
|
7075
|
-
nextSteps: []
|
|
6790
|
+
const meta = apiErrorMeta(status, options.error?.error?.code);
|
|
6791
|
+
return new CliStructuredError("POSTGRES.API_ERROR", options.summary, {
|
|
6792
|
+
why: apiErrorWhy(status, options.error?.error?.message),
|
|
6793
|
+
...meta === void 0 ? {} : { meta },
|
|
6794
|
+
nextActions: apiErrorActions(status, options.error?.error?.hint)
|
|
7076
6795
|
});
|
|
7077
6796
|
}
|
|
7078
6797
|
async function planLimitReachedError(options) {
|
|
7079
6798
|
const subscription = options.workspaceId ? await readWorkspaceSubscription(options.client, options.workspaceId, options.signal) : null;
|
|
7080
|
-
const workspaceLine = options.workspaceId ? `Workspace: ${options.workspaceId}` : "Workspace: unavailable";
|
|
7081
6799
|
const planName = subscription?.planName || null;
|
|
7082
6800
|
const usageBlocked = subscription?.usageBlocked ?? null;
|
|
7083
6801
|
const upgradeUrl = subscription?.upgradeUrl || null;
|
|
7084
|
-
|
|
7085
|
-
return new CliError({
|
|
7086
|
-
code: "PLAN_LIMIT_REACHED",
|
|
7087
|
-
domain: "database",
|
|
7088
|
-
summary: "Workspace plan limit reached",
|
|
6802
|
+
return new CliStructuredError("POSTGRES.PLAN_LIMIT_REACHED", "Workspace plan limit reached", {
|
|
7089
6803
|
why: "Database operations are blocked because this workspace has used the operations included in its plan. This is a workspace plan limit, not a Prisma outage.",
|
|
7090
|
-
fix: upgradeUrl ? `Upgrade the workspace plan at ${upgradeUrl}.` : "Open Prisma Console and upgrade the affected workspace plan.",
|
|
7091
6804
|
meta: {
|
|
7092
6805
|
workspaceId: options.workspaceId ?? null,
|
|
7093
6806
|
blockedFeature: null,
|
|
@@ -7095,16 +6808,11 @@ async function planLimitReachedError(options) {
|
|
|
7095
6808
|
usageBlocked,
|
|
7096
6809
|
upgradeUrl
|
|
7097
6810
|
},
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
"Database operations are blocked because this workspace has used the operations included in its plan. This is a workspace plan limit, not a Prisma outage.",
|
|
7104
|
-
"",
|
|
7105
|
-
workspaceLine,
|
|
7106
|
-
...recoveryLines
|
|
7107
|
-
]
|
|
6811
|
+
nextActions: [{
|
|
6812
|
+
kind: "user-choice",
|
|
6813
|
+
label: "Upgrade the workspace plan",
|
|
6814
|
+
reason: upgradeUrl ? `Upgrade at ${upgradeUrl}${planName ? ` (current plan: ${planName})` : ""}.` : "Open Prisma Console and upgrade the affected workspace plan."
|
|
6815
|
+
}]
|
|
7108
6816
|
});
|
|
7109
6817
|
}
|
|
7110
6818
|
function isPlanLimitApiError(error) {
|
|
@@ -7145,10 +6853,6 @@ const databasePositional = positional.string({
|
|
|
7145
6853
|
brief: "Database id or name",
|
|
7146
6854
|
placeholder: "database"
|
|
7147
6855
|
});
|
|
7148
|
-
/** The legacy helpers build their nextSteps through a command
|
|
7149
|
-
* formatter. This CLI phrases every command string as `${CLI_NAME} …`; the
|
|
7150
|
-
* error mapper rewrites the `database` group name to `postgres`. */
|
|
7151
|
-
const legacyCommandFormatter = (args) => [CLI_NAME, ...args].join(" ");
|
|
7152
6856
|
async function resolvePostgresContext(ctx, flags, commandName) {
|
|
7153
6857
|
const workspace = await resolveActiveWorkspace(ctx);
|
|
7154
6858
|
const target = await resolvePinnedProject(ctx, workspace, flags.project, commandName);
|
|
@@ -7167,99 +6871,6 @@ async function resolvePostgresProviderOnly(ctx) {
|
|
|
7167
6871
|
return createManagementDatabaseProvider(ctx.api, { ...workspaceId === void 0 ? {} : { workspaceId } });
|
|
7168
6872
|
}
|
|
7169
6873
|
//#endregion
|
|
7170
|
-
//#region src/commands/postgres/errors.ts
|
|
7171
|
-
const POSTGRES_CODE_MAP = {
|
|
7172
|
-
USAGE_ERROR: "POSTGRES.USAGE_ERROR",
|
|
7173
|
-
DATABASE_NOT_FOUND: "POSTGRES.NOT_FOUND",
|
|
7174
|
-
DATABASE_AMBIGUOUS: "POSTGRES.AMBIGUOUS",
|
|
7175
|
-
DATABASE_CONNECTION_MISSING: "POSTGRES.CONNECTION_MISSING",
|
|
7176
|
-
DATABASE_CONNECTION_STRING_MISSING: "POSTGRES.CONNECTION_STRING_MISSING",
|
|
7177
|
-
DATABASE_BACKUPS_UNSUPPORTED: "POSTGRES.BACKUPS_UNSUPPORTED",
|
|
7178
|
-
DATABASE_RESTORE_CONFLICT: "POSTGRES.RESTORE_CONFLICT",
|
|
7179
|
-
DATABASE_BACKUP_NOT_FOUND: "POSTGRES.BACKUP_NOT_FOUND",
|
|
7180
|
-
DATABASE_API_ERROR: "POSTGRES.API_ERROR"
|
|
7181
|
-
};
|
|
7182
|
-
/** The project-resolution codes this group can raise; they keep the
|
|
7183
|
-
* project group's dotted codes and copy. */
|
|
7184
|
-
const PROJECT_CODES = new Set([
|
|
7185
|
-
"PROJECT_NOT_FOUND",
|
|
7186
|
-
"PROJECT_AMBIGUOUS",
|
|
7187
|
-
"PROJECT_SETUP_REQUIRED",
|
|
7188
|
-
"LOCAL_STATE_STALE",
|
|
7189
|
-
"LOCAL_PROJECT_WORKSPACE_MISMATCH"
|
|
7190
|
-
]);
|
|
7191
|
-
const PACKAGE_RUNNER = /[\w./@-]+(?: [\w.-]+)? @prisma\/cli@\S+ /g;
|
|
7192
|
-
const COMMENT_PREFIX = /^#\s*/;
|
|
7193
|
-
const LEGACY_GROUP = new RegExp(`${CLI_NAME} database `, "g");
|
|
7194
|
-
/** §0 rename: wherever a legacy command reference appears — a
|
|
7195
|
-
* nextSteps string or prose inside `fix` — the package-runner prefix
|
|
7196
|
-
* becomes `${CLI_NAME}` and the `database` group becomes `postgres`.
|
|
7197
|
-
* The resource noun "database" in prose is left alone. */
|
|
7198
|
-
function portCommandReferences(text) {
|
|
7199
|
-
return text.replace(PACKAGE_RUNNER, `${CLI_NAME} `).replace(LEGACY_GROUP, `${CLI_NAME} postgres `);
|
|
7200
|
-
}
|
|
7201
|
-
function portPostgresCommand(command) {
|
|
7202
|
-
const named = portCommandReferences(command);
|
|
7203
|
-
return named.startsWith(`prisma `) ? named : `${CLI_NAME} ${named}`;
|
|
7204
|
-
}
|
|
7205
|
-
const STALE_INTERACTIVE_SIGN_IN = /, or rerun the command in a TTY to sign in interactively\./g;
|
|
7206
|
-
const TRACE_FLAG = /--trace/g;
|
|
7207
|
-
/** `--trace` is gone in this CLI; the log level replaces it. Interactive
|
|
7208
|
-
* sign-in is gone too (R-S2b-2), so the legacy offer to rerun in a TTY
|
|
7209
|
-
* describes something this CLI cannot do; `auth login` is the whole remedy. */
|
|
7210
|
-
function portFixText(fix) {
|
|
7211
|
-
return portCommandReferences(fix).replace(TRACE_FLAG, "--log-level verbose").replace(STALE_INTERACTIVE_SIGN_IN, ".");
|
|
7212
|
-
}
|
|
7213
|
-
function runCommandActions(nextSteps) {
|
|
7214
|
-
const actions = [];
|
|
7215
|
-
let reason;
|
|
7216
|
-
for (const step of nextSteps) {
|
|
7217
|
-
if (step.startsWith("#")) {
|
|
7218
|
-
reason = step.replace(COMMENT_PREFIX, "");
|
|
7219
|
-
continue;
|
|
7220
|
-
}
|
|
7221
|
-
const command = portPostgresCommand(step);
|
|
7222
|
-
actions.push({
|
|
7223
|
-
kind: "run-command",
|
|
7224
|
-
label: command,
|
|
7225
|
-
command,
|
|
7226
|
-
...reason === void 0 ? {} : { reason }
|
|
7227
|
-
});
|
|
7228
|
-
reason = void 0;
|
|
7229
|
-
}
|
|
7230
|
-
return actions;
|
|
7231
|
-
}
|
|
7232
|
-
/** PR #127's plan-limit error: the legacy full-page `humanLines`
|
|
7233
|
-
* rendering does not port, so the recovery guidance becomes the one
|
|
7234
|
-
* nextAction beside the verbatim why and meta. */
|
|
7235
|
-
function planLimitError(error) {
|
|
7236
|
-
const upgradeUrl = error.meta.upgradeUrl;
|
|
7237
|
-
const planName = error.meta.planName;
|
|
7238
|
-
const reason = typeof upgradeUrl === "string" && upgradeUrl ? `Upgrade at ${upgradeUrl}${typeof planName === "string" && planName ? ` (current plan: ${planName})` : ""}.` : "Open Prisma Console and upgrade the affected workspace plan.";
|
|
7239
|
-
return new CliStructuredError("POSTGRES.PLAN_LIMIT_REACHED", error.summary, {
|
|
7240
|
-
why: error.why ?? void 0,
|
|
7241
|
-
meta: error.meta,
|
|
7242
|
-
nextActions: [{
|
|
7243
|
-
kind: "user-choice",
|
|
7244
|
-
label: "Upgrade the workspace plan",
|
|
7245
|
-
reason
|
|
7246
|
-
}]
|
|
7247
|
-
});
|
|
7248
|
-
}
|
|
7249
|
-
function mapPostgresOperationError(error) {
|
|
7250
|
-
if (!(error instanceof CliError)) return null;
|
|
7251
|
-
if (error.code === "PLAN_LIMIT_REACHED") return planLimitError(error);
|
|
7252
|
-
if (PROJECT_CODES.has(error.code)) return mapProjectOperationError(error);
|
|
7253
|
-
return new CliStructuredError(POSTGRES_CODE_MAP[error.code] ?? `POSTGRES.${error.code}`, error.summary, {
|
|
7254
|
-
why: error.why ?? void 0,
|
|
7255
|
-
meta: Object.keys(error.meta).length > 0 ? error.meta : void 0,
|
|
7256
|
-
nextActions: [...error.fix ? [{
|
|
7257
|
-
kind: "user-choice",
|
|
7258
|
-
label: portFixText(error.fix)
|
|
7259
|
-
}] : [], ...runCommandActions(error.nextSteps)]
|
|
7260
|
-
});
|
|
7261
|
-
}
|
|
7262
|
-
//#endregion
|
|
7263
6874
|
//#region src/commands/postgres/presentation.ts
|
|
7264
6875
|
/** Legacy `formatDatabaseTarget`. */
|
|
7265
6876
|
function postgresTargetLabel(projectName, branchName) {
|
|
@@ -7418,28 +7029,22 @@ const postgresBackupListCommand = defineCommand({
|
|
|
7418
7029
|
},
|
|
7419
7030
|
needs: { credentials: true },
|
|
7420
7031
|
handler: async (args, ctx) => {
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
return ok(ctx.present({ data: result }, backupListPresentations(result)));
|
|
7438
|
-
} catch (error) {
|
|
7439
|
-
const mapped = mapPostgresOperationError(error);
|
|
7440
|
-
if (mapped) return notOk(mapped);
|
|
7441
|
-
throw error;
|
|
7442
|
-
}
|
|
7032
|
+
const limit = parseBackupLimit(args.flags.limit);
|
|
7033
|
+
const { provider, target, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres backup list");
|
|
7034
|
+
const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
|
|
7035
|
+
const backups = await provider.listBackups(database.id, {
|
|
7036
|
+
limit,
|
|
7037
|
+
signal: ctx.signal
|
|
7038
|
+
});
|
|
7039
|
+
const result = {
|
|
7040
|
+
projectId,
|
|
7041
|
+
projectName,
|
|
7042
|
+
database,
|
|
7043
|
+
backups: backups.backups,
|
|
7044
|
+
retentionDays: backups.retentionDays,
|
|
7045
|
+
hasMore: backups.hasMore
|
|
7046
|
+
};
|
|
7047
|
+
return ok(ctx.present({ data: result }, backupListPresentations(result)));
|
|
7443
7048
|
}
|
|
7444
7049
|
});
|
|
7445
7050
|
//#endregion
|
|
@@ -7519,44 +7124,41 @@ const postgresBackupRestoreCommand = defineCommand({
|
|
|
7519
7124
|
},
|
|
7520
7125
|
needs: { credentials: true },
|
|
7521
7126
|
handler: async (args, ctx) => {
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
"backup",
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
|
|
7537
|
-
const sourceDatabase = args.flags.sourceDatabase ? await resolveDatabase(provider, target, args.flags.sourceDatabase, args.flags.branch, ctx.signal) : database;
|
|
7538
|
-
await ctx.prompt.consent(CONSENT_QUESTION$5, { token: database.id });
|
|
7539
|
-
const result = {
|
|
7540
|
-
projectId,
|
|
7541
|
-
projectName,
|
|
7542
|
-
database: await provider.restoreDatabase({
|
|
7543
|
-
targetDatabaseId: database.id,
|
|
7544
|
-
sourceDatabaseId: sourceDatabase.id,
|
|
7545
|
-
backupId,
|
|
7546
|
-
projectId,
|
|
7547
|
-
signal: ctx.signal
|
|
7548
|
-
}),
|
|
7549
|
-
source: {
|
|
7550
|
-
databaseId: sourceDatabase.id,
|
|
7551
|
-
backupId
|
|
7552
|
-
}
|
|
7553
|
-
};
|
|
7554
|
-
return ok(ctx.present({ data: result }, restorePresentations(result, sourceDatabase.id === database.id ? null : sourceDatabase.id, database.id)));
|
|
7555
|
-
} catch (error) {
|
|
7556
|
-
const mapped = mapPostgresOperationError(error);
|
|
7557
|
-
if (mapped) return notOk(mapped);
|
|
7558
|
-
throw error;
|
|
7127
|
+
const backupId = args.flags.backup?.trim();
|
|
7128
|
+
if (!backupId) {
|
|
7129
|
+
const listCommand = `${CLI_NAME} postgres backup list <database>`;
|
|
7130
|
+
throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Backup id required", {
|
|
7131
|
+
why: "Database restore needs the backup to restore from.",
|
|
7132
|
+
nextActions: [{
|
|
7133
|
+
kind: "user-choice",
|
|
7134
|
+
label: `Pass --backup <backup-id> from ${listCommand}.`
|
|
7135
|
+
}, {
|
|
7136
|
+
kind: "run-command",
|
|
7137
|
+
label: listCommand,
|
|
7138
|
+
command: listCommand
|
|
7139
|
+
}]
|
|
7140
|
+
});
|
|
7559
7141
|
}
|
|
7142
|
+
const { provider, target, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres backup restore");
|
|
7143
|
+
const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
|
|
7144
|
+
const sourceDatabase = args.flags.sourceDatabase ? await resolveDatabase(provider, target, args.flags.sourceDatabase, args.flags.branch, ctx.signal) : database;
|
|
7145
|
+
await ctx.prompt.consent(CONSENT_QUESTION$5, { token: database.id });
|
|
7146
|
+
const result = {
|
|
7147
|
+
projectId,
|
|
7148
|
+
projectName,
|
|
7149
|
+
database: await provider.restoreDatabase({
|
|
7150
|
+
targetDatabaseId: database.id,
|
|
7151
|
+
sourceDatabaseId: sourceDatabase.id,
|
|
7152
|
+
backupId,
|
|
7153
|
+
projectId,
|
|
7154
|
+
signal: ctx.signal
|
|
7155
|
+
}),
|
|
7156
|
+
source: {
|
|
7157
|
+
databaseId: sourceDatabase.id,
|
|
7158
|
+
backupId
|
|
7159
|
+
}
|
|
7160
|
+
};
|
|
7161
|
+
return ok(ctx.present({ data: result }, restorePresentations(result, sourceDatabase.id === database.id ? null : sourceDatabase.id, database.id)));
|
|
7560
7162
|
}
|
|
7561
7163
|
});
|
|
7562
7164
|
//#endregion
|
|
@@ -7580,32 +7182,26 @@ const postgresConnectionCreateCommand = defineCommand({
|
|
|
7580
7182
|
},
|
|
7581
7183
|
needs: { credentials: true },
|
|
7582
7184
|
handler: async (args, ctx) => {
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
}));
|
|
7604
|
-
} catch (error) {
|
|
7605
|
-
const mapped = mapPostgresOperationError(error);
|
|
7606
|
-
if (mapped) return notOk(mapped);
|
|
7607
|
-
throw error;
|
|
7608
|
-
}
|
|
7185
|
+
const { provider, target, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres connection create");
|
|
7186
|
+
const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
|
|
7187
|
+
const created = await provider.createConnection({
|
|
7188
|
+
databaseId: database.id,
|
|
7189
|
+
name: args.flags.name?.trim() || defaultConnectionName(),
|
|
7190
|
+
signal: ctx.signal
|
|
7191
|
+
});
|
|
7192
|
+
const result = {
|
|
7193
|
+
projectId,
|
|
7194
|
+
projectName,
|
|
7195
|
+
database,
|
|
7196
|
+
connection: created.connection,
|
|
7197
|
+
connectionString: created.connectionString
|
|
7198
|
+
};
|
|
7199
|
+
return ok(ctx.present({ data: result }, {
|
|
7200
|
+
human: () => secretBlocks(`Added a connection to "${database.name}" in ${postgresTargetLabel(projectName, database.branchName)}.`, result.connectionString),
|
|
7201
|
+
stdout: () => [result.connectionString],
|
|
7202
|
+
json: () => result,
|
|
7203
|
+
next: () => []
|
|
7204
|
+
}));
|
|
7609
7205
|
}
|
|
7610
7206
|
});
|
|
7611
7207
|
//#endregion
|
|
@@ -7623,40 +7219,47 @@ const postgresConnectionDeleteCommand = defineCommand({
|
|
|
7623
7219
|
},
|
|
7624
7220
|
needs: { credentials: true },
|
|
7625
7221
|
handler: async (args, ctx) => {
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
kind: "fields",
|
|
7641
|
-
rows: [{
|
|
7642
|
-
label: "connection",
|
|
7643
|
-
value: connectionId
|
|
7644
|
-
}]
|
|
7645
|
-
},
|
|
7646
|
-
{
|
|
7647
|
-
kind: "list",
|
|
7648
|
-
items: ["The connection metadata was deleted. Existing one-time secrets were not shown."]
|
|
7649
|
-
}
|
|
7650
|
-
],
|
|
7651
|
-
stdout: () => [],
|
|
7652
|
-
json: () => ({ connection: result.connection }),
|
|
7653
|
-
next: () => []
|
|
7654
|
-
}));
|
|
7655
|
-
} catch (error) {
|
|
7656
|
-
const mapped = mapPostgresOperationError(error);
|
|
7657
|
-
if (mapped) return notOk(mapped);
|
|
7658
|
-
throw error;
|
|
7222
|
+
const connectionId = args.positionals.connection.trim();
|
|
7223
|
+
if (!connectionId) {
|
|
7224
|
+
const example = `${CLI_NAME} postgres connection delete <connection-id> --confirm <connection-id>`;
|
|
7225
|
+
throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Connection id required", {
|
|
7226
|
+
why: "Database connection deletion needs a connection id.",
|
|
7227
|
+
nextActions: [{
|
|
7228
|
+
kind: "user-choice",
|
|
7229
|
+
label: "Pass the connection id to delete."
|
|
7230
|
+
}, {
|
|
7231
|
+
kind: "run-command",
|
|
7232
|
+
label: example,
|
|
7233
|
+
command: example
|
|
7234
|
+
}]
|
|
7235
|
+
});
|
|
7659
7236
|
}
|
|
7237
|
+
await ctx.prompt.consent(CONSENT_QUESTION$4, { token: connectionId });
|
|
7238
|
+
await (await resolvePostgresProviderOnly(ctx)).removeConnection(connectionId, { signal: ctx.signal });
|
|
7239
|
+
const result = { connection: { id: connectionId } };
|
|
7240
|
+
return ok(ctx.present({ data: result }, {
|
|
7241
|
+
human: () => [
|
|
7242
|
+
{
|
|
7243
|
+
kind: "summary",
|
|
7244
|
+
status: "ok",
|
|
7245
|
+
text: "Deleting database connection."
|
|
7246
|
+
},
|
|
7247
|
+
{
|
|
7248
|
+
kind: "fields",
|
|
7249
|
+
rows: [{
|
|
7250
|
+
label: "connection",
|
|
7251
|
+
value: connectionId
|
|
7252
|
+
}]
|
|
7253
|
+
},
|
|
7254
|
+
{
|
|
7255
|
+
kind: "list",
|
|
7256
|
+
items: ["The connection metadata was deleted. Existing one-time secrets were not shown."]
|
|
7257
|
+
}
|
|
7258
|
+
],
|
|
7259
|
+
stdout: () => [],
|
|
7260
|
+
json: () => ({ connection: result.connection }),
|
|
7261
|
+
next: () => []
|
|
7262
|
+
}));
|
|
7660
7263
|
}
|
|
7661
7264
|
});
|
|
7662
7265
|
//#endregion
|
|
@@ -7728,21 +7331,15 @@ const postgresConnectionListCommand = defineCommand({
|
|
|
7728
7331
|
},
|
|
7729
7332
|
needs: { credentials: true },
|
|
7730
7333
|
handler: async (args, ctx) => {
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
return ok(ctx.present({ data: result }, listPresentations$4(result)));
|
|
7741
|
-
} catch (error) {
|
|
7742
|
-
const mapped = mapPostgresOperationError(error);
|
|
7743
|
-
if (mapped) return notOk(mapped);
|
|
7744
|
-
throw error;
|
|
7745
|
-
}
|
|
7334
|
+
const { provider, target, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres connection list");
|
|
7335
|
+
const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
|
|
7336
|
+
const result = {
|
|
7337
|
+
projectId,
|
|
7338
|
+
projectName,
|
|
7339
|
+
database,
|
|
7340
|
+
connections: await provider.listConnections(database.id, { signal: ctx.signal })
|
|
7341
|
+
};
|
|
7342
|
+
return ok(ctx.present({ data: result }, listPresentations$4(result)));
|
|
7746
7343
|
}
|
|
7747
7344
|
});
|
|
7748
7345
|
//#endregion
|
|
@@ -7760,35 +7357,35 @@ const postgresConnectionRotateCommand = defineCommand({
|
|
|
7760
7357
|
},
|
|
7761
7358
|
needs: { credentials: true },
|
|
7762
7359
|
handler: async (args, ctx) => {
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
"connection",
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
database: rotated.database,
|
|
7778
|
-
connectionString: rotated.connectionString
|
|
7779
|
-
};
|
|
7780
|
-
const subject = result.database ? `"${result.database.name}"` : `connection ${result.connection.id}`;
|
|
7781
|
-
return ok(ctx.present({ data: result }, {
|
|
7782
|
-
human: () => secretBlocks(`Rotated credentials for ${subject}. The previous credentials no longer work.`, result.connectionString),
|
|
7783
|
-
stdout: () => [result.connectionString],
|
|
7784
|
-
json: () => result,
|
|
7785
|
-
next: () => []
|
|
7786
|
-
}));
|
|
7787
|
-
} catch (error) {
|
|
7788
|
-
const mapped = mapPostgresOperationError(error);
|
|
7789
|
-
if (mapped) return notOk(mapped);
|
|
7790
|
-
throw error;
|
|
7360
|
+
const connectionId = args.positionals.connection.trim();
|
|
7361
|
+
if (!connectionId) {
|
|
7362
|
+
const example = `${CLI_NAME} postgres connection rotate <connection-id> --confirm <connection-id>`;
|
|
7363
|
+
throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Connection id required", {
|
|
7364
|
+
why: "Database connection rotation needs a connection id.",
|
|
7365
|
+
nextActions: [{
|
|
7366
|
+
kind: "user-choice",
|
|
7367
|
+
label: "Pass the connection id to rotate."
|
|
7368
|
+
}, {
|
|
7369
|
+
kind: "run-command",
|
|
7370
|
+
label: example,
|
|
7371
|
+
command: example
|
|
7372
|
+
}]
|
|
7373
|
+
});
|
|
7791
7374
|
}
|
|
7375
|
+
await ctx.prompt.consent(CONSENT_QUESTION$3, { token: connectionId });
|
|
7376
|
+
const rotated = await (await resolvePostgresProviderOnly(ctx)).rotateConnection(connectionId, { signal: ctx.signal });
|
|
7377
|
+
const result = {
|
|
7378
|
+
connection: rotated.connection,
|
|
7379
|
+
database: rotated.database,
|
|
7380
|
+
connectionString: rotated.connectionString
|
|
7381
|
+
};
|
|
7382
|
+
const subject = result.database ? `"${result.database.name}"` : `connection ${result.connection.id}`;
|
|
7383
|
+
return ok(ctx.present({ data: result }, {
|
|
7384
|
+
human: () => secretBlocks(`Rotated credentials for ${subject}. The previous credentials no longer work.`, result.connectionString),
|
|
7385
|
+
stdout: () => [result.connectionString],
|
|
7386
|
+
json: () => result,
|
|
7387
|
+
next: () => []
|
|
7388
|
+
}));
|
|
7792
7389
|
}
|
|
7793
7390
|
});
|
|
7794
7391
|
//#endregion
|
|
@@ -7815,35 +7412,42 @@ const postgresCreateCommand = defineCommand({
|
|
|
7815
7412
|
},
|
|
7816
7413
|
needs: { credentials: true },
|
|
7817
7414
|
handler: async (args, ctx) => {
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7415
|
+
const name = args.positionals.name.trim();
|
|
7416
|
+
if (!name) {
|
|
7417
|
+
const example = `${CLI_NAME} postgres create <name>`;
|
|
7418
|
+
throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Database name required", {
|
|
7419
|
+
why: "Database create needs a non-empty name.",
|
|
7420
|
+
nextActions: [{
|
|
7421
|
+
kind: "user-choice",
|
|
7422
|
+
label: "Pass a database name."
|
|
7423
|
+
}, {
|
|
7424
|
+
kind: "run-command",
|
|
7425
|
+
label: example,
|
|
7426
|
+
command: example
|
|
7427
|
+
}]
|
|
7828
7428
|
});
|
|
7829
|
-
const result = {
|
|
7830
|
-
projectId,
|
|
7831
|
-
projectName,
|
|
7832
|
-
database: ensureProjectId(created.database, projectId),
|
|
7833
|
-
connection: created.connection,
|
|
7834
|
-
connectionString: created.connectionString
|
|
7835
|
-
};
|
|
7836
|
-
return ok(ctx.present({ data: result }, {
|
|
7837
|
-
human: () => secretBlocks(`Created database "${result.database.name}" in ${postgresTargetLabel(projectName, result.database.branchName)}.`, result.connectionString),
|
|
7838
|
-
stdout: () => [result.connectionString],
|
|
7839
|
-
json: () => result,
|
|
7840
|
-
next: () => []
|
|
7841
|
-
}));
|
|
7842
|
-
} catch (error) {
|
|
7843
|
-
const mapped = mapPostgresOperationError(error);
|
|
7844
|
-
if (mapped) return notOk(mapped);
|
|
7845
|
-
throw error;
|
|
7846
7429
|
}
|
|
7430
|
+
const { provider, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres create");
|
|
7431
|
+
const created = await provider.createDatabase({
|
|
7432
|
+
projectId,
|
|
7433
|
+
name,
|
|
7434
|
+
branchName: args.flags.branch,
|
|
7435
|
+
region: args.flags.region,
|
|
7436
|
+
signal: ctx.signal
|
|
7437
|
+
});
|
|
7438
|
+
const result = {
|
|
7439
|
+
projectId,
|
|
7440
|
+
projectName,
|
|
7441
|
+
database: ensureProjectId(created.database, projectId),
|
|
7442
|
+
connection: created.connection,
|
|
7443
|
+
connectionString: created.connectionString
|
|
7444
|
+
};
|
|
7445
|
+
return ok(ctx.present({ data: result }, {
|
|
7446
|
+
human: () => secretBlocks(`Created database "${result.database.name}" in ${postgresTargetLabel(projectName, result.database.branchName)}.`, result.connectionString),
|
|
7447
|
+
stdout: () => [result.connectionString],
|
|
7448
|
+
json: () => result,
|
|
7449
|
+
next: () => []
|
|
7450
|
+
}));
|
|
7847
7451
|
}
|
|
7848
7452
|
});
|
|
7849
7453
|
//#endregion
|
|
@@ -7899,22 +7503,16 @@ const postgresDeleteCommand = defineCommand({
|
|
|
7899
7503
|
},
|
|
7900
7504
|
needs: { credentials: true },
|
|
7901
7505
|
handler: async (args, ctx) => {
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
return ok(ctx.present({ data: result }, deletePresentations$3(result)));
|
|
7913
|
-
} catch (error) {
|
|
7914
|
-
const mapped = mapPostgresOperationError(error);
|
|
7915
|
-
if (mapped) return notOk(mapped);
|
|
7916
|
-
throw error;
|
|
7917
|
-
}
|
|
7506
|
+
const { provider, target, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres delete");
|
|
7507
|
+
const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
|
|
7508
|
+
await ctx.prompt.consent(CONSENT_QUESTION$2, { token: database.id });
|
|
7509
|
+
await provider.removeDatabase(database.id, { signal: ctx.signal });
|
|
7510
|
+
const result = {
|
|
7511
|
+
projectId,
|
|
7512
|
+
projectName,
|
|
7513
|
+
database
|
|
7514
|
+
};
|
|
7515
|
+
return ok(ctx.present({ data: result }, deletePresentations$3(result)));
|
|
7918
7516
|
}
|
|
7919
7517
|
});
|
|
7920
7518
|
//#endregion
|
|
@@ -7997,25 +7595,19 @@ const postgresListCommand = defineCommand({
|
|
|
7997
7595
|
},
|
|
7998
7596
|
needs: { credentials: true },
|
|
7999
7597
|
handler: async (args, ctx) => {
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
return ok(ctx.present({ data: result }, listPresentations$3(result)));
|
|
8014
|
-
} catch (error) {
|
|
8015
|
-
const mapped = mapPostgresOperationError(error);
|
|
8016
|
-
if (mapped) return notOk(mapped);
|
|
8017
|
-
throw error;
|
|
8018
|
-
}
|
|
7598
|
+
const { provider, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres list");
|
|
7599
|
+
const databases = sortDatabases(await provider.listDatabases({
|
|
7600
|
+
projectId,
|
|
7601
|
+
branchName: args.flags.branch,
|
|
7602
|
+
signal: ctx.signal
|
|
7603
|
+
}));
|
|
7604
|
+
const result = {
|
|
7605
|
+
projectId,
|
|
7606
|
+
projectName,
|
|
7607
|
+
branchName: args.flags.branch ?? null,
|
|
7608
|
+
databases
|
|
7609
|
+
};
|
|
7610
|
+
return ok(ctx.present({ data: result }, listPresentations$3(result)));
|
|
8019
7611
|
}
|
|
8020
7612
|
});
|
|
8021
7613
|
//#endregion
|
|
@@ -8119,21 +7711,15 @@ const postgresShowCommand = defineCommand({
|
|
|
8119
7711
|
},
|
|
8120
7712
|
needs: { credentials: true },
|
|
8121
7713
|
handler: async (args, ctx) => {
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
return ok(ctx.present({ data: result }, showPresentations$2(result)));
|
|
8132
|
-
} catch (error) {
|
|
8133
|
-
const mapped = mapPostgresOperationError(error);
|
|
8134
|
-
if (mapped) return notOk(mapped);
|
|
8135
|
-
throw error;
|
|
8136
|
-
}
|
|
7714
|
+
const { provider, target, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres show");
|
|
7715
|
+
const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
|
|
7716
|
+
const result = {
|
|
7717
|
+
projectId,
|
|
7718
|
+
projectName,
|
|
7719
|
+
database,
|
|
7720
|
+
connections: await provider.listConnections(database.id, { signal: ctx.signal })
|
|
7721
|
+
};
|
|
7722
|
+
return ok(ctx.present({ data: result }, showPresentations$2(result)));
|
|
8137
7723
|
}
|
|
8138
7724
|
});
|
|
8139
7725
|
//#endregion
|
|
@@ -8251,39 +7837,35 @@ const postgresUsageCommand = defineCommand({
|
|
|
8251
7837
|
},
|
|
8252
7838
|
needs: { credentials: true },
|
|
8253
7839
|
handler: async (args, ctx) => {
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
"
|
|
8260
|
-
"
|
|
8261
|
-
|
|
8262
|
-
"
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
]
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
}
|
|
8283
|
-
const mapped = mapPostgresOperationError(error);
|
|
8284
|
-
if (mapped) return notOk(mapped);
|
|
8285
|
-
throw error;
|
|
8286
|
-
}
|
|
7840
|
+
const from = parseUsageDate(args.flags.from, "--from", "start");
|
|
7841
|
+
const to = parseUsageDate(args.flags.to, "--to", "end");
|
|
7842
|
+
if (from && to && Date.parse(from) > Date.parse(to)) throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Invalid usage period", {
|
|
7843
|
+
why: "--from must not be later than --to.",
|
|
7844
|
+
nextActions: [{
|
|
7845
|
+
kind: "user-choice",
|
|
7846
|
+
label: "Pass a --from date that is on or before the --to date."
|
|
7847
|
+
}, {
|
|
7848
|
+
kind: "run-command",
|
|
7849
|
+
label: USAGE_PERIOD_EXAMPLE_COMMAND,
|
|
7850
|
+
command: USAGE_PERIOD_EXAMPLE_COMMAND
|
|
7851
|
+
}]
|
|
7852
|
+
});
|
|
7853
|
+
const { provider, target, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres usage");
|
|
7854
|
+
const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
|
|
7855
|
+
const usage = await provider.getUsage(database.id, {
|
|
7856
|
+
from,
|
|
7857
|
+
to,
|
|
7858
|
+
signal: ctx.signal
|
|
7859
|
+
});
|
|
7860
|
+
const result = {
|
|
7861
|
+
projectId,
|
|
7862
|
+
projectName,
|
|
7863
|
+
database,
|
|
7864
|
+
period: usage.period,
|
|
7865
|
+
metrics: usage.metrics,
|
|
7866
|
+
generatedAt: usage.generatedAt
|
|
7867
|
+
};
|
|
7868
|
+
return ok(ctx.present({ data: result }, usagePresentations(result)));
|
|
8287
7869
|
}
|
|
8288
7870
|
});
|
|
8289
7871
|
//#endregion
|
|
@@ -8933,17 +8515,6 @@ function localPinDiagnostics(warnings) {
|
|
|
8933
8515
|
nextActions: []
|
|
8934
8516
|
}));
|
|
8935
8517
|
}
|
|
8936
|
-
/** The legacy NextAction shape minus its `journey` field, which the engine
|
|
8937
|
-
* protocol does not carry. */
|
|
8938
|
-
function toNextActions(actions) {
|
|
8939
|
-
return actions.map((action) => ({
|
|
8940
|
-
kind: action.kind,
|
|
8941
|
-
label: action.label,
|
|
8942
|
-
...action.command ? { command: portCommandString(action.command) } : {},
|
|
8943
|
-
...action.commands ? { commands: action.commands.map(portCommandString) } : {},
|
|
8944
|
-
...action.reason ? { reason: action.reason } : {}
|
|
8945
|
-
}));
|
|
8946
|
-
}
|
|
8947
8518
|
function setupPresentations(result) {
|
|
8948
8519
|
return {
|
|
8949
8520
|
stdout: () => [],
|
|
@@ -8988,38 +8559,32 @@ const projectCreateCommand = defineCommand({
|
|
|
8988
8559
|
},
|
|
8989
8560
|
needs: { credentials: true },
|
|
8990
8561
|
handler: async (args, ctx) => {
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
fallbackFix: "Retry the command, or choose an existing Project with prisma project link <id-or-name>."
|
|
9010
|
-
});
|
|
8562
|
+
const workspace = await resolveActiveWorkspace(ctx);
|
|
8563
|
+
if (!isValidProjectSetupName(args.positionals.name)) throw projectSetupNameRequiredError("project create");
|
|
8564
|
+
const name = args.positionals.name.trim();
|
|
8565
|
+
const created = await createAppProvider(ctx.api).createProject({
|
|
8566
|
+
name,
|
|
8567
|
+
region: args.flags.region,
|
|
8568
|
+
signal: ctx.signal
|
|
8569
|
+
}).catch((error) => {
|
|
8570
|
+
/** A cancelled run is cancelled, not a failed creation. The
|
|
8571
|
+
* provider flattens the underlying AbortError into a plain
|
|
8572
|
+
* Error, which the engine would settle as a bug, so hand it
|
|
8573
|
+
* back its own abort reason and let it settle the run as
|
|
8574
|
+
* cancelled. */
|
|
8575
|
+
if (ctx.signal.aborted) throw ctx.signal.reason;
|
|
8576
|
+
throw projectCreateFailedError(error, name, workspace, {
|
|
8577
|
+
nextSteps: ["prisma project list", "prisma project link <id-or-name>"],
|
|
8578
|
+
permissionFix: "Grant the token permission to create Projects in this workspace, or link an existing Project.",
|
|
8579
|
+
fallbackFix: "Retry the command, or choose an existing Project with prisma project link <id-or-name>."
|
|
9011
8580
|
});
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
}
|
|
9017
|
-
|
|
9018
|
-
}
|
|
9019
|
-
const mapped = mapProjectOperationError(error);
|
|
9020
|
-
if (mapped) return notOk(mapped);
|
|
9021
|
-
throw error;
|
|
9022
|
-
}
|
|
8581
|
+
});
|
|
8582
|
+
const result = await bindDirectoryToProject(ctx, workspace, {
|
|
8583
|
+
id: created.id,
|
|
8584
|
+
name: created.name,
|
|
8585
|
+
...created.defaultRegion != null ? { defaultRegion: created.defaultRegion } : {}
|
|
8586
|
+
}, "created");
|
|
8587
|
+
return ok(ctx.present({ data: result }, setupPresentations(result)));
|
|
9023
8588
|
}
|
|
9024
8589
|
});
|
|
9025
8590
|
//#endregion
|
|
@@ -9072,34 +8637,56 @@ const projectDeleteCommand = defineCommand({
|
|
|
9072
8637
|
},
|
|
9073
8638
|
needs: { credentials: true },
|
|
9074
8639
|
handler: async (args, ctx) => {
|
|
9075
|
-
|
|
9076
|
-
|
|
9077
|
-
|
|
9078
|
-
|
|
9079
|
-
|
|
9080
|
-
|
|
9081
|
-
|
|
9082
|
-
|
|
9083
|
-
|
|
9084
|
-
|
|
9085
|
-
|
|
9086
|
-
|
|
9087
|
-
|
|
9088
|
-
|
|
9089
|
-
|
|
9090
|
-
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
|
|
9094
|
-
}, deletePresentations$2(result)));
|
|
9095
|
-
} catch (error) {
|
|
9096
|
-
const mapped = mapProjectOperationError(error);
|
|
9097
|
-
if (mapped) return notOk(mapped);
|
|
9098
|
-
throw error;
|
|
9099
|
-
}
|
|
8640
|
+
const workspace = await resolveActiveWorkspace(ctx);
|
|
8641
|
+
const projects = await listWorkspaceProjects$1(ctx);
|
|
8642
|
+
const project = toProjectSummary(resolveProjectForSetup(args.positionals.project.trim(), projects, workspace));
|
|
8643
|
+
await ctx.prompt.consent(CONSENT_QUESTION$1, { token: project.id });
|
|
8644
|
+
await createManagementProjectProvider(ctx.api).removeProject({
|
|
8645
|
+
projectId: project.id,
|
|
8646
|
+
signal: ctx.signal
|
|
8647
|
+
});
|
|
8648
|
+
const warnings = [];
|
|
8649
|
+
const result = {
|
|
8650
|
+
workspace,
|
|
8651
|
+
project,
|
|
8652
|
+
localPin: { cleared: await cleanupLocalPinForProject(legacyOperationContext(ctx), project.id, { onError: (message) => warnings.push(message) }) }
|
|
8653
|
+
};
|
|
8654
|
+
const diagnostics = localPinDiagnostics(warnings);
|
|
8655
|
+
return ok(ctx.present({
|
|
8656
|
+
data: result,
|
|
8657
|
+
diagnostics
|
|
8658
|
+
}, deletePresentations$2(result)));
|
|
9100
8659
|
}
|
|
9101
8660
|
});
|
|
9102
8661
|
//#endregion
|
|
8662
|
+
//#region src/lib/app/env-errors.ts
|
|
8663
|
+
/**
|
|
8664
|
+
* The structured errors the `project env` code paths raise, with the
|
|
8665
|
+
* registered PROJECT.* codes assigned at origin. This is the lowest
|
|
8666
|
+
* layer the env commands, controllers and parsers share, so the
|
|
8667
|
+
* parsers can raise without depending on the controllers.
|
|
8668
|
+
*/
|
|
8669
|
+
function userChoice(label) {
|
|
8670
|
+
return {
|
|
8671
|
+
kind: "user-choice",
|
|
8672
|
+
label
|
|
8673
|
+
};
|
|
8674
|
+
}
|
|
8675
|
+
function runCommand(command, reason) {
|
|
8676
|
+
return {
|
|
8677
|
+
kind: "run-command",
|
|
8678
|
+
label: command,
|
|
8679
|
+
command,
|
|
8680
|
+
...reason === void 0 ? {} : { reason }
|
|
8681
|
+
};
|
|
8682
|
+
}
|
|
8683
|
+
function envUsageError(summary, why, fix, commands = []) {
|
|
8684
|
+
return new CliStructuredError("PROJECT.USAGE_ERROR", summary, {
|
|
8685
|
+
why,
|
|
8686
|
+
nextActions: [userChoice(fix), ...commands.map((step) => runCommand(step))]
|
|
8687
|
+
});
|
|
8688
|
+
}
|
|
8689
|
+
//#endregion
|
|
9103
8690
|
//#region src/lib/app/env-config.ts
|
|
9104
8691
|
const VALID_ROLES = new Set(["production", "preview"]);
|
|
9105
8692
|
function positionalHint(command) {
|
|
@@ -9108,9 +8695,9 @@ function positionalHint(command) {
|
|
|
9108
8695
|
return "";
|
|
9109
8696
|
}
|
|
9110
8697
|
function resolveEnvScope(flags, options) {
|
|
9111
|
-
if (flags.roleName && flags.branchName) throw
|
|
8698
|
+
if (flags.roleName && flags.branchName) throw envUsageError(`prisma project env ${options.command} accepts either --role or --branch`, "--role targets a project-level config map; --branch targets a preview branch override.", "Pass exactly one scope flag.", [`prisma project env ${options.command} ${positionalHint(options.command)}--role preview`, `prisma project env ${options.command} ${positionalHint(options.command)}--branch feature/foo`]);
|
|
9112
8699
|
if (flags.roleName) {
|
|
9113
|
-
if (!VALID_ROLES.has(flags.roleName)) throw
|
|
8700
|
+
if (!VALID_ROLES.has(flags.roleName)) throw envUsageError(`Unknown role "${flags.roleName}"`, "--role accepts production or preview.", "Pass --role production or --role preview.", [`prisma project env ${options.command} --role production`, `prisma project env ${options.command} --role preview`]);
|
|
9114
8701
|
return {
|
|
9115
8702
|
kind: "role",
|
|
9116
8703
|
role: flags.roleName
|
|
@@ -9122,16 +8709,16 @@ function resolveEnvScope(flags, options) {
|
|
|
9122
8709
|
};
|
|
9123
8710
|
if (options.requireExplicit) {
|
|
9124
8711
|
const positional = positionalHint(options.command);
|
|
9125
|
-
throw
|
|
8712
|
+
throw envUsageError(`prisma project env ${options.command} requires --role or --branch`, "Writing without an explicit scope is rejected so the command never silently targets production.", "Pass --role production, --role preview, or --branch <git-name>.", [
|
|
9126
8713
|
`prisma project env ${options.command} ${positional}--role production`,
|
|
9127
8714
|
`prisma project env ${options.command} ${positional}--role preview`,
|
|
9128
8715
|
`prisma project env ${options.command} ${positional}--branch feature/foo`
|
|
9129
|
-
]
|
|
8716
|
+
]);
|
|
9130
8717
|
}
|
|
9131
8718
|
return null;
|
|
9132
8719
|
}
|
|
9133
8720
|
function parseKeyValuePositional(raw, command, env = process.env) {
|
|
9134
|
-
if (!raw) throw
|
|
8721
|
+
if (!raw) throw envUsageError(`prisma project env ${command} requires KEY=VALUE`, "No KEY=VALUE positional argument was supplied.", "Pass the variable as KEY=VALUE, e.g. STRIPE_KEY=sk_test_xxx.", [`prisma project env ${command} STRIPE_KEY=sk_test_xxx --role production`]);
|
|
9135
8722
|
const separatorIndex = raw.indexOf("=");
|
|
9136
8723
|
if (separatorIndex === -1) {
|
|
9137
8724
|
if (KEY_SHAPE.test(raw)) {
|
|
@@ -9141,14 +8728,14 @@ function parseKeyValuePositional(raw, command, env = process.env) {
|
|
|
9141
8728
|
key: raw,
|
|
9142
8729
|
value
|
|
9143
8730
|
};
|
|
9144
|
-
throw
|
|
8731
|
+
throw envUsageError(`Value for "${raw}" was not provided`, `No KEY=VALUE assignment was supplied, and ${raw} is not set in the current environment.`, "Pass KEY=VALUE or export the variable before running the command.", [`prisma project env ${command} ${raw}=value --role production`, `${raw}=value prisma project env ${command} ${raw} --role production`]);
|
|
9145
8732
|
}
|
|
9146
|
-
throw
|
|
8733
|
+
throw envUsageError(`KEY=VALUE argument is missing the = separator`, `"${raw}" does not contain an = character.`, "Pass the variable as KEY=VALUE, e.g. STRIPE_KEY=sk_test_xxx.", [`prisma project env ${command} STRIPE_KEY=sk_test_xxx --role production`]);
|
|
9147
8734
|
}
|
|
9148
8735
|
const key = raw.slice(0, separatorIndex);
|
|
9149
8736
|
const value = raw.slice(separatorIndex + 1);
|
|
9150
8737
|
validateKey(key, command);
|
|
9151
|
-
if (value.length === 0) throw
|
|
8738
|
+
if (value.length === 0) throw envUsageError(`KEY=VALUE argument has an empty value`, `"${raw}" has an empty value after the = separator.`, `Pass a non-empty value, or use prisma project env delete to delete a variable.`, [`prisma project env ${command} ${key}=value --role production`]);
|
|
9152
8739
|
return {
|
|
9153
8740
|
key,
|
|
9154
8741
|
value
|
|
@@ -9156,9 +8743,9 @@ function parseKeyValuePositional(raw, command, env = process.env) {
|
|
|
9156
8743
|
}
|
|
9157
8744
|
const KEY_SHAPE = /^[A-Z_][A-Z0-9_]*$/;
|
|
9158
8745
|
function validateKey(key, command) {
|
|
9159
|
-
if (key.length === 0) throw
|
|
9160
|
-
if (key.length > 256) throw
|
|
9161
|
-
if (!KEY_SHAPE.test(key)) throw
|
|
8746
|
+
if (key.length === 0) throw envUsageError(`Variable key cannot be empty`, "An empty key was passed.", "Pass an env-var key, e.g. STRIPE_KEY.", [`prisma project env ${command} STRIPE_KEY=value --role production`]);
|
|
8747
|
+
if (key.length > 256) throw envUsageError(`Variable key "${key}" exceeds the 256-character limit`, "Env-var keys are capped at 256 characters by the platform.", "Use a shorter key.");
|
|
8748
|
+
if (!KEY_SHAPE.test(key)) throw envUsageError(`Variable key "${key}" must match the POSIX env-var shape`, "Keys must start with an uppercase letter or underscore and contain only uppercase letters, digits, and underscores.", "Rename the key to match [A-Z_][A-Z0-9_]*.", [`prisma project env ${command} STRIPE_KEY=value --role production`]);
|
|
9162
8749
|
}
|
|
9163
8750
|
function formatScopeLabel(scope) {
|
|
9164
8751
|
if (scope.kind === "role") return scope.role;
|
|
@@ -9173,18 +8760,18 @@ async function readEnvFileAssignments(cwd, filePath, command) {
|
|
|
9173
8760
|
try {
|
|
9174
8761
|
contents = await readFile(resolvedPath, "utf8");
|
|
9175
8762
|
} catch (error) {
|
|
9176
|
-
throw
|
|
8763
|
+
throw envUsageError(`Failed to read env file "${filePath}"`, error instanceof Error ? error.message : "The file could not be read.", "Pass a readable dotenv file path.", [`prisma project env ${command} --file .env --role preview`]);
|
|
9177
8764
|
}
|
|
9178
8765
|
return parseEnvFileContents(contents, filePath, command);
|
|
9179
8766
|
}
|
|
9180
8767
|
function parseEnvFileContents(contents, filePath, command) {
|
|
9181
8768
|
const parsedKeys = extractParsedKeys(contents);
|
|
9182
|
-
if (parsedKeys.length === 0) throw
|
|
8769
|
+
if (parsedKeys.length === 0) throw envUsageError(`No environment variables found in "${filePath}"`, "The file does not contain any KEY=VALUE assignments.", "Pass a dotenv file with at least one non-empty variable.");
|
|
9183
8770
|
const seen = /* @__PURE__ */ new Map();
|
|
9184
8771
|
for (const entry of parsedKeys) {
|
|
9185
8772
|
validateEnvFileKey(entry.key, entry.line, filePath, command);
|
|
9186
8773
|
const firstLine = seen.get(entry.key);
|
|
9187
|
-
if (firstLine !== void 0) throw
|
|
8774
|
+
if (firstLine !== void 0) throw envUsageError(`Duplicate environment variable "${entry.key}" in "${filePath}"`, `Lines ${firstLine} and ${entry.line} both define ${entry.key}.`, "Keep one assignment for each key before importing the file.");
|
|
9188
8775
|
seen.set(entry.key, entry.line);
|
|
9189
8776
|
}
|
|
9190
8777
|
const parsedValues = parse(contents);
|
|
@@ -9192,7 +8779,7 @@ function parseEnvFileContents(contents, filePath, command) {
|
|
|
9192
8779
|
const value = parsedValues[key];
|
|
9193
8780
|
if (typeof value !== "string" || value.length === 0) {
|
|
9194
8781
|
const line = seen.get(key);
|
|
9195
|
-
throw
|
|
8782
|
+
throw envUsageError(`Environment variable "${key}" in "${filePath}" has an empty value`, line === void 0 ? `${key} has an empty value.` : `Line ${line} defines ${key} with an empty value.`, "Pass a non-empty value, or omit the key from the file.");
|
|
9196
8783
|
}
|
|
9197
8784
|
return {
|
|
9198
8785
|
key,
|
|
@@ -9228,7 +8815,7 @@ function validateEnvFileKey(key, line, filePath, command) {
|
|
|
9228
8815
|
validateKey(key, command);
|
|
9229
8816
|
} catch (error) {
|
|
9230
8817
|
const reason = error instanceof Error && error.message.length > 0 ? error.message : "Invalid environment variable key.";
|
|
9231
|
-
throw
|
|
8818
|
+
throw envUsageError(`Invalid environment variable "${key}" in "${filePath}"`, `Line ${line}: ${reason}`, "Use a valid env-var key and retry the import.");
|
|
9232
8819
|
}
|
|
9233
8820
|
}
|
|
9234
8821
|
function hasClosingQuote(value, quote, startIndex) {
|
|
@@ -9277,15 +8864,18 @@ function apiCallError(summary, response, error) {
|
|
|
9277
8864
|
const apiCode = error?.error?.code;
|
|
9278
8865
|
const apiMessage = error?.error?.message;
|
|
9279
8866
|
const apiHint = error?.error?.hint;
|
|
9280
|
-
if (status === 401 || status === 403) return
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
8867
|
+
if (status === 401 || status === 403) return new CliStructuredError("PROJECT.ENV_API_ERROR", summary, {
|
|
8868
|
+
why: "The Management API rejected the request as unauthorized or forbidden.",
|
|
8869
|
+
meta: { status },
|
|
8870
|
+
nextActions: [runCommand("prisma auth login")]
|
|
8871
|
+
});
|
|
8872
|
+
return new CliStructuredError("PROJECT.ENV_API_ERROR", summary, {
|
|
9285
8873
|
why: apiMessage ?? `The Management API returned status ${status || "unknown"}.`,
|
|
9286
|
-
|
|
9287
|
-
|
|
9288
|
-
|
|
8874
|
+
...status || apiCode !== void 0 ? { meta: {
|
|
8875
|
+
...status ? { status } : {},
|
|
8876
|
+
...apiCode !== void 0 ? { apiCode } : {}
|
|
8877
|
+
} } : {},
|
|
8878
|
+
nextActions: [userChoice(apiHint ?? "Re-run with --log-level verbose for the underlying API response details.")]
|
|
9289
8879
|
});
|
|
9290
8880
|
}
|
|
9291
8881
|
function formatDescriptorLabel(scope) {
|
|
@@ -9296,15 +8886,15 @@ function formatDescriptorLabel(scope) {
|
|
|
9296
8886
|
//#endregion
|
|
9297
8887
|
//#region src/controllers/app-env.ts
|
|
9298
8888
|
function resolveEnvWriteSource(rawAssignment, filePath, command) {
|
|
9299
|
-
if (filePath !== void 0 && rawAssignment !== void 0) throw
|
|
8889
|
+
if (filePath !== void 0 && rawAssignment !== void 0) throw envUsageError(`prisma project env ${command} accepts either KEY=VALUE or --file`, "The command received both a positional assignment and a dotenv file path.", "Pass one input source.", [`prisma project env ${command} KEY=value --role preview`, `prisma project env ${command} --file .env --role preview`]);
|
|
9300
8890
|
if (filePath !== void 0) {
|
|
9301
|
-
if (filePath.length === 0) throw
|
|
8891
|
+
if (filePath.length === 0) throw envUsageError(`prisma project env ${command} --file requires a path`, "The --file flag was passed without a file path.", "Pass a readable dotenv file path.", [`prisma project env ${command} --file .env --role preview`]);
|
|
9302
8892
|
return {
|
|
9303
8893
|
kind: "file",
|
|
9304
8894
|
filePath
|
|
9305
8895
|
};
|
|
9306
8896
|
}
|
|
9307
|
-
if (rawAssignment === void 0) throw
|
|
8897
|
+
if (rawAssignment === void 0) throw envUsageError(`prisma project env ${command} requires KEY=VALUE or --file`, "No environment variable input was supplied.", "Pass a single KEY=VALUE assignment or a dotenv file path.", [`prisma project env ${command} KEY=value --role preview`, `prisma project env ${command} --file .env --role preview`]);
|
|
9308
8898
|
return {
|
|
9309
8899
|
kind: "single",
|
|
9310
8900
|
rawAssignment
|
|
@@ -9334,14 +8924,9 @@ async function resolveScopeToApi(client, projectId, scope, options) {
|
|
|
9334
8924
|
}
|
|
9335
8925
|
};
|
|
9336
8926
|
const branch = options.createBranchIfMissing ? await resolveOrCreateBranch(client, projectId, scope.branchName, options.signal) : await resolveExistingBranch(client, projectId, scope.branchName, options.signal);
|
|
9337
|
-
if (branch.role === "production") throw new
|
|
9338
|
-
code: "ENV_BRANCH_SCOPE_IS_PRODUCTION",
|
|
9339
|
-
domain: "app",
|
|
9340
|
-
summary: `Branch "${scope.branchName}" is the production branch`,
|
|
8927
|
+
if (branch.role === "production") throw new CliStructuredError("PROJECT.ENV_BRANCH_SCOPE_IS_PRODUCTION", `Branch "${scope.branchName}" is the production branch`, {
|
|
9341
8928
|
why: "Production variables are project-level only; branch overrides apply to preview branches.",
|
|
9342
|
-
|
|
9343
|
-
exitCode: 1,
|
|
9344
|
-
nextSteps: ["prisma project env list --role production"]
|
|
8929
|
+
nextActions: [userChoice("Use --role production for the production branch."), runCommand("prisma project env list --role production")]
|
|
9345
8930
|
});
|
|
9346
8931
|
return {
|
|
9347
8932
|
scope,
|
|
@@ -9420,28 +9005,18 @@ async function listBranchesByName(client, projectId, branchName, signal) {
|
|
|
9420
9005
|
}
|
|
9421
9006
|
async function resolveExistingBranch(client, projectId, branchName, signal) {
|
|
9422
9007
|
const branch = (await listBranchesByName(client, projectId, branchName, signal))[0];
|
|
9423
|
-
if (!branch) throw new
|
|
9424
|
-
code: "ENV_BRANCH_NOT_FOUND",
|
|
9425
|
-
domain: "app",
|
|
9426
|
-
summary: `Branch "${branchName}" not found`,
|
|
9008
|
+
if (!branch) throw new CliStructuredError("PROJECT.ENV_BRANCH_NOT_FOUND", `Branch "${branchName}" not found`, {
|
|
9427
9009
|
why: "Branch update, list, and delete commands only target existing preview branches.",
|
|
9428
|
-
|
|
9429
|
-
exitCode: 1,
|
|
9430
|
-
nextSteps: [`prisma project env add KEY=value --branch ${branchName}`]
|
|
9010
|
+
nextActions: [userChoice("Create the branch by deploying it, or use `project env add --branch` to create its first override."), runCommand(`prisma project env add KEY=value --branch ${branchName}`)]
|
|
9431
9011
|
});
|
|
9432
9012
|
return branch;
|
|
9433
9013
|
}
|
|
9434
9014
|
async function resolveOrCreateBranch(client, projectId, branchName, signal) {
|
|
9435
9015
|
const existing = (await listBranchesByName(client, projectId, branchName, signal))[0];
|
|
9436
9016
|
if (existing) return existing;
|
|
9437
|
-
if (!await projectHasDefaultBranch(client, projectId, signal)) throw new
|
|
9438
|
-
code: "ENV_BRANCH_CREATE_REQUIRES_DEFAULT_BRANCH",
|
|
9439
|
-
domain: "app",
|
|
9440
|
-
summary: `Cannot create branch "${branchName}" from project env`,
|
|
9017
|
+
if (!await projectHasDefaultBranch(client, projectId, signal)) throw new CliStructuredError("PROJECT.ENV_BRANCH_CREATE_REQUIRES_DEFAULT_BRANCH", `Cannot create branch "${branchName}" from project env`, {
|
|
9441
9018
|
why: "Creating the first branch would make it the project default, but branch overrides are preview-only.",
|
|
9442
|
-
|
|
9443
|
-
exitCode: 1,
|
|
9444
|
-
nextSteps: ["prisma git connect <repository-url>"]
|
|
9019
|
+
nextActions: [userChoice("Create or deploy the default branch first, then add the branch override."), runCommand("prisma git connect <repository-url>")]
|
|
9445
9020
|
});
|
|
9446
9021
|
const { data, error, response } = await client.POST("/v1/projects/{projectId}/branches", {
|
|
9447
9022
|
params: { path: { projectId } },
|
|
@@ -9529,18 +9104,13 @@ function materializeEffectiveRows(rows, resolved) {
|
|
|
9529
9104
|
async function runEnvAddFile(context, client, projectId, resolved, filePath, assignments, verboseContext) {
|
|
9530
9105
|
const existing = await findVariablesByNaturalKey(client, projectId, assignments.map((assignment) => assignment.key), resolved, context.runtime.signal);
|
|
9531
9106
|
const existingKeys = assignments.map((assignment) => assignment.key).filter((key) => existing.has(key));
|
|
9532
|
-
if (existingKeys.length > 0) throw new
|
|
9533
|
-
code: "ENV_VARIABLE_ALREADY_EXISTS",
|
|
9534
|
-
domain: "app",
|
|
9535
|
-
summary: `${existingKeys.length} environment variable(s) already exist in ${formatScopeLabel(resolved.scope)}`,
|
|
9107
|
+
if (existingKeys.length > 0) throw new CliStructuredError("PROJECT.ENV_VARIABLE_ALREADY_EXISTS", `${existingKeys.length} environment variable(s) already exist in ${formatScopeLabel(resolved.scope)}`, {
|
|
9536
9108
|
why: `Existing keys: ${formatKeyList(existingKeys)}.`,
|
|
9537
|
-
|
|
9538
|
-
|
|
9539
|
-
nextSteps: splitFileNextSteps(filePath, resolved.scope, {
|
|
9109
|
+
meta: { keys: existingKeys },
|
|
9110
|
+
nextActions: [userChoice("Split the input file by key state: update existing keys and add new keys separately."), ...splitFileActions(filePath, resolved.scope, {
|
|
9540
9111
|
existingKeys,
|
|
9541
9112
|
first: "update-existing"
|
|
9542
|
-
})
|
|
9543
|
-
meta: { keys: existingKeys }
|
|
9113
|
+
})]
|
|
9544
9114
|
});
|
|
9545
9115
|
const warnings = await missingPreviewDefaultWarnings(client, projectId, resolved.scope, assignments.map((assignment) => assignment.key), context.runtime.signal);
|
|
9546
9116
|
const variables = [];
|
|
@@ -9572,25 +9142,19 @@ async function runEnvAddFile(context, client, projectId, resolved, filePath, ass
|
|
|
9572
9142
|
count: variables.length
|
|
9573
9143
|
}
|
|
9574
9144
|
},
|
|
9575
|
-
warnings
|
|
9576
|
-
nextSteps: []
|
|
9145
|
+
warnings
|
|
9577
9146
|
};
|
|
9578
9147
|
}
|
|
9579
9148
|
async function runEnvUpdateFile(context, client, projectId, resolved, filePath, assignments, verboseContext) {
|
|
9580
9149
|
const existing = await findVariablesByNaturalKey(client, projectId, assignments.map((assignment) => assignment.key), resolved, context.runtime.signal);
|
|
9581
9150
|
const missingKeys = assignments.map((assignment) => assignment.key).filter((key) => !existing.has(key));
|
|
9582
|
-
if (missingKeys.length > 0) throw new
|
|
9583
|
-
code: "ENV_VARIABLE_NOT_FOUND",
|
|
9584
|
-
domain: "app",
|
|
9585
|
-
summary: `${missingKeys.length} environment variable(s) not found in ${formatScopeLabel(resolved.scope)}`,
|
|
9151
|
+
if (missingKeys.length > 0) throw new CliStructuredError("PROJECT.ENV_VARIABLE_NOT_FOUND", `${missingKeys.length} environment variable(s) not found in ${formatScopeLabel(resolved.scope)}`, {
|
|
9586
9152
|
why: `Missing keys: ${formatKeyList(missingKeys)}.`,
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
nextSteps: splitFileNextSteps(filePath, resolved.scope, {
|
|
9153
|
+
meta: { keys: missingKeys },
|
|
9154
|
+
nextActions: [userChoice("Split the input file by key state: add missing keys and update existing keys separately."), ...splitFileActions(filePath, resolved.scope, {
|
|
9590
9155
|
missingKeys,
|
|
9591
9156
|
first: "add-missing"
|
|
9592
|
-
})
|
|
9593
|
-
meta: { keys: missingKeys }
|
|
9157
|
+
})]
|
|
9594
9158
|
});
|
|
9595
9159
|
const variables = [];
|
|
9596
9160
|
for (const assignment of assignments) {
|
|
@@ -9620,8 +9184,7 @@ async function runEnvUpdateFile(context, client, projectId, resolved, filePath,
|
|
|
9620
9184
|
count: variables.length
|
|
9621
9185
|
}
|
|
9622
9186
|
},
|
|
9623
|
-
warnings: []
|
|
9624
|
-
nextSteps: []
|
|
9187
|
+
warnings: []
|
|
9625
9188
|
};
|
|
9626
9189
|
}
|
|
9627
9190
|
async function findVariablesByNaturalKey(client, projectId, keys, resolved, signal) {
|
|
@@ -9656,20 +9219,20 @@ async function missingPreviewDefaultWarnings(client, projectId, scope, keys, sig
|
|
|
9656
9219
|
}
|
|
9657
9220
|
function envFileApplyFailedError(command, filePath, scope, failedKey, writtenVariables, error) {
|
|
9658
9221
|
const writtenKeys = writtenVariables.map((variable) => variable.key);
|
|
9659
|
-
const cause = error instanceof
|
|
9660
|
-
return new
|
|
9661
|
-
code: "ENV_FILE_APPLY_FAILED",
|
|
9662
|
-
domain: "app",
|
|
9663
|
-
summary: `Failed to ${command} "${failedKey}" from "${filePath}"`,
|
|
9222
|
+
const cause = error instanceof Error ? error.message : "Unknown error.";
|
|
9223
|
+
return new CliStructuredError("PROJECT.ENV_FILE_APPLY_FAILED", `Failed to ${command} "${failedKey}" from "${filePath}"`, {
|
|
9664
9224
|
why: writtenKeys.length === 0 ? `No variables were written before ${failedKey} failed. Cause: ${cause}` : `Written keys before failure: ${formatKeyList(writtenKeys)}. Cause: ${cause}`,
|
|
9665
|
-
fix: "Inspect the target scope, then retry the remaining keys once the API issue is resolved.",
|
|
9666
|
-
exitCode: 1,
|
|
9667
|
-
nextSteps: [`prisma project env list ${formatScopeFlag(scope)}`, retryStepForApplyFailure(command, filePath, scope, writtenKeys)],
|
|
9668
9225
|
meta: {
|
|
9669
9226
|
file: filePath,
|
|
9670
9227
|
failedKey,
|
|
9671
9228
|
writtenKeys
|
|
9672
|
-
}
|
|
9229
|
+
},
|
|
9230
|
+
cause: error,
|
|
9231
|
+
nextActions: [
|
|
9232
|
+
userChoice("Inspect the target scope, then retry the remaining keys once the API issue is resolved."),
|
|
9233
|
+
runCommand(`prisma project env list ${formatScopeFlag(scope)}`),
|
|
9234
|
+
runCommand(retryStepForApplyFailure(command, filePath, scope, writtenKeys))
|
|
9235
|
+
]
|
|
9673
9236
|
});
|
|
9674
9237
|
}
|
|
9675
9238
|
function retryStepForApplyFailure(command, filePath, scope, writtenKeys) {
|
|
@@ -9677,22 +9240,13 @@ function retryStepForApplyFailure(command, filePath, scope, writtenKeys) {
|
|
|
9677
9240
|
if (writtenKeys.length === 0) return `prisma project env add --file ${filePath} ${formatScopeFlag(scope)}`;
|
|
9678
9241
|
return `prisma project env add --file <remaining.env> ${formatScopeFlag(scope)}`;
|
|
9679
9242
|
}
|
|
9680
|
-
|
|
9243
|
+
/** Each command carries the key list it applies to as its reason. */
|
|
9244
|
+
function splitFileActions(filePath, scope, options) {
|
|
9681
9245
|
const scopeFlag = formatScopeFlag(scope);
|
|
9682
9246
|
const existingFile = `${filePath}.existing`;
|
|
9683
9247
|
const newFile = `${filePath}.new`;
|
|
9684
|
-
if (options.first === "update-existing") return [
|
|
9685
|
-
|
|
9686
|
-
`prisma project env update --file ${existingFile} ${scopeFlag}`,
|
|
9687
|
-
"# new keys only",
|
|
9688
|
-
`prisma project env add --file ${newFile} ${scopeFlag}`
|
|
9689
|
-
];
|
|
9690
|
-
return [
|
|
9691
|
-
`# missing keys: ${formatKeyList(options.missingKeys)}`,
|
|
9692
|
-
`prisma project env add --file ${newFile} ${scopeFlag}`,
|
|
9693
|
-
"# existing keys only",
|
|
9694
|
-
`prisma project env update --file ${existingFile} ${scopeFlag}`
|
|
9695
|
-
];
|
|
9248
|
+
if (options.first === "update-existing") return [runCommand(`prisma project env update --file ${existingFile} ${scopeFlag}`, `existing keys: ${formatKeyList(options.existingKeys)}`), runCommand(`prisma project env add --file ${newFile} ${scopeFlag}`, "new keys only")];
|
|
9249
|
+
return [runCommand(`prisma project env add --file ${newFile} ${scopeFlag}`, `missing keys: ${formatKeyList(options.missingKeys)}`), runCommand(`prisma project env update --file ${existingFile} ${scopeFlag}`, "existing keys only")];
|
|
9696
9250
|
}
|
|
9697
9251
|
function formatKeyList(keys) {
|
|
9698
9252
|
return keys.map((key) => `"${key}"`).join(", ");
|
|
@@ -9769,7 +9323,7 @@ function requireEnvScope(flags, command) {
|
|
|
9769
9323
|
requireExplicit: true,
|
|
9770
9324
|
command
|
|
9771
9325
|
});
|
|
9772
|
-
if (!scope) throw
|
|
9326
|
+
if (!scope) throw envUsageError(`prisma project env ${command} requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [`prisma project env ${command} KEY=value --role production`]);
|
|
9773
9327
|
return scope;
|
|
9774
9328
|
}
|
|
9775
9329
|
/** Workspace, pinned project and the API scope every env write needs. */
|
|
@@ -9923,74 +9477,63 @@ const projectEnvAddCommand = defineCommand({
|
|
|
9923
9477
|
},
|
|
9924
9478
|
needs: { credentials: true },
|
|
9925
9479
|
handler: async (args, ctx) => {
|
|
9926
|
-
|
|
9927
|
-
|
|
9928
|
-
|
|
9929
|
-
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
const written = await runEnvAddFile(legacyOperationContext(ctx), ctx.api, projectId, resolved, input.filePath, input.assignments, verboseContext);
|
|
9933
|
-
const result = {
|
|
9934
|
-
projectId,
|
|
9935
|
-
scope: resolved.descriptor,
|
|
9936
|
-
variables: written.result.variables,
|
|
9937
|
-
file: written.result.file
|
|
9938
|
-
};
|
|
9939
|
-
return ok(ctx.present({
|
|
9940
|
-
data: result,
|
|
9941
|
-
diagnostics: previewDefaultDiagnostics(written.warnings)
|
|
9942
|
-
}, fileWritePresentations({
|
|
9943
|
-
title: "Setting new environment variables from file.",
|
|
9944
|
-
emptyMessage: "No environment variables imported.",
|
|
9945
|
-
scope: result.scope,
|
|
9946
|
-
filePath: result.file.path,
|
|
9947
|
-
variables: result.variables
|
|
9948
|
-
}, result)));
|
|
9949
|
-
}
|
|
9950
|
-
if (await findVariableByNaturalKey(ctx.api, projectId, input.key, resolved, ctx.signal)) throw new CliError({
|
|
9951
|
-
code: "ENV_VARIABLE_ALREADY_EXISTS",
|
|
9952
|
-
domain: "app",
|
|
9953
|
-
summary: `Variable "${input.key}" already exists in ${formatScopeLabel(scope)}`,
|
|
9954
|
-
why: "A variable with this key already exists in the targeted scope.",
|
|
9955
|
-
fix: "Use `prisma project env update` to change an existing variable's value.",
|
|
9956
|
-
exitCode: 1,
|
|
9957
|
-
nextSteps: [`prisma project env update ${input.key}=<new-value> ${formatScopeFlag$1(scope)}`]
|
|
9958
|
-
});
|
|
9959
|
-
const warnings = scope.kind === "branch" && !await findVariableByNaturalKey(ctx.api, projectId, input.key, {
|
|
9960
|
-
descriptor: {
|
|
9961
|
-
kind: "role",
|
|
9962
|
-
role: "preview"
|
|
9963
|
-
},
|
|
9964
|
-
apiTarget: {
|
|
9965
|
-
class: "preview",
|
|
9966
|
-
branchId: null
|
|
9967
|
-
}
|
|
9968
|
-
}, ctx.signal) ? [`Variable "${input.key}" does not exist in preview. It will only exist on ${formatScopeLabel(scope)}.`] : [];
|
|
9969
|
-
const { data, error, response } = await ctx.api.POST("/v1/environment-variables", {
|
|
9970
|
-
body: {
|
|
9971
|
-
projectId,
|
|
9972
|
-
class: resolved.apiTarget.class,
|
|
9973
|
-
...resolved.apiTarget.branchId !== null ? { branchId: resolved.apiTarget.branchId } : {},
|
|
9974
|
-
key: input.key,
|
|
9975
|
-
value: input.value
|
|
9976
|
-
},
|
|
9977
|
-
signal: ctx.signal
|
|
9978
|
-
});
|
|
9979
|
-
if (error || !data) throw apiCallError(`Failed to add ${input.key}`, response, error);
|
|
9480
|
+
const source = resolveEnvWriteSource(args.positionals.assignment, args.flags.file, "add");
|
|
9481
|
+
const scope = requireEnvScope(args.flags, "add");
|
|
9482
|
+
const input = await resolveEnvWriteInput(legacyOperationContext(ctx), source, "add");
|
|
9483
|
+
const { projectId, verboseContext, resolved } = await resolveEnvTarget(ctx, args.flags, scope, "project env add", true);
|
|
9484
|
+
if (input.kind === "file") {
|
|
9485
|
+
const written = await runEnvAddFile(legacyOperationContext(ctx), ctx.api, projectId, resolved, input.filePath, input.assignments, verboseContext);
|
|
9980
9486
|
const result = {
|
|
9981
9487
|
projectId,
|
|
9982
9488
|
scope: resolved.descriptor,
|
|
9983
|
-
|
|
9489
|
+
variables: written.result.variables,
|
|
9490
|
+
file: written.result.file
|
|
9984
9491
|
};
|
|
9985
9492
|
return ok(ctx.present({
|
|
9986
9493
|
data: result,
|
|
9987
|
-
diagnostics: previewDefaultDiagnostics(warnings)
|
|
9988
|
-
},
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
|
|
9993
|
-
|
|
9494
|
+
diagnostics: previewDefaultDiagnostics(written.warnings)
|
|
9495
|
+
}, fileWritePresentations({
|
|
9496
|
+
title: "Setting new environment variables from file.",
|
|
9497
|
+
emptyMessage: "No environment variables imported.",
|
|
9498
|
+
scope: result.scope,
|
|
9499
|
+
filePath: result.file.path,
|
|
9500
|
+
variables: result.variables
|
|
9501
|
+
}, result)));
|
|
9502
|
+
}
|
|
9503
|
+
if (await findVariableByNaturalKey(ctx.api, projectId, input.key, resolved, ctx.signal)) throw new CliStructuredError("PROJECT.ENV_VARIABLE_ALREADY_EXISTS", `Variable "${input.key}" already exists in ${formatScopeLabel(scope)}`, {
|
|
9504
|
+
why: "A variable with this key already exists in the targeted scope.",
|
|
9505
|
+
nextActions: [userChoice("Use `prisma project env update` to change an existing variable's value."), runCommand(`prisma project env update ${input.key}=<new-value> ${formatScopeFlag$1(scope)}`)]
|
|
9506
|
+
});
|
|
9507
|
+
const warnings = scope.kind === "branch" && !await findVariableByNaturalKey(ctx.api, projectId, input.key, {
|
|
9508
|
+
descriptor: {
|
|
9509
|
+
kind: "role",
|
|
9510
|
+
role: "preview"
|
|
9511
|
+
},
|
|
9512
|
+
apiTarget: {
|
|
9513
|
+
class: "preview",
|
|
9514
|
+
branchId: null
|
|
9515
|
+
}
|
|
9516
|
+
}, ctx.signal) ? [`Variable "${input.key}" does not exist in preview. It will only exist on ${formatScopeLabel(scope)}.`] : [];
|
|
9517
|
+
const { data, error, response } = await ctx.api.POST("/v1/environment-variables", {
|
|
9518
|
+
body: {
|
|
9519
|
+
projectId,
|
|
9520
|
+
class: resolved.apiTarget.class,
|
|
9521
|
+
...resolved.apiTarget.branchId !== null ? { branchId: resolved.apiTarget.branchId } : {},
|
|
9522
|
+
key: input.key,
|
|
9523
|
+
value: input.value
|
|
9524
|
+
},
|
|
9525
|
+
signal: ctx.signal
|
|
9526
|
+
});
|
|
9527
|
+
if (error || !data) throw apiCallError(`Failed to add ${input.key}`, response, error);
|
|
9528
|
+
const result = {
|
|
9529
|
+
projectId,
|
|
9530
|
+
scope: resolved.descriptor,
|
|
9531
|
+
variable: toMetadata(data.data, resolved.descriptor)
|
|
9532
|
+
};
|
|
9533
|
+
return ok(ctx.present({
|
|
9534
|
+
data: result,
|
|
9535
|
+
diagnostics: previewDefaultDiagnostics(warnings)
|
|
9536
|
+
}, singlePresentations$1(result)));
|
|
9994
9537
|
}
|
|
9995
9538
|
});
|
|
9996
9539
|
//#endregion
|
|
@@ -10047,36 +9590,25 @@ const projectEnvDeleteCommand = defineCommand({
|
|
|
10047
9590
|
},
|
|
10048
9591
|
needs: { credentials: true },
|
|
10049
9592
|
handler: async (args, ctx) => {
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
|
|
10058
|
-
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
const result = {
|
|
10070
|
-
projectId,
|
|
10071
|
-
scope: resolved.descriptor,
|
|
10072
|
-
key
|
|
10073
|
-
};
|
|
10074
|
-
return ok(ctx.present({ data: result }, deletePresentations$1(result)));
|
|
10075
|
-
} catch (error) {
|
|
10076
|
-
const mapped = mapProjectOperationError(error);
|
|
10077
|
-
if (mapped) return notOk(mapped);
|
|
10078
|
-
throw error;
|
|
10079
|
-
}
|
|
9593
|
+
const key = args.positionals.key;
|
|
9594
|
+
const scope = requireEnvScope(args.flags, "delete");
|
|
9595
|
+
const { projectId, resolved } = await resolveEnvTarget(ctx, args.flags, scope, "project env delete", false);
|
|
9596
|
+
const existing = await findVariableByNaturalKey(ctx.api, projectId, key, resolved, ctx.signal);
|
|
9597
|
+
if (!existing) throw new CliStructuredError("PROJECT.ENV_VARIABLE_NOT_FOUND", `Variable "${key}" not found in ${formatScopeLabel(scope)}`, {
|
|
9598
|
+
why: "No variable with this key exists in the targeted scope, so there is nothing to delete.",
|
|
9599
|
+
nextActions: [userChoice("Run prisma project env list with the same scope to see the available variables."), runCommand(`prisma project env list ${formatScopeFlag$1(scope)}`)]
|
|
9600
|
+
});
|
|
9601
|
+
const { error, response } = await ctx.api.DELETE("/v1/environment-variables/{envVarId}", {
|
|
9602
|
+
params: { path: { envVarId: existing.id } },
|
|
9603
|
+
signal: ctx.signal
|
|
9604
|
+
});
|
|
9605
|
+
if (error) throw apiCallError(`Failed to delete ${key}`, response, error);
|
|
9606
|
+
const result = {
|
|
9607
|
+
projectId,
|
|
9608
|
+
scope: resolved.descriptor,
|
|
9609
|
+
key
|
|
9610
|
+
};
|
|
9611
|
+
return ok(ctx.present({ data: result }, deletePresentations$1(result)));
|
|
10080
9612
|
}
|
|
10081
9613
|
});
|
|
10082
9614
|
//#endregion
|
|
@@ -10143,33 +9675,27 @@ const projectEnvListCommand = defineCommand({
|
|
|
10143
9675
|
},
|
|
10144
9676
|
needs: { credentials: true },
|
|
10145
9677
|
handler: async (args, ctx) => {
|
|
10146
|
-
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
return ok(ctx.present({ data: result }, listPresentations$2(result, formatScopeFlag$1(resolved.addScope))));
|
|
10168
|
-
} catch (error) {
|
|
10169
|
-
const mapped = mapProjectOperationError(error);
|
|
10170
|
-
if (mapped) return notOk(mapped);
|
|
10171
|
-
throw error;
|
|
10172
|
-
}
|
|
9678
|
+
const explicit = resolveEnvScope({
|
|
9679
|
+
roleName: args.flags.role,
|
|
9680
|
+
branchName: args.flags.branch
|
|
9681
|
+
}, {
|
|
9682
|
+
requireExplicit: false,
|
|
9683
|
+
command: "list"
|
|
9684
|
+
});
|
|
9685
|
+
const projectId = (await resolvePinnedProject(ctx, await resolveActiveWorkspace(ctx), args.flags.project, "project env list")).project.id;
|
|
9686
|
+
const resolved = await resolveListScopeToApi(ctx.api, projectId, explicit ?? void 0, { signal: ctx.signal });
|
|
9687
|
+
const rows = resolved.kind === "scoped" ? await listVariables(ctx.api, projectId, {
|
|
9688
|
+
scope: resolved.addScope,
|
|
9689
|
+
descriptor: resolved.descriptor,
|
|
9690
|
+
apiTarget: resolved.apiTarget
|
|
9691
|
+
}, ctx.signal) : await listOverviewVariables(ctx.api, projectId, ctx.signal);
|
|
9692
|
+
const result = {
|
|
9693
|
+
projectId,
|
|
9694
|
+
scope: resolved.descriptor,
|
|
9695
|
+
target: resolved.target,
|
|
9696
|
+
variables: rows.map((row) => toMetadata(row, resolved.descriptor))
|
|
9697
|
+
};
|
|
9698
|
+
return ok(ctx.present({ data: result }, listPresentations$2(result, formatScopeFlag$1(resolved.addScope))));
|
|
10173
9699
|
}
|
|
10174
9700
|
});
|
|
10175
9701
|
//#endregion
|
|
@@ -10215,54 +9741,43 @@ const projectEnvUpdateCommand = defineCommand({
|
|
|
10215
9741
|
},
|
|
10216
9742
|
needs: { credentials: true },
|
|
10217
9743
|
handler: async (args, ctx) => {
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
|
|
10221
|
-
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
const written = await runEnvUpdateFile(legacyOperationContext(ctx), ctx.api, projectId, resolved, input.filePath, input.assignments, verboseContext);
|
|
10225
|
-
const result = {
|
|
10226
|
-
projectId,
|
|
10227
|
-
scope: resolved.descriptor,
|
|
10228
|
-
variables: written.result.variables,
|
|
10229
|
-
file: written.result.file
|
|
10230
|
-
};
|
|
10231
|
-
return ok(ctx.present({ data: result }, fileWritePresentations({
|
|
10232
|
-
title: "Replacing environment variable values from file.",
|
|
10233
|
-
emptyMessage: "No environment variables updated.",
|
|
10234
|
-
scope: result.scope,
|
|
10235
|
-
filePath: result.file.path,
|
|
10236
|
-
variables: result.variables
|
|
10237
|
-
}, result)));
|
|
10238
|
-
}
|
|
10239
|
-
const existing = await findVariableByNaturalKey(ctx.api, projectId, input.key, resolved, ctx.signal);
|
|
10240
|
-
if (!existing) throw new CliError({
|
|
10241
|
-
code: "ENV_VARIABLE_NOT_FOUND",
|
|
10242
|
-
domain: "app",
|
|
10243
|
-
summary: `Variable "${input.key}" not found in ${formatScopeLabel(scope)}`,
|
|
10244
|
-
why: "No variable with this key exists in the targeted scope.",
|
|
10245
|
-
fix: "Use `prisma project env add` to create a new variable.",
|
|
10246
|
-
exitCode: 1,
|
|
10247
|
-
nextSteps: [`prisma project env add ${input.key}=<value> ${formatScopeFlag$1(scope)}`]
|
|
10248
|
-
});
|
|
10249
|
-
const { data, error, response } = await ctx.api.PATCH("/v1/environment-variables/{envVarId}", {
|
|
10250
|
-
params: { path: { envVarId: existing.id } },
|
|
10251
|
-
body: { value: input.value },
|
|
10252
|
-
signal: ctx.signal
|
|
10253
|
-
});
|
|
10254
|
-
if (error || !data) throw apiCallError(`Failed to update value for ${input.key}`, response, error);
|
|
9744
|
+
const source = resolveEnvWriteSource(args.positionals.assignment, args.flags.file, "update");
|
|
9745
|
+
const scope = requireEnvScope(args.flags, "update");
|
|
9746
|
+
const input = await resolveEnvWriteInput(legacyOperationContext(ctx), source, "update");
|
|
9747
|
+
const { projectId, verboseContext, resolved } = await resolveEnvTarget(ctx, args.flags, scope, "project env update", false);
|
|
9748
|
+
if (input.kind === "file") {
|
|
9749
|
+
const written = await runEnvUpdateFile(legacyOperationContext(ctx), ctx.api, projectId, resolved, input.filePath, input.assignments, verboseContext);
|
|
10255
9750
|
const result = {
|
|
10256
9751
|
projectId,
|
|
10257
9752
|
scope: resolved.descriptor,
|
|
10258
|
-
|
|
9753
|
+
variables: written.result.variables,
|
|
9754
|
+
file: written.result.file
|
|
10259
9755
|
};
|
|
10260
|
-
return ok(ctx.present({ data: result },
|
|
10261
|
-
|
|
10262
|
-
|
|
10263
|
-
|
|
10264
|
-
|
|
10265
|
-
|
|
9756
|
+
return ok(ctx.present({ data: result }, fileWritePresentations({
|
|
9757
|
+
title: "Replacing environment variable values from file.",
|
|
9758
|
+
emptyMessage: "No environment variables updated.",
|
|
9759
|
+
scope: result.scope,
|
|
9760
|
+
filePath: result.file.path,
|
|
9761
|
+
variables: result.variables
|
|
9762
|
+
}, result)));
|
|
9763
|
+
}
|
|
9764
|
+
const existing = await findVariableByNaturalKey(ctx.api, projectId, input.key, resolved, ctx.signal);
|
|
9765
|
+
if (!existing) throw new CliStructuredError("PROJECT.ENV_VARIABLE_NOT_FOUND", `Variable "${input.key}" not found in ${formatScopeLabel(scope)}`, {
|
|
9766
|
+
why: "No variable with this key exists in the targeted scope.",
|
|
9767
|
+
nextActions: [userChoice("Use `prisma project env add` to create a new variable."), runCommand(`prisma project env add ${input.key}=<value> ${formatScopeFlag$1(scope)}`)]
|
|
9768
|
+
});
|
|
9769
|
+
const { data, error, response } = await ctx.api.PATCH("/v1/environment-variables/{envVarId}", {
|
|
9770
|
+
params: { path: { envVarId: existing.id } },
|
|
9771
|
+
body: { value: input.value },
|
|
9772
|
+
signal: ctx.signal
|
|
9773
|
+
});
|
|
9774
|
+
if (error || !data) throw apiCallError(`Failed to update value for ${input.key}`, response, error);
|
|
9775
|
+
const result = {
|
|
9776
|
+
projectId,
|
|
9777
|
+
scope: resolved.descriptor,
|
|
9778
|
+
variable: toMetadata(data.data, resolved.descriptor)
|
|
9779
|
+
};
|
|
9780
|
+
return ok(ctx.present({ data: result }, singlePresentations(result)));
|
|
10266
9781
|
}
|
|
10267
9782
|
});
|
|
10268
9783
|
//#endregion
|
|
@@ -10271,7 +9786,25 @@ const projectEnvUpdateCommand = defineCommand({
|
|
|
10271
9786
|
const CREATE_CHOICE = "__create__";
|
|
10272
9787
|
const CANCEL_CHOICE = "__cancel__";
|
|
10273
9788
|
function setupCanceledError() {
|
|
10274
|
-
return
|
|
9789
|
+
return new CliStructuredError("PROJECT.USAGE_ERROR", "Project setup canceled", {
|
|
9790
|
+
why: "Project link needs a Project before it can continue.",
|
|
9791
|
+
nextActions: [
|
|
9792
|
+
{
|
|
9793
|
+
kind: "user-choice",
|
|
9794
|
+
label: "Choose an existing Project or create a new one, then rerun project link."
|
|
9795
|
+
},
|
|
9796
|
+
{
|
|
9797
|
+
kind: "run-command",
|
|
9798
|
+
label: "prisma project link <id-or-name>",
|
|
9799
|
+
command: "prisma project link <id-or-name>"
|
|
9800
|
+
},
|
|
9801
|
+
{
|
|
9802
|
+
kind: "run-command",
|
|
9803
|
+
label: "prisma project create <name>",
|
|
9804
|
+
command: "prisma project create <name>"
|
|
9805
|
+
}
|
|
9806
|
+
]
|
|
9807
|
+
});
|
|
10275
9808
|
}
|
|
10276
9809
|
function choiceOptions(projects) {
|
|
10277
9810
|
const sorted = sortProjects(projects);
|
|
@@ -10356,14 +9889,8 @@ const projectLinkCommand = defineCommand({
|
|
|
10356
9889
|
},
|
|
10357
9890
|
needs: { credentials: true },
|
|
10358
9891
|
handler: async (args, ctx) => {
|
|
10359
|
-
|
|
10360
|
-
|
|
10361
|
-
return ok(ctx.present({ data: result }, setupPresentations(result)));
|
|
10362
|
-
} catch (error) {
|
|
10363
|
-
const mapped = mapProjectOperationError(error);
|
|
10364
|
-
if (mapped) return notOk(mapped);
|
|
10365
|
-
throw error;
|
|
10366
|
-
}
|
|
9892
|
+
const result = await linkDirectoryToProject(ctx, args.positionals.project);
|
|
9893
|
+
return ok(ctx.present({ data: result }, setupPresentations(result)));
|
|
10367
9894
|
}
|
|
10368
9895
|
});
|
|
10369
9896
|
//#endregion
|
|
@@ -10390,10 +9917,10 @@ function projectStdoutRows(result) {
|
|
|
10390
9917
|
}
|
|
10391
9918
|
function nextActionsFor(result) {
|
|
10392
9919
|
if (result.localBinding?.status === "linked") return [];
|
|
10393
|
-
return
|
|
9920
|
+
return buildProjectSetupNextActions({
|
|
10394
9921
|
createCommand: `${CLI_NAME} project create <name>`,
|
|
10395
9922
|
reason: result.localBinding?.status === "invalid" ? "This directory has an invalid local Project binding. Ask the user which Prisma Project to link before running Project-scoped commands." : "This directory is not linked to a Prisma Project. Project list shows available Projects, but none is selected for this directory."
|
|
10396
|
-
})
|
|
9923
|
+
});
|
|
10397
9924
|
}
|
|
10398
9925
|
function listPresentations$1(result) {
|
|
10399
9926
|
const rows = projectRows(result);
|
|
@@ -10438,21 +9965,15 @@ const projectListCommand = defineCommand({
|
|
|
10438
9965
|
},
|
|
10439
9966
|
needs: { credentials: true },
|
|
10440
9967
|
handler: async (_args, ctx) => {
|
|
10441
|
-
|
|
10442
|
-
|
|
10443
|
-
|
|
10444
|
-
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
|
|
10449
|
-
|
|
10450
|
-
return ok(ctx.present({ data: result }, listPresentations$1(result)));
|
|
10451
|
-
} catch (error) {
|
|
10452
|
-
const mapped = mapProjectOperationError(error);
|
|
10453
|
-
if (mapped) return notOk(mapped);
|
|
10454
|
-
throw error;
|
|
10455
|
-
}
|
|
9968
|
+
const workspace = await resolveActiveWorkspace(ctx);
|
|
9969
|
+
const projects = sortProjects(await listWorkspaceProjects$1(ctx));
|
|
9970
|
+
const localBinding = await readProjectListLocalBinding(ctx.cwd, projects, ctx.signal);
|
|
9971
|
+
const result = {
|
|
9972
|
+
workspace,
|
|
9973
|
+
projects: projects.map(toProjectSummary),
|
|
9974
|
+
localBinding
|
|
9975
|
+
};
|
|
9976
|
+
return ok(ctx.present({ data: result }, listPresentations$1(result)));
|
|
10456
9977
|
}
|
|
10457
9978
|
});
|
|
10458
9979
|
//#endregion
|
|
@@ -10510,26 +10031,20 @@ const projectRenameCommand = defineCommand({
|
|
|
10510
10031
|
},
|
|
10511
10032
|
needs: { credentials: true },
|
|
10512
10033
|
handler: async (args, ctx) => {
|
|
10513
|
-
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
|
|
10519
|
-
|
|
10520
|
-
|
|
10521
|
-
|
|
10522
|
-
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
return ok(ctx.present({ data: result }, renamePresentations(result)));
|
|
10528
|
-
} catch (error) {
|
|
10529
|
-
const mapped = mapProjectOperationError(error);
|
|
10530
|
-
if (mapped) return notOk(mapped);
|
|
10531
|
-
throw error;
|
|
10532
|
-
}
|
|
10034
|
+
const workspace = await resolveActiveWorkspace(ctx);
|
|
10035
|
+
const name = args.positionals.name.trim();
|
|
10036
|
+
if (!isValidProjectSetupName(name)) throw projectSetupNameRequiredError("project rename");
|
|
10037
|
+
const target = await resolvePinnedProject(ctx, workspace, args.flags.project, "project rename");
|
|
10038
|
+
const result = {
|
|
10039
|
+
workspace,
|
|
10040
|
+
project: await createManagementProjectProvider(ctx.api).renameProject({
|
|
10041
|
+
projectId: target.project.id,
|
|
10042
|
+
name,
|
|
10043
|
+
signal: ctx.signal
|
|
10044
|
+
}),
|
|
10045
|
+
previousName: target.project.name
|
|
10046
|
+
};
|
|
10047
|
+
return ok(ctx.present({ data: result }, renamePresentations(result)));
|
|
10533
10048
|
}
|
|
10534
10049
|
});
|
|
10535
10050
|
//#endregion
|
|
@@ -10631,12 +10146,12 @@ function showPresentations$1(result, cwd, env) {
|
|
|
10631
10146
|
rows
|
|
10632
10147
|
}],
|
|
10633
10148
|
stdout: () => stdoutFieldRows(result, cwd).map((row) => `${row.label}: ${row.value}`),
|
|
10634
|
-
next: () => result.project === null ?
|
|
10149
|
+
next: () => result.project === null ? buildProjectSetupNextActions({
|
|
10635
10150
|
commandName: "project show",
|
|
10636
10151
|
retryCommand: "prisma project show <id-or-name>",
|
|
10637
10152
|
suggestedProjectName: result.suggestedProjectName,
|
|
10638
10153
|
reason: "This directory is not linked to a Prisma Project. Package and directory names can suggest setup defaults, but they do not select a Project."
|
|
10639
|
-
})
|
|
10154
|
+
}) : []
|
|
10640
10155
|
};
|
|
10641
10156
|
}
|
|
10642
10157
|
const projectShowCommand = defineCommand({
|
|
@@ -10650,23 +10165,17 @@ const projectShowCommand = defineCommand({
|
|
|
10650
10165
|
},
|
|
10651
10166
|
needs: { credentials: true },
|
|
10652
10167
|
handler: async (args, ctx) => {
|
|
10653
|
-
|
|
10654
|
-
|
|
10655
|
-
|
|
10656
|
-
|
|
10657
|
-
|
|
10658
|
-
|
|
10659
|
-
|
|
10660
|
-
|
|
10661
|
-
|
|
10662
|
-
|
|
10663
|
-
|
|
10664
|
-
return ok(ctx.present({ data: result }, showPresentations$1(result, ctx.cwd, ctx.env)));
|
|
10665
|
-
} catch (error) {
|
|
10666
|
-
const mapped = mapProjectOperationError(error);
|
|
10667
|
-
if (mapped) return notOk(mapped);
|
|
10668
|
-
throw error;
|
|
10669
|
-
}
|
|
10168
|
+
const workspace = await resolveActiveWorkspace(ctx);
|
|
10169
|
+
const inspected = await inspectProjectBinding({
|
|
10170
|
+
context: legacyOperationContext(ctx),
|
|
10171
|
+
workspace,
|
|
10172
|
+
explicitProject: args.positionals.project,
|
|
10173
|
+
listProjects: () => listWorkspaceProjects$1(ctx),
|
|
10174
|
+
commandName: "project show"
|
|
10175
|
+
});
|
|
10176
|
+
if (inspected.isErr()) throw projectResolutionErrorToStructured(inspected.error);
|
|
10177
|
+
const result = inspected.value;
|
|
10178
|
+
return ok(ctx.present({ data: result }, showPresentations$1(result, ctx.cwd, ctx.env)));
|
|
10670
10179
|
}
|
|
10671
10180
|
});
|
|
10672
10181
|
//#endregion
|
|
@@ -10850,11 +10359,11 @@ const projectTransferCommand = defineCommand({
|
|
|
10850
10359
|
},
|
|
10851
10360
|
needs: { credentials: true },
|
|
10852
10361
|
handler: async (args, ctx) => {
|
|
10853
|
-
|
|
10854
|
-
|
|
10855
|
-
|
|
10856
|
-
|
|
10857
|
-
|
|
10362
|
+
const workspace = await resolveActiveWorkspace(ctx);
|
|
10363
|
+
const toWorkspace = args.flags.toWorkspace?.trim() || void 0;
|
|
10364
|
+
const recipientToken = args.flags.recipientToken?.trim() || void 0;
|
|
10365
|
+
if (toWorkspace && recipientToken) {
|
|
10366
|
+
const retry = formatCommand([
|
|
10858
10367
|
"project",
|
|
10859
10368
|
"transfer",
|
|
10860
10369
|
"<project>",
|
|
@@ -10862,42 +10371,49 @@ const projectTransferCommand = defineCommand({
|
|
|
10862
10371
|
"<id-or-name>",
|
|
10863
10372
|
"--confirm",
|
|
10864
10373
|
"<project-id>"
|
|
10865
|
-
])
|
|
10866
|
-
|
|
10867
|
-
|
|
10868
|
-
|
|
10869
|
-
|
|
10870
|
-
|
|
10871
|
-
|
|
10872
|
-
|
|
10873
|
-
|
|
10874
|
-
|
|
10875
|
-
|
|
10876
|
-
recipientAccessToken: recipient.accessToken,
|
|
10877
|
-
signal: ctx.signal
|
|
10374
|
+
]);
|
|
10375
|
+
throw new CliStructuredError("PROJECT.USAGE_ERROR", "Choose one transfer recipient source", {
|
|
10376
|
+
why: "--to-workspace and --recipient-token are mutually exclusive.",
|
|
10377
|
+
nextActions: [{
|
|
10378
|
+
kind: "user-choice",
|
|
10379
|
+
label: "Pass either --to-workspace <id-or-name> or --recipient-token <token>."
|
|
10380
|
+
}, {
|
|
10381
|
+
kind: "run-command",
|
|
10382
|
+
label: retry,
|
|
10383
|
+
command: retry
|
|
10384
|
+
}]
|
|
10878
10385
|
});
|
|
10879
|
-
const warnings = [];
|
|
10880
|
-
const action = await rewriteOrClearLocalPinForProject(legacyOperationContext(ctx), project.id, recipient.workspaceId, { onError: (message) => warnings.push(message) });
|
|
10881
|
-
const result = {
|
|
10882
|
-
workspace,
|
|
10883
|
-
project,
|
|
10884
|
-
recipient: {
|
|
10885
|
-
workspaceId: recipient.workspaceId,
|
|
10886
|
-
workspaceName: recipient.workspaceName,
|
|
10887
|
-
source: recipient.source
|
|
10888
|
-
},
|
|
10889
|
-
localPin: { action }
|
|
10890
|
-
};
|
|
10891
|
-
const diagnostics = localPinDiagnostics(warnings);
|
|
10892
|
-
return ok(ctx.present({
|
|
10893
|
-
data: result,
|
|
10894
|
-
diagnostics
|
|
10895
|
-
}, transferPresentations(result, toWorkspace)));
|
|
10896
|
-
} catch (error) {
|
|
10897
|
-
const mapped = mapProjectOperationError(error);
|
|
10898
|
-
if (mapped) return notOk(mapped);
|
|
10899
|
-
throw error;
|
|
10900
10386
|
}
|
|
10387
|
+
if (!toWorkspace && !recipientToken) throw transferRecipientRequiredError(formatCommand);
|
|
10388
|
+
const projects = await listWorkspaceProjects$1(ctx);
|
|
10389
|
+
const project = toProjectSummary(resolveProjectForSetup(args.positionals.project.trim(), projects, workspace));
|
|
10390
|
+
await ctx.prompt.consent(CONSENT_QUESTION, { token: project.id });
|
|
10391
|
+
const recipient = await resolveRecipient(ctx, {
|
|
10392
|
+
toWorkspace,
|
|
10393
|
+
recipientToken
|
|
10394
|
+
});
|
|
10395
|
+
await createManagementProjectProvider(ctx.api).transferProject({
|
|
10396
|
+
projectId: project.id,
|
|
10397
|
+
recipientAccessToken: recipient.accessToken,
|
|
10398
|
+
signal: ctx.signal
|
|
10399
|
+
});
|
|
10400
|
+
const warnings = [];
|
|
10401
|
+
const action = await rewriteOrClearLocalPinForProject(legacyOperationContext(ctx), project.id, recipient.workspaceId, { onError: (message) => warnings.push(message) });
|
|
10402
|
+
const result = {
|
|
10403
|
+
workspace,
|
|
10404
|
+
project,
|
|
10405
|
+
recipient: {
|
|
10406
|
+
workspaceId: recipient.workspaceId,
|
|
10407
|
+
workspaceName: recipient.workspaceName,
|
|
10408
|
+
source: recipient.source
|
|
10409
|
+
},
|
|
10410
|
+
localPin: { action }
|
|
10411
|
+
};
|
|
10412
|
+
const diagnostics = localPinDiagnostics(warnings);
|
|
10413
|
+
return ok(ctx.present({
|
|
10414
|
+
data: result,
|
|
10415
|
+
diagnostics
|
|
10416
|
+
}, transferPresentations(result, toWorkspace)));
|
|
10901
10417
|
}
|
|
10902
10418
|
});
|
|
10903
10419
|
//#endregion
|
|
@@ -10928,40 +10444,9 @@ function adviceAction(label) {
|
|
|
10928
10444
|
label
|
|
10929
10445
|
};
|
|
10930
10446
|
}
|
|
10931
|
-
function toEngineNextAction(action) {
|
|
10932
|
-
return {
|
|
10933
|
-
kind: action.kind,
|
|
10934
|
-
label: action.label,
|
|
10935
|
-
...action.command !== void 0 ? { command: action.command } : {},
|
|
10936
|
-
...action.commands !== void 0 ? { commands: action.commands } : {},
|
|
10937
|
-
...action.reason !== void 0 ? { reason: action.reason } : {}
|
|
10938
|
-
};
|
|
10939
|
-
}
|
|
10940
10447
|
const CNAME_HINT = /\bcname(?:s)?\s+to\b/;
|
|
10941
10448
|
const PRISMA_BUILD_HOST = /\b((?:[a-z0-9-]+\.)+prisma\.build)\b/i;
|
|
10942
10449
|
/**
|
|
10943
|
-
* Maps a legacy CliError onto the engine error protocol: the flat code
|
|
10944
|
-
* becomes `SERVICE.<code>`, the free-text fix becomes a user-choice
|
|
10945
|
-
* action carried alongside any typed legacy actions, and each nextSteps
|
|
10946
|
-
* command line becomes a run-command action. Copy passes through
|
|
10947
|
-
* unchanged: the producers write the commands a user types today.
|
|
10948
|
-
*/
|
|
10949
|
-
function fromLegacyCliError(error) {
|
|
10950
|
-
const fixAction = error.fix ? [adviceAction(error.fix)] : [];
|
|
10951
|
-
const nextActions = error.nextActions.length > 0 ? [...error.nextActions.map(toEngineNextAction), ...fixAction] : [...fixAction, ...error.nextSteps.map((step) => ({
|
|
10952
|
-
kind: "run-command",
|
|
10953
|
-
label: "Run",
|
|
10954
|
-
command: step
|
|
10955
|
-
}))];
|
|
10956
|
-
return new CliStructuredError(`SERVICE.${error.code}`, error.summary, {
|
|
10957
|
-
...error.why ? { why: error.why } : {},
|
|
10958
|
-
nextActions,
|
|
10959
|
-
...error.where ? { where: { path: error.where } } : {},
|
|
10960
|
-
...Object.keys(error.meta).length > 0 ? { meta: error.meta } : {},
|
|
10961
|
-
...error.docsUrl ? { docsUrl: error.docsUrl } : {}
|
|
10962
|
-
});
|
|
10963
|
-
}
|
|
10964
|
-
/**
|
|
10965
10450
|
* Consent declined interactively. The engine settles this code as a
|
|
10966
10451
|
* user cancellation (exit 3).
|
|
10967
10452
|
*/
|
|
@@ -11826,7 +11311,7 @@ function toBranchKind(name) {
|
|
|
11826
11311
|
*/
|
|
11827
11312
|
async function listWorkspaceProjects(ctx) {
|
|
11828
11313
|
const { data, error, response } = await ctx.api.GET("/v1/projects", { signal: ctx.signal });
|
|
11829
|
-
if (error || !data) throw
|
|
11314
|
+
if (error || !data) throw projectApiError("Failed to list projects", response, error);
|
|
11830
11315
|
return sortProjects((data.data ?? []).map((project) => ({
|
|
11831
11316
|
id: project.id,
|
|
11832
11317
|
name: project.name,
|
|
@@ -11855,7 +11340,7 @@ async function resolveServiceProjectContext(ctx, explicitProject, options) {
|
|
|
11855
11340
|
listProjects: () => Promise.resolve(projects),
|
|
11856
11341
|
commandName: options.commandName
|
|
11857
11342
|
});
|
|
11858
|
-
if (resolvedResult.isErr()) throw
|
|
11343
|
+
if (resolvedResult.isErr()) throw projectResolutionErrorToStructured(resolvedResult.error);
|
|
11859
11344
|
const resolved = resolvedResult.value;
|
|
11860
11345
|
const requested = options.branchName ? {
|
|
11861
11346
|
name: options.branchName,
|
|
@@ -13374,6 +12859,7 @@ const serviceVersionStopCommand = defineCommand({
|
|
|
13374
12859
|
* part of either product's. */
|
|
13375
12860
|
const skillsCommandFamily = defineCommandFamily({
|
|
13376
12861
|
configSection: skillsConfigSection,
|
|
12862
|
+
docsBaseUrl: DOCS_ERRORS_BASE_URL,
|
|
13377
12863
|
commands: {
|
|
13378
12864
|
sync: skillsSyncCommand,
|
|
13379
12865
|
list: defineCommand({
|
|
@@ -13417,63 +12903,66 @@ const skillsCommandFamily = defineCommandFamily({
|
|
|
13417
12903
|
});
|
|
13418
12904
|
//#endregion
|
|
13419
12905
|
//#region src/cli.ts
|
|
13420
|
-
const platformCommandFamily = defineCommandFamily({
|
|
13421
|
-
|
|
13422
|
-
|
|
13423
|
-
|
|
13424
|
-
|
|
13425
|
-
|
|
13426
|
-
|
|
13427
|
-
|
|
13428
|
-
|
|
13429
|
-
|
|
13430
|
-
|
|
13431
|
-
|
|
13432
|
-
|
|
13433
|
-
|
|
13434
|
-
|
|
13435
|
-
|
|
13436
|
-
|
|
13437
|
-
|
|
13438
|
-
|
|
13439
|
-
|
|
13440
|
-
|
|
13441
|
-
|
|
13442
|
-
|
|
13443
|
-
|
|
13444
|
-
|
|
13445
|
-
|
|
13446
|
-
|
|
13447
|
-
|
|
13448
|
-
|
|
13449
|
-
|
|
13450
|
-
|
|
13451
|
-
|
|
13452
|
-
|
|
13453
|
-
|
|
13454
|
-
|
|
13455
|
-
|
|
13456
|
-
|
|
13457
|
-
|
|
13458
|
-
|
|
13459
|
-
|
|
13460
|
-
|
|
13461
|
-
|
|
13462
|
-
|
|
13463
|
-
|
|
13464
|
-
|
|
13465
|
-
|
|
13466
|
-
|
|
13467
|
-
|
|
13468
|
-
|
|
13469
|
-
|
|
13470
|
-
|
|
13471
|
-
|
|
13472
|
-
|
|
13473
|
-
|
|
13474
|
-
|
|
13475
|
-
|
|
13476
|
-
|
|
12906
|
+
const platformCommandFamily = defineCommandFamily({
|
|
12907
|
+
docsBaseUrl: DOCS_ERRORS_BASE_URL,
|
|
12908
|
+
commands: {
|
|
12909
|
+
login: authLoginCommand,
|
|
12910
|
+
logout: authLogoutCommand,
|
|
12911
|
+
whoami: authWhoamiCommand,
|
|
12912
|
+
workspaceList: authWorkspaceListCommand,
|
|
12913
|
+
workspaceUse: authWorkspaceUseCommand,
|
|
12914
|
+
workspaceLogout: authWorkspaceLogoutCommand,
|
|
12915
|
+
projectList: projectListCommand,
|
|
12916
|
+
projectShow: projectShowCommand,
|
|
12917
|
+
projectCreate: projectCreateCommand,
|
|
12918
|
+
projectLink: projectLinkCommand,
|
|
12919
|
+
projectRename: projectRenameCommand,
|
|
12920
|
+
projectDelete: projectDeleteCommand,
|
|
12921
|
+
projectTransfer: projectTransferCommand,
|
|
12922
|
+
projectEnvAdd: projectEnvAddCommand,
|
|
12923
|
+
projectEnvUpdate: projectEnvUpdateCommand,
|
|
12924
|
+
projectEnvList: projectEnvListCommand,
|
|
12925
|
+
projectEnvDelete: projectEnvDeleteCommand,
|
|
12926
|
+
postgresList: postgresListCommand,
|
|
12927
|
+
postgresShow: postgresShowCommand,
|
|
12928
|
+
postgresCreate: postgresCreateCommand,
|
|
12929
|
+
postgresUsage: postgresUsageCommand,
|
|
12930
|
+
postgresBackupRestore: postgresBackupRestoreCommand,
|
|
12931
|
+
postgresDelete: postgresDeleteCommand,
|
|
12932
|
+
postgresBackupList: postgresBackupListCommand,
|
|
12933
|
+
postgresConnectionList: postgresConnectionListCommand,
|
|
12934
|
+
postgresConnectionCreate: postgresConnectionCreateCommand,
|
|
12935
|
+
postgresConnectionRotate: postgresConnectionRotateCommand,
|
|
12936
|
+
postgresConnectionDelete: postgresConnectionDeleteCommand,
|
|
12937
|
+
bucketList: bucketListCommand,
|
|
12938
|
+
bucketCreate: bucketCreateCommand,
|
|
12939
|
+
bucketDelete: bucketDeleteCommand,
|
|
12940
|
+
bucketKeyList: bucketKeyListCommand,
|
|
12941
|
+
bucketKeyCreate: bucketKeyCreateCommand,
|
|
12942
|
+
bucketKeyDelete: bucketKeyDeleteCommand,
|
|
12943
|
+
branchList: branchListCommand,
|
|
12944
|
+
gitConnect: gitConnectCommand,
|
|
12945
|
+
gitDisconnect: gitDisconnectCommand,
|
|
12946
|
+
serviceList: serviceListCommand,
|
|
12947
|
+
serviceLogs: serviceLogsCommand,
|
|
12948
|
+
serviceCreate: serviceCreateCommand,
|
|
12949
|
+
serviceShow: serviceShowCommand,
|
|
12950
|
+
serviceOpen: serviceOpenCommand,
|
|
12951
|
+
serviceVersionList: serviceVersionListCommand,
|
|
12952
|
+
serviceVersionShow: serviceVersionShowCommand,
|
|
12953
|
+
serviceVersionPromote: serviceVersionPromoteCommand,
|
|
12954
|
+
serviceVersionRollback: serviceVersionRollbackCommand,
|
|
12955
|
+
serviceVersionStart: serviceVersionStartCommand,
|
|
12956
|
+
serviceVersionStop: serviceVersionStopCommand,
|
|
12957
|
+
serviceVersionDelete: serviceVersionDeleteCommand,
|
|
12958
|
+
serviceDelete: serviceDeleteCommand,
|
|
12959
|
+
serviceDomainAdd: serviceDomainAddCommand,
|
|
12960
|
+
serviceDomainShow: serviceDomainShowCommand,
|
|
12961
|
+
serviceDomainDelete: serviceDomainDeleteCommand,
|
|
12962
|
+
serviceDomainRetry: serviceDomainRetryCommand,
|
|
12963
|
+
serviceDomainWait: serviceDomainWaitCommand
|
|
12964
|
+
}
|
|
12965
|
+
});
|
|
13477
12966
|
/**
|
|
13478
12967
|
* Composer's commands, contributed by composer's own package and run by
|
|
13479
12968
|
* this process, mounted as shipped. Only the command definitions and
|