@prisma/cli 2.20.0 → 3.0.0-alpha.0
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/LICENSE +158 -0
- package/README.md +29 -28
- package/dist/adapters/config.js +74 -0
- package/dist/adapters/local-state.js +98 -0
- package/dist/adapters/mock-api.js +57 -0
- package/dist/adapters/token-storage.js +43 -0
- package/dist/cli.js +9 -0
- package/dist/cli2.js +59 -0
- package/dist/commands/app/index.js +223 -0
- package/dist/commands/auth/index.js +42 -0
- package/dist/commands/branch/index.js +51 -0
- package/dist/commands/project/index.js +45 -0
- package/dist/controllers/app.js +813 -0
- package/dist/controllers/auth.js +107 -0
- package/dist/controllers/branch.js +73 -0
- package/dist/controllers/project.js +214 -0
- package/dist/controllers/select-prompt-port.js +12 -0
- package/dist/lib/app/bun-project.js +39 -0
- package/dist/lib/app/env-vars.js +24 -0
- package/dist/lib/app/local-dev.js +149 -0
- package/dist/lib/app/preview-build.js +283 -0
- package/dist/lib/app/preview-interaction.js +38 -0
- package/dist/lib/app/preview-progress.js +139 -0
- package/dist/lib/app/preview-provider.js +232 -0
- package/dist/lib/auth/auth-ops.js +57 -0
- package/dist/lib/auth/client.js +22 -0
- package/dist/lib/auth/guard.js +34 -0
- package/dist/lib/auth/login.js +117 -0
- package/dist/output/patterns.js +93 -0
- package/dist/presenters/app.js +405 -0
- package/dist/presenters/auth.js +73 -0
- package/dist/presenters/branch.js +111 -0
- package/dist/presenters/project.js +84 -0
- package/dist/shell/command-meta.js +320 -0
- package/dist/shell/command-runner.js +33 -0
- package/dist/shell/errors.js +66 -0
- package/dist/shell/global-flags.js +25 -0
- package/dist/shell/help.js +78 -0
- package/dist/shell/output.js +54 -0
- package/dist/shell/prompt.js +31 -0
- package/dist/shell/runtime.js +51 -0
- package/dist/shell/ui.js +59 -0
- package/dist/use-cases/auth.js +70 -0
- package/dist/use-cases/branch.js +95 -0
- package/dist/use-cases/create-cli-gateways.js +93 -0
- package/dist/use-cases/project.js +75 -0
- package/package.json +49 -137
- package/build/child.js +0 -4110
- package/build/index.js +0 -104447
- package/build/public/demo.html +0 -17
- package/build/public/demoChunk.js +0 -2
- package/build/public/electron-darwin.html +0 -19
- package/build/public/electron-linux.html +0 -18
- package/build/public/electron-mac.html +0 -18
- package/build/public/electron-win.html +0 -17
- package/build/public/electron-win32.html +0 -18
- package/build/public/electron.html +0 -17
- package/build/public/electronBus.js +0 -2
- package/build/public/electronChunk.js +0 -2
- package/build/public/favicon/apple-touch-icon.png +0 -0
- package/build/public/favicon/favicon-16x16.png +0 -0
- package/build/public/favicon/favicon-32x32.png +0 -0
- package/build/public/favicon/prisma.png +0 -0
- package/build/public/fonts/Inter.ttf +0 -0
- package/build/public/fonts/RobotoMono.ttf +0 -0
- package/build/public/icons/.DS_Store +0 -0
- package/build/public/icons/alert.svg +0 -5
- package/build/public/icons/array.svg +0 -4
- package/build/public/icons/bin.svg +0 -37
- package/build/public/icons/boolean.svg +0 -4
- package/build/public/icons/check.svg +0 -3
- package/build/public/icons/chevron-down.svg +0 -3
- package/build/public/icons/code.svg +0 -4
- package/build/public/icons/cross.svg +0 -11
- package/build/public/icons/data-tool.svg +0 -5
- package/build/public/icons/database.svg +0 -4
- package/build/public/icons/datetime.svg +0 -4
- package/build/public/icons/double-arrow-right.svg +0 -4
- package/build/public/icons/download.svg +0 -4
- package/build/public/icons/ellipsis.svg +0 -10
- package/build/public/icons/enum.svg +0 -6
- package/build/public/icons/expand.svg +0 -4
- package/build/public/icons/eye.svg +0 -20
- package/build/public/icons/filters.svg +0 -5
- package/build/public/icons/folder.svg +0 -6
- package/build/public/icons/hamburger.svg +0 -4
- package/build/public/icons/icon.svg +0 -199
- package/build/public/icons/logo.svg +0 -199
- package/build/public/icons/logotype.svg +0 -4
- package/build/public/icons/number.svg +0 -7
- package/build/public/icons/object.svg +0 -5
- package/build/public/icons/play.svg +0 -6
- package/build/public/icons/plus.svg +0 -4
- package/build/public/icons/refresh.svg +0 -4
- package/build/public/icons/search.svg +0 -7
- package/build/public/icons/settings.svg +0 -8
- package/build/public/icons/string.svg +0 -4
- package/build/public/icons/tick-indeterminate.svg +0 -3
- package/build/public/icons/tick.svg +0 -4
- package/build/public/illustrations/.DS_Store +0 -0
- package/build/public/illustrations/empty.svg +0 -1
- package/build/public/illustrations/read.svg +0 -1
- package/build/public/illustrations/searching.svg +0 -1
- package/build/public/images/.DS_Store +0 -0
- package/build/public/images/icon-1024.png +0 -0
- package/build/public/index.html +0 -49
- package/build/public/main.31f688a6254e294d9128.css +0 -118
- package/build/public/main.31f688a6254e294d9128.css.map +0 -1
- package/build/public/main.364c712f32662ff0e43d.css +0 -116
- package/build/public/main.364c712f32662ff0e43d.css.map +0 -1
- package/build/public/main.42ef3fe7e97347765c91.css +0 -118
- package/build/public/main.42ef3fe7e97347765c91.css.map +0 -1
- package/build/public/main.503446defafe7aeca2f3.css +0 -116
- package/build/public/main.503446defafe7aeca2f3.css.map +0 -1
- package/build/public/main.c50a3b5980fe26f78b65.css +0 -118
- package/build/public/main.c50a3b5980fe26f78b65.css.map +0 -1
- package/build/public/main.cda25e5813776c2af295.css +0 -116
- package/build/public/main.cda25e5813776c2af295.css.map +0 -1
- package/build/public/main.dbac3b290f78404ff579.css +0 -116
- package/build/public/main.dbac3b290f78404ff579.css.map +0 -1
- package/build/public/main.f8ce44e122e5e2b8f778.css +0 -118
- package/build/public/main.f8ce44e122e5e2b8f778.css.map +0 -1
- package/build/public/main.fc9ea7521a4aecce1a77.css +0 -118
- package/build/public/main.fc9ea7521a4aecce1a77.css.map +0 -1
- package/build/public/main.js +0 -403
- package/build/public/main.js.map +0 -1
- package/build/public/mainChunk.0a84f3ba4d2ab481e877.css +0 -116
- package/build/public/mainChunk.1b327d58afbddc917bce.css +0 -118
- package/build/public/mainChunk.2cdf583b2bd51aa67587.css +0 -118
- package/build/public/mainChunk.544af00f7e9ffcbe782c.css +0 -118
- package/build/public/mainChunk.56f96a13868b2b4a53be.css +0 -118
- package/build/public/mainChunk.5cee429bfbf06e7ecb39.css +0 -118
- package/build/public/mainChunk.a862474ed4cdb421ffa2.css +0 -118
- package/build/public/mainChunk.ada34153c4911b5ac22b.css +0 -118
- package/build/public/mainChunk.c16a8d01b2c49b2f2751.css +0 -118
- package/build/public/mainChunk.ca4a878d5478b9320be8.css +0 -116
- package/build/public/mainChunk.e3adc8758b4395546cef.css +0 -116
- package/build/public/mainChunk.f2c59fcbfc455d8b9de5.css +0 -118
- package/build/public/mainChunk.js +0 -396
- package/build/public/preview.html +0 -16
- package/build/public/previewBus.js +0 -2
- package/build/public/previewChunk.js +0 -2
- package/build/public/projects.html +0 -86
- package/build/public/server.html +0 -16
- package/build/public/serverBus.js +0 -2
- package/build/public/serverChunk.js +0 -2
- package/build/public/splash.html +0 -17
- package/build/public/studioBundle.828f34b1781061528841.css +0 -118
- package/build/public/studioBundle.bfe9138b2e0293fcb7da.css +0 -118
- package/build/public/studioBundle.c5b256eede880e502aac.css +0 -118
- package/build/public/studioBundle.e43df511c8e2e368900a.css +0 -118
- package/build/public/studioBundle.e69382554c2a2942ae32.css +0 -118
- package/build/public/studioBundle.ee21aa56a3999a2b380b.css +0 -118
- package/build/public/studioBundle.js +0 -396
- package/build/public/vercel.html +0 -16
- package/build/public/vercelChunk.js +0 -2
- package/build/xdg-open +0 -1066
- package/install/index.js +0 -5
- package/preinstall/index.js +0 -71
- package/prisma-client/README.md +0 -27
- package/prisma-client/generator-build/.DS_Store +0 -0
- package/prisma-client/generator-build/index.js +0 -79697
- package/prisma-client/index-browser.js +0 -3
- package/prisma-client/index.d.ts +0 -1
- package/prisma-client/index.js +0 -11
- package/prisma-client/package.json +0 -138
- package/prisma-client/runtime/index-browser.d.ts +0 -267
- package/prisma-client/runtime/index-browser.js +0 -2273
- package/prisma-client/runtime/index.d.ts +0 -1018
- package/prisma-client/runtime/index.js +0 -35028
- package/prisma-client/scripts/backup-index-browser.js +0 -3
- package/prisma-client/scripts/backup-index.d.ts +0 -1
- package/prisma-client/scripts/backup-index.js +0 -11
- package/prisma-client/scripts/colors.js +0 -180
- package/prisma-client/scripts/default-index-browser.js +0 -12
- package/prisma-client/scripts/default-index.d.ts +0 -47
- package/prisma-client/scripts/default-index.js +0 -12
- package/prisma-client/scripts/get-packed-client.js +0 -12
- package/prisma-client/scripts/mock-fs.js +0 -14
- package/prisma-client/scripts/postinstall.d.ts +0 -5
- package/prisma-client/scripts/postinstall.js +0 -398
- package/scripts/install-entry.js +0 -8
- package/scripts/preinstall-entry.js +0 -9
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { FileTokenStorage } from "../../adapters/token-storage.js";
|
|
2
|
+
import { requireComputeAuth } from "./guard.js";
|
|
3
|
+
import { login } from "./login.js";
|
|
4
|
+
//#region src/lib/auth/auth-ops.ts
|
|
5
|
+
function decodeJwtPayload(token) {
|
|
6
|
+
try {
|
|
7
|
+
const payload = token.split(".")[1];
|
|
8
|
+
if (!payload) return {};
|
|
9
|
+
return JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
|
|
10
|
+
} catch {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async function performLogin(env) {
|
|
15
|
+
await login({
|
|
16
|
+
tokenStorage: new FileTokenStorage(env),
|
|
17
|
+
env
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async function readAuthState(env) {
|
|
21
|
+
const tokens = await new FileTokenStorage(env).getTokens();
|
|
22
|
+
if (!tokens) return {
|
|
23
|
+
authenticated: false,
|
|
24
|
+
provider: null,
|
|
25
|
+
user: null,
|
|
26
|
+
workspace: null,
|
|
27
|
+
linkedProjectId: null
|
|
28
|
+
};
|
|
29
|
+
const claims = decodeJwtPayload(tokens.accessToken);
|
|
30
|
+
const client = await requireComputeAuth(env);
|
|
31
|
+
let workspaceId = tokens.workspaceId;
|
|
32
|
+
let workspaceName = tokens.workspaceId;
|
|
33
|
+
if (client) try {
|
|
34
|
+
const { data } = await client.GET("/v1/workspaces/{id}", { params: { path: { id: tokens.workspaceId } } });
|
|
35
|
+
if (data?.data?.id) workspaceId = data.data.id;
|
|
36
|
+
if (data?.data?.name) workspaceName = data.data.name;
|
|
37
|
+
} catch {}
|
|
38
|
+
return {
|
|
39
|
+
authenticated: true,
|
|
40
|
+
provider: null,
|
|
41
|
+
user: {
|
|
42
|
+
id: claims.sub ?? "",
|
|
43
|
+
name: claims.name ?? "",
|
|
44
|
+
email: claims.email ?? ""
|
|
45
|
+
},
|
|
46
|
+
workspace: {
|
|
47
|
+
id: workspaceId,
|
|
48
|
+
name: workspaceName
|
|
49
|
+
},
|
|
50
|
+
linkedProjectId: null
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function performLogout(env) {
|
|
54
|
+
await new FileTokenStorage(env).clearTokens();
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { performLogin, performLogout, readAuthState };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
//#region src/lib/auth/client.ts
|
|
4
|
+
const CLIENT_ID = "cmm3lndn701oo0uefvxzo0ivw";
|
|
5
|
+
const SERVICE_TOKEN_ENV_VAR = "PRISMA_API_TOKEN";
|
|
6
|
+
const AUTH_FILE_ENV_VAR = "PRISMA_COMPUTE_AUTH_FILE";
|
|
7
|
+
function getApiBaseUrl(env = process.env) {
|
|
8
|
+
return env.PRISMA_MANAGEMENT_API_URL?.trim() || "https://api.prisma.io";
|
|
9
|
+
}
|
|
10
|
+
function getAuthFilePath(env = process.env) {
|
|
11
|
+
const configured = env[AUTH_FILE_ENV_VAR];
|
|
12
|
+
if (configured?.trim()) return path.resolve(configured);
|
|
13
|
+
if (process.platform === "darwin") return path.join(os.homedir(), "Library", "Application Support", "prisma", "auth.json");
|
|
14
|
+
if (process.platform === "win32") {
|
|
15
|
+
const appData = env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
|
|
16
|
+
return path.join(appData, "prisma", "auth.json");
|
|
17
|
+
}
|
|
18
|
+
const xdgConfigHome = env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config");
|
|
19
|
+
return path.join(xdgConfigHome, "prisma", "auth.json");
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { CLIENT_ID, SERVICE_TOKEN_ENV_VAR, getApiBaseUrl, getAuthFilePath };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CLIENT_ID, SERVICE_TOKEN_ENV_VAR, getApiBaseUrl } from "./client.js";
|
|
2
|
+
import { FileTokenStorage } from "../../adapters/token-storage.js";
|
|
3
|
+
import { createManagementApiClient, createManagementApiSdk } from "@prisma/management-api-sdk";
|
|
4
|
+
//#region src/lib/auth/guard.ts
|
|
5
|
+
/**
|
|
6
|
+
* Resolve authentication and return a ManagementApiClient.
|
|
7
|
+
*
|
|
8
|
+
* Priority:
|
|
9
|
+
* 1. PRISMA_API_TOKEN env var → service token (CI / headless)
|
|
10
|
+
* 2. Stored OAuth tokens → SDK with auto-refresh
|
|
11
|
+
*
|
|
12
|
+
* Returns null if not authenticated.
|
|
13
|
+
*/
|
|
14
|
+
async function requireComputeAuth(env = process.env) {
|
|
15
|
+
const rawToken = env[SERVICE_TOKEN_ENV_VAR];
|
|
16
|
+
if (rawToken !== void 0) {
|
|
17
|
+
const token = rawToken.trim();
|
|
18
|
+
if (token.length === 0) throw new Error(`${SERVICE_TOKEN_ENV_VAR} is set but empty. Provide a valid token or unset the variable.`);
|
|
19
|
+
return createManagementApiClient({
|
|
20
|
+
baseUrl: getApiBaseUrl(env),
|
|
21
|
+
token
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
const tokenStorage = new FileTokenStorage(env);
|
|
25
|
+
if (!await tokenStorage.getTokens()) return null;
|
|
26
|
+
return createManagementApiSdk({
|
|
27
|
+
clientId: CLIENT_ID,
|
|
28
|
+
redirectUri: "http://localhost:0/auth/callback",
|
|
29
|
+
tokenStorage,
|
|
30
|
+
apiBaseUrl: getApiBaseUrl(env)
|
|
31
|
+
}).client;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { requireComputeAuth };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { getApiBaseUrl } from "./client.js";
|
|
2
|
+
import { FileTokenStorage } from "../../adapters/token-storage.js";
|
|
3
|
+
import open from "open";
|
|
4
|
+
import { AuthError, createManagementApiSdk } from "@prisma/management-api-sdk";
|
|
5
|
+
import events from "node:events";
|
|
6
|
+
import http from "node:http";
|
|
7
|
+
//#region src/lib/auth/login.ts
|
|
8
|
+
var AuthError$1 = class extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "AuthError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
async function login(options = {}) {
|
|
15
|
+
const hostname = options.hostname ?? "localhost";
|
|
16
|
+
const port = options.port ?? 0;
|
|
17
|
+
const server = http.createServer();
|
|
18
|
+
server.listen({
|
|
19
|
+
host: hostname,
|
|
20
|
+
port
|
|
21
|
+
});
|
|
22
|
+
try {
|
|
23
|
+
const state = new LoginState({
|
|
24
|
+
hostname,
|
|
25
|
+
port: (await events.once(server, "listening").then(() => server.address())).port,
|
|
26
|
+
tokenStorage: options.tokenStorage,
|
|
27
|
+
clientId: options.clientId,
|
|
28
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
29
|
+
authBaseUrl: options.authBaseUrl,
|
|
30
|
+
openUrl: options.openUrl,
|
|
31
|
+
env: options.env
|
|
32
|
+
});
|
|
33
|
+
const authResult = new Promise((resolve, reject) => {
|
|
34
|
+
server.on("request", async (req, res) => {
|
|
35
|
+
const url = new URL(`http://${state.host}${req.url}`);
|
|
36
|
+
if (url.pathname !== "/auth/callback") {
|
|
37
|
+
res.statusCode = 404;
|
|
38
|
+
res.end("Not found");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
await state.handleCallback(url);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
res.statusCode = 400;
|
|
45
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
46
|
+
res.end(message);
|
|
47
|
+
reject(error);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
res.setHeader("Content-Type", "text/html");
|
|
51
|
+
res.end(`<html><body style="font-family:system-ui;max-width:400px;margin:80px auto;text-align:center"><h2>✓ Signed in</h2><p>You may now close this tab and return to the terminal.</p></body></html>`);
|
|
52
|
+
resolve();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
await state.openLoginPage();
|
|
56
|
+
await authResult;
|
|
57
|
+
} finally {
|
|
58
|
+
if (server.listening) await new Promise((resolve) => server.close(() => resolve()));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
var LoginState = class {
|
|
62
|
+
latestVerifier;
|
|
63
|
+
latestState;
|
|
64
|
+
sdk;
|
|
65
|
+
openUrl;
|
|
66
|
+
constructor(options) {
|
|
67
|
+
this.options = options;
|
|
68
|
+
const tokenStorage = options.tokenStorage ?? new FileTokenStorage(options.env);
|
|
69
|
+
this.sdk = createManagementApiSdk({
|
|
70
|
+
clientId: options.clientId ?? "cmm3lndn701oo0uefvxzo0ivw",
|
|
71
|
+
redirectUri: `http://${options.hostname}:${options.port}/auth/callback`,
|
|
72
|
+
tokenStorage,
|
|
73
|
+
apiBaseUrl: options.apiBaseUrl ?? getApiBaseUrl(options.env),
|
|
74
|
+
authBaseUrl: options.authBaseUrl
|
|
75
|
+
});
|
|
76
|
+
this.openUrl = options.openUrl ?? open;
|
|
77
|
+
}
|
|
78
|
+
async openLoginPage() {
|
|
79
|
+
const { url, state, verifier } = await this.sdk.getLoginUrl({
|
|
80
|
+
scope: "workspace:admin offline_access",
|
|
81
|
+
additionalParams: {
|
|
82
|
+
utm_source: "prisma-cli",
|
|
83
|
+
utm_medium: "command-login",
|
|
84
|
+
utm_campaign: "prisma-cli"
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
this.latestState = state;
|
|
88
|
+
this.latestVerifier = verifier;
|
|
89
|
+
await this.openUrl(url);
|
|
90
|
+
}
|
|
91
|
+
async handleCallback(url) {
|
|
92
|
+
if (url.pathname !== "/auth/callback") throw new AuthError$1("Not a callback URL");
|
|
93
|
+
const params = url.searchParams;
|
|
94
|
+
const error = params.get("error");
|
|
95
|
+
if (error) {
|
|
96
|
+
const desc = params.get("error_description");
|
|
97
|
+
throw new AuthError$1(desc ? `${error}: ${desc}` : error);
|
|
98
|
+
}
|
|
99
|
+
if (!this.latestVerifier) throw new AuthError$1("No verifier found");
|
|
100
|
+
if (!this.latestState) throw new AuthError$1("No state found");
|
|
101
|
+
try {
|
|
102
|
+
await this.sdk.handleCallback({
|
|
103
|
+
callbackUrl: url,
|
|
104
|
+
verifier: this.latestVerifier,
|
|
105
|
+
expectedState: this.latestState
|
|
106
|
+
});
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (error instanceof AuthError) throw new AuthError$1(error.message);
|
|
109
|
+
throw new AuthError$1(error instanceof Error ? error.message : "Unknown error during login");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
get host() {
|
|
113
|
+
return `${this.options.hostname}:${this.options.port}`;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
//#endregion
|
|
117
|
+
export { login };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { formatDescriptorLabel } from "../shell/command-meta.js";
|
|
2
|
+
import { maskValue, padDisplay, renderSummaryLine } from "../shell/ui.js";
|
|
3
|
+
import stringWidth from "string-width";
|
|
4
|
+
//#region src/output/patterns.ts
|
|
5
|
+
function renderList(input, ui) {
|
|
6
|
+
const keyWidth = Math.max(stringWidth(`${input.parentContext.key}:`), ...input.items.map((item) => stringWidth(`⚬ ${item.noun}:`)), stringWidth("Read more"));
|
|
7
|
+
const lines = renderCardTitle(input.descriptor, input.title, ui);
|
|
8
|
+
lines.push(renderCardRow(ui, keyWidth, input.parentContext.key, input.parentContext.value));
|
|
9
|
+
if (input.items.length === 0) lines.push(renderPlainCardLine(ui, ui.dim(input.emptyMessage)));
|
|
10
|
+
else for (const item of input.items) lines.push(renderCardRow(ui, keyWidth, `⚬ ${item.noun}`, formatListItemValue(ui, item)));
|
|
11
|
+
lines.push(renderCardDivider(ui));
|
|
12
|
+
lines.push(renderReadMore(ui, keyWidth, input.descriptor));
|
|
13
|
+
return lines;
|
|
14
|
+
}
|
|
15
|
+
function serializeList(input) {
|
|
16
|
+
return {
|
|
17
|
+
context: input.context,
|
|
18
|
+
items: input.items.map((item) => ({
|
|
19
|
+
name: item.label,
|
|
20
|
+
id: item.id,
|
|
21
|
+
status: item.status
|
|
22
|
+
})),
|
|
23
|
+
count: input.items.length
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function renderShow(input, ui) {
|
|
27
|
+
const keyWidth = Math.max(...input.fields.map((field) => stringWidth(`${field.key}:`)), stringWidth("Read more"));
|
|
28
|
+
const lines = renderCardTitle(input.descriptor, input.title, ui);
|
|
29
|
+
for (const field of input.fields) lines.push(renderCardRow(ui, keyWidth, field.key, formatValue(ui, field.value, field.tone, field.sensitive)));
|
|
30
|
+
lines.push(renderCardDivider(ui));
|
|
31
|
+
lines.push(renderReadMore(ui, keyWidth, input.descriptor));
|
|
32
|
+
return lines;
|
|
33
|
+
}
|
|
34
|
+
function renderMutate(input, ui) {
|
|
35
|
+
const rows = [...input.context, {
|
|
36
|
+
key: "mode",
|
|
37
|
+
value: "apply",
|
|
38
|
+
tone: "dim"
|
|
39
|
+
}];
|
|
40
|
+
const keyWidth = Math.max(...rows.map((row) => stringWidth(`${row.key}:`)), stringWidth("Read more"));
|
|
41
|
+
const lines = renderCardTitle(input.descriptor, input.title, ui);
|
|
42
|
+
for (const row of rows) lines.push(renderCardRow(ui, keyWidth, row.key, formatValue(ui, row.value, row.tone, row.sensitive)));
|
|
43
|
+
lines.push(renderCardDivider(ui));
|
|
44
|
+
lines.push(renderReadMore(ui, keyWidth, input.descriptor));
|
|
45
|
+
lines.push("");
|
|
46
|
+
lines.push(`${ui.warning("◇")} ${input.operationDescription}...`);
|
|
47
|
+
lines.push(renderSummaryLine(ui, "success", `Applied ${input.operationCount} operation(s)`));
|
|
48
|
+
for (const detail of input.details) lines.push(` ${detail}`);
|
|
49
|
+
for (const alert of input.alerts ?? []) {
|
|
50
|
+
lines.push("");
|
|
51
|
+
lines.push(renderSummaryLine(ui, alert.tone, alert.text));
|
|
52
|
+
}
|
|
53
|
+
return lines;
|
|
54
|
+
}
|
|
55
|
+
function renderCardTitle(descriptor, title, ui) {
|
|
56
|
+
return [`${ui.strong(formatDescriptorLabel(descriptor))} ${ui.dim("→")} ${ui.dim(title)}`, ""];
|
|
57
|
+
}
|
|
58
|
+
function renderCardRow(ui, keyWidth, key, value) {
|
|
59
|
+
return `${renderCardRail(ui)} ${ui.accent(padDisplay(`${key}:`, keyWidth))} ${value}`;
|
|
60
|
+
}
|
|
61
|
+
function renderPlainCardLine(ui, text) {
|
|
62
|
+
return `${renderCardRail(ui)} ${text}`;
|
|
63
|
+
}
|
|
64
|
+
function renderCardDivider(ui) {
|
|
65
|
+
return renderCardRail(ui);
|
|
66
|
+
}
|
|
67
|
+
function renderReadMore(ui, keyWidth, descriptor) {
|
|
68
|
+
return `${renderCardRail(ui)} ${ui.accent(padDisplay("Read more", keyWidth))} ${ui.link(descriptor.docsPath ?? "")}`;
|
|
69
|
+
}
|
|
70
|
+
function renderCardRail(ui) {
|
|
71
|
+
return ui.dim("│");
|
|
72
|
+
}
|
|
73
|
+
function formatListItemValue(ui, item) {
|
|
74
|
+
const annotation = renderAnnotation(ui, item.status);
|
|
75
|
+
return annotation ? `${item.label} ${annotation}` : item.label;
|
|
76
|
+
}
|
|
77
|
+
function renderAnnotation(ui, status) {
|
|
78
|
+
if (status === "active") return ui.success("(active)");
|
|
79
|
+
if (status === "linked") return ui.accent("(linked)");
|
|
80
|
+
if (status === "default") return ui.dim("(default)");
|
|
81
|
+
return "";
|
|
82
|
+
}
|
|
83
|
+
function formatValue(ui, value, tone = "default", sensitive = false) {
|
|
84
|
+
const resolvedValue = sensitive ? maskValue(value) : value;
|
|
85
|
+
if (tone === "success") return ui.success(resolvedValue);
|
|
86
|
+
if (tone === "warning") return ui.warning(resolvedValue);
|
|
87
|
+
if (tone === "error") return ui.error(resolvedValue);
|
|
88
|
+
if (tone === "link") return ui.link(resolvedValue);
|
|
89
|
+
if (tone === "dim") return ui.dim(resolvedValue);
|
|
90
|
+
return resolvedValue;
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
export { renderList, renderMutate, renderShow, serializeList };
|