@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.
Files changed (183) hide show
  1. package/LICENSE +158 -0
  2. package/README.md +29 -28
  3. package/dist/adapters/config.js +74 -0
  4. package/dist/adapters/local-state.js +98 -0
  5. package/dist/adapters/mock-api.js +57 -0
  6. package/dist/adapters/token-storage.js +43 -0
  7. package/dist/cli.js +9 -0
  8. package/dist/cli2.js +59 -0
  9. package/dist/commands/app/index.js +223 -0
  10. package/dist/commands/auth/index.js +42 -0
  11. package/dist/commands/branch/index.js +51 -0
  12. package/dist/commands/project/index.js +45 -0
  13. package/dist/controllers/app.js +813 -0
  14. package/dist/controllers/auth.js +107 -0
  15. package/dist/controllers/branch.js +73 -0
  16. package/dist/controllers/project.js +214 -0
  17. package/dist/controllers/select-prompt-port.js +12 -0
  18. package/dist/lib/app/bun-project.js +39 -0
  19. package/dist/lib/app/env-vars.js +24 -0
  20. package/dist/lib/app/local-dev.js +149 -0
  21. package/dist/lib/app/preview-build.js +283 -0
  22. package/dist/lib/app/preview-interaction.js +38 -0
  23. package/dist/lib/app/preview-progress.js +139 -0
  24. package/dist/lib/app/preview-provider.js +232 -0
  25. package/dist/lib/auth/auth-ops.js +57 -0
  26. package/dist/lib/auth/client.js +22 -0
  27. package/dist/lib/auth/guard.js +34 -0
  28. package/dist/lib/auth/login.js +117 -0
  29. package/dist/output/patterns.js +93 -0
  30. package/dist/presenters/app.js +405 -0
  31. package/dist/presenters/auth.js +73 -0
  32. package/dist/presenters/branch.js +111 -0
  33. package/dist/presenters/project.js +84 -0
  34. package/dist/shell/command-meta.js +320 -0
  35. package/dist/shell/command-runner.js +33 -0
  36. package/dist/shell/errors.js +66 -0
  37. package/dist/shell/global-flags.js +25 -0
  38. package/dist/shell/help.js +78 -0
  39. package/dist/shell/output.js +54 -0
  40. package/dist/shell/prompt.js +31 -0
  41. package/dist/shell/runtime.js +51 -0
  42. package/dist/shell/ui.js +59 -0
  43. package/dist/use-cases/auth.js +70 -0
  44. package/dist/use-cases/branch.js +95 -0
  45. package/dist/use-cases/create-cli-gateways.js +93 -0
  46. package/dist/use-cases/project.js +75 -0
  47. package/package.json +49 -137
  48. package/build/child.js +0 -4110
  49. package/build/index.js +0 -104447
  50. package/build/public/demo.html +0 -17
  51. package/build/public/demoChunk.js +0 -2
  52. package/build/public/electron-darwin.html +0 -19
  53. package/build/public/electron-linux.html +0 -18
  54. package/build/public/electron-mac.html +0 -18
  55. package/build/public/electron-win.html +0 -17
  56. package/build/public/electron-win32.html +0 -18
  57. package/build/public/electron.html +0 -17
  58. package/build/public/electronBus.js +0 -2
  59. package/build/public/electronChunk.js +0 -2
  60. package/build/public/favicon/apple-touch-icon.png +0 -0
  61. package/build/public/favicon/favicon-16x16.png +0 -0
  62. package/build/public/favicon/favicon-32x32.png +0 -0
  63. package/build/public/favicon/prisma.png +0 -0
  64. package/build/public/fonts/Inter.ttf +0 -0
  65. package/build/public/fonts/RobotoMono.ttf +0 -0
  66. package/build/public/icons/.DS_Store +0 -0
  67. package/build/public/icons/alert.svg +0 -5
  68. package/build/public/icons/array.svg +0 -4
  69. package/build/public/icons/bin.svg +0 -37
  70. package/build/public/icons/boolean.svg +0 -4
  71. package/build/public/icons/check.svg +0 -3
  72. package/build/public/icons/chevron-down.svg +0 -3
  73. package/build/public/icons/code.svg +0 -4
  74. package/build/public/icons/cross.svg +0 -11
  75. package/build/public/icons/data-tool.svg +0 -5
  76. package/build/public/icons/database.svg +0 -4
  77. package/build/public/icons/datetime.svg +0 -4
  78. package/build/public/icons/double-arrow-right.svg +0 -4
  79. package/build/public/icons/download.svg +0 -4
  80. package/build/public/icons/ellipsis.svg +0 -10
  81. package/build/public/icons/enum.svg +0 -6
  82. package/build/public/icons/expand.svg +0 -4
  83. package/build/public/icons/eye.svg +0 -20
  84. package/build/public/icons/filters.svg +0 -5
  85. package/build/public/icons/folder.svg +0 -6
  86. package/build/public/icons/hamburger.svg +0 -4
  87. package/build/public/icons/icon.svg +0 -199
  88. package/build/public/icons/logo.svg +0 -199
  89. package/build/public/icons/logotype.svg +0 -4
  90. package/build/public/icons/number.svg +0 -7
  91. package/build/public/icons/object.svg +0 -5
  92. package/build/public/icons/play.svg +0 -6
  93. package/build/public/icons/plus.svg +0 -4
  94. package/build/public/icons/refresh.svg +0 -4
  95. package/build/public/icons/search.svg +0 -7
  96. package/build/public/icons/settings.svg +0 -8
  97. package/build/public/icons/string.svg +0 -4
  98. package/build/public/icons/tick-indeterminate.svg +0 -3
  99. package/build/public/icons/tick.svg +0 -4
  100. package/build/public/illustrations/.DS_Store +0 -0
  101. package/build/public/illustrations/empty.svg +0 -1
  102. package/build/public/illustrations/read.svg +0 -1
  103. package/build/public/illustrations/searching.svg +0 -1
  104. package/build/public/images/.DS_Store +0 -0
  105. package/build/public/images/icon-1024.png +0 -0
  106. package/build/public/index.html +0 -49
  107. package/build/public/main.31f688a6254e294d9128.css +0 -118
  108. package/build/public/main.31f688a6254e294d9128.css.map +0 -1
  109. package/build/public/main.364c712f32662ff0e43d.css +0 -116
  110. package/build/public/main.364c712f32662ff0e43d.css.map +0 -1
  111. package/build/public/main.42ef3fe7e97347765c91.css +0 -118
  112. package/build/public/main.42ef3fe7e97347765c91.css.map +0 -1
  113. package/build/public/main.503446defafe7aeca2f3.css +0 -116
  114. package/build/public/main.503446defafe7aeca2f3.css.map +0 -1
  115. package/build/public/main.c50a3b5980fe26f78b65.css +0 -118
  116. package/build/public/main.c50a3b5980fe26f78b65.css.map +0 -1
  117. package/build/public/main.cda25e5813776c2af295.css +0 -116
  118. package/build/public/main.cda25e5813776c2af295.css.map +0 -1
  119. package/build/public/main.dbac3b290f78404ff579.css +0 -116
  120. package/build/public/main.dbac3b290f78404ff579.css.map +0 -1
  121. package/build/public/main.f8ce44e122e5e2b8f778.css +0 -118
  122. package/build/public/main.f8ce44e122e5e2b8f778.css.map +0 -1
  123. package/build/public/main.fc9ea7521a4aecce1a77.css +0 -118
  124. package/build/public/main.fc9ea7521a4aecce1a77.css.map +0 -1
  125. package/build/public/main.js +0 -403
  126. package/build/public/main.js.map +0 -1
  127. package/build/public/mainChunk.0a84f3ba4d2ab481e877.css +0 -116
  128. package/build/public/mainChunk.1b327d58afbddc917bce.css +0 -118
  129. package/build/public/mainChunk.2cdf583b2bd51aa67587.css +0 -118
  130. package/build/public/mainChunk.544af00f7e9ffcbe782c.css +0 -118
  131. package/build/public/mainChunk.56f96a13868b2b4a53be.css +0 -118
  132. package/build/public/mainChunk.5cee429bfbf06e7ecb39.css +0 -118
  133. package/build/public/mainChunk.a862474ed4cdb421ffa2.css +0 -118
  134. package/build/public/mainChunk.ada34153c4911b5ac22b.css +0 -118
  135. package/build/public/mainChunk.c16a8d01b2c49b2f2751.css +0 -118
  136. package/build/public/mainChunk.ca4a878d5478b9320be8.css +0 -116
  137. package/build/public/mainChunk.e3adc8758b4395546cef.css +0 -116
  138. package/build/public/mainChunk.f2c59fcbfc455d8b9de5.css +0 -118
  139. package/build/public/mainChunk.js +0 -396
  140. package/build/public/preview.html +0 -16
  141. package/build/public/previewBus.js +0 -2
  142. package/build/public/previewChunk.js +0 -2
  143. package/build/public/projects.html +0 -86
  144. package/build/public/server.html +0 -16
  145. package/build/public/serverBus.js +0 -2
  146. package/build/public/serverChunk.js +0 -2
  147. package/build/public/splash.html +0 -17
  148. package/build/public/studioBundle.828f34b1781061528841.css +0 -118
  149. package/build/public/studioBundle.bfe9138b2e0293fcb7da.css +0 -118
  150. package/build/public/studioBundle.c5b256eede880e502aac.css +0 -118
  151. package/build/public/studioBundle.e43df511c8e2e368900a.css +0 -118
  152. package/build/public/studioBundle.e69382554c2a2942ae32.css +0 -118
  153. package/build/public/studioBundle.ee21aa56a3999a2b380b.css +0 -118
  154. package/build/public/studioBundle.js +0 -396
  155. package/build/public/vercel.html +0 -16
  156. package/build/public/vercelChunk.js +0 -2
  157. package/build/xdg-open +0 -1066
  158. package/install/index.js +0 -5
  159. package/preinstall/index.js +0 -71
  160. package/prisma-client/README.md +0 -27
  161. package/prisma-client/generator-build/.DS_Store +0 -0
  162. package/prisma-client/generator-build/index.js +0 -79697
  163. package/prisma-client/index-browser.js +0 -3
  164. package/prisma-client/index.d.ts +0 -1
  165. package/prisma-client/index.js +0 -11
  166. package/prisma-client/package.json +0 -138
  167. package/prisma-client/runtime/index-browser.d.ts +0 -267
  168. package/prisma-client/runtime/index-browser.js +0 -2273
  169. package/prisma-client/runtime/index.d.ts +0 -1018
  170. package/prisma-client/runtime/index.js +0 -35028
  171. package/prisma-client/scripts/backup-index-browser.js +0 -3
  172. package/prisma-client/scripts/backup-index.d.ts +0 -1
  173. package/prisma-client/scripts/backup-index.js +0 -11
  174. package/prisma-client/scripts/colors.js +0 -180
  175. package/prisma-client/scripts/default-index-browser.js +0 -12
  176. package/prisma-client/scripts/default-index.d.ts +0 -47
  177. package/prisma-client/scripts/default-index.js +0 -12
  178. package/prisma-client/scripts/get-packed-client.js +0 -12
  179. package/prisma-client/scripts/mock-fs.js +0 -14
  180. package/prisma-client/scripts/postinstall.d.ts +0 -5
  181. package/prisma-client/scripts/postinstall.js +0 -398
  182. package/scripts/install-entry.js +0 -8
  183. 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 };