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