betterstart-cli 0.0.37 → 0.0.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -5384,14 +5384,14 @@ function buildStepsConstant(steps) {
5384
5384
  ${entries.join(",\n")}
5385
5385
  ]`;
5386
5386
  }
5387
- function buildComponentSource(p22) {
5387
+ function buildComponentSource(p23) {
5388
5388
  const providerOpen = ` <NuqsAdapter>
5389
5389
  <React.Suspense fallback={null}>
5390
- <${p22.pascal}FormInner />
5390
+ <${p23.pascal}FormInner />
5391
5391
  </React.Suspense>
5392
5392
  </NuqsAdapter>`;
5393
5393
  const exportWrapper = `
5394
- export function ${p22.pascal}Form() {
5394
+ export function ${p23.pascal}Form() {
5395
5395
  return (
5396
5396
  ${providerOpen}
5397
5397
  )
@@ -5400,22 +5400,22 @@ ${providerOpen}
5400
5400
  return `'use client'
5401
5401
 
5402
5402
  import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'
5403
- import { ChevronLeft, ChevronRight${p22.hasListFields ? ", Trash2" : ""} } from 'lucide-react'
5403
+ import { ChevronLeft, ChevronRight${p23.hasListFields ? ", Trash2" : ""} } from 'lucide-react'
5404
5404
  import { createParser, useQueryState } from 'nuqs'
5405
5405
  import { NuqsAdapter } from 'nuqs/adapters/next/app'
5406
5406
  import * as React from 'react'
5407
- ${p22.rhfImport}
5407
+ ${p23.rhfImport}
5408
5408
  import { z } from 'zod/v3'
5409
- import { create${p22.pascal}Submission } from '@admin/actions/${p22.actionImportPath}'
5409
+ import { create${p23.pascal}Submission } from '@admin/actions/${p23.actionImportPath}'
5410
5410
 
5411
5411
  const formSchema = z.object({
5412
- ${p22.zodFields}
5412
+ ${p23.zodFields}
5413
5413
  })
5414
5414
 
5415
5415
  type FormValues = z.infer<typeof formSchema>
5416
5416
  ${buildFieldErrorHelper()}
5417
5417
 
5418
- ${p22.stepsConst}
5418
+ ${p23.stepsConst}
5419
5419
 
5420
5420
  const stepParser = createParser({
5421
5421
  parse(value) {
@@ -5433,7 +5433,7 @@ const stepParser = createParser({
5433
5433
  }
5434
5434
  }).withDefault(0)
5435
5435
 
5436
- function ${p22.pascal}FormInner() {
5436
+ function ${p23.pascal}FormInner() {
5437
5437
  const [currentStep, setCurrentStep] = useQueryState('step', stepParser)
5438
5438
  const [submitted, setSubmitted] = React.useState(false)
5439
5439
  const [submitting, startSubmitTransition] = React.useTransition()
@@ -5441,11 +5441,11 @@ function ${p22.pascal}FormInner() {
5441
5441
  const form = useForm<FormValues>({
5442
5442
  resolver: standardSchemaResolver(formSchema),
5443
5443
  defaultValues: {
5444
- ${p22.defaults}
5444
+ ${p23.defaults}
5445
5445
  },
5446
5446
  })
5447
5447
 
5448
- ${p22.fieldArraySetup}${p22.watchSetup}
5448
+ ${p23.fieldArraySetup}${p23.watchSetup}
5449
5449
  async function handleNext() {
5450
5450
  const stepFields = STEPS[currentStep].fields as (keyof FormValues)[]
5451
5451
  const isValid = await form.trigger(stepFields, { shouldFocus: true })
@@ -5461,9 +5461,9 @@ ${p22.fieldArraySetup}${p22.watchSetup}
5461
5461
  function onSubmit(values: FormValues) {
5462
5462
  startSubmitTransition(async () => {
5463
5463
  try {
5464
- const result = await create${p22.pascal}Submission(values)
5464
+ const result = await create${p23.pascal}Submission(values)
5465
5465
  if (result.success) {
5466
- ${p22.successHandler}
5466
+ ${p23.successHandler}
5467
5467
  } else {
5468
5468
  form.setError('root', { message: result.error || 'Something went wrong' })
5469
5469
  }
@@ -5477,7 +5477,7 @@ ${p22.fieldArraySetup}${p22.watchSetup}
5477
5477
  return (
5478
5478
  <div className="rounded-sm border p-6 text-center">
5479
5479
  <h3 className="text-lg font-semibold">Thank you!</h3>
5480
- <p className="mt-2 text-muted-foreground">${p22.successMessage}</p>
5480
+ <p className="mt-2 text-muted-foreground">${p23.successMessage}</p>
5481
5481
  </div>
5482
5482
  )
5483
5483
  }
@@ -5494,7 +5494,7 @@ ${p22.fieldArraySetup}${p22.watchSetup}
5494
5494
 
5495
5495
  {/* Step content */}
5496
5496
  <div key={currentStep} className="animate-in fade-in duration-300 space-y-6">
5497
- ${p22.stepContentBlocks}
5497
+ ${p23.stepContentBlocks}
5498
5498
  </div>
5499
5499
 
5500
5500
  {form.formState.errors.root && (
@@ -5528,7 +5528,7 @@ ${p22.stepContentBlocks}
5528
5528
  onClick={() => form.handleSubmit(onSubmit)()}
5529
5529
  className="inline-flex h-9 items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-xs transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50"
5530
5530
  >
5531
- {submitting ? 'Submitting...' : ${p22.submitText}}
5531
+ {submitting ? 'Submitting...' : ${p23.submitText}}
5532
5532
  </button>
5533
5533
  )}
5534
5534
  </div>
@@ -19793,23 +19793,32 @@ function eraseClackLine(options = {}) {
19793
19793
  const restore = below > 0 ? `\x1B[${below}B` : "";
19794
19794
  process.stdout.write(`\x1B[${up}A\x1B[${CLACK_LINE_ROWS}M${restore}`);
19795
19795
  }
19796
+ var activeSpinners = 0;
19797
+ function hasActiveSpinner() {
19798
+ return activeSpinners > 0;
19799
+ }
19796
19800
  function spinner2(options) {
19797
- const inner = p5.spinner(options);
19801
+ const inner = p5.spinner({ ...options, cancelMessage: "Setup cancelled." });
19798
19802
  const guided = options?.withGuide !== false;
19799
19803
  let started = false;
19804
+ const setStarted = (next) => {
19805
+ if (started === next) return;
19806
+ started = next;
19807
+ activeSpinners += next ? 1 : -1;
19808
+ };
19800
19809
  return {
19801
19810
  start: (message = "") => {
19802
- started = true;
19811
+ setStarted(true);
19803
19812
  inner.start(fitSpinnerMessage(message));
19804
19813
  },
19805
19814
  message: (message = "") => inner.message(fitSpinnerMessage(message)),
19806
19815
  stop: (message = "") => {
19807
- started = false;
19816
+ setStarted(false);
19808
19817
  inner.stop(message);
19809
19818
  },
19810
19819
  clear: () => {
19811
19820
  if (!started) return;
19812
- started = false;
19821
+ setStarted(false);
19813
19822
  inner.clear();
19814
19823
  if (guided && process.stdout.isTTY && process.env.CI !== "true") {
19815
19824
  process.stdout.write("\x1B[1A\x1B[2K");
@@ -21826,7 +21835,24 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
21826
21835
  import { execFileSync as execFileSync5, spawn as spawn4 } from "child_process";
21827
21836
  import fs39 from "fs";
21828
21837
  import path50 from "path";
21829
- import * as p17 from "@clack/prompts";
21838
+ import * as p18 from "@clack/prompts";
21839
+
21840
+ // core-engine/utils/cancel-guard.ts
21841
+ import * as p10 from "@clack/prompts";
21842
+ function installSetupCancelGuard() {
21843
+ const onSignal = () => {
21844
+ if (!hasActiveSpinner()) {
21845
+ p10.cancel("Setup cancelled.");
21846
+ }
21847
+ process.exit(0);
21848
+ };
21849
+ process.on("SIGINT", onSignal);
21850
+ process.on("SIGTERM", onSignal);
21851
+ return () => {
21852
+ process.off("SIGINT", onSignal);
21853
+ process.off("SIGTERM", onSignal);
21854
+ };
21855
+ }
21830
21856
 
21831
21857
  // core-engine/utils/prompt-theme.ts
21832
21858
  var GREEN_STEP_SUBMIT = "\x1B[32m\u25C7\x1B[39m";
@@ -21913,7 +21939,7 @@ function deferNextPromptCheck() {
21913
21939
 
21914
21940
  // adapters/next/init/prompts/database.ts
21915
21941
  import { execFileSync as execFileSync4 } from "child_process";
21916
- import * as p10 from "@clack/prompts";
21942
+ import * as p11 from "@clack/prompts";
21917
21943
  import pc from "picocolors";
21918
21944
  var VERCEL_NEON_URL = "https://vercel.com/dashboard/integrations/checkout/neon";
21919
21945
  async function promptServices(options) {
@@ -21921,7 +21947,7 @@ async function promptServices(options) {
21921
21947
  const url2 = await promptConnectionString();
21922
21948
  return { provider: "manual", url: url2 };
21923
21949
  }
21924
- const choice = await p10.select({
21950
+ const choice = await p11.select({
21925
21951
  message: "Connect a PostgreSQL Database",
21926
21952
  options: [
21927
21953
  {
@@ -21937,8 +21963,8 @@ async function promptServices(options) {
21937
21963
  ],
21938
21964
  initialValue: "vercel"
21939
21965
  });
21940
- if (p10.isCancel(choice)) {
21941
- p10.cancel("Setup cancelled.");
21966
+ if (p11.isCancel(choice)) {
21967
+ p11.cancel("Setup cancelled.");
21942
21968
  process.exit(0);
21943
21969
  }
21944
21970
  if (choice === "vercel") {
@@ -21949,7 +21975,7 @@ async function promptServices(options) {
21949
21975
  }
21950
21976
  function openBrowserVercelNeon() {
21951
21977
  openBrowser(VERCEL_NEON_URL);
21952
- p10.log.info(
21978
+ p11.log.info(
21953
21979
  `Opening Vercel... Create a Neon Postgres database, then copy the ${pc.cyan("DATABASE_URL")} from the dashboard.`
21954
21980
  );
21955
21981
  }
@@ -21959,12 +21985,12 @@ function openBrowserVercelNeonResource(url) {
21959
21985
  return;
21960
21986
  }
21961
21987
  openBrowser(url);
21962
- p10.log.info(
21988
+ p11.log.info(
21963
21989
  `Opening Vercel... Copy the Neon ${pc.cyan("DATABASE_URL")} from the database dashboard, then paste it below.`
21964
21990
  );
21965
21991
  }
21966
21992
  async function promptConnectionString() {
21967
- const input = await p10.text({
21993
+ const input = await p11.text({
21968
21994
  message: "Paste your PostgreSQL connection string",
21969
21995
  placeholder: "postgres://user:pass@host/db",
21970
21996
  validate(val) {
@@ -21978,8 +22004,8 @@ async function promptConnectionString() {
21978
22004
  }
21979
22005
  }
21980
22006
  });
21981
- if (p10.isCancel(input)) {
21982
- p10.cancel("Setup cancelled.");
22007
+ if (p11.isCancel(input)) {
22008
+ p11.cancel("Setup cancelled.");
21983
22009
  process.exit(0);
21984
22010
  }
21985
22011
  return input.replace(/^['"]|['"]$/g, "").trim();
@@ -22000,7 +22026,7 @@ function openBrowser(url) {
22000
22026
 
22001
22027
  // adapters/next/init/prompts/plugins.ts
22002
22028
  import { styleText } from "util";
22003
- import * as p11 from "@clack/prompts";
22029
+ import * as p12 from "@clack/prompts";
22004
22030
 
22005
22031
  // adapters/next/init/scaffolders/env.ts
22006
22032
  import crypto2 from "crypto";
@@ -22160,7 +22186,7 @@ async function promptPlugins(cwd, options = {}) {
22160
22186
  }
22161
22187
  };
22162
22188
  const storageStep = deferNextPromptCheck();
22163
- const storage = await p11.select({
22189
+ const storage = await p12.select({
22164
22190
  message: "Choose a file storage",
22165
22191
  options: [
22166
22192
  {
@@ -22179,8 +22205,8 @@ async function promptPlugins(cwd, options = {}) {
22179
22205
  ],
22180
22206
  initialValue: "vercel-blob"
22181
22207
  });
22182
- if (p11.isCancel(storage)) {
22183
- p11.cancel("Setup cancelled.");
22208
+ if (p12.isCancel(storage)) {
22209
+ p12.cancel("Setup cancelled.");
22184
22210
  process.exit(0);
22185
22211
  }
22186
22212
  if (storage === "r2") {
@@ -22191,7 +22217,7 @@ async function promptPlugins(cwd, options = {}) {
22191
22217
  const flow = !existingToken && options.provisionVercelBlob ? await options.provisionVercelBlob() : void 0;
22192
22218
  if (flow?.ok && flow.token) {
22193
22219
  persistBlobReadWriteToken(cwd, flow.token);
22194
- p11.log.success("Saved BLOB_READ_WRITE_TOKEN to .env.local");
22220
+ p12.log.success("Saved BLOB_READ_WRITE_TOKEN to .env.local");
22195
22221
  sections.push({
22196
22222
  header: "Storage (Vercel Blob)",
22197
22223
  vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: flow.token }]
@@ -22199,20 +22225,26 @@ async function promptPlugins(cwd, options = {}) {
22199
22225
  overwriteKeys.add("BLOB_READ_WRITE_TOKEN");
22200
22226
  if (flow.usedTerminalFallback) storageStep.skip();
22201
22227
  else storageStep.confirm();
22228
+ } else if (flow) {
22229
+ p12.log.warn(
22230
+ "Continuing without a Blob token \u2014 add BLOB_READ_WRITE_TOKEN to .env.local from the store on vercel.com/dashboard/stores."
22231
+ );
22232
+ sections.push({
22233
+ header: "Storage (Vercel Blob)",
22234
+ vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: "" }]
22235
+ });
22236
+ storageStep.skip();
22202
22237
  } else {
22203
22238
  if (existingToken) {
22204
- p11.log.info("Using the existing Vercel Blob token from .env.local");
22205
- } else if (options.provisionVercelBlob) {
22206
- p11.log.info("Falling back to a manual Vercel Blob token.");
22239
+ p12.log.info("Using the existing Vercel Blob token from .env.local");
22207
22240
  }
22208
22241
  mergeIntegrationConfig(await collectIntegrationConfig(cwd, ["vercel-blob"]));
22209
- if (flow) storageStep.skip();
22210
- else storageStep.confirm();
22242
+ storageStep.confirm();
22211
22243
  }
22212
22244
  } else {
22213
22245
  storageStep.confirm();
22214
22246
  }
22215
- const selectedPlugins = await p11.multiselect({
22247
+ const selectedPlugins = await p12.multiselect({
22216
22248
  message: "Select presets",
22217
22249
  options: [
22218
22250
  {
@@ -22224,12 +22256,12 @@ async function promptPlugins(cwd, options = {}) {
22224
22256
  required: false,
22225
22257
  initialValues: ["blog"]
22226
22258
  });
22227
- if (p11.isCancel(selectedPlugins)) {
22228
- p11.cancel("Setup cancelled.");
22259
+ if (p12.isCancel(selectedPlugins)) {
22260
+ p12.cancel("Setup cancelled.");
22229
22261
  process.exit(0);
22230
22262
  }
22231
22263
  const integrations = [];
22232
- const setupResend = await p11.confirm({
22264
+ const setupResend = await p12.confirm({
22233
22265
  message: `Do you want to setup resend.com emails?
22234
22266
  ${styleText(
22235
22267
  "dim",
@@ -22237,8 +22269,8 @@ ${styleText(
22237
22269
  )}`,
22238
22270
  initialValue: true
22239
22271
  });
22240
- if (p11.isCancel(setupResend)) {
22241
- p11.cancel("Setup cancelled.");
22272
+ if (p12.isCancel(setupResend)) {
22273
+ p12.cancel("Setup cancelled.");
22242
22274
  process.exit(0);
22243
22275
  }
22244
22276
  if (setupResend) {
@@ -22267,10 +22299,10 @@ ${styleText(
22267
22299
  }
22268
22300
 
22269
22301
  // adapters/next/init/prompts/project.ts
22270
- import * as p12 from "@clack/prompts";
22302
+ import * as p13 from "@clack/prompts";
22271
22303
  import pc2 from "picocolors";
22272
22304
  async function promptProject(defaultName) {
22273
- const projectName = await p12.text({
22305
+ const projectName = await p13.text({
22274
22306
  message: [
22275
22307
  "What is your project name?",
22276
22308
  `${pc2.cyan("\u2502")} ${pc2.dim("If you don't want a directory created with the project name, just press `")}${pc2.bold(
@@ -22289,16 +22321,16 @@ async function promptProject(defaultName) {
22289
22321
  return void 0;
22290
22322
  }
22291
22323
  });
22292
- if (p12.isCancel(projectName)) {
22293
- p12.cancel("Setup cancelled.");
22324
+ if (p13.isCancel(projectName)) {
22325
+ p13.cancel("Setup cancelled.");
22294
22326
  process.exit(0);
22295
22327
  }
22296
- const useSrcDir = await p12.confirm({
22328
+ const useSrcDir = await p13.confirm({
22297
22329
  message: "Use src/ directory?",
22298
22330
  initialValue: false
22299
22331
  });
22300
- if (p12.isCancel(useSrcDir)) {
22301
- p12.cancel("Setup cancelled.");
22332
+ if (p13.isCancel(useSrcDir)) {
22333
+ p13.cancel("Setup cancelled.");
22302
22334
  process.exit(0);
22303
22335
  }
22304
22336
  return { projectName: projectName.trim(), useSrcDir };
@@ -24144,11 +24176,11 @@ function scaffoldTsconfig(cwd, config) {
24144
24176
  }
24145
24177
 
24146
24178
  // adapters/next/init/vercel/flow.ts
24147
- import * as p16 from "@clack/prompts";
24179
+ import * as p17 from "@clack/prompts";
24148
24180
  import pc6 from "picocolors";
24149
24181
 
24150
24182
  // adapters/next/init/vercel/auth.ts
24151
- import * as p13 from "@clack/prompts";
24183
+ import * as p14 from "@clack/prompts";
24152
24184
  import pc3 from "picocolors";
24153
24185
 
24154
24186
  // adapters/next/init/vercel/runner.ts
@@ -24356,7 +24388,7 @@ async function ensureVercelAuth(runner, cwd, options) {
24356
24388
  checkSpinner.clear();
24357
24389
  const signInMessage = `Sign in to Vercel to continue ${pc3.dim("(or press Ctrl-C to enter a connection string manually)")}`;
24358
24390
  const signInRows = clackLogRows(signInMessage);
24359
- p13.log.info(signInMessage);
24391
+ p14.log.info(signInMessage);
24360
24392
  let streamedRows = 0;
24361
24393
  const login = await runVercel(runner, ["login"], {
24362
24394
  cwd,
@@ -24388,7 +24420,7 @@ function signedInMessage(username) {
24388
24420
  }
24389
24421
 
24390
24422
  // adapters/next/init/vercel/blob.ts
24391
- import * as p14 from "@clack/prompts";
24423
+ import * as p15 from "@clack/prompts";
24392
24424
  import pc4 from "picocolors";
24393
24425
 
24394
24426
  // adapters/next/init/vercel/env-pull.ts
@@ -24499,21 +24531,46 @@ function readLinkedProjectJson(cwd) {
24499
24531
  // adapters/next/init/vercel/blob.ts
24500
24532
  var PROVISION_TIMEOUT_MS = 18e4;
24501
24533
  var INTERACTIVE_PROVISION_TIMEOUT_MS = 6e5;
24502
- var MAX_STORE_NAME_ATTEMPTS = 5;
24534
+ var MAX_STORE_NAME_ATTEMPTS = 10;
24503
24535
  function blobStoreName(projectName) {
24504
24536
  return sanitizeVercelProjectName(`${projectName}-blob`);
24505
24537
  }
24506
24538
  function isNameTaken(result) {
24507
- return /already exists|already taken|already in use|name.*taken/i.test(
24539
+ return /already exists|already taken|already in use|name.*taken|conflict|\b409\b/i.test(
24508
24540
  `${result.stdout}
24509
24541
  ${result.stderr}`
24510
24542
  );
24511
24543
  }
24544
+ function candidateStoreName(baseName, attempt) {
24545
+ if (attempt < MAX_STORE_NAME_ATTEMPTS) return versionedVercelProjectName(baseName, attempt);
24546
+ const suffix = `-${Date.now().toString(36)}`;
24547
+ return `${baseName.slice(0, 100 - suffix.length)}${suffix}`;
24548
+ }
24549
+ function isEnvVarConflict(result) {
24550
+ return /already has an existing environment variable/i.test(`${result.stdout}
24551
+ ${result.stderr}`);
24552
+ }
24553
+ function createdStoreId(result) {
24554
+ return /Blob store created:[^(]*\((store_[A-Za-z0-9]+)\)/.exec(
24555
+ `${result.stdout}
24556
+ ${result.stderr}`
24557
+ )?.[1];
24558
+ }
24559
+ async function deleteOrphanStore(runner, cwd, createResult, env) {
24560
+ const storeId = createdStoreId(createResult);
24561
+ if (!storeId) return;
24562
+ await runVercel(runner, ["blob", "delete-store", storeId, "--yes"], {
24563
+ cwd,
24564
+ mode: "capture",
24565
+ timeoutMs: PROVISION_TIMEOUT_MS,
24566
+ env
24567
+ });
24568
+ }
24512
24569
  async function createStoreQuiet(runner, cwd, baseName, env) {
24513
24570
  let result;
24514
24571
  let storeName = baseName;
24515
24572
  for (let attempt = 1; attempt <= MAX_STORE_NAME_ATTEMPTS; attempt++) {
24516
- storeName = versionedVercelProjectName(baseName, attempt);
24573
+ storeName = candidateStoreName(baseName, attempt);
24517
24574
  result = await runVercel(
24518
24575
  runner,
24519
24576
  ["blob", "create-store", storeName, "--access", "public", "--yes"],
@@ -24526,45 +24583,80 @@ async function createStoreQuiet(runner, cwd, baseName, env) {
24526
24583
  async function provisionBlobStoreInteractive(runner, cwd, options) {
24527
24584
  const quietSpinner = spinner2();
24528
24585
  quietSpinner.start("Creating your Vercel Blob store");
24529
- const quiet = await createStoreQuiet(runner, cwd, options.name, options.env);
24530
- if (quiet.result.success) {
24531
- quietSpinner.message("Retrieving the Blob read-write token");
24532
- const token2 = await pullBlobToken(runner, cwd, options.env);
24533
- if (!token2) {
24534
- quietSpinner.stop("No BLOB_READ_WRITE_TOKEN came back from Vercel");
24535
- return { storeName: quiet.storeName, failure: "env-pull-empty" };
24536
- }
24586
+ let terminalFallbackRan = false;
24587
+ let lastResult;
24588
+ const pullToken = async (activeSpinner, storeName) => {
24589
+ const token = await pullBlobToken(runner, cwd, options.env);
24590
+ if (!token) {
24591
+ activeSpinner.stop("No BLOB_READ_WRITE_TOKEN came back from Vercel");
24592
+ return { ...storeName && { storeName }, failure: "env-pull-empty" };
24593
+ }
24594
+ activeSpinner.clear();
24595
+ return {
24596
+ token,
24597
+ ...storeName && { storeName },
24598
+ ...terminalFallbackRan && { usedTerminalFallback: true }
24599
+ };
24600
+ };
24601
+ for (let attempt = 1; attempt <= MAX_STORE_NAME_ATTEMPTS; attempt++) {
24602
+ const storeName = candidateStoreName(options.name, attempt);
24603
+ const result = await runVercel(
24604
+ runner,
24605
+ ["blob", "create-store", storeName, "--access", "public", "--yes"],
24606
+ { cwd, mode: "capture", timeoutMs: PROVISION_TIMEOUT_MS, env: options.env }
24607
+ );
24608
+ lastResult = result;
24609
+ if (result.success) {
24610
+ quietSpinner.message("Retrieving the Blob read-write token");
24611
+ return pullToken(quietSpinner, storeName);
24612
+ }
24613
+ if (isEnvVarConflict(result)) {
24614
+ await deleteOrphanStore(runner, cwd, result, options.env);
24615
+ quietSpinner.message("Retrieving the Blob read-write token");
24616
+ return pullToken(quietSpinner);
24617
+ }
24618
+ if (isNameTaken(result)) continue;
24619
+ if (terminalFallbackRan) break;
24620
+ terminalFallbackRan = true;
24537
24621
  quietSpinner.clear();
24538
- return { token: token2, storeName: quiet.storeName };
24539
- }
24540
- quietSpinner.clear();
24541
- p14.log.info(
24542
- `Create your Blob store in the Vercel prompts below ${pc4.dim(
24543
- "(connect it to all environments)."
24544
- )}`
24545
- );
24546
- const add = await runVercel(
24547
- runner,
24548
- ["blob", "create-store", quiet.storeName, "--access", "public"],
24549
- { cwd, mode: "inherit", timeoutMs: INTERACTIVE_PROVISION_TIMEOUT_MS, env: options.env }
24550
- );
24551
- if (!add.success) {
24552
- return { failure: add.timedOut ? "timeout" : "provision-failed" };
24553
- }
24554
- const pullSpinner = spinner2();
24555
- pullSpinner.start("Retrieving the Blob read-write token");
24556
- const token = await pullBlobToken(runner, cwd, options.env);
24557
- if (!token) {
24558
- pullSpinner.stop("No BLOB_READ_WRITE_TOKEN came back from Vercel");
24559
- return { storeName: quiet.storeName, failure: "env-pull-empty" };
24622
+ p15.log.info(
24623
+ `Create your Blob store in the Vercel prompts below ${pc4.dim(
24624
+ "(connect it to all environments)."
24625
+ )}`
24626
+ );
24627
+ const add = await runVercel(runner, ["blob", "create-store", storeName, "--access", "public"], {
24628
+ cwd,
24629
+ mode: "inherit",
24630
+ timeoutMs: INTERACTIVE_PROVISION_TIMEOUT_MS,
24631
+ env: options.env
24632
+ });
24633
+ if (add.success) {
24634
+ const pullSpinner = spinner2();
24635
+ pullSpinner.start("Retrieving the Blob read-write token");
24636
+ return pullToken(pullSpinner, storeName);
24637
+ }
24638
+ if (add.timedOut) return { failure: "timeout" };
24639
+ lastResult = add;
24640
+ quietSpinner.start("Creating your Vercel Blob store");
24560
24641
  }
24561
- pullSpinner.clear();
24562
- return { token, storeName: quiet.storeName, usedTerminalFallback: true };
24642
+ quietSpinner.stop("Vercel Blob provisioning did not complete");
24643
+ return { failure: lastResult?.timedOut ? "timeout" : "provision-failed" };
24563
24644
  }
24564
24645
  async function provisionBlobStore(runner, cwd, options) {
24565
24646
  const provisionSpinner = spinner2();
24566
24647
  provisionSpinner.start("Creating your Vercel Blob store");
24567
24648
  const { result, storeName } = await createStoreQuiet(runner, cwd, options.name, options.env);
24649
+ if (!result.success && isEnvVarConflict(result)) {
24650
+ await deleteOrphanStore(runner, cwd, result, options.env);
24651
+ provisionSpinner.message("Retrieving the Blob read-write token");
24652
+ const token2 = await pullBlobToken(runner, cwd, options.env);
24653
+ if (!token2) {
24654
+ provisionSpinner.stop("No BLOB_READ_WRITE_TOKEN came back from Vercel");
24655
+ return { failure: "env-pull-empty" };
24656
+ }
24657
+ provisionSpinner.clear();
24658
+ return { token: token2 };
24659
+ }
24568
24660
  if (!result.success) {
24569
24661
  provisionSpinner.stop("Vercel Blob provisioning did not complete");
24570
24662
  const combined = `${result.stdout}
@@ -24793,7 +24885,7 @@ function guardEnvLocal(cwd) {
24793
24885
  }
24794
24886
 
24795
24887
  // adapters/next/init/vercel/neon.ts
24796
- import * as p15 from "@clack/prompts";
24888
+ import * as p16 from "@clack/prompts";
24797
24889
  import pc5 from "picocolors";
24798
24890
  var PROVISION_TIMEOUT_MS2 = 18e4;
24799
24891
  var INTERACTIVE_PROVISION_TIMEOUT_MS2 = 6e5;
@@ -24811,7 +24903,7 @@ async function provisionNeonInteractive(runner, cwd, options) {
24811
24903
  return readNeonProvisionInfo(quiet.stdout);
24812
24904
  }
24813
24905
  quietSpinner.clear();
24814
- p15.log.info(
24906
+ p16.log.info(
24815
24907
  `Create your Neon database in the Vercel prompts below ${pc5.dim(
24816
24908
  "(the Free plan is recommended)."
24817
24909
  )}`
@@ -24885,14 +24977,14 @@ async function runVercelNeonFlow(options) {
24885
24977
  runnerSpinner.clear();
24886
24978
  const auth = await ensureVercelAuth(runner, options.cwd, { env });
24887
24979
  if (!auth.authed) {
24888
- p16.log.warn(authFailureMessage(auth.reason));
24980
+ p17.log.warn(authFailureMessage(auth.reason));
24889
24981
  return { ok: false };
24890
24982
  }
24891
24983
  const linkLinePrinted = await ensureLinkedProject(runner, options.cwd, options.projectName, env);
24892
24984
  const neon = await provisionNeonForMode(runner, options);
24893
24985
  if (neon.failure) {
24894
- p16.log.warn(neonFailureMessage(neon.failure));
24895
- if (neon.detail) p16.log.message(pc6.dim(neon.detail));
24986
+ p17.log.warn(neonFailureMessage(neon.failure));
24987
+ if (neon.detail) p17.log.message(pc6.dim(neon.detail));
24896
24988
  return { ok: false };
24897
24989
  }
24898
24990
  const databaseUrl = await pullNeonDatabaseUrl(runner, options.cwd, env);
@@ -24905,7 +24997,7 @@ async function runVercelNeonFlow(options) {
24905
24997
  dismissSignedInNote
24906
24998
  };
24907
24999
  } catch (error) {
24908
- p16.log.warn(
25000
+ p17.log.warn(
24909
25001
  `Vercel provisioning failed: ${error instanceof Error ? error.message : String(error)}`
24910
25002
  );
24911
25003
  return { ok: false };
@@ -24923,14 +25015,14 @@ async function runVercelBlobFlow(options) {
24923
25015
  runnerSpinner.clear();
24924
25016
  const auth = await ensureVercelAuth(runner, options.cwd, { env, quietIfAuthed: true });
24925
25017
  if (!auth.authed) {
24926
- p16.log.warn(authFailureMessage(auth.reason));
25018
+ p17.log.warn(authFailureMessage(auth.reason));
24927
25019
  return { ok: false };
24928
25020
  }
24929
25021
  await ensureLinkedProject(runner, options.cwd, options.projectName, env);
24930
25022
  const blob = await provisionBlobForMode(runner, options);
24931
25023
  if (blob.failure || !blob.token) {
24932
- p16.log.warn(blobFailureMessage(blob.failure));
24933
- if (blob.detail) p16.log.message(pc6.dim(blob.detail));
25024
+ p17.log.warn(blobFailureMessage(blob.failure));
25025
+ if (blob.detail) p17.log.message(pc6.dim(blob.detail));
24934
25026
  return { ok: false };
24935
25027
  }
24936
25028
  return {
@@ -24940,7 +25032,7 @@ async function runVercelBlobFlow(options) {
24940
25032
  usedTerminalFallback: blob.usedTerminalFallback
24941
25033
  };
24942
25034
  } catch (error) {
24943
- p16.log.warn(
25035
+ p17.log.warn(
24944
25036
  `Vercel provisioning failed: ${error instanceof Error ? error.message : String(error)}`
24945
25037
  );
24946
25038
  return { ok: false };
@@ -24958,7 +25050,7 @@ async function runVercelDeployFlow(options) {
24958
25050
  runnerSpinner.clear();
24959
25051
  const auth = await ensureVercelAuth(runner, options.cwd, { env, quietIfAuthed: true });
24960
25052
  if (!auth.authed) {
24961
- p16.log.warn(authFailureMessage(auth.reason));
25053
+ p17.log.warn(authFailureMessage(auth.reason));
24962
25054
  printManualDeployHint();
24963
25055
  return { ok: false };
24964
25056
  }
@@ -24976,17 +25068,17 @@ async function runVercelDeployFlow(options) {
24976
25068
  let printedWarnings = false;
24977
25069
  for (const key of sync.failed) {
24978
25070
  printedWarnings = true;
24979
- p16.log.warn(
25071
+ p17.log.warn(
24980
25072
  `Could not set ${pc6.cyan(key)} on Vercel \u2014 add it in the project's environment settings.`
24981
25073
  );
24982
25074
  }
24983
25075
  if (ensureVercelJsonFramework(options.cwd) === "created") {
24984
- p16.log.success(`Created ${pc6.cyan("vercel.json")} with the Next.js framework preset`);
25076
+ p17.log.success(`Created ${pc6.cyan("vercel.json")} with the Next.js framework preset`);
24985
25077
  }
24986
25078
  const packageGuard = guardProjectForDeploy(options.cwd);
24987
25079
  for (const dep of packageGuard.localSpecDeps) {
24988
25080
  printedWarnings = true;
24989
- p16.log.warn(
25081
+ p17.log.warn(
24990
25082
  `Dependency ${pc6.cyan(dep)} uses a local spec that cannot install on Vercel \u2014 the remote build may fail.`
24991
25083
  );
24992
25084
  }
@@ -25000,7 +25092,7 @@ async function runVercelDeployFlow(options) {
25000
25092
  }
25001
25093
  if (deploy.failure) {
25002
25094
  deploySpinner.stop(`${pc6.yellow("\u25B2")} ${deployFailureMessage(deploy.failure)}`);
25003
- if (deploy.detail) p16.log.message(pc6.dim(deploy.detail));
25095
+ if (deploy.detail) p17.log.message(pc6.dim(deploy.detail));
25004
25096
  printManualDeployHint();
25005
25097
  return { ok: false };
25006
25098
  }
@@ -25009,7 +25101,7 @@ async function runVercelDeployFlow(options) {
25009
25101
  deploySpinner.stop(url ? `Deployed ${pc6.cyan(url)}` : "Deployed to Vercel");
25010
25102
  return { ok: true, url, syncedEnvKeys: sync.synced, cleanScreen: !printedWarnings };
25011
25103
  } catch (error) {
25012
- p16.log.warn(`Vercel deploy failed: ${error instanceof Error ? error.message : String(error)}`);
25104
+ p17.log.warn(`Vercel deploy failed: ${error instanceof Error ? error.message : String(error)}`);
25013
25105
  printManualDeployHint();
25014
25106
  return { ok: false };
25015
25107
  } finally {
@@ -25017,7 +25109,7 @@ async function runVercelDeployFlow(options) {
25017
25109
  }
25018
25110
  }
25019
25111
  function printManualDeployHint() {
25020
- p16.log.info(`You can deploy manually: ${pc6.cyan("vercel deploy --prod")}`);
25112
+ p17.log.info(`You can deploy manually: ${pc6.cyan("vercel deploy --prod")}`);
25021
25113
  }
25022
25114
  async function ensureLinkedProject(runner, cwd, projectName, env) {
25023
25115
  if (readLinkedProjectId(cwd)) return false;
@@ -25065,11 +25157,11 @@ function authFailureMessage(reason) {
25065
25157
  function blobFailureMessage(reason) {
25066
25158
  switch (reason) {
25067
25159
  case "env-pull-empty":
25068
- return "Created a Blob store, but no BLOB_READ_WRITE_TOKEN came back \u2014 falling back to manual entry.";
25160
+ return "Created a Blob store, but no BLOB_READ_WRITE_TOKEN came back from Vercel.";
25069
25161
  case "timeout":
25070
- return "Vercel Blob provisioning timed out \u2014 falling back to manual entry.";
25162
+ return "Vercel Blob provisioning timed out.";
25071
25163
  default:
25072
- return "Vercel Blob provisioning did not complete \u2014 falling back to manual entry.";
25164
+ return "Vercel Blob provisioning did not complete.";
25073
25165
  }
25074
25166
  }
25075
25167
  function deployFailureMessage(reason) {
@@ -25483,7 +25575,8 @@ function removeExistingAdminPaths(cwd, namespaces) {
25483
25575
  }
25484
25576
  async function runInitCommand(name, options) {
25485
25577
  installPromptCheckmarks();
25486
- p17.box(
25578
+ const disposeCancelGuard = installSetupCancelGuard();
25579
+ p18.box(
25487
25580
  `
25488
25581
  \u2584 \u2597 \u2597 \u2584\u2596\u2597 \u2597
25489
25582
  \u2599\u2598\u2588\u258C\u259C\u2598\u259C\u2598\u2588\u258C\u259B\u2598\u259A \u259C\u2598\u2580\u258C\u259B\u2598\u259C\u2598
@@ -25504,13 +25597,13 @@ async function runInitCommand(name, options) {
25504
25597
  try {
25505
25598
  namespace = validateAdminNamespace(options.namespace);
25506
25599
  } catch (error) {
25507
- p17.log.error(error instanceof Error ? error.message : String(error));
25600
+ p18.log.error(error instanceof Error ? error.message : String(error));
25508
25601
  process.exit(1);
25509
25602
  }
25510
25603
  }
25511
25604
  if (!options.yes && !options.namespace) {
25512
25605
  const defaultDashboardPath = resolveAdminNamespace(DEFAULT_ADMIN_NAMESPACE).routePath;
25513
- const namespaceInput = await p17.text({
25606
+ const namespaceInput = await p18.text({
25514
25607
  message: "Enter the dashboard path",
25515
25608
  placeholder: `eg. ${defaultDashboardPath}`,
25516
25609
  defaultValue: defaultDashboardPath,
@@ -25523,8 +25616,8 @@ async function runInitCommand(name, options) {
25523
25616
  }
25524
25617
  }
25525
25618
  });
25526
- if (p17.isCancel(namespaceInput)) {
25527
- p17.cancel("Setup cancelled.");
25619
+ if (p18.isCancel(namespaceInput)) {
25620
+ p18.cancel("Setup cancelled.");
25528
25621
  process.exit(0);
25529
25622
  }
25530
25623
  namespace = validateAdminDashboardPath(namespaceInput);
@@ -25536,13 +25629,13 @@ async function runInitCommand(name, options) {
25536
25629
  if (project2.isExisting) {
25537
25630
  srcDir = project2.hasSrcDir;
25538
25631
  if (!project2.hasTypeScript) {
25539
- p17.log.error("TypeScript is required. Please add a tsconfig.json first.");
25632
+ p18.log.error("TypeScript is required. Please add a tsconfig.json first.");
25540
25633
  process.exit(1);
25541
25634
  }
25542
25635
  if (forceMode) {
25543
25636
  const nuked = removeExistingAdminPaths(cwd, await resolveForceInitNamespaces(cwd, namespace));
25544
25637
  if (nuked > 0) {
25545
- p17.log.warn(`${pc7.yellow("Force mode:")} removed ${nuked} existing admin paths`);
25638
+ p18.log.warn(`${pc7.yellow("Force mode:")} removed ${nuked} existing admin paths`);
25546
25639
  }
25547
25640
  project2 = detectProject(cwd, namespace);
25548
25641
  } else if (project2.conflicts.length > 0) {
@@ -25551,9 +25644,9 @@ async function runInitCommand(name, options) {
25551
25644
  "",
25552
25645
  pc7.dim(`Use ${pc7.bold("--force")} to remove existing admin files before scaffolding.`)
25553
25646
  );
25554
- p17.note(conflictLines.join("\n"), pc7.yellow("Conflicts"));
25647
+ p18.note(conflictLines.join("\n"), pc7.yellow("Conflicts"));
25555
25648
  if (!options.yes) {
25556
- const proceed = await p17.confirm({
25649
+ const proceed = await p18.confirm({
25557
25650
  message: [
25558
25651
  `Continue with ${pc7.bold(pc7.cyan("--force"))}?`,
25559
25652
  `${pc7.cyan("\u2502")} ${pc7.dim("This will force overwrite the existing admin code.")}`,
@@ -25561,8 +25654,8 @@ async function runInitCommand(name, options) {
25561
25654
  ].join("\n"),
25562
25655
  initialValue: true
25563
25656
  });
25564
- if (p17.isCancel(proceed) || !proceed) {
25565
- p17.cancel("Setup cancelled.");
25657
+ if (p18.isCancel(proceed) || !proceed) {
25658
+ p18.cancel("Setup cancelled.");
25566
25659
  process.exit(0);
25567
25660
  }
25568
25661
  forceMode = true;
@@ -25571,23 +25664,23 @@ async function runInitCommand(name, options) {
25571
25664
  await resolveForceInitNamespaces(cwd, namespace)
25572
25665
  );
25573
25666
  if (nuked > 0) {
25574
- p17.log.warn(`${pc7.yellow("Force mode:")} removed ${nuked} existing admin paths`);
25667
+ p18.log.warn(`${pc7.yellow("Force mode:")} removed ${nuked} existing admin paths`);
25575
25668
  }
25576
25669
  project2 = detectProject(cwd, namespace);
25577
25670
  }
25578
25671
  }
25579
25672
  } else {
25580
- p17.log.info("No Next.js app found \u2014 Running the fresh project mode...");
25673
+ p18.log.info("No Next.js app found \u2014 Running the fresh project mode...");
25581
25674
  let projectPrompt;
25582
25675
  try {
25583
25676
  projectPrompt = options.yes ? resolveNonInteractiveProject(name) : await promptProject(name);
25584
25677
  } catch (error) {
25585
- p17.log.error(error instanceof Error ? error.message : String(error));
25678
+ p18.log.error(error instanceof Error ? error.message : String(error));
25586
25679
  process.exit(1);
25587
25680
  }
25588
25681
  srcDir = projectPrompt.useSrcDir;
25589
25682
  if (!options.yes) {
25590
- const pmChoice = await p17.select({
25683
+ const pmChoice = await p18.select({
25591
25684
  message: "Which package manager do you want to use?",
25592
25685
  options: [
25593
25686
  { value: "pnpm", label: "pnpm", hint: "recommended" },
@@ -25596,8 +25689,8 @@ async function runInitCommand(name, options) {
25596
25689
  { value: "bun", label: "bun" }
25597
25690
  ]
25598
25691
  });
25599
- if (p17.isCancel(pmChoice)) {
25600
- p17.cancel("Setup cancelled.");
25692
+ if (p18.isCancel(pmChoice)) {
25693
+ p18.cancel("Setup cancelled.");
25601
25694
  process.exit(0);
25602
25695
  }
25603
25696
  pm = pmChoice;
@@ -25631,8 +25724,8 @@ async function runInitCommand(name, options) {
25631
25724
  process.stderr.write(`${createNextAppResult.output.trimEnd()}
25632
25725
  `);
25633
25726
  }
25634
- p17.log.error(createNextAppResult.error);
25635
- p17.log.info(
25727
+ p18.log.error(createNextAppResult.error);
25728
+ p18.log.info(
25636
25729
  `You can create the project manually:
25637
25730
  ${pc7.cyan(`npx create-next-app@latest ${projectPrompt.projectName} --typescript --tailwind --app`)}
25638
25731
  Then run ${pc7.cyan("betterstart init")} inside it.`
@@ -25646,11 +25739,11 @@ async function runInitCommand(name, options) {
25646
25739
  );
25647
25740
  if (!hasPackageJson || !hasNextConfig) {
25648
25741
  createNextAppSpinner.stop(`Failed to create Next.js app: ${displayName}`);
25649
- p17.log.error(
25742
+ p18.log.error(
25650
25743
  "create-next-app completed but the project was not created. This can happen with nested npx calls."
25651
25744
  );
25652
25745
  const manualCmd = `npx create-next-app@latest ${projectPrompt.projectName} --typescript --tailwind --app`;
25653
- p17.log.info(
25746
+ p18.log.info(
25654
25747
  `Create the project manually:
25655
25748
  ${pc7.cyan(manualCmd)}
25656
25749
  Then run ${pc7.cyan("betterstart init")} inside it.`
@@ -25669,7 +25762,7 @@ async function runInitCommand(name, options) {
25669
25762
  if (options.yes) {
25670
25763
  if (options.databaseUrl) {
25671
25764
  if (!isValidDbUrl(options.databaseUrl)) {
25672
- p17.log.error(
25765
+ p18.log.error(
25673
25766
  `Invalid database URL. Must start with ${pc7.cyan("postgres://")} or ${pc7.cyan("postgresql://")}`
25674
25767
  );
25675
25768
  process.exit(1);
@@ -25689,24 +25782,24 @@ async function runInitCommand(name, options) {
25689
25782
  databaseUrl = flow.databaseUrl;
25690
25783
  persistDatabaseUrl(cwd, databaseUrl);
25691
25784
  } else if (flow.ok) {
25692
- p17.log.warn("Created a Neon database, but DATABASE_URL could not be retrieved from Vercel.");
25785
+ p18.log.warn("Created a Neon database, but DATABASE_URL could not be retrieved from Vercel.");
25693
25786
  const resourceUrl = flow.dashboardUrl ?? flow.resourceUrl;
25694
25787
  if (resourceUrl) {
25695
- p17.log.info(
25788
+ p18.log.info(
25696
25789
  `Open ${pc7.cyan(resourceUrl)} to copy DATABASE_URL, then rerun ${pc7.cyan("betterstart init --database-url <url> --yes")}.`
25697
25790
  );
25698
25791
  } else {
25699
- p17.log.info(
25792
+ p18.log.info(
25700
25793
  `Rerun ${pc7.cyan("betterstart init --database-url <url> --yes")} with a Postgres connection string.`
25701
25794
  );
25702
25795
  }
25703
25796
  } else {
25704
- p17.log.warn("Vercel provisioning did not complete; continuing without a database URL.");
25797
+ p18.log.warn("Vercel provisioning did not complete; continuing without a database URL.");
25705
25798
  }
25706
25799
  }
25707
25800
  } else if (existingDbUrl) {
25708
25801
  const masked = maskDbUrl(existingDbUrl);
25709
- p17.log.info(`Using existing database URL from .env.local ${pc7.dim(`(${masked})`)}`);
25802
+ p18.log.info(`Using existing database URL from .env.local ${pc7.dim(`(${masked})`)}`);
25710
25803
  databaseUrl = existingDbUrl;
25711
25804
  } else {
25712
25805
  const databaseStep = deferNextPromptCheck();
@@ -25732,7 +25825,7 @@ async function runInitCommand(name, options) {
25732
25825
  persistDatabaseUrl(cwd, databaseUrl);
25733
25826
  } else {
25734
25827
  databaseStep.skip();
25735
- p17.log.info("Falling back to a manual database connection string.");
25828
+ p18.log.info("Falling back to a manual database connection string.");
25736
25829
  openBrowserVercelNeon();
25737
25830
  databaseUrl = await promptConnectionString();
25738
25831
  }
@@ -25774,21 +25867,20 @@ async function runInitCommand(name, options) {
25774
25867
  });
25775
25868
  if (flow.ok && flow.token) {
25776
25869
  persistBlobReadWriteToken(cwd, flow.token);
25777
- p17.log.success(`Saved BLOB_READ_WRITE_TOKEN to ${pc7.cyan(".env.local")}`);
25870
+ p18.log.success(`Saved BLOB_READ_WRITE_TOKEN to ${pc7.cyan(".env.local")}`);
25778
25871
  collectedIntegrationConfig.sections.push({
25779
25872
  header: "Storage (Vercel Blob)",
25780
25873
  vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: flow.token }]
25781
25874
  });
25782
25875
  collectedIntegrationConfig.overwriteKeys.add("BLOB_READ_WRITE_TOKEN");
25783
- } else if (!options.yes) {
25784
- p17.log.info("Falling back to a manual Vercel Blob token.");
25785
- const collected = await collectIntegrationConfig(cwd, ["vercel-blob"]);
25786
- collectedIntegrationConfig.sections.push(...collected.sections);
25787
- for (const key of collected.overwriteKeys) {
25788
- collectedIntegrationConfig.overwriteKeys.add(key);
25789
- }
25790
25876
  } else {
25791
- p17.log.warn("Vercel Blob provisioning did not complete; continuing without a Blob token.");
25877
+ p18.log.warn(
25878
+ "Continuing without a Blob token \u2014 add BLOB_READ_WRITE_TOKEN to .env.local from the store on vercel.com/dashboard/stores."
25879
+ );
25880
+ collectedIntegrationConfig.sections.push({
25881
+ header: "Storage (Vercel Blob)",
25882
+ vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: "" }]
25883
+ });
25792
25884
  }
25793
25885
  }
25794
25886
  } else if (options.yes) {
@@ -25891,7 +25983,7 @@ async function runInitCommand(name, options) {
25891
25983
  });
25892
25984
  s.stop("");
25893
25985
  process.stdout.write("\x1B[2A\x1B[J");
25894
- p17.note(noteLines.join("\n"), "Scaffolded admin");
25986
+ p18.note(noteLines.join("\n"), "Scaffolded admin");
25895
25987
  const drizzleConfigPath = path50.join(cwd, "drizzle.config.ts");
25896
25988
  if (!dbFiles.includes("drizzle.config.ts") && fs39.existsSync(drizzleConfigPath)) {
25897
25989
  if (forceMode) {
@@ -25901,20 +25993,20 @@ async function runInitCommand(name, options) {
25901
25993
  readNamespacedTemplate("drizzle.config.ts", namespace),
25902
25994
  "utf-8"
25903
25995
  );
25904
- p17.log.success("Updated drizzle.config.ts");
25996
+ p18.log.success("Updated drizzle.config.ts");
25905
25997
  } else if (!options.yes) {
25906
- const overwrite = await p17.confirm({
25998
+ const overwrite = await p18.confirm({
25907
25999
  message: "drizzle.config.ts already exists. Overwrite with latest version?",
25908
26000
  initialValue: true
25909
26001
  });
25910
- if (!p17.isCancel(overwrite) && overwrite) {
26002
+ if (!p18.isCancel(overwrite) && overwrite) {
25911
26003
  const { readNamespacedTemplate } = await import("./template-reader-X6KFP7B3.js");
25912
26004
  fs39.writeFileSync(
25913
26005
  drizzleConfigPath,
25914
26006
  readNamespacedTemplate("drizzle.config.ts", namespace),
25915
26007
  "utf-8"
25916
26008
  );
25917
- p17.log.success("Updated drizzle.config.ts");
26009
+ p18.log.success("Updated drizzle.config.ts");
25918
26010
  }
25919
26011
  }
25920
26012
  }
@@ -25940,8 +26032,8 @@ async function runInitCommand(name, options) {
25940
26032
  depsInstalled = true;
25941
26033
  } else {
25942
26034
  s.stop("Failed to install dependencies");
25943
- p17.log.warning(depsResult.error ?? "Unknown error");
25944
- p17.log.info(
26035
+ p18.log.warning(depsResult.error ?? "Unknown error");
26036
+ p18.log.info(
25945
26037
  `You can install them manually:
25946
26038
  ${pc7.cyan(`${pm} add ${depsResult.dependencies.join(" ")}`)}
25947
26039
  ${pc7.cyan(`${pm} add -D ${depsResult.devDeps.join(" ")}`)}`
@@ -26031,10 +26123,10 @@ async function runInitCommand(name, options) {
26031
26123
  ` ${pc7.yellow("\u25B2")} Local filesystem storage is only for development or self-hosted persistent-disk deployments. Use Vercel Blob or Cloudflare R2 for hosted/serverless production.`
26032
26124
  );
26033
26125
  }
26034
- p17.note(installLines.join("\n"), "Installed");
26126
+ p18.note(installLines.join("\n"), "Installed");
26035
26127
  let dbPushed = false;
26036
26128
  if (depsResult.success && options.skipMigration && hasDbUrl(cwd)) {
26037
- p17.log.info(`Skipping database schema push ${pc7.dim("(--skip-migration)")}`);
26129
+ p18.log.info(`Skipping database schema push ${pc7.dim("(--skip-migration)")}`);
26038
26130
  } else if (depsResult.success && hasDbUrl(cwd)) {
26039
26131
  let driverReady = hasDrizzleKitPostgresDriverDependency(cwd);
26040
26132
  if (!driverReady) {
@@ -26049,8 +26141,8 @@ async function runInitCommand(name, options) {
26049
26141
  });
26050
26142
  if (!driverResult.success) {
26051
26143
  s.stop("Database push failed");
26052
- p17.log.warning(driverResult.error ?? `Failed to install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP}`);
26053
- p17.log.info(
26144
+ p18.log.warning(driverResult.error ?? `Failed to install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP}`);
26145
+ p18.log.info(
26054
26146
  `Install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP} manually, then run: ${pc7.cyan(drizzlePushCommand(pm))}`
26055
26147
  );
26056
26148
  } else {
@@ -26061,7 +26153,7 @@ async function runInitCommand(name, options) {
26061
26153
  if (driverReady) {
26062
26154
  const useTerminalForDbPush = shouldUseTerminalForDbPush(options);
26063
26155
  if (useTerminalForDbPush) {
26064
- p17.log.info(`Running ${pc7.cyan("drizzle-kit push --force")}`);
26156
+ p18.log.info(`Running ${pc7.cyan("drizzle-kit push --force")}`);
26065
26157
  } else {
26066
26158
  s.start("Pushing database schema (drizzle-kit push)");
26067
26159
  }
@@ -26070,13 +26162,13 @@ async function runInitCommand(name, options) {
26070
26162
  if (useTerminalForDbPush) {
26071
26163
  const verification = await verifyDatabaseReachable(cwd);
26072
26164
  if (!verification.success) {
26073
- p17.log.warning(verification.error);
26074
- p17.log.error("Database was not reachable. Aborting setup.");
26165
+ p18.log.warning(verification.error);
26166
+ p18.log.error("Database was not reachable. Aborting setup.");
26075
26167
  process.exit(1);
26076
26168
  }
26077
26169
  }
26078
26170
  if (useTerminalForDbPush) {
26079
- p17.log.success("Database schema pushed");
26171
+ p18.log.success("Database schema pushed");
26080
26172
  } else {
26081
26173
  s.stop(`${pc7.green("\u2713")} Database schema pushed`);
26082
26174
  }
@@ -26086,12 +26178,12 @@ async function runInitCommand(name, options) {
26086
26178
  s.stop("Database push failed");
26087
26179
  }
26088
26180
  const pushError = pushResult.error ?? "Unknown error";
26089
- p17.log.warning(pushError);
26181
+ p18.log.warning(pushError);
26090
26182
  if (isDatabaseReachabilityError(pushError)) {
26091
- p17.log.error("Database was not reachable. Aborting setup.");
26183
+ p18.log.error("Database was not reachable. Aborting setup.");
26092
26184
  process.exit(1);
26093
26185
  }
26094
- p17.log.info(`You can run it manually: ${pc7.cyan(drizzlePushCommand(pm))}`);
26186
+ p18.log.info(`You can run it manually: ${pc7.cyan(drizzlePushCommand(pm))}`);
26095
26187
  }
26096
26188
  }
26097
26189
  }
@@ -26100,7 +26192,7 @@ async function runInitCommand(name, options) {
26100
26192
  let seedSuccess = false;
26101
26193
  let adminAccountReady = false;
26102
26194
  if (dbPushed && options.skipAdminCreation) {
26103
- p17.log.info(
26195
+ p18.log.info(
26104
26196
  `Skipping admin user creation ${pc7.dim(`(use ${betterstartExecCommand(pm, "seed")} later)`)}`
26105
26197
  );
26106
26198
  }
@@ -26110,14 +26202,14 @@ async function runInitCommand(name, options) {
26110
26202
  let replaceExistingAdmin = false;
26111
26203
  const adminCheck = await checkExistingAdmin(cwd, adminDir, authBasePath);
26112
26204
  if (adminCheck.error) {
26113
- p17.log.warning(`Could not verify existing admin account ${pc7.dim(`(${adminCheck.error})`)}`);
26205
+ p18.log.warning(`Could not verify existing admin account ${pc7.dim(`(${adminCheck.error})`)}`);
26114
26206
  if (isDatabaseReachabilityError(adminCheck.error)) {
26115
- p17.log.error("Database was not reachable. Aborting setup.");
26207
+ p18.log.error("Database was not reachable. Aborting setup.");
26116
26208
  process.exit(1);
26117
26209
  }
26118
26210
  } else if (adminCheck.existingAdmin) {
26119
26211
  const existingAdminLabel = formatAdminIdentity(adminCheck.existingAdmin);
26120
- const adminAction = await p17.select({
26212
+ const adminAction = await p18.select({
26121
26213
  message: "Found an already existing admin account. Do you want to replace it or skip?",
26122
26214
  options: [
26123
26215
  {
@@ -26131,34 +26223,34 @@ async function runInitCommand(name, options) {
26131
26223
  }
26132
26224
  ]
26133
26225
  });
26134
- if (p17.isCancel(adminAction)) {
26135
- p17.cancel("Setup cancelled.");
26226
+ if (p18.isCancel(adminAction)) {
26227
+ p18.cancel("Setup cancelled.");
26136
26228
  process.exit(0);
26137
26229
  }
26138
26230
  if (adminAction === "skip") {
26139
26231
  adminAccountReady = true;
26140
- p17.log.info(`Keeping existing admin account ${pc7.dim(`(${existingAdminLabel})`)}`);
26232
+ p18.log.info(`Keeping existing admin account ${pc7.dim(`(${existingAdminLabel})`)}`);
26141
26233
  } else {
26142
26234
  replaceExistingAdmin = true;
26143
26235
  }
26144
26236
  }
26145
26237
  if (!adminAccountReady) {
26146
- p17.note(
26238
+ p18.note(
26147
26239
  pc7.dim(
26148
26240
  replaceExistingAdmin ? "Replace the existing admin account to access the admin." : "Create your first admin user to access the dashboard."
26149
26241
  ),
26150
26242
  "Admin account"
26151
26243
  );
26152
- const credentials = await p17.group(
26244
+ const credentials = await p18.group(
26153
26245
  {
26154
- email: () => p17.text({
26246
+ email: () => p18.text({
26155
26247
  message: "Admin email",
26156
26248
  placeholder: "admin@example.com",
26157
26249
  validate: (v) => {
26158
26250
  if (!v || !v.includes("@")) return "Please enter a valid email";
26159
26251
  }
26160
26252
  }),
26161
- password: () => p17.password({
26253
+ password: () => p18.password({
26162
26254
  message: "Admin password",
26163
26255
  validate: (v) => {
26164
26256
  if (!v || v.length < 8) return "Password must be at least 8 characters";
@@ -26167,7 +26259,7 @@ async function runInitCommand(name, options) {
26167
26259
  },
26168
26260
  {
26169
26261
  onCancel: () => {
26170
- p17.cancel("Setup cancelled.");
26262
+ p18.cancel("Setup cancelled.");
26171
26263
  process.exit(0);
26172
26264
  }
26173
26265
  }
@@ -26186,11 +26278,11 @@ async function runInitCommand(name, options) {
26186
26278
  );
26187
26279
  if (seedResult.existingUser) {
26188
26280
  s.stop(`${pc7.yellow("\u25B2")} An account already exists for ${credentials.email}`);
26189
- const replace = await p17.confirm({
26281
+ const replace = await p18.confirm({
26190
26282
  message: "Replace the existing account with this email?",
26191
26283
  initialValue: false
26192
26284
  });
26193
- if (!p17.isCancel(replace) && replace) {
26285
+ if (!p18.isCancel(replace) && replace) {
26194
26286
  seedOverwriteMode = "email";
26195
26287
  s.start("Replacing admin user");
26196
26288
  seedResult = await runSeed(
@@ -26211,14 +26303,14 @@ async function runInitCommand(name, options) {
26211
26303
  adminAccountReady = true;
26212
26304
  } else if (seedResult.error) {
26213
26305
  s.stop(`${pc7.red("\u2717")} Failed to create admin user`);
26214
- p17.note(
26306
+ p18.note(
26215
26307
  `${pc7.red(seedResult.error)}
26216
26308
 
26217
26309
  Run manually: ${pc7.cyan(betterstartExecCommand(pm, "seed"))}`,
26218
26310
  pc7.red("Seed failed")
26219
26311
  );
26220
26312
  if (isDatabaseReachabilityError(seedResult.error)) {
26221
- p17.log.error("Database was not reachable. Aborting setup.");
26313
+ p18.log.error("Database was not reachable. Aborting setup.");
26222
26314
  process.exit(1);
26223
26315
  }
26224
26316
  }
@@ -26250,16 +26342,16 @@ Run manually: ${pc7.cyan(betterstartExecCommand(pm, "seed"))}`,
26250
26342
  `Files created: ${pc7.cyan(String(totalFiles))}`,
26251
26343
  `Env vars: ${envResult.added.length} added, ${envResult.skipped.length} skipped`
26252
26344
  ];
26253
- p17.note(summaryLines.join("\n"), "Admin scaffolded successfully");
26345
+ p18.note(summaryLines.join("\n"), "Admin scaffolded successfully");
26254
26346
  if (options.vercel !== false && !options.skipDeploy) {
26255
26347
  const linkedVercelProject = Boolean(readLinkedProjectId(cwd));
26256
26348
  if (!options.yes) {
26257
26349
  const deployStep = deferNextPromptCheck();
26258
- const deployNow = await p17.confirm({
26350
+ const deployNow = await p18.confirm({
26259
26351
  message: "Deploy to Vercel now?",
26260
26352
  initialValue: linkedVercelProject
26261
26353
  });
26262
- if (!p17.isCancel(deployNow) && deployNow) {
26354
+ if (!p18.isCancel(deployNow) && deployNow) {
26263
26355
  const deployFlow = await runVercelDeployFlow({ cwd, projectName, env: process.env });
26264
26356
  if (deployFlow.ok && deployFlow.cleanScreen) deployStep.confirm();
26265
26357
  else deployStep.skip();
@@ -26273,17 +26365,18 @@ Run manually: ${pc7.cyan(betterstartExecCommand(pm, "seed"))}`,
26273
26365
  env: process.env
26274
26366
  });
26275
26367
  if (!deployFlow.ok) {
26276
- p17.log.warn("Vercel deploy did not complete; continuing.");
26368
+ p18.log.warn("Vercel deploy did not complete; continuing.");
26277
26369
  }
26278
26370
  }
26279
26371
  }
26280
26372
  if (!options.yes && !options.skipDevServerStart) {
26281
26373
  const devCmd = runCommand(pm, "dev");
26282
- const startDev = await p17.confirm({
26374
+ const startDev = await p18.confirm({
26283
26375
  message: "Start the development server?",
26284
26376
  initialValue: true
26285
26377
  });
26286
- if (!p17.isCancel(startDev) && startDev) {
26378
+ if (!p18.isCancel(startDev) && startDev) {
26379
+ disposeCancelGuard();
26287
26380
  await startManagedDevServer(cwd, devCmd, adminLoginUrl, {
26288
26381
  email: seedSuccess && seedEmail ? seedEmail : void 0,
26289
26382
  password: seedSuccess && seedPassword ? seedPassword : void 0
@@ -26291,7 +26384,8 @@ Run manually: ${pc7.cyan(betterstartExecCommand(pm, "seed"))}`,
26291
26384
  return;
26292
26385
  }
26293
26386
  }
26294
- p17.outro("Done!");
26387
+ disposeCancelGuard();
26388
+ p18.outro("Done!");
26295
26389
  }
26296
26390
  function isValidDbUrl(url) {
26297
26391
  return url.startsWith("postgres://") || url.startsWith("postgresql://");
@@ -26678,7 +26772,7 @@ function printAdminReadyNote(state) {
26678
26772
  lines.unshift(`Password: ${pc7.cyan(state.adminPassword)}`);
26679
26773
  lines.unshift(`Admin user: ${pc7.cyan(state.adminEmail)}`);
26680
26774
  }
26681
- p17.note(lines.join("\n"), "Admin ready");
26775
+ p18.note(lines.join("\n"), "Admin ready");
26682
26776
  }
26683
26777
  function shouldSuppressDevServerStartupLine(line) {
26684
26778
  const plain = stripAnsi2(line).trim();
@@ -26790,7 +26884,7 @@ function startManagedDevServer(cwd, devCmd, adminLoginUrl, adminCredentials) {
26790
26884
 
26791
26885
  // adapters/next/commands/list-integrations.ts
26792
26886
  import path51 from "path";
26793
- import * as p18 from "@clack/prompts";
26887
+ import * as p19 from "@clack/prompts";
26794
26888
  async function runListIntegrationsCommand(options) {
26795
26889
  const cwd = options.cwd ? path51.resolve(options.cwd) : process.cwd();
26796
26890
  const config = await resolveConfig(cwd);
@@ -26799,15 +26893,15 @@ async function runListIntegrationsCommand(options) {
26799
26893
  const status = installedIntegrations.has(integration.id) ? "installed" : "available";
26800
26894
  return `${integration.id.padEnd(12)} ${status.padEnd(10)} ${integration.kind.padEnd(8)} ${integration.description}`;
26801
26895
  });
26802
- p18.note(lines.join("\n"), "BetterStart integrations");
26803
- p18.outro(
26896
+ p19.note(lines.join("\n"), "BetterStart integrations");
26897
+ p19.outro(
26804
26898
  `${installedIntegrations.size} installed, ${lines.length - installedIntegrations.size} available`
26805
26899
  );
26806
26900
  }
26807
26901
 
26808
26902
  // adapters/next/commands/list-plugins.ts
26809
26903
  import path52 from "path";
26810
- import * as p19 from "@clack/prompts";
26904
+ import * as p20 from "@clack/prompts";
26811
26905
  async function runListPluginsCommand(options) {
26812
26906
  const cwd = options.cwd ? path52.resolve(options.cwd) : process.cwd();
26813
26907
  const config = await resolveConfig(cwd);
@@ -26816,34 +26910,34 @@ async function runListPluginsCommand(options) {
26816
26910
  const status = installedPlugins.has(plugin.id) ? "installed" : "available";
26817
26911
  return `${plugin.id.padEnd(10)} ${status.padEnd(10)} ${plugin.kind.padEnd(12)} ${plugin.description}`;
26818
26912
  });
26819
- p19.note(lines.join("\n"), "BetterStart plugins");
26820
- p19.outro(`${installedPlugins.size} installed, ${lines.length - installedPlugins.size} available`);
26913
+ p20.note(lines.join("\n"), "BetterStart plugins");
26914
+ p20.outro(`${installedPlugins.size} installed, ${lines.length - installedPlugins.size} available`);
26821
26915
  }
26822
26916
 
26823
26917
  // adapters/next/commands/remove.ts
26824
26918
  import path53 from "path";
26825
- import * as p20 from "@clack/prompts";
26919
+ import * as p21 from "@clack/prompts";
26826
26920
  async function runRemoveCommand(items, options) {
26827
26921
  const removeIntegrationsMode = Boolean(options.integration);
26828
26922
  if (!removeIntegrationsMode && items.includes("core")) {
26829
- p20.log.error("The core Admin cannot be removed.");
26923
+ p21.log.error("The core Admin cannot be removed.");
26830
26924
  process.exit(1);
26831
26925
  }
26832
26926
  const pluginIds = items.filter(isPluginId);
26833
26927
  const integrationIds = items.filter(isIntegrationId);
26834
26928
  if (!removeIntegrationsMode && integrationIds.length > 0) {
26835
- p20.log.error(
26929
+ p21.log.error(
26836
26930
  `Integration IDs require --integration. Run \`betterstart remove --integration ${integrationIds.join(" ")}\`.`
26837
26931
  );
26838
26932
  process.exit(1);
26839
26933
  }
26840
26934
  if (removeIntegrationsMode && pluginIds.length > 0) {
26841
- p20.log.error(`Plugin IDs cannot be removed with --integration: ${pluginIds.join(", ")}`);
26935
+ p21.log.error(`Plugin IDs cannot be removed with --integration: ${pluginIds.join(", ")}`);
26842
26936
  process.exit(1);
26843
26937
  }
26844
26938
  const invalidItems = removeIntegrationsMode ? items.filter((integrationId) => !isIntegrationId(integrationId)) : items.filter((pluginId) => !isPluginId(pluginId));
26845
26939
  if (invalidItems.length > 0) {
26846
- p20.log.error(
26940
+ p21.log.error(
26847
26941
  removeIntegrationsMode ? formatUnknownIntegrationMessage(invalidItems) : formatUnknownPluginMessage(invalidItems)
26848
26942
  );
26849
26943
  process.exit(1);
@@ -26852,12 +26946,12 @@ async function runRemoveCommand(items, options) {
26852
26946
  const config = await resolveConfig(cwd);
26853
26947
  const pm = detectPackageManager(cwd);
26854
26948
  if (!options.force) {
26855
- const confirmed = await p20.confirm({
26949
+ const confirmed = await p21.confirm({
26856
26950
  message: `Remove ${removeIntegrationsMode ? "integration" : "plugin"}${items.length === 1 ? "" : "s"} ${items.join(", ")}?`,
26857
26951
  initialValue: false
26858
26952
  });
26859
- if (p20.isCancel(confirmed) || !confirmed) {
26860
- p20.cancel(`${removeIntegrationsMode ? "Integration" : "Plugin"} removal cancelled.`);
26953
+ if (p21.isCancel(confirmed) || !confirmed) {
26954
+ p21.cancel(`${removeIntegrationsMode ? "Integration" : "Plugin"} removal cancelled.`);
26861
26955
  process.exit(0);
26862
26956
  }
26863
26957
  }
@@ -26870,13 +26964,13 @@ async function runRemoveCommand(items, options) {
26870
26964
  });
26871
26965
  writeConfigFile(cwd, result2.config);
26872
26966
  if (result2.removed.length === 0) {
26873
- p20.outro("No integrations were removed.");
26967
+ p21.outro("No integrations were removed.");
26874
26968
  return;
26875
26969
  }
26876
26970
  if (result2.warnings.length > 0) {
26877
- p20.note(result2.warnings.join("\n"), "Warnings");
26971
+ p21.note(result2.warnings.join("\n"), "Warnings");
26878
26972
  }
26879
- p20.outro(
26973
+ p21.outro(
26880
26974
  `Removed integration${result2.removed.length === 1 ? "" : "s"}: ${result2.removed.join(", ")}`
26881
26975
  );
26882
26976
  return;
@@ -26889,13 +26983,13 @@ async function runRemoveCommand(items, options) {
26889
26983
  });
26890
26984
  writeConfigFile(cwd, result.config);
26891
26985
  if (result.removed.length === 0) {
26892
- p20.outro("No plugins were removed.");
26986
+ p21.outro("No plugins were removed.");
26893
26987
  return;
26894
26988
  }
26895
26989
  if (result.warnings.length > 0) {
26896
- p20.note(result.warnings.join("\n"), "Warnings");
26990
+ p21.note(result.warnings.join("\n"), "Warnings");
26897
26991
  }
26898
- p20.outro(`Removed plugin${result.removed.length === 1 ? "" : "s"}: ${result.removed.join(", ")}`);
26992
+ p21.outro(`Removed plugin${result.removed.length === 1 ? "" : "s"}: ${result.removed.join(", ")}`);
26899
26993
  }
26900
26994
 
26901
26995
  // adapters/next/commands/remove-schema.ts
@@ -27045,7 +27139,7 @@ async function runRemoveSchemaCommand(schemaName, options) {
27045
27139
  // adapters/next/commands/uninstall.ts
27046
27140
  import fs42 from "fs";
27047
27141
  import path55 from "path";
27048
- import * as p21 from "@clack/prompts";
27142
+ import * as p22 from "@clack/prompts";
27049
27143
  import pc8 from "picocolors";
27050
27144
 
27051
27145
  // adapters/next/commands/uninstall-cleaners.ts
@@ -27267,8 +27361,8 @@ function buildUninstallPlan(cwd, namespaceValue) {
27267
27361
  count: configFiles.length,
27268
27362
  unit: configFiles.length === 1 ? "file" : "files",
27269
27363
  execute() {
27270
- for (const p22 of configPaths) {
27271
- if (fs42.existsSync(p22)) fs42.unlinkSync(p22);
27364
+ for (const p23 of configPaths) {
27365
+ if (fs42.existsSync(p23)) fs42.unlinkSync(p23);
27272
27366
  }
27273
27367
  }
27274
27368
  });
@@ -27332,7 +27426,7 @@ function buildUninstallPlan(cwd, namespaceValue) {
27332
27426
  }
27333
27427
  async function runUninstallCommand(options) {
27334
27428
  const cwd = options.cwd ? path55.resolve(options.cwd) : process.cwd();
27335
- p21.intro(pc8.bgRed(pc8.white(" BetterStart Uninstall ")));
27429
+ p22.intro(pc8.bgRed(pc8.white(" BetterStart Uninstall ")));
27336
27430
  let namespace = DEFAULT_ADMIN_NAMESPACE;
27337
27431
  try {
27338
27432
  const config = await resolveConfig(cwd);
@@ -27341,8 +27435,8 @@ async function runUninstallCommand(options) {
27341
27435
  }
27342
27436
  const steps = buildUninstallPlan(cwd, namespace);
27343
27437
  if (steps.length === 0) {
27344
- p21.log.success(`${pc8.green("\u2713")} Nothing to remove \u2014 project is already clean.`);
27345
- p21.outro("Done");
27438
+ p22.log.success(`${pc8.green("\u2713")} Nothing to remove \u2014 project is already clean.`);
27439
+ p22.outro("Done");
27346
27440
  return;
27347
27441
  }
27348
27442
  const planLines = steps.map((step) => {
@@ -27350,14 +27444,14 @@ async function runUninstallCommand(options) {
27350
27444
  const countLabel = pc8.dim(`${step.count} ${step.unit}`);
27351
27445
  return `${pc8.red("\xD7")} ${names} ${countLabel}`;
27352
27446
  });
27353
- p21.note(planLines.join("\n"), "Uninstall plan");
27447
+ p22.note(planLines.join("\n"), "Uninstall plan");
27354
27448
  if (!options.force) {
27355
- const confirmed = await p21.confirm({
27449
+ const confirmed = await p22.confirm({
27356
27450
  message: "Proceed with uninstall?",
27357
27451
  initialValue: false
27358
27452
  });
27359
- if (p21.isCancel(confirmed) || !confirmed) {
27360
- p21.cancel("Uninstall cancelled.");
27453
+ if (p22.isCancel(confirmed) || !confirmed) {
27454
+ p22.cancel("Uninstall cancelled.");
27361
27455
  process.exit(0);
27362
27456
  }
27363
27457
  }
@@ -27369,8 +27463,8 @@ async function runUninstallCommand(options) {
27369
27463
  }
27370
27464
  const parts = steps.map((step) => `${step.count} ${step.unit}`);
27371
27465
  s.stop(`Removed ${parts.join(", ")}`);
27372
- p21.note(pc8.dim("Database tables were NOT dropped \u2014 drop them manually if needed."), "Next steps");
27373
- p21.outro("Uninstall complete");
27466
+ p22.note(pc8.dim("Database tables were NOT dropped \u2014 drop them manually if needed."), "Next steps");
27467
+ p22.outro("Uninstall complete");
27374
27468
  }
27375
27469
 
27376
27470
  // adapters/next/commands/update-component.ts