@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.
@@ -20,6 +20,7 @@ import { PREVIEW_BUILD_TYPES, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild
20
20
  import { PREVIEW_DEFAULT_REGION } from "../lib/app/preview-interaction.js";
21
21
  import { createPreviewDeployProgress, createPreviewDeployProgressState, createPreviewPromoteProgress } from "../lib/app/preview-progress.js";
22
22
  import { PreviewDomainApiError, createPreviewAppProvider } from "../lib/app/preview-provider.js";
23
+ import { enforceProductionDeployGate } from "../lib/app/production-deploy-gate.js";
23
24
  import { formatDomainFailureFix } from "../lib/app/domain-guidance.js";
24
25
  import { createSelectPromptPort } from "./select-prompt-port.js";
25
26
  import { requireAuthenticatedAuthState } from "./auth.js";
@@ -48,7 +49,8 @@ async function runAppBuild(context, entrypoint, requestedBuildType) {
48
49
  const { artifact, buildType: actualBuildType } = await executePreviewBuild({
49
50
  appPath: context.runtime.cwd,
50
51
  entrypoint,
51
- buildType
52
+ buildType,
53
+ signal: context.runtime.signal
52
54
  });
53
55
  return {
54
56
  command: "app.build",
@@ -78,12 +80,13 @@ async function runAppRun(context, entrypoint, requestedBuildType, requestedPort)
78
80
  buildType: resolvedBuildType,
79
81
  entrypoint,
80
82
  port,
81
- env: context.runtime.env
83
+ env: context.runtime.env,
84
+ signal: context.runtime.signal
82
85
  });
83
86
  } catch (error) {
84
87
  throw runFailedError("Local app run failed", error);
85
88
  }
86
- if (runResult.signal === "SIGINT" || runResult.signal === "SIGTERM") process.exitCode = runResult.signal === "SIGINT" ? 130 : 143;
89
+ if (runResult.signal === "SIGINT" || runResult.signal === "SIGTERM") throw new DOMException("Command canceled", "AbortError");
87
90
  else if (runResult.exitCode !== 0) throw runFailedError("Local app run failed", `The ${formatFrameworkName(runResult.framework)} process exited with code ${runResult.exitCode}.`, runResult.exitCode);
88
91
  return {
89
92
  command: "app.run",
@@ -107,7 +110,7 @@ async function runAppDeploy(context, appName, options) {
107
110
  envProjectId
108
111
  });
109
112
  const skipLocalPin = Boolean(envProjectId || options?.projectRef || options?.createProjectName);
110
- const localPin = skipLocalPin ? { kind: "missing" } : await readLocalResolutionPin(context.runtime.cwd);
113
+ const localPin = skipLocalPin ? { kind: "missing" } : await readLocalResolutionPin(context.runtime.cwd, context.runtime.signal);
111
114
  if (!skipLocalPin && localPin.kind === "invalid") throw localResolutionPinStaleError();
112
115
  const branch = await resolveDeployBranch(context, options?.branchName);
113
116
  if (options?.httpPort) parseDeployHttpPort(options.httpPort);
@@ -138,7 +141,7 @@ async function runAppDeploy(context, appName, options) {
138
141
  explicitAppName: appName,
139
142
  explicitAppId: envAppId,
140
143
  firstDeploy: Boolean(target.localPinAction),
141
- inferName: () => inferTargetName(context.runtime.cwd)
144
+ inferName: () => inferTargetName(context.runtime.cwd, context.runtime.signal)
142
145
  });
143
146
  await maybeRenderDeploySetupBlock(context, {
144
147
  includeDirectory: !target.localPinAction,
@@ -156,9 +159,15 @@ async function runAppDeploy(context, appName, options) {
156
159
  });
157
160
  framework = customized.framework;
158
161
  runtime = customized.runtime;
162
+ await enforceProductionDeployGate(context, provider, {
163
+ appId: selectedApp.appId,
164
+ appName: selectedApp.displayName,
165
+ branchKind: target.branch.kind,
166
+ prod: options?.prod === true
167
+ });
159
168
  const buildType = framework.buildType;
160
169
  assertSupportedEntrypoint(buildType, options?.entrypoint, "deploy");
161
- const entrypoint = await resolveDeployEntrypoint(context.runtime.cwd, framework, options?.entrypoint);
170
+ const entrypoint = await resolveDeployEntrypoint(context.runtime.cwd, framework, options?.entrypoint, context.runtime.signal);
162
171
  const portMapping = parseDeployPortMapping(String(runtime.port));
163
172
  const progressState = createPreviewDeployProgressState();
164
173
  const deployStartedAt = Date.now();
@@ -174,6 +183,7 @@ async function runAppDeploy(context, appName, options) {
174
183
  portMapping,
175
184
  envVars,
176
185
  interaction: void 0,
186
+ signal: context.runtime.signal,
177
187
  progress: createPreviewDeployProgress(context.output.stderr, context.ui, !context.flags.json && !context.flags.quiet, progressState)
178
188
  }).catch((error) => {
179
189
  throw appDeployFailedError(error, progressState);
@@ -217,7 +227,7 @@ async function runAppListDeploys(context, appName, projectRef) {
217
227
  warnings: [],
218
228
  nextSteps: ["prisma-cli app deploy"]
219
229
  };
220
- const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
230
+ const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
221
231
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app deploy"]);
222
232
  });
223
233
  const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
@@ -256,7 +266,7 @@ async function runAppShow(context, appName, projectRef) {
256
266
  warnings: [],
257
267
  nextSteps: ["prisma-cli app deploy"]
258
268
  };
259
- const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
269
+ const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
260
270
  throw deployFailedError("Failed to inspect app", error, ["prisma-cli app list-deploys"]);
261
271
  });
262
272
  const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
@@ -284,7 +294,7 @@ async function runAppShow(context, appName, projectRef) {
284
294
  }
285
295
  async function runAppShowDeploy(context, deploymentId) {
286
296
  ensurePreviewAppMode(context);
287
- const deployment = await (await requirePreviewAppProvider(context)).showDeployment(deploymentId).catch((error) => {
297
+ const deployment = await (await requirePreviewAppProvider(context)).showDeployment(deploymentId, { signal: context.runtime.signal }).catch((error) => {
288
298
  throw deployFailedError("Failed to show deployment", error, ["prisma-cli app list-deploys"]);
289
299
  });
290
300
  if (!deployment) throw new CliError({
@@ -321,7 +331,7 @@ async function runAppOpen(context, appName, projectRef) {
321
331
  const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app open" });
322
332
  const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName);
323
333
  if (!selectedApp) throw noDeploymentsError("No deployments available to open", "The resolved project does not have any deployed app yet.");
324
- const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
334
+ const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
325
335
  throw deployFailedError("Failed to resolve app URL", error, ["prisma-cli app show"]);
326
336
  });
327
337
  const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
@@ -334,7 +344,11 @@ async function runAppOpen(context, appName, projectRef) {
334
344
  if (!liveDeployment) throw noDeploymentsError("No deployments available to open", `The selected app "${deploymentsResult.app.name}" does not have any deployments yet.`);
335
345
  if (!deploymentsResult.app.liveUrl) throw featureUnavailableError("Live URL is not available for the selected app", "Deployments exist, but the provider does not expose a stable live service URL for this app yet.", "Run prisma-cli app show to inspect the current deployment state and try again after the app reports a live URL.", ["prisma-cli app show"], "app");
336
346
  const shouldOpen = canPrompt(context);
337
- if (shouldOpen) await open(deploymentsResult.app.liveUrl);
347
+ if (shouldOpen) {
348
+ context.runtime.signal.throwIfAborted();
349
+ await open(deploymentsResult.app.liveUrl);
350
+ context.runtime.signal.throwIfAborted();
351
+ }
338
352
  return {
339
353
  command: "app.open",
340
354
  result: {
@@ -355,7 +369,8 @@ async function runAppDomainAdd(context, hostname, options) {
355
369
  const target = await resolveAppDomainTarget(context, options, `app domain add ${normalizedHostname}`);
356
370
  const added = await target.provider.addDomain({
357
371
  appId: target.app.id,
358
- hostname: normalizedHostname
372
+ hostname: normalizedHostname,
373
+ signal: context.runtime.signal
359
374
  }).catch((error) => {
360
375
  throw domainCommandError("add", error, normalizedHostname);
361
376
  });
@@ -373,8 +388,8 @@ async function runAppDomainAdd(context, hostname, options) {
373
388
  async function runAppDomainShow(context, hostname, options) {
374
389
  const normalizedHostname = normalizeDomainHostname(hostname);
375
390
  const target = await resolveAppDomainTarget(context, options, `app domain show ${normalizedHostname}`);
376
- const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show");
377
- const detail = await target.provider.showDomain(domain.id).catch((error) => {
391
+ const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "show", context.runtime.signal);
392
+ const detail = await target.provider.showDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
378
393
  throw domainCommandError("show", error, normalizedHostname);
379
394
  });
380
395
  return {
@@ -390,9 +405,9 @@ async function runAppDomainShow(context, hostname, options) {
390
405
  async function runAppDomainRemove(context, hostname, options) {
391
406
  const normalizedHostname = normalizeDomainHostname(hostname);
392
407
  const target = await resolveAppDomainTarget(context, options, `app domain remove ${normalizedHostname}`);
393
- const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove");
408
+ const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "remove", context.runtime.signal);
394
409
  await confirmDomainRemoval(context, target.resultTarget, normalizedHostname);
395
- await target.provider.removeDomain(domain.id).catch((error) => {
410
+ await target.provider.removeDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
396
411
  throw domainCommandError("remove", error, normalizedHostname);
397
412
  });
398
413
  return {
@@ -409,8 +424,8 @@ async function runAppDomainRemove(context, hostname, options) {
409
424
  async function runAppDomainRetry(context, hostname, options) {
410
425
  const normalizedHostname = normalizeDomainHostname(hostname);
411
426
  const target = await resolveAppDomainTarget(context, options, `app domain retry ${normalizedHostname}`);
412
- const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry");
413
- const retried = await target.provider.retryDomain(domain.id).catch((error) => {
427
+ const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "retry", context.runtime.signal);
428
+ const retried = await target.provider.retryDomain(domain.id, { signal: context.runtime.signal }).catch((error) => {
414
429
  throw domainCommandError("retry", error, normalizedHostname);
415
430
  });
416
431
  return {
@@ -427,7 +442,7 @@ async function runAppDomainWait(context, hostname, options) {
427
442
  const normalizedHostname = normalizeDomainHostname(hostname);
428
443
  const timeoutMs = parseDomainWaitTimeout(options?.timeout);
429
444
  const target = await resolveAppDomainTarget(context, options, `app domain wait ${normalizedHostname}`);
430
- const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait");
445
+ const domain = await resolveDomainByHostname(target.provider, target.app.id, normalizedHostname, "wait", context.runtime.signal);
431
446
  if (!context.flags.json && !context.flags.quiet) context.output.stderr.write([
432
447
  `app domain wait -> Waiting for ${normalizedHostname} to become active.`,
433
448
  "",
@@ -470,8 +485,8 @@ async function runAppDomainWait(context, hostname, options) {
470
485
  exitCode: 1,
471
486
  nextSteps: [`prisma-cli app domain show ${normalizedHostname}`]
472
487
  });
473
- await sleep(Math.min(pollIntervalMs, Math.max(deadline - Date.now(), 0)));
474
- current = await target.provider.showDomain(current.id).catch((error) => {
488
+ await sleep(Math.min(pollIntervalMs, Math.max(deadline - Date.now(), 0)), context.runtime.signal);
489
+ current = await target.provider.showDomain(current.id, { signal: context.runtime.signal }).catch((error) => {
475
490
  throw domainCommandError("wait", error, normalizedHostname);
476
491
  });
477
492
  }
@@ -504,6 +519,7 @@ async function runAppLogs(context, appName, deploymentId, projectRef) {
504
519
  }
505
520
  await provider.streamDeploymentLogs({
506
521
  deploymentId: target.deployment.id,
522
+ signal: context.runtime.signal,
507
523
  onRecord: (record) => writeLogRecord(context, record)
508
524
  }).catch((error) => {
509
525
  throw deployFailedError("Failed to stream app logs", error, [`prisma-cli app show-deploy ${target.deployment.id}`, "prisma-cli app list-deploys"]);
@@ -513,7 +529,7 @@ async function resolveExplicitLogDeployment(context, provider, projectId, branch
513
529
  if (appName) {
514
530
  const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, branchName), appName);
515
531
  if (!selectedApp) throw noDeploymentsError("No deployments available to stream logs", "The resolved project does not have any deployed app yet.");
516
- const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
532
+ const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
517
533
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
518
534
  });
519
535
  const deployment = requireDeploymentForApp(deploymentsResult.deployments, deploymentId, selectedApp.name);
@@ -526,7 +542,7 @@ async function resolveExplicitLogDeployment(context, provider, projectId, branch
526
542
  deployment
527
543
  };
528
544
  }
529
- const shown = await provider.showDeployment(deploymentId).catch((error) => {
545
+ const shown = await provider.showDeployment(deploymentId, { signal: context.runtime.signal }).catch((error) => {
530
546
  throw deployFailedError("Failed to show deployment", error, ["prisma-cli app list-deploys"]);
531
547
  });
532
548
  if (!shown) throw new CliError({
@@ -569,7 +585,7 @@ async function resolveExplicitLogDeployment(context, provider, projectId, branch
569
585
  async function resolveLiveLogDeployment(context, provider, projectId, branchName, appName) {
570
586
  const selectedApp = await resolveExistingAppSelection(context, projectId, await listApps(context, provider, projectId, branchName), appName);
571
587
  if (!selectedApp) throw noDeploymentsError("No deployments available to stream logs", "The resolved project does not have any deployed app yet.");
572
- const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
588
+ const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
573
589
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
574
590
  });
575
591
  const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
@@ -604,7 +620,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
604
620
  ensurePreviewAppMode(context);
605
621
  const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app promote" });
606
622
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "promote");
607
- const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
623
+ const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
608
624
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
609
625
  });
610
626
  const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
@@ -617,6 +633,7 @@ async function runAppPromote(context, deploymentId, appName, projectRef) {
617
633
  if (!targetAlreadyLive) await provider.promoteDeployment({
618
634
  appId: selectedApp.id,
619
635
  deploymentId: targetDeployment.id,
636
+ signal: context.runtime.signal,
620
637
  progress: createPreviewPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
621
638
  }).catch((error) => {
622
639
  throw deployFailedError("Failed to promote deployment", error, ["prisma-cli app list-deploys"]);
@@ -644,7 +661,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
644
661
  ensurePreviewAppMode(context);
645
662
  const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app rollback" });
646
663
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "rollback");
647
- const deploymentsResult = await provider.listDeployments(selectedApp.id).catch((error) => {
664
+ const deploymentsResult = await provider.listDeployments(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
648
665
  throw deployFailedError("Failed to list app deployments", error, ["prisma-cli app list-deploys"]);
649
666
  });
650
667
  const currentLiveDeploymentId = await resolveCurrentLiveDeploymentId(context, projectId, deploymentsResult.app, deploymentsResult.deployments);
@@ -658,6 +675,7 @@ async function runAppRollback(context, appName, deploymentId, projectRef) {
658
675
  if (!targetAlreadyLive) await provider.promoteDeployment({
659
676
  appId: selectedApp.id,
660
677
  deploymentId: targetDeployment.id,
678
+ signal: context.runtime.signal,
661
679
  progress: createPreviewPromoteProgress(context.output.stderr, !context.flags.json && !context.flags.quiet)
662
680
  }).catch((error) => {
663
681
  throw deployFailedError("Failed to roll back deployment", error, ["prisma-cli app list-deploys"]);
@@ -687,7 +705,7 @@ async function runAppRemove(context, appName, projectRef) {
687
705
  const { provider, target, projectId } = await requireProviderAndProjectContext(context, projectRef, { commandName: "app remove" });
688
706
  const selectedApp = await requireReleaseAppSelection(context, projectId, await listApps(context, provider, projectId, target.branch.name), appName, "remove");
689
707
  await confirmAppRemoval(context, selectedApp);
690
- const removedApp = await provider.removeApp(selectedApp.id).catch((error) => {
708
+ const removedApp = await provider.removeApp(selectedApp.id, { signal: context.runtime.signal }).catch((error) => {
691
709
  throw removeFailedError("Failed to remove app", error, ["prisma-cli app show", "prisma-cli app list-deploys"]);
692
710
  });
693
711
  const warnings = await cleanupRemovedAppState(context, projectId, removedApp.id);
@@ -762,8 +780,8 @@ async function resolveDomainAppSelection(context, projectId, apps, options) {
762
780
  if (selectedApp) return selectedApp;
763
781
  throw usageError("Custom domain requires an existing app on the production branch", "The resolved production branch does not have an app that can receive a custom domain.", "Deploy or promote an app to production first, then rerun the domain command.", ["prisma-cli app deploy --branch production", "prisma-cli app show"], "app");
764
782
  }
765
- async function resolveDomainByHostname(provider, appId, hostname, command) {
766
- const matched = (await provider.listDomains(appId).catch((error) => {
783
+ async function resolveDomainByHostname(provider, appId, hostname, command, signal) {
784
+ const matched = (await provider.listDomains(appId, { signal }).catch((error) => {
767
785
  throw domainCommandError(command, error, hostname);
768
786
  })).find((domain) => sameDomainHostname(domain.hostname, hostname));
769
787
  if (matched) return matched;
@@ -994,9 +1012,20 @@ function formatElapsed(milliseconds) {
994
1012
  const remainingSeconds = seconds % 60;
995
1013
  return `${minutes}:${String(remainingSeconds).padStart(2, "0")}`;
996
1014
  }
997
- async function sleep(milliseconds) {
1015
+ async function sleep(milliseconds, signal) {
998
1016
  if (milliseconds <= 0) return;
999
- await new Promise((resolve) => setTimeout(resolve, milliseconds));
1017
+ signal.throwIfAborted();
1018
+ await new Promise((resolve, reject) => {
1019
+ const onAbort = () => {
1020
+ clearTimeout(timeout);
1021
+ reject(signal.reason);
1022
+ };
1023
+ const timeout = setTimeout(() => {
1024
+ signal.removeEventListener("abort", onAbort);
1025
+ resolve();
1026
+ }, milliseconds);
1027
+ signal.addEventListener("abort", onAbort, { once: true });
1028
+ });
1000
1029
  }
1001
1030
  async function resolveDeployAppSelection(context, projectId, apps, options) {
1002
1031
  if (options.explicitAppName) {
@@ -1211,7 +1240,10 @@ function resolveRollbackTarget(deployments, currentLiveDeploymentId) {
1211
1240
  });
1212
1241
  }
1213
1242
  async function listApps(context, provider, projectId, branchName) {
1214
- return provider.listApps(projectId, { branchName }).then(sortApps).catch((error) => {
1243
+ return provider.listApps(projectId, {
1244
+ branchName,
1245
+ signal: context.runtime.signal
1246
+ }).then(sortApps).catch((error) => {
1215
1247
  if (isMissingProjectError(error)) throw new CliError({
1216
1248
  code: "PROJECT_NOT_FOUND",
1217
1249
  domain: "project",
@@ -1229,20 +1261,20 @@ async function requirePreviewAppProvider(context) {
1229
1261
  return provider;
1230
1262
  }
1231
1263
  async function requirePreviewAppProviderWithClient(context) {
1232
- const client = await requireComputeAuth(context.runtime.env);
1264
+ const client = await requireComputeAuth(context.runtime.env, context.runtime.signal);
1233
1265
  if (!client) throw authRequiredError(["prisma-cli auth login"]);
1234
1266
  return {
1235
1267
  client,
1236
- provider: createPreviewAppProvider(client, createPreviewLogAuthOptions(context.runtime.env))
1268
+ provider: createPreviewAppProvider(client, createPreviewLogAuthOptions(context.runtime.env, context.runtime.signal))
1237
1269
  };
1238
1270
  }
1239
- function createPreviewLogAuthOptions(env) {
1271
+ function createPreviewLogAuthOptions(env, signal) {
1240
1272
  const rawToken = env[SERVICE_TOKEN_ENV_VAR]?.trim();
1241
1273
  if (rawToken) return {
1242
1274
  baseUrl: getApiBaseUrl(env),
1243
1275
  getToken: async () => rawToken
1244
1276
  };
1245
- const tokenStorage = new FileTokenStorage(env);
1277
+ const tokenStorage = new FileTokenStorage(env, signal);
1246
1278
  return {
1247
1279
  baseUrl: getApiBaseUrl(env),
1248
1280
  getToken: async () => {
@@ -1280,7 +1312,7 @@ async function resolveProjectContext(context, client, explicitProject, options)
1280
1312
  workspace: authState.workspace,
1281
1313
  explicitProject,
1282
1314
  envProjectId: options?.envProjectId,
1283
- listProjects: () => listRealWorkspaceProjects(client, authState.workspace),
1315
+ listProjects: () => listRealWorkspaceProjects(client, authState.workspace, context.runtime.signal),
1284
1316
  commandName: options?.commandName
1285
1317
  });
1286
1318
  const branch = options?.branch ?? await resolveDeployBranch(context, void 0);
@@ -1296,8 +1328,8 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1296
1328
  const workspace = (await requireAuthenticatedAuthState(context)).workspace;
1297
1329
  if (!workspace) throw workspaceRequiredError();
1298
1330
  const branch = options.branch ?? await resolveDeployBranch(context, void 0);
1299
- const projects = await listRealWorkspaceProjects(client, workspace);
1300
- if (explicitProject) return withDeployBranch({
1331
+ const projects = await listRealWorkspaceProjects(client, workspace, context.runtime.signal);
1332
+ if (explicitProject) return withRemoteDeployBranch(provider, {
1301
1333
  workspace,
1302
1334
  project: toProjectSummary(resolveProjectForSetup(explicitProject, projects, workspace)),
1303
1335
  resolution: {
@@ -1306,25 +1338,25 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1306
1338
  targetNameSource: "explicit"
1307
1339
  },
1308
1340
  localPinAction: "linked"
1309
- }, branch);
1341
+ }, branch, context.runtime.signal);
1310
1342
  if (options.createProjectName) {
1311
1343
  const projectName = options.createProjectName.trim();
1312
1344
  if (!projectName) throw projectSetupNameRequiredError("app deploy --create-project");
1313
- return withDeployBranch({
1345
+ return withRemoteDeployBranch(provider, {
1314
1346
  workspace,
1315
- project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace)),
1347
+ project: toProjectSummary(await createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal)),
1316
1348
  resolution: {
1317
1349
  projectSource: "created",
1318
1350
  targetName: projectName,
1319
1351
  targetNameSource: "explicit"
1320
1352
  },
1321
1353
  localPinAction: "created"
1322
- }, branch);
1354
+ }, branch, context.runtime.signal);
1323
1355
  }
1324
1356
  if (options.envProjectId) {
1325
1357
  const project = projects.find((candidate) => candidate.id === options.envProjectId);
1326
1358
  if (!project) throw projectNotFoundError(options.envProjectId, workspace);
1327
- return withDeployBranch({
1359
+ return withRemoteDeployBranch(provider, {
1328
1360
  workspace,
1329
1361
  project: toProjectSummary(project),
1330
1362
  resolution: {
@@ -1332,14 +1364,14 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1332
1364
  targetName: options.envProjectId,
1333
1365
  targetNameSource: "env"
1334
1366
  }
1335
- }, branch);
1367
+ }, branch, context.runtime.signal);
1336
1368
  }
1337
1369
  const localPin = options.localPin;
1338
1370
  if (localPin.kind === "present") {
1339
1371
  if (localPin.pin.workspaceId !== workspace.id) throw localResolutionPinStaleError();
1340
1372
  const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
1341
1373
  if (!project) throw localResolutionPinStaleError();
1342
- return withDeployBranch({
1374
+ return withRemoteDeployBranch(provider, {
1343
1375
  workspace,
1344
1376
  project: toProjectSummary(project),
1345
1377
  resolution: {
@@ -1347,10 +1379,10 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1347
1379
  targetName: project.name,
1348
1380
  targetNameSource: "local-pin"
1349
1381
  }
1350
- }, branch);
1382
+ }, branch, context.runtime.signal);
1351
1383
  }
1352
1384
  const platformMapping = await resolveDurablePlatformMapping();
1353
- if (platformMapping && platformMapping.workspace.id === workspace.id) return withDeployBranch({
1385
+ if (platformMapping && platformMapping.workspace.id === workspace.id) return withRemoteDeployBranch(provider, {
1354
1386
  workspace,
1355
1387
  project: toProjectSummary(platformMapping),
1356
1388
  resolution: {
@@ -1358,15 +1390,15 @@ async function resolveDeployProjectContext(context, client, provider, explicitPr
1358
1390
  targetName: platformMapping.name,
1359
1391
  targetNameSource: "platform-mapping"
1360
1392
  }
1361
- }, branch);
1362
- if (canPrompt(context) && !context.flags.yes) return withDeployBranch(await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects), branch);
1363
- throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd));
1393
+ }, branch, context.runtime.signal);
1394
+ if (canPrompt(context) && !context.flags.yes) return withRemoteDeployBranch(provider, await resolveInteractiveDeployProjectSetup(context, provider, workspace, projects), branch, context.runtime.signal);
1395
+ throw projectSetupRequiredError(projects, await inferTargetName(context.runtime.cwd, context.runtime.signal));
1364
1396
  }
1365
1397
  async function resolveInteractiveDeployProjectSetup(context, provider, workspace, projects) {
1366
1398
  const setup = await promptForProjectSetupChoice({
1367
1399
  context,
1368
1400
  projects,
1369
- createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace),
1401
+ createProject: (projectName) => createProjectForDeploySetup(provider, projectName, workspace, context.runtime.signal),
1370
1402
  cancel: {
1371
1403
  why: "Deploy needs a Project before it can continue.",
1372
1404
  fix: "Choose an existing Project or create a new one, then rerun deploy.",
@@ -1384,8 +1416,11 @@ async function resolveInteractiveDeployProjectSetup(context, provider, workspace
1384
1416
  localPinAction: setup.action
1385
1417
  };
1386
1418
  }
1387
- async function createProjectForDeploySetup(provider, projectName, workspace) {
1388
- const created = await provider.createProject({ name: projectName }).catch((error) => {
1419
+ async function createProjectForDeploySetup(provider, projectName, workspace, signal) {
1420
+ const created = await provider.createProject({
1421
+ name: projectName,
1422
+ signal
1423
+ }).catch((error) => {
1389
1424
  throw projectCreateFailedError(error, projectName, workspace, {
1390
1425
  nextSteps: [
1391
1426
  "prisma-cli project list",
@@ -1402,12 +1437,16 @@ async function createProjectForDeploySetup(provider, projectName, workspace) {
1402
1437
  workspace
1403
1438
  };
1404
1439
  }
1405
- function withDeployBranch(target, branch) {
1440
+ async function withRemoteDeployBranch(provider, target, branch, signal) {
1441
+ const remoteBranch = await provider.resolveBranch(target.project.id, {
1442
+ branchName: branch.name,
1443
+ signal
1444
+ });
1406
1445
  return {
1407
1446
  ...target,
1408
1447
  branch: {
1409
- name: branch.name,
1410
- kind: toBranchKind(branch.name)
1448
+ name: remoteBranch.name,
1449
+ kind: remoteBranch.role
1411
1450
  }
1412
1451
  };
1413
1452
  }
@@ -1432,7 +1471,7 @@ async function resolveDeployBranch(context, explicitBranchName) {
1432
1471
  name: explicitBranchName,
1433
1472
  annotation: "set by --branch"
1434
1473
  };
1435
- const gitBranch = await readLocalGitBranch(context.runtime.cwd);
1474
+ const gitBranch = await readLocalGitBranch(context.runtime.cwd, context.runtime.signal);
1436
1475
  if (gitBranch) return {
1437
1476
  name: gitBranch,
1438
1477
  annotation: "from local Git branch"
@@ -1442,26 +1481,39 @@ async function resolveDeployBranch(context, explicitBranchName) {
1442
1481
  annotation: "default"
1443
1482
  };
1444
1483
  }
1445
- async function readLocalGitBranch(cwd) {
1446
- const headPath = await resolveGitHeadPath(path.join(cwd, ".git"));
1484
+ async function readLocalGitBranch(cwd, signal) {
1485
+ const headPath = await resolveGitHeadPath(path.join(cwd, ".git"), signal);
1447
1486
  if (!headPath) return null;
1448
1487
  try {
1449
- const head = (await readFile(headPath, "utf8")).trim();
1488
+ const head = (await readFile(headPath, {
1489
+ encoding: "utf8",
1490
+ signal
1491
+ })).trim();
1450
1492
  if (head.startsWith("ref: refs/heads/")) return head.slice(16);
1451
- } catch {
1493
+ } catch (error) {
1494
+ if (signal.aborted) throw error;
1452
1495
  return null;
1453
1496
  }
1454
1497
  return null;
1455
1498
  }
1456
- async function resolveGitHeadPath(gitPath) {
1499
+ async function resolveGitHeadPath(gitPath, signal) {
1500
+ signal.throwIfAborted();
1457
1501
  try {
1458
- const raw = await readFile(gitPath, "utf8");
1502
+ const raw = await readFile(gitPath, {
1503
+ encoding: "utf8",
1504
+ signal
1505
+ });
1459
1506
  if (raw.startsWith("gitdir:")) return path.join(path.resolve(path.dirname(gitPath), raw.slice(7).trim()), "HEAD");
1460
- } catch {}
1507
+ } catch (error) {
1508
+ if (signal.aborted) throw error;
1509
+ }
1510
+ signal.throwIfAborted();
1461
1511
  try {
1462
1512
  await access(path.join(gitPath, "HEAD"));
1513
+ signal.throwIfAborted();
1463
1514
  return path.join(gitPath, "HEAD");
1464
- } catch {
1515
+ } catch (error) {
1516
+ if (signal.aborted) throw error;
1465
1517
  return null;
1466
1518
  }
1467
1519
  }
@@ -1473,7 +1525,7 @@ async function resolveDeployFramework(context, options) {
1473
1525
  displayName: "Bun",
1474
1526
  annotation: "set by --entry"
1475
1527
  };
1476
- const detected = await detectDeployFramework(context.runtime.cwd);
1528
+ const detected = await detectDeployFramework(context.runtime.cwd, context.runtime.signal);
1477
1529
  if (detected) return detected;
1478
1530
  throw frameworkNotDetectedError(context.runtime.cwd);
1479
1531
  }
@@ -1491,23 +1543,26 @@ function assertSupportedEntrypointForRequestedDeployShape(options) {
1491
1543
  if (!options.requestedFramework) return;
1492
1544
  assertSupportedEntrypoint(frameworkFromUserFacingValue(options.requestedFramework, "set by --framework").buildType, options.entrypoint, "deploy");
1493
1545
  }
1494
- async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint) {
1546
+ async function resolveDeployEntrypoint(cwd, framework, explicitEntrypoint, signal) {
1495
1547
  if (explicitEntrypoint || framework.buildType !== "bun") return explicitEntrypoint;
1496
- const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd));
1548
+ const packageEntrypoint = readBunPackageEntrypoint(await readBunPackageJson(cwd, signal));
1497
1549
  if (packageEntrypoint) return packageEntrypoint;
1498
1550
  if (framework.key !== "hono") return;
1499
1551
  const defaultEntrypoint = "src/index.ts";
1552
+ signal.throwIfAborted();
1500
1553
  try {
1501
1554
  await access(path.join(cwd, defaultEntrypoint));
1555
+ signal.throwIfAborted();
1502
1556
  return defaultEntrypoint;
1503
1557
  } catch (error) {
1558
+ if (signal.aborted) throw error;
1504
1559
  if (error.code !== "ENOENT") throw error;
1505
1560
  return;
1506
1561
  }
1507
1562
  }
1508
- async function detectDeployFramework(cwd) {
1509
- const packageJson = await readBunPackageJson(cwd);
1510
- const nextConfig = await detectNextConfig(cwd);
1563
+ async function detectDeployFramework(cwd, signal) {
1564
+ const packageJson = await readBunPackageJson(cwd, signal);
1565
+ const nextConfig = await detectNextConfig(cwd, signal);
1511
1566
  if (nextConfig.exists || hasPackageDependency(packageJson, "next")) return {
1512
1567
  key: "nextjs",
1513
1568
  buildType: "nextjs",
@@ -1528,7 +1583,7 @@ async function detectDeployFramework(cwd) {
1528
1583
  };
1529
1584
  return null;
1530
1585
  }
1531
- async function detectNextConfig(cwd) {
1586
+ async function detectNextConfig(cwd, signal) {
1532
1587
  for (const candidate of [
1533
1588
  "next.config.js",
1534
1589
  "next.config.mjs",
@@ -1537,13 +1592,18 @@ async function detectNextConfig(cwd) {
1537
1592
  "next.config.mts"
1538
1593
  ]) {
1539
1594
  const filePath = path.join(cwd, candidate);
1595
+ signal.throwIfAborted();
1540
1596
  try {
1541
- const content = await readFile(filePath, "utf8");
1597
+ const content = await readFile(filePath, {
1598
+ encoding: "utf8",
1599
+ signal
1600
+ });
1542
1601
  return {
1543
1602
  exists: true,
1544
1603
  standalone: /\boutput\s*:\s*["'`]standalone["'`]/.test(content)
1545
1604
  };
1546
1605
  } catch (error) {
1606
+ if (signal.aborted) throw error;
1547
1607
  if (error.code !== "ENOENT") throw error;
1548
1608
  }
1549
1609
  }
@@ -1715,7 +1775,7 @@ function formatDeployDirectory(cwd) {
1715
1775
  async function readCurrentWorkspaceId(context) {
1716
1776
  const state = await context.stateStore.read();
1717
1777
  if (state.auth?.workspaceId) return state.auth.workspaceId;
1718
- return (await readAuthState(context.runtime.env)).workspace?.id ?? null;
1778
+ return (await readAuthState(context.runtime.env, context.runtime.signal)).workspace?.id ?? null;
1719
1779
  }
1720
1780
  function normalizeBuildType(requestedBuildType) {
1721
1781
  if (!requestedBuildType) return "auto";
@@ -1737,7 +1797,7 @@ function assertSupportedEntrypoint(buildType, entrypoint, commandName) {
1737
1797
  }
1738
1798
  }
1739
1799
  async function requireLocalBuildType(context, buildType, commandName) {
1740
- const resolvedBuildType = await resolveLocalBuildType(context.runtime.cwd, buildType);
1800
+ const resolvedBuildType = await resolveLocalBuildType(context.runtime.cwd, buildType, context.runtime.signal);
1741
1801
  if (resolvedBuildType) return resolvedBuildType;
1742
1802
  throw usageError(`App ${commandName} requires an explicit framework when detection is ambiguous`, "This preview only starts local dev servers for clear Next.js or Bun project shapes.", "Pass --build-type nextjs for a Next.js app, or pass --build-type bun with --entry <path> for a Bun app.", [`prisma-cli app ${commandName} --build-type nextjs`, `prisma-cli app ${commandName} --build-type bun --entry server.ts`], "app");
1743
1803
  }
@@ -11,32 +11,32 @@ function isRealMode(context) {
11
11
  async function runAuthLogin(context, options) {
12
12
  let result;
13
13
  if (isRealMode(context)) {
14
- await performLogin(context.runtime.env);
15
- result = await readAuthState(context.runtime.env);
14
+ await performLogin(context.runtime.env, context.runtime.signal);
15
+ result = await readAuthState(context.runtime.env, context.runtime.signal);
16
16
  } else result = await loginWithSelectionFlow(context, createAuthUseCases(createCliUseCaseGateways(context)), options);
17
17
  return createAuthSuccess("auth.login", result, ["prisma-cli auth whoami", "prisma-cli project list"]);
18
18
  }
19
19
  async function runAuthLogout(context) {
20
20
  let result;
21
21
  if (isRealMode(context)) {
22
- await performLogout(context.runtime.env);
23
- result = await readAuthState(context.runtime.env);
22
+ await performLogout(context.runtime.env, context.runtime.signal);
23
+ result = await readAuthState(context.runtime.env, context.runtime.signal);
24
24
  } else result = await createAuthUseCases(createCliUseCaseGateways(context)).logout();
25
25
  return createAuthSuccess("auth.logout", result, ["prisma-cli auth login"]);
26
26
  }
27
27
  async function runAuthWhoAmI(context) {
28
28
  let result;
29
- if (isRealMode(context)) result = await readAuthState(context.runtime.env);
29
+ if (isRealMode(context)) result = await readAuthState(context.runtime.env, context.runtime.signal);
30
30
  else result = await createAuthUseCases(createCliUseCaseGateways(context)).whoami();
31
31
  return createAuthSuccess("auth.whoami", result, result.authenticated ? [] : ["prisma-cli auth login"]);
32
32
  }
33
33
  async function requireAuthenticatedAuthState(context) {
34
34
  if (isRealMode(context)) {
35
- const current = await readAuthState(context.runtime.env);
35
+ const current = await readAuthState(context.runtime.env, context.runtime.signal);
36
36
  if (current.authenticated) return current;
37
37
  if (!canPrompt(context)) throw authRequiredError();
38
- await performLogin(context.runtime.env);
39
- return readAuthState(context.runtime.env);
38
+ await performLogin(context.runtime.env, context.runtime.signal);
39
+ return readAuthState(context.runtime.env, context.runtime.signal);
40
40
  }
41
41
  const useCases = createAuthUseCases(createCliUseCaseGateways(context));
42
42
  const current = await useCases.whoami();