betterstart-cli 0.0.39 → 0.0.41

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
@@ -22176,6 +22176,11 @@ function scaffoldEnv(cwd, options) {
22176
22176
  }
22177
22177
 
22178
22178
  // adapters/next/init/prompts/plugins.ts
22179
+ function maskBlobToken(token) {
22180
+ const match = /^(vercel_blob_rw_[A-Za-z0-9]+)_/.exec(token);
22181
+ if (match) return `${match[1]}_***`;
22182
+ return `${token.slice(0, 8)}***`;
22183
+ }
22179
22184
  async function promptPlugins(cwd, options = {}) {
22180
22185
  const sections = [];
22181
22186
  const overwriteKeys = /* @__PURE__ */ new Set();
@@ -22235,7 +22240,12 @@ async function promptPlugins(cwd, options = {}) {
22235
22240
  storageStep.skip();
22236
22241
  } else {
22237
22242
  if (existingToken) {
22238
- p12.log.info("Using the existing Vercel Blob token from .env.local");
22243
+ p12.log.info(
22244
+ `Using the existing BLOB_READ_WRITE_TOKEN from .env.local ${styleText(
22245
+ "dim",
22246
+ `(${maskBlobToken(existingToken)})`
22247
+ )}`
22248
+ );
22239
22249
  }
22240
22250
  mergeIntegrationConfig(await collectIntegrationConfig(cwd, ["vercel-blob"]));
22241
22251
  storageStep.confirm();
@@ -22260,30 +22270,13 @@ async function promptPlugins(cwd, options = {}) {
22260
22270
  process.exit(0);
22261
22271
  }
22262
22272
  const integrations = [];
22263
- const setupResend = await p12.confirm({
22264
- message: `Do you want to setup resend.com emails?
22265
- ${styleText(
22266
- "dim",
22267
- "This will be used for form submission confirmations, password resets etc."
22268
- )}`,
22269
- initialValue: true
22273
+ sections.push({
22274
+ header: "Email (Resend)",
22275
+ vars: [
22276
+ { key: "BETTERSTART_RESEND_API_KEY", value: "" },
22277
+ { key: "BETTERSTART_EMAIL_FROM", value: "" }
22278
+ ]
22270
22279
  });
22271
- if (p12.isCancel(setupResend)) {
22272
- p12.cancel("Setup cancelled.");
22273
- process.exit(0);
22274
- }
22275
- if (setupResend) {
22276
- integrations.push("resend");
22277
- mergeIntegrationConfig(await collectIntegrationConfig(cwd, ["resend"]));
22278
- } else {
22279
- sections.push({
22280
- header: "Email (Resend)",
22281
- vars: [
22282
- { key: "BETTERSTART_RESEND_API_KEY", value: "" },
22283
- { key: "BETTERSTART_EMAIL_FROM", value: "" }
22284
- ]
22285
- });
22286
- }
22287
22280
  if (storage === "r2") {
22288
22281
  integrations.push("r2");
22289
22282
  } else if (storage === "vercel-blob") {
@@ -25495,18 +25488,6 @@ function readNamespaceFromConfigSource(source) {
25495
25488
  const match = source.match(/\bnamespace\s*:\s*(['"`])([^'"`]+)\1/);
25496
25489
  return normalizeNamespaceForRemoval(match?.[2]);
25497
25490
  }
25498
- function formatStorageProviderLabel(provider) {
25499
- switch (provider) {
25500
- case "vercel-blob":
25501
- return "Vercel Blob";
25502
- case "r2":
25503
- return "Cloudflare R2";
25504
- case "local":
25505
- return "local filesystem";
25506
- default:
25507
- return provider;
25508
- }
25509
- }
25510
25491
  function resolveNonInteractiveProject(name) {
25511
25492
  const projectName = name?.trim() || "my-app";
25512
25493
  if (projectName !== "." && !/^[a-z0-9_-]+$/i.test(projectName)) {
@@ -25798,7 +25779,7 @@ async function runInitCommand(name, options) {
25798
25779
  }
25799
25780
  } else if (existingDbUrl) {
25800
25781
  const masked = maskDbUrl(existingDbUrl);
25801
- p18.log.info(`Using existing database URL from .env.local ${pc7.dim(`(${masked})`)}`);
25782
+ p18.log.info(`Using the existing DATABASE_URL from .env.local ${pc7.dim(`(${masked})`)}`);
25802
25783
  databaseUrl = existingDbUrl;
25803
25784
  } else {
25804
25785
  const databaseStep = deferNextPromptCheck();
@@ -25903,35 +25884,21 @@ async function runInitCommand(name, options) {
25903
25884
  config.frameworkConfig.next.paths = deriveNamespacePaths(namespace, srcDir);
25904
25885
  config.paths = config.frameworkConfig.next.paths;
25905
25886
  config.database.migrationsDir = deriveMigrationsDir(namespace);
25906
- const results = [];
25907
25887
  const s = spinner2();
25908
25888
  s.start("Directory structure");
25909
- const baseFiles = scaffoldBase({
25889
+ scaffoldBase({
25910
25890
  cwd,
25911
25891
  config,
25912
25892
  includeEmailTemplatesDir: pluginSelection.integrations.some(isEmailProviderIntegration)
25913
25893
  });
25914
- results.push({ label: "Directory structure", result: `${baseFiles.length} files` });
25915
25894
  s.message("TypeScript aliases");
25916
- const tsResult = scaffoldTsconfig(cwd, config);
25917
- results.push({
25918
- label: "TypeScript aliases",
25919
- result: tsResult.added.length > 0 ? `${tsResult.added.length} paths` : "already set"
25920
- });
25895
+ scaffoldTsconfig(cwd, config);
25921
25896
  s.message("Tailwind CSS");
25922
- const twResult = scaffoldTailwind(cwd, srcDir, namespace);
25923
- results.push({
25924
- label: "Tailwind CSS",
25925
- result: twResult.appended ? "updated" : twResult.file ? "already set" : "no CSS file"
25926
- });
25897
+ scaffoldTailwind(cwd, srcDir, namespace);
25927
25898
  s.message("Dev server");
25928
- const devServerResult = ensureDefaultDevScriptPort(cwd);
25929
- results.push({
25930
- label: "Dev server",
25931
- result: formatDevServerResult(devServerResult)
25932
- });
25899
+ ensureDefaultDevScriptPort(cwd);
25933
25900
  s.message("Environment variables");
25934
- const envResult = scaffoldEnv(cwd, { databaseUrl, namespace });
25901
+ scaffoldEnv(cwd, { databaseUrl, namespace });
25935
25902
  if (collectedIntegrationConfig.sections.length > 0) {
25936
25903
  appendEnvVars(
25937
25904
  cwd,
@@ -25939,49 +25906,26 @@ async function runInitCommand(name, options) {
25939
25906
  collectedIntegrationConfig.overwriteKeys
25940
25907
  );
25941
25908
  }
25942
- const envCount = envResult.added.length + envResult.updated.length;
25943
- results.push({
25944
- label: "Environment variables",
25945
- result: envCount > 0 ? `${envCount} vars` : "already set"
25946
- });
25947
25909
  s.message("Database");
25948
25910
  const dbFiles = scaffoldDatabase({ cwd, config });
25949
- results.push({ label: "Database", result: `${dbFiles.length} files` });
25950
25911
  s.message("Authentication");
25951
- const authFiles = scaffoldAuth({ cwd, config });
25952
- results.push({ label: "Authentication", result: `${authFiles.length} files` });
25912
+ scaffoldAuth({ cwd, config });
25953
25913
  s.message("Components");
25954
- const compFiles = scaffoldComponents({ cwd, config });
25955
- results.push({ label: "Components", result: `${compFiles.length} files` });
25914
+ scaffoldComponents({ cwd, config });
25956
25915
  s.message("Pages & layouts");
25957
- const layoutFiles = scaffoldLayout({ cwd, config });
25958
- results.push({ label: "Pages & layouts", result: `${layoutFiles.length} files` });
25916
+ scaffoldLayout({ cwd, config });
25959
25917
  s.message("API routes");
25960
- const apiFiles = scaffoldApiRoutes({ cwd, config });
25961
- results.push({ label: "API routes", result: `${apiFiles.length} routes` });
25918
+ scaffoldApiRoutes({ cwd, config });
25962
25919
  s.message("Linter");
25963
- let linterResult;
25964
25920
  if (project2.linter.type === "none") {
25965
- const biomeResult = scaffoldBiome(cwd, project2.linter);
25966
- linterResult = biomeResult.installed ? "biome (new)" : "none";
25967
- } else {
25968
- linterResult = project2.linter.type;
25921
+ scaffoldBiome(cwd, project2.linter);
25969
25922
  }
25970
- results.push({ label: "Linter", result: linterResult });
25971
25923
  s.message("Next.js config");
25972
25924
  const nextConfigResult = scaffoldNextConfig({ cwd, nextMajorVersion });
25973
- results.push({
25974
- label: "Next.js config",
25975
- result: nextConfigResult.status === "created" ? "created" : nextConfigResult.status === "updated" ? "updated" : nextConfigResult.status === "already-set" ? "already set" : nextConfigResult.status === "not-needed" ? "not needed" : "manual setup needed"
25976
- });
25977
- const maxLabel = Math.max(...results.map((r) => r.label.length));
25978
- const noteLines = results.map((r) => {
25979
- const padded = r.label.padEnd(maxLabel + 3);
25980
- return `${pc7.green("\u2713")} ${padded}${pc7.dim(r.result)}`;
25981
- });
25982
- s.stop("");
25983
- process.stdout.write("\x1B[2A\x1B[J");
25984
- p18.note(noteLines.join("\n"), "Scaffolded admin");
25925
+ s.clear();
25926
+ if (nextConfigResult.status === "unsupported") {
25927
+ p18.log.warn("The Next.js config could not be updated automatically \u2014 review it manually.");
25928
+ }
25985
25929
  const drizzleConfigPath = path50.join(cwd, "drizzle.config.ts");
25986
25930
  if (!dbFiles.includes("drizzle.config.ts") && fs39.existsSync(drizzleConfigPath)) {
25987
25931
  if (forceMode) {
@@ -26086,42 +26030,20 @@ async function runInitCommand(name, options) {
26086
26030
  }
26087
26031
  writeConfigFile(cwd, resolvedIntegrationInstallResult.config);
26088
26032
  const usesLocalStorage = resolvedIntegrationInstallResult.config.storage.provider === "local";
26089
- const installLines = [];
26090
- if (depsInstalled) {
26091
- installLines.push(
26092
- `${pc7.green("\u2713")} Dependencies ${pc7.dim(`${depsResult.dependencies.length} deps + ${depsResult.devDeps.length} dev deps`)}`
26093
- );
26033
+ for (const err of coreSchemasResult.errors) {
26034
+ p18.log.warn(`Core schemas: ${err}`);
26094
26035
  }
26095
- if (coreSchemasResult.errors.length > 0) {
26096
- installLines.push(
26097
- `${pc7.yellow("\u25B2")} Core schemas ${pc7.dim(`${coreSchemasResult.errors.length} warning(s)`)}`
26098
- );
26099
- for (const err of coreSchemasResult.errors) {
26100
- installLines.push(` ${pc7.dim(err)}`);
26101
- }
26102
- } else {
26103
- installLines.push(
26104
- `${pc7.green("\u2713")} Core schemas ${pc7.dim(`${coreSchemasResult.schemas.length} schemas, ${coreSchemasResult.generatedFiles.length} files`)}`
26105
- );
26106
- }
26107
- installLines.push(
26108
- `${pc7.green("\u2713")} Plugins ${pc7.dim(resolvedPluginInstallResult.installed.length > 0 ? resolvedPluginInstallResult.installed.join(", ") : "core only")}`
26109
- );
26110
26036
  for (const warning of resolvedPluginInstallResult.warnings) {
26111
- installLines.push(` ${pc7.yellow("\u25B2")} ${warning}`);
26037
+ p18.log.warn(warning);
26112
26038
  }
26113
- installLines.push(
26114
- `${pc7.green("\u2713")} Integrations ${pc7.dim(resolvedIntegrationInstallResult.installed.length > 0 ? resolvedIntegrationInstallResult.installed.join(", ") : "none")}`
26115
- );
26116
26039
  for (const warning of resolvedIntegrationInstallResult.warnings) {
26117
- installLines.push(` ${pc7.yellow("\u25B2")} ${warning}`);
26040
+ p18.log.warn(warning);
26118
26041
  }
26119
26042
  if (usesLocalStorage) {
26120
- installLines.push(
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.`
26043
+ p18.log.warn(
26044
+ "Local filesystem storage is only for development or self-hosted persistent-disk deployments. Use Vercel Blob or Cloudflare R2 for hosted/serverless production."
26122
26045
  );
26123
26046
  }
26124
- p18.note(installLines.join("\n"), "Installed");
26125
26047
  let dbPushed = false;
26126
26048
  if (depsResult.success && options.skipMigration && hasDbUrl(cwd)) {
26127
26049
  p18.log.info(`Skipping database schema push ${pc7.dim("(--skip-migration)")}`);
@@ -26150,9 +26072,7 @@ async function runInitCommand(name, options) {
26150
26072
  }
26151
26073
  if (driverReady) {
26152
26074
  const useTerminalForDbPush = shouldUseTerminalForDbPush(options);
26153
- if (useTerminalForDbPush) {
26154
- p18.log.info(`Running ${pc7.cyan("drizzle-kit push --force")}`);
26155
- } else {
26075
+ if (!useTerminalForDbPush) {
26156
26076
  s.start("Pushing database schema (drizzle-kit push)");
26157
26077
  }
26158
26078
  const pushResult = await runDrizzlePush(cwd, { interactive: useTerminalForDbPush });
@@ -26164,11 +26084,8 @@ async function runInitCommand(name, options) {
26164
26084
  p18.log.error("Database was not reachable. Aborting setup.");
26165
26085
  process.exit(1);
26166
26086
  }
26167
- }
26168
- if (useTerminalForDbPush) {
26169
- p18.log.success("Database schema pushed");
26170
26087
  } else {
26171
- s.stop(`${pc7.green("\u2713")} Database schema pushed`);
26088
+ s.clear();
26172
26089
  }
26173
26090
  dbPushed = true;
26174
26091
  } else {
@@ -26233,12 +26150,6 @@ async function runInitCommand(name, options) {
26233
26150
  }
26234
26151
  }
26235
26152
  if (!adminAccountReady) {
26236
- p18.note(
26237
- pc7.dim(
26238
- replaceExistingAdmin ? "Replace the existing admin account to access the admin." : "Create your first admin user to access the dashboard."
26239
- ),
26240
- "Admin account"
26241
- );
26242
26153
  const credentials = await p18.group(
26243
26154
  {
26244
26155
  email: () => p18.text({
@@ -26328,19 +26239,8 @@ Run manually: ${pc7.cyan(betterstartExecCommand(pm, "seed"))}`,
26328
26239
  s.stop("Git commit skipped");
26329
26240
  }
26330
26241
  }
26331
- const totalFiles = baseFiles.length + dbFiles.length + authFiles.length + compFiles.length + layoutFiles.length + apiFiles.length;
26332
26242
  const adminNamespace = resolveAdminNamespace(namespace);
26333
26243
  const adminLoginUrl = `http://localhost:${detectDevPort(cwd)}${adminNamespace.routePath}/login`;
26334
- const installedPluginList = resolvedPluginInstallResult.installed.length > 0 ? resolvedPluginInstallResult.installed.join(", ") : "core only";
26335
- const installedIntegrationList = resolvedIntegrationInstallResult.installed.length > 0 ? resolvedIntegrationInstallResult.installed.join(", ") : "none";
26336
- const summaryLines = [
26337
- `Plugins: ${pc7.cyan(installedPluginList)}`,
26338
- `Integrations: ${pc7.cyan(installedIntegrationList)}`,
26339
- `Storage: ${pc7.cyan(formatStorageProviderLabel(resolvedIntegrationInstallResult.config.storage.provider))}`,
26340
- `Files created: ${pc7.cyan(String(totalFiles))}`,
26341
- `Env vars: ${envResult.added.length} added, ${envResult.skipped.length} skipped`
26342
- ];
26343
- p18.note(summaryLines.join("\n"), "Admin scaffolded successfully");
26344
26244
  if (options.vercel !== false && !options.skipDeploy) {
26345
26245
  const linkedVercelProject = Boolean(readLinkedProjectId(cwd));
26346
26246
  if (!options.yes) {
@@ -26453,20 +26353,6 @@ function detectInstalledNextVersion(cwd) {
26453
26353
  return void 0;
26454
26354
  }
26455
26355
  }
26456
- function formatDevServerResult(result) {
26457
- switch (result.status) {
26458
- case "updated":
26459
- return `set to ${result.port}`;
26460
- case "preserved":
26461
- return `using ${result.port}`;
26462
- case "skipped":
26463
- return `custom script preserved; BetterStart uses ${result.port}`;
26464
- case "missing-script":
26465
- return `no dev script; BetterStart uses ${result.port}`;
26466
- case "missing-package":
26467
- return `package.json missing; BetterStart uses ${result.port}`;
26468
- }
26469
- }
26470
26356
  function shouldUseTerminalForDbPush(options) {
26471
26357
  return !options.yes && process.env.CI !== "true" && Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
26472
26358
  }