betterstart-cli 0.0.29 → 0.0.31

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
@@ -21852,6 +21852,16 @@ function openBrowserVercelNeon() {
21852
21852
  `Opening Vercel... Create a Neon Postgres database, then copy the ${pc.cyan("DATABASE_URL")} from the dashboard.`
21853
21853
  );
21854
21854
  }
21855
+ function openBrowserVercelNeonResource(url) {
21856
+ if (!url) {
21857
+ openBrowserVercelNeon();
21858
+ return;
21859
+ }
21860
+ openBrowser(url);
21861
+ p10.log.info(
21862
+ `Opening Vercel... Copy the Neon ${pc.cyan("DATABASE_URL")} from the database dashboard, then paste it below.`
21863
+ );
21864
+ }
21855
21865
  async function promptConnectionString() {
21856
21866
  const input = await p10.text({
21857
21867
  message: "Paste your PostgreSQL connection string",
@@ -24655,25 +24665,17 @@ import pc5 from "picocolors";
24655
24665
  var PROVISION_TIMEOUT_MS2 = 18e4;
24656
24666
  var INTERACTIVE_PROVISION_TIMEOUT_MS2 = 6e5;
24657
24667
  async function provisionNeonInteractive(runner, cwd, options) {
24658
- const addArgs = ["integration", "add", "neon", "--no-env-pull"];
24659
- if (options.plan) addArgs.push("--plan", options.plan);
24660
24668
  const quietSpinner = spinner2();
24661
24669
  quietSpinner.start("Creating your Neon database");
24662
- const quiet = await runVercel(runner, addArgs, {
24670
+ const quiet = await runVercel(runner, neonAddArgs(options), {
24663
24671
  cwd,
24664
24672
  mode: "capture",
24665
24673
  timeoutMs: PROVISION_TIMEOUT_MS2,
24666
24674
  env: options.env
24667
24675
  });
24668
24676
  if (quiet.success) {
24669
- quietSpinner.message("Retrieving the database connection string");
24670
- const databaseUrl2 = await pullDatabaseUrl(runner, cwd, options.env);
24671
- if (!databaseUrl2) {
24672
- quietSpinner.stop("No DATABASE_URL came back from Vercel");
24673
- return { failure: "env-pull-empty" };
24674
- }
24675
24677
  quietSpinner.clear();
24676
- return { databaseUrl: databaseUrl2 };
24678
+ return readNeonProvisionInfo(quiet.stdout);
24677
24679
  }
24678
24680
  quietSpinner.clear();
24679
24681
  p15.log.info(
@@ -24681,7 +24683,7 @@ async function provisionNeonInteractive(runner, cwd, options) {
24681
24683
  "(the Free plan is recommended)."
24682
24684
  )}`
24683
24685
  );
24684
- const add = await runVercel(runner, addArgs, {
24686
+ const add = await runVercel(runner, connectedNeonAddArgs(options), {
24685
24687
  cwd,
24686
24688
  mode: "inherit",
24687
24689
  timeoutMs: INTERACTIVE_PROVISION_TIMEOUT_MS2,
@@ -24690,22 +24692,12 @@ async function provisionNeonInteractive(runner, cwd, options) {
24690
24692
  if (!add.success) {
24691
24693
  return { failure: add.timedOut ? "timeout" : "provision-failed" };
24692
24694
  }
24693
- const pullSpinner = spinner2();
24694
- pullSpinner.start("Retrieving the database connection string");
24695
- const databaseUrl = await pullDatabaseUrl(runner, cwd, options.env);
24696
- if (!databaseUrl) {
24697
- pullSpinner.stop("No DATABASE_URL came back from Vercel");
24698
- return { failure: "env-pull-empty" };
24699
- }
24700
- pullSpinner.clear();
24701
- return { databaseUrl };
24695
+ return {};
24702
24696
  }
24703
24697
  async function provisionNeon(runner, cwd, options) {
24704
- const addArgs = ["integration", "add", "neon", "--no-env-pull", "--format", "json"];
24705
- if (options.plan) addArgs.push("--plan", options.plan);
24706
24698
  const provisionSpinner = spinner2();
24707
24699
  provisionSpinner.start("Creating your Neon database");
24708
- const add = await runVercel(runner, addArgs, {
24700
+ const add = await runVercel(runner, neonAddArgs(options), {
24709
24701
  cwd,
24710
24702
  mode: "capture",
24711
24703
  timeoutMs: PROVISION_TIMEOUT_MS2,
@@ -24720,33 +24712,34 @@ ${add.stderr}`.trim();
24720
24712
  if (/claim/i.test(combined)) return { failure: "claim-required", detail };
24721
24713
  return { failure: add.timedOut ? "timeout" : "provision-failed", detail };
24722
24714
  }
24723
- const meta = parseVercelJson(add.stdout);
24724
- const resourceName = meta?.resourceName ?? meta?.name;
24725
- provisionSpinner.message("Retrieving the database connection string");
24726
- const databaseUrl = await pullDatabaseUrl(runner, cwd, options.env);
24727
- if (!databaseUrl) {
24728
- provisionSpinner.stop("No DATABASE_URL came back from Vercel");
24729
- return { resourceName, failure: "env-pull-empty" };
24730
- }
24731
24715
  provisionSpinner.clear();
24732
- return { databaseUrl, resourceName };
24733
- }
24734
- function pullDatabaseUrl(runner, cwd, env) {
24735
- return pullVercelEnvValue(runner, cwd, readDbUrlFromDotenv, env);
24716
+ return readNeonProvisionInfo(add.stdout);
24736
24717
  }
24737
24718
  function readDbUrlFromDotenv(filePath) {
24738
24719
  const vars = parseDotenvFile(filePath);
24739
24720
  const isPg = (v) => !!v && (v.startsWith("postgres://") || v.startsWith("postgresql://"));
24740
24721
  if (isPg(vars.get("DATABASE_URL"))) return vars.get("DATABASE_URL");
24741
24722
  if (isPg(vars.get("POSTGRES_URL"))) return vars.get("POSTGRES_URL");
24742
- for (const [key, value] of vars) {
24743
- if (/DATABASE_URL$/.test(key) && isPg(value)) return value;
24744
- }
24745
- for (const value of vars.values()) {
24746
- if (isPg(value)) return value;
24747
- }
24748
24723
  return void 0;
24749
24724
  }
24725
+ function readNeonProvisionInfo(stdout) {
24726
+ const meta = parseVercelJson(stdout);
24727
+ if (!meta) return {};
24728
+ const info = {};
24729
+ if (meta.dashboardUrl) info.dashboardUrl = meta.dashboardUrl;
24730
+ if (meta.ssoUrl?.resource) info.resourceUrl = meta.ssoUrl.resource;
24731
+ return info;
24732
+ }
24733
+ function neonAddArgs(options) {
24734
+ const addArgs = ["integration", "add", "neon", "--format", "json"];
24735
+ if (options.plan) addArgs.push("--plan", options.plan);
24736
+ return addArgs;
24737
+ }
24738
+ function connectedNeonAddArgs(options) {
24739
+ const addArgs = ["integration", "add", "neon"];
24740
+ if (options.plan) addArgs.push("--plan", options.plan);
24741
+ return addArgs;
24742
+ }
24750
24743
 
24751
24744
  // adapters/next/init/vercel/flow.ts
24752
24745
  async function runVercelNeonFlow(options) {
@@ -24762,23 +24755,19 @@ async function runVercelNeonFlow(options) {
24762
24755
  p16.log.warn(authFailureMessage(auth.reason));
24763
24756
  return { ok: false };
24764
24757
  }
24765
- const projectSpinner = spinner2();
24766
- projectSpinner.start(`Creating a Vercel project ${pc6.cyan(options.projectName)}`);
24767
- const project2 = await createVercelProject(runner, options.cwd, options.projectName, env);
24768
- projectSpinner.stop(
24769
- project2.linked ? `Linked Vercel project ${pc6.cyan(project2.name)}` : `Using Vercel project ${pc6.cyan(project2.name)}`
24770
- );
24758
+ await ensureLinkedProject(runner, options.cwd, options.projectName, env);
24771
24759
  const neon = await provisionNeonForMode(runner, options);
24772
- if (neon.failure || !neon.databaseUrl) {
24760
+ if (neon.failure) {
24773
24761
  p16.log.warn(neonFailureMessage(neon.failure));
24774
24762
  if (neon.detail) p16.log.message(pc6.dim(neon.detail));
24775
24763
  return { ok: false };
24776
24764
  }
24765
+ const databaseUrl = await pullNeonDatabaseUrl(runner, options.cwd, env);
24777
24766
  return {
24778
24767
  ok: true,
24779
- databaseUrl: neon.databaseUrl,
24780
- vercelProjectId: project2.projectId,
24781
- neonResourceName: neon.resourceName
24768
+ databaseUrl,
24769
+ dashboardUrl: neon.dashboardUrl,
24770
+ resourceUrl: neon.resourceUrl
24782
24771
  };
24783
24772
  } catch (error) {
24784
24773
  p16.log.warn(
@@ -24802,16 +24791,7 @@ async function runVercelBlobFlow(options) {
24802
24791
  p16.log.warn(authFailureMessage(auth.reason));
24803
24792
  return { ok: false };
24804
24793
  }
24805
- let projectId = readLinkedProjectId(options.cwd);
24806
- if (!projectId) {
24807
- const projectSpinner = spinner2();
24808
- projectSpinner.start(`Creating a Vercel project ${pc6.cyan(options.projectName)}`);
24809
- const project2 = await createVercelProject(runner, options.cwd, options.projectName, env);
24810
- projectSpinner.stop(
24811
- project2.linked ? `Linked Vercel project ${pc6.cyan(project2.name)}` : `Using Vercel project ${pc6.cyan(project2.name)}`
24812
- );
24813
- projectId = project2.projectId;
24814
- }
24794
+ await ensureLinkedProject(runner, options.cwd, options.projectName, env);
24815
24795
  const blob = await provisionBlobForMode(runner, options);
24816
24796
  if (blob.failure || !blob.token) {
24817
24797
  p16.log.warn(blobFailureMessage(blob.failure));
@@ -24821,7 +24801,6 @@ async function runVercelBlobFlow(options) {
24821
24801
  return {
24822
24802
  ok: true,
24823
24803
  token: blob.token,
24824
- vercelProjectId: projectId,
24825
24804
  blobStoreName: blob.storeName
24826
24805
  };
24827
24806
  } catch (error) {
@@ -24847,14 +24826,7 @@ async function runVercelDeployFlow(options) {
24847
24826
  printManualDeployHint();
24848
24827
  return { ok: false };
24849
24828
  }
24850
- if (!readLinkedProjectId(options.cwd)) {
24851
- const projectSpinner = spinner2();
24852
- projectSpinner.start(`Creating a Vercel project ${pc6.cyan(options.projectName)}`);
24853
- const project2 = await createVercelProject(runner, options.cwd, options.projectName, env);
24854
- projectSpinner.stop(
24855
- project2.linked ? `Linked Vercel project ${pc6.cyan(project2.name)}` : `Using Vercel project ${pc6.cyan(project2.name)}`
24856
- );
24857
- }
24829
+ await ensureLinkedProject(runner, options.cwd, options.projectName, env);
24858
24830
  const envSpinner = spinner2();
24859
24831
  envSpinner.start("Syncing environment variables to Vercel");
24860
24832
  const sync = await syncVercelProductionEnv(runner, options.cwd, env);
@@ -24908,6 +24880,26 @@ async function runVercelDeployFlow(options) {
24908
24880
  function printManualDeployHint() {
24909
24881
  p16.log.info(`You can deploy manually: ${pc6.cyan("vercel deploy --prod")}`);
24910
24882
  }
24883
+ async function ensureLinkedProject(runner, cwd, projectName, env) {
24884
+ if (readLinkedProjectId(cwd)) return;
24885
+ const projectSpinner = spinner2();
24886
+ projectSpinner.start(`Creating a Vercel project ${pc6.cyan(projectName)}`);
24887
+ const project2 = await createVercelProject(runner, cwd, projectName, env);
24888
+ projectSpinner.stop(
24889
+ project2.linked ? `Linked Vercel project ${pc6.cyan(project2.name)}` : `Using Vercel project ${pc6.cyan(project2.name)}`
24890
+ );
24891
+ }
24892
+ async function pullNeonDatabaseUrl(runner, cwd, env) {
24893
+ const neonSpinner = spinner2();
24894
+ neonSpinner.start("Retrieving DATABASE_URL from Vercel");
24895
+ const databaseUrl = await pullVercelEnvValue(runner, cwd, readDbUrlFromDotenv, env);
24896
+ if (!databaseUrl) {
24897
+ neonSpinner.stop(`${pc6.yellow("\u25B2")} No DATABASE_URL came back from Vercel`);
24898
+ return void 0;
24899
+ }
24900
+ neonSpinner.clear();
24901
+ return databaseUrl;
24902
+ }
24911
24903
  function provisionBlobForMode(runner, options) {
24912
24904
  const opts = { name: blobStoreName(options.projectName), env: options.env };
24913
24905
  return options.interactive ? provisionBlobStoreInteractive(runner, options.cwd, opts) : provisionBlobStore(runner, options.cwd, opts);
@@ -24954,8 +24946,6 @@ function neonFailureMessage(reason) {
24954
24946
  return "Provisioning needs the Neon marketplace terms accepted \u2014 falling back to manual entry.";
24955
24947
  case "claim-required":
24956
24948
  return "The Neon resource needs to be claimed \u2014 falling back to manual entry.";
24957
- case "env-pull-empty":
24958
- return "Provisioned Neon, but no DATABASE_URL came back \u2014 falling back to manual entry.";
24959
24949
  case "timeout":
24960
24950
  return "Neon provisioning timed out \u2014 falling back to manual entry.";
24961
24951
  default:
@@ -25557,6 +25547,18 @@ async function runInitCommand(name, options) {
25557
25547
  databaseUrl = flow.databaseUrl;
25558
25548
  persistDatabaseUrl(cwd, databaseUrl);
25559
25549
  p17.log.success(`Saved DATABASE_URL to ${pc7.cyan(".env.local")}`);
25550
+ } else if (flow.ok) {
25551
+ p17.log.warn("Created a Neon database, but DATABASE_URL could not be retrieved from Vercel.");
25552
+ const resourceUrl = flow.resourceUrl ?? flow.dashboardUrl;
25553
+ if (resourceUrl) {
25554
+ p17.log.info(
25555
+ `Open ${pc7.cyan(resourceUrl)} to copy DATABASE_URL, then rerun ${pc7.cyan("betterstart init --database-url <url> --yes")}.`
25556
+ );
25557
+ } else {
25558
+ p17.log.info(
25559
+ `Rerun ${pc7.cyan("betterstart init --database-url <url> --yes")} with a Postgres connection string.`
25560
+ );
25561
+ }
25560
25562
  } else {
25561
25563
  p17.log.warn("Vercel provisioning did not complete; continuing without a database URL.");
25562
25564
  }
@@ -25579,6 +25581,11 @@ async function runInitCommand(name, options) {
25579
25581
  databaseUrl = flow.databaseUrl;
25580
25582
  persistDatabaseUrl(cwd, databaseUrl);
25581
25583
  p17.log.success(`Saved DATABASE_URL to ${pc7.cyan(".env.local")}`);
25584
+ } else if (flow.ok) {
25585
+ openBrowserVercelNeonResource(flow.resourceUrl ?? flow.dashboardUrl);
25586
+ databaseUrl = await promptConnectionString();
25587
+ persistDatabaseUrl(cwd, databaseUrl);
25588
+ p17.log.success(`Saved DATABASE_URL to ${pc7.cyan(".env.local")}`);
25582
25589
  } else {
25583
25590
  p17.log.info("Falling back to a manual database connection string.");
25584
25591
  openBrowserVercelNeon();
@@ -26105,7 +26112,11 @@ Run manually: ${pc7.cyan(betterstartExecCommand(pm, "seed"))}`,
26105
26112
  await runVercelDeployFlow({ cwd, projectName, env: process.env });
26106
26113
  }
26107
26114
  } else if (linkedVercelProject && process.env.VERCEL_TOKEN) {
26108
- const deployFlow = await runVercelDeployFlow({ cwd, projectName, env: process.env });
26115
+ const deployFlow = await runVercelDeployFlow({
26116
+ cwd,
26117
+ projectName,
26118
+ env: process.env
26119
+ });
26109
26120
  if (!deployFlow.ok) {
26110
26121
  p17.log.warn("Vercel deploy did not complete; continuing.");
26111
26122
  }