create-better-fullstack 1.4.16 → 1.5.1

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/README.md CHANGED
@@ -38,6 +38,7 @@ Configure your stack visually — pick every option from a UI, preview your choi
38
38
  --yolo # Scaffold a random stack — good for exploring
39
39
  --template <name> # Use a preset (t3, mern, pern, uniwind)
40
40
  --ecosystem <lang> # Start in rust, python, or go mode
41
+ --version-channel # Dependency channel: stable, latest, beta
41
42
  --no-git # Skip git initialization
42
43
  --no-install # Skip dependency installation
43
44
  --package-manager # Package manager (bun, pnpm, npm, yarn)
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { s as createBtsCli } from "./src-BVbxA2WC.mjs";
2
+ import { s as createBtsCli } from "./src-hfdQPH0o.mjs";
3
3
 
4
4
  //#region src/cli.ts
5
5
  createBtsCli().run();
package/dist/index.d.mts CHANGED
@@ -278,6 +278,11 @@ declare const router: {
278
278
  yarn: "yarn";
279
279
  }>>;
280
280
  install: z.ZodOptional<z.ZodBoolean>;
281
+ versionChannel: z.ZodOptional<z.ZodEnum<{
282
+ stable: "stable";
283
+ latest: "latest";
284
+ beta: "beta";
285
+ }>>;
281
286
  dbSetup: z.ZodOptional<z.ZodEnum<{
282
287
  none: "none";
283
288
  turso: "turso";
@@ -543,6 +548,7 @@ declare const router: {
543
548
  payments: "none" | "polar" | "stripe" | "lemon-squeezy" | "paddle" | "dodo";
544
549
  git: boolean;
545
550
  packageManager: "bun" | "npm" | "pnpm" | "yarn";
551
+ versionChannel: "stable" | "latest" | "beta";
546
552
  install: boolean;
547
553
  dbSetup: "none" | "turso" | "neon" | "prisma-postgres" | "planetscale" | "mongodb-atlas" | "supabase" | "upstash" | "d1" | "docker";
548
554
  api: "none" | "trpc" | "orpc" | "ts-rest" | "garph";
@@ -629,6 +635,7 @@ declare const router: {
629
635
  payments: "none" | "polar" | "stripe" | "lemon-squeezy" | "paddle" | "dodo";
630
636
  git: boolean;
631
637
  packageManager: "bun" | "npm" | "pnpm" | "yarn";
638
+ versionChannel: "stable" | "latest" | "beta";
632
639
  install: boolean;
633
640
  dbSetup: "none" | "turso" | "neon" | "prisma-postgres" | "planetscale" | "mongodb-atlas" | "supabase" | "upstash" | "d1" | "docker";
634
641
  api: "none" | "trpc" | "orpc" | "ts-rest" | "garph";
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { a as builder, c as createVirtual, d as history, f as router, i as add, l as docs, n as TEMPLATE_COUNT, o as create, p as sponsors, r as VirtualFileSystem, s as createBtsCli, t as EMBEDDED_TEMPLATES, u as generateVirtualProject } from "./src-BVbxA2WC.mjs";
2
+ import { a as builder, c as createVirtual, d as history, f as router, i as add, l as docs, n as TEMPLATE_COUNT, o as create, p as sponsors, r as VirtualFileSystem, s as createBtsCli, t as EMBEDDED_TEMPLATES, u as generateVirtualProject } from "./src-hfdQPH0o.mjs";
3
3
 
4
4
  export { EMBEDDED_TEMPLATES, TEMPLATE_COUNT, VirtualFileSystem, add, builder, create, createBtsCli, createVirtual, docs, generateVirtualProject, history, router, sponsors };
@@ -67,6 +67,7 @@ const DEFAULT_CONFIG_BASE = {
67
67
  examples: [],
68
68
  git: true,
69
69
  install: true,
70
+ versionChannel: "stable",
70
71
  dbSetup: "none",
71
72
  backend: "hono",
72
73
  runtime: "bun",
@@ -1288,10 +1289,9 @@ const ADDON_GROUPS = {
1288
1289
  "tauri",
1289
1290
  "opentui",
1290
1291
  "wxt",
1291
- "ruler",
1292
- "msw",
1293
- "storybook"
1292
+ "ruler"
1294
1293
  ],
1294
+ Integrations: ["msw", "storybook"],
1295
1295
  "AI Agents": ["mcp", "skills"],
1296
1296
  TanStack: [
1297
1297
  "tanstack-query",
@@ -1308,6 +1308,7 @@ async function getAddonsChoice(addons, frontends, auth) {
1308
1308
  Tooling: [],
1309
1309
  Documentation: [],
1310
1310
  Extensions: [],
1311
+ Integrations: [],
1311
1312
  "AI Agents": [],
1312
1313
  TanStack: []
1313
1314
  };
@@ -1324,6 +1325,7 @@ async function getAddonsChoice(addons, frontends, auth) {
1324
1325
  if (ADDON_GROUPS.Tooling.includes(addon)) groupedOptions.Tooling.push(option);
1325
1326
  else if (ADDON_GROUPS.Documentation.includes(addon)) groupedOptions.Documentation.push(option);
1326
1327
  else if (ADDON_GROUPS.Extensions.includes(addon)) groupedOptions.Extensions.push(option);
1328
+ else if (ADDON_GROUPS.Integrations.includes(addon)) groupedOptions.Integrations.push(option);
1327
1329
  else if (ADDON_GROUPS["AI Agents"].includes(addon)) groupedOptions["AI Agents"].push(option);
1328
1330
  else if (ADDON_GROUPS.TanStack.includes(addon)) groupedOptions.TanStack.push(option);
1329
1331
  }
@@ -1350,6 +1352,7 @@ async function getAddonsToAdd(frontend, existingAddons = [], auth) {
1350
1352
  Tooling: [],
1351
1353
  Documentation: [],
1352
1354
  Extensions: [],
1355
+ Integrations: [],
1353
1356
  "AI Agents": [],
1354
1357
  TanStack: []
1355
1358
  };
@@ -1365,6 +1368,7 @@ async function getAddonsToAdd(frontend, existingAddons = [], auth) {
1365
1368
  if (ADDON_GROUPS.Tooling.includes(addon)) groupedOptions.Tooling.push(option);
1366
1369
  else if (ADDON_GROUPS.Documentation.includes(addon)) groupedOptions.Documentation.push(option);
1367
1370
  else if (ADDON_GROUPS.Extensions.includes(addon)) groupedOptions.Extensions.push(option);
1371
+ else if (ADDON_GROUPS.Integrations.includes(addon)) groupedOptions.Integrations.push(option);
1368
1372
  else if (ADDON_GROUPS["AI Agents"].includes(addon)) groupedOptions["AI Agents"].push(option);
1369
1373
  else if (ADDON_GROUPS.TanStack.includes(addon)) groupedOptions.TanStack.push(option);
1370
1374
  }
@@ -1413,6 +1417,7 @@ async function writeBtsConfig(projectConfig) {
1413
1417
  forms: projectConfig.forms,
1414
1418
  testing: projectConfig.testing,
1415
1419
  packageManager: projectConfig.packageManager,
1420
+ versionChannel: projectConfig.versionChannel,
1416
1421
  dbSetup: projectConfig.dbSetup,
1417
1422
  api: projectConfig.api,
1418
1423
  webDeploy: projectConfig.webDeploy,
@@ -1472,6 +1477,7 @@ async function writeBtsConfig(projectConfig) {
1472
1477
  forms: btsConfig.forms,
1473
1478
  testing: btsConfig.testing,
1474
1479
  packageManager: btsConfig.packageManager,
1480
+ versionChannel: btsConfig.versionChannel,
1475
1481
  dbSetup: btsConfig.dbSetup,
1476
1482
  api: btsConfig.api,
1477
1483
  webDeploy: btsConfig.webDeploy,
@@ -1558,6 +1564,166 @@ async function updateBtsConfig(projectDir, updates) {
1558
1564
  } catch {}
1559
1565
  }
1560
1566
 
1567
+ //#endregion
1568
+ //#region src/utils/dependency-version-channel.ts
1569
+ const VERSION_CACHE = /* @__PURE__ */ new Map();
1570
+ const PRERELEASE_TAG_PRIORITY = [
1571
+ "beta",
1572
+ "next",
1573
+ "rc",
1574
+ "canary",
1575
+ "alpha"
1576
+ ];
1577
+ const REGISTRY_FETCH_TIMEOUT_MS = 1e4;
1578
+ const REGISTRY_CONCURRENCY = 10;
1579
+ function mapWithConcurrency(items, fn, concurrency) {
1580
+ const results = Array.from({ length: items.length });
1581
+ let index = 0;
1582
+ async function worker() {
1583
+ while (index < items.length) {
1584
+ const i = index++;
1585
+ results[i] = await fn(items[i], i);
1586
+ }
1587
+ }
1588
+ return Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker())).then(() => results);
1589
+ }
1590
+ function parseVersion(value) {
1591
+ const normalized = value.replace(/^[^\d]*/, "");
1592
+ const dashIdx = normalized.indexOf("-");
1593
+ const base = dashIdx === -1 ? normalized : normalized.slice(0, dashIdx);
1594
+ const prerelease = dashIdx === -1 ? "" : normalized.slice(dashIdx + 1);
1595
+ const [major = "0", minor = "0", patch = "0"] = base.split(".");
1596
+ return {
1597
+ major: Number(major) || 0,
1598
+ minor: Number(minor) || 0,
1599
+ patch: Number(patch) || 0,
1600
+ prerelease: prerelease ? prerelease.split(/[.-]/).map((part) => /^\d+$/.test(part) ? Number(part) : part) : []
1601
+ };
1602
+ }
1603
+ function compareVersions(a, b) {
1604
+ const left = parseVersion(a);
1605
+ const right = parseVersion(b);
1606
+ if (left.major !== right.major) return left.major - right.major;
1607
+ if (left.minor !== right.minor) return left.minor - right.minor;
1608
+ if (left.patch !== right.patch) return left.patch - right.patch;
1609
+ if (left.prerelease.length === 0 && right.prerelease.length === 0) return 0;
1610
+ if (left.prerelease.length === 0) return 1;
1611
+ if (right.prerelease.length === 0) return -1;
1612
+ const maxLength = Math.max(left.prerelease.length, right.prerelease.length);
1613
+ for (let index = 0; index < maxLength; index++) {
1614
+ const leftPart = left.prerelease[index];
1615
+ const rightPart = right.prerelease[index];
1616
+ if (leftPart === void 0) return -1;
1617
+ if (rightPart === void 0) return 1;
1618
+ if (leftPart === rightPart) continue;
1619
+ if (typeof leftPart === "number" && typeof rightPart === "number") return leftPart - rightPart;
1620
+ if (typeof leftPart === "number") return -1;
1621
+ if (typeof rightPart === "number") return 1;
1622
+ return leftPart.localeCompare(rightPart);
1623
+ }
1624
+ return 0;
1625
+ }
1626
+ function isPrerelease(version) {
1627
+ return /-(alpha|beta|rc|next|canary)/i.test(version);
1628
+ }
1629
+ function getVersionPrefix(version) {
1630
+ return version.match(/^[^\d]*/)?.[0] ?? "";
1631
+ }
1632
+ function applyVersionPrefix(currentVersion, resolvedVersion) {
1633
+ return `${getVersionPrefix(currentVersion)}${resolvedVersion}`;
1634
+ }
1635
+ function isRegistrySemverSpec(version) {
1636
+ return /^[~^]?\d/.test(version);
1637
+ }
1638
+ async function fetchPackageInfo(packageName) {
1639
+ const cached = VERSION_CACHE.get(packageName);
1640
+ if (cached) return cached;
1641
+ const encodedName = encodeURIComponent(packageName).replace("%40", "@");
1642
+ const response = await fetch(`https://registry.npmjs.org/${encodedName}`, {
1643
+ headers: { Accept: "application/vnd.npm.install-v1+json" },
1644
+ signal: AbortSignal.timeout(REGISTRY_FETCH_TIMEOUT_MS)
1645
+ });
1646
+ if (!response.ok) throw new Error(`Package ${packageName} not found (${response.status})`);
1647
+ const raw = await response.json();
1648
+ const data = {
1649
+ "dist-tags": raw["dist-tags"],
1650
+ versions: raw["versions"]
1651
+ };
1652
+ VERSION_CACHE.set(packageName, data);
1653
+ return data;
1654
+ }
1655
+ function selectRegistryVersionForChannel(packageInfo, channel) {
1656
+ const tags = packageInfo["dist-tags"] ?? {};
1657
+ if (channel === "latest") return tags.latest ?? null;
1658
+ for (const tag of PRERELEASE_TAG_PRIORITY) if (tags[tag]) return tags[tag];
1659
+ const prereleases = Object.keys(packageInfo.versions ?? {}).filter(isPrerelease);
1660
+ if (prereleases.length > 0) return prereleases.sort((left, right) => compareVersions(right, left))[0] ?? null;
1661
+ return tags.latest ?? null;
1662
+ }
1663
+ async function resolveRegistryVersion(packageName, channel) {
1664
+ const version = selectRegistryVersionForChannel(await fetchPackageInfo(packageName), channel);
1665
+ if (!version) throw new Error(`No ${channel} version available for ${packageName}`);
1666
+ return version;
1667
+ }
1668
+ async function collectPackageJsonPaths$1(projectDir) {
1669
+ const results = [];
1670
+ async function walk(currentDir) {
1671
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
1672
+ for (const entry of entries) {
1673
+ if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".turbo") continue;
1674
+ const fullPath = path.join(currentDir, entry.name);
1675
+ if (entry.isDirectory()) {
1676
+ await walk(fullPath);
1677
+ continue;
1678
+ }
1679
+ if (entry.isFile() && entry.name === "package.json") results.push(fullPath);
1680
+ }
1681
+ }
1682
+ await walk(projectDir);
1683
+ return results.sort();
1684
+ }
1685
+ async function applyDependencyVersionChannel(projectDir, channel) {
1686
+ if (channel === "stable") return;
1687
+ const packageJsonPaths = await collectPackageJsonPaths$1(projectDir);
1688
+ if (packageJsonPaths.length === 0) return;
1689
+ const packageNames = /* @__PURE__ */ new Set();
1690
+ for (const packageJsonPath of packageJsonPaths) {
1691
+ const packageJson = await fs.readJson(packageJsonPath);
1692
+ for (const [depName, depVersion] of Object.entries(packageJson.dependencies ?? {})) if (typeof depVersion === "string" && isRegistrySemverSpec(depVersion)) packageNames.add(depName);
1693
+ for (const [depName, depVersion] of Object.entries(packageJson.devDependencies ?? {})) if (typeof depVersion === "string" && isRegistrySemverSpec(depVersion)) packageNames.add(depName);
1694
+ }
1695
+ if (packageNames.size === 0) return;
1696
+ const resolvedVersions = /* @__PURE__ */ new Map();
1697
+ await mapWithConcurrency([...packageNames], async (packageName) => {
1698
+ try {
1699
+ const resolvedVersion = await resolveRegistryVersion(packageName, channel);
1700
+ resolvedVersions.set(packageName, resolvedVersion);
1701
+ } catch (error) {
1702
+ log.warn(`Failed to resolve ${channel} version for ${packageName}: ${error instanceof Error ? error.message : String(error)}`);
1703
+ }
1704
+ }, REGISTRY_CONCURRENCY);
1705
+ if (resolvedVersions.size === 0) return;
1706
+ for (const packageJsonPath of packageJsonPaths) {
1707
+ const packageJson = await fs.readJson(packageJsonPath);
1708
+ let changed = false;
1709
+ for (const sectionName of ["dependencies", "devDependencies"]) {
1710
+ const section = packageJson[sectionName];
1711
+ if (!section) continue;
1712
+ for (const [packageName, currentVersion] of Object.entries(section)) {
1713
+ if (!isRegistrySemverSpec(currentVersion)) continue;
1714
+ const resolvedVersion = resolvedVersions.get(packageName);
1715
+ if (!resolvedVersion) continue;
1716
+ const nextVersion = applyVersionPrefix(currentVersion, resolvedVersion);
1717
+ if (nextVersion !== currentVersion) {
1718
+ section[packageName] = nextVersion;
1719
+ changed = true;
1720
+ }
1721
+ }
1722
+ }
1723
+ if (changed) await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
1724
+ }
1725
+ }
1726
+
1561
1727
  //#endregion
1562
1728
  //#region src/utils/add-package-deps.ts
1563
1729
  const addPackageDependency = async (opts) => {
@@ -2946,6 +3112,7 @@ async function addHandlerInternal(input) {
2946
3112
  config
2947
3113
  }, projectDir);
2948
3114
  await setupAddons(config);
3115
+ await applyDependencyVersionChannel(projectDir, config.versionChannel);
2949
3116
  const configUpdates = { addons: [...new Set([...existingAddons, ...addonsToAdd])] };
2950
3117
  if (input.webDeploy !== void 0) configUpdates.webDeploy = input.webDeploy;
2951
3118
  if (input.serverDeploy !== void 0) configUpdates.serverDeploy = input.serverDeploy;
@@ -5039,6 +5206,12 @@ const ICON_LIBRARY_OPTIONS = [
5039
5206
  }
5040
5207
  ];
5041
5208
  const COLOR_THEME_OPTIONS = [
5209
+ {
5210
+ value: "neutral",
5211
+ label: "── Neutrals ──",
5212
+ hint: "",
5213
+ disabled: true
5214
+ },
5042
5215
  {
5043
5216
  value: "neutral",
5044
5217
  label: "Neutral",
@@ -5059,20 +5232,52 @@ const COLOR_THEME_OPTIONS = [
5059
5232
  label: "Gray",
5060
5233
  hint: "Blue-tinted neutral"
5061
5234
  },
5235
+ {
5236
+ value: "neutral",
5237
+ label: "── Cool ──",
5238
+ hint: "",
5239
+ disabled: true
5240
+ },
5062
5241
  {
5063
5242
  value: "blue",
5064
5243
  label: "Blue",
5065
5244
  hint: "Trust and reliability"
5066
5245
  },
5246
+ {
5247
+ value: "indigo",
5248
+ label: "Indigo",
5249
+ hint: "Deep and focused"
5250
+ },
5067
5251
  {
5068
5252
  value: "violet",
5069
5253
  label: "Violet",
5070
5254
  hint: "Creative and modern"
5071
5255
  },
5072
5256
  {
5073
- value: "green",
5074
- label: "Green",
5075
- hint: "Growth and success"
5257
+ value: "purple",
5258
+ label: "Purple",
5259
+ hint: "Royal and elegant"
5260
+ },
5261
+ {
5262
+ value: "cyan",
5263
+ label: "Cyan",
5264
+ hint: "Cool and refreshing"
5265
+ },
5266
+ {
5267
+ value: "sky",
5268
+ label: "Sky",
5269
+ hint: "Light and airy"
5270
+ },
5271
+ {
5272
+ value: "teal",
5273
+ label: "Teal",
5274
+ hint: "Calm and sophisticated"
5275
+ },
5276
+ {
5277
+ value: "neutral",
5278
+ label: "── Warm ──",
5279
+ hint: "",
5280
+ disabled: true
5076
5281
  },
5077
5282
  {
5078
5283
  value: "red",
@@ -5104,36 +5309,16 @@ const COLOR_THEME_OPTIONS = [
5104
5309
  label: "Lime",
5105
5310
  hint: "Fresh and lively"
5106
5311
  },
5312
+ {
5313
+ value: "green",
5314
+ label: "Green",
5315
+ hint: "Growth and success"
5316
+ },
5107
5317
  {
5108
5318
  value: "emerald",
5109
5319
  label: "Emerald",
5110
5320
  hint: "Rich and luxurious"
5111
5321
  },
5112
- {
5113
- value: "teal",
5114
- label: "Teal",
5115
- hint: "Calm and sophisticated"
5116
- },
5117
- {
5118
- value: "cyan",
5119
- label: "Cyan",
5120
- hint: "Cool and refreshing"
5121
- },
5122
- {
5123
- value: "sky",
5124
- label: "Sky",
5125
- hint: "Light and airy"
5126
- },
5127
- {
5128
- value: "indigo",
5129
- label: "Indigo",
5130
- hint: "Deep and focused"
5131
- },
5132
- {
5133
- value: "purple",
5134
- label: "Purple",
5135
- hint: "Royal and elegant"
5136
- },
5137
5322
  {
5138
5323
  value: "fuchsia",
5139
5324
  label: "Fuchsia",
@@ -5985,6 +6170,35 @@ async function getProjectName(initialName) {
5985
6170
  return projectPath;
5986
6171
  }
5987
6172
 
6173
+ //#endregion
6174
+ //#region src/prompts/version-channel.ts
6175
+ async function getVersionChannelChoice(versionChannel) {
6176
+ if (versionChannel !== void 0) return versionChannel;
6177
+ const response = await navigableSelect({
6178
+ message: "Choose dependency version channel",
6179
+ options: [
6180
+ {
6181
+ value: "stable",
6182
+ label: "Stable",
6183
+ hint: "Use Better Fullstack's curated pinned versions"
6184
+ },
6185
+ {
6186
+ value: "latest",
6187
+ label: "Latest",
6188
+ hint: "Resolve current npm latest tags during scaffolding"
6189
+ },
6190
+ {
6191
+ value: "beta",
6192
+ label: "Beta",
6193
+ hint: "Prefer npm beta/next prerelease tags when available"
6194
+ }
6195
+ ],
6196
+ initialValue: "stable"
6197
+ });
6198
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
6199
+ return response;
6200
+ }
6201
+
5988
6202
  //#endregion
5989
6203
  //#region src/utils/telemetry.ts
5990
6204
  /**
@@ -6077,6 +6291,7 @@ function displayConfig(config) {
6077
6291
  configDisplay.push(`${pc.blue("Git Init:")} ${gitText}`);
6078
6292
  }
6079
6293
  if (config.packageManager !== void 0) configDisplay.push(`${pc.blue("Package Manager:")} ${String(config.packageManager)}`);
6294
+ if (config.versionChannel !== void 0) configDisplay.push(`${pc.blue("Version Channel:")} ${String(config.versionChannel)}`);
6080
6295
  if (config.install !== void 0) {
6081
6296
  const installText = typeof config.install === "boolean" ? config.install ? "Yes" : "No" : String(config.install);
6082
6297
  configDisplay.push(`${pc.blue("Install Dependencies:")} ${installText}`);
@@ -6109,6 +6324,7 @@ function appendCommonFlags(flags, config) {
6109
6324
  else flags.push("--ai-docs none");
6110
6325
  flags.push(config.git ? "--git" : "--no-git");
6111
6326
  flags.push(`--package-manager ${config.packageManager}`);
6327
+ if (config.versionChannel !== "stable") flags.push(`--version-channel ${config.versionChannel}`);
6112
6328
  flags.push(config.install ? "--install" : "--no-install");
6113
6329
  }
6114
6330
  function appendSharedNonTypeScriptFlags(flags, config) {
@@ -6432,6 +6648,7 @@ function processFlags(options, projectName) {
6432
6648
  if (options.runtime) config.runtime = options.runtime;
6433
6649
  if (options.dbSetup) config.dbSetup = options.dbSetup;
6434
6650
  if (options.packageManager) config.packageManager = options.packageManager;
6651
+ if (options.versionChannel) config.versionChannel = options.versionChannel;
6435
6652
  if (options.webDeploy) config.webDeploy = options.webDeploy;
6436
6653
  if (options.serverDeploy) config.serverDeploy = options.serverDeploy;
6437
6654
  const derivedName = deriveProjectName(projectName, options.projectDirectory);
@@ -6789,6 +7006,15 @@ function validateDatabaseSetup(config, providedFlags) {
6789
7006
  function validateEcosystemAuthCompatibility(config, providedFlags) {
6790
7007
  const auth = config.auth;
6791
7008
  if (!auth || auth === "none") return;
7009
+ if (auth === "better-auth" && config.orm && [
7010
+ "typeorm",
7011
+ "sequelize",
7012
+ "mikroorm"
7013
+ ].includes(config.orm)) {
7014
+ config.auth = "none";
7015
+ if (providedFlags?.has("auth") && !isSilent()) consola.warn(pc.yellow(`Unsupported auth selection '${auth}' with ${config.orm}: no Better Auth adapter exists. Falling back to '--auth none'.`));
7016
+ return;
7017
+ }
6792
7018
  const normalized = (0, types_exports.normalizeCapabilitySelection)("auth", {
6793
7019
  ecosystem: config.ecosystem,
6794
7020
  backend: config.backend,
@@ -8321,7 +8547,7 @@ async function displayPostInstallInstructions(config) {
8321
8547
  let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
8322
8548
  let stepCounter = 2;
8323
8549
  if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} ${packageManager} install\n`;
8324
- if (hasFresh) output += `${pc.yellow("NOTE:")} Fresh projects require ${pc.white("deno")} on your PATH\n`;
8550
+ if (hasFresh) output += `${pc.yellow("NOTE:")} Fresh projects require ${pc.white("deno")} on your PATH.\n Install: ${pc.underline("https://docs.deno.com/runtime/getting_started/installation/")}\n`;
8325
8551
  if (database === "sqlite" && dbSetup !== "d1") output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} db:local\n${pc.dim(" (optional - starts local SQLite database)")}\n`;
8326
8552
  if (isConvex) {
8327
8553
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup\n${pc.dim(" (this will guide you through Convex project setup)")}\n`;
@@ -8409,7 +8635,7 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
8409
8635
  }
8410
8636
  if (dbSetup === "turso" && orm === "prisma") instructions.push(`${pc.yellow("NOTE:")} Follow Turso's Prisma guide for migrations via the Turso CLI:\n https://docs.turso.tech/sdk/ts/orm/prisma`);
8411
8637
  if (orm === "prisma") {
8412
- if (database === "mongodb" && dbSetup === "docker") instructions.push(`${pc.yellow("WARNING:")} Prisma + MongoDB + Docker combination\n may not work.`);
8638
+ if (database === "mongodb" && dbSetup === "docker") instructions.push(`${pc.yellow("WARNING:")} Prisma + MongoDB + Docker: this combination has known issues.\n Consider using MongoDB Atlas (${pc.underline("https://www.mongodb.com/atlas")}) for reliable Prisma + MongoDB support.`);
8413
8639
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
8414
8640
  if (!(dbSetup === "d1" && serverDeploy === "cloudflare")) {
8415
8641
  instructions.push(`${pc.cyan("•")} Generate Prisma Client: ${`${runCmd} db:generate`}`);
@@ -8607,6 +8833,7 @@ async function createProject(options, cliInput = {}) {
8607
8833
  await setPackageManagerVersion(projectDir, options.packageManager);
8608
8834
  if (!isConvex && options.database !== "none") await setupDatabase(options, cliInput);
8609
8835
  if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
8836
+ await applyDependencyVersionChannel(projectDir, options.versionChannel);
8610
8837
  await writeBtsConfig(options);
8611
8838
  await formatProject(projectDir);
8612
8839
  if (!isSilent()) log.success("Project template successfully scaffolded!");
@@ -8651,6 +8878,10 @@ async function setPackageManagerVersion(projectDir, packageManager) {
8651
8878
 
8652
8879
  //#endregion
8653
8880
  //#region src/helpers/core/command-handlers.ts
8881
+ function shouldPromptForVersionChannel(input) {
8882
+ if (input.yes || input.versionChannel !== void 0 || isSilent()) return false;
8883
+ return process.stdin.isTTY === true && process.stdout.isTTY === true && process.env.CI !== "true";
8884
+ }
8654
8885
  async function createProjectHandler(input, options = {}) {
8655
8886
  const { silent = false } = options;
8656
8887
  return runWithContextAsync({ silent }, async () => {
@@ -8672,6 +8903,7 @@ async function createProjectHandler(input, options = {}) {
8672
8903
  }
8673
8904
  currentPathInput = defaultName;
8674
8905
  } else currentPathInput = await getProjectName(input.projectName);
8906
+ const versionChannel = shouldPromptForVersionChannel(input) ? await getVersionChannelChoice() : input.versionChannel ?? "stable";
8675
8907
  let finalPathInput;
8676
8908
  let shouldClearDirectory;
8677
8909
  try {
@@ -8707,6 +8939,7 @@ async function createProjectHandler(input, options = {}) {
8707
8939
  effect: "none",
8708
8940
  git: false,
8709
8941
  packageManager: "npm",
8942
+ versionChannel: "stable",
8710
8943
  install: false,
8711
8944
  dbSetup: "none",
8712
8945
  api: "none",
@@ -8791,7 +9024,8 @@ async function createProjectHandler(input, options = {}) {
8791
9024
  ...flagConfig,
8792
9025
  projectName: finalBaseName,
8793
9026
  projectDir: finalResolvedPath,
8794
- relativePath: finalPathInput
9027
+ relativePath: finalPathInput,
9028
+ versionChannel
8795
9029
  };
8796
9030
  validateConfigCompatibility(config, providedFlags, cliInput);
8797
9031
  const yesPreflight = validatePreflightConfig(config);
@@ -8808,7 +9042,10 @@ async function createProjectHandler(input, options = {}) {
8808
9042
  log.message(displayConfig(otherFlags));
8809
9043
  log.message("");
8810
9044
  }
8811
- config = await gatherConfig(flagConfig, finalBaseName, finalResolvedPath, finalPathInput);
9045
+ config = {
9046
+ ...await gatherConfig(flagConfig, finalBaseName, finalResolvedPath, finalPathInput),
9047
+ versionChannel
9048
+ };
8812
9049
  }
8813
9050
  const preflight = validatePreflightConfig(config);
8814
9051
  if (preflight.hasWarnings && !isSilent()) displayPreflightWarnings(preflight);
@@ -9002,6 +9239,7 @@ const router = os.router({
9002
9239
  git: z.boolean().optional(),
9003
9240
  packageManager: types_exports.PackageManagerSchema.optional(),
9004
9241
  install: z.boolean().optional(),
9242
+ versionChannel: types_exports.VersionChannelSchema.optional().describe("Dependency version channel (stable, latest, beta)"),
9005
9243
  dbSetup: types_exports.DatabaseSetupSchema.optional(),
9006
9244
  backend: types_exports.BackendSchema.optional(),
9007
9245
  runtime: types_exports.RuntimeSchema.optional(),
@@ -9065,7 +9303,7 @@ const router = os.router({
9065
9303
  log.message(`Please visit ${DOCS_URL}`);
9066
9304
  }
9067
9305
  }),
9068
- builder: os.meta({ description: "Open the web-based stack builder" }).handler(async () => {
9306
+ builder: os.meta({ description: "Open the interactive web-based stack builder at better-fullstack.dev" }).handler(async () => {
9069
9307
  const BUILDER_URL = "https://better-fullstack-web.vercel.app/new";
9070
9308
  try {
9071
9309
  await openUrl(BUILDER_URL);
@@ -9074,7 +9312,7 @@ const router = os.router({
9074
9312
  log.message(`Please visit ${BUILDER_URL}`);
9075
9313
  }
9076
9314
  }),
9077
- add: os.meta({ description: "Add addons to an existing Better Fullstack project" }).input(z.object({
9315
+ add: os.meta({ description: "Add addons or deployment targets to an existing Better Fullstack project" }).input(z.object({
9078
9316
  addons: z.array(types_exports.AddonsSchema).optional().describe("Addons to add"),
9079
9317
  webDeploy: types_exports.WebDeploySchema.optional().describe("Web deployment option to set"),
9080
9318
  serverDeploy: types_exports.ServerDeploySchema.optional().describe("Server deployment option to set"),
@@ -9084,7 +9322,7 @@ const router = os.router({
9084
9322
  })).handler(async ({ input }) => {
9085
9323
  await addHandler(input);
9086
9324
  }),
9087
- history: os.meta({ description: "Show project creation history" }).input(z.object({
9325
+ history: os.meta({ description: "Show history of scaffolded projects with reproducible commands" }).input(z.object({
9088
9326
  limit: z.number().optional().default(10).describe("Number of entries to show"),
9089
9327
  clear: z.boolean().optional().default(false).describe("Clear all history"),
9090
9328
  json: z.boolean().optional().default(false).describe("Output as JSON")
@@ -9226,6 +9464,7 @@ async function createVirtual(options) {
9226
9464
  effect: options.effect || "none",
9227
9465
  git: options.git ?? false,
9228
9466
  packageManager: options.packageManager || "bun",
9467
+ versionChannel: options.versionChannel || "stable",
9229
9468
  install: false,
9230
9469
  dbSetup: options.dbSetup || "none",
9231
9470
  api: options.api || "trpc",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-fullstack",
3
- "version": "1.4.16",
3
+ "version": "1.5.1",
4
4
  "description": "Scaffold production-ready fullstack apps in seconds. Pick your stack from 270+ options — the CLI wires everything together.",
5
5
  "keywords": [
6
6
  "angular",
@@ -108,17 +108,17 @@
108
108
  "prepublishOnly": "npm run build"
109
109
  },
110
110
  "dependencies": {
111
- "@better-fullstack/template-generator": "^1.4.16",
112
- "@better-fullstack/types": "^1.4.16",
111
+ "@better-fullstack/template-generator": "^1.5.1",
112
+ "@better-fullstack/types": "^1.5.1",
113
113
  "@clack/core": "^0.5.0",
114
114
  "@clack/prompts": "^1.1.0",
115
- "@orpc/server": "^1.13.9",
115
+ "@orpc/server": "^1.13.13",
116
116
  "consola": "^3.4.2",
117
117
  "env-paths": "^4.0.0",
118
118
  "execa": "^9.6.1",
119
119
  "fs-extra": "^11.3.4",
120
120
  "gradient-string": "^3.0.0",
121
- "handlebars": "^4.7.8",
121
+ "handlebars": "^4.7.9",
122
122
  "jsonc-parser": "^3.3.1",
123
123
  "oxfmt": "^0.19.0",
124
124
  "picocolors": "^1.1.1",