@prisma/cli 3.0.0-beta.2 → 3.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/adapters/git.js +8 -3
- package/dist/adapters/local-state.js +11 -3
- package/dist/adapters/mock-api.js +6 -2
- package/dist/adapters/token-storage.js +63 -22
- package/dist/cli.js +17 -2
- package/dist/cli2.js +3 -0
- package/dist/commands/app/index.js +4 -2
- package/dist/commands/branch/index.js +2 -27
- package/dist/controllers/app-env.js +227 -64
- package/dist/controllers/app.js +116 -91
- package/dist/controllers/auth.js +8 -8
- package/dist/controllers/branch.js +73 -47
- package/dist/controllers/project.js +107 -67
- package/dist/lib/app/bun-project.js +12 -5
- package/dist/lib/app/local-dev.js +34 -18
- package/dist/lib/app/preview-build.js +90 -62
- package/dist/lib/app/preview-provider.js +143 -54
- package/dist/lib/app/production-deploy-gate.js +160 -0
- package/dist/lib/auth/auth-ops.js +30 -21
- package/dist/lib/auth/guard.js +3 -2
- package/dist/lib/auth/login.js +109 -14
- package/dist/lib/git/local-branch.js +41 -0
- package/dist/lib/project/interactive-setup.js +1 -1
- package/dist/lib/project/local-pin.js +27 -8
- package/dist/lib/project/resolution.js +14 -8
- package/dist/lib/project/setup.js +2 -2
- package/dist/presenters/app-env.js +14 -3
- package/dist/presenters/branch.js +29 -101
- package/dist/shell/command-meta.js +6 -24
- package/dist/shell/command-runner.js +9 -4
- package/dist/shell/errors.js +12 -1
- package/dist/shell/runtime.js +2 -2
- package/dist/shell/ui.js +4 -1
- package/dist/shell/update-check.js +247 -0
- package/dist/use-cases/branch.js +20 -68
- package/dist/use-cases/create-cli-gateways.js +2 -17
- package/package.json +10 -10
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import { CliError, authRequiredError, usageError, workspaceRequiredError } from "../shell/errors.js";
|
|
2
2
|
import { requireComputeAuth } from "../lib/auth/guard.js";
|
|
3
3
|
import { resolveProjectTarget } from "../lib/project/resolution.js";
|
|
4
|
+
import { readLocalGitBranch } from "../lib/git/local-branch.js";
|
|
4
5
|
import { requireAuthenticatedAuthState } from "./auth.js";
|
|
5
6
|
import { listRealWorkspaceProjects } from "./project.js";
|
|
6
7
|
import { formatScopeLabel, parseKeyValuePositional, resolveEnvScope } from "../lib/app/env-config.js";
|
|
7
8
|
//#region src/controllers/app-env.ts
|
|
8
|
-
function defaultRoleScope() {
|
|
9
|
-
return {
|
|
10
|
-
kind: "role",
|
|
11
|
-
role: "production"
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
9
|
async function runEnvAdd(context, rawAssignment, flags) {
|
|
15
10
|
const { key, value } = parseKeyValuePositional(rawAssignment, "add", context.runtime.env);
|
|
16
11
|
const scope = resolveEnvScope(flags, {
|
|
@@ -19,8 +14,11 @@ async function runEnvAdd(context, rawAssignment, flags) {
|
|
|
19
14
|
});
|
|
20
15
|
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
16
|
const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env add");
|
|
22
|
-
const resolved = await resolveScopeToApi(client, projectId, scope, {
|
|
23
|
-
|
|
17
|
+
const resolved = await resolveScopeToApi(client, projectId, scope, {
|
|
18
|
+
createBranchIfMissing: true,
|
|
19
|
+
signal: context.runtime.signal
|
|
20
|
+
});
|
|
21
|
+
if (await findVariableByNaturalKey(client, projectId, key, resolved, context.runtime.signal)) throw new CliError({
|
|
24
22
|
code: "ENV_VARIABLE_ALREADY_EXISTS",
|
|
25
23
|
domain: "app",
|
|
26
24
|
summary: `Variable "${key}" already exists in ${formatScopeLabel(scope)}`,
|
|
@@ -42,14 +40,17 @@ async function runEnvAdd(context, rawAssignment, flags) {
|
|
|
42
40
|
class: "preview",
|
|
43
41
|
branchId: null
|
|
44
42
|
}
|
|
45
|
-
}) ? [`Variable "${key}" does not exist in preview. It will only exist on ${formatScopeLabel(scope)}.`] : [];
|
|
46
|
-
const { data, error, response } = await client.POST("/v1/environment-variables", {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
}, context.runtime.signal) ? [`Variable "${key}" does not exist in preview. It will only exist on ${formatScopeLabel(scope)}.`] : [];
|
|
44
|
+
const { data, error, response } = await client.POST("/v1/environment-variables", {
|
|
45
|
+
body: {
|
|
46
|
+
projectId,
|
|
47
|
+
class: resolved.apiTarget.class,
|
|
48
|
+
...resolved.apiTarget.branchId !== null ? { branchId: resolved.apiTarget.branchId } : {},
|
|
49
|
+
key,
|
|
50
|
+
value
|
|
51
|
+
},
|
|
52
|
+
signal: context.runtime.signal
|
|
53
|
+
});
|
|
53
54
|
if (error || !data) throw apiCallError(`Failed to add ${key}`, response, error);
|
|
54
55
|
return {
|
|
55
56
|
command: "project.env.add",
|
|
@@ -70,8 +71,11 @@ async function runEnvUpdate(context, rawAssignment, flags) {
|
|
|
70
71
|
});
|
|
71
72
|
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");
|
|
72
73
|
const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env update");
|
|
73
|
-
const resolved = await resolveScopeToApi(client, projectId, scope, {
|
|
74
|
-
|
|
74
|
+
const resolved = await resolveScopeToApi(client, projectId, scope, {
|
|
75
|
+
createBranchIfMissing: false,
|
|
76
|
+
signal: context.runtime.signal
|
|
77
|
+
});
|
|
78
|
+
const existing = await findVariableByNaturalKey(client, projectId, key, resolved, context.runtime.signal);
|
|
75
79
|
if (!existing) throw new CliError({
|
|
76
80
|
code: "ENV_VARIABLE_NOT_FOUND",
|
|
77
81
|
domain: "app",
|
|
@@ -83,7 +87,8 @@ async function runEnvUpdate(context, rawAssignment, flags) {
|
|
|
83
87
|
});
|
|
84
88
|
const { data, error, response } = await client.PATCH("/v1/environment-variables/{envVarId}", {
|
|
85
89
|
params: { path: { envVarId: existing.id } },
|
|
86
|
-
body: { value }
|
|
90
|
+
body: { value },
|
|
91
|
+
signal: context.runtime.signal
|
|
87
92
|
});
|
|
88
93
|
if (error || !data) throw apiCallError(`Failed to update value for ${key}`, response, error);
|
|
89
94
|
return {
|
|
@@ -98,22 +103,30 @@ async function runEnvUpdate(context, rawAssignment, flags) {
|
|
|
98
103
|
};
|
|
99
104
|
}
|
|
100
105
|
async function runEnvList(context, flags) {
|
|
101
|
-
const
|
|
106
|
+
const explicit = resolveEnvScope(flags, {
|
|
102
107
|
requireExplicit: false,
|
|
103
108
|
command: "list"
|
|
104
|
-
})
|
|
109
|
+
});
|
|
105
110
|
const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env list");
|
|
106
|
-
const resolved = await
|
|
107
|
-
|
|
111
|
+
const resolved = await resolveListScopeToApi(client, projectId, explicit, {
|
|
112
|
+
cwd: context.runtime.cwd,
|
|
113
|
+
signal: context.runtime.signal
|
|
114
|
+
});
|
|
115
|
+
const variables = resolved.kind === "scoped" ? await listVariables(client, projectId, {
|
|
116
|
+
scope: resolved.addScope,
|
|
117
|
+
descriptor: resolved.descriptor,
|
|
118
|
+
apiTarget: resolved.apiTarget
|
|
119
|
+
}, context.runtime.signal) : await listOverviewVariables(client, projectId, context.runtime.signal);
|
|
108
120
|
return {
|
|
109
121
|
command: "project.env.list",
|
|
110
122
|
result: {
|
|
111
123
|
projectId,
|
|
112
124
|
scope: resolved.descriptor,
|
|
125
|
+
target: resolved.target,
|
|
113
126
|
variables: variables.map((row) => toMetadata(row, resolved.descriptor))
|
|
114
127
|
},
|
|
115
128
|
warnings: [],
|
|
116
|
-
nextSteps: variables.length === 0 ? [`prisma-cli project env add KEY=value ${formatScopeFlag(
|
|
129
|
+
nextSteps: variables.length === 0 ? [`prisma-cli project env add KEY=value ${formatScopeFlag(resolved.addScope)}`] : []
|
|
117
130
|
};
|
|
118
131
|
}
|
|
119
132
|
async function runEnvRemove(context, key, flags) {
|
|
@@ -124,8 +137,11 @@ async function runEnvRemove(context, key, flags) {
|
|
|
124
137
|
});
|
|
125
138
|
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");
|
|
126
139
|
const { client, projectId } = await requireClientAndProject(context, flags.projectRef, "project env remove");
|
|
127
|
-
const resolved = await resolveScopeToApi(client, projectId, scope, {
|
|
128
|
-
|
|
140
|
+
const resolved = await resolveScopeToApi(client, projectId, scope, {
|
|
141
|
+
createBranchIfMissing: false,
|
|
142
|
+
signal: context.runtime.signal
|
|
143
|
+
});
|
|
144
|
+
const existing = await findVariableByNaturalKey(client, projectId, key, resolved, context.runtime.signal);
|
|
129
145
|
if (!existing) throw new CliError({
|
|
130
146
|
code: "ENV_VARIABLE_NOT_FOUND",
|
|
131
147
|
domain: "app",
|
|
@@ -135,7 +151,10 @@ async function runEnvRemove(context, key, flags) {
|
|
|
135
151
|
exitCode: 1,
|
|
136
152
|
nextSteps: [`prisma-cli project env list ${formatScopeFlag(scope)}`]
|
|
137
153
|
});
|
|
138
|
-
const { error, response } = await client.DELETE("/v1/environment-variables/{envVarId}", {
|
|
154
|
+
const { error, response } = await client.DELETE("/v1/environment-variables/{envVarId}", {
|
|
155
|
+
params: { path: { envVarId: existing.id } },
|
|
156
|
+
signal: context.runtime.signal
|
|
157
|
+
});
|
|
139
158
|
if (error) throw apiCallError(`Failed to remove ${key}`, response, error);
|
|
140
159
|
return {
|
|
141
160
|
command: "project.env.remove",
|
|
@@ -150,7 +169,7 @@ async function runEnvRemove(context, key, flags) {
|
|
|
150
169
|
}
|
|
151
170
|
async function requireClientAndProject(context, explicitProject, commandName) {
|
|
152
171
|
const authState = await requireAuthenticatedAuthState(context);
|
|
153
|
-
const client = await requireComputeAuth(context.runtime.env);
|
|
172
|
+
const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
|
|
154
173
|
if (!client) throw authRequiredError(["prisma-cli auth login"]);
|
|
155
174
|
if (!authState.workspace) throw workspaceRequiredError();
|
|
156
175
|
return {
|
|
@@ -159,7 +178,7 @@ async function requireClientAndProject(context, explicitProject, commandName) {
|
|
|
159
178
|
context,
|
|
160
179
|
workspace: authState.workspace,
|
|
161
180
|
explicitProject,
|
|
162
|
-
listProjects: () => listRealWorkspaceProjects(client, authState.workspace),
|
|
181
|
+
listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
|
|
163
182
|
commandName
|
|
164
183
|
})).project.id
|
|
165
184
|
};
|
|
@@ -176,13 +195,13 @@ async function resolveScopeToApi(client, projectId, scope, options) {
|
|
|
176
195
|
branchId: null
|
|
177
196
|
}
|
|
178
197
|
};
|
|
179
|
-
const branch = options.createBranchIfMissing ? await resolveOrCreateBranch(client, projectId, scope.branchName) : await resolveExistingBranch(client, projectId, scope.branchName);
|
|
180
|
-
if (branch.
|
|
198
|
+
const branch = options.createBranchIfMissing ? await resolveOrCreateBranch(client, projectId, scope.branchName, options.signal) : await resolveExistingBranch(client, projectId, scope.branchName, options.signal);
|
|
199
|
+
if (branch.role === "production") throw new CliError({
|
|
181
200
|
code: "ENV_BRANCH_SCOPE_IS_PRODUCTION",
|
|
182
201
|
domain: "app",
|
|
183
|
-
summary: `Branch "${scope.branchName}" is the
|
|
202
|
+
summary: `Branch "${scope.branchName}" is the production branch`,
|
|
184
203
|
why: "Production variables are project-level only; branch overrides apply to preview branches.",
|
|
185
|
-
fix: "Use --role production for the
|
|
204
|
+
fix: "Use --role production for the production branch.",
|
|
186
205
|
exitCode: 1,
|
|
187
206
|
nextSteps: ["prisma-cli project env list --role production"]
|
|
188
207
|
});
|
|
@@ -199,20 +218,140 @@ async function resolveScopeToApi(client, projectId, scope, options) {
|
|
|
199
218
|
}
|
|
200
219
|
};
|
|
201
220
|
}
|
|
221
|
+
async function resolveListScopeToApi(client, projectId, explicit, options) {
|
|
222
|
+
if (explicit) {
|
|
223
|
+
const resolved = await resolveScopeToApi(client, projectId, explicit, {
|
|
224
|
+
createBranchIfMissing: false,
|
|
225
|
+
signal: options.signal
|
|
226
|
+
});
|
|
227
|
+
return {
|
|
228
|
+
kind: "scoped",
|
|
229
|
+
descriptor: resolved.descriptor,
|
|
230
|
+
target: targetFromExplicitScope(resolved.descriptor),
|
|
231
|
+
apiTarget: resolved.apiTarget,
|
|
232
|
+
addScope: resolved.scope
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
const gitBranch = await readLocalGitBranch(options.cwd, options.signal);
|
|
236
|
+
if (gitBranch) {
|
|
237
|
+
const branch = (await listBranchesByName(client, projectId, gitBranch, options.signal))[0];
|
|
238
|
+
if (!branch) return {
|
|
239
|
+
kind: "scoped",
|
|
240
|
+
descriptor: {
|
|
241
|
+
kind: "role",
|
|
242
|
+
role: "preview"
|
|
243
|
+
},
|
|
244
|
+
target: {
|
|
245
|
+
source: "local-git",
|
|
246
|
+
branchName: gitBranch,
|
|
247
|
+
branchExists: false,
|
|
248
|
+
envMap: "preview"
|
|
249
|
+
},
|
|
250
|
+
apiTarget: {
|
|
251
|
+
class: "preview",
|
|
252
|
+
branchId: null
|
|
253
|
+
},
|
|
254
|
+
addScope: {
|
|
255
|
+
kind: "branch",
|
|
256
|
+
branchName: gitBranch
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
if (branch.role === "production") return {
|
|
260
|
+
kind: "scoped",
|
|
261
|
+
descriptor: {
|
|
262
|
+
kind: "role",
|
|
263
|
+
role: "production"
|
|
264
|
+
},
|
|
265
|
+
target: {
|
|
266
|
+
source: "local-git",
|
|
267
|
+
branchName: branch.gitName,
|
|
268
|
+
branchId: branch.id,
|
|
269
|
+
branchRole: branch.role,
|
|
270
|
+
branchExists: true,
|
|
271
|
+
envMap: "production"
|
|
272
|
+
},
|
|
273
|
+
apiTarget: {
|
|
274
|
+
class: "production",
|
|
275
|
+
branchId: null
|
|
276
|
+
},
|
|
277
|
+
addScope: {
|
|
278
|
+
kind: "role",
|
|
279
|
+
role: "production"
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
return {
|
|
283
|
+
kind: "scoped",
|
|
284
|
+
descriptor: {
|
|
285
|
+
kind: "branch",
|
|
286
|
+
branchName: branch.gitName,
|
|
287
|
+
branchId: branch.id
|
|
288
|
+
},
|
|
289
|
+
target: {
|
|
290
|
+
source: "local-git",
|
|
291
|
+
branchName: branch.gitName,
|
|
292
|
+
branchId: branch.id,
|
|
293
|
+
branchRole: branch.role,
|
|
294
|
+
branchExists: true,
|
|
295
|
+
envMap: "preview"
|
|
296
|
+
},
|
|
297
|
+
apiTarget: {
|
|
298
|
+
class: "preview",
|
|
299
|
+
branchId: branch.id
|
|
300
|
+
},
|
|
301
|
+
addScope: {
|
|
302
|
+
kind: "branch",
|
|
303
|
+
branchName: branch.gitName
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
kind: "overview",
|
|
309
|
+
descriptor: { kind: "overview" },
|
|
310
|
+
target: {
|
|
311
|
+
source: "overview",
|
|
312
|
+
envMap: "overview"
|
|
313
|
+
},
|
|
314
|
+
addScope: {
|
|
315
|
+
kind: "role",
|
|
316
|
+
role: "preview"
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
function targetFromExplicitScope(scope) {
|
|
321
|
+
if (scope.kind === "branch") return {
|
|
322
|
+
source: "explicit",
|
|
323
|
+
branchName: scope.branchName,
|
|
324
|
+
branchId: scope.branchId,
|
|
325
|
+
branchRole: "preview",
|
|
326
|
+
branchExists: true,
|
|
327
|
+
envMap: "preview"
|
|
328
|
+
};
|
|
329
|
+
if (scope.kind === "role") return {
|
|
330
|
+
source: "explicit",
|
|
331
|
+
envMap: scope.role
|
|
332
|
+
};
|
|
333
|
+
return {
|
|
334
|
+
source: "overview",
|
|
335
|
+
envMap: "overview"
|
|
336
|
+
};
|
|
337
|
+
}
|
|
202
338
|
function formatScopeFlag(scope) {
|
|
203
339
|
if (scope.kind === "role") return `--role ${scope.role}`;
|
|
204
340
|
return `--branch ${scope.branchName}`;
|
|
205
341
|
}
|
|
206
|
-
async function listBranchesByName(client, projectId, branchName) {
|
|
207
|
-
const { data, error, response } = await client.GET("/v1/projects/{projectId}/branches", {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
342
|
+
async function listBranchesByName(client, projectId, branchName, signal) {
|
|
343
|
+
const { data, error, response } = await client.GET("/v1/projects/{projectId}/branches", {
|
|
344
|
+
params: {
|
|
345
|
+
path: { projectId },
|
|
346
|
+
query: { gitName: branchName }
|
|
347
|
+
},
|
|
348
|
+
signal
|
|
349
|
+
});
|
|
211
350
|
if (error || !data) throw apiCallError(`Failed to resolve branch "${branchName}"`, response, error);
|
|
212
351
|
return data.data;
|
|
213
352
|
}
|
|
214
|
-
async function resolveExistingBranch(client, projectId, branchName) {
|
|
215
|
-
const branch = (await listBranchesByName(client, projectId, branchName))[0];
|
|
353
|
+
async function resolveExistingBranch(client, projectId, branchName, signal) {
|
|
354
|
+
const branch = (await listBranchesByName(client, projectId, branchName, signal))[0];
|
|
216
355
|
if (!branch) throw new CliError({
|
|
217
356
|
code: "ENV_BRANCH_NOT_FOUND",
|
|
218
357
|
domain: "app",
|
|
@@ -224,10 +363,10 @@ async function resolveExistingBranch(client, projectId, branchName) {
|
|
|
224
363
|
});
|
|
225
364
|
return branch;
|
|
226
365
|
}
|
|
227
|
-
async function resolveOrCreateBranch(client, projectId, branchName) {
|
|
228
|
-
const existing = (await listBranchesByName(client, projectId, branchName))[0];
|
|
366
|
+
async function resolveOrCreateBranch(client, projectId, branchName, signal) {
|
|
367
|
+
const existing = (await listBranchesByName(client, projectId, branchName, signal))[0];
|
|
229
368
|
if (existing) return existing;
|
|
230
|
-
if (!await projectHasDefaultBranch(client, projectId)) throw new CliError({
|
|
369
|
+
if (!await projectHasDefaultBranch(client, projectId, signal)) throw new CliError({
|
|
231
370
|
code: "ENV_BRANCH_CREATE_REQUIRES_DEFAULT_BRANCH",
|
|
232
371
|
domain: "app",
|
|
233
372
|
summary: `Cannot create branch "${branchName}" from project env`,
|
|
@@ -241,58 +380,81 @@ async function resolveOrCreateBranch(client, projectId, branchName) {
|
|
|
241
380
|
body: {
|
|
242
381
|
gitName: branchName,
|
|
243
382
|
isDefault: false
|
|
244
|
-
}
|
|
383
|
+
},
|
|
384
|
+
signal
|
|
245
385
|
});
|
|
246
386
|
if (error || !data) {
|
|
247
387
|
if (response?.status === 409) {
|
|
248
|
-
const raced = (await listBranchesByName(client, projectId, branchName))[0];
|
|
388
|
+
const raced = (await listBranchesByName(client, projectId, branchName, signal))[0];
|
|
249
389
|
if (raced) return raced;
|
|
250
390
|
}
|
|
251
391
|
throw apiCallError(`Failed to create branch "${branchName}"`, response, error);
|
|
252
392
|
}
|
|
253
393
|
return data.data;
|
|
254
394
|
}
|
|
255
|
-
async function projectHasDefaultBranch(client, projectId) {
|
|
395
|
+
async function projectHasDefaultBranch(client, projectId, signal) {
|
|
256
396
|
let cursor;
|
|
257
397
|
while (true) {
|
|
258
398
|
const query = {};
|
|
259
399
|
if (cursor !== void 0) query.cursor = cursor;
|
|
260
|
-
const result = await client.GET("/v1/projects/{projectId}/branches", {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
400
|
+
const result = await client.GET("/v1/projects/{projectId}/branches", {
|
|
401
|
+
params: {
|
|
402
|
+
path: { projectId },
|
|
403
|
+
query
|
|
404
|
+
},
|
|
405
|
+
signal
|
|
406
|
+
});
|
|
264
407
|
if (result.error || !result.data) throw apiCallError("Failed to check project default branch", result.response, result.error);
|
|
265
408
|
if (result.data.data.some((branch) => branch.isDefault)) return true;
|
|
266
409
|
if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) return false;
|
|
267
410
|
cursor = result.data.pagination.nextCursor;
|
|
268
411
|
}
|
|
269
412
|
}
|
|
270
|
-
async function findVariableByNaturalKey(client, projectId, key, resolved) {
|
|
271
|
-
const { data, error, response } = await client.GET("/v1/environment-variables", {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
413
|
+
async function findVariableByNaturalKey(client, projectId, key, resolved, signal) {
|
|
414
|
+
const { data, error, response } = await client.GET("/v1/environment-variables", {
|
|
415
|
+
params: { query: {
|
|
416
|
+
projectId,
|
|
417
|
+
class: resolved.apiTarget.class,
|
|
418
|
+
key
|
|
419
|
+
} },
|
|
420
|
+
signal
|
|
421
|
+
});
|
|
276
422
|
if (error || !data) throw apiCallError(`Failed to look up ${key}`, response, error);
|
|
277
423
|
return data.data.filter((row) => rowMatchesExactScope(row, resolved))[0] ?? null;
|
|
278
424
|
}
|
|
279
|
-
async function listVariables(client, projectId, resolved) {
|
|
425
|
+
async function listVariables(client, projectId, resolved, signal) {
|
|
426
|
+
return materializeEffectiveRows(await collectEnvironmentVariables(client, projectId, signal, {
|
|
427
|
+
className: resolved.apiTarget.class,
|
|
428
|
+
filter: (row) => rowMatchesScope(row, resolved)
|
|
429
|
+
}), resolved);
|
|
430
|
+
}
|
|
431
|
+
async function listOverviewVariables(client, projectId, signal) {
|
|
432
|
+
return (await collectEnvironmentVariables(client, projectId, signal, { filter: (row) => row.branchId === null && (row.class === "production" || row.class === "preview") })).sort((left, right) => {
|
|
433
|
+
const roleOrder = roleSortOrder(left.class) - roleSortOrder(right.class);
|
|
434
|
+
return roleOrder !== 0 ? roleOrder : left.key.localeCompare(right.key);
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
async function collectEnvironmentVariables(client, projectId, signal, options) {
|
|
280
438
|
const collected = [];
|
|
281
439
|
let cursor;
|
|
282
440
|
while (true) {
|
|
283
|
-
const query = {
|
|
284
|
-
|
|
285
|
-
class: resolved.apiTarget.class
|
|
286
|
-
};
|
|
441
|
+
const query = { projectId };
|
|
442
|
+
if (options.className !== void 0) query.class = options.className;
|
|
287
443
|
if (cursor !== void 0) query.cursor = cursor;
|
|
288
|
-
const result = await client.GET("/v1/environment-variables", {
|
|
444
|
+
const result = await client.GET("/v1/environment-variables", {
|
|
445
|
+
params: { query },
|
|
446
|
+
signal
|
|
447
|
+
});
|
|
289
448
|
if (result.error || !result.data) throw apiCallError(`Failed to list environment variables`, result.response, result.error);
|
|
290
|
-
const page = result.data.data.filter(
|
|
449
|
+
const page = result.data.data.filter(options.filter);
|
|
291
450
|
collected.push(...page);
|
|
292
451
|
if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
|
|
293
452
|
cursor = result.data.pagination.nextCursor;
|
|
294
453
|
}
|
|
295
|
-
return
|
|
454
|
+
return collected;
|
|
455
|
+
}
|
|
456
|
+
function roleSortOrder(role) {
|
|
457
|
+
return role === "production" ? 0 : 1;
|
|
296
458
|
}
|
|
297
459
|
function rowMatchesScope(row, resolved) {
|
|
298
460
|
if (row.class !== resolved.apiTarget.class) return false;
|
|
@@ -325,6 +487,7 @@ function toMetadata(row, requestedScope) {
|
|
|
325
487
|
}
|
|
326
488
|
function formatDescriptorLabel(scope) {
|
|
327
489
|
if (scope.kind === "role") return scope.role ?? "unknown";
|
|
490
|
+
if (scope.kind === "overview") return "overview";
|
|
328
491
|
return `branch:${scope.branchName ?? scope.branchId ?? "unknown"}`;
|
|
329
492
|
}
|
|
330
493
|
function apiCallError(summary, response, error) {
|