@prisma/cli 3.0.0-beta.3 → 3.0.0-beta.30

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 (99) hide show
  1. package/README.md +6 -15
  2. package/dist/adapters/local-state.js +15 -4
  3. package/dist/adapters/mock-api.js +244 -0
  4. package/dist/adapters/token-storage.js +335 -34
  5. package/dist/cli.js +7 -7
  6. package/dist/cli2.js +24 -5
  7. package/dist/commands/agent/index.js +60 -0
  8. package/dist/commands/app/index.js +91 -61
  9. package/dist/commands/auth/index.js +55 -2
  10. package/dist/commands/branch/index.js +2 -27
  11. package/dist/commands/bucket/index.js +123 -0
  12. package/dist/commands/build/index.js +29 -0
  13. package/dist/commands/database/index.js +249 -0
  14. package/dist/commands/env.js +8 -4
  15. package/dist/commands/feedback/index.js +20 -0
  16. package/dist/commands/git/index.js +1 -1
  17. package/dist/commands/init/index.js +33 -0
  18. package/dist/commands/project/index.js +54 -5
  19. package/dist/controllers/agent-setup.js +52 -0
  20. package/dist/controllers/agent.js +228 -0
  21. package/dist/controllers/app-env-api.js +55 -0
  22. package/dist/controllers/app-env-file.js +181 -0
  23. package/dist/controllers/app-env.js +227 -104
  24. package/dist/controllers/app.js +746 -306
  25. package/dist/controllers/auth.js +247 -3
  26. package/dist/controllers/branch.js +78 -48
  27. package/dist/controllers/bucket.js +278 -0
  28. package/dist/controllers/build.js +88 -0
  29. package/dist/controllers/database.js +567 -0
  30. package/dist/controllers/feedback.js +86 -0
  31. package/dist/controllers/init.js +753 -0
  32. package/dist/controllers/project.js +377 -22
  33. package/dist/controllers/select-prompt-port.js +1 -0
  34. package/dist/lib/agent/cli-command.js +20 -0
  35. package/dist/lib/agent/constants.js +12 -0
  36. package/dist/lib/agent/package-manager.js +99 -0
  37. package/dist/lib/agent/setup-status.js +83 -0
  38. package/dist/lib/app/{preview-provider.js → app-provider.js} +137 -88
  39. package/dist/lib/app/branch-database-api.js +102 -0
  40. package/dist/lib/app/branch-database-deploy.js +326 -0
  41. package/dist/lib/app/branch-database.js +216 -0
  42. package/dist/lib/app/build-settings.js +93 -0
  43. package/dist/lib/app/build.js +83 -0
  44. package/dist/lib/app/bun-project.js +3 -4
  45. package/dist/lib/app/compute-config.js +145 -0
  46. package/dist/lib/app/deploy-plan.js +59 -0
  47. package/dist/lib/app/{preview-progress.js → deploy-progress.js} +12 -12
  48. package/dist/lib/app/env-config.js +1 -1
  49. package/dist/lib/app/env-file.js +82 -0
  50. package/dist/lib/app/env-vars.js +28 -2
  51. package/dist/lib/app/local-dev.js +3 -60
  52. package/dist/lib/app/production-deploy-gate.js +162 -0
  53. package/dist/lib/app/read-branch.js +30 -0
  54. package/dist/lib/auth/auth-ops.js +10 -4
  55. package/dist/lib/auth/guard.js +4 -1
  56. package/dist/lib/auth/login.js +33 -26
  57. package/dist/lib/auth/recipient.js +42 -0
  58. package/dist/lib/bucket/provider.js +139 -0
  59. package/dist/lib/database/provider.js +378 -0
  60. package/dist/lib/diagnostics.js +15 -0
  61. package/dist/lib/fs/home-path.js +24 -0
  62. package/dist/lib/git/local-branch.js +53 -0
  63. package/dist/lib/git/local-status.js +57 -0
  64. package/dist/lib/project/interactive-setup.js +5 -4
  65. package/dist/lib/project/local-pin.js +171 -41
  66. package/dist/lib/project/provider.js +92 -0
  67. package/dist/lib/project/resolution.js +199 -48
  68. package/dist/lib/project/setup.js +67 -20
  69. package/dist/output/patterns.js +1 -1
  70. package/dist/presenters/agent.js +74 -0
  71. package/dist/presenters/app-env.js +149 -14
  72. package/dist/presenters/app.js +208 -27
  73. package/dist/presenters/auth.js +99 -2
  74. package/dist/presenters/branch.js +37 -102
  75. package/dist/presenters/bucket.js +174 -0
  76. package/dist/presenters/database.js +448 -0
  77. package/dist/presenters/feedback.js +26 -0
  78. package/dist/presenters/init.js +30 -0
  79. package/dist/presenters/project.js +139 -27
  80. package/dist/presenters/verbose-context.js +64 -0
  81. package/dist/shell/cli-command.js +12 -0
  82. package/dist/shell/command-arguments.js +7 -1
  83. package/dist/shell/command-meta.js +458 -17
  84. package/dist/shell/command-runner.js +58 -18
  85. package/dist/shell/diagnostics-output.js +57 -0
  86. package/dist/shell/errors.js +56 -1
  87. package/dist/shell/help.js +31 -20
  88. package/dist/shell/output.js +72 -1
  89. package/dist/shell/prompt.js +12 -5
  90. package/dist/shell/runtime.js +8 -4
  91. package/dist/shell/ui.js +42 -3
  92. package/dist/shell/update-check.js +2 -2
  93. package/dist/use-cases/auth.js +68 -1
  94. package/dist/use-cases/branch.js +20 -68
  95. package/dist/use-cases/create-cli-gateways.js +2 -17
  96. package/dist/use-cases/project.js +2 -1
  97. package/package.json +21 -4
  98. package/dist/lib/app/preview-build.js +0 -312
  99. package/dist/lib/app/preview-interaction.js +0 -5
@@ -1,42 +1,38 @@
1
1
  import { CliError, authRequiredError, usageError, workspaceRequiredError } from "../shell/errors.js";
2
+ import { formatScopeLabel, parseKeyValuePositional, resolveEnvScope } from "../lib/app/env-config.js";
3
+ import { readEnvFileAssignments } from "../lib/app/env-file.js";
4
+ import { projectResolutionErrorToCliError, resolveProjectTarget } from "../lib/project/resolution.js";
2
5
  import { requireComputeAuth } from "../lib/auth/guard.js";
3
- import { resolveProjectTarget } from "../lib/project/resolution.js";
6
+ import { readLocalGitBranch } from "../lib/git/local-branch.js";
4
7
  import { requireAuthenticatedAuthState } from "./auth.js";
5
8
  import { listRealWorkspaceProjects } from "./project.js";
6
- import { formatScopeLabel, parseKeyValuePositional, resolveEnvScope } from "../lib/app/env-config.js";
9
+ import { apiCallError, findVariableByNaturalKey, toMetadata } from "./app-env-api.js";
10
+ import { runEnvAddFile, runEnvUpdateFile } from "./app-env-file.js";
7
11
  //#region src/controllers/app-env.ts
8
- function defaultRoleScope() {
9
- return {
10
- kind: "role",
11
- role: "production"
12
- };
13
- }
14
12
  async function runEnvAdd(context, rawAssignment, flags) {
15
- const { key, value } = parseKeyValuePositional(rawAssignment, "add", context.runtime.env);
13
+ const source = resolveEnvWriteSource(rawAssignment, flags.filePath, "add");
16
14
  const scope = resolveEnvScope(flags, {
17
15
  requireExplicit: true,
18
16
  command: "add"
19
17
  });
20
- if (!scope) throw usageError(`prisma-cli project env add requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [`prisma-cli project env add ${key}=${value} --role production`], "app");
21
- const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env add");
18
+ if (!scope) throw usageError(`prisma-cli project env add requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", ["prisma-cli project env add KEY=value --role production"], "app");
19
+ const input = await resolveEnvWriteInput(context, source, "add");
20
+ const { client, projectId, verboseContext } = await requireClientAndProject(context, flags.projectRef, "project env add");
22
21
  const resolved = await resolveScopeToApi(client, projectId, scope, {
23
22
  createBranchIfMissing: true,
24
23
  signal: context.runtime.signal
25
24
  });
26
- if (await findVariableByNaturalKey(client, projectId, key, resolved, context.runtime.signal)) throw new CliError({
25
+ if (input.kind === "file") return runEnvAddFile(context, client, projectId, resolved, input.filePath, input.assignments, verboseContext);
26
+ if (await findVariableByNaturalKey(client, projectId, input.key, resolved, context.runtime.signal)) throw new CliError({
27
27
  code: "ENV_VARIABLE_ALREADY_EXISTS",
28
28
  domain: "app",
29
- summary: `Variable "${key}" already exists in ${formatScopeLabel(scope)}`,
29
+ summary: `Variable "${input.key}" already exists in ${formatScopeLabel(scope)}`,
30
30
  why: "A variable with this key already exists in the targeted scope.",
31
31
  fix: "Use `prisma-cli project env update` to change an existing variable's value.",
32
32
  exitCode: 1,
33
- nextSteps: [`prisma-cli project env update ${key}=<new-value> ${formatScopeFlag(scope)}`]
33
+ nextSteps: [`prisma-cli project env update ${input.key}=<new-value> ${formatScopeFlag(scope)}`]
34
34
  });
35
- const warnings = scope.kind === "branch" && !await findVariableByNaturalKey(client, projectId, key, {
36
- scope: {
37
- kind: "role",
38
- role: "preview"
39
- },
35
+ const warnings = scope.kind === "branch" && !await findVariableByNaturalKey(client, projectId, input.key, {
40
36
  descriptor: {
41
37
  kind: "role",
42
38
  role: "preview"
@@ -45,22 +41,23 @@ async function runEnvAdd(context, rawAssignment, flags) {
45
41
  class: "preview",
46
42
  branchId: null
47
43
  }
48
- }, context.runtime.signal) ? [`Variable "${key}" does not exist in preview. It will only exist on ${formatScopeLabel(scope)}.`] : [];
44
+ }, context.runtime.signal) ? [`Variable "${input.key}" does not exist in preview. It will only exist on ${formatScopeLabel(scope)}.`] : [];
49
45
  const { data, error, response } = await client.POST("/v1/environment-variables", {
50
46
  body: {
51
47
  projectId,
52
48
  class: resolved.apiTarget.class,
53
49
  ...resolved.apiTarget.branchId !== null ? { branchId: resolved.apiTarget.branchId } : {},
54
- key,
55
- value
50
+ key: input.key,
51
+ value: input.value
56
52
  },
57
53
  signal: context.runtime.signal
58
54
  });
59
- if (error || !data) throw apiCallError(`Failed to add ${key}`, response, error);
55
+ if (error || !data) throw apiCallError(`Failed to add ${input.key}`, response, error);
60
56
  return {
61
57
  command: "project.env.add",
62
58
  result: {
63
59
  projectId,
60
+ verboseContext,
64
61
  scope: resolved.descriptor,
65
62
  variable: toMetadata(data.data, resolved.descriptor)
66
63
  },
@@ -69,37 +66,40 @@ async function runEnvAdd(context, rawAssignment, flags) {
69
66
  };
70
67
  }
71
68
  async function runEnvUpdate(context, rawAssignment, flags) {
72
- const { key, value } = parseKeyValuePositional(rawAssignment, "update", context.runtime.env);
69
+ const source = resolveEnvWriteSource(rawAssignment, flags.filePath, "update");
73
70
  const scope = resolveEnvScope(flags, {
74
71
  requireExplicit: true,
75
72
  command: "update"
76
73
  });
77
- if (!scope) throw usageError(`prisma-cli project env update requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [`prisma-cli project env update ${key}=${value} --role production`], "app");
78
- const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env update");
74
+ if (!scope) throw usageError(`prisma-cli project env update requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", ["prisma-cli project env update KEY=value --role production"], "app");
75
+ const input = await resolveEnvWriteInput(context, source, "update");
76
+ const { client, projectId, verboseContext } = await requireClientAndProject(context, flags.projectRef, "project env update");
79
77
  const resolved = await resolveScopeToApi(client, projectId, scope, {
80
78
  createBranchIfMissing: false,
81
79
  signal: context.runtime.signal
82
80
  });
83
- const existing = await findVariableByNaturalKey(client, projectId, key, resolved, context.runtime.signal);
81
+ if (input.kind === "file") return runEnvUpdateFile(context, client, projectId, resolved, input.filePath, input.assignments, verboseContext);
82
+ const existing = await findVariableByNaturalKey(client, projectId, input.key, resolved, context.runtime.signal);
84
83
  if (!existing) throw new CliError({
85
84
  code: "ENV_VARIABLE_NOT_FOUND",
86
85
  domain: "app",
87
- summary: `Variable "${key}" not found in ${formatScopeLabel(scope)}`,
86
+ summary: `Variable "${input.key}" not found in ${formatScopeLabel(scope)}`,
88
87
  why: "No variable with this key exists in the targeted scope.",
89
88
  fix: "Use `prisma-cli project env add` to create a new variable.",
90
89
  exitCode: 1,
91
- nextSteps: [`prisma-cli project env add ${key}=<value> ${formatScopeFlag(scope)}`]
90
+ nextSteps: [`prisma-cli project env add ${input.key}=<value> ${formatScopeFlag(scope)}`]
92
91
  });
93
92
  const { data, error, response } = await client.PATCH("/v1/environment-variables/{envVarId}", {
94
93
  params: { path: { envVarId: existing.id } },
95
- body: { value },
94
+ body: { value: input.value },
96
95
  signal: context.runtime.signal
97
96
  });
98
- if (error || !data) throw apiCallError(`Failed to update value for ${key}`, response, error);
97
+ if (error || !data) throw apiCallError(`Failed to update value for ${input.key}`, response, error);
99
98
  return {
100
99
  command: "project.env.update",
101
100
  result: {
102
101
  projectId,
102
+ verboseContext,
103
103
  scope: resolved.descriptor,
104
104
  variable: toMetadata(data.data, resolved.descriptor)
105
105
  },
@@ -107,26 +107,58 @@ async function runEnvUpdate(context, rawAssignment, flags) {
107
107
  nextSteps: []
108
108
  };
109
109
  }
110
+ function resolveEnvWriteSource(rawAssignment, filePath, command) {
111
+ if (filePath !== void 0 && rawAssignment !== void 0) throw usageError(`prisma-cli 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-cli project env ${command} KEY=value --role preview`, `prisma-cli project env ${command} --file .env --role preview`], "app");
112
+ if (filePath !== void 0) {
113
+ if (filePath.length === 0) throw usageError(`prisma-cli project env ${command} --file requires a path`, "The --file flag was passed without a file path.", "Pass a readable dotenv file path.", [`prisma-cli project env ${command} --file .env --role preview`], "app");
114
+ return {
115
+ kind: "file",
116
+ filePath
117
+ };
118
+ }
119
+ if (rawAssignment === void 0) throw usageError(`prisma-cli 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-cli project env ${command} KEY=value --role preview`, `prisma-cli project env ${command} --file .env --role preview`], "app");
120
+ return {
121
+ kind: "single",
122
+ rawAssignment
123
+ };
124
+ }
125
+ async function resolveEnvWriteInput(context, source, command) {
126
+ if (source.kind === "file") return {
127
+ kind: "file",
128
+ filePath: source.filePath,
129
+ assignments: await readEnvFileAssignments(context.runtime.cwd, source.filePath, command)
130
+ };
131
+ return {
132
+ kind: "single",
133
+ ...parseKeyValuePositional(source.rawAssignment, command, context.runtime.env)
134
+ };
135
+ }
110
136
  async function runEnvList(context, flags) {
111
- const scope = resolveEnvScope(flags, {
137
+ const explicit = resolveEnvScope(flags, {
112
138
  requireExplicit: false,
113
139
  command: "list"
114
- }) ?? defaultRoleScope();
115
- const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env list");
116
- const resolved = await resolveScopeToApi(client, projectId, scope, {
117
- createBranchIfMissing: false,
140
+ });
141
+ const { client, projectId, verboseContext } = await requireClientAndProject(context, flags.projectRef, "project env list");
142
+ const resolved = await resolveListScopeToApi(client, projectId, explicit ?? void 0, {
143
+ cwd: context.runtime.cwd,
118
144
  signal: context.runtime.signal
119
145
  });
120
- const variables = await listVariables(client, projectId, resolved, context.runtime.signal);
146
+ const variables = resolved.kind === "scoped" ? await listVariables(client, projectId, {
147
+ scope: resolved.addScope,
148
+ descriptor: resolved.descriptor,
149
+ apiTarget: resolved.apiTarget
150
+ }, context.runtime.signal) : await listOverviewVariables(client, projectId, context.runtime.signal);
121
151
  return {
122
152
  command: "project.env.list",
123
153
  result: {
124
154
  projectId,
155
+ verboseContext,
125
156
  scope: resolved.descriptor,
157
+ target: resolved.target,
126
158
  variables: variables.map((row) => toMetadata(row, resolved.descriptor))
127
159
  },
128
160
  warnings: [],
129
- nextSteps: variables.length === 0 ? [`prisma-cli project env add KEY=value ${formatScopeFlag(scope)}`] : []
161
+ nextSteps: variables.length === 0 ? [`prisma-cli project env add KEY=value ${formatScopeFlag(resolved.addScope)}`] : []
130
162
  };
131
163
  }
132
164
  async function runEnvRemove(context, key, flags) {
@@ -136,7 +168,7 @@ async function runEnvRemove(context, key, flags) {
136
168
  command: "remove"
137
169
  });
138
170
  if (!scope) throw usageError("prisma-cli project env remove requires --role or --branch", "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [`prisma-cli project env remove ${key} --role production`], "app");
139
- const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env remove");
171
+ const { client, projectId, verboseContext } = await requireClientAndProject(context, flags.projectRef, "project env remove");
140
172
  const resolved = await resolveScopeToApi(client, projectId, scope, {
141
173
  createBranchIfMissing: false,
142
174
  signal: context.runtime.signal
@@ -160,6 +192,7 @@ async function runEnvRemove(context, key, flags) {
160
192
  command: "project.env.remove",
161
193
  result: {
162
194
  projectId,
195
+ verboseContext,
163
196
  scope: resolved.descriptor,
164
197
  key
165
198
  },
@@ -171,16 +204,25 @@ async function requireClientAndProject(context, explicitProject, commandName) {
171
204
  const authState = await requireAuthenticatedAuthState(context);
172
205
  const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
173
206
  if (!client) throw authRequiredError(["prisma-cli auth login"]);
174
- if (!authState.workspace) throw workspaceRequiredError();
207
+ const workspace = authState.workspace;
208
+ if (!workspace) throw workspaceRequiredError();
209
+ const targetResult = await resolveProjectTarget({
210
+ context,
211
+ workspace,
212
+ explicitProject,
213
+ listProjects: () => listRealWorkspaceProjects(client, workspace, context.runtime.signal),
214
+ commandName
215
+ });
216
+ if (targetResult.isErr()) throw projectResolutionErrorToCliError(targetResult.error);
217
+ const target = targetResult.value;
175
218
  return {
176
219
  client,
177
- projectId: (await resolveProjectTarget({
178
- context,
179
- workspace: authState.workspace,
180
- explicitProject,
181
- listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
182
- commandName
183
- })).project.id
220
+ projectId: target.project.id,
221
+ verboseContext: {
222
+ workspace,
223
+ project: target.project,
224
+ resolution: target.resolution
225
+ }
184
226
  };
185
227
  }
186
228
  async function resolveScopeToApi(client, projectId, scope, options) {
@@ -196,12 +238,12 @@ async function resolveScopeToApi(client, projectId, scope, options) {
196
238
  }
197
239
  };
198
240
  const branch = options.createBranchIfMissing ? await resolveOrCreateBranch(client, projectId, scope.branchName, options.signal) : await resolveExistingBranch(client, projectId, scope.branchName, options.signal);
199
- if (branch.isDefault) throw new CliError({
241
+ if (branch.role === "production") throw new CliError({
200
242
  code: "ENV_BRANCH_SCOPE_IS_PRODUCTION",
201
243
  domain: "app",
202
- summary: `Branch "${scope.branchName}" is the default branch`,
244
+ summary: `Branch "${scope.branchName}" is the production branch`,
203
245
  why: "Production variables are project-level only; branch overrides apply to preview branches.",
204
- fix: "Use --role production for the default branch.",
246
+ fix: "Use --role production for the production branch.",
205
247
  exitCode: 1,
206
248
  nextSteps: ["prisma-cli project env list --role production"]
207
249
  });
@@ -218,6 +260,123 @@ async function resolveScopeToApi(client, projectId, scope, options) {
218
260
  }
219
261
  };
220
262
  }
263
+ async function resolveListScopeToApi(client, projectId, explicit, options) {
264
+ if (explicit) {
265
+ const resolved = await resolveScopeToApi(client, projectId, explicit, {
266
+ createBranchIfMissing: false,
267
+ signal: options.signal
268
+ });
269
+ return {
270
+ kind: "scoped",
271
+ descriptor: resolved.descriptor,
272
+ target: targetFromExplicitScope(resolved.descriptor),
273
+ apiTarget: resolved.apiTarget,
274
+ addScope: resolved.scope
275
+ };
276
+ }
277
+ const gitBranch = await readLocalGitBranch(options.cwd, options.signal);
278
+ if (gitBranch) {
279
+ const branch = (await listBranchesByName(client, projectId, gitBranch, options.signal))[0];
280
+ if (!branch) return {
281
+ kind: "scoped",
282
+ descriptor: {
283
+ kind: "role",
284
+ role: "preview"
285
+ },
286
+ target: {
287
+ source: "local-git",
288
+ branchName: gitBranch,
289
+ branchExists: false,
290
+ envMap: "preview"
291
+ },
292
+ apiTarget: {
293
+ class: "preview",
294
+ branchId: null
295
+ },
296
+ addScope: {
297
+ kind: "branch",
298
+ branchName: gitBranch
299
+ }
300
+ };
301
+ if (branch.role === "production") return {
302
+ kind: "scoped",
303
+ descriptor: {
304
+ kind: "role",
305
+ role: "production"
306
+ },
307
+ target: {
308
+ source: "local-git",
309
+ branchName: branch.gitName,
310
+ branchId: branch.id,
311
+ branchRole: branch.role,
312
+ branchExists: true,
313
+ envMap: "production"
314
+ },
315
+ apiTarget: {
316
+ class: "production",
317
+ branchId: null
318
+ },
319
+ addScope: {
320
+ kind: "role",
321
+ role: "production"
322
+ }
323
+ };
324
+ return {
325
+ kind: "scoped",
326
+ descriptor: {
327
+ kind: "branch",
328
+ branchName: branch.gitName,
329
+ branchId: branch.id
330
+ },
331
+ target: {
332
+ source: "local-git",
333
+ branchName: branch.gitName,
334
+ branchId: branch.id,
335
+ branchRole: branch.role,
336
+ branchExists: true,
337
+ envMap: "preview"
338
+ },
339
+ apiTarget: {
340
+ class: "preview",
341
+ branchId: branch.id
342
+ },
343
+ addScope: {
344
+ kind: "branch",
345
+ branchName: branch.gitName
346
+ }
347
+ };
348
+ }
349
+ return {
350
+ kind: "overview",
351
+ descriptor: { kind: "overview" },
352
+ target: {
353
+ source: "overview",
354
+ envMap: "overview"
355
+ },
356
+ addScope: {
357
+ kind: "role",
358
+ role: "preview"
359
+ }
360
+ };
361
+ }
362
+ function targetFromExplicitScope(scope) {
363
+ if (scope.kind === "branch") return {
364
+ source: "explicit",
365
+ branchName: scope.branchName,
366
+ branchId: scope.branchId,
367
+ branchRole: "preview",
368
+ branchExists: true,
369
+ envMap: "preview"
370
+ };
371
+ if (scope.kind === "role") return {
372
+ source: "explicit",
373
+ envMap: scope.role
374
+ };
375
+ return {
376
+ source: "overview",
377
+ envMap: "overview"
378
+ };
379
+ }
221
380
  function formatScopeFlag(scope) {
222
381
  if (scope.kind === "role") return `--role ${scope.role}`;
223
382
  return `--branch ${scope.branchName}`;
@@ -293,47 +452,45 @@ async function projectHasDefaultBranch(client, projectId, signal) {
293
452
  cursor = result.data.pagination.nextCursor;
294
453
  }
295
454
  }
296
- async function findVariableByNaturalKey(client, projectId, key, resolved, signal) {
297
- const { data, error, response } = await client.GET("/v1/environment-variables", {
298
- params: { query: {
299
- projectId,
300
- class: resolved.apiTarget.class,
301
- key
302
- } },
303
- signal
455
+ async function listVariables(client, projectId, resolved, signal) {
456
+ return materializeEffectiveRows(await collectEnvironmentVariables(client, projectId, signal, {
457
+ className: resolved.apiTarget.class,
458
+ filter: (row) => rowMatchesScope(row, resolved)
459
+ }), resolved);
460
+ }
461
+ async function listOverviewVariables(client, projectId, signal) {
462
+ return (await collectEnvironmentVariables(client, projectId, signal, { filter: (row) => row.branchId === null && (row.class === "production" || row.class === "preview") })).sort((left, right) => {
463
+ const roleOrder = roleSortOrder(left.class) - roleSortOrder(right.class);
464
+ return roleOrder !== 0 ? roleOrder : left.key.localeCompare(right.key);
304
465
  });
305
- if (error || !data) throw apiCallError(`Failed to look up ${key}`, response, error);
306
- return data.data.filter((row) => rowMatchesExactScope(row, resolved))[0] ?? null;
307
466
  }
308
- async function listVariables(client, projectId, resolved, signal) {
467
+ async function collectEnvironmentVariables(client, projectId, signal, options) {
309
468
  const collected = [];
310
469
  let cursor;
311
470
  while (true) {
312
- const query = {
313
- projectId,
314
- class: resolved.apiTarget.class
315
- };
471
+ const query = { projectId };
472
+ if (options.className !== void 0) query.class = options.className;
316
473
  if (cursor !== void 0) query.cursor = cursor;
317
474
  const result = await client.GET("/v1/environment-variables", {
318
475
  params: { query },
319
476
  signal
320
477
  });
321
478
  if (result.error || !result.data) throw apiCallError(`Failed to list environment variables`, result.response, result.error);
322
- const page = result.data.data.filter((row) => rowMatchesScope(row, resolved));
479
+ const page = result.data.data.filter(options.filter);
323
480
  collected.push(...page);
324
481
  if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
325
482
  cursor = result.data.pagination.nextCursor;
326
483
  }
327
- return materializeEffectiveRows(collected, resolved);
484
+ return collected;
485
+ }
486
+ function roleSortOrder(role) {
487
+ return role === "production" ? 0 : 1;
328
488
  }
329
489
  function rowMatchesScope(row, resolved) {
330
490
  if (row.class !== resolved.apiTarget.class) return false;
331
491
  if (resolved.apiTarget.branchId === null) return row.branchId === null;
332
492
  return row.branchId === null || row.branchId === resolved.apiTarget.branchId;
333
493
  }
334
- function rowMatchesExactScope(row, resolved) {
335
- return row.class === resolved.apiTarget.class && row.branchId === resolved.apiTarget.branchId;
336
- }
337
494
  function materializeEffectiveRows(rows, resolved) {
338
495
  if (resolved.apiTarget.branchId === null) return rows;
339
496
  const byKey = /* @__PURE__ */ new Map();
@@ -341,39 +498,5 @@ function materializeEffectiveRows(rows, resolved) {
341
498
  for (const row of rows) if (row.branchId === resolved.apiTarget.branchId) byKey.set(row.key, row);
342
499
  return [...byKey.values()].sort((left, right) => left.key.localeCompare(right.key));
343
500
  }
344
- function toMetadata(row, requestedScope) {
345
- const rowScope = row.branchId === null ? {
346
- kind: "role",
347
- role: row.class
348
- } : requestedScope;
349
- return {
350
- id: row.id,
351
- key: row.key,
352
- scope: rowScope,
353
- source: formatDescriptorLabel(rowScope),
354
- isManagedBySystem: row.isManagedBySystem,
355
- updatedAt: row.updatedAt
356
- };
357
- }
358
- function formatDescriptorLabel(scope) {
359
- if (scope.kind === "role") return scope.role ?? "unknown";
360
- return `branch:${scope.branchName ?? scope.branchId ?? "unknown"}`;
361
- }
362
- function apiCallError(summary, response, error) {
363
- const status = response?.status ?? 0;
364
- const apiCode = error?.error?.code;
365
- const apiMessage = error?.error?.message;
366
- const apiHint = error?.error?.hint;
367
- if (status === 401 || status === 403) return authRequiredError(["prisma auth login"]);
368
- return new CliError({
369
- code: apiCode ?? "ENV_API_ERROR",
370
- domain: "app",
371
- summary,
372
- why: apiMessage ?? `The Management API returned status ${status || "unknown"}.`,
373
- fix: apiHint ?? "Re-run with --trace for the underlying API response details.",
374
- exitCode: 1,
375
- nextSteps: []
376
- });
377
- }
378
501
  //#endregion
379
502
  export { runEnvAdd, runEnvList, runEnvRemove, runEnvUpdate };