@prisma/cli 3.0.0-dev.53.1 → 3.0.0-dev.56.1

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.
@@ -19,7 +19,10 @@ function createPreviewAppProvider(client, options) {
19
19
  const sdk = new ComputeClient(client);
20
20
  return {
21
21
  async createProject(options) {
22
- const projectResult = await sdk.createProject({ name: options.name });
22
+ const projectResult = await sdk.createProject({
23
+ name: options.name,
24
+ signal: options.signal
25
+ });
23
26
  if (projectResult.isErr()) throw new Error(projectResult.error.message);
24
27
  return {
25
28
  id: projectResult.value.id,
@@ -29,17 +32,34 @@ function createPreviewAppProvider(client, options) {
29
32
  async listApps(projectId, options) {
30
33
  return listComputeServices(client, {
31
34
  projectId,
32
- branchGitName: options?.branchName
35
+ branchGitName: options?.branchName,
36
+ signal: options?.signal
33
37
  });
34
38
  },
35
- async removeApp(appId) {
36
- const appResult = await sdk.showService({ serviceId: appId });
39
+ async resolveBranch(projectId, options) {
40
+ const branch = await resolveOrCreateBranch(client, {
41
+ projectId,
42
+ gitName: options.branchName,
43
+ signal: options.signal
44
+ });
45
+ return {
46
+ id: branch.id,
47
+ name: branch.gitName,
48
+ role: branch.role
49
+ };
50
+ },
51
+ async removeApp(appId, options) {
52
+ const appResult = await sdk.showService({
53
+ serviceId: appId,
54
+ signal: options?.signal
55
+ });
37
56
  if (appResult.isErr()) throw new Error(appResult.error.message);
38
57
  const destroyResult = await sdk.destroyService({
39
58
  serviceId: appId,
40
59
  keepService: false,
41
60
  timeoutSeconds: 120,
42
- pollIntervalMs: 2e3
61
+ pollIntervalMs: 2e3,
62
+ signal: options?.signal
43
63
  });
44
64
  if (destroyResult.isErr()) throw new Error(destroyResult.error.message);
45
65
  return {
@@ -47,17 +67,18 @@ function createPreviewAppProvider(client, options) {
47
67
  name: appResult.value.name
48
68
  };
49
69
  },
50
- async listDomains(appId) {
51
- return listComputeServiceDomains(client, appId);
70
+ async listDomains(appId, options) {
71
+ return listComputeServiceDomains(client, appId, options?.signal);
52
72
  },
53
73
  async addDomain(options) {
54
74
  const result = await client.POST("/v1/compute-services/{computeServiceId}/domains", {
55
75
  params: { path: { computeServiceId: options.appId } },
56
- body: { hostname: options.hostname }
76
+ body: { hostname: options.hostname },
77
+ signal: options.signal
57
78
  });
58
79
  if (result.error || !result.data) {
59
80
  if (result.response.status === 409) {
60
- const existing = (await listComputeServiceDomains(client, options.appId)).find((domain) => sameHostname(domain.hostname, options.hostname));
81
+ const existing = (await listComputeServiceDomains(client, options.appId, options.signal)).find((domain) => sameHostname(domain.hostname, options.hostname));
61
82
  if (existing) return {
62
83
  domain: existing,
63
84
  existing: true
@@ -70,17 +91,26 @@ function createPreviewAppProvider(client, options) {
70
91
  existing: false
71
92
  };
72
93
  },
73
- async showDomain(domainId) {
74
- const result = await client.GET("/v1/domains/{domainId}", { params: { path: { domainId } } });
94
+ async showDomain(domainId, options) {
95
+ const result = await client.GET("/v1/domains/{domainId}", {
96
+ params: { path: { domainId } },
97
+ signal: options?.signal
98
+ });
75
99
  if (result.error || !result.data) throw domainApiCallError("Failed to show custom domain", result.response, result.error);
76
100
  return normalizeDomainRecord(result.data.data);
77
101
  },
78
- async removeDomain(domainId) {
79
- const result = await client.DELETE("/v1/domains/{domainId}", { params: { path: { domainId } } });
102
+ async removeDomain(domainId, options) {
103
+ const result = await client.DELETE("/v1/domains/{domainId}", {
104
+ params: { path: { domainId } },
105
+ signal: options?.signal
106
+ });
80
107
  if (result.error) throw domainApiCallError("Failed to remove custom domain", result.response, result.error);
81
108
  },
82
- async retryDomain(domainId) {
83
- const result = await client.POST("/v1/domains/{domainId}/retry", { params: { path: { domainId } } });
109
+ async retryDomain(domainId, options) {
110
+ const result = await client.POST("/v1/domains/{domainId}/retry", {
111
+ params: { path: { domainId } },
112
+ signal: options?.signal
113
+ });
84
114
  if (result.error || !result.data) throw domainApiCallError("Failed to retry custom domain", result.response, result.error);
85
115
  return normalizeDomainRecord(result.data.data);
86
116
  },
@@ -90,6 +120,7 @@ function createPreviewAppProvider(client, options) {
90
120
  versionId: options.deploymentId,
91
121
  timeoutSeconds: 120,
92
122
  pollIntervalMs: 2e3,
123
+ signal: options.signal,
93
124
  progress: options.progress
94
125
  });
95
126
  if (promoteResult.isErr()) throw new Error(promoteResult.error.message);
@@ -103,7 +134,8 @@ function createPreviewAppProvider(client, options) {
103
134
  projectId: options.projectId,
104
135
  branchName: options.branchName,
105
136
  appName: options.appName,
106
- region: options.region
137
+ region: options.region,
138
+ signal: options.signal
107
139
  }) : {
108
140
  appId: void 0,
109
141
  appName: options.appName,
@@ -113,7 +145,8 @@ function createPreviewAppProvider(client, options) {
113
145
  strategy: new PreviewBuildStrategy({
114
146
  appPath: path.resolve(options.cwd),
115
147
  entrypoint: options.entrypoint,
116
- buildType: options.buildType
148
+ buildType: options.buildType,
149
+ signal: options.signal
117
150
  }),
118
151
  projectId: options.projectId,
119
152
  serviceId: resolvedApp.appId,
@@ -124,6 +157,7 @@ function createPreviewAppProvider(client, options) {
124
157
  timeoutSeconds: 120,
125
158
  pollIntervalMs: 2e3,
126
159
  interaction: options.interaction,
160
+ signal: options.signal,
127
161
  progress: options.progress
128
162
  });
129
163
  if (deployResult.isErr()) throw new Error(deployResult.error.message);
@@ -150,6 +184,7 @@ function createPreviewAppProvider(client, options) {
150
184
  envVars: options.envVars,
151
185
  timeoutSeconds: 120,
152
186
  pollIntervalMs: 2e3,
187
+ signal: options.signal,
153
188
  progress: options.progress
154
189
  });
155
190
  if (updateResult.isErr()) throw new Error(updateResult.error.message);
@@ -158,10 +193,17 @@ function createPreviewAppProvider(client, options) {
158
193
  versionId: updateResult.value.versionId,
159
194
  timeoutSeconds: 120,
160
195
  pollIntervalMs: 2e3,
196
+ signal: options.signal,
161
197
  progress: options.promoteProgress
162
198
  });
163
199
  if (promoteResult.isErr()) throw new Error(promoteResult.error.message);
164
- const [serviceResult, versionResult] = await Promise.all([sdk.showService({ serviceId: options.appId }), sdk.showVersion({ versionId: updateResult.value.versionId })]);
200
+ const [serviceResult, versionResult] = await Promise.all([sdk.showService({
201
+ serviceId: options.appId,
202
+ signal: options.signal
203
+ }), sdk.showVersion({
204
+ versionId: updateResult.value.versionId,
205
+ signal: options.signal
206
+ })]);
165
207
  if (serviceResult.isErr()) throw new Error(serviceResult.error.message);
166
208
  if (versionResult.isErr()) throw new Error(versionResult.error.message);
167
209
  return {
@@ -184,7 +226,13 @@ function createPreviewAppProvider(client, options) {
184
226
  };
185
227
  },
186
228
  async listAppEnvNames(options) {
187
- const [serviceResult, versionResult] = await Promise.all([sdk.showService({ serviceId: options.appId }), sdk.showVersion({ versionId: options.deploymentId })]);
229
+ const [serviceResult, versionResult] = await Promise.all([sdk.showService({
230
+ serviceId: options.appId,
231
+ signal: options.signal
232
+ }), sdk.showVersion({
233
+ versionId: options.deploymentId,
234
+ signal: options.signal
235
+ })]);
188
236
  if (serviceResult.isErr()) throw new Error(serviceResult.error.message);
189
237
  if (versionResult.isErr()) throw new Error(versionResult.error.message);
190
238
  return {
@@ -206,8 +254,14 @@ function createPreviewAppProvider(client, options) {
206
254
  variables: envVarNames(versionResult.value.envVars)
207
255
  };
208
256
  },
209
- async listDeployments(appId) {
210
- const [appResult, versionsResult] = await Promise.all([sdk.showService({ serviceId: appId }), sdk.listVersions({ serviceId: appId })]);
257
+ async listDeployments(appId, options) {
258
+ const [appResult, versionsResult] = await Promise.all([sdk.showService({
259
+ serviceId: appId,
260
+ signal: options?.signal
261
+ }), sdk.listVersions({
262
+ serviceId: appId,
263
+ signal: options?.signal
264
+ })]);
211
265
  if (appResult.isErr()) throw new Error(appResult.error.message);
212
266
  if (versionsResult.isErr()) throw new Error(versionsResult.error.message);
213
267
  return {
@@ -230,14 +284,17 @@ function createPreviewAppProvider(client, options) {
230
284
  }))
231
285
  };
232
286
  },
233
- async showDeployment(deploymentId) {
234
- const deploymentResult = await sdk.showVersion({ versionId: deploymentId });
287
+ async showDeployment(deploymentId, options) {
288
+ const deploymentResult = await sdk.showVersion({
289
+ versionId: deploymentId,
290
+ signal: options?.signal
291
+ });
235
292
  if (deploymentResult.isErr()) {
236
293
  if (ApiError.is(deploymentResult.error) && deploymentResult.error.statusCode === 404) return null;
237
294
  throw new Error(deploymentResult.error.message);
238
295
  }
239
296
  return {
240
- app: await findAppForDeployment(sdk, deploymentId),
297
+ app: await findAppForDeployment(sdk, deploymentId, options?.signal),
241
298
  deployment: {
242
299
  id: deploymentResult.value.id,
243
300
  status: deploymentResult.value.status,
@@ -263,22 +320,28 @@ function createPreviewAppProvider(client, options) {
263
320
  };
264
321
  }
265
322
  async function listBranches(client, options) {
266
- const result = await client.GET("/v1/projects/{projectId}/branches", { params: {
267
- path: { projectId: options.projectId },
268
- query: { gitName: options.gitName }
269
- } });
323
+ const result = await client.GET("/v1/projects/{projectId}/branches", {
324
+ params: {
325
+ path: { projectId: options.projectId },
326
+ query: { gitName: options.gitName }
327
+ },
328
+ signal: options.signal
329
+ });
270
330
  if (result.error || !result.data) throw apiCallError("Failed to list branches", result.response, result.error);
271
- return result.data.data;
331
+ return result.data.data.map((branch) => ({
332
+ id: branch.id,
333
+ gitName: branch.gitName,
334
+ isDefault: branch.isDefault,
335
+ role: branch.role
336
+ }));
272
337
  }
273
338
  async function resolveOrCreateBranch(client, options) {
274
339
  const existing = (await listBranches(client, options))[0];
275
340
  if (existing) return existing;
276
341
  const result = await client.POST("/v1/projects/{projectId}/branches", {
277
342
  params: { path: { projectId: options.projectId } },
278
- body: {
279
- gitName: options.gitName,
280
- isDefault: options.gitName === "main"
281
- }
343
+ body: { gitName: options.gitName },
344
+ signal: options.signal
282
345
  });
283
346
  if (result.error || !result.data) {
284
347
  if (result.response.status === 409) {
@@ -287,17 +350,26 @@ async function resolveOrCreateBranch(client, options) {
287
350
  }
288
351
  throw apiCallError(`Failed to create branch "${options.gitName}"`, result.response, result.error);
289
352
  }
290
- return result.data.data;
353
+ const branch = result.data.data;
354
+ return {
355
+ id: branch.id,
356
+ gitName: branch.gitName,
357
+ isDefault: branch.isDefault,
358
+ role: branch.role
359
+ };
291
360
  }
292
361
  async function listComputeServices(client, options) {
293
362
  const services = [];
294
363
  let cursor;
295
364
  while (true) {
296
- const result = await client.GET("/v1/compute-services", { params: { query: {
297
- projectId: options.projectId,
298
- branchGitName: options.branchGitName,
299
- cursor
300
- } } });
365
+ const result = await client.GET("/v1/compute-services", {
366
+ params: { query: {
367
+ projectId: options.projectId,
368
+ branchGitName: options.branchGitName,
369
+ cursor
370
+ } },
371
+ signal: options.signal
372
+ });
301
373
  if (result.error || !result.data) throw apiCallError("Failed to list apps", result.response, result.error);
302
374
  services.push(...result.data.data);
303
375
  if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
@@ -312,8 +384,11 @@ async function listComputeServices(client, options) {
312
384
  liveUrl: toAbsoluteUrl(service.serviceEndpointDomain ?? null)
313
385
  }));
314
386
  }
315
- async function listComputeServiceDomains(client, computeServiceId) {
316
- const result = await client.GET("/v1/compute-services/{computeServiceId}/domains", { params: { path: { computeServiceId } } });
387
+ async function listComputeServiceDomains(client, computeServiceId, signal) {
388
+ const result = await client.GET("/v1/compute-services/{computeServiceId}/domains", {
389
+ params: { path: { computeServiceId } },
390
+ signal
391
+ });
317
392
  if (result.error || !result.data) throw domainApiCallError("Failed to list custom domains", result.response, result.error);
318
393
  return result.data.data.map((domain) => normalizeDomainRecord(domain));
319
394
  }
@@ -355,19 +430,24 @@ function normalizeHostnameForComparison(hostname) {
355
430
  async function createBranchApp(client, options) {
356
431
  const branch = await resolveOrCreateBranch(client, {
357
432
  projectId: options.projectId,
358
- gitName: options.branchName
433
+ gitName: options.branchName,
434
+ signal: options.signal
435
+ });
436
+ const result = await client.POST("/v1/compute-services", {
437
+ body: {
438
+ projectId: options.projectId,
439
+ branchId: branch.id,
440
+ displayName: options.appName,
441
+ ...options.region ? { regionId: options.region } : {}
442
+ },
443
+ signal: options.signal
359
444
  });
360
- const result = await client.POST("/v1/compute-services", { body: {
361
- projectId: options.projectId,
362
- branchId: branch.id,
363
- displayName: options.appName,
364
- ...options.region ? { regionId: options.region } : {}
365
- } });
366
445
  if (result.error || !result.data) {
367
446
  if (result.response.status === 409) {
368
447
  const matched = (await listComputeServices(client, {
369
448
  projectId: options.projectId,
370
- branchGitName: options.branchName
449
+ branchGitName: options.branchName,
450
+ signal: options.signal
371
451
  })).find((app) => app.name === options.appName);
372
452
  if (matched) return {
373
453
  appId: matched.id,
@@ -399,14 +479,20 @@ function domainApiCallError(summary, response, error) {
399
479
  hint: error.error?.hint ?? null
400
480
  });
401
481
  }
402
- async function findAppForDeployment(sdk, deploymentId) {
403
- const projectsResult = await sdk.listProjects();
482
+ async function findAppForDeployment(sdk, deploymentId, signal) {
483
+ const projectsResult = await sdk.listProjects({ signal });
404
484
  if (projectsResult.isErr()) throw new Error(projectsResult.error.message);
405
485
  for (const project of projectsResult.value) {
406
- const servicesResult = await sdk.listServices({ projectId: project.id });
486
+ const servicesResult = await sdk.listServices({
487
+ projectId: project.id,
488
+ signal
489
+ });
407
490
  if (servicesResult.isErr()) throw new Error(servicesResult.error.message);
408
491
  for (const service of servicesResult.value) {
409
- const detailResult = await sdk.showService({ serviceId: service.id });
492
+ const detailResult = await sdk.showService({
493
+ serviceId: service.id,
494
+ signal
495
+ });
410
496
  if (detailResult.isErr()) throw new Error(detailResult.error.message);
411
497
  const app = {
412
498
  id: detailResult.value.id,
@@ -416,7 +502,10 @@ async function findAppForDeployment(sdk, deploymentId) {
416
502
  liveUrl: toAbsoluteUrl(detailResult.value.serviceEndpointDomain ?? null)
417
503
  };
418
504
  if (app.liveDeploymentId === deploymentId) return app;
419
- const versionsResult = await sdk.listVersions({ serviceId: service.id });
505
+ const versionsResult = await sdk.listVersions({
506
+ serviceId: service.id,
507
+ signal
508
+ });
420
509
  if (versionsResult.isErr()) throw new Error(versionsResult.error.message);
421
510
  if (versionsResult.value.some((version) => version.id === deploymentId)) return app;
422
511
  }
@@ -0,0 +1,160 @@
1
+ import { CliError } from "../../shell/errors.js";
2
+ import { canPrompt } from "../../shell/runtime.js";
3
+ import { confirmPrompt } from "../../shell/prompt.js";
4
+ //#region src/lib/app/production-deploy-gate.ts
5
+ async function enforceProductionDeployGate(context, provider, options) {
6
+ if (options.branchKind !== "production") return;
7
+ if (!options.appId) {
8
+ renderFirstProductionDeployLine(context, options.appName);
9
+ return;
10
+ }
11
+ const currentLiveDeployment = resolveCurrentProductionDeployment(await provider.listDeployments(options.appId).catch((error) => {
12
+ throw productionDeployInspectionFailedError(error);
13
+ }));
14
+ if (!currentLiveDeployment) {
15
+ renderFirstProductionDeployLine(context, options.appName);
16
+ return;
17
+ }
18
+ if (!options.prod) throw productionDeployRequiresFlagError();
19
+ if (context.flags.yes) {
20
+ renderProductionDeployYesLine(context);
21
+ return;
22
+ }
23
+ if (!canPrompt(context)) throw productionDeployConfirmationRequiredError(options.appName);
24
+ renderProductionDeployConfirmation(context, currentLiveDeployment);
25
+ if (!await confirmPrompt({
26
+ input: context.runtime.stdin,
27
+ output: context.output.stderr,
28
+ message: "Deploy to production?",
29
+ initialValue: false
30
+ })) throw productionDeployCancelledError();
31
+ }
32
+ function resolveCurrentProductionDeployment(result) {
33
+ if (result.deployments.length === 0) return null;
34
+ if (result.app.liveDeploymentId) {
35
+ const live = result.deployments.find((deployment) => deployment.id === result.app.liveDeploymentId);
36
+ if (live) return live;
37
+ }
38
+ return result.deployments.find((deployment) => deployment.live === true) ?? result.deployments[0] ?? null;
39
+ }
40
+ function renderFirstProductionDeployLine(context, appName) {
41
+ if (context.flags.json || context.flags.quiet) return;
42
+ context.output.stderr.write(`First deploy of "${appName}" -- promoting to production.\n\n`);
43
+ }
44
+ function renderProductionDeployYesLine(context) {
45
+ if (context.flags.json || context.flags.quiet) return;
46
+ context.output.stderr.write("Deploying to production (--prod --yes).\n\n");
47
+ }
48
+ function renderProductionDeployConfirmation(context, currentLiveDeployment) {
49
+ if (context.flags.json || context.flags.quiet) return;
50
+ const lines = [
51
+ "This will deploy to production and replace the live deployment.",
52
+ "",
53
+ ` Current live: ${currentLiveDeployment.id} deployed ${formatDeploymentAge(currentLiveDeployment.createdAt)}`,
54
+ " New deploy: will be built from your local code",
55
+ ""
56
+ ];
57
+ context.output.stderr.write(`${lines.join("\n")}\n`);
58
+ }
59
+ function formatDeploymentAge(createdAt) {
60
+ const createdAtMs = Date.parse(createdAt);
61
+ if (!Number.isFinite(createdAtMs)) return createdAt;
62
+ const elapsedMs = Math.max(0, Date.now() - createdAtMs);
63
+ for (const unit of [
64
+ {
65
+ label: "day",
66
+ ms: 1440 * 60 * 1e3
67
+ },
68
+ {
69
+ label: "hour",
70
+ ms: 3600 * 1e3
71
+ },
72
+ {
73
+ label: "minute",
74
+ ms: 60 * 1e3
75
+ }
76
+ ]) if (elapsedMs >= unit.ms) {
77
+ const value = Math.floor(elapsedMs / unit.ms);
78
+ return `${value} ${unit.label}${value === 1 ? "" : "s"} ago`;
79
+ }
80
+ return "less than a minute ago";
81
+ }
82
+ function productionDeployRequiresFlagError() {
83
+ return new CliError({
84
+ code: "PROD_DEPLOY_REQUIRES_FLAG",
85
+ domain: "app",
86
+ summary: "Production deploy requires --prod",
87
+ why: "The resolved Branch is production and this App already has a production deployment.",
88
+ fix: "Re-run with --prod, or deploy from a preview Branch.",
89
+ exitCode: 2,
90
+ nextActions: [{
91
+ kind: "run-command",
92
+ journey: "deploy-app",
93
+ label: "Deploy to production",
94
+ command: "prisma-cli app deploy --prod"
95
+ }, {
96
+ kind: "run-command",
97
+ journey: "deploy-app",
98
+ label: "Create a preview branch",
99
+ commands: ["git checkout -b <branch-name>", "prisma-cli app deploy"]
100
+ }],
101
+ humanLines: [
102
+ "This would deploy to production.",
103
+ "",
104
+ "Production deploys require explicit intent. Re-run with:",
105
+ "",
106
+ " prisma app deploy --prod",
107
+ "",
108
+ "Or deploy a preview from a feature branch:",
109
+ "",
110
+ " git checkout -b <branch-name>",
111
+ " prisma app deploy"
112
+ ]
113
+ });
114
+ }
115
+ function productionDeployConfirmationRequiredError(appName) {
116
+ return new CliError({
117
+ code: "CONFIRMATION_REQUIRED",
118
+ domain: "app",
119
+ summary: "Production deploy requires confirmation in the current mode",
120
+ why: "This command cannot prompt for production deploy confirmation in the current mode.",
121
+ fix: `Pass --prod --yes to confirm deployment of "${appName}" to production.`,
122
+ exitCode: 1,
123
+ nextSteps: ["prisma-cli app deploy --prod --yes"],
124
+ nextActions: [{
125
+ kind: "run-command",
126
+ journey: "deploy-app",
127
+ label: "Deploy to production non-interactively",
128
+ command: "prisma-cli app deploy --prod --yes"
129
+ }]
130
+ });
131
+ }
132
+ function productionDeployCancelledError() {
133
+ return new CliError({
134
+ code: "CONFIRMATION_REQUIRED",
135
+ domain: "app",
136
+ summary: "Production deploy cancelled",
137
+ why: null,
138
+ fix: null,
139
+ exitCode: 0,
140
+ humanLines: ["Cancelled."]
141
+ });
142
+ }
143
+ function productionDeployInspectionFailedError(error) {
144
+ return new CliError({
145
+ code: "DEPLOY_FAILED",
146
+ domain: "app",
147
+ summary: "Failed to inspect production deployments",
148
+ why: error instanceof Error ? error.message : String(error),
149
+ fix: "Retry the command, or rerun with --trace for more detailed diagnostics.",
150
+ debug: formatDebugDetails(error),
151
+ exitCode: 1,
152
+ nextSteps: ["prisma-cli app list-deploys"]
153
+ });
154
+ }
155
+ function formatDebugDetails(error) {
156
+ if (error instanceof Error) return error.stack ?? error.message;
157
+ return typeof error === "string" ? error : null;
158
+ }
159
+ //#endregion
160
+ export { enforceProductionDeployGate };
@@ -24,20 +24,21 @@ function workspaceIdFromClaims(claims) {
24
24
  const id = sub.slice(10).trim();
25
25
  return id.length > 0 ? id : null;
26
26
  }
27
- async function performLogin(env) {
27
+ async function performLogin(env, signal) {
28
28
  await login({
29
- tokenStorage: new FileTokenStorage(env),
30
- env
29
+ tokenStorage: new FileTokenStorage(env, signal),
30
+ env,
31
+ signal
31
32
  });
32
33
  }
33
- async function readAuthState(env) {
34
+ async function readAuthState(env, signal) {
34
35
  const rawServiceToken = env[SERVICE_TOKEN_ENV_VAR];
35
36
  if (rawServiceToken !== void 0) {
36
37
  const serviceToken = rawServiceToken.trim();
37
38
  if (serviceToken.length === 0) throw new Error(`${SERVICE_TOKEN_ENV_VAR} is set but empty. Provide a valid token or unset the variable.`);
38
- return readServiceTokenAuthState(serviceToken, env);
39
+ return readServiceTokenAuthState(serviceToken, env, signal);
39
40
  }
40
- const tokens = await new FileTokenStorage(env).getTokens();
41
+ const tokens = await new FileTokenStorage(env, signal).getTokens();
41
42
  if (!tokens) return {
42
43
  authenticated: false,
43
44
  provider: null,
@@ -45,20 +46,21 @@ async function readAuthState(env) {
45
46
  workspace: null,
46
47
  credential: null
47
48
  };
48
- const client = await requireComputeAuth(env);
49
- const currentPrincipal = await readCurrentPrincipalAuthState(client);
49
+ const client = await requireComputeAuth(env, signal);
50
+ const currentPrincipal = await readCurrentPrincipalAuthState(client, signal);
50
51
  if (currentPrincipal) return currentPrincipal;
51
52
  const claims = decodeJwtPayload(tokens.accessToken);
52
53
  return buildAuthState({
53
54
  workspaceIdFromCredential: tokens.workspaceId,
54
55
  claims,
55
56
  env,
56
- client
57
+ client,
58
+ signal
57
59
  });
58
60
  }
59
- async function readServiceTokenAuthState(token, env) {
60
- const client = await requireComputeAuth(env);
61
- const currentPrincipal = await readCurrentPrincipalAuthState(client);
61
+ async function readServiceTokenAuthState(token, env, signal) {
62
+ const client = await requireComputeAuth(env, signal);
63
+ const currentPrincipal = await readCurrentPrincipalAuthState(client, signal);
62
64
  if (currentPrincipal) return currentPrincipal;
63
65
  const claims = decodeJwtPayload(token);
64
66
  const workspaceId = workspaceIdFromClaims(claims);
@@ -73,15 +75,19 @@ async function readServiceTokenAuthState(token, env) {
73
75
  workspaceIdFromCredential: workspaceId,
74
76
  claims,
75
77
  env,
76
- client
78
+ client,
79
+ signal
77
80
  });
78
81
  }
79
- async function buildAuthState({ workspaceIdFromCredential, claims, env, client }) {
82
+ async function buildAuthState({ workspaceIdFromCredential, claims, env, client, signal }) {
80
83
  let workspaceId = workspaceIdFromCredential;
81
84
  let workspaceName = workspaceIdFromCredential;
82
- client ??= await requireComputeAuth(env);
85
+ client ??= await requireComputeAuth(env, signal);
83
86
  if (client) try {
84
- const { data, response } = await client.GET("/v1/workspaces/{id}", { params: { path: { id: workspaceIdFromCredential } } });
87
+ const { data, response } = await client.GET("/v1/workspaces/{id}", {
88
+ params: { path: { id: workspaceIdFromCredential } },
89
+ signal
90
+ });
85
91
  if (response?.status === 401) return {
86
92
  authenticated: false,
87
93
  provider: null,
@@ -94,7 +100,9 @@ async function buildAuthState({ workspaceIdFromCredential, claims, env, client }
94
100
  workspaceName = data.data.id;
95
101
  }
96
102
  if (data?.data?.name) workspaceName = data.data.name;
97
- } catch {}
103
+ } catch {
104
+ signal?.throwIfAborted();
105
+ }
98
106
  const email = emailFromClaims(claims);
99
107
  return {
100
108
  authenticated: true,
@@ -107,10 +115,10 @@ async function buildAuthState({ workspaceIdFromCredential, claims, env, client }
107
115
  credential: null
108
116
  };
109
117
  }
110
- async function readCurrentPrincipalAuthState(client) {
118
+ async function readCurrentPrincipalAuthState(client, signal) {
111
119
  if (!client) return null;
112
120
  try {
113
- const { data, response } = await client.GET("/v1/me");
121
+ const { data, response } = await client.GET("/v1/me", { signal });
114
122
  if (response?.status === 401) return {
115
123
  authenticated: false,
116
124
  provider: null,
@@ -133,11 +141,12 @@ async function readCurrentPrincipalAuthState(client) {
133
141
  credential: principal.credential
134
142
  };
135
143
  } catch {
144
+ signal?.throwIfAborted();
136
145
  return null;
137
146
  }
138
147
  }
139
- async function performLogout(env) {
140
- await new FileTokenStorage(env).clearTokens();
148
+ async function performLogout(env, signal) {
149
+ await new FileTokenStorage(env, signal).clearTokens();
141
150
  }
142
151
  //#endregion
143
152
  export { performLogin, performLogout, readAuthState };
@@ -11,7 +11,8 @@ import { createManagementApiClient, createManagementApiSdk } from "@prisma/manag
11
11
  *
12
12
  * Returns null if not authenticated.
13
13
  */
14
- async function requireComputeAuth(env = process.env) {
14
+ async function requireComputeAuth(env = process.env, signal) {
15
+ signal?.throwIfAborted();
15
16
  const rawToken = env[SERVICE_TOKEN_ENV_VAR];
16
17
  if (rawToken !== void 0) {
17
18
  const token = rawToken.trim();
@@ -21,7 +22,7 @@ async function requireComputeAuth(env = process.env) {
21
22
  token
22
23
  });
23
24
  }
24
- const tokenStorage = new FileTokenStorage(env);
25
+ const tokenStorage = new FileTokenStorage(env, signal);
25
26
  if (!await tokenStorage.getTokens()) return null;
26
27
  return createManagementApiSdk({
27
28
  clientId: CLIENT_ID,