betterstart-cli 0.0.37 → 0.0.39

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,6 @@ 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");
22195
22220
  sections.push({
22196
22221
  header: "Storage (Vercel Blob)",
22197
22222
  vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: flow.token }]
@@ -22199,20 +22224,26 @@ async function promptPlugins(cwd, options = {}) {
22199
22224
  overwriteKeys.add("BLOB_READ_WRITE_TOKEN");
22200
22225
  if (flow.usedTerminalFallback) storageStep.skip();
22201
22226
  else storageStep.confirm();
22227
+ } else if (flow) {
22228
+ p12.log.warn(
22229
+ "Continuing without a Blob token \u2014 add BLOB_READ_WRITE_TOKEN to .env.local from the store on vercel.com/dashboard/stores."
22230
+ );
22231
+ sections.push({
22232
+ header: "Storage (Vercel Blob)",
22233
+ vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: "" }]
22234
+ });
22235
+ storageStep.skip();
22202
22236
  } else {
22203
22237
  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.");
22238
+ p12.log.info("Using the existing Vercel Blob token from .env.local");
22207
22239
  }
22208
22240
  mergeIntegrationConfig(await collectIntegrationConfig(cwd, ["vercel-blob"]));
22209
- if (flow) storageStep.skip();
22210
- else storageStep.confirm();
22241
+ storageStep.confirm();
22211
22242
  }
22212
22243
  } else {
22213
22244
  storageStep.confirm();
22214
22245
  }
22215
- const selectedPlugins = await p11.multiselect({
22246
+ const selectedPlugins = await p12.multiselect({
22216
22247
  message: "Select presets",
22217
22248
  options: [
22218
22249
  {
@@ -22224,12 +22255,12 @@ async function promptPlugins(cwd, options = {}) {
22224
22255
  required: false,
22225
22256
  initialValues: ["blog"]
22226
22257
  });
22227
- if (p11.isCancel(selectedPlugins)) {
22228
- p11.cancel("Setup cancelled.");
22258
+ if (p12.isCancel(selectedPlugins)) {
22259
+ p12.cancel("Setup cancelled.");
22229
22260
  process.exit(0);
22230
22261
  }
22231
22262
  const integrations = [];
22232
- const setupResend = await p11.confirm({
22263
+ const setupResend = await p12.confirm({
22233
22264
  message: `Do you want to setup resend.com emails?
22234
22265
  ${styleText(
22235
22266
  "dim",
@@ -22237,8 +22268,8 @@ ${styleText(
22237
22268
  )}`,
22238
22269
  initialValue: true
22239
22270
  });
22240
- if (p11.isCancel(setupResend)) {
22241
- p11.cancel("Setup cancelled.");
22271
+ if (p12.isCancel(setupResend)) {
22272
+ p12.cancel("Setup cancelled.");
22242
22273
  process.exit(0);
22243
22274
  }
22244
22275
  if (setupResend) {
@@ -22267,10 +22298,10 @@ ${styleText(
22267
22298
  }
22268
22299
 
22269
22300
  // adapters/next/init/prompts/project.ts
22270
- import * as p12 from "@clack/prompts";
22301
+ import * as p13 from "@clack/prompts";
22271
22302
  import pc2 from "picocolors";
22272
22303
  async function promptProject(defaultName) {
22273
- const projectName = await p12.text({
22304
+ const projectName = await p13.text({
22274
22305
  message: [
22275
22306
  "What is your project name?",
22276
22307
  `${pc2.cyan("\u2502")} ${pc2.dim("If you don't want a directory created with the project name, just press `")}${pc2.bold(
@@ -22289,16 +22320,16 @@ async function promptProject(defaultName) {
22289
22320
  return void 0;
22290
22321
  }
22291
22322
  });
22292
- if (p12.isCancel(projectName)) {
22293
- p12.cancel("Setup cancelled.");
22323
+ if (p13.isCancel(projectName)) {
22324
+ p13.cancel("Setup cancelled.");
22294
22325
  process.exit(0);
22295
22326
  }
22296
- const useSrcDir = await p12.confirm({
22327
+ const useSrcDir = await p13.confirm({
22297
22328
  message: "Use src/ directory?",
22298
22329
  initialValue: false
22299
22330
  });
22300
- if (p12.isCancel(useSrcDir)) {
22301
- p12.cancel("Setup cancelled.");
22331
+ if (p13.isCancel(useSrcDir)) {
22332
+ p13.cancel("Setup cancelled.");
22302
22333
  process.exit(0);
22303
22334
  }
22304
22335
  return { projectName: projectName.trim(), useSrcDir };
@@ -24144,11 +24175,11 @@ function scaffoldTsconfig(cwd, config) {
24144
24175
  }
24145
24176
 
24146
24177
  // adapters/next/init/vercel/flow.ts
24147
- import * as p16 from "@clack/prompts";
24178
+ import * as p17 from "@clack/prompts";
24148
24179
  import pc6 from "picocolors";
24149
24180
 
24150
24181
  // adapters/next/init/vercel/auth.ts
24151
- import * as p13 from "@clack/prompts";
24182
+ import * as p14 from "@clack/prompts";
24152
24183
  import pc3 from "picocolors";
24153
24184
 
24154
24185
  // adapters/next/init/vercel/runner.ts
@@ -24356,7 +24387,7 @@ async function ensureVercelAuth(runner, cwd, options) {
24356
24387
  checkSpinner.clear();
24357
24388
  const signInMessage = `Sign in to Vercel to continue ${pc3.dim("(or press Ctrl-C to enter a connection string manually)")}`;
24358
24389
  const signInRows = clackLogRows(signInMessage);
24359
- p13.log.info(signInMessage);
24390
+ p14.log.info(signInMessage);
24360
24391
  let streamedRows = 0;
24361
24392
  const login = await runVercel(runner, ["login"], {
24362
24393
  cwd,
@@ -24388,7 +24419,7 @@ function signedInMessage(username) {
24388
24419
  }
24389
24420
 
24390
24421
  // adapters/next/init/vercel/blob.ts
24391
- import * as p14 from "@clack/prompts";
24422
+ import * as p15 from "@clack/prompts";
24392
24423
  import pc4 from "picocolors";
24393
24424
 
24394
24425
  // adapters/next/init/vercel/env-pull.ts
@@ -24499,21 +24530,46 @@ function readLinkedProjectJson(cwd) {
24499
24530
  // adapters/next/init/vercel/blob.ts
24500
24531
  var PROVISION_TIMEOUT_MS = 18e4;
24501
24532
  var INTERACTIVE_PROVISION_TIMEOUT_MS = 6e5;
24502
- var MAX_STORE_NAME_ATTEMPTS = 5;
24533
+ var MAX_STORE_NAME_ATTEMPTS = 10;
24503
24534
  function blobStoreName(projectName) {
24504
24535
  return sanitizeVercelProjectName(`${projectName}-blob`);
24505
24536
  }
24506
24537
  function isNameTaken(result) {
24507
- return /already exists|already taken|already in use|name.*taken/i.test(
24538
+ return /already exists|already taken|already in use|name.*taken|conflict|\b409\b/i.test(
24508
24539
  `${result.stdout}
24509
24540
  ${result.stderr}`
24510
24541
  );
24511
24542
  }
24543
+ function candidateStoreName(baseName, attempt) {
24544
+ if (attempt < MAX_STORE_NAME_ATTEMPTS) return versionedVercelProjectName(baseName, attempt);
24545
+ const suffix = `-${Date.now().toString(36)}`;
24546
+ return `${baseName.slice(0, 100 - suffix.length)}${suffix}`;
24547
+ }
24548
+ function isEnvVarConflict(result) {
24549
+ return /already has an existing environment variable/i.test(`${result.stdout}
24550
+ ${result.stderr}`);
24551
+ }
24552
+ function createdStoreId(result) {
24553
+ return /Blob store created:[^(]*\((store_[A-Za-z0-9]+)\)/.exec(
24554
+ `${result.stdout}
24555
+ ${result.stderr}`
24556
+ )?.[1];
24557
+ }
24558
+ async function deleteOrphanStore(runner, cwd, createResult, env) {
24559
+ const storeId = createdStoreId(createResult);
24560
+ if (!storeId) return;
24561
+ await runVercel(runner, ["blob", "delete-store", storeId, "--yes"], {
24562
+ cwd,
24563
+ mode: "capture",
24564
+ timeoutMs: PROVISION_TIMEOUT_MS,
24565
+ env
24566
+ });
24567
+ }
24512
24568
  async function createStoreQuiet(runner, cwd, baseName, env) {
24513
24569
  let result;
24514
24570
  let storeName = baseName;
24515
24571
  for (let attempt = 1; attempt <= MAX_STORE_NAME_ATTEMPTS; attempt++) {
24516
- storeName = versionedVercelProjectName(baseName, attempt);
24572
+ storeName = candidateStoreName(baseName, attempt);
24517
24573
  result = await runVercel(
24518
24574
  runner,
24519
24575
  ["blob", "create-store", storeName, "--access", "public", "--yes"],
@@ -24526,45 +24582,80 @@ async function createStoreQuiet(runner, cwd, baseName, env) {
24526
24582
  async function provisionBlobStoreInteractive(runner, cwd, options) {
24527
24583
  const quietSpinner = spinner2();
24528
24584
  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
- }
24585
+ let terminalFallbackRan = false;
24586
+ let lastResult;
24587
+ const pullToken = async (activeSpinner, storeName) => {
24588
+ const token = await pullBlobToken(runner, cwd, options.env);
24589
+ if (!token) {
24590
+ activeSpinner.stop("No BLOB_READ_WRITE_TOKEN came back from Vercel");
24591
+ return { ...storeName && { storeName }, failure: "env-pull-empty" };
24592
+ }
24593
+ activeSpinner.clear();
24594
+ return {
24595
+ token,
24596
+ ...storeName && { storeName },
24597
+ ...terminalFallbackRan && { usedTerminalFallback: true }
24598
+ };
24599
+ };
24600
+ for (let attempt = 1; attempt <= MAX_STORE_NAME_ATTEMPTS; attempt++) {
24601
+ const storeName = candidateStoreName(options.name, attempt);
24602
+ const result = await runVercel(
24603
+ runner,
24604
+ ["blob", "create-store", storeName, "--access", "public", "--yes"],
24605
+ { cwd, mode: "capture", timeoutMs: PROVISION_TIMEOUT_MS, env: options.env }
24606
+ );
24607
+ lastResult = result;
24608
+ if (result.success) {
24609
+ quietSpinner.message("Retrieving the Blob read-write token");
24610
+ return pullToken(quietSpinner, storeName);
24611
+ }
24612
+ if (isEnvVarConflict(result)) {
24613
+ await deleteOrphanStore(runner, cwd, result, options.env);
24614
+ quietSpinner.message("Retrieving the Blob read-write token");
24615
+ return pullToken(quietSpinner);
24616
+ }
24617
+ if (isNameTaken(result)) continue;
24618
+ if (terminalFallbackRan) break;
24619
+ terminalFallbackRan = true;
24537
24620
  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" };
24621
+ p15.log.info(
24622
+ `Create your Blob store in the Vercel prompts below ${pc4.dim(
24623
+ "(connect it to all environments)."
24624
+ )}`
24625
+ );
24626
+ const add = await runVercel(runner, ["blob", "create-store", storeName, "--access", "public"], {
24627
+ cwd,
24628
+ mode: "inherit",
24629
+ timeoutMs: INTERACTIVE_PROVISION_TIMEOUT_MS,
24630
+ env: options.env
24631
+ });
24632
+ if (add.success) {
24633
+ const pullSpinner = spinner2();
24634
+ pullSpinner.start("Retrieving the Blob read-write token");
24635
+ return pullToken(pullSpinner, storeName);
24636
+ }
24637
+ if (add.timedOut) return { failure: "timeout" };
24638
+ lastResult = add;
24639
+ quietSpinner.start("Creating your Vercel Blob store");
24560
24640
  }
24561
- pullSpinner.clear();
24562
- return { token, storeName: quiet.storeName, usedTerminalFallback: true };
24641
+ quietSpinner.stop("Vercel Blob provisioning did not complete");
24642
+ return { failure: lastResult?.timedOut ? "timeout" : "provision-failed" };
24563
24643
  }
24564
24644
  async function provisionBlobStore(runner, cwd, options) {
24565
24645
  const provisionSpinner = spinner2();
24566
24646
  provisionSpinner.start("Creating your Vercel Blob store");
24567
24647
  const { result, storeName } = await createStoreQuiet(runner, cwd, options.name, options.env);
24648
+ if (!result.success && isEnvVarConflict(result)) {
24649
+ await deleteOrphanStore(runner, cwd, result, options.env);
24650
+ provisionSpinner.message("Retrieving the Blob read-write token");
24651
+ const token2 = await pullBlobToken(runner, cwd, options.env);
24652
+ if (!token2) {
24653
+ provisionSpinner.stop("No BLOB_READ_WRITE_TOKEN came back from Vercel");
24654
+ return { failure: "env-pull-empty" };
24655
+ }
24656
+ provisionSpinner.clear();
24657
+ return { token: token2 };
24658
+ }
24568
24659
  if (!result.success) {
24569
24660
  provisionSpinner.stop("Vercel Blob provisioning did not complete");
24570
24661
  const combined = `${result.stdout}
@@ -24793,7 +24884,7 @@ function guardEnvLocal(cwd) {
24793
24884
  }
24794
24885
 
24795
24886
  // adapters/next/init/vercel/neon.ts
24796
- import * as p15 from "@clack/prompts";
24887
+ import * as p16 from "@clack/prompts";
24797
24888
  import pc5 from "picocolors";
24798
24889
  var PROVISION_TIMEOUT_MS2 = 18e4;
24799
24890
  var INTERACTIVE_PROVISION_TIMEOUT_MS2 = 6e5;
@@ -24811,7 +24902,7 @@ async function provisionNeonInteractive(runner, cwd, options) {
24811
24902
  return readNeonProvisionInfo(quiet.stdout);
24812
24903
  }
24813
24904
  quietSpinner.clear();
24814
- p15.log.info(
24905
+ p16.log.info(
24815
24906
  `Create your Neon database in the Vercel prompts below ${pc5.dim(
24816
24907
  "(the Free plan is recommended)."
24817
24908
  )}`
@@ -24885,14 +24976,14 @@ async function runVercelNeonFlow(options) {
24885
24976
  runnerSpinner.clear();
24886
24977
  const auth = await ensureVercelAuth(runner, options.cwd, { env });
24887
24978
  if (!auth.authed) {
24888
- p16.log.warn(authFailureMessage(auth.reason));
24979
+ p17.log.warn(authFailureMessage(auth.reason));
24889
24980
  return { ok: false };
24890
24981
  }
24891
24982
  const linkLinePrinted = await ensureLinkedProject(runner, options.cwd, options.projectName, env);
24892
24983
  const neon = await provisionNeonForMode(runner, options);
24893
24984
  if (neon.failure) {
24894
- p16.log.warn(neonFailureMessage(neon.failure));
24895
- if (neon.detail) p16.log.message(pc6.dim(neon.detail));
24985
+ p17.log.warn(neonFailureMessage(neon.failure));
24986
+ if (neon.detail) p17.log.message(pc6.dim(neon.detail));
24896
24987
  return { ok: false };
24897
24988
  }
24898
24989
  const databaseUrl = await pullNeonDatabaseUrl(runner, options.cwd, env);
@@ -24905,7 +24996,7 @@ async function runVercelNeonFlow(options) {
24905
24996
  dismissSignedInNote
24906
24997
  };
24907
24998
  } catch (error) {
24908
- p16.log.warn(
24999
+ p17.log.warn(
24909
25000
  `Vercel provisioning failed: ${error instanceof Error ? error.message : String(error)}`
24910
25001
  );
24911
25002
  return { ok: false };
@@ -24923,14 +25014,14 @@ async function runVercelBlobFlow(options) {
24923
25014
  runnerSpinner.clear();
24924
25015
  const auth = await ensureVercelAuth(runner, options.cwd, { env, quietIfAuthed: true });
24925
25016
  if (!auth.authed) {
24926
- p16.log.warn(authFailureMessage(auth.reason));
25017
+ p17.log.warn(authFailureMessage(auth.reason));
24927
25018
  return { ok: false };
24928
25019
  }
24929
25020
  await ensureLinkedProject(runner, options.cwd, options.projectName, env);
24930
25021
  const blob = await provisionBlobForMode(runner, options);
24931
25022
  if (blob.failure || !blob.token) {
24932
- p16.log.warn(blobFailureMessage(blob.failure));
24933
- if (blob.detail) p16.log.message(pc6.dim(blob.detail));
25023
+ p17.log.warn(blobFailureMessage(blob.failure));
25024
+ if (blob.detail) p17.log.message(pc6.dim(blob.detail));
24934
25025
  return { ok: false };
24935
25026
  }
24936
25027
  return {
@@ -24940,7 +25031,7 @@ async function runVercelBlobFlow(options) {
24940
25031
  usedTerminalFallback: blob.usedTerminalFallback
24941
25032
  };
24942
25033
  } catch (error) {
24943
- p16.log.warn(
25034
+ p17.log.warn(
24944
25035
  `Vercel provisioning failed: ${error instanceof Error ? error.message : String(error)}`
24945
25036
  );
24946
25037
  return { ok: false };
@@ -24958,7 +25049,7 @@ async function runVercelDeployFlow(options) {
24958
25049
  runnerSpinner.clear();
24959
25050
  const auth = await ensureVercelAuth(runner, options.cwd, { env, quietIfAuthed: true });
24960
25051
  if (!auth.authed) {
24961
- p16.log.warn(authFailureMessage(auth.reason));
25052
+ p17.log.warn(authFailureMessage(auth.reason));
24962
25053
  printManualDeployHint();
24963
25054
  return { ok: false };
24964
25055
  }
@@ -24976,17 +25067,17 @@ async function runVercelDeployFlow(options) {
24976
25067
  let printedWarnings = false;
24977
25068
  for (const key of sync.failed) {
24978
25069
  printedWarnings = true;
24979
- p16.log.warn(
25070
+ p17.log.warn(
24980
25071
  `Could not set ${pc6.cyan(key)} on Vercel \u2014 add it in the project's environment settings.`
24981
25072
  );
24982
25073
  }
24983
25074
  if (ensureVercelJsonFramework(options.cwd) === "created") {
24984
- p16.log.success(`Created ${pc6.cyan("vercel.json")} with the Next.js framework preset`);
25075
+ p17.log.success(`Created ${pc6.cyan("vercel.json")} with the Next.js framework preset`);
24985
25076
  }
24986
25077
  const packageGuard = guardProjectForDeploy(options.cwd);
24987
25078
  for (const dep of packageGuard.localSpecDeps) {
24988
25079
  printedWarnings = true;
24989
- p16.log.warn(
25080
+ p17.log.warn(
24990
25081
  `Dependency ${pc6.cyan(dep)} uses a local spec that cannot install on Vercel \u2014 the remote build may fail.`
24991
25082
  );
24992
25083
  }
@@ -25000,7 +25091,7 @@ async function runVercelDeployFlow(options) {
25000
25091
  }
25001
25092
  if (deploy.failure) {
25002
25093
  deploySpinner.stop(`${pc6.yellow("\u25B2")} ${deployFailureMessage(deploy.failure)}`);
25003
- if (deploy.detail) p16.log.message(pc6.dim(deploy.detail));
25094
+ if (deploy.detail) p17.log.message(pc6.dim(deploy.detail));
25004
25095
  printManualDeployHint();
25005
25096
  return { ok: false };
25006
25097
  }
@@ -25009,7 +25100,7 @@ async function runVercelDeployFlow(options) {
25009
25100
  deploySpinner.stop(url ? `Deployed ${pc6.cyan(url)}` : "Deployed to Vercel");
25010
25101
  return { ok: true, url, syncedEnvKeys: sync.synced, cleanScreen: !printedWarnings };
25011
25102
  } catch (error) {
25012
- p16.log.warn(`Vercel deploy failed: ${error instanceof Error ? error.message : String(error)}`);
25103
+ p17.log.warn(`Vercel deploy failed: ${error instanceof Error ? error.message : String(error)}`);
25013
25104
  printManualDeployHint();
25014
25105
  return { ok: false };
25015
25106
  } finally {
@@ -25017,7 +25108,7 @@ async function runVercelDeployFlow(options) {
25017
25108
  }
25018
25109
  }
25019
25110
  function printManualDeployHint() {
25020
- p16.log.info(`You can deploy manually: ${pc6.cyan("vercel deploy --prod")}`);
25111
+ p17.log.info(`You can deploy manually: ${pc6.cyan("vercel deploy --prod")}`);
25021
25112
  }
25022
25113
  async function ensureLinkedProject(runner, cwd, projectName, env) {
25023
25114
  if (readLinkedProjectId(cwd)) return false;
@@ -25065,11 +25156,11 @@ function authFailureMessage(reason) {
25065
25156
  function blobFailureMessage(reason) {
25066
25157
  switch (reason) {
25067
25158
  case "env-pull-empty":
25068
- return "Created a Blob store, but no BLOB_READ_WRITE_TOKEN came back \u2014 falling back to manual entry.";
25159
+ return "Created a Blob store, but no BLOB_READ_WRITE_TOKEN came back from Vercel.";
25069
25160
  case "timeout":
25070
- return "Vercel Blob provisioning timed out \u2014 falling back to manual entry.";
25161
+ return "Vercel Blob provisioning timed out.";
25071
25162
  default:
25072
- return "Vercel Blob provisioning did not complete \u2014 falling back to manual entry.";
25163
+ return "Vercel Blob provisioning did not complete.";
25073
25164
  }
25074
25165
  }
25075
25166
  function deployFailureMessage(reason) {
@@ -25483,7 +25574,8 @@ function removeExistingAdminPaths(cwd, namespaces) {
25483
25574
  }
25484
25575
  async function runInitCommand(name, options) {
25485
25576
  installPromptCheckmarks();
25486
- p17.box(
25577
+ const disposeCancelGuard = installSetupCancelGuard();
25578
+ p18.box(
25487
25579
  `
25488
25580
  \u2584 \u2597 \u2597 \u2584\u2596\u2597 \u2597
25489
25581
  \u2599\u2598\u2588\u258C\u259C\u2598\u259C\u2598\u2588\u258C\u259B\u2598\u259A \u259C\u2598\u2580\u258C\u259B\u2598\u259C\u2598
@@ -25504,13 +25596,13 @@ async function runInitCommand(name, options) {
25504
25596
  try {
25505
25597
  namespace = validateAdminNamespace(options.namespace);
25506
25598
  } catch (error) {
25507
- p17.log.error(error instanceof Error ? error.message : String(error));
25599
+ p18.log.error(error instanceof Error ? error.message : String(error));
25508
25600
  process.exit(1);
25509
25601
  }
25510
25602
  }
25511
25603
  if (!options.yes && !options.namespace) {
25512
25604
  const defaultDashboardPath = resolveAdminNamespace(DEFAULT_ADMIN_NAMESPACE).routePath;
25513
- const namespaceInput = await p17.text({
25605
+ const namespaceInput = await p18.text({
25514
25606
  message: "Enter the dashboard path",
25515
25607
  placeholder: `eg. ${defaultDashboardPath}`,
25516
25608
  defaultValue: defaultDashboardPath,
@@ -25523,8 +25615,8 @@ async function runInitCommand(name, options) {
25523
25615
  }
25524
25616
  }
25525
25617
  });
25526
- if (p17.isCancel(namespaceInput)) {
25527
- p17.cancel("Setup cancelled.");
25618
+ if (p18.isCancel(namespaceInput)) {
25619
+ p18.cancel("Setup cancelled.");
25528
25620
  process.exit(0);
25529
25621
  }
25530
25622
  namespace = validateAdminDashboardPath(namespaceInput);
@@ -25536,13 +25628,13 @@ async function runInitCommand(name, options) {
25536
25628
  if (project2.isExisting) {
25537
25629
  srcDir = project2.hasSrcDir;
25538
25630
  if (!project2.hasTypeScript) {
25539
- p17.log.error("TypeScript is required. Please add a tsconfig.json first.");
25631
+ p18.log.error("TypeScript is required. Please add a tsconfig.json first.");
25540
25632
  process.exit(1);
25541
25633
  }
25542
25634
  if (forceMode) {
25543
25635
  const nuked = removeExistingAdminPaths(cwd, await resolveForceInitNamespaces(cwd, namespace));
25544
25636
  if (nuked > 0) {
25545
- p17.log.warn(`${pc7.yellow("Force mode:")} removed ${nuked} existing admin paths`);
25637
+ p18.log.warn(`${pc7.yellow("Force mode:")} removed ${nuked} existing admin paths`);
25546
25638
  }
25547
25639
  project2 = detectProject(cwd, namespace);
25548
25640
  } else if (project2.conflicts.length > 0) {
@@ -25551,9 +25643,9 @@ async function runInitCommand(name, options) {
25551
25643
  "",
25552
25644
  pc7.dim(`Use ${pc7.bold("--force")} to remove existing admin files before scaffolding.`)
25553
25645
  );
25554
- p17.note(conflictLines.join("\n"), pc7.yellow("Conflicts"));
25646
+ p18.note(conflictLines.join("\n"), pc7.yellow("Conflicts"));
25555
25647
  if (!options.yes) {
25556
- const proceed = await p17.confirm({
25648
+ const proceed = await p18.confirm({
25557
25649
  message: [
25558
25650
  `Continue with ${pc7.bold(pc7.cyan("--force"))}?`,
25559
25651
  `${pc7.cyan("\u2502")} ${pc7.dim("This will force overwrite the existing admin code.")}`,
@@ -25561,8 +25653,8 @@ async function runInitCommand(name, options) {
25561
25653
  ].join("\n"),
25562
25654
  initialValue: true
25563
25655
  });
25564
- if (p17.isCancel(proceed) || !proceed) {
25565
- p17.cancel("Setup cancelled.");
25656
+ if (p18.isCancel(proceed) || !proceed) {
25657
+ p18.cancel("Setup cancelled.");
25566
25658
  process.exit(0);
25567
25659
  }
25568
25660
  forceMode = true;
@@ -25571,23 +25663,23 @@ async function runInitCommand(name, options) {
25571
25663
  await resolveForceInitNamespaces(cwd, namespace)
25572
25664
  );
25573
25665
  if (nuked > 0) {
25574
- p17.log.warn(`${pc7.yellow("Force mode:")} removed ${nuked} existing admin paths`);
25666
+ p18.log.warn(`${pc7.yellow("Force mode:")} removed ${nuked} existing admin paths`);
25575
25667
  }
25576
25668
  project2 = detectProject(cwd, namespace);
25577
25669
  }
25578
25670
  }
25579
25671
  } else {
25580
- p17.log.info("No Next.js app found \u2014 Running the fresh project mode...");
25672
+ p18.log.info("No Next.js app found \u2014 Running the fresh project mode...");
25581
25673
  let projectPrompt;
25582
25674
  try {
25583
25675
  projectPrompt = options.yes ? resolveNonInteractiveProject(name) : await promptProject(name);
25584
25676
  } catch (error) {
25585
- p17.log.error(error instanceof Error ? error.message : String(error));
25677
+ p18.log.error(error instanceof Error ? error.message : String(error));
25586
25678
  process.exit(1);
25587
25679
  }
25588
25680
  srcDir = projectPrompt.useSrcDir;
25589
25681
  if (!options.yes) {
25590
- const pmChoice = await p17.select({
25682
+ const pmChoice = await p18.select({
25591
25683
  message: "Which package manager do you want to use?",
25592
25684
  options: [
25593
25685
  { value: "pnpm", label: "pnpm", hint: "recommended" },
@@ -25596,8 +25688,8 @@ async function runInitCommand(name, options) {
25596
25688
  { value: "bun", label: "bun" }
25597
25689
  ]
25598
25690
  });
25599
- if (p17.isCancel(pmChoice)) {
25600
- p17.cancel("Setup cancelled.");
25691
+ if (p18.isCancel(pmChoice)) {
25692
+ p18.cancel("Setup cancelled.");
25601
25693
  process.exit(0);
25602
25694
  }
25603
25695
  pm = pmChoice;
@@ -25631,8 +25723,8 @@ async function runInitCommand(name, options) {
25631
25723
  process.stderr.write(`${createNextAppResult.output.trimEnd()}
25632
25724
  `);
25633
25725
  }
25634
- p17.log.error(createNextAppResult.error);
25635
- p17.log.info(
25726
+ p18.log.error(createNextAppResult.error);
25727
+ p18.log.info(
25636
25728
  `You can create the project manually:
25637
25729
  ${pc7.cyan(`npx create-next-app@latest ${projectPrompt.projectName} --typescript --tailwind --app`)}
25638
25730
  Then run ${pc7.cyan("betterstart init")} inside it.`
@@ -25646,11 +25738,11 @@ async function runInitCommand(name, options) {
25646
25738
  );
25647
25739
  if (!hasPackageJson || !hasNextConfig) {
25648
25740
  createNextAppSpinner.stop(`Failed to create Next.js app: ${displayName}`);
25649
- p17.log.error(
25741
+ p18.log.error(
25650
25742
  "create-next-app completed but the project was not created. This can happen with nested npx calls."
25651
25743
  );
25652
25744
  const manualCmd = `npx create-next-app@latest ${projectPrompt.projectName} --typescript --tailwind --app`;
25653
- p17.log.info(
25745
+ p18.log.info(
25654
25746
  `Create the project manually:
25655
25747
  ${pc7.cyan(manualCmd)}
25656
25748
  Then run ${pc7.cyan("betterstart init")} inside it.`
@@ -25669,7 +25761,7 @@ async function runInitCommand(name, options) {
25669
25761
  if (options.yes) {
25670
25762
  if (options.databaseUrl) {
25671
25763
  if (!isValidDbUrl(options.databaseUrl)) {
25672
- p17.log.error(
25764
+ p18.log.error(
25673
25765
  `Invalid database URL. Must start with ${pc7.cyan("postgres://")} or ${pc7.cyan("postgresql://")}`
25674
25766
  );
25675
25767
  process.exit(1);
@@ -25689,24 +25781,24 @@ async function runInitCommand(name, options) {
25689
25781
  databaseUrl = flow.databaseUrl;
25690
25782
  persistDatabaseUrl(cwd, databaseUrl);
25691
25783
  } else if (flow.ok) {
25692
- p17.log.warn("Created a Neon database, but DATABASE_URL could not be retrieved from Vercel.");
25784
+ p18.log.warn("Created a Neon database, but DATABASE_URL could not be retrieved from Vercel.");
25693
25785
  const resourceUrl = flow.dashboardUrl ?? flow.resourceUrl;
25694
25786
  if (resourceUrl) {
25695
- p17.log.info(
25787
+ p18.log.info(
25696
25788
  `Open ${pc7.cyan(resourceUrl)} to copy DATABASE_URL, then rerun ${pc7.cyan("betterstart init --database-url <url> --yes")}.`
25697
25789
  );
25698
25790
  } else {
25699
- p17.log.info(
25791
+ p18.log.info(
25700
25792
  `Rerun ${pc7.cyan("betterstart init --database-url <url> --yes")} with a Postgres connection string.`
25701
25793
  );
25702
25794
  }
25703
25795
  } else {
25704
- p17.log.warn("Vercel provisioning did not complete; continuing without a database URL.");
25796
+ p18.log.warn("Vercel provisioning did not complete; continuing without a database URL.");
25705
25797
  }
25706
25798
  }
25707
25799
  } else if (existingDbUrl) {
25708
25800
  const masked = maskDbUrl(existingDbUrl);
25709
- p17.log.info(`Using existing database URL from .env.local ${pc7.dim(`(${masked})`)}`);
25801
+ p18.log.info(`Using existing database URL from .env.local ${pc7.dim(`(${masked})`)}`);
25710
25802
  databaseUrl = existingDbUrl;
25711
25803
  } else {
25712
25804
  const databaseStep = deferNextPromptCheck();
@@ -25732,7 +25824,7 @@ async function runInitCommand(name, options) {
25732
25824
  persistDatabaseUrl(cwd, databaseUrl);
25733
25825
  } else {
25734
25826
  databaseStep.skip();
25735
- p17.log.info("Falling back to a manual database connection string.");
25827
+ p18.log.info("Falling back to a manual database connection string.");
25736
25828
  openBrowserVercelNeon();
25737
25829
  databaseUrl = await promptConnectionString();
25738
25830
  }
@@ -25774,21 +25866,19 @@ async function runInitCommand(name, options) {
25774
25866
  });
25775
25867
  if (flow.ok && flow.token) {
25776
25868
  persistBlobReadWriteToken(cwd, flow.token);
25777
- p17.log.success(`Saved BLOB_READ_WRITE_TOKEN to ${pc7.cyan(".env.local")}`);
25778
25869
  collectedIntegrationConfig.sections.push({
25779
25870
  header: "Storage (Vercel Blob)",
25780
25871
  vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: flow.token }]
25781
25872
  });
25782
25873
  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
25874
  } else {
25791
- p17.log.warn("Vercel Blob provisioning did not complete; continuing without a Blob token.");
25875
+ p18.log.warn(
25876
+ "Continuing without a Blob token \u2014 add BLOB_READ_WRITE_TOKEN to .env.local from the store on vercel.com/dashboard/stores."
25877
+ );
25878
+ collectedIntegrationConfig.sections.push({
25879
+ header: "Storage (Vercel Blob)",
25880
+ vars: [{ key: "BLOB_READ_WRITE_TOKEN", value: "" }]
25881
+ });
25792
25882
  }
25793
25883
  }
25794
25884
  } else if (options.yes) {
@@ -25891,7 +25981,7 @@ async function runInitCommand(name, options) {
25891
25981
  });
25892
25982
  s.stop("");
25893
25983
  process.stdout.write("\x1B[2A\x1B[J");
25894
- p17.note(noteLines.join("\n"), "Scaffolded admin");
25984
+ p18.note(noteLines.join("\n"), "Scaffolded admin");
25895
25985
  const drizzleConfigPath = path50.join(cwd, "drizzle.config.ts");
25896
25986
  if (!dbFiles.includes("drizzle.config.ts") && fs39.existsSync(drizzleConfigPath)) {
25897
25987
  if (forceMode) {
@@ -25901,20 +25991,20 @@ async function runInitCommand(name, options) {
25901
25991
  readNamespacedTemplate("drizzle.config.ts", namespace),
25902
25992
  "utf-8"
25903
25993
  );
25904
- p17.log.success("Updated drizzle.config.ts");
25994
+ p18.log.success("Updated drizzle.config.ts");
25905
25995
  } else if (!options.yes) {
25906
- const overwrite = await p17.confirm({
25996
+ const overwrite = await p18.confirm({
25907
25997
  message: "drizzle.config.ts already exists. Overwrite with latest version?",
25908
25998
  initialValue: true
25909
25999
  });
25910
- if (!p17.isCancel(overwrite) && overwrite) {
26000
+ if (!p18.isCancel(overwrite) && overwrite) {
25911
26001
  const { readNamespacedTemplate } = await import("./template-reader-X6KFP7B3.js");
25912
26002
  fs39.writeFileSync(
25913
26003
  drizzleConfigPath,
25914
26004
  readNamespacedTemplate("drizzle.config.ts", namespace),
25915
26005
  "utf-8"
25916
26006
  );
25917
- p17.log.success("Updated drizzle.config.ts");
26007
+ p18.log.success("Updated drizzle.config.ts");
25918
26008
  }
25919
26009
  }
25920
26010
  }
@@ -25940,8 +26030,8 @@ async function runInitCommand(name, options) {
25940
26030
  depsInstalled = true;
25941
26031
  } else {
25942
26032
  s.stop("Failed to install dependencies");
25943
- p17.log.warning(depsResult.error ?? "Unknown error");
25944
- p17.log.info(
26033
+ p18.log.warning(depsResult.error ?? "Unknown error");
26034
+ p18.log.info(
25945
26035
  `You can install them manually:
25946
26036
  ${pc7.cyan(`${pm} add ${depsResult.dependencies.join(" ")}`)}
25947
26037
  ${pc7.cyan(`${pm} add -D ${depsResult.devDeps.join(" ")}`)}`
@@ -26031,10 +26121,10 @@ async function runInitCommand(name, options) {
26031
26121
  ` ${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
26122
  );
26033
26123
  }
26034
- p17.note(installLines.join("\n"), "Installed");
26124
+ p18.note(installLines.join("\n"), "Installed");
26035
26125
  let dbPushed = false;
26036
26126
  if (depsResult.success && options.skipMigration && hasDbUrl(cwd)) {
26037
- p17.log.info(`Skipping database schema push ${pc7.dim("(--skip-migration)")}`);
26127
+ p18.log.info(`Skipping database schema push ${pc7.dim("(--skip-migration)")}`);
26038
26128
  } else if (depsResult.success && hasDbUrl(cwd)) {
26039
26129
  let driverReady = hasDrizzleKitPostgresDriverDependency(cwd);
26040
26130
  if (!driverReady) {
@@ -26049,8 +26139,8 @@ async function runInitCommand(name, options) {
26049
26139
  });
26050
26140
  if (!driverResult.success) {
26051
26141
  s.stop("Database push failed");
26052
- p17.log.warning(driverResult.error ?? `Failed to install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP}`);
26053
- p17.log.info(
26142
+ p18.log.warning(driverResult.error ?? `Failed to install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP}`);
26143
+ p18.log.info(
26054
26144
  `Install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP} manually, then run: ${pc7.cyan(drizzlePushCommand(pm))}`
26055
26145
  );
26056
26146
  } else {
@@ -26061,7 +26151,7 @@ async function runInitCommand(name, options) {
26061
26151
  if (driverReady) {
26062
26152
  const useTerminalForDbPush = shouldUseTerminalForDbPush(options);
26063
26153
  if (useTerminalForDbPush) {
26064
- p17.log.info(`Running ${pc7.cyan("drizzle-kit push --force")}`);
26154
+ p18.log.info(`Running ${pc7.cyan("drizzle-kit push --force")}`);
26065
26155
  } else {
26066
26156
  s.start("Pushing database schema (drizzle-kit push)");
26067
26157
  }
@@ -26070,13 +26160,13 @@ async function runInitCommand(name, options) {
26070
26160
  if (useTerminalForDbPush) {
26071
26161
  const verification = await verifyDatabaseReachable(cwd);
26072
26162
  if (!verification.success) {
26073
- p17.log.warning(verification.error);
26074
- p17.log.error("Database was not reachable. Aborting setup.");
26163
+ p18.log.warning(verification.error);
26164
+ p18.log.error("Database was not reachable. Aborting setup.");
26075
26165
  process.exit(1);
26076
26166
  }
26077
26167
  }
26078
26168
  if (useTerminalForDbPush) {
26079
- p17.log.success("Database schema pushed");
26169
+ p18.log.success("Database schema pushed");
26080
26170
  } else {
26081
26171
  s.stop(`${pc7.green("\u2713")} Database schema pushed`);
26082
26172
  }
@@ -26086,12 +26176,12 @@ async function runInitCommand(name, options) {
26086
26176
  s.stop("Database push failed");
26087
26177
  }
26088
26178
  const pushError = pushResult.error ?? "Unknown error";
26089
- p17.log.warning(pushError);
26179
+ p18.log.warning(pushError);
26090
26180
  if (isDatabaseReachabilityError(pushError)) {
26091
- p17.log.error("Database was not reachable. Aborting setup.");
26181
+ p18.log.error("Database was not reachable. Aborting setup.");
26092
26182
  process.exit(1);
26093
26183
  }
26094
- p17.log.info(`You can run it manually: ${pc7.cyan(drizzlePushCommand(pm))}`);
26184
+ p18.log.info(`You can run it manually: ${pc7.cyan(drizzlePushCommand(pm))}`);
26095
26185
  }
26096
26186
  }
26097
26187
  }
@@ -26100,7 +26190,7 @@ async function runInitCommand(name, options) {
26100
26190
  let seedSuccess = false;
26101
26191
  let adminAccountReady = false;
26102
26192
  if (dbPushed && options.skipAdminCreation) {
26103
- p17.log.info(
26193
+ p18.log.info(
26104
26194
  `Skipping admin user creation ${pc7.dim(`(use ${betterstartExecCommand(pm, "seed")} later)`)}`
26105
26195
  );
26106
26196
  }
@@ -26110,14 +26200,14 @@ async function runInitCommand(name, options) {
26110
26200
  let replaceExistingAdmin = false;
26111
26201
  const adminCheck = await checkExistingAdmin(cwd, adminDir, authBasePath);
26112
26202
  if (adminCheck.error) {
26113
- p17.log.warning(`Could not verify existing admin account ${pc7.dim(`(${adminCheck.error})`)}`);
26203
+ p18.log.warning(`Could not verify existing admin account ${pc7.dim(`(${adminCheck.error})`)}`);
26114
26204
  if (isDatabaseReachabilityError(adminCheck.error)) {
26115
- p17.log.error("Database was not reachable. Aborting setup.");
26205
+ p18.log.error("Database was not reachable. Aborting setup.");
26116
26206
  process.exit(1);
26117
26207
  }
26118
26208
  } else if (adminCheck.existingAdmin) {
26119
26209
  const existingAdminLabel = formatAdminIdentity(adminCheck.existingAdmin);
26120
- const adminAction = await p17.select({
26210
+ const adminAction = await p18.select({
26121
26211
  message: "Found an already existing admin account. Do you want to replace it or skip?",
26122
26212
  options: [
26123
26213
  {
@@ -26131,34 +26221,34 @@ async function runInitCommand(name, options) {
26131
26221
  }
26132
26222
  ]
26133
26223
  });
26134
- if (p17.isCancel(adminAction)) {
26135
- p17.cancel("Setup cancelled.");
26224
+ if (p18.isCancel(adminAction)) {
26225
+ p18.cancel("Setup cancelled.");
26136
26226
  process.exit(0);
26137
26227
  }
26138
26228
  if (adminAction === "skip") {
26139
26229
  adminAccountReady = true;
26140
- p17.log.info(`Keeping existing admin account ${pc7.dim(`(${existingAdminLabel})`)}`);
26230
+ p18.log.info(`Keeping existing admin account ${pc7.dim(`(${existingAdminLabel})`)}`);
26141
26231
  } else {
26142
26232
  replaceExistingAdmin = true;
26143
26233
  }
26144
26234
  }
26145
26235
  if (!adminAccountReady) {
26146
- p17.note(
26236
+ p18.note(
26147
26237
  pc7.dim(
26148
26238
  replaceExistingAdmin ? "Replace the existing admin account to access the admin." : "Create your first admin user to access the dashboard."
26149
26239
  ),
26150
26240
  "Admin account"
26151
26241
  );
26152
- const credentials = await p17.group(
26242
+ const credentials = await p18.group(
26153
26243
  {
26154
- email: () => p17.text({
26244
+ email: () => p18.text({
26155
26245
  message: "Admin email",
26156
26246
  placeholder: "admin@example.com",
26157
26247
  validate: (v) => {
26158
26248
  if (!v || !v.includes("@")) return "Please enter a valid email";
26159
26249
  }
26160
26250
  }),
26161
- password: () => p17.password({
26251
+ password: () => p18.password({
26162
26252
  message: "Admin password",
26163
26253
  validate: (v) => {
26164
26254
  if (!v || v.length < 8) return "Password must be at least 8 characters";
@@ -26167,7 +26257,7 @@ async function runInitCommand(name, options) {
26167
26257
  },
26168
26258
  {
26169
26259
  onCancel: () => {
26170
- p17.cancel("Setup cancelled.");
26260
+ p18.cancel("Setup cancelled.");
26171
26261
  process.exit(0);
26172
26262
  }
26173
26263
  }
@@ -26186,11 +26276,11 @@ async function runInitCommand(name, options) {
26186
26276
  );
26187
26277
  if (seedResult.existingUser) {
26188
26278
  s.stop(`${pc7.yellow("\u25B2")} An account already exists for ${credentials.email}`);
26189
- const replace = await p17.confirm({
26279
+ const replace = await p18.confirm({
26190
26280
  message: "Replace the existing account with this email?",
26191
26281
  initialValue: false
26192
26282
  });
26193
- if (!p17.isCancel(replace) && replace) {
26283
+ if (!p18.isCancel(replace) && replace) {
26194
26284
  seedOverwriteMode = "email";
26195
26285
  s.start("Replacing admin user");
26196
26286
  seedResult = await runSeed(
@@ -26211,14 +26301,14 @@ async function runInitCommand(name, options) {
26211
26301
  adminAccountReady = true;
26212
26302
  } else if (seedResult.error) {
26213
26303
  s.stop(`${pc7.red("\u2717")} Failed to create admin user`);
26214
- p17.note(
26304
+ p18.note(
26215
26305
  `${pc7.red(seedResult.error)}
26216
26306
 
26217
26307
  Run manually: ${pc7.cyan(betterstartExecCommand(pm, "seed"))}`,
26218
26308
  pc7.red("Seed failed")
26219
26309
  );
26220
26310
  if (isDatabaseReachabilityError(seedResult.error)) {
26221
- p17.log.error("Database was not reachable. Aborting setup.");
26311
+ p18.log.error("Database was not reachable. Aborting setup.");
26222
26312
  process.exit(1);
26223
26313
  }
26224
26314
  }
@@ -26250,16 +26340,16 @@ Run manually: ${pc7.cyan(betterstartExecCommand(pm, "seed"))}`,
26250
26340
  `Files created: ${pc7.cyan(String(totalFiles))}`,
26251
26341
  `Env vars: ${envResult.added.length} added, ${envResult.skipped.length} skipped`
26252
26342
  ];
26253
- p17.note(summaryLines.join("\n"), "Admin scaffolded successfully");
26343
+ p18.note(summaryLines.join("\n"), "Admin scaffolded successfully");
26254
26344
  if (options.vercel !== false && !options.skipDeploy) {
26255
26345
  const linkedVercelProject = Boolean(readLinkedProjectId(cwd));
26256
26346
  if (!options.yes) {
26257
26347
  const deployStep = deferNextPromptCheck();
26258
- const deployNow = await p17.confirm({
26348
+ const deployNow = await p18.confirm({
26259
26349
  message: "Deploy to Vercel now?",
26260
26350
  initialValue: linkedVercelProject
26261
26351
  });
26262
- if (!p17.isCancel(deployNow) && deployNow) {
26352
+ if (!p18.isCancel(deployNow) && deployNow) {
26263
26353
  const deployFlow = await runVercelDeployFlow({ cwd, projectName, env: process.env });
26264
26354
  if (deployFlow.ok && deployFlow.cleanScreen) deployStep.confirm();
26265
26355
  else deployStep.skip();
@@ -26273,17 +26363,18 @@ Run manually: ${pc7.cyan(betterstartExecCommand(pm, "seed"))}`,
26273
26363
  env: process.env
26274
26364
  });
26275
26365
  if (!deployFlow.ok) {
26276
- p17.log.warn("Vercel deploy did not complete; continuing.");
26366
+ p18.log.warn("Vercel deploy did not complete; continuing.");
26277
26367
  }
26278
26368
  }
26279
26369
  }
26280
26370
  if (!options.yes && !options.skipDevServerStart) {
26281
26371
  const devCmd = runCommand(pm, "dev");
26282
- const startDev = await p17.confirm({
26372
+ const startDev = await p18.confirm({
26283
26373
  message: "Start the development server?",
26284
26374
  initialValue: true
26285
26375
  });
26286
- if (!p17.isCancel(startDev) && startDev) {
26376
+ if (!p18.isCancel(startDev) && startDev) {
26377
+ disposeCancelGuard();
26287
26378
  await startManagedDevServer(cwd, devCmd, adminLoginUrl, {
26288
26379
  email: seedSuccess && seedEmail ? seedEmail : void 0,
26289
26380
  password: seedSuccess && seedPassword ? seedPassword : void 0
@@ -26291,7 +26382,8 @@ Run manually: ${pc7.cyan(betterstartExecCommand(pm, "seed"))}`,
26291
26382
  return;
26292
26383
  }
26293
26384
  }
26294
- p17.outro("Done!");
26385
+ disposeCancelGuard();
26386
+ p18.outro("Done!");
26295
26387
  }
26296
26388
  function isValidDbUrl(url) {
26297
26389
  return url.startsWith("postgres://") || url.startsWith("postgresql://");
@@ -26678,7 +26770,7 @@ function printAdminReadyNote(state) {
26678
26770
  lines.unshift(`Password: ${pc7.cyan(state.adminPassword)}`);
26679
26771
  lines.unshift(`Admin user: ${pc7.cyan(state.adminEmail)}`);
26680
26772
  }
26681
- p17.note(lines.join("\n"), "Admin ready");
26773
+ p18.note(lines.join("\n"), "Admin ready");
26682
26774
  }
26683
26775
  function shouldSuppressDevServerStartupLine(line) {
26684
26776
  const plain = stripAnsi2(line).trim();
@@ -26790,7 +26882,7 @@ function startManagedDevServer(cwd, devCmd, adminLoginUrl, adminCredentials) {
26790
26882
 
26791
26883
  // adapters/next/commands/list-integrations.ts
26792
26884
  import path51 from "path";
26793
- import * as p18 from "@clack/prompts";
26885
+ import * as p19 from "@clack/prompts";
26794
26886
  async function runListIntegrationsCommand(options) {
26795
26887
  const cwd = options.cwd ? path51.resolve(options.cwd) : process.cwd();
26796
26888
  const config = await resolveConfig(cwd);
@@ -26799,15 +26891,15 @@ async function runListIntegrationsCommand(options) {
26799
26891
  const status = installedIntegrations.has(integration.id) ? "installed" : "available";
26800
26892
  return `${integration.id.padEnd(12)} ${status.padEnd(10)} ${integration.kind.padEnd(8)} ${integration.description}`;
26801
26893
  });
26802
- p18.note(lines.join("\n"), "BetterStart integrations");
26803
- p18.outro(
26894
+ p19.note(lines.join("\n"), "BetterStart integrations");
26895
+ p19.outro(
26804
26896
  `${installedIntegrations.size} installed, ${lines.length - installedIntegrations.size} available`
26805
26897
  );
26806
26898
  }
26807
26899
 
26808
26900
  // adapters/next/commands/list-plugins.ts
26809
26901
  import path52 from "path";
26810
- import * as p19 from "@clack/prompts";
26902
+ import * as p20 from "@clack/prompts";
26811
26903
  async function runListPluginsCommand(options) {
26812
26904
  const cwd = options.cwd ? path52.resolve(options.cwd) : process.cwd();
26813
26905
  const config = await resolveConfig(cwd);
@@ -26816,34 +26908,34 @@ async function runListPluginsCommand(options) {
26816
26908
  const status = installedPlugins.has(plugin.id) ? "installed" : "available";
26817
26909
  return `${plugin.id.padEnd(10)} ${status.padEnd(10)} ${plugin.kind.padEnd(12)} ${plugin.description}`;
26818
26910
  });
26819
- p19.note(lines.join("\n"), "BetterStart plugins");
26820
- p19.outro(`${installedPlugins.size} installed, ${lines.length - installedPlugins.size} available`);
26911
+ p20.note(lines.join("\n"), "BetterStart plugins");
26912
+ p20.outro(`${installedPlugins.size} installed, ${lines.length - installedPlugins.size} available`);
26821
26913
  }
26822
26914
 
26823
26915
  // adapters/next/commands/remove.ts
26824
26916
  import path53 from "path";
26825
- import * as p20 from "@clack/prompts";
26917
+ import * as p21 from "@clack/prompts";
26826
26918
  async function runRemoveCommand(items, options) {
26827
26919
  const removeIntegrationsMode = Boolean(options.integration);
26828
26920
  if (!removeIntegrationsMode && items.includes("core")) {
26829
- p20.log.error("The core Admin cannot be removed.");
26921
+ p21.log.error("The core Admin cannot be removed.");
26830
26922
  process.exit(1);
26831
26923
  }
26832
26924
  const pluginIds = items.filter(isPluginId);
26833
26925
  const integrationIds = items.filter(isIntegrationId);
26834
26926
  if (!removeIntegrationsMode && integrationIds.length > 0) {
26835
- p20.log.error(
26927
+ p21.log.error(
26836
26928
  `Integration IDs require --integration. Run \`betterstart remove --integration ${integrationIds.join(" ")}\`.`
26837
26929
  );
26838
26930
  process.exit(1);
26839
26931
  }
26840
26932
  if (removeIntegrationsMode && pluginIds.length > 0) {
26841
- p20.log.error(`Plugin IDs cannot be removed with --integration: ${pluginIds.join(", ")}`);
26933
+ p21.log.error(`Plugin IDs cannot be removed with --integration: ${pluginIds.join(", ")}`);
26842
26934
  process.exit(1);
26843
26935
  }
26844
26936
  const invalidItems = removeIntegrationsMode ? items.filter((integrationId) => !isIntegrationId(integrationId)) : items.filter((pluginId) => !isPluginId(pluginId));
26845
26937
  if (invalidItems.length > 0) {
26846
- p20.log.error(
26938
+ p21.log.error(
26847
26939
  removeIntegrationsMode ? formatUnknownIntegrationMessage(invalidItems) : formatUnknownPluginMessage(invalidItems)
26848
26940
  );
26849
26941
  process.exit(1);
@@ -26852,12 +26944,12 @@ async function runRemoveCommand(items, options) {
26852
26944
  const config = await resolveConfig(cwd);
26853
26945
  const pm = detectPackageManager(cwd);
26854
26946
  if (!options.force) {
26855
- const confirmed = await p20.confirm({
26947
+ const confirmed = await p21.confirm({
26856
26948
  message: `Remove ${removeIntegrationsMode ? "integration" : "plugin"}${items.length === 1 ? "" : "s"} ${items.join(", ")}?`,
26857
26949
  initialValue: false
26858
26950
  });
26859
- if (p20.isCancel(confirmed) || !confirmed) {
26860
- p20.cancel(`${removeIntegrationsMode ? "Integration" : "Plugin"} removal cancelled.`);
26951
+ if (p21.isCancel(confirmed) || !confirmed) {
26952
+ p21.cancel(`${removeIntegrationsMode ? "Integration" : "Plugin"} removal cancelled.`);
26861
26953
  process.exit(0);
26862
26954
  }
26863
26955
  }
@@ -26870,13 +26962,13 @@ async function runRemoveCommand(items, options) {
26870
26962
  });
26871
26963
  writeConfigFile(cwd, result2.config);
26872
26964
  if (result2.removed.length === 0) {
26873
- p20.outro("No integrations were removed.");
26965
+ p21.outro("No integrations were removed.");
26874
26966
  return;
26875
26967
  }
26876
26968
  if (result2.warnings.length > 0) {
26877
- p20.note(result2.warnings.join("\n"), "Warnings");
26969
+ p21.note(result2.warnings.join("\n"), "Warnings");
26878
26970
  }
26879
- p20.outro(
26971
+ p21.outro(
26880
26972
  `Removed integration${result2.removed.length === 1 ? "" : "s"}: ${result2.removed.join(", ")}`
26881
26973
  );
26882
26974
  return;
@@ -26889,13 +26981,13 @@ async function runRemoveCommand(items, options) {
26889
26981
  });
26890
26982
  writeConfigFile(cwd, result.config);
26891
26983
  if (result.removed.length === 0) {
26892
- p20.outro("No plugins were removed.");
26984
+ p21.outro("No plugins were removed.");
26893
26985
  return;
26894
26986
  }
26895
26987
  if (result.warnings.length > 0) {
26896
- p20.note(result.warnings.join("\n"), "Warnings");
26988
+ p21.note(result.warnings.join("\n"), "Warnings");
26897
26989
  }
26898
- p20.outro(`Removed plugin${result.removed.length === 1 ? "" : "s"}: ${result.removed.join(", ")}`);
26990
+ p21.outro(`Removed plugin${result.removed.length === 1 ? "" : "s"}: ${result.removed.join(", ")}`);
26899
26991
  }
26900
26992
 
26901
26993
  // adapters/next/commands/remove-schema.ts
@@ -27045,7 +27137,7 @@ async function runRemoveSchemaCommand(schemaName, options) {
27045
27137
  // adapters/next/commands/uninstall.ts
27046
27138
  import fs42 from "fs";
27047
27139
  import path55 from "path";
27048
- import * as p21 from "@clack/prompts";
27140
+ import * as p22 from "@clack/prompts";
27049
27141
  import pc8 from "picocolors";
27050
27142
 
27051
27143
  // adapters/next/commands/uninstall-cleaners.ts
@@ -27267,8 +27359,8 @@ function buildUninstallPlan(cwd, namespaceValue) {
27267
27359
  count: configFiles.length,
27268
27360
  unit: configFiles.length === 1 ? "file" : "files",
27269
27361
  execute() {
27270
- for (const p22 of configPaths) {
27271
- if (fs42.existsSync(p22)) fs42.unlinkSync(p22);
27362
+ for (const p23 of configPaths) {
27363
+ if (fs42.existsSync(p23)) fs42.unlinkSync(p23);
27272
27364
  }
27273
27365
  }
27274
27366
  });
@@ -27332,7 +27424,7 @@ function buildUninstallPlan(cwd, namespaceValue) {
27332
27424
  }
27333
27425
  async function runUninstallCommand(options) {
27334
27426
  const cwd = options.cwd ? path55.resolve(options.cwd) : process.cwd();
27335
- p21.intro(pc8.bgRed(pc8.white(" BetterStart Uninstall ")));
27427
+ p22.intro(pc8.bgRed(pc8.white(" BetterStart Uninstall ")));
27336
27428
  let namespace = DEFAULT_ADMIN_NAMESPACE;
27337
27429
  try {
27338
27430
  const config = await resolveConfig(cwd);
@@ -27341,8 +27433,8 @@ async function runUninstallCommand(options) {
27341
27433
  }
27342
27434
  const steps = buildUninstallPlan(cwd, namespace);
27343
27435
  if (steps.length === 0) {
27344
- p21.log.success(`${pc8.green("\u2713")} Nothing to remove \u2014 project is already clean.`);
27345
- p21.outro("Done");
27436
+ p22.log.success(`${pc8.green("\u2713")} Nothing to remove \u2014 project is already clean.`);
27437
+ p22.outro("Done");
27346
27438
  return;
27347
27439
  }
27348
27440
  const planLines = steps.map((step) => {
@@ -27350,14 +27442,14 @@ async function runUninstallCommand(options) {
27350
27442
  const countLabel = pc8.dim(`${step.count} ${step.unit}`);
27351
27443
  return `${pc8.red("\xD7")} ${names} ${countLabel}`;
27352
27444
  });
27353
- p21.note(planLines.join("\n"), "Uninstall plan");
27445
+ p22.note(planLines.join("\n"), "Uninstall plan");
27354
27446
  if (!options.force) {
27355
- const confirmed = await p21.confirm({
27447
+ const confirmed = await p22.confirm({
27356
27448
  message: "Proceed with uninstall?",
27357
27449
  initialValue: false
27358
27450
  });
27359
- if (p21.isCancel(confirmed) || !confirmed) {
27360
- p21.cancel("Uninstall cancelled.");
27451
+ if (p22.isCancel(confirmed) || !confirmed) {
27452
+ p22.cancel("Uninstall cancelled.");
27361
27453
  process.exit(0);
27362
27454
  }
27363
27455
  }
@@ -27369,8 +27461,8 @@ async function runUninstallCommand(options) {
27369
27461
  }
27370
27462
  const parts = steps.map((step) => `${step.count} ${step.unit}`);
27371
27463
  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");
27464
+ p22.note(pc8.dim("Database tables were NOT dropped \u2014 drop them manually if needed."), "Next steps");
27465
+ p22.outro("Uninstall complete");
27374
27466
  }
27375
27467
 
27376
27468
  // adapters/next/commands/update-component.ts