create-better-t-stack 3.2.4 → 3.2.6-canary.46b928a7

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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { createBtsCli } from "./src-BcBhk2Os.js";
2
+ import { n as createBtsCli } from "./src-D60cASJW.js";
3
3
 
4
4
  //#region src/cli.ts
5
5
  createBtsCli().run();
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { builder, createBtsCli, docs, init, router, sponsors } from "./src-BcBhk2Os.js";
2
+ import { a as router, i as init, n as createBtsCli, o as sponsors, r as docs, t as builder } from "./src-D60cASJW.js";
3
3
 
4
4
  export { builder, createBtsCli, docs, init, router, sponsors };
@@ -66,7 +66,7 @@ const dependencyVersionMap = {
66
66
  "@better-auth/expo": "^1.3.13",
67
67
  "@clerk/nextjs": "^6.31.5",
68
68
  "@clerk/clerk-react": "^5.45.0",
69
- "@clerk/tanstack-react-start": "^0.25.1",
69
+ "@clerk/tanstack-react-start": "^0.26.3",
70
70
  "@clerk/clerk-expo": "^2.14.25",
71
71
  "drizzle-orm": "^0.44.2",
72
72
  "drizzle-kit": "^0.31.2",
@@ -334,11 +334,9 @@ function isWebFrontend(value) {
334
334
  return WEB_FRAMEWORKS.includes(value);
335
335
  }
336
336
  function splitFrontends(values = []) {
337
- const web = values.filter((f) => isWebFrontend(f));
338
- const native = values.filter((f) => f === "native-nativewind" || f === "native-unistyles");
339
337
  return {
340
- web,
341
- native
338
+ web: values.filter((f) => isWebFrontend(f)),
339
+ native: values.filter((f) => f === "native-nativewind" || f === "native-unistyles")
342
340
  };
343
341
  }
344
342
  function ensureSingleWebAndNative(frontends) {
@@ -411,13 +409,10 @@ function validateServerDeployRequiresBackend(serverDeploy, backend) {
411
409
  function validateAddonCompatibility(addon, frontend, _auth) {
412
410
  const compatibleFrontends = ADDON_COMPATIBILITY[addon];
413
411
  if (compatibleFrontends.length > 0) {
414
- if (!frontend.some((f) => compatibleFrontends.includes(f))) {
415
- const frontendList = compatibleFrontends.join(", ");
416
- return {
417
- isCompatible: false,
418
- reason: `${addon} addon requires one of these frontends: ${frontendList}`
419
- };
420
- }
412
+ if (!frontend.some((f) => compatibleFrontends.includes(f))) return {
413
+ isCompatible: false,
414
+ reason: `${addon} addon requires one of these frontends: ${compatibleFrontends.join(", ")}`
415
+ };
421
416
  }
422
417
  return { isCompatible: true };
423
418
  }
@@ -546,11 +541,10 @@ async function getAddonsChoice(addons, frontends, auth) {
546
541
  Object.keys(groupedOptions).forEach((group$1) => {
547
542
  if (groupedOptions[group$1].length === 0) delete groupedOptions[group$1];
548
543
  });
549
- const initialValues = DEFAULT_CONFIG.addons.filter((addonValue) => Object.values(groupedOptions).some((options) => options.some((opt) => opt.value === addonValue)));
550
544
  const response = await groupMultiselect({
551
545
  message: "Select addons",
552
546
  options: groupedOptions,
553
- initialValues,
547
+ initialValues: DEFAULT_CONFIG.addons.filter((addonValue) => Object.values(groupedOptions).some((options) => options.some((opt) => opt.value === addonValue))),
554
548
  required: false,
555
549
  selectableGroups: false
556
550
  });
@@ -924,46 +918,45 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
924
918
  if (isCancel(frontendTypes)) return exitCancelled("Operation cancelled");
925
919
  const result = [];
926
920
  if (frontendTypes.includes("web")) {
927
- const webOptions = [
928
- {
929
- value: "tanstack-router",
930
- label: "TanStack Router",
931
- hint: "Modern and scalable routing for React Applications"
932
- },
933
- {
934
- value: "react-router",
935
- label: "React Router",
936
- hint: "A user‑obsessed, standards‑focused, multi‑strategy router"
937
- },
938
- {
939
- value: "next",
940
- label: "Next.js",
941
- hint: "The React Framework for the Web"
942
- },
943
- {
944
- value: "nuxt",
945
- label: "Nuxt",
946
- hint: "The Progressive Web Framework for Vue.js"
947
- },
948
- {
949
- value: "svelte",
950
- label: "Svelte",
951
- hint: "web development for the rest of us"
952
- },
953
- {
954
- value: "solid",
955
- label: "Solid",
956
- hint: "Simple and performant reactivity for building user interfaces"
957
- },
958
- {
959
- value: "tanstack-start",
960
- label: "TanStack Start",
961
- hint: "SSR, Server Functions, API Routes and more with TanStack Router"
962
- }
963
- ].filter((option) => isFrontendAllowedWithBackend(option.value, backend, auth));
964
921
  const webFramework = await select({
965
922
  message: "Choose web",
966
- options: webOptions,
923
+ options: [
924
+ {
925
+ value: "tanstack-router",
926
+ label: "TanStack Router",
927
+ hint: "Modern and scalable routing for React Applications"
928
+ },
929
+ {
930
+ value: "react-router",
931
+ label: "React Router",
932
+ hint: "A user‑obsessed, standards‑focused, multi‑strategy router"
933
+ },
934
+ {
935
+ value: "next",
936
+ label: "Next.js",
937
+ hint: "The React Framework for the Web"
938
+ },
939
+ {
940
+ value: "nuxt",
941
+ label: "Nuxt",
942
+ hint: "The Progressive Web Framework for Vue.js"
943
+ },
944
+ {
945
+ value: "svelte",
946
+ label: "Svelte",
947
+ hint: "web development for the rest of us"
948
+ },
949
+ {
950
+ value: "solid",
951
+ label: "Solid",
952
+ hint: "Simple and performant reactivity for building user interfaces"
953
+ },
954
+ {
955
+ value: "tanstack-start",
956
+ label: "TanStack Start",
957
+ hint: "SSR, Server Functions, API Routes and more with TanStack Router"
958
+ }
959
+ ].filter((option) => isFrontendAllowedWithBackend(option.value, backend, auth)),
967
960
  initialValue: DEFAULT_CONFIG.frontend[0]
968
961
  });
969
962
  if (isCancel(webFramework)) return exitCancelled("Operation cancelled");
@@ -1036,10 +1029,9 @@ async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
1036
1029
  if (backend === "convex") return "none";
1037
1030
  if (!hasDatabase) return "none";
1038
1031
  if (orm !== void 0) return orm;
1039
- const options = [...database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [ormOptions.drizzle, ormOptions.prisma]];
1040
1032
  const response = await select({
1041
1033
  message: "Select ORM",
1042
- options,
1034
+ options: [...database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [ormOptions.drizzle, ormOptions.prisma]],
1043
1035
  initialValue: database === "mongodb" ? "prisma" : runtime === "workers" ? "drizzle" : DEFAULT_CONFIG.orm
1044
1036
  });
1045
1037
  if (isCancel(response)) return exitCancelled("Operation cancelled");
@@ -1050,7 +1042,6 @@ async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
1050
1042
  //#region src/prompts/package-manager.ts
1051
1043
  async function getPackageManagerChoice(packageManager) {
1052
1044
  if (packageManager !== void 0) return packageManager;
1053
- const detectedPackageManager = getUserPkgManager();
1054
1045
  const response = await select({
1055
1046
  message: "Choose package manager",
1056
1047
  options: [
@@ -1070,7 +1061,7 @@ async function getPackageManagerChoice(packageManager) {
1070
1061
  hint: "All-in-one JavaScript runtime & toolkit"
1071
1062
  }
1072
1063
  ],
1073
- initialValue: detectedPackageManager
1064
+ initialValue: getUserPkgManager()
1074
1065
  });
1075
1066
  if (isCancel(response)) return exitCancelled("Operation cancelled");
1076
1067
  return response;
@@ -1219,21 +1210,20 @@ function getDeploymentDisplay(deployment) {
1219
1210
  async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []) {
1220
1211
  if (deployment !== void 0) return deployment;
1221
1212
  if (!hasWebFrontend(frontend)) return "none";
1222
- const options = [
1223
- "wrangler",
1224
- "alchemy",
1225
- "none"
1226
- ].map((deploy) => {
1227
- const { label, hint } = getDeploymentDisplay(deploy);
1228
- return {
1229
- value: deploy,
1230
- label,
1231
- hint
1232
- };
1233
- });
1234
1213
  const response = await select({
1235
1214
  message: "Select web deployment",
1236
- options,
1215
+ options: [
1216
+ "wrangler",
1217
+ "alchemy",
1218
+ "none"
1219
+ ].map((deploy) => {
1220
+ const { label, hint } = getDeploymentDisplay(deploy);
1221
+ return {
1222
+ value: deploy,
1223
+ label,
1224
+ hint
1225
+ };
1226
+ }),
1237
1227
  initialValue: DEFAULT_CONFIG.webDeploy
1238
1228
  });
1239
1229
  if (isCancel(response)) return exitCancelled("Operation cancelled");
@@ -1333,10 +1323,8 @@ function validateDirectoryName(name) {
1333
1323
  async function getProjectName(initialName) {
1334
1324
  if (initialName) {
1335
1325
  if (initialName === ".") return initialName;
1336
- const finalDirName = path.basename(initialName);
1337
- if (!validateDirectoryName(finalDirName)) {
1338
- const projectDir = path.resolve(process.cwd(), initialName);
1339
- if (isPathWithinCwd(projectDir)) return initialName;
1326
+ if (!validateDirectoryName(path.basename(initialName))) {
1327
+ if (isPathWithinCwd(path.resolve(process.cwd(), initialName))) return initialName;
1340
1328
  consola.error(pc.red("Project path must be within current directory"));
1341
1329
  }
1342
1330
  }
@@ -1356,12 +1344,10 @@ async function getProjectName(initialName) {
1356
1344
  defaultValue: defaultName,
1357
1345
  validate: (value) => {
1358
1346
  const nameToUse = String(value ?? "").trim() || defaultName;
1359
- const finalDirName = path.basename(nameToUse);
1360
- const validationError = validateDirectoryName(finalDirName);
1347
+ const validationError = validateDirectoryName(path.basename(nameToUse));
1361
1348
  if (validationError) return validationError;
1362
1349
  if (nameToUse !== ".") {
1363
- const projectDir = path.resolve(process.cwd(), nameToUse);
1364
- if (!isPathWithinCwd(projectDir)) return "Project path must be within current directory";
1350
+ if (!isPathWithinCwd(path.resolve(process.cwd(), nameToUse))) return "Project path must be within current directory";
1365
1351
  }
1366
1352
  }
1367
1353
  });
@@ -1389,7 +1375,7 @@ const getLatestCLIVersion = () => {
1389
1375
  */
1390
1376
  function isTelemetryEnabled() {
1391
1377
  const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
1392
- const BTS_TELEMETRY = "1";
1378
+ const BTS_TELEMETRY = "0";
1393
1379
  if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
1394
1380
  if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
1395
1381
  return true;
@@ -1397,8 +1383,8 @@ function isTelemetryEnabled() {
1397
1383
 
1398
1384
  //#endregion
1399
1385
  //#region src/utils/analytics.ts
1400
- const POSTHOG_API_KEY = "phc_8ZUxEwwfKMajJLvxz1daGd931dYbQrwKNficBmsdIrs";
1401
- const POSTHOG_HOST = "https://us.i.posthog.com";
1386
+ const POSTHOG_API_KEY = "random";
1387
+ const POSTHOG_HOST = "random";
1402
1388
  function generateSessionId() {
1403
1389
  const rand = Math.random().toString(36).slice(2);
1404
1390
  return `cli_${Date.now().toString(36)}${rand}`;
@@ -1552,11 +1538,9 @@ async function handleDirectoryConflict(currentPathInput, silent = false) {
1552
1538
  finalPathInput: currentPathInput,
1553
1539
  shouldClearDirectory: false
1554
1540
  };
1555
- case "rename": {
1541
+ case "rename":
1556
1542
  log.info("Please choose a different project name or path.");
1557
- const newPathInput = await getProjectName(void 0);
1558
- return await handleDirectoryConflict(newPathInput);
1559
- }
1543
+ return await handleDirectoryConflict(await getProjectName(void 0));
1560
1544
  case "cancel": return exitCancelled("Operation cancelled.");
1561
1545
  }
1562
1546
  }
@@ -1621,8 +1605,7 @@ const catppuccinTheme = {
1621
1605
  const renderTitle = () => {
1622
1606
  const terminalWidth = process.stdout.columns || 80;
1623
1607
  const titleLines = TITLE_TEXT.split("\n");
1624
- const titleWidth = Math.max(...titleLines.map((line) => line.length));
1625
- if (terminalWidth < titleWidth) console.log(gradient(Object.values(catppuccinTheme)).multiline(`
1608
+ if (terminalWidth < Math.max(...titleLines.map((line) => line.length))) console.log(gradient(Object.values(catppuccinTheme)).multiline(`
1626
1609
  ╔══════════════════╗
1627
1610
  ║ Better T Stack ║
1628
1611
  ╚══════════════════╝
@@ -1904,10 +1887,7 @@ function processAndValidateFlags(options, providedFlags, projectName) {
1904
1887
  return config;
1905
1888
  }
1906
1889
  function processProvidedFlagsWithoutValidation(options, projectName) {
1907
- if (!options.yolo) {
1908
- const providedFlags = getProvidedFlags(options);
1909
- validateYesFlagCombination(options, providedFlags);
1910
- }
1890
+ if (!options.yolo) validateYesFlagCombination(options, getProvidedFlags(options));
1911
1891
  const config = processFlags(options, projectName);
1912
1892
  const validatedProjectName = extractAndValidateProjectName(projectName, options.projectDirectory, true);
1913
1893
  if (validatedProjectName) config.projectName = validatedProjectName;
@@ -2088,8 +2068,7 @@ async function setupFumadocs(config) {
2088
2068
  initialValue: "next-mdx"
2089
2069
  });
2090
2070
  if (isCancel(template)) return exitCancelled("Operation cancelled");
2091
- const commandWithArgs = `create-fumadocs-app@latest fumadocs --template ${TEMPLATES[template].value} --src --no-install --pm ${packageManager} --no-eslint --no-git`;
2092
- const fumadocsInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2071
+ const fumadocsInitCommand = getPackageExecutionCommand(packageManager, `create-fumadocs-app@latest fumadocs --template ${TEMPLATES[template].value} --src --no-install --pm ${packageManager} --no-eslint --no-git`);
2093
2072
  const s = spinner();
2094
2073
  s.start("Setting up Fumadocs...");
2095
2074
  const appsDir = path.join(projectDir, "apps");
@@ -2176,8 +2155,7 @@ async function setupRuler(config) {
2176
2155
  const s = spinner();
2177
2156
  s.start("Applying rules with Ruler...");
2178
2157
  try {
2179
- const rulerApplyCmd = getPackageExecutionCommand(packageManager, `@intellectronica/ruler@latest apply --agents ${selectedEditors.join(",")} --local-only`);
2180
- await execa(rulerApplyCmd, {
2158
+ await execa(getPackageExecutionCommand(packageManager, `@intellectronica/ruler@latest apply --agents ${selectedEditors.join(",")} --local-only`), {
2181
2159
  cwd: projectDir,
2182
2160
  env: { CI: "true" },
2183
2161
  shell: true
@@ -2211,7 +2189,7 @@ async function setupStarlight(config) {
2211
2189
  const s = spinner();
2212
2190
  try {
2213
2191
  s.start("Setting up Starlight docs...");
2214
- const commandWithArgs = `create-astro@latest ${[
2192
+ const starlightInitCommand = getPackageExecutionCommand(packageManager, `create-astro@latest ${[
2215
2193
  "docs",
2216
2194
  "--template",
2217
2195
  "starlight",
@@ -2220,8 +2198,7 @@ async function setupStarlight(config) {
2220
2198
  "tailwind",
2221
2199
  "--no-git",
2222
2200
  "--skip-houston"
2223
- ].join(" ")}`;
2224
- const starlightInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2201
+ ].join(" ")}`);
2225
2202
  const appsDir = path.join(projectDir, "apps");
2226
2203
  await fs.ensureDir(appsDir);
2227
2204
  await execa(starlightInitCommand, {
@@ -2268,7 +2245,7 @@ async function setupTauri(config) {
2268
2245
  const hasNext = frontend.includes("next");
2269
2246
  const devUrl = hasReactRouter || hasSvelte ? "http://localhost:5173" : hasNext ? "http://localhost:3001" : "http://localhost:3001";
2270
2247
  const frontendDist = hasNuxt ? "../.output/public" : hasSvelte ? "../build" : hasNext ? "../.next" : hasReactRouter ? "../build/client" : "../dist";
2271
- const commandWithArgs = `@tauri-apps/cli@latest ${[
2248
+ await execa(getPackageExecutionCommand(packageManager, `@tauri-apps/cli@latest ${[
2272
2249
  "init",
2273
2250
  `--app-name=${path.basename(projectDir)}`,
2274
2251
  `--window-title=${path.basename(projectDir)}`,
@@ -2276,9 +2253,7 @@ async function setupTauri(config) {
2276
2253
  `--dev-url=${devUrl}`,
2277
2254
  `--before-dev-command="${packageManager} run dev"`,
2278
2255
  `--before-build-command="${packageManager} run build"`
2279
- ].join(" ")}`;
2280
- const tauriInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2281
- await execa(tauriInitCommand, {
2256
+ ].join(" ")}`), {
2282
2257
  cwd: clientPackageDir,
2283
2258
  env: { CI: "true" },
2284
2259
  shell: true
@@ -2350,8 +2325,7 @@ async function setupUltracite(config, hasHusky) {
2350
2325
  if (editors.length > 0) ultraciteArgs.push("--editors", ...editors);
2351
2326
  if (rules.length > 0) ultraciteArgs.push("--rules", ...rules);
2352
2327
  if (hasHusky) ultraciteArgs.push("--integrations", "husky", "lint-staged");
2353
- const commandWithArgs = `ultracite@latest ${ultraciteArgs.join(" ")} --skip-install`;
2354
- const ultraciteInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2328
+ const ultraciteInitCommand = getPackageExecutionCommand(packageManager, `ultracite@latest ${ultraciteArgs.join(" ")} --skip-install`);
2355
2329
  const s = spinner();
2356
2330
  s.start("Setting up Ultracite...");
2357
2331
  await execa(ultraciteInitCommand, {
@@ -2738,8 +2712,7 @@ async function processAndCopyFiles(sourcePattern, baseSourceDir, destDir, contex
2738
2712
  }
2739
2713
  }
2740
2714
  async function copyBaseTemplate(projectDir, context) {
2741
- const templateDir = path.join(PKG_ROOT, "templates/base");
2742
- await processAndCopyFiles(["**/*"], templateDir, projectDir, context);
2715
+ await processAndCopyFiles(["**/*"], path.join(PKG_ROOT, "templates/base"), projectDir, context);
2743
2716
  }
2744
2717
  async function setupFrontendTemplates(projectDir, context) {
2745
2718
  const hasReactWeb = context.frontend.some((f) => [
@@ -3191,8 +3164,7 @@ async function setupDeploymentTemplates(projectDir, context) {
3191
3164
  if (context.webDeploy === "alchemy" && (context.serverDeploy === "alchemy" || isBackendSelf)) {
3192
3165
  if (await fs.pathExists(alchemyTemplateSrc)) {
3193
3166
  const webAppDir = path.join(projectDir, "apps/web");
3194
- const destDir = isBackendSelf && await fs.pathExists(webAppDir) ? webAppDir : projectDir;
3195
- await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, destDir, context);
3167
+ await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, isBackendSelf && await fs.pathExists(webAppDir) ? webAppDir : projectDir, context);
3196
3168
  await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3197
3169
  }
3198
3170
  } else {
@@ -3289,16 +3261,14 @@ async function addAddonsToProject(input) {
3289
3261
  await setupAddonsTemplate(projectDir, config);
3290
3262
  await setupAddons(config, true);
3291
3263
  const currentAddons = detectedConfig.addons || [];
3292
- const mergedAddons = [...new Set([...currentAddons, ...input.addons])];
3293
- await updateBtsConfig(projectDir, { addons: mergedAddons });
3264
+ await updateBtsConfig(projectDir, { addons: [...new Set([...currentAddons, ...input.addons])] });
3294
3265
  if (config.install) await installDependencies({
3295
3266
  projectDir,
3296
3267
  packageManager: config.packageManager
3297
3268
  });
3298
3269
  else if (!input.suppressInstallMessage) log.info(pc.yellow(`Run ${pc.bold(`${config.packageManager} install`)} to install dependencies`));
3299
3270
  } catch (error) {
3300
- const message = error instanceof Error ? error.message : String(error);
3301
- exitWithError(`Error adding addons: ${message}`);
3271
+ exitWithError(`Error adding addons: ${error instanceof Error ? error.message : String(error)}`);
3302
3272
  }
3303
3273
  }
3304
3274
 
@@ -3342,8 +3312,7 @@ async function generateCloudflareWorkerTypes({ serverDir, packageManager }) {
3342
3312
  const s = spinner();
3343
3313
  try {
3344
3314
  s.start("Generating Cloudflare Workers types...");
3345
- const runCmd = getPackageExecutionCommand(packageManager, "wrangler types --env-interface CloudflareBindings");
3346
- await execa(runCmd, {
3315
+ await execa(getPackageExecutionCommand(packageManager, "wrangler types --env-interface CloudflareBindings"), {
3347
3316
  cwd: serverDir,
3348
3317
  shell: true
3349
3318
  });
@@ -4017,8 +3986,7 @@ async function addDeploymentToProject(input) {
4017
3986
  });
4018
3987
  else if (!input.suppressInstallMessage) log.info(pc.yellow(`Run ${pc.bold(`${config.packageManager} install`)} to install dependencies`));
4019
3988
  } catch (error) {
4020
- const message = error instanceof Error ? error.message : String(error);
4021
- exitWithError(`Error adding deployment: ${message}`);
3989
+ exitWithError(`Error adding deployment: ${error instanceof Error ? error.message : String(error)}`);
4022
3990
  }
4023
3991
  }
4024
3992
 
@@ -4337,6 +4305,10 @@ async function setupApi(config) {
4337
4305
  });
4338
4306
  }
4339
4307
  }
4308
+ if (config.auth === "better-auth" && (backend === "express" || backend === "fastify")) await addPackageDependency({
4309
+ dependencies: ["better-auth"],
4310
+ projectDir: apiPackageDir
4311
+ });
4340
4312
  if (webDirExists && apiDeps.web) await addPackageDependency({
4341
4313
  dependencies: apiDeps.web.dependencies,
4342
4314
  devDependencies: apiDeps.web.devDependencies,
@@ -4442,8 +4414,7 @@ async function setupBetterAuthPlugins(projectDir, config) {
4442
4414
  if (betterAuthCall) {
4443
4415
  const configObject = betterAuthCall.getArguments()[0];
4444
4416
  if (configObject && configObject.getKind() === SyntaxKind.ObjectLiteralExpression) {
4445
- const objLiteral = configObject.asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
4446
- const pluginsArray = ensureArrayProperty(objLiteral, "plugins");
4417
+ const pluginsArray = ensureArrayProperty(configObject.asKindOrThrow(SyntaxKind.ObjectLiteralExpression), "plugins");
4447
4418
  pluginsToAdd.forEach((plugin) => {
4448
4419
  pluginsArray.addElement(plugin);
4449
4420
  });
@@ -4681,11 +4652,9 @@ async function setupEnvironmentVariables(config) {
4681
4652
  const clientDir = path.join(projectDir, "apps/web");
4682
4653
  if (await fs.pathExists(clientDir)) {
4683
4654
  const baseVar = getClientServerVar(frontend, backend);
4684
- const envVarName = backend === "convex" ? getConvexVar(frontend) : baseVar.key;
4685
- const serverUrl = backend === "convex" ? "https://<YOUR_CONVEX_URL>" : baseVar.value;
4686
4655
  const clientVars = [{
4687
- key: envVarName,
4688
- value: serverUrl,
4656
+ key: backend === "convex" ? getConvexVar(frontend) : baseVar.key,
4657
+ value: backend === "convex" ? "https://<YOUR_CONVEX_URL>" : baseVar.value,
4689
4658
  condition: backend === "convex" ? true : baseVar.write
4690
4659
  }];
4691
4660
  if (backend === "convex" && auth === "clerk") {
@@ -4860,14 +4829,12 @@ ${hasWeb ? "# npx convex env set SITE_URL http://localhost:3001\n" : ""}
4860
4829
  } else if (await fs.pathExists(serverDir)) await addEnvVariablesToFile(path.join(serverDir, ".env"), serverVars);
4861
4830
  const isUnifiedAlchemy = webDeploy === "alchemy" && serverDeploy === "alchemy";
4862
4831
  const isIndividualAlchemy = webDeploy === "alchemy" || serverDeploy === "alchemy";
4863
- if (isUnifiedAlchemy) {
4864
- const rootEnvPath = path.join(projectDir, ".env");
4865
- await addEnvVariablesToFile(rootEnvPath, [{
4866
- key: "ALCHEMY_PASSWORD",
4867
- value: "please-change-this",
4868
- condition: true
4869
- }]);
4870
- } else if (isIndividualAlchemy) {
4832
+ if (isUnifiedAlchemy) await addEnvVariablesToFile(path.join(projectDir, ".env"), [{
4833
+ key: "ALCHEMY_PASSWORD",
4834
+ value: "please-change-this",
4835
+ condition: true
4836
+ }]);
4837
+ else if (isIndividualAlchemy) {
4871
4838
  if (webDeploy === "alchemy") {
4872
4839
  const webDir = path.join(projectDir, "apps/web");
4873
4840
  if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), [{
@@ -4929,10 +4896,9 @@ async function setupCloudflareD1(config) {
4929
4896
  try {
4930
4897
  await addEnvVariablesToFile(envPath, variables);
4931
4898
  } catch (_err) {}
4932
- const serverDir = path.join(projectDir, backend === "self" ? "apps/web" : "apps/server");
4933
4899
  await addPackageDependency({
4934
4900
  dependencies: ["@prisma/adapter-d1"],
4935
- projectDir: serverDir
4901
+ projectDir: path.join(projectDir, backend === "self" ? "apps/web" : "apps/server")
4936
4902
  });
4937
4903
  }
4938
4904
  }
@@ -4950,13 +4916,11 @@ async function setupDockerCompose(config) {
4950
4916
  }
4951
4917
  async function writeEnvFile$4(projectDir, database, projectName, backend) {
4952
4918
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
4953
- const envPath = path.join(projectDir, targetApp, ".env");
4954
- const variables = [{
4919
+ await addEnvVariablesToFile(path.join(projectDir, targetApp, ".env"), [{
4955
4920
  key: "DATABASE_URL",
4956
4921
  value: getDatabaseUrl(database, projectName),
4957
4922
  condition: true
4958
- }];
4959
- await addEnvVariablesToFile(envPath, variables);
4923
+ }]);
4960
4924
  }
4961
4925
  function getDatabaseUrl(database, projectName) {
4962
4926
  switch (database) {
@@ -5026,13 +4990,11 @@ async function initMongoDBAtlas(serverDir) {
5026
4990
  async function writeEnvFile$3(projectDir, backend, config) {
5027
4991
  try {
5028
4992
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
5029
- const envPath = path.join(projectDir, targetApp, ".env");
5030
- const variables = [{
4993
+ await addEnvVariablesToFile(path.join(projectDir, targetApp, ".env"), [{
5031
4994
  key: "DATABASE_URL",
5032
4995
  value: config?.connectionString ?? "mongodb://localhost:27017/mydb",
5033
4996
  condition: true
5034
- }];
5035
- await addEnvVariablesToFile(envPath, variables);
4997
+ }]);
5036
4998
  } catch (_error) {
5037
4999
  consola.error("Failed to update environment configuration");
5038
5000
  }
@@ -5155,8 +5117,7 @@ async function executeNeonCommand(packageManager, commandArgsString, spinnerText
5155
5117
  }
5156
5118
  async function createNeonProject(projectName, regionId, packageManager) {
5157
5119
  try {
5158
- const commandArgsString = `neonctl@latest projects create --name ${projectName} --region-id ${regionId} --output json`;
5159
- const { stdout } = await executeNeonCommand(packageManager, commandArgsString, `Creating Neon project "${projectName}"...`);
5120
+ const { stdout } = await executeNeonCommand(packageManager, `neonctl@latest projects create --name ${projectName} --region-id ${regionId} --output json`, `Creating Neon project "${projectName}"...`);
5160
5121
  const response = JSON.parse(stdout);
5161
5122
  if (response.project && response.connection_uris && response.connection_uris.length > 0) {
5162
5123
  const projectId = response.project.id;
@@ -5177,13 +5138,11 @@ async function createNeonProject(projectName, regionId, packageManager) {
5177
5138
  }
5178
5139
  async function writeEnvFile$2(projectDir, backend, config) {
5179
5140
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
5180
- const envPath = path.join(projectDir, targetApp, ".env");
5181
- const variables = [{
5141
+ await addEnvVariablesToFile(path.join(projectDir, targetApp, ".env"), [{
5182
5142
  key: "DATABASE_URL",
5183
5143
  value: config?.connectionString ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
5184
5144
  condition: true
5185
- }];
5186
- await addEnvVariablesToFile(envPath, variables);
5145
+ }]);
5187
5146
  return true;
5188
5147
  }
5189
5148
  async function setupWithNeonDb(projectDir, packageManager, backend) {
@@ -5193,8 +5152,7 @@ async function setupWithNeonDb(projectDir, packageManager, backend) {
5193
5152
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
5194
5153
  const targetDir = path.join(projectDir, targetApp);
5195
5154
  await fs.ensureDir(targetDir);
5196
- const packageCmd = getPackageExecutionCommand(packageManager, "neondb@latest --yes");
5197
- await execa(packageCmd, {
5155
+ await execa(getPackageExecutionCommand(packageManager, "neondb@latest --yes"), {
5198
5156
  shell: true,
5199
5157
  cwd: targetDir
5200
5158
  });
@@ -5412,8 +5370,7 @@ async function initPrismaDatabase(serverDir, packageManager) {
5412
5370
  const prismaDir = path.join(serverDir, "prisma");
5413
5371
  await fs.ensureDir(prismaDir);
5414
5372
  log.info("Starting Prisma PostgreSQL setup.");
5415
- const prismaInitCommand = getPackageExecutionCommand(packageManager, "prisma init --db");
5416
- await execa(prismaInitCommand, {
5373
+ await execa(getPackageExecutionCommand(packageManager, "prisma init --db"), {
5417
5374
  cwd: serverDir,
5418
5375
  stdio: "inherit",
5419
5376
  shell: true
@@ -5474,10 +5431,9 @@ DATABASE_URL="your_database_url"`);
5474
5431
  }
5475
5432
  async function addPrismaAccelerateExtension(projectDir) {
5476
5433
  try {
5477
- const dbPackageDir = path.join(projectDir, "packages/db");
5478
5434
  await addPackageDependency({
5479
5435
  dependencies: ["@prisma/extension-accelerate"],
5480
- projectDir: dbPackageDir
5436
+ projectDir: path.join(projectDir, "packages/db")
5481
5437
  });
5482
5438
  return true;
5483
5439
  } catch (_error) {
@@ -5588,8 +5544,7 @@ function extractDbUrl(output) {
5588
5544
  async function initializeSupabase(serverDir, packageManager) {
5589
5545
  log.info("Initializing Supabase project...");
5590
5546
  try {
5591
- const supabaseInitCommand = getPackageExecutionCommand(packageManager, "supabase init");
5592
- await execa(supabaseInitCommand, {
5547
+ await execa(getPackageExecutionCommand(packageManager, "supabase init"), {
5593
5548
  cwd: serverDir,
5594
5549
  stdio: "inherit",
5595
5550
  shell: true
@@ -5780,13 +5735,12 @@ async function selectTursoGroup() {
5780
5735
  log.info(`Using the only available group: ${pc.blue(groups[0].name)}`);
5781
5736
  return groups[0].name;
5782
5737
  }
5783
- const groupOptions = groups.map((group$1) => ({
5784
- value: group$1.name,
5785
- label: `${group$1.name} (${group$1.locations})`
5786
- }));
5787
5738
  const selectedGroup = await select({
5788
5739
  message: "Select a Turso database group:",
5789
- options: groupOptions
5740
+ options: groups.map((group$1) => ({
5741
+ value: group$1.name,
5742
+ label: `${group$1.name} (${group$1.locations})`
5743
+ }))
5790
5744
  });
5791
5745
  if (isCancel(selectedGroup)) return exitCancelled("Operation cancelled");
5792
5746
  return selectedGroup;
@@ -5817,8 +5771,7 @@ async function createTursoDatabase(dbName, groupName) {
5817
5771
  }
5818
5772
  async function writeEnvFile(projectDir, backend, config) {
5819
5773
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
5820
- const envPath = path.join(projectDir, targetApp, ".env");
5821
- const variables = [{
5774
+ await addEnvVariablesToFile(path.join(projectDir, targetApp, ".env"), [{
5822
5775
  key: "DATABASE_URL",
5823
5776
  value: config?.dbUrl ?? "",
5824
5777
  condition: true
@@ -5826,8 +5779,7 @@ async function writeEnvFile(projectDir, backend, config) {
5826
5779
  key: "DATABASE_AUTH_TOKEN",
5827
5780
  value: config?.authToken ?? "",
5828
5781
  condition: true
5829
- }];
5830
- await addEnvVariablesToFile(envPath, variables);
5782
+ }]);
5831
5783
  }
5832
5784
  function displayManualSetupInstructions() {
5833
5785
  log.info(`Manual Turso Setup Instructions:
@@ -5908,8 +5860,7 @@ async function setupTurso(config, cliInput) {
5908
5860
  if (isCancel(dbNameResponse)) return exitCancelled("Operation cancelled");
5909
5861
  dbName = dbNameResponse;
5910
5862
  try {
5911
- const config$1 = await createTursoDatabase(dbName, selectedGroup);
5912
- await writeEnvFile(projectDir, backend, config$1);
5863
+ await writeEnvFile(projectDir, backend, await createTursoDatabase(dbName, selectedGroup));
5913
5864
  success = true;
5914
5865
  } catch (error) {
5915
5866
  if (error instanceof Error && error.message === "DATABASE_EXISTS") {
@@ -7079,7 +7030,6 @@ async function createProjectHandler(input) {
7079
7030
  shouldClearDirectory = result.shouldClearDirectory;
7080
7031
  }
7081
7032
  } catch (error) {
7082
- const elapsedTimeMs$1 = Date.now() - startTime;
7083
7033
  return {
7084
7034
  success: false,
7085
7035
  projectConfig: {
@@ -7105,7 +7055,7 @@ async function createProjectHandler(input) {
7105
7055
  },
7106
7056
  reproducibleCommand: "",
7107
7057
  timeScaffolded,
7108
- elapsedTimeMs: elapsedTimeMs$1,
7058
+ elapsedTimeMs: Date.now() - startTime,
7109
7059
  projectDirectory: "",
7110
7060
  relativePath: "",
7111
7061
  error: error instanceof Error ? error.message : String(error)
@@ -7357,11 +7307,10 @@ const router = os.router({
7357
7307
  manualDb: z$1.boolean().optional().default(false).describe("Skip automatic/manual database setup prompt and use manual setup")
7358
7308
  })])).handler(async ({ input }) => {
7359
7309
  const [projectName, options] = input;
7360
- const combinedInput = {
7310
+ const result = await createProjectHandler({
7361
7311
  projectName,
7362
7312
  ...options
7363
- };
7364
- const result = await createProjectHandler(combinedInput);
7313
+ });
7365
7314
  if (options.verbose) return result;
7366
7315
  }),
7367
7316
  add: os.meta({ description: "Add addons or deployment configurations to an existing Better-T-Stack project" }).input(z$1.tuple([z$1.object({
@@ -7379,8 +7328,7 @@ const router = os.router({
7379
7328
  try {
7380
7329
  renderTitle();
7381
7330
  intro(pc.magenta("Better-T-Stack Sponsors"));
7382
- const sponsors$1 = await fetchSponsors();
7383
- displaySponsors(sponsors$1);
7331
+ displaySponsors(await fetchSponsors());
7384
7332
  } catch (error) {
7385
7333
  handleError(error, "Failed to display sponsors");
7386
7334
  }
@@ -7468,4 +7416,4 @@ async function builder() {
7468
7416
  }
7469
7417
 
7470
7418
  //#endregion
7471
- export { builder, createBtsCli, docs, init, router, sponsors };
7419
+ export { router as a, init as i, createBtsCli as n, sponsors as o, docs as r, builder as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "3.2.4",
3
+ "version": "3.2.6-canary.46b928a7",
4
4
  "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -67,9 +67,9 @@
67
67
  },
68
68
  "dependencies": {
69
69
  "@biomejs/js-api": "^3.0.0",
70
- "@biomejs/wasm-nodejs": "^2.2.4",
71
- "@clack/prompts": "^1.0.0-alpha.5",
72
- "@orpc/server": "^1.9.0",
70
+ "@biomejs/wasm-nodejs": "^2.2.6",
71
+ "@clack/prompts": "^1.0.0-alpha.6",
72
+ "@orpc/server": "^1.10.0",
73
73
  "consola": "^3.4.2",
74
74
  "execa": "^9.6.0",
75
75
  "fs-extra": "^11.3.2",
@@ -78,18 +78,18 @@
78
78
  "jsonc-parser": "^3.3.1",
79
79
  "picocolors": "^1.1.1",
80
80
  "tinyglobby": "^0.2.15",
81
- "trpc-cli": "^0.11.0",
82
- "ts-morph": "^27.0.0",
83
- "yaml": "^2.7.0",
84
- "zod": "^4.1.11"
81
+ "trpc-cli": "^0.12.0",
82
+ "ts-morph": "^27.0.2",
83
+ "yaml": "^2.8.1",
84
+ "zod": "^4.1.12"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@types/fs-extra": "^11.0.4",
88
- "@types/node": "^24.5.2",
88
+ "@types/node": "^24.9.1",
89
89
  "@vitest/ui": "^3.2.4",
90
- "publint": "^0.3.13",
91
- "tsdown": "^0.15.4",
92
- "typescript": "^5.9.2",
90
+ "publint": "^0.3.15",
91
+ "tsdown": "^0.15.9",
92
+ "typescript": "^5.9.3",
93
93
  "vitest": "^3.2.4"
94
94
  }
95
95
  }
@@ -0,0 +1,8 @@
1
+ import { clerkMiddleware } from '@clerk/tanstack-react-start/server'
2
+ import { createStart } from '@tanstack/react-start'
3
+
4
+ export const startInstance = createStart(() => {
5
+ return {
6
+ requestMiddleware: [clerkMiddleware()],
7
+ }
8
+ })
@@ -28,15 +28,14 @@ import Loader from "@/components/loader";
28
28
 
29
29
  {{#if (and (eq backend "convex") (eq auth "clerk"))}}
30
30
  import { ClerkProvider, useAuth } from "@clerk/tanstack-react-start";
31
- import { getAuth } from "@clerk/tanstack-react-start/server";
31
+ import { auth } from "@clerk/tanstack-react-start/server";
32
32
  import { createServerFn } from "@tanstack/react-start";
33
- import { getRequest } from "@tanstack/react-start/server";
34
33
  import { ConvexProviderWithClerk } from "convex/react-clerk";
35
34
 
36
35
  const fetchClerkAuth = createServerFn({ method: "GET" }).handler(async () => {
37
- const auth = await getAuth(getRequest());
38
- const token = await auth.getToken({ template: "convex" });
39
- return { userId: auth.userId, token };
36
+ const clerkAuth = await auth();
37
+ const token = await clerkAuth.getToken({ template: "convex" });
38
+ return { userId: clerkAuth.userId, token };
40
39
  });
41
40
  {{else if (and (eq backend "convex") (eq auth "better-auth"))}}
42
41
  import { createServerFn } from "@tanstack/react-start";
@@ -1,19 +0,0 @@
1
- import { createClerkHandler } from "@clerk/tanstack-react-start/server";
2
- import {
3
- createStartHandler,
4
- defaultStreamHandler,
5
- defineHandlerCallback,
6
- } from "@tanstack/react-start/server";
7
- import { createRouter } from "./router";
8
-
9
- // this is broken right now, waiting for a fix
10
- const handlerFactory = createClerkHandler(
11
- createStartHandler({
12
- createRouter,
13
- }),
14
- );
15
-
16
- export default defineHandlerCallback(async (event) => {
17
- const startHandler = await handlerFactory(defaultStreamHandler);
18
- return startHandler(event);
19
- });