create-better-t-stack 2.48.3 → 2.49.1-canary.80158905

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.
Files changed (75) hide show
  1. package/dist/cli.d.ts +1 -2
  2. package/dist/cli.js +1 -1
  3. package/dist/index.d.ts +228 -101
  4. package/dist/index.js +1 -1
  5. package/dist/{src-BwIapwZk.js → src-CyG8-I-3.js} +451 -414
  6. package/package.json +9 -8
  7. package/templates/api/orpc/server/base/_gitignore +34 -0
  8. package/templates/api/orpc/server/base/package.json.hbs +24 -0
  9. package/templates/{backend/server/server-base → api/orpc/server/base}/src/routers/index.ts.hbs +2 -2
  10. package/templates/api/orpc/server/base/tsconfig.json.hbs +10 -0
  11. package/templates/api/orpc/server/base/tsdown.config.ts.hbs +7 -0
  12. package/templates/api/orpc/server/{base/src/lib → rest/src}/context.ts.hbs +5 -5
  13. package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +1 -1
  14. package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +1 -1
  15. package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +1 -1
  16. package/templates/api/orpc/web/svelte/src/lib/orpc.ts.hbs +1 -1
  17. package/templates/api/trpc/server/base/_gitignore +34 -0
  18. package/templates/api/trpc/server/base/package.json.hbs +23 -0
  19. package/templates/api/trpc/server/base/src/routers/index.ts.hbs +55 -0
  20. package/templates/api/trpc/server/base/tsconfig.json.hbs +13 -0
  21. package/templates/api/trpc/server/base/tsdown.config.ts.hbs +7 -0
  22. package/templates/api/trpc/server/{base/src/lib → rest/src}/context.ts.hbs +5 -5
  23. package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +2 -2
  24. package/templates/auth/better-auth/server/base/_gitignore +34 -0
  25. package/templates/auth/better-auth/server/base/package.json.hbs +24 -0
  26. package/templates/auth/better-auth/server/base/src/{lib/auth.ts.hbs → index.ts.hbs} +6 -6
  27. package/templates/auth/better-auth/server/base/tsconfig.json.hbs +13 -0
  28. package/templates/auth/better-auth/server/base/tsdown.config.ts.hbs +7 -0
  29. package/templates/backend/server/{server-base → base}/package.json.hbs +0 -1
  30. package/templates/backend/server/{server-base → base}/tsconfig.json.hbs +5 -10
  31. package/templates/backend/server/base/tsdown.config.ts.hbs +14 -0
  32. package/templates/backend/server/elysia/src/index.ts.hbs +5 -5
  33. package/templates/backend/server/express/src/index.ts.hbs +5 -5
  34. package/templates/backend/server/fastify/src/index.ts.hbs +5 -5
  35. package/templates/backend/server/hono/src/index.ts.hbs +5 -5
  36. package/templates/base/_gitignore +47 -1
  37. package/templates/base/package.json.hbs +1 -3
  38. package/templates/base/tsconfig.base.json +23 -0
  39. package/templates/db/base/_gitignore +34 -0
  40. package/templates/db/base/package.json.hbs +23 -0
  41. package/templates/db/base/tsconfig.json.hbs +13 -0
  42. package/templates/db/base/tsdown.config.ts.hbs +7 -0
  43. package/templates/db/drizzle/mysql/drizzle.config.ts.hbs +7 -2
  44. package/templates/db/drizzle/postgres/drizzle.config.ts.hbs +7 -2
  45. package/templates/db/drizzle/sqlite/drizzle.config.ts.hbs +7 -2
  46. package/templates/db/prisma/mongodb/prisma.config.ts.hbs +5 -1
  47. package/templates/db/prisma/mongodb/src/index.ts.hbs +5 -0
  48. package/templates/db/prisma/mysql/prisma.config.ts.hbs +5 -1
  49. package/templates/db/prisma/mysql/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  50. package/templates/db/prisma/postgres/prisma.config.ts.hbs +7 -3
  51. package/templates/db/prisma/postgres/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  52. package/templates/db/prisma/sqlite/prisma.config.ts.hbs +5 -1
  53. package/templates/db/prisma/sqlite/src/{db/index.ts.hbs → index.ts.hbs} +3 -3
  54. package/templates/examples/todo/server/drizzle/base/src/routers/todo.ts.hbs +6 -6
  55. package/templates/examples/todo/server/mongoose/base/src/routers/todo.ts.hbs +4 -4
  56. package/templates/examples/todo/server/prisma/base/src/routers/todo.ts.hbs +4 -4
  57. package/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs +1 -1
  58. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +1 -1
  59. package/templates/db/prisma/mongodb/src/db/index.ts.hbs +0 -5
  60. /package/templates/api/orpc/server/{base/src/lib/orpc.ts.hbs → rest/src/index.ts.hbs} +0 -0
  61. /package/templates/api/trpc/server/{base/src/lib/trpc.ts.hbs → rest/src/index.ts.hbs} +0 -0
  62. /package/templates/auth/better-auth/server/db/drizzle/mysql/src/{db/schema → schema}/auth.ts +0 -0
  63. /package/templates/auth/better-auth/server/db/drizzle/postgres/src/{db/schema → schema}/auth.ts +0 -0
  64. /package/templates/auth/better-auth/server/db/drizzle/sqlite/src/{db/schema → schema}/auth.ts +0 -0
  65. /package/templates/auth/better-auth/server/db/mongoose/mongodb/src/{db/models → models}/auth.model.ts +0 -0
  66. /package/templates/backend/server/{server-base → base}/_gitignore +0 -0
  67. /package/templates/db/drizzle/mysql/src/{db/index.ts.hbs → index.ts.hbs} +0 -0
  68. /package/templates/db/drizzle/postgres/src/{db/index.ts.hbs → index.ts.hbs} +0 -0
  69. /package/templates/db/drizzle/sqlite/src/{db/index.ts.hbs → index.ts.hbs} +0 -0
  70. /package/templates/db/mongoose/mongodb/src/{db/index.ts.hbs → index.ts.hbs} +0 -0
  71. /package/templates/examples/todo/server/mongoose/mongodb/src/db/models/{todo.model.ts → todo.model.ts.hbs} +0 -0
  72. /package/templates/examples/todo/server/prisma/mongodb/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  73. /package/templates/examples/todo/server/prisma/mysql/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  74. /package/templates/examples/todo/server/prisma/postgres/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  75. /package/templates/examples/todo/server/prisma/sqlite/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { autocompleteMultiselect, cancel, confirm, group, groupMultiselect, intro, isCancel, log, multiselect, outro, select, spinner, text } from "@clack/prompts";
3
+ import { createRouterClient, os } from "@orpc/server";
3
4
  import pc from "picocolors";
4
- import { createCli, trpcServer } from "trpc-cli";
5
- import z from "zod";
5
+ import { createCli } from "trpc-cli";
6
+ import z$1, { z } from "zod";
6
7
  import path from "node:path";
7
8
  import consola, { consola as consola$1 } from "consola";
8
9
  import fs from "fs-extra";
@@ -14,7 +15,7 @@ import { IndentationText, Node, Project, QuoteKind, SyntaxKind } from "ts-morph"
14
15
  import { glob } from "tinyglobby";
15
16
  import handlebars from "handlebars";
16
17
  import { Biome } from "@biomejs/js-api/nodejs";
17
- import os from "node:os";
18
+ import os$1 from "node:os";
18
19
 
19
20
  //#region src/utils/get-package-manager.ts
20
21
  const getUserPkgManager = () => {
@@ -70,6 +71,7 @@ const dependencyVersionMap = {
70
71
  "drizzle-kit": "^0.31.2",
71
72
  "@planetscale/database": "^1.19.0",
72
73
  "@libsql/client": "^0.15.9",
74
+ libsql: "^0.5.22",
73
75
  "@neondatabase/serverless": "^1.0.1",
74
76
  pg: "^8.14.1",
75
77
  "@types/pg": "^8.11.11",
@@ -145,7 +147,9 @@ const dependencyVersionMap = {
145
147
  "@cloudflare/workers-types": "^4.20250822.0",
146
148
  alchemy: "^0.67.0",
147
149
  nitropack: "^2.12.4",
148
- dotenv: "^17.2.1",
150
+ dotenv: "^17.2.2",
151
+ tsdown: "^0.15.5",
152
+ zod: "^4.1.11",
149
153
  "@polar-sh/better-auth": "^1.1.3",
150
154
  "@polar-sh/sdk": "^0.34.16"
151
155
  };
@@ -263,7 +267,7 @@ const AuthSchema = z.enum([
263
267
  ]).describe("Authentication provider");
264
268
  const PaymentsSchema = z.enum(["polar", "none"]).describe("Payments provider");
265
269
  const ProjectNameSchema = z.string().min(1, "Project name cannot be empty").max(255, "Project name must be less than 255 characters").refine((name) => name === "." || !name.startsWith("."), "Project name cannot start with a dot (except for '.')").refine((name) => name === "." || !name.startsWith("-"), "Project name cannot start with a dash").refine((name) => {
266
- const invalidChars = [
270
+ return ![
267
271
  "<",
268
272
  ">",
269
273
  ":",
@@ -271,8 +275,7 @@ const ProjectNameSchema = z.string().min(1, "Project name cannot be empty").max(
271
275
  "|",
272
276
  "?",
273
277
  "*"
274
- ];
275
- return !invalidChars.some((char) => name.includes(char));
278
+ ].some((char) => name.includes(char));
276
279
  }, "Project name contains invalid characters").refine((name) => name.toLowerCase() !== "node_modules", "Project name is reserved").describe("Project name or path");
277
280
  const WebDeploySchema = z.enum([
278
281
  "wrangler",
@@ -360,12 +363,11 @@ function validateApiFrontendCompatibility(api, frontends = []) {
360
363
  function isFrontendAllowedWithBackend(frontend, backend, auth) {
361
364
  if (backend === "convex" && frontend === "solid") return false;
362
365
  if (auth === "clerk" && backend === "convex") {
363
- const incompatibleFrontends = [
366
+ if ([
364
367
  "nuxt",
365
368
  "svelte",
366
369
  "solid"
367
- ];
368
- if (incompatibleFrontends.includes(frontend)) return false;
370
+ ].includes(frontend)) return false;
369
371
  }
370
372
  return true;
371
373
  }
@@ -385,8 +387,7 @@ function isExampleTodoAllowed(backend, database) {
385
387
  return !(backend !== "convex" && backend !== "none" && database === "none");
386
388
  }
387
389
  function isExampleAIAllowed(_backend, frontends = []) {
388
- const includesSolid = frontends.includes("solid");
389
- if (includesSolid) return false;
390
+ if (frontends.includes("solid")) return false;
390
391
  return true;
391
392
  }
392
393
  function validateWebDeployRequiresWebFrontend(webDeploy, hasWebFrontendFlag) {
@@ -398,8 +399,7 @@ function validateServerDeployRequiresBackend(serverDeploy, backend) {
398
399
  function validateAddonCompatibility(addon, frontend, _auth) {
399
400
  const compatibleFrontends = ADDON_COMPATIBILITY[addon];
400
401
  if (compatibleFrontends.length > 0) {
401
- const hasCompatibleFrontend = frontend.some((f) => compatibleFrontends.includes(f));
402
- if (!hasCompatibleFrontend) {
402
+ if (!frontend.some((f) => compatibleFrontends.includes(f))) {
403
403
  const frontendList = compatibleFrontends.join(", ");
404
404
  return {
405
405
  isCompatible: false,
@@ -912,7 +912,7 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
912
912
  if (isCancel(frontendTypes)) return exitCancelled("Operation cancelled");
913
913
  const result = [];
914
914
  if (frontendTypes.includes("web")) {
915
- const allWebOptions = [
915
+ const webOptions = [
916
916
  {
917
917
  value: "tanstack-router",
918
918
  label: "TanStack Router",
@@ -948,8 +948,7 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
948
948
  label: "TanStack Start",
949
949
  hint: "SSR, Server Functions, API Routes and more with TanStack Router"
950
950
  }
951
- ];
952
- const webOptions = allWebOptions.filter((option) => isFrontendAllowedWithBackend(option.value, backend, auth));
951
+ ].filter((option) => isFrontendAllowedWithBackend(option.value, backend, auth));
953
952
  const webFramework = await select({
954
953
  message: "Choose web",
955
954
  options: webOptions,
@@ -1069,20 +1068,18 @@ async function getPackageManagerChoice(packageManager) {
1069
1068
  //#region src/prompts/payments.ts
1070
1069
  async function getPaymentsChoice(payments, auth, backend, frontends) {
1071
1070
  if (payments !== void 0) return payments;
1072
- const isPolarCompatible = auth === "better-auth" && backend !== "convex" && (frontends?.length === 0 || splitFrontends(frontends).web.length > 0);
1073
- if (!isPolarCompatible) return "none";
1074
- const options = [{
1075
- value: "polar",
1076
- label: "Polar",
1077
- hint: "Turn your software into a business. 6 lines of code."
1078
- }, {
1079
- value: "none",
1080
- label: "None",
1081
- hint: "No payments integration"
1082
- }];
1071
+ if (!(auth === "better-auth" && backend !== "convex" && (frontends?.length === 0 || splitFrontends(frontends).web.length > 0))) return "none";
1083
1072
  const response = await select({
1084
1073
  message: "Select payments provider",
1085
- options,
1074
+ options: [{
1075
+ value: "polar",
1076
+ label: "Polar",
1077
+ hint: "Turn your software into a business. 6 lines of code."
1078
+ }, {
1079
+ value: "none",
1080
+ label: "None",
1081
+ hint: "No payments integration"
1082
+ }],
1086
1083
  initialValue: DEFAULT_CONFIG.payments
1087
1084
  });
1088
1085
  if (isCancel(response)) return exitCancelled("Operation cancelled");
@@ -1211,12 +1208,11 @@ function getDeploymentDisplay(deployment) {
1211
1208
  async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []) {
1212
1209
  if (deployment !== void 0) return deployment;
1213
1210
  if (!hasWebFrontend(frontend)) return "none";
1214
- const availableDeployments = [
1211
+ const options = [
1215
1212
  "wrangler",
1216
1213
  "alchemy",
1217
1214
  "none"
1218
- ];
1219
- const options = availableDeployments.map((deploy) => {
1215
+ ].map((deploy) => {
1220
1216
  const { label, hint } = getDeploymentDisplay(deploy);
1221
1217
  return {
1222
1218
  value: deploy,
@@ -1322,14 +1318,12 @@ function validateDirectoryName(name) {
1322
1318
  if (name === ".") return void 0;
1323
1319
  const result = ProjectNameSchema.safeParse(name);
1324
1320
  if (!result.success) return result.error.issues[0]?.message || "Invalid project name";
1325
- return void 0;
1326
1321
  }
1327
1322
  async function getProjectName(initialName) {
1328
1323
  if (initialName) {
1329
1324
  if (initialName === ".") return initialName;
1330
1325
  const finalDirName = path.basename(initialName);
1331
- const validationError = validateDirectoryName(finalDirName);
1332
- if (!validationError) {
1326
+ if (!validateDirectoryName(finalDirName)) {
1333
1327
  const projectDir = path.resolve(process.cwd(), initialName);
1334
1328
  if (isPathWithinCwd(projectDir)) return initialName;
1335
1329
  consola.error(pc.red("Project path must be within current directory"));
@@ -1358,7 +1352,6 @@ async function getProjectName(initialName) {
1358
1352
  const projectDir = path.resolve(process.cwd(), nameToUse);
1359
1353
  if (!isPathWithinCwd(projectDir)) return "Project path must be within current directory";
1360
1354
  }
1361
- return void 0;
1362
1355
  }
1363
1356
  });
1364
1357
  if (isCancel(response)) return exitCancelled("Operation cancelled.");
@@ -1372,8 +1365,7 @@ async function getProjectName(initialName) {
1372
1365
  //#region src/utils/get-latest-cli-version.ts
1373
1366
  const getLatestCLIVersion = () => {
1374
1367
  const packageJsonPath = path.join(PKG_ROOT, "package.json");
1375
- const packageJsonContent = fs.readJSONSync(packageJsonPath);
1376
- return packageJsonContent.version ?? "1.0.0";
1368
+ return fs.readJSONSync(packageJsonPath).version ?? "1.0.0";
1377
1369
  };
1378
1370
 
1379
1371
  //#endregion
@@ -1386,7 +1378,7 @@ const getLatestCLIVersion = () => {
1386
1378
  */
1387
1379
  function isTelemetryEnabled() {
1388
1380
  const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
1389
- const BTS_TELEMETRY = "1";
1381
+ const BTS_TELEMETRY = "0";
1390
1382
  if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
1391
1383
  if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
1392
1384
  return true;
@@ -1394,12 +1386,11 @@ function isTelemetryEnabled() {
1394
1386
 
1395
1387
  //#endregion
1396
1388
  //#region src/utils/analytics.ts
1397
- const POSTHOG_API_KEY = "phc_8ZUxEwwfKMajJLvxz1daGd931dYbQrwKNficBmsdIrs";
1398
- const POSTHOG_HOST = "https://us.i.posthog.com";
1389
+ const POSTHOG_API_KEY = "random";
1390
+ const POSTHOG_HOST = "random";
1399
1391
  function generateSessionId() {
1400
1392
  const rand = Math.random().toString(36).slice(2);
1401
- const now = Date.now().toString(36);
1402
- return `cli_${now}${rand}`;
1393
+ return `cli_${Date.now().toString(36)}${rand}`;
1403
1394
  }
1404
1395
  async function trackProjectCreation(config, disableAnalytics = false) {
1405
1396
  if (!isTelemetryEnabled() || disableAnalytics) return;
@@ -1506,9 +1497,7 @@ function generateReproducibleCommand(config) {
1506
1497
  async function handleDirectoryConflict(currentPathInput, silent = false) {
1507
1498
  while (true) {
1508
1499
  const resolvedPath = path.resolve(process.cwd(), currentPathInput);
1509
- const dirExists = await fs.pathExists(resolvedPath);
1510
- const dirIsNotEmpty = dirExists && (await fs.readdir(resolvedPath)).length > 0;
1511
- if (!dirIsNotEmpty) return {
1500
+ if (!(await fs.pathExists(resolvedPath) && (await fs.readdir(resolvedPath)).length > 0)) return {
1512
1501
  finalPathInput: currentPathInput,
1513
1502
  shouldClearDirectory: false
1514
1503
  };
@@ -1622,14 +1611,12 @@ const renderTitle = () => {
1622
1611
  const terminalWidth = process.stdout.columns || 80;
1623
1612
  const titleLines = TITLE_TEXT.split("\n");
1624
1613
  const titleWidth = Math.max(...titleLines.map((line) => line.length));
1625
- if (terminalWidth < titleWidth) {
1626
- const simplifiedTitle = `
1614
+ if (terminalWidth < titleWidth) console.log(gradient(Object.values(catppuccinTheme)).multiline(`
1627
1615
  ╔══════════════════╗
1628
1616
  ║ Better T Stack ║
1629
1617
  ╚══════════════════╝
1630
- `;
1631
- console.log(gradient(Object.values(catppuccinTheme)).multiline(simplifiedTitle));
1632
- } else console.log(gradient(Object.values(catppuccinTheme)).multiline(TITLE_TEXT));
1618
+ `));
1619
+ else console.log(gradient(Object.values(catppuccinTheme)).multiline(TITLE_TEXT));
1633
1620
  };
1634
1621
 
1635
1622
  //#endregion
@@ -1755,8 +1742,7 @@ function validateConvexConstraints(config, providedFlags) {
1755
1742
  "tanstack-start",
1756
1743
  "next"
1757
1744
  ];
1758
- const hasSupportedFrontend = config.frontend?.some((f) => supportedFrontends.includes(f));
1759
- if (!hasSupportedFrontend) exitWithError("Better-Auth with Convex backend is only supported with TanStack Router, TanStack Start, or Next.js frontends. Please use '--auth clerk' or '--auth none'.");
1745
+ if (!config.frontend?.some((f) => supportedFrontends.includes(f))) exitWithError("Better-Auth with Convex backend is only supported with TanStack Router, TanStack Start, or Next.js frontends. Please use '--auth clerk' or '--auth none'.");
1760
1746
  }
1761
1747
  }
1762
1748
  function validateBackendNoneConstraints(config, providedFlags) {
@@ -1987,8 +1973,7 @@ async function updateBtsConfig(projectDir, updates) {
1987
1973
  try {
1988
1974
  const configPath = path.join(projectDir, BTS_CONFIG_FILE);
1989
1975
  if (!await fs.pathExists(configPath)) return;
1990
- const configContent = await fs.readFile(configPath, "utf-8");
1991
- let modifiedContent = configContent;
1976
+ let modifiedContent = await fs.readFile(configPath, "utf-8");
1992
1977
  for (const [key, value] of Object.entries(updates)) {
1993
1978
  const editResult = JSONC.modify(modifiedContent, [key], value, { formattingOptions: {
1994
1979
  tabSize: 2,
@@ -2010,15 +1995,17 @@ const addPackageDependency = async (opts) => {
2010
1995
  if (!pkgJson.dependencies) pkgJson.dependencies = {};
2011
1996
  if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
2012
1997
  for (const pkgName of dependencies) {
2013
- const version = customDependencies[pkgName] || dependencyVersionMap[pkgName];
1998
+ const version = dependencyVersionMap[pkgName];
2014
1999
  if (version) pkgJson.dependencies[pkgName] = version;
2015
2000
  else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
2016
2001
  }
2017
2002
  for (const pkgName of devDependencies) {
2018
- const version = customDevDependencies[pkgName] || dependencyVersionMap[pkgName];
2003
+ const version = dependencyVersionMap[pkgName];
2019
2004
  if (version) pkgJson.devDependencies[pkgName] = version;
2020
2005
  else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
2021
2006
  }
2007
+ for (const [pkgName, version] of Object.entries(customDependencies)) pkgJson.dependencies[pkgName] = version;
2008
+ for (const [pkgName, version] of Object.entries(customDevDependencies)) pkgJson.devDependencies[pkgName] = version;
2022
2009
  await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
2023
2010
  };
2024
2011
 
@@ -2078,8 +2065,7 @@ async function setupFumadocs(config) {
2078
2065
  initialValue: "next-mdx"
2079
2066
  });
2080
2067
  if (isCancel(template)) return exitCancelled("Operation cancelled");
2081
- const templateArg = TEMPLATES[template].value;
2082
- const commandWithArgs = `create-fumadocs-app@latest fumadocs --template ${templateArg} --src --no-install --pm ${packageManager} --no-eslint --no-git`;
2068
+ const commandWithArgs = `create-fumadocs-app@latest fumadocs --template ${TEMPLATES[template].value} --src --no-install --pm ${packageManager} --no-eslint --no-git`;
2083
2069
  const fumadocsInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2084
2070
  await execa(fumadocsInitCommand, {
2085
2071
  cwd: path.join(projectDir, "apps"),
@@ -2112,27 +2098,26 @@ async function setupRuler(config) {
2112
2098
  log.error(pc.red("Ruler template directory not found. Please ensure ruler addon is properly installed."));
2113
2099
  return;
2114
2100
  }
2115
- const EDITORS$1 = {
2116
- amp: { label: "AMP" },
2117
- copilot: { label: "GitHub Copilot" },
2118
- claude: { label: "Claude Code" },
2119
- codex: { label: "OpenAI Codex CLI" },
2120
- cursor: { label: "Cursor" },
2121
- windsurf: { label: "Windsurf" },
2122
- cline: { label: "Cline" },
2123
- aider: { label: "Aider" },
2124
- firebase: { label: "Firebase Studio" },
2125
- "gemini-cli": { label: "Gemini CLI" },
2126
- junie: { label: "Junie" },
2127
- kilocode: { label: "Kilo Code" },
2128
- opencode: { label: "OpenCode" },
2129
- crush: { label: "Crush" },
2130
- zed: { label: "Zed" },
2131
- qwen: { label: "Qwen" }
2132
- };
2133
2101
  const selectedEditors = await autocompleteMultiselect({
2134
2102
  message: "Select AI assistants for Ruler",
2135
- options: Object.entries(EDITORS$1).map(([key, v]) => ({
2103
+ options: Object.entries({
2104
+ amp: { label: "AMP" },
2105
+ copilot: { label: "GitHub Copilot" },
2106
+ claude: { label: "Claude Code" },
2107
+ codex: { label: "OpenAI Codex CLI" },
2108
+ cursor: { label: "Cursor" },
2109
+ windsurf: { label: "Windsurf" },
2110
+ cline: { label: "Cline" },
2111
+ aider: { label: "Aider" },
2112
+ firebase: { label: "Firebase Studio" },
2113
+ "gemini-cli": { label: "Gemini CLI" },
2114
+ junie: { label: "Junie" },
2115
+ kilocode: { label: "Kilo Code" },
2116
+ opencode: { label: "OpenCode" },
2117
+ crush: { label: "Crush" },
2118
+ zed: { label: "Zed" },
2119
+ qwen: { label: "Qwen" }
2120
+ }).map(([key, v]) => ({
2136
2121
  value: key,
2137
2122
  label: v.label
2138
2123
  })),
@@ -2145,8 +2130,7 @@ async function setupRuler(config) {
2145
2130
  return;
2146
2131
  }
2147
2132
  const configFile = path.join(rulerDir, "ruler.toml");
2148
- const currentConfig = await fs.readFile(configFile, "utf-8");
2149
- let updatedConfig = currentConfig;
2133
+ let updatedConfig = await fs.readFile(configFile, "utf-8");
2150
2134
  const defaultAgentsLine = `default_agents = [${selectedEditors.map((editor) => `"${editor}"`).join(", ")}]`;
2151
2135
  updatedConfig = updatedConfig.replace(/default_agents = \[\]/, defaultAgentsLine);
2152
2136
  await fs.writeFile(configFile, updatedConfig);
@@ -2189,7 +2173,7 @@ async function setupStarlight(config) {
2189
2173
  const s = spinner();
2190
2174
  try {
2191
2175
  s.start("Setting up Starlight docs...");
2192
- const starlightArgs = [
2176
+ const commandWithArgs = `create-astro@latest ${[
2193
2177
  "docs",
2194
2178
  "--template",
2195
2179
  "starlight",
@@ -2198,9 +2182,7 @@ async function setupStarlight(config) {
2198
2182
  "tailwind",
2199
2183
  "--no-git",
2200
2184
  "--skip-houston"
2201
- ];
2202
- const starlightArgsString = starlightArgs.join(" ");
2203
- const commandWithArgs = `create-astro@latest ${starlightArgsString}`;
2185
+ ].join(" ")}`;
2204
2186
  const starlightInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2205
2187
  await execa(starlightInitCommand, {
2206
2188
  cwd: path.join(projectDir, "apps"),
@@ -2246,7 +2228,7 @@ async function setupTauri(config) {
2246
2228
  const hasNext = frontend.includes("next");
2247
2229
  const devUrl = hasReactRouter || hasSvelte ? "http://localhost:5173" : hasNext ? "http://localhost:3001" : "http://localhost:3001";
2248
2230
  const frontendDist = hasNuxt ? "../.output/public" : hasSvelte ? "../build" : hasNext ? "../.next" : hasReactRouter ? "../build/client" : "../dist";
2249
- const tauriArgs = [
2231
+ const commandWithArgs = `@tauri-apps/cli@latest ${[
2250
2232
  "init",
2251
2233
  `--app-name=${path.basename(projectDir)}`,
2252
2234
  `--window-title=${path.basename(projectDir)}`,
@@ -2254,9 +2236,7 @@ async function setupTauri(config) {
2254
2236
  `--dev-url=${devUrl}`,
2255
2237
  `--before-dev-command="${packageManager} run dev"`,
2256
2238
  `--before-build-command="${packageManager} run build"`
2257
- ];
2258
- const tauriArgsString = tauriArgs.join(" ");
2259
- const commandWithArgs = `@tauri-apps/cli@latest ${tauriArgsString}`;
2239
+ ].join(" ")}`;
2260
2240
  const tauriInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2261
2241
  await execa(tauriInitCommand, {
2262
2242
  cwd: clientPackageDir,
@@ -2330,8 +2310,7 @@ async function setupUltracite(config, hasHusky) {
2330
2310
  if (editors.length > 0) ultraciteArgs.push("--editors", ...editors);
2331
2311
  if (rules.length > 0) ultraciteArgs.push("--rules", ...rules);
2332
2312
  if (hasHusky) ultraciteArgs.push("--integrations", "husky", "lint-staged");
2333
- const ultraciteArgsString = ultraciteArgs.join(" ");
2334
- const commandWithArgs = `ultracite@latest ${ultraciteArgsString} --skip-install`;
2313
+ const commandWithArgs = `ultracite@latest ${ultraciteArgs.join(" ")} --skip-install`;
2335
2314
  const ultraciteInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2336
2315
  await execa(ultraciteInitCommand, {
2337
2316
  cwd: projectDir,
@@ -2371,8 +2350,7 @@ function ensureArrayProperty(obj, name) {
2371
2350
  async function addPwaToViteConfig(viteConfigPath, projectName) {
2372
2351
  const sourceFile = tsProject.addSourceFileAtPathIfExists(viteConfigPath);
2373
2352
  if (!sourceFile) throw new Error("vite config not found");
2374
- const hasImport = sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === "vite-plugin-pwa");
2375
- if (!hasImport) sourceFile.insertImportDeclaration(0, {
2353
+ if (!sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === "vite-plugin-pwa")) sourceFile.insertImportDeclaration(0, {
2376
2354
  namedImports: ["VitePWA"],
2377
2355
  moduleSpecifier: "vite-plugin-pwa"
2378
2356
  });
@@ -2381,12 +2359,10 @@ async function addPwaToViteConfig(viteConfigPath, projectName) {
2381
2359
  return Node.isIdentifier(expression) && expression.getText() === "defineConfig";
2382
2360
  });
2383
2361
  if (!defineCall) throw new Error("Could not find defineConfig call in vite config");
2384
- const callExpr = defineCall;
2385
- const configObject = callExpr.getArguments()[0];
2362
+ const configObject = defineCall.getArguments()[0];
2386
2363
  if (!configObject) throw new Error("defineConfig argument is not an object literal");
2387
2364
  const pluginsArray = ensureArrayProperty(configObject, "plugins");
2388
- const alreadyPresent = pluginsArray.getElements().some((el) => el.getText().startsWith("VitePWA("));
2389
- if (!alreadyPresent) pluginsArray.addElement(`VitePWA({
2365
+ if (!pluginsArray.getElements().some((el) => el.getText().startsWith("VitePWA("))) pluginsArray.addElement(`VitePWA({
2390
2366
  registerType: "autoUpdate",
2391
2367
  manifest: {
2392
2368
  name: "${projectName}",
@@ -2487,12 +2463,11 @@ async function setupHusky(projectDir, linter) {
2487
2463
  }
2488
2464
  }
2489
2465
  async function setupPwa(projectDir, frontends) {
2490
- const isCompatibleFrontend = frontends.some((f) => [
2466
+ if (!frontends.some((f) => [
2491
2467
  "react-router",
2492
2468
  "tanstack-router",
2493
2469
  "solid"
2494
- ].includes(f));
2495
- if (!isCompatibleFrontend) return;
2470
+ ].includes(f))) return;
2496
2471
  const clientPackageDir = getWebAppDir(projectDir, frontends);
2497
2472
  if (!await fs.pathExists(clientPackageDir)) return;
2498
2473
  await addPackageDependency({
@@ -2592,8 +2567,7 @@ async function installDependencies({ projectDir, packageManager }) {
2592
2567
  function initializeBiome() {
2593
2568
  try {
2594
2569
  const biome = new Biome();
2595
- const result = biome.openProject("./");
2596
- const projectKey = result.projectKey;
2570
+ const projectKey = biome.openProject("./").projectKey;
2597
2571
  biome.applyConfiguration(projectKey, {
2598
2572
  formatter: {
2599
2573
  enabled: true,
@@ -2615,27 +2589,25 @@ function initializeBiome() {
2615
2589
  }
2616
2590
  function isSupportedFile(filePath) {
2617
2591
  const ext = path.extname(filePath).toLowerCase();
2618
- const supportedExtensions = [
2592
+ return [
2619
2593
  ".js",
2620
2594
  ".jsx",
2621
2595
  ".ts",
2622
2596
  ".tsx",
2623
2597
  ".json",
2624
2598
  ".jsonc"
2625
- ];
2626
- return supportedExtensions.includes(ext);
2599
+ ].includes(ext);
2627
2600
  }
2628
2601
  function shouldSkipFile(filePath) {
2629
2602
  const basename = path.basename(filePath);
2630
- const skipPatterns = [
2603
+ return [
2631
2604
  ".hbs",
2632
2605
  "package-lock.json",
2633
2606
  "yarn.lock",
2634
2607
  "pnpm-lock.yaml",
2635
2608
  "bun.lock",
2636
2609
  ".d.ts"
2637
- ];
2638
- return skipPatterns.some((pattern) => basename.includes(pattern));
2610
+ ].some((pattern) => basename.includes(pattern));
2639
2611
  }
2640
2612
  function formatFileWithBiome(filePath, content) {
2641
2613
  if (!isSupportedFile(filePath) || shouldSkipFile(filePath)) return null;
@@ -2672,8 +2644,7 @@ async function processTemplate(srcPath, destPath, context) {
2672
2644
  let content;
2673
2645
  if (srcPath.endsWith(".hbs")) {
2674
2646
  const templateContent = await fs.readFile(srcPath, "utf-8");
2675
- const template = handlebars.compile(templateContent);
2676
- content = template(context);
2647
+ content = handlebars.compile(templateContent)(context);
2677
2648
  } else content = await fs.readFile(srcPath, "utf-8");
2678
2649
  try {
2679
2650
  const formattedContent = await formatFileWithBiome(destPath, content);
@@ -2804,23 +2775,28 @@ async function setupBackendFramework(projectDir, context) {
2804
2775
  return;
2805
2776
  }
2806
2777
  await fs.ensureDir(serverAppDir);
2807
- const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/server-base");
2778
+ const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/base");
2808
2779
  if (await fs.pathExists(serverBaseDir)) await processAndCopyFiles("**/*", serverBaseDir, serverAppDir, context);
2809
2780
  const frameworkSrcDir = path.join(PKG_ROOT, `templates/backend/server/${context.backend}`);
2810
2781
  if (await fs.pathExists(frameworkSrcDir)) await processAndCopyFiles("**/*", frameworkSrcDir, serverAppDir, context, true);
2811
2782
  if (context.api !== "none") {
2783
+ const apiPackageDir = path.join(projectDir, "packages/api");
2784
+ await fs.ensureDir(apiPackageDir);
2812
2785
  const apiServerBaseDir = path.join(PKG_ROOT, `templates/api/${context.api}/server/base`);
2813
- if (await fs.pathExists(apiServerBaseDir)) await processAndCopyFiles("**/*", apiServerBaseDir, serverAppDir, context, true);
2814
- const apiServerFrameworkDir = path.join(PKG_ROOT, `templates/api/${context.api}/server/${context.backend}`);
2815
- if (await fs.pathExists(apiServerFrameworkDir)) await processAndCopyFiles("**/*", apiServerFrameworkDir, serverAppDir, context, true);
2786
+ if (await fs.pathExists(apiServerBaseDir)) await processAndCopyFiles("**/*", apiServerBaseDir, apiPackageDir, context);
2787
+ let apiServerFrameworkDir = "";
2788
+ if (context.backend === "next") apiServerFrameworkDir = path.join(PKG_ROOT, `templates/api/${context.api}/server/${context.backend}`);
2789
+ else apiServerFrameworkDir = path.join(PKG_ROOT, `templates/api/${context.api}/server/rest`);
2790
+ if (await fs.pathExists(apiServerFrameworkDir)) await processAndCopyFiles("**/*", apiServerFrameworkDir, apiPackageDir, context, true);
2791
+ }
2792
+ if (context.database !== "none" && context.orm !== "none") {
2793
+ const dbPackageDir = path.join(projectDir, "packages/db");
2794
+ await fs.ensureDir(dbPackageDir);
2795
+ const dbBaseDir = path.join(PKG_ROOT, "templates/db/base");
2796
+ if (await fs.pathExists(dbBaseDir)) await processAndCopyFiles("**/*", dbBaseDir, dbPackageDir, context);
2797
+ const dbOrmSrcDir = path.join(PKG_ROOT, `templates/db/${context.orm}/${context.database}`);
2798
+ if (await fs.pathExists(dbOrmSrcDir)) await processAndCopyFiles("**/*", dbOrmSrcDir, dbPackageDir, context);
2816
2799
  }
2817
- }
2818
- async function setupDbOrmTemplates(projectDir, context) {
2819
- if (context.backend === "convex" || context.orm === "none" || context.database === "none") return;
2820
- const serverAppDir = path.join(projectDir, "apps/server");
2821
- await fs.ensureDir(serverAppDir);
2822
- const dbOrmSrcDir = path.join(PKG_ROOT, `templates/db/${context.orm}/${context.database}`);
2823
- if (await fs.pathExists(dbOrmSrcDir)) await processAndCopyFiles("**/*", dbOrmSrcDir, serverAppDir, context);
2824
2800
  }
2825
2801
  async function setupAuthTemplate(projectDir, context) {
2826
2802
  if (!context.auth || context.auth === "none") return;
@@ -2901,20 +2877,24 @@ async function setupAuthTemplate(projectDir, context) {
2901
2877
  return;
2902
2878
  }
2903
2879
  if (serverAppDirExists && context.backend !== "convex") {
2880
+ const authPackageDir = path.join(projectDir, "packages/auth");
2881
+ await fs.ensureDir(authPackageDir);
2904
2882
  const authServerBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/base`);
2905
- if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc, serverAppDir, context);
2883
+ if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc, authPackageDir, context);
2906
2884
  if (context.backend === "next") {
2907
2885
  const authServerNextSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/next`);
2908
- if (await fs.pathExists(authServerNextSrc)) await processAndCopyFiles("**/*", authServerNextSrc, serverAppDir, context);
2886
+ if (await fs.pathExists(authServerNextSrc)) await processAndCopyFiles("**/*", authServerNextSrc, authPackageDir, context);
2909
2887
  }
2910
2888
  if (context.orm !== "none" && context.database !== "none") {
2889
+ const dbPackageDir = path.join(projectDir, "packages/db");
2890
+ await fs.ensureDir(dbPackageDir);
2911
2891
  const orm = context.orm;
2912
2892
  const db = context.database;
2913
2893
  let authDbSrc = "";
2914
2894
  if (orm === "drizzle") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/drizzle/${db}`);
2915
2895
  else if (orm === "prisma") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/prisma/${db}`);
2916
2896
  else if (orm === "mongoose") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/mongoose/${db}`);
2917
- if (authDbSrc && await fs.pathExists(authDbSrc)) await processAndCopyFiles("**/*", authDbSrc, serverAppDir, context);
2897
+ if (authDbSrc && await fs.pathExists(authDbSrc)) await processAndCopyFiles("**/*", authDbSrc, dbPackageDir, context);
2918
2898
  }
2919
2899
  }
2920
2900
  if ((hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb) && webAppDirExists) {
@@ -2961,8 +2941,10 @@ async function setupPaymentsTemplate(projectDir, context) {
2961
2941
  const serverAppDirExists = await fs.pathExists(serverAppDir);
2962
2942
  const webAppDirExists = await fs.pathExists(webAppDir);
2963
2943
  if (serverAppDirExists && context.backend !== "convex") {
2944
+ const authPackageDir = path.join(projectDir, "packages/auth");
2945
+ await fs.ensureDir(authPackageDir);
2964
2946
  const paymentsServerSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/server/base`);
2965
- if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc, serverAppDir, context);
2947
+ if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc, authPackageDir, context);
2966
2948
  }
2967
2949
  const hasReactWeb = context.frontend.some((f) => [
2968
2950
  "tanstack-router",
@@ -3040,15 +3022,21 @@ async function setupExamplesTemplate(projectDir, context) {
3040
3022
  const exampleBaseDir = path.join(PKG_ROOT, `templates/examples/${example}`);
3041
3023
  if (serverAppDirExists && context.backend !== "convex" && context.backend !== "none") {
3042
3024
  const exampleServerSrc = path.join(exampleBaseDir, "server");
3043
- if (example === "ai" && context.backend === "next") {
3044
- const aiNextServerSrc = path.join(exampleServerSrc, "next");
3045
- if (await fs.pathExists(aiNextServerSrc)) await processAndCopyFiles("**/*", aiNextServerSrc, serverAppDir, context, false);
3025
+ if (context.api !== "none") {
3026
+ const apiPackageDir = path.join(projectDir, "packages/api");
3027
+ await fs.ensureDir(apiPackageDir);
3028
+ const exampleOrmBaseSrc = path.join(exampleServerSrc, context.orm, "base");
3029
+ if (await fs.pathExists(exampleOrmBaseSrc)) await processAndCopyFiles("**/*", exampleOrmBaseSrc, apiPackageDir, context, false);
3046
3030
  }
3047
3031
  if (context.orm !== "none" && context.database !== "none") {
3048
- const exampleOrmBaseSrc = path.join(exampleServerSrc, context.orm, "base");
3049
- if (await fs.pathExists(exampleOrmBaseSrc)) await processAndCopyFiles("**/*", exampleOrmBaseSrc, serverAppDir, context, false);
3032
+ const dbPackageDir = path.join(projectDir, "packages/db");
3033
+ await fs.ensureDir(dbPackageDir);
3050
3034
  const exampleDbSchemaSrc = path.join(exampleServerSrc, context.orm, context.database);
3051
- if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc, serverAppDir, context, false);
3035
+ if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc, dbPackageDir, context, false);
3036
+ }
3037
+ if (example === "ai" && context.backend === "next") {
3038
+ const aiNextServerSrc = path.join(exampleServerSrc, "next");
3039
+ if (await fs.pathExists(aiNextServerSrc)) await processAndCopyFiles("**/*", aiNextServerSrc, serverAppDir, context, false);
3052
3040
  }
3053
3041
  }
3054
3042
  if (webAppDirExists) {
@@ -3115,9 +3103,9 @@ async function handleExtras(projectDir, context) {
3115
3103
  }
3116
3104
  async function setupDockerComposeTemplates(projectDir, context) {
3117
3105
  if (context.dbSetup !== "docker" || context.database === "none") return;
3118
- const serverAppDir = path.join(projectDir, "apps/server");
3106
+ const dbPackageDir = path.join(projectDir, "packages/db");
3119
3107
  const dockerSrcDir = path.join(PKG_ROOT, `templates/db-setup/docker-compose/${context.database}`);
3120
- if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, serverAppDir, context);
3108
+ if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, dbPackageDir, context);
3121
3109
  }
3122
3110
  async function setupDeploymentTemplates(projectDir, context) {
3123
3111
  if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") if (context.webDeploy === "alchemy" && context.serverDeploy === "alchemy") {
@@ -3175,8 +3163,7 @@ async function setupDeploymentTemplates(projectDir, context) {
3175
3163
  async function addAddonsToProject(input) {
3176
3164
  try {
3177
3165
  const projectDir = input.projectDir || process.cwd();
3178
- const isBetterTStack = await isBetterTStackProject(projectDir);
3179
- if (!isBetterTStack) exitWithError("This doesn't appear to be a Better-T-Stack project. Please run this command from the root of a Better-T-Stack project.");
3166
+ if (!await isBetterTStackProject(projectDir)) exitWithError("This doesn't appear to be a Better-T-Stack project. Please run this command from the root of a Better-T-Stack project.");
3180
3167
  const detectedConfig = await detectProjectConfig(projectDir);
3181
3168
  if (!detectedConfig) exitWithError("Could not detect the project configuration. Please ensure this is a valid Better-T-Stack project.");
3182
3169
  const config = {
@@ -3323,15 +3310,13 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
3323
3310
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3324
3311
  }
3325
3312
  const openNextConfigPath = path.join(webAppDir, "open-next.config.ts");
3326
- const openNextConfigContent = `import { defineCloudflareConfig } from "@opennextjs/cloudflare";
3313
+ await fs.writeFile(openNextConfigPath, `import { defineCloudflareConfig } from "@opennextjs/cloudflare";
3327
3314
 
3328
3315
  export default defineCloudflareConfig({});
3329
- `;
3330
- await fs.writeFile(openNextConfigPath, openNextConfigContent);
3316
+ `);
3331
3317
  const gitignorePath = path.join(webAppDir, ".gitignore");
3332
3318
  if (await fs.pathExists(gitignorePath)) {
3333
- const gitignoreContent = await fs.readFile(gitignorePath, "utf-8");
3334
- if (!gitignoreContent.includes("wrangler.jsonc")) await fs.appendFile(gitignorePath, "\nwrangler.jsonc\n");
3319
+ if (!(await fs.readFile(gitignorePath, "utf-8")).includes("wrangler.jsonc")) await fs.appendFile(gitignorePath, "\nwrangler.jsonc\n");
3335
3320
  } else await fs.writeFile(gitignorePath, "wrangler.jsonc\n");
3336
3321
  }
3337
3322
 
@@ -3367,8 +3352,7 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
3367
3352
  quoteKind: QuoteKind.Double
3368
3353
  } });
3369
3354
  project.addSourceFileAtPath(nuxtConfigPath);
3370
- const sourceFile = project.getSourceFileOrThrow(nuxtConfigPath);
3371
- const exportAssignment = sourceFile.getExportAssignment((d) => !d.isExportEquals());
3355
+ const exportAssignment = project.getSourceFileOrThrow(nuxtConfigPath).getExportAssignment((d) => !d.isExportEquals());
3372
3356
  if (!exportAssignment) return;
3373
3357
  const defineConfigCall = exportAssignment.getExpression();
3374
3358
  if (!Node.isCallExpression(defineConfigCall) || defineConfigCall.getExpression().getText() !== "defineNuxtConfig") return;
@@ -3389,8 +3373,7 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
3389
3373
  if (modulesProperty && Node.isPropertyAssignment(modulesProperty)) {
3390
3374
  const initializer = modulesProperty.getInitializer();
3391
3375
  if (Node.isArrayLiteralExpression(initializer)) {
3392
- const hasModule = initializer.getElements().some((el) => el.getText() === "\"nitro-cloudflare-dev\"" || el.getText() === "'nitro-cloudflare-dev'");
3393
- if (!hasModule) initializer.addElement("\"nitro-cloudflare-dev\"");
3376
+ if (!initializer.getElements().some((el) => el.getText() === "\"nitro-cloudflare-dev\"" || el.getText() === "'nitro-cloudflare-dev'")) initializer.addElement("\"nitro-cloudflare-dev\"");
3394
3377
  }
3395
3378
  } else if (!modulesProperty) configObject.addPropertyAssignment({
3396
3379
  name: "modules",
@@ -3477,8 +3460,7 @@ async function setupSvelteAlchemyDeploy(projectDir, _packageManager, options) {
3477
3460
  } });
3478
3461
  project.addSourceFileAtPath(svelteConfigPath);
3479
3462
  const sourceFile = project.getSourceFileOrThrow(svelteConfigPath);
3480
- const importDeclarations = sourceFile.getImportDeclarations();
3481
- const adapterImport = importDeclarations.find((imp) => imp.getModuleSpecifierValue().includes("@sveltejs/adapter"));
3463
+ const adapterImport = sourceFile.getImportDeclarations().find((imp) => imp.getModuleSpecifierValue().includes("@sveltejs/adapter"));
3482
3464
  if (adapterImport) {
3483
3465
  adapterImport.setModuleSpecifier("alchemy/cloudflare/sveltekit");
3484
3466
  adapterImport.removeDefaultImport();
@@ -3604,8 +3586,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3604
3586
  if (pluginsProperty && Node.isPropertyAssignment(pluginsProperty)) {
3605
3587
  const initializer = pluginsProperty.getInitializer();
3606
3588
  if (Node.isArrayLiteralExpression(initializer)) {
3607
- const hasShim = initializer.getElements().some((el) => el.getText().includes("alchemy"));
3608
- if (!hasShim) initializer.addElement("alchemy()");
3589
+ if (!initializer.getElements().some((el) => el.getText().includes("alchemy"))) initializer.addElement("alchemy()");
3609
3590
  const tanstackElements = initializer.getElements().filter((el) => el.getText().includes("tanstackStart"));
3610
3591
  let needsReactPlugin = false;
3611
3592
  tanstackElements.forEach((element) => {
@@ -3623,8 +3604,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3623
3604
  name: "target",
3624
3605
  initializer: "\"cloudflare-module\""
3625
3606
  });
3626
- const hasCustomViteReactPlugin = !!configObj.getProperty("customViteReactPlugin");
3627
- if (!hasCustomViteReactPlugin) configObj.addPropertyAssignment({
3607
+ if (!!!configObj.getProperty("customViteReactPlugin")) configObj.addPropertyAssignment({
3628
3608
  name: "customViteReactPlugin",
3629
3609
  initializer: "true"
3630
3610
  });
@@ -3645,7 +3625,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3645
3625
  console.warn("Failed to update vite.config.ts:", error);
3646
3626
  }
3647
3627
  const nitroConfigPath = path.join(webAppDir, "nitro.config.ts");
3648
- const nitroConfigContent = `import { defineNitroConfig } from "nitropack/config";
3628
+ await fs.writeFile(nitroConfigPath, `import { defineNitroConfig } from "nitropack/config";
3649
3629
 
3650
3630
  export default defineNitroConfig({
3651
3631
  preset: "cloudflare-module",
@@ -3653,8 +3633,7 @@ export default defineNitroConfig({
3653
3633
  nodeCompat: true,
3654
3634
  },
3655
3635
  });
3656
- `;
3657
- await fs.writeFile(nitroConfigPath, nitroConfigContent, "utf-8");
3636
+ `, "utf-8");
3658
3637
  }
3659
3638
 
3660
3639
  //#endregion
@@ -3772,8 +3751,7 @@ async function setupNuxtWorkersDeploy(projectDir, packageManager) {
3772
3751
  if (modulesProp && modulesProp.getKind() === SyntaxKind.PropertyAssignment) {
3773
3752
  const arrayExpr = modulesProp.getFirstDescendantByKind(SyntaxKind.ArrayLiteralExpression);
3774
3753
  if (arrayExpr) {
3775
- const alreadyHas = arrayExpr.getElements().some((el) => el.getText().replace(/['"`]/g, "") === "nitro-cloudflare-dev");
3776
- if (!alreadyHas) arrayExpr.addElement("'nitro-cloudflare-dev'");
3754
+ if (!arrayExpr.getElements().some((el) => el.getText().replace(/['"`]/g, "") === "nitro-cloudflare-dev")) arrayExpr.addElement("'nitro-cloudflare-dev'");
3777
3755
  }
3778
3756
  } else configObj.addPropertyAssignment({
3779
3757
  name: "modules",
@@ -3808,13 +3786,10 @@ async function setupSvelteWorkersDeploy(projectDir, packageManager) {
3808
3786
  if (!sourceFile) return;
3809
3787
  const adapterImport = sourceFile.getImportDeclarations().find((imp) => ["@sveltejs/adapter-auto", "@sveltejs/adapter-node"].includes(imp.getModuleSpecifierValue()));
3810
3788
  if (adapterImport) adapterImport.setModuleSpecifier("@sveltejs/adapter-cloudflare");
3811
- else {
3812
- const alreadyHasCloudflare = sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === "@sveltejs/adapter-cloudflare");
3813
- if (!alreadyHasCloudflare) sourceFile.insertImportDeclaration(0, {
3814
- defaultImport: "adapter",
3815
- moduleSpecifier: "@sveltejs/adapter-cloudflare"
3816
- });
3817
- }
3789
+ else if (!sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === "@sveltejs/adapter-cloudflare")) sourceFile.insertImportDeclaration(0, {
3790
+ defaultImport: "adapter",
3791
+ moduleSpecifier: "@sveltejs/adapter-cloudflare"
3792
+ });
3818
3793
  await tsProject.save();
3819
3794
  }
3820
3795
  }
@@ -3865,8 +3840,7 @@ async function setupTanstackStartWorkersDeploy(projectDir, packageManager) {
3865
3840
  const tanstackPluginText = "tanstackStart({ target: \"cloudflare-module\", customViteReactPlugin: true })";
3866
3841
  if (tanstackPluginIndex === -1) pluginsArray.addElement(tanstackPluginText);
3867
3842
  else pluginsArray.getElements()[tanstackPluginIndex].replaceWithText(tanstackPluginText);
3868
- const hasReactPlugin = pluginsArray.getElements().some((el) => Node.isCallExpression(el) && el.getExpression().getText() === reactPluginIdentifier);
3869
- if (!hasReactPlugin) {
3843
+ if (!pluginsArray.getElements().some((el) => Node.isCallExpression(el) && el.getExpression().getText() === reactPluginIdentifier)) {
3870
3844
  const nextIndex = pluginsArray.getElements().findIndex((el) => el.getText().includes("tanstackStart(")) + 1;
3871
3845
  if (nextIndex > 0) pluginsArray.insertElement(nextIndex, `${reactPluginIdentifier}()`);
3872
3846
  else pluginsArray.addElement(`${reactPluginIdentifier}()`);
@@ -3886,8 +3860,7 @@ async function setupWorkersVitePlugin(projectDir) {
3886
3860
  });
3887
3861
  const sourceFile = tsProject.addSourceFileAtPathIfExists(viteConfigPath);
3888
3862
  if (!sourceFile) throw new Error("vite.config.ts not found in web app directory");
3889
- const hasCloudflareImport = sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === "@cloudflare/vite-plugin");
3890
- if (!hasCloudflareImport) sourceFile.insertImportDeclaration(0, {
3863
+ if (!sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === "@cloudflare/vite-plugin")) sourceFile.insertImportDeclaration(0, {
3891
3864
  namedImports: ["cloudflare"],
3892
3865
  moduleSpecifier: "@cloudflare/vite-plugin"
3893
3866
  });
@@ -3896,12 +3869,10 @@ async function setupWorkersVitePlugin(projectDir) {
3896
3869
  return Node.isIdentifier(expression) && expression.getText() === "defineConfig";
3897
3870
  });
3898
3871
  if (!defineCall) throw new Error("Could not find defineConfig call in vite config");
3899
- const callExpr = defineCall;
3900
- const configObject = callExpr.getArguments()[0];
3872
+ const configObject = defineCall.getArguments()[0];
3901
3873
  if (!configObject) throw new Error("defineConfig argument is not an object literal");
3902
3874
  const pluginsArray = ensureArrayProperty(configObject, "plugins");
3903
- const hasCloudflarePlugin = pluginsArray.getElements().some((el) => el.getText().includes("cloudflare("));
3904
- if (!hasCloudflarePlugin) pluginsArray.addElement("cloudflare()");
3875
+ if (!pluginsArray.getElements().some((el) => el.getText().includes("cloudflare("))) pluginsArray.addElement("cloudflare()");
3905
3876
  await tsProject.save();
3906
3877
  }
3907
3878
 
@@ -3960,8 +3931,7 @@ async function setupWorkersWebDeploy(projectDir, pkgManager) {
3960
3931
  async function addDeploymentToProject(input) {
3961
3932
  try {
3962
3933
  const projectDir = input.projectDir || process.cwd();
3963
- const isBetterTStack = await isBetterTStackProject(projectDir);
3964
- if (!isBetterTStack) exitWithError("This doesn't appear to be a Better-T-Stack project. Please run this command from the root of a Better-T-Stack project.");
3934
+ if (!await isBetterTStackProject(projectDir)) exitWithError("This doesn't appear to be a Better-T-Stack project. Please run this command from the root of a Better-T-Stack project.");
3965
3935
  const detectedConfig = await detectProjectConfig(projectDir);
3966
3936
  if (!detectedConfig) exitWithError("Could not detect the project configuration. Please ensure this is a valid Better-T-Stack project.");
3967
3937
  if (input.webDeploy && detectedConfig.webDeploy === input.webDeploy) exitWithError(`${input.webDeploy} web deployment is already configured for this project.`);
@@ -4015,10 +3985,10 @@ async function setupExamples(config) {
4015
3985
  if (examples.includes("ai")) {
4016
3986
  const webClientDir = path.join(projectDir, "apps/web");
4017
3987
  const nativeClientDir = path.join(projectDir, "apps/native");
4018
- const serverDir = path.join(projectDir, "apps/server");
3988
+ const apiDir = path.join(projectDir, "packages/api");
4019
3989
  const webClientDirExists = await fs.pathExists(webClientDir);
4020
3990
  const nativeClientDirExists = await fs.pathExists(nativeClientDir);
4021
- const serverDirExists = await fs.pathExists(serverDir);
3991
+ const apiDirExists = await fs.pathExists(apiDir);
4022
3992
  const hasNuxt = frontend.includes("nuxt");
4023
3993
  const hasSvelte = frontend.includes("svelte");
4024
3994
  const hasReactWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next") || frontend.includes("tanstack-start");
@@ -4039,9 +4009,9 @@ async function setupExamples(config) {
4039
4009
  dependencies: ["ai", "@ai-sdk/react"],
4040
4010
  projectDir: nativeClientDir
4041
4011
  });
4042
- if (serverDirExists && backend !== "none") await addPackageDependency({
4012
+ if (apiDirExists && backend !== "none") await addPackageDependency({
4043
4013
  dependencies: ["ai", "@ai-sdk/google"],
4044
- projectDir: serverDir
4014
+ projectDir: apiDir
4045
4015
  });
4046
4016
  }
4047
4017
  }
@@ -4131,8 +4101,7 @@ function getQueryDependencies(frontend) {
4131
4101
  "native-unistyles"
4132
4102
  ];
4133
4103
  const deps = {};
4134
- const needsReactQuery = frontend.some((f) => reactBasedFrontends.includes(f));
4135
- if (needsReactQuery) {
4104
+ if (frontend.some((f) => reactBasedFrontends.includes(f))) {
4136
4105
  const hasReactWeb = frontend.some((f) => f !== "native-nativewind" && f !== "native-unistyles" && reactBasedFrontends.includes(f));
4137
4106
  const hasNative = frontend.includes("native-nativewind") || frontend.includes("native-unistyles");
4138
4107
  if (hasReactWeb) deps.web = {
@@ -4158,32 +4127,54 @@ function getConvexDependencies(frontend) {
4158
4127
  return deps;
4159
4128
  }
4160
4129
  async function setupApi(config) {
4161
- const { api, projectName, frontend, backend, packageManager, projectDir } = config;
4130
+ const { api, projectName, frontend, backend, packageManager, projectDir, auth } = config;
4162
4131
  const isConvex = backend === "convex";
4163
4132
  const webDir = path.join(projectDir, "apps/web");
4164
4133
  const nativeDir = path.join(projectDir, "apps/native");
4165
4134
  const serverDir = path.join(projectDir, "apps/server");
4166
4135
  const webDirExists = await fs.pathExists(webDir);
4167
4136
  const nativeDirExists = await fs.pathExists(nativeDir);
4168
- const serverDirExists = await fs.pathExists(serverDir);
4137
+ await fs.pathExists(serverDir);
4169
4138
  const frontendType = getFrontendType(frontend);
4170
4139
  if (!isConvex && api !== "none") {
4171
4140
  const apiDeps = getApiDependencies(api, frontendType);
4172
- if (serverDirExists && apiDeps.server) {
4141
+ const apiPackageDir = path.join(projectDir, "packages/api");
4142
+ if (apiDeps.server) {
4173
4143
  await addPackageDependency({
4174
4144
  dependencies: apiDeps.server.dependencies,
4175
- projectDir: serverDir
4145
+ projectDir: apiPackageDir
4146
+ });
4147
+ const frameworkDeps = [];
4148
+ if (backend === "hono") frameworkDeps.push("hono");
4149
+ else if (backend === "elysia") frameworkDeps.push("elysia");
4150
+ else if (backend === "express") frameworkDeps.push("express", "@types/express");
4151
+ else if (backend === "fastify") frameworkDeps.push("fastify");
4152
+ if (frameworkDeps.length > 0) await addPackageDependency({
4153
+ dependencies: frameworkDeps,
4154
+ projectDir: apiPackageDir
4176
4155
  });
4177
4156
  if (api === "trpc") {
4178
4157
  if (backend === "hono") await addPackageDependency({
4179
4158
  dependencies: ["@hono/trpc-server"],
4180
- projectDir: serverDir
4159
+ projectDir: apiPackageDir
4181
4160
  });
4182
4161
  else if (backend === "elysia") await addPackageDependency({
4183
4162
  dependencies: ["@elysiajs/trpc"],
4184
- projectDir: serverDir
4163
+ projectDir: apiPackageDir
4164
+ });
4165
+ else if (backend === "express") await addPackageDependency({
4166
+ dependencies: ["@trpc/server"],
4167
+ projectDir: apiPackageDir
4168
+ });
4169
+ else if (backend === "fastify") await addPackageDependency({
4170
+ dependencies: ["@trpc/server"],
4171
+ projectDir: apiPackageDir
4185
4172
  });
4186
4173
  }
4174
+ if (auth === "better-auth") await addPackageDependency({
4175
+ dependencies: ["better-auth"],
4176
+ projectDir: apiPackageDir
4177
+ });
4187
4178
  }
4188
4179
  if (webDirExists && apiDeps.web) await addPackageDependency({
4189
4180
  dependencies: apiDeps.web.dependencies,
@@ -4227,7 +4218,7 @@ async function setupApi(config) {
4227
4218
  //#endregion
4228
4219
  //#region src/helpers/core/backend-setup.ts
4229
4220
  async function setupBackendDependencies(config) {
4230
- const { backend, runtime, api, projectDir } = config;
4221
+ const { backend, runtime, api, auth, examples, projectDir } = config;
4231
4222
  if (backend === "convex") return;
4232
4223
  const framework = backend;
4233
4224
  const serverDir = path.join(projectDir, "apps/server");
@@ -4255,6 +4246,13 @@ async function setupBackendDependencies(config) {
4255
4246
  dependencies.push("fastify", "@fastify/cors");
4256
4247
  if (runtime === "node") devDependencies.push("tsx", "@types/node");
4257
4248
  }
4249
+ if (api === "trpc") {
4250
+ if (framework === "express") dependencies.push("@trpc/server");
4251
+ else if (framework === "fastify") dependencies.push("@trpc/server");
4252
+ else if (runtime === "workers") dependencies.push("@trpc/server");
4253
+ } else if (api === "orpc") dependencies.push("@orpc/server", "@orpc/openapi", "@orpc/zod");
4254
+ if (auth === "better-auth") dependencies.push("better-auth");
4255
+ if (examples.includes("ai")) dependencies.push("ai", "@ai-sdk/google");
4258
4256
  if (runtime === "bun") devDependencies.push("@types/bun");
4259
4257
  if (dependencies.length > 0 || devDependencies.length > 0) await addPackageDependency({
4260
4258
  dependencies,
@@ -4273,7 +4271,7 @@ async function setupAuth(config) {
4273
4271
  const nativeDir = path.join(projectDir, "apps/native");
4274
4272
  const clientDirExists = await fs.pathExists(clientDir);
4275
4273
  const nativeDirExists = await fs.pathExists(nativeDir);
4276
- const serverDirExists = await fs.pathExists(serverDir);
4274
+ await fs.pathExists(serverDir);
4277
4275
  try {
4278
4276
  if (backend === "convex") {
4279
4277
  if (auth === "clerk" && clientDirExists) {
@@ -4295,8 +4293,7 @@ async function setupAuth(config) {
4295
4293
  }
4296
4294
  if (auth === "better-auth") {
4297
4295
  const convexBackendDir = path.join(projectDir, "packages/backend");
4298
- const convexBackendDirExists = await fs.pathExists(convexBackendDir);
4299
- if (convexBackendDirExists) await addPackageDependency({
4296
+ if (await fs.pathExists(convexBackendDir)) await addPackageDependency({
4300
4297
  dependencies: ["better-auth", "@convex-dev/better-auth"],
4301
4298
  customDependencies: { "better-auth": "1.3.8" },
4302
4299
  projectDir: convexBackendDir
@@ -4330,11 +4327,13 @@ async function setupAuth(config) {
4330
4327
  });
4331
4328
  return;
4332
4329
  }
4333
- if (serverDirExists && auth === "better-auth") await addPackageDependency({
4330
+ const authPackageDir = path.join(projectDir, "packages/auth");
4331
+ const authPackageDirExists = await fs.pathExists(authPackageDir);
4332
+ if (authPackageDirExists && auth === "better-auth") await addPackageDependency({
4334
4333
  dependencies: ["better-auth"],
4335
- projectDir: serverDir
4334
+ projectDir: authPackageDir
4336
4335
  });
4337
- const hasWebFrontend$1 = frontend.some((f) => [
4336
+ if (frontend.some((f) => [
4338
4337
  "react-router",
4339
4338
  "tanstack-router",
4340
4339
  "tanstack-start",
@@ -4342,8 +4341,7 @@ async function setupAuth(config) {
4342
4341
  "nuxt",
4343
4342
  "svelte",
4344
4343
  "solid"
4345
- ].includes(f));
4346
- if (hasWebFrontend$1 && clientDirExists) {
4344
+ ].includes(f)) && clientDirExists) {
4347
4345
  if (auth === "better-auth") await addPackageDependency({
4348
4346
  dependencies: ["better-auth"],
4349
4347
  projectDir: clientDir
@@ -4355,9 +4353,9 @@ async function setupAuth(config) {
4355
4353
  dependencies: ["better-auth", "@better-auth/expo"],
4356
4354
  projectDir: nativeDir
4357
4355
  });
4358
- if (serverDirExists) await addPackageDependency({
4356
+ if (authPackageDirExists) await addPackageDependency({
4359
4357
  dependencies: ["@better-auth/expo"],
4360
- projectDir: serverDir
4358
+ projectDir: authPackageDir
4361
4359
  });
4362
4360
  }
4363
4361
  }
@@ -4411,8 +4409,7 @@ async function addEnvVariablesToFile(filePath, variables) {
4411
4409
  let exampleContentToAdd = "";
4412
4410
  for (const exampleVar of exampleVariables) {
4413
4411
  const key = exampleVar.split("=")[0];
4414
- const regex = new RegExp(`^${key}=.*$`, "m");
4415
- if (!regex.test(exampleEnvContent)) {
4412
+ if (!new RegExp(`^${key}=.*$`, "m").test(exampleEnvContent)) {
4416
4413
  exampleContentToAdd += `${exampleVar}\n`;
4417
4414
  exampleModified = true;
4418
4415
  }
@@ -4432,8 +4429,7 @@ async function setupEnvironmentVariables(config) {
4432
4429
  const hasNuxt = frontend.includes("nuxt");
4433
4430
  const hasSvelte = frontend.includes("svelte");
4434
4431
  const hasSolid = frontend.includes("solid");
4435
- const hasWebFrontend$1 = hasReactRouter || hasTanStackRouter || hasTanStackStart || hasNextJs || hasNuxt || hasSolid || hasSvelte;
4436
- if (hasWebFrontend$1) {
4432
+ if (hasReactRouter || hasTanStackRouter || hasTanStackStart || hasNextJs || hasNuxt || hasSolid || hasSvelte) {
4437
4433
  const clientDir = path.join(projectDir, "apps/web");
4438
4434
  if (await fs.pathExists(clientDir)) {
4439
4435
  let envVarName = "VITE_SERVER_URL";
@@ -4522,15 +4518,12 @@ async function setupEnvironmentVariables(config) {
4522
4518
  const convexBackendDir = path.join(projectDir, "packages/backend");
4523
4519
  if (await fs.pathExists(convexBackendDir)) {
4524
4520
  const envLocalPath = path.join(convexBackendDir, ".env.local");
4525
- if (!await fs.pathExists(envLocalPath) || !(await fs.readFile(envLocalPath, "utf8")).includes("npx convex env set")) {
4526
- const convexCommands = `# Set Convex environment variables
4521
+ if (!await fs.pathExists(envLocalPath) || !(await fs.readFile(envLocalPath, "utf8")).includes("npx convex env set")) await fs.appendFile(envLocalPath, `# Set Convex environment variables
4527
4522
  # npx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)
4528
4523
  # npx convex env set SITE_URL http://localhost:3001
4529
4524
 
4530
- `;
4531
- await fs.appendFile(envLocalPath, convexCommands);
4532
- }
4533
- const convexBackendVars = [{
4525
+ `);
4526
+ await addEnvVariablesToFile(envLocalPath, [{
4534
4527
  key: hasNextJs ? "NEXT_PUBLIC_CONVEX_SITE_URL" : "VITE_CONVEX_SITE_URL",
4535
4528
  value: "",
4536
4529
  condition: true,
@@ -4539,15 +4532,12 @@ async function setupEnvironmentVariables(config) {
4539
4532
  key: "SITE_URL",
4540
4533
  value: "http://localhost:3001",
4541
4534
  condition: true
4542
- }];
4543
- await addEnvVariablesToFile(envLocalPath, convexBackendVars);
4535
+ }]);
4544
4536
  }
4545
4537
  }
4546
4538
  return;
4547
4539
  }
4548
4540
  const serverDir = path.join(projectDir, "apps/server");
4549
- if (!await fs.pathExists(serverDir)) return;
4550
- const envPath = path.join(serverDir, ".env");
4551
4541
  let corsOrigin = "http://localhost:3001";
4552
4542
  if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
4553
4543
  let databaseUrl = null;
@@ -4563,80 +4553,73 @@ async function setupEnvironmentVariables(config) {
4563
4553
  break;
4564
4554
  case "sqlite":
4565
4555
  if (config.runtime === "workers") databaseUrl = "http://127.0.0.1:8080";
4566
- else databaseUrl = "file:./local.db";
4556
+ else databaseUrl = `file:${path.join(config.projectDir, "apps/server", "local.db")}`;
4567
4557
  break;
4568
4558
  }
4569
- const serverVars = [
4570
- {
4571
- key: "CORS_ORIGIN",
4572
- value: corsOrigin,
4573
- condition: true
4574
- },
4575
- {
4576
- key: "BETTER_AUTH_SECRET",
4577
- value: generateAuthSecret(),
4578
- condition: !!auth
4579
- },
4580
- {
4581
- key: "BETTER_AUTH_URL",
4582
- value: "http://localhost:3000",
4583
- condition: !!auth
4584
- },
4585
- {
4586
- key: "DATABASE_URL",
4587
- value: databaseUrl,
4588
- condition: database !== "none" && dbSetup === "none"
4589
- },
4590
- {
4591
- key: "GOOGLE_GENERATIVE_AI_API_KEY",
4592
- value: "",
4593
- condition: examples?.includes("ai") || false
4594
- },
4595
- {
4596
- key: "POLAR_ACCESS_TOKEN",
4597
- value: "",
4598
- condition: config.payments === "polar"
4599
- },
4600
- {
4601
- key: "POLAR_SUCCESS_URL",
4602
- value: `${corsOrigin}/success?checkout_id={CHECKOUT_ID}`,
4603
- condition: config.payments === "polar"
4604
- }
4605
- ];
4606
- await addEnvVariablesToFile(envPath, serverVars);
4559
+ if (await fs.pathExists(serverDir)) {
4560
+ const serverEnvPath = path.join(serverDir, ".env");
4561
+ const serverVars = [
4562
+ {
4563
+ key: "BETTER_AUTH_SECRET",
4564
+ value: generateAuthSecret(),
4565
+ condition: !!auth
4566
+ },
4567
+ {
4568
+ key: "BETTER_AUTH_URL",
4569
+ value: "http://localhost:3000",
4570
+ condition: !!auth
4571
+ },
4572
+ {
4573
+ key: "POLAR_ACCESS_TOKEN",
4574
+ value: "",
4575
+ condition: config.payments === "polar"
4576
+ },
4577
+ {
4578
+ key: "POLAR_SUCCESS_URL",
4579
+ value: `${corsOrigin}/success?checkout_id={CHECKOUT_ID}`,
4580
+ condition: config.payments === "polar"
4581
+ },
4582
+ {
4583
+ key: "CORS_ORIGIN",
4584
+ value: corsOrigin,
4585
+ condition: true
4586
+ },
4587
+ {
4588
+ key: "GOOGLE_GENERATIVE_AI_API_KEY",
4589
+ value: "",
4590
+ condition: examples?.includes("ai") || false
4591
+ },
4592
+ {
4593
+ key: "DATABASE_URL",
4594
+ value: databaseUrl,
4595
+ condition: database !== "none" && dbSetup === "none"
4596
+ }
4597
+ ];
4598
+ await addEnvVariablesToFile(serverEnvPath, serverVars);
4599
+ }
4607
4600
  const isUnifiedAlchemy = webDeploy === "alchemy" && serverDeploy === "alchemy";
4608
4601
  const isIndividualAlchemy = webDeploy === "alchemy" || serverDeploy === "alchemy";
4609
4602
  if (isUnifiedAlchemy) {
4610
4603
  const rootEnvPath = path.join(projectDir, ".env");
4611
- const rootAlchemyVars = [{
4604
+ await addEnvVariablesToFile(rootEnvPath, [{
4612
4605
  key: "ALCHEMY_PASSWORD",
4613
4606
  value: "please-change-this",
4614
4607
  condition: true
4615
- }];
4616
- await addEnvVariablesToFile(rootEnvPath, rootAlchemyVars);
4608
+ }]);
4617
4609
  } else if (isIndividualAlchemy) {
4618
4610
  if (webDeploy === "alchemy") {
4619
4611
  const webDir = path.join(projectDir, "apps/web");
4620
- if (await fs.pathExists(webDir)) {
4621
- const webAlchemyVars = [{
4622
- key: "ALCHEMY_PASSWORD",
4623
- value: "please-change-this",
4624
- condition: true
4625
- }];
4626
- await addEnvVariablesToFile(path.join(webDir, ".env"), webAlchemyVars);
4627
- }
4628
- }
4629
- if (serverDeploy === "alchemy") {
4630
- const serverDir$1 = path.join(projectDir, "apps/server");
4631
- if (await fs.pathExists(serverDir$1)) {
4632
- const serverAlchemyVars = [{
4633
- key: "ALCHEMY_PASSWORD",
4634
- value: "please-change-this",
4635
- condition: true
4636
- }];
4637
- await addEnvVariablesToFile(path.join(serverDir$1, ".env"), serverAlchemyVars);
4638
- }
4612
+ if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), [{
4613
+ key: "ALCHEMY_PASSWORD",
4614
+ value: "please-change-this",
4615
+ condition: true
4616
+ }]);
4639
4617
  }
4618
+ if (serverDeploy === "alchemy") await addEnvVariablesToFile(path.join(serverDir, ".env"), [{
4619
+ key: "ALCHEMY_PASSWORD",
4620
+ value: "please-change-this",
4621
+ condition: true
4622
+ }]);
4640
4623
  }
4641
4624
  }
4642
4625
 
@@ -4671,7 +4654,7 @@ async function setupCloudflareD1(config) {
4671
4654
  const envPath = path.join(projectDir, "apps/server", ".env");
4672
4655
  const variables = [{
4673
4656
  key: "DATABASE_URL",
4674
- value: "file:./local.db",
4657
+ value: `file:${path.join(projectDir, "apps/server", "local.db")}`,
4675
4658
  condition: true
4676
4659
  }];
4677
4660
  try {
@@ -4718,13 +4701,8 @@ function getDatabaseUrl(database, projectName) {
4718
4701
  //#region src/utils/command-exists.ts
4719
4702
  async function commandExists(command) {
4720
4703
  try {
4721
- const isWindows = process.platform === "win32";
4722
- if (isWindows) {
4723
- const result$1 = await execa("where", [command]);
4724
- return result$1.exitCode === 0;
4725
- }
4726
- const result = await execa("which", [command]);
4727
- return result.exitCode === 0;
4704
+ if (process.platform === "win32") return (await execa("where", [command])).exitCode === 0;
4705
+ return (await execa("which", [command])).exitCode === 0;
4728
4706
  } catch {
4729
4707
  return false;
4730
4708
  }
@@ -4746,8 +4724,7 @@ async function checkAtlasCLI() {
4746
4724
  }
4747
4725
  async function initMongoDBAtlas(serverDir) {
4748
4726
  try {
4749
- const hasAtlas = await checkAtlasCLI();
4750
- if (!hasAtlas) {
4727
+ if (!await checkAtlasCLI()) {
4751
4728
  consola.error(pc.red("MongoDB Atlas CLI not found."));
4752
4729
  log.info(pc.yellow("Please install it from: https://www.mongodb.com/docs/atlas/cli/current/install-atlas-cli/"));
4753
4730
  return null;
@@ -5152,9 +5129,8 @@ async function setupWithCreateDb(serverDir, packageManager, orm) {
5152
5129
  consola$1.error("Failed to parse create-db response");
5153
5130
  return null;
5154
5131
  }
5155
- const databaseUrl = orm === "drizzle" ? createDbResponse.directConnectionString : createDbResponse.connectionString;
5156
5132
  return {
5157
- databaseUrl,
5133
+ databaseUrl: orm === "drizzle" ? createDbResponse.directConnectionString : createDbResponse.connectionString,
5158
5134
  claimUrl: createDbResponse.claimUrl
5159
5135
  };
5160
5136
  } catch (error) {
@@ -5208,9 +5184,9 @@ async function writeEnvFile$1(projectDir, config) {
5208
5184
  }
5209
5185
  async function addDotenvImportToPrismaConfig(projectDir) {
5210
5186
  try {
5211
- const prismaConfigPath = path.join(projectDir, "apps/server/prisma.config.ts");
5187
+ const prismaConfigPath = path.join(projectDir, "packages/db/prisma.config.ts");
5212
5188
  let content = await fs.readFile(prismaConfigPath, "utf8");
5213
- content = `import "dotenv/config";\n${content}`;
5189
+ content = `import dotenv from "dotenv";\ndotenv.config({ path: "../../apps/server/.env" });\n${content}`;
5214
5190
  await fs.writeFile(prismaConfigPath, content);
5215
5191
  } catch (_error) {
5216
5192
  consola$1.error("Failed to update prisma.config.ts");
@@ -5226,11 +5202,12 @@ function displayManualSetupInstructions$1() {
5226
5202
 
5227
5203
  DATABASE_URL="your_database_url"`);
5228
5204
  }
5229
- async function addPrismaAccelerateExtension(serverDir) {
5205
+ async function addPrismaAccelerateExtension(projectDir) {
5230
5206
  try {
5207
+ const dbPackageDir = path.join(projectDir, "packages/db");
5231
5208
  await addPackageDependency({
5232
5209
  dependencies: ["@prisma/extension-accelerate"],
5233
- projectDir: serverDir
5210
+ projectDir: dbPackageDir
5234
5211
  });
5235
5212
  return true;
5236
5213
  } catch (_error) {
@@ -5291,7 +5268,7 @@ async function setupPrismaPostgres(config, cliInput) {
5291
5268
  await writeEnvFile$1(projectDir, prismaConfig);
5292
5269
  if (orm === "prisma") {
5293
5270
  await addDotenvImportToPrismaConfig(projectDir);
5294
- await addPrismaAccelerateExtension(serverDir);
5271
+ await addPrismaAccelerateExtension(projectDir);
5295
5272
  }
5296
5273
  const connectionType = orm === "drizzle" ? "direct connection" : "Prisma Accelerate";
5297
5274
  log.success(pc.green(`Prisma Postgres database configured successfully with ${connectionType}!`));
@@ -5316,7 +5293,7 @@ async function writeSupabaseEnvFile(projectDir, databaseUrl) {
5316
5293
  try {
5317
5294
  const envPath = path.join(projectDir, "apps/server", ".env");
5318
5295
  const dbUrlToUse = databaseUrl || "postgresql://postgres:postgres@127.0.0.1:54322/postgres";
5319
- const variables = [{
5296
+ await addEnvVariablesToFile(envPath, [{
5320
5297
  key: "DATABASE_URL",
5321
5298
  value: dbUrlToUse,
5322
5299
  condition: true
@@ -5324,8 +5301,7 @@ async function writeSupabaseEnvFile(projectDir, databaseUrl) {
5324
5301
  key: "DIRECT_URL",
5325
5302
  value: dbUrlToUse,
5326
5303
  condition: true
5327
- }];
5328
- await addEnvVariablesToFile(envPath, variables);
5304
+ }]);
5329
5305
  return true;
5330
5306
  } catch (error) {
5331
5307
  consola$1.error(pc.red("Failed to update .env file for Supabase."));
@@ -5334,8 +5310,7 @@ async function writeSupabaseEnvFile(projectDir, databaseUrl) {
5334
5310
  }
5335
5311
  }
5336
5312
  function extractDbUrl(output) {
5337
- const dbUrlMatch = output.match(/DB URL:\s*(postgresql:\/\/[^\s]+)/);
5338
- const url = dbUrlMatch?.[1];
5313
+ const url = output.match(/DB URL:\s*(postgresql:\/\/[^\s]+)/)?.[1];
5339
5314
  if (url) return url;
5340
5315
  return null;
5341
5316
  }
@@ -5393,18 +5368,18 @@ function displayManualSupabaseInstructions(output) {
5393
5368
  log.info(`"Manual Supabase Setup Instructions:"
5394
5369
  1. Ensure Docker is installed and running.
5395
5370
  2. Install the Supabase CLI (e.g., \`npm install -g supabase\`).
5396
- 3. Run \`supabase init\` in your project's \`apps/server\` directory.
5397
- 4. Run \`supabase start\` in your project's \`apps/server\` directory.
5371
+ 3. Run \`supabase init\` in your project's \`packages/db\` directory.
5372
+ 4. Run \`supabase start\` in your project's \`packages/db\` directory.
5398
5373
  5. Copy the 'DB URL' from the output.${output ? `
5399
5374
  ${pc.bold("Relevant output from `supabase start`:")}
5400
5375
  ${pc.dim(output)}` : ""}
5401
- 6. Add the DB URL to the .env file in \`apps/server/.env\` as \`DATABASE_URL\`:
5376
+ 6. Add the DB URL to the .env file in \`packages/db/.env\` as \`DATABASE_URL\`:
5402
5377
  ${pc.gray("DATABASE_URL=\"your_supabase_db_url\"")}`);
5403
5378
  }
5404
5379
  async function setupSupabase(config, cliInput) {
5405
5380
  const { projectDir, packageManager } = config;
5406
5381
  const manualDb = cliInput?.manualDb ?? false;
5407
- const serverDir = path.join(projectDir, "apps", "server");
5382
+ const serverDir = path.join(projectDir, "packages", "db");
5408
5383
  try {
5409
5384
  await fs.ensureDir(serverDir);
5410
5385
  if (manualDb) {
@@ -5431,8 +5406,7 @@ async function setupSupabase(config, cliInput) {
5431
5406
  await writeSupabaseEnvFile(projectDir, "");
5432
5407
  return;
5433
5408
  }
5434
- const initialized = await initializeSupabase(serverDir, packageManager);
5435
- if (!initialized) {
5409
+ if (!await initializeSupabase(serverDir, packageManager)) {
5436
5410
  displayManualSupabaseInstructions();
5437
5411
  return;
5438
5412
  }
@@ -5442,14 +5416,12 @@ async function setupSupabase(config, cliInput) {
5442
5416
  return;
5443
5417
  }
5444
5418
  const dbUrl = extractDbUrl(supabaseOutput);
5445
- if (dbUrl) {
5446
- const envUpdated = await writeSupabaseEnvFile(projectDir, dbUrl);
5447
- if (envUpdated) log.success(pc.green("Supabase local development setup ready!"));
5448
- else {
5449
- log.error(pc.red("Supabase setup completed, but failed to update .env automatically."));
5450
- displayManualSupabaseInstructions(supabaseOutput);
5451
- }
5452
- } else {
5419
+ if (dbUrl) if (await writeSupabaseEnvFile(projectDir, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
5420
+ else {
5421
+ log.error(pc.red("Supabase setup completed, but failed to update .env automatically."));
5422
+ displayManualSupabaseInstructions(supabaseOutput);
5423
+ }
5424
+ else {
5453
5425
  log.error(pc.yellow("Supabase started, but could not extract DB URL automatically."));
5454
5426
  displayManualSupabaseInstructions(supabaseOutput);
5455
5427
  }
@@ -5467,8 +5439,7 @@ async function isTursoInstalled() {
5467
5439
  }
5468
5440
  async function isTursoLoggedIn() {
5469
5441
  try {
5470
- const output = await $`turso auth whoami`;
5471
- return !output.stdout.includes("You are not logged in");
5442
+ return !(await $`turso auth whoami`).stdout.includes("You are not logged in");
5472
5443
  } catch {
5473
5444
  return false;
5474
5445
  }
@@ -5627,10 +5598,9 @@ async function setupTurso(config, cliInput) {
5627
5598
  return;
5628
5599
  }
5629
5600
  setupSpinner.start("Checking Turso CLI availability...");
5630
- const platform = os.platform();
5601
+ const platform = os$1.platform();
5631
5602
  const isMac = platform === "darwin";
5632
- const isWindows = platform === "win32";
5633
- if (isWindows) {
5603
+ if (platform === "win32") {
5634
5604
  if (setupSpinner) setupSpinner.stop(pc.yellow("Turso setup not supported on Windows"));
5635
5605
  log.warn(pc.yellow("Automatic Turso setup is not supported on Windows."));
5636
5606
  await writeEnvFile(projectDir);
@@ -5638,8 +5608,7 @@ async function setupTurso(config, cliInput) {
5638
5608
  return;
5639
5609
  }
5640
5610
  if (setupSpinner) setupSpinner.stop("Turso CLI availability checked");
5641
- const isCliInstalled = await isTursoInstalled();
5642
- if (!isCliInstalled) {
5611
+ if (!await isTursoInstalled()) {
5643
5612
  const shouldInstall = await confirm({
5644
5613
  message: "Would you like to install Turso CLI?",
5645
5614
  initialValue: true
@@ -5652,8 +5621,7 @@ async function setupTurso(config, cliInput) {
5652
5621
  }
5653
5622
  await installTursoCLI(isMac);
5654
5623
  }
5655
- const isLoggedIn = await isTursoLoggedIn();
5656
- if (!isLoggedIn) await loginToTurso();
5624
+ if (!await isTursoLoggedIn()) await loginToTurso();
5657
5625
  const selectedGroup = await selectTursoGroup();
5658
5626
  let success = false;
5659
5627
  let dbName = "";
@@ -5694,15 +5662,15 @@ async function setupDatabase(config, cliInput) {
5694
5662
  const { database, orm, dbSetup, backend, projectDir } = config;
5695
5663
  if (backend === "convex" || database === "none") {
5696
5664
  if (backend !== "convex") {
5697
- const serverDir$1 = path.join(projectDir, "apps/server");
5698
- const serverDbDir = path.join(serverDir$1, "src/db");
5665
+ const serverDir = path.join(projectDir, "apps/server");
5666
+ const serverDbDir = path.join(serverDir, "src/db");
5699
5667
  if (await fs.pathExists(serverDbDir)) await fs.remove(serverDbDir);
5700
5668
  }
5701
5669
  return;
5702
5670
  }
5703
5671
  const s = spinner();
5704
- const serverDir = path.join(projectDir, "apps/server");
5705
- if (!await fs.pathExists(serverDir)) return;
5672
+ const dbPackageDir = path.join(projectDir, "packages/db");
5673
+ if (!await fs.pathExists(dbPackageDir)) return;
5706
5674
  try {
5707
5675
  if (orm === "prisma") if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
5708
5676
  dependencies: [
@@ -5711,23 +5679,27 @@ async function setupDatabase(config, cliInput) {
5711
5679
  "@planetscale/database"
5712
5680
  ],
5713
5681
  devDependencies: ["prisma"],
5714
- projectDir: serverDir
5682
+ projectDir: dbPackageDir
5715
5683
  });
5716
5684
  else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
5717
5685
  dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
5718
5686
  devDependencies: ["prisma"],
5719
- projectDir: serverDir
5687
+ projectDir: dbPackageDir
5720
5688
  });
5721
5689
  else await addPackageDependency({
5722
5690
  dependencies: ["@prisma/client"],
5723
5691
  devDependencies: ["prisma"],
5724
- projectDir: serverDir
5692
+ projectDir: dbPackageDir
5725
5693
  });
5726
5694
  else if (orm === "drizzle") {
5727
5695
  if (database === "sqlite") await addPackageDependency({
5728
- dependencies: ["drizzle-orm", "@libsql/client"],
5696
+ dependencies: [
5697
+ "drizzle-orm",
5698
+ "@libsql/client",
5699
+ "libsql"
5700
+ ],
5729
5701
  devDependencies: ["drizzle-kit"],
5730
- projectDir: serverDir
5702
+ projectDir: dbPackageDir
5731
5703
  });
5732
5704
  else if (database === "postgres") if (dbSetup === "neon") await addPackageDependency({
5733
5705
  dependencies: [
@@ -5736,32 +5708,32 @@ async function setupDatabase(config, cliInput) {
5736
5708
  "ws"
5737
5709
  ],
5738
5710
  devDependencies: ["drizzle-kit", "@types/ws"],
5739
- projectDir: serverDir
5711
+ projectDir: dbPackageDir
5740
5712
  });
5741
5713
  else if (dbSetup === "planetscale") await addPackageDependency({
5742
5714
  dependencies: ["drizzle-orm", "pg"],
5743
5715
  devDependencies: ["drizzle-kit", "@types/pg"],
5744
- projectDir: serverDir
5716
+ projectDir: dbPackageDir
5745
5717
  });
5746
5718
  else await addPackageDependency({
5747
5719
  dependencies: ["drizzle-orm", "pg"],
5748
5720
  devDependencies: ["drizzle-kit", "@types/pg"],
5749
- projectDir: serverDir
5721
+ projectDir: dbPackageDir
5750
5722
  });
5751
5723
  else if (database === "mysql") if (dbSetup === "planetscale") await addPackageDependency({
5752
5724
  dependencies: ["drizzle-orm", "@planetscale/database"],
5753
5725
  devDependencies: ["drizzle-kit"],
5754
- projectDir: serverDir
5726
+ projectDir: dbPackageDir
5755
5727
  });
5756
5728
  else await addPackageDependency({
5757
5729
  dependencies: ["drizzle-orm", "mysql2"],
5758
5730
  devDependencies: ["drizzle-kit"],
5759
- projectDir: serverDir
5731
+ projectDir: dbPackageDir
5760
5732
  });
5761
5733
  } else if (orm === "mongoose") await addPackageDependency({
5762
5734
  dependencies: ["mongoose"],
5763
5735
  devDependencies: [],
5764
- projectDir: serverDir
5736
+ projectDir: dbPackageDir
5765
5737
  });
5766
5738
  if (dbSetup === "docker") await setupDockerCompose(config);
5767
5739
  else if (database === "sqlite" && dbSetup === "turso") await setupTurso(config, cliInput);
@@ -5910,8 +5882,7 @@ function generateStackDescription(frontend, backend, api, isConvex) {
5910
5882
  const hasSvelte = frontend.includes("svelte");
5911
5883
  const hasNuxt = frontend.includes("nuxt");
5912
5884
  const hasSolid = frontend.includes("solid");
5913
- const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
5914
- if (!hasFrontendNone) {
5885
+ if (!(frontend.length === 0 || frontend.includes("none"))) {
5915
5886
  if (hasTanstackRouter) parts.push("React, TanStack Router");
5916
5887
  else if (hasReactRouter) parts.push("React, React Router");
5917
5888
  else if (hasNext) parts.push("Next.js");
@@ -5967,8 +5938,7 @@ function generateProjectStructure(projectName, frontend, backend, addons, isConv
5967
5938
  structure.push(`│ ├── web/ # Frontend application (${frontendType})`);
5968
5939
  }
5969
5940
  }
5970
- const hasNative = frontend.includes("native-nativewind") || frontend.includes("native-unistyles");
5971
- if (hasNative) structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
5941
+ if (frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
5972
5942
  if (addons.includes("starlight")) structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
5973
5943
  if (isConvex) {
5974
5944
  structure.push("├── packages/");
@@ -6129,7 +6099,7 @@ SERVER_URL={your-production-server-domain}
6129
6099
  CORS_ORIGIN={your-production-web-domain}
6130
6100
  BETTER_AUTH_URL={your-production-server-domain}
6131
6101
  \`\`\`
6132
- - In \`apps/server/lib/auth.ts\`, uncomment the \`session.cookieCache\` and \`advanced.crossSubDomainCookies\` sections and replace \`<your-workers-subdomain>\` with your actual workers subdomain. These settings are required to ensure cookies are transferred properly between your web and server domains.
6102
+ - In \`apps/server/src/lib/auth.ts\`, uncomment the \`session.cookieCache\` and \`advanced.crossSubDomainCookies\` sections and replace \`<your-workers-subdomain>\` with your actual workers subdomain. These settings are required to ensure cookies are transferred properly between your web and server domains.
6133
6103
  `;
6134
6104
  }
6135
6105
  function generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeploy) {
@@ -6152,12 +6122,11 @@ function generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeplo
6152
6122
  //#region src/helpers/core/git.ts
6153
6123
  async function initializeGit(projectDir, useGit) {
6154
6124
  if (!useGit) return;
6155
- const gitVersionResult = await $({
6125
+ if ((await $({
6156
6126
  cwd: projectDir,
6157
6127
  reject: false,
6158
6128
  stderr: "pipe"
6159
- })`git --version`;
6160
- if (gitVersionResult.exitCode !== 0) {
6129
+ })`git --version`).exitCode !== 0) {
6161
6130
  log.warn(pc.yellow("Git is not installed"));
6162
6131
  return;
6163
6132
  }
@@ -6187,7 +6156,7 @@ async function setupPayments(config) {
6187
6156
  projectDir: serverDir
6188
6157
  });
6189
6158
  if (clientDirExists) {
6190
- const hasWebFrontend$1 = frontend.some((f) => [
6159
+ if (frontend.some((f) => [
6191
6160
  "react-router",
6192
6161
  "tanstack-router",
6193
6162
  "tanstack-start",
@@ -6195,8 +6164,7 @@ async function setupPayments(config) {
6195
6164
  "nuxt",
6196
6165
  "svelte",
6197
6166
  "solid"
6198
- ].includes(f));
6199
- if (hasWebFrontend$1) await addPackageDependency({
6167
+ ].includes(f))) await addPackageDependency({
6200
6168
  dependencies: ["@polar-sh/better-auth"],
6201
6169
  projectDir: clientDir
6202
6170
  });
@@ -6237,15 +6205,13 @@ function getDockerInstallInstructions(platform, database) {
6237
6205
  return `${pc.yellow("IMPORTANT:")} Docker required for ${databaseName}. Install for ${platformName}:\n${pc.blue(installUrl)}`;
6238
6206
  }
6239
6207
  async function getDockerStatus(database) {
6240
- const platform = os.platform();
6241
- const installed = await isDockerInstalled();
6242
- if (!installed) return {
6208
+ const platform = os$1.platform();
6209
+ if (!await isDockerInstalled()) return {
6243
6210
  installed: false,
6244
6211
  running: false,
6245
6212
  message: getDockerInstallInstructions(platform, database)
6246
6213
  };
6247
- const running = await isDockerRunning();
6248
- if (!running) return {
6214
+ if (!await isDockerRunning()) return {
6249
6215
  installed: true,
6250
6216
  running: false,
6251
6217
  message: `${pc.yellow("IMPORTANT:")} Docker is installed but not running.`
@@ -6424,12 +6390,66 @@ function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
6424
6390
  return instructions.length ? `\n${instructions.join("\n")}` : "";
6425
6391
  }
6426
6392
 
6393
+ //#endregion
6394
+ //#region src/helpers/core/workspace-setup.ts
6395
+ async function setupWorkspaceDependencies(projectDir, options) {
6396
+ const projectName = options.projectName;
6397
+ const workspaceVersion = options.packageManager === "npm" ? "*" : "workspace:*";
6398
+ const commonDeps = ["dotenv", "zod"];
6399
+ const commonDevDeps = ["tsdown"];
6400
+ const dbPackageDir = path.join(projectDir, "packages/db");
6401
+ if (await fs.pathExists(dbPackageDir)) await addPackageDependency({
6402
+ dependencies: commonDeps,
6403
+ devDependencies: commonDevDeps,
6404
+ projectDir: dbPackageDir
6405
+ });
6406
+ const authPackageDir = path.join(projectDir, "packages/auth");
6407
+ if (await fs.pathExists(authPackageDir)) await addPackageDependency({
6408
+ dependencies: commonDeps,
6409
+ devDependencies: commonDevDeps,
6410
+ customDependencies: { [`@${projectName}/db`]: workspaceVersion },
6411
+ projectDir: authPackageDir
6412
+ });
6413
+ const apiPackageDir = path.join(projectDir, "packages/api");
6414
+ if (await fs.pathExists(apiPackageDir)) await addPackageDependency({
6415
+ dependencies: commonDeps,
6416
+ devDependencies: commonDevDeps,
6417
+ customDependencies: {
6418
+ [`@${projectName}/auth`]: workspaceVersion,
6419
+ [`@${projectName}/db`]: workspaceVersion
6420
+ },
6421
+ projectDir: apiPackageDir
6422
+ });
6423
+ const serverPackageDir = path.join(projectDir, "apps/server");
6424
+ if (await fs.pathExists(serverPackageDir)) await addPackageDependency({
6425
+ dependencies: commonDeps,
6426
+ devDependencies: commonDevDeps,
6427
+ customDependencies: {
6428
+ [`@${projectName}/api`]: workspaceVersion,
6429
+ [`@${projectName}/auth`]: workspaceVersion,
6430
+ [`@${projectName}/db`]: workspaceVersion
6431
+ },
6432
+ projectDir: serverPackageDir
6433
+ });
6434
+ if (options.api && options.api !== "none") {
6435
+ const webPackageDir = path.join(projectDir, "apps/web");
6436
+ if (await fs.pathExists(webPackageDir)) await addPackageDependency({
6437
+ customDependencies: { [`@${projectName}/api`]: workspaceVersion },
6438
+ projectDir: webPackageDir
6439
+ });
6440
+ }
6441
+ }
6442
+
6427
6443
  //#endregion
6428
6444
  //#region src/helpers/core/project-config.ts
6429
6445
  async function updatePackageConfigurations(projectDir, options) {
6430
6446
  await updateRootPackageJson(projectDir, options);
6431
- if (options.backend !== "convex") await updateServerPackageJson(projectDir, options);
6432
- else await updateConvexPackageJson(projectDir, options);
6447
+ if (options.backend !== "convex") {
6448
+ await updateServerPackageJson(projectDir, options);
6449
+ await updateAuthPackageJson(projectDir, options);
6450
+ await updateApiPackageJson(projectDir, options);
6451
+ await setupWorkspaceDependencies(projectDir, options);
6452
+ } else await updateConvexPackageJson(projectDir, options);
6433
6453
  }
6434
6454
  async function updateRootPackageJson(projectDir, options) {
6435
6455
  const rootPackageJsonPath = path.join(projectDir, "package.json");
@@ -6439,6 +6459,7 @@ async function updateRootPackageJson(projectDir, options) {
6439
6459
  if (!packageJson.scripts) packageJson.scripts = {};
6440
6460
  const scripts = packageJson.scripts;
6441
6461
  const backendPackageName = options.backend === "convex" ? `@${options.projectName}/backend` : "server";
6462
+ const dbPackageName = `@${options.projectName}/db`;
6442
6463
  let serverDevScript = "";
6443
6464
  if (options.addons.includes("turborepo")) serverDevScript = `turbo -F ${backendPackageName} dev`;
6444
6465
  else if (options.packageManager === "bun") serverDevScript = `bun run --filter ${backendPackageName} dev`;
@@ -6458,14 +6479,14 @@ async function updateRootPackageJson(projectDir, options) {
6458
6479
  scripts["dev:server"] = serverDevScript;
6459
6480
  if (options.backend === "convex") scripts["dev:setup"] = `turbo -F ${backendPackageName} dev:setup`;
6460
6481
  if (needsDbScripts) {
6461
- scripts["db:push"] = `turbo -F ${backendPackageName} db:push`;
6462
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${backendPackageName} db:studio`;
6482
+ scripts["db:push"] = `turbo -F ${dbPackageName} db:push`;
6483
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${dbPackageName} db:studio`;
6463
6484
  if (options.orm === "prisma") {
6464
- scripts["db:generate"] = `turbo -F ${backendPackageName} db:generate`;
6465
- scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
6485
+ scripts["db:generate"] = `turbo -F ${dbPackageName} db:generate`;
6486
+ scripts["db:migrate"] = `turbo -F ${dbPackageName} db:migrate`;
6466
6487
  } else if (options.orm === "drizzle") {
6467
- scripts["db:generate"] = `turbo -F ${backendPackageName} db:generate`;
6468
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
6488
+ scripts["db:generate"] = `turbo -F ${dbPackageName} db:generate`;
6489
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${dbPackageName} db:migrate`;
6469
6490
  }
6470
6491
  }
6471
6492
  if (options.dbSetup === "docker") {
@@ -6483,14 +6504,14 @@ async function updateRootPackageJson(projectDir, options) {
6483
6504
  scripts["dev:server"] = serverDevScript;
6484
6505
  if (options.backend === "convex") scripts["dev:setup"] = `pnpm --filter ${backendPackageName} dev:setup`;
6485
6506
  if (needsDbScripts) {
6486
- scripts["db:push"] = `pnpm --filter ${backendPackageName} db:push`;
6487
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${backendPackageName} db:studio`;
6507
+ scripts["db:push"] = `pnpm --filter ${dbPackageName} db:push`;
6508
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${dbPackageName} db:studio`;
6488
6509
  if (options.orm === "prisma") {
6489
- scripts["db:generate"] = `pnpm --filter ${backendPackageName} db:generate`;
6490
- scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
6510
+ scripts["db:generate"] = `pnpm --filter ${dbPackageName} db:generate`;
6511
+ scripts["db:migrate"] = `pnpm --filter ${dbPackageName} db:migrate`;
6491
6512
  } else if (options.orm === "drizzle") {
6492
- scripts["db:generate"] = `pnpm --filter ${backendPackageName} db:generate`;
6493
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
6513
+ scripts["db:generate"] = `pnpm --filter ${dbPackageName} db:generate`;
6514
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${dbPackageName} db:migrate`;
6494
6515
  }
6495
6516
  }
6496
6517
  if (options.dbSetup === "docker") {
@@ -6508,14 +6529,14 @@ async function updateRootPackageJson(projectDir, options) {
6508
6529
  scripts["dev:server"] = serverDevScript;
6509
6530
  if (options.backend === "convex") scripts["dev:setup"] = `npm run dev:setup --workspace ${backendPackageName}`;
6510
6531
  if (needsDbScripts) {
6511
- scripts["db:push"] = `npm run db:push --workspace ${backendPackageName}`;
6512
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${backendPackageName}`;
6532
+ scripts["db:push"] = `npm run db:push --workspace ${dbPackageName}`;
6533
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${dbPackageName}`;
6513
6534
  if (options.orm === "prisma") {
6514
- scripts["db:generate"] = `npm run db:generate --workspace ${backendPackageName}`;
6515
- scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
6535
+ scripts["db:generate"] = `npm run db:generate --workspace ${dbPackageName}`;
6536
+ scripts["db:migrate"] = `npm run db:migrate --workspace ${dbPackageName}`;
6516
6537
  } else if (options.orm === "drizzle") {
6517
- scripts["db:generate"] = `npm run db:generate --workspace ${backendPackageName}`;
6518
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
6538
+ scripts["db:generate"] = `npm run db:generate --workspace ${dbPackageName}`;
6539
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${dbPackageName}`;
6519
6540
  }
6520
6541
  }
6521
6542
  if (options.dbSetup === "docker") {
@@ -6533,14 +6554,14 @@ async function updateRootPackageJson(projectDir, options) {
6533
6554
  scripts["dev:server"] = serverDevScript;
6534
6555
  if (options.backend === "convex") scripts["dev:setup"] = `bun run --filter ${backendPackageName} dev:setup`;
6535
6556
  if (needsDbScripts) {
6536
- scripts["db:push"] = `bun run --filter ${backendPackageName} db:push`;
6537
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${backendPackageName} db:studio`;
6557
+ scripts["db:push"] = `bun run --filter ${dbPackageName} db:push`;
6558
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${dbPackageName} db:studio`;
6538
6559
  if (options.orm === "prisma") {
6539
- scripts["db:generate"] = `bun run --filter ${backendPackageName} db:generate`;
6540
- scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
6560
+ scripts["db:generate"] = `bun run --filter ${dbPackageName} db:generate`;
6561
+ scripts["db:migrate"] = `bun run --filter ${dbPackageName} db:migrate`;
6541
6562
  } else if (options.orm === "drizzle") {
6542
- scripts["db:generate"] = `bun run --filter ${backendPackageName} db:generate`;
6543
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
6563
+ scripts["db:generate"] = `bun run --filter ${dbPackageName} db:generate`;
6564
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${dbPackageName} db:migrate`;
6544
6565
  }
6545
6566
  }
6546
6567
  if (options.dbSetup === "docker") {
@@ -6560,8 +6581,7 @@ async function updateRootPackageJson(projectDir, options) {
6560
6581
  const workspaces = packageJson.workspaces;
6561
6582
  if (options.backend === "convex") {
6562
6583
  if (!workspaces.includes("packages/*")) workspaces.push("packages/*");
6563
- const needsAppsDir = options.frontend.length > 0 || options.addons.includes("starlight");
6564
- if (needsAppsDir && !workspaces.includes("apps/*")) workspaces.push("apps/*");
6584
+ if ((options.frontend.length > 0 || options.addons.includes("starlight")) && !workspaces.includes("apps/*")) workspaces.push("apps/*");
6565
6585
  } else {
6566
6586
  if (!workspaces.includes("apps/*")) workspaces.push("apps/*");
6567
6587
  if (!workspaces.includes("packages/*")) workspaces.push("packages/*");
@@ -6574,6 +6594,22 @@ async function updateServerPackageJson(projectDir, options) {
6574
6594
  const serverPackageJson = await fs.readJson(serverPackageJsonPath);
6575
6595
  if (!serverPackageJson.scripts) serverPackageJson.scripts = {};
6576
6596
  const scripts = serverPackageJson.scripts;
6597
+ if (options.dbSetup === "docker") {
6598
+ scripts["db:start"] = "docker compose up -d";
6599
+ scripts["db:watch"] = "docker compose up";
6600
+ scripts["db:stop"] = "docker compose stop";
6601
+ scripts["db:down"] = "docker compose down";
6602
+ }
6603
+ await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
6604
+ await updateDbPackageJson(projectDir, options);
6605
+ }
6606
+ async function updateDbPackageJson(projectDir, options) {
6607
+ const dbPackageJsonPath = path.join(projectDir, "packages/db/package.json");
6608
+ if (!await fs.pathExists(dbPackageJsonPath)) return;
6609
+ const dbPackageJson = await fs.readJson(dbPackageJsonPath);
6610
+ dbPackageJson.name = `@${options.projectName}/db`;
6611
+ if (!dbPackageJson.scripts) dbPackageJson.scripts = {};
6612
+ const scripts = dbPackageJson.scripts;
6577
6613
  if (options.database !== "none") {
6578
6614
  if (options.database === "sqlite" && options.orm === "drizzle" && options.dbSetup !== "d1") scripts["db:local"] = "turso dev --db-file local.db";
6579
6615
  if (options.orm === "prisma") {
@@ -6588,13 +6624,21 @@ async function updateServerPackageJson(projectDir, options) {
6588
6624
  if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = "drizzle-kit migrate";
6589
6625
  }
6590
6626
  }
6591
- if (options.dbSetup === "docker") {
6592
- scripts["db:start"] = "docker compose up -d";
6593
- scripts["db:watch"] = "docker compose up";
6594
- scripts["db:stop"] = "docker compose stop";
6595
- scripts["db:down"] = "docker compose down";
6596
- }
6597
- await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
6627
+ await fs.writeJson(dbPackageJsonPath, dbPackageJson, { spaces: 2 });
6628
+ }
6629
+ async function updateAuthPackageJson(projectDir, options) {
6630
+ const authPackageJsonPath = path.join(projectDir, "packages/auth/package.json");
6631
+ if (!await fs.pathExists(authPackageJsonPath)) return;
6632
+ const authPackageJson = await fs.readJson(authPackageJsonPath);
6633
+ authPackageJson.name = `@${options.projectName}/auth`;
6634
+ await fs.writeJson(authPackageJsonPath, authPackageJson, { spaces: 2 });
6635
+ }
6636
+ async function updateApiPackageJson(projectDir, options) {
6637
+ const apiPackageJsonPath = path.join(projectDir, "packages/api/package.json");
6638
+ if (!await fs.pathExists(apiPackageJsonPath)) return;
6639
+ const apiPackageJson = await fs.readJson(apiPackageJsonPath);
6640
+ apiPackageJson.name = `@${options.projectName}/api`;
6641
+ await fs.writeJson(apiPackageJsonPath, apiPackageJson, { spaces: 2 });
6598
6642
  }
6599
6643
  async function updateConvexPackageJson(projectDir, options) {
6600
6644
  const convexPackageJsonPath = path.join(projectDir, "packages/backend/package.json");
@@ -6615,10 +6659,7 @@ async function createProject(options, cliInput) {
6615
6659
  await copyBaseTemplate(projectDir, options);
6616
6660
  await setupFrontendTemplates(projectDir, options);
6617
6661
  await setupBackendFramework(projectDir, options);
6618
- if (!isConvex) {
6619
- await setupDbOrmTemplates(projectDir, options);
6620
- await setupDockerComposeTemplates(projectDir, options);
6621
- }
6662
+ if (!isConvex) await setupDockerComposeTemplates(projectDir, options);
6622
6663
  await setupAuthTemplate(projectDir, options);
6623
6664
  if (options.payments && options.payments !== "none") await setupPaymentsTemplate(projectDir, options);
6624
6665
  if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamplesTemplate(projectDir, options);
@@ -6781,9 +6822,7 @@ async function handleDirectoryConflictProgrammatically(currentPathInput, strateg
6781
6822
  finalPathInput: currentPathInput,
6782
6823
  shouldClearDirectory: false
6783
6824
  };
6784
- const dirContents = await fs.readdir(currentPath);
6785
- const isNotEmpty = dirContents.length > 0;
6786
- if (!isNotEmpty) return {
6825
+ if (!((await fs.readdir(currentPath)).length > 0)) return {
6787
6826
  finalPathInput: currentPathInput,
6788
6827
  shouldClearDirectory: false
6789
6828
  };
@@ -6945,26 +6984,25 @@ function displaySponsorsBox(sponsors$1) {
6945
6984
 
6946
6985
  //#endregion
6947
6986
  //#region src/index.ts
6948
- const t = trpcServer.initTRPC.create();
6949
- const router = t.router({
6950
- init: t.procedure.meta({
6987
+ const router = os.router({
6988
+ init: os.meta({
6951
6989
  description: "Create a new Better-T-Stack project",
6952
6990
  default: true,
6953
6991
  negateBooleans: true
6954
- }).input(z.tuple([ProjectNameSchema.optional(), z.object({
6955
- yes: z.boolean().optional().default(false).describe("Use default configuration"),
6956
- yolo: z.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
6957
- verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
6992
+ }).input(z$1.tuple([ProjectNameSchema.optional(), z$1.object({
6993
+ yes: z$1.boolean().optional().default(false).describe("Use default configuration"),
6994
+ yolo: z$1.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
6995
+ verbose: z$1.boolean().optional().default(false).describe("Show detailed result information"),
6958
6996
  database: DatabaseSchema.optional(),
6959
6997
  orm: ORMSchema.optional(),
6960
6998
  auth: AuthSchema.optional(),
6961
6999
  payments: PaymentsSchema.optional(),
6962
- frontend: z.array(FrontendSchema).optional(),
6963
- addons: z.array(AddonsSchema).optional(),
6964
- examples: z.array(ExamplesSchema).optional(),
6965
- git: z.boolean().optional(),
7000
+ frontend: z$1.array(FrontendSchema).optional(),
7001
+ addons: z$1.array(AddonsSchema).optional(),
7002
+ examples: z$1.array(ExamplesSchema).optional(),
7003
+ git: z$1.boolean().optional(),
6966
7004
  packageManager: PackageManagerSchema.optional(),
6967
- install: z.boolean().optional(),
7005
+ install: z$1.boolean().optional(),
6968
7006
  dbSetup: DatabaseSetupSchema.optional(),
6969
7007
  backend: BackendSchema.optional(),
6970
7008
  runtime: RuntimeSchema.optional(),
@@ -6972,10 +7010,10 @@ const router = t.router({
6972
7010
  webDeploy: WebDeploySchema.optional(),
6973
7011
  serverDeploy: ServerDeploySchema.optional(),
6974
7012
  directoryConflict: DirectoryConflictSchema.optional(),
6975
- renderTitle: z.boolean().optional(),
6976
- disableAnalytics: z.boolean().optional().default(false).describe("Disable analytics"),
6977
- manualDb: z.boolean().optional().default(false).describe("Skip automatic/manual database setup prompt and use manual setup")
6978
- })])).mutation(async ({ input }) => {
7013
+ renderTitle: z$1.boolean().optional(),
7014
+ disableAnalytics: z$1.boolean().optional().default(false).describe("Disable analytics"),
7015
+ manualDb: z$1.boolean().optional().default(false).describe("Skip automatic/manual database setup prompt and use manual setup")
7016
+ })])).handler(async ({ input }) => {
6979
7017
  const [projectName, options] = input;
6980
7018
  const combinedInput = {
6981
7019
  projectName,
@@ -6984,18 +7022,18 @@ const router = t.router({
6984
7022
  const result = await createProjectHandler(combinedInput);
6985
7023
  if (options.verbose) return result;
6986
7024
  }),
6987
- add: t.procedure.meta({ description: "Add addons or deployment configurations to an existing Better-T-Stack project" }).input(z.tuple([z.object({
6988
- addons: z.array(AddonsSchema).optional().default([]),
7025
+ add: os.meta({ description: "Add addons or deployment configurations to an existing Better-T-Stack project" }).input(z$1.tuple([z$1.object({
7026
+ addons: z$1.array(AddonsSchema).optional().default([]),
6989
7027
  webDeploy: WebDeploySchema.optional(),
6990
7028
  serverDeploy: ServerDeploySchema.optional(),
6991
- projectDir: z.string().optional(),
6992
- install: z.boolean().optional().default(false).describe("Install dependencies after adding addons or deployment"),
7029
+ projectDir: z$1.string().optional(),
7030
+ install: z$1.boolean().optional().default(false).describe("Install dependencies after adding addons or deployment"),
6993
7031
  packageManager: PackageManagerSchema.optional()
6994
- })])).mutation(async ({ input }) => {
7032
+ })])).handler(async ({ input }) => {
6995
7033
  const [options] = input;
6996
7034
  await addAddonsHandler(options);
6997
7035
  }),
6998
- sponsors: t.procedure.meta({ description: "Show Better-T-Stack sponsors" }).mutation(async () => {
7036
+ sponsors: os.meta({ description: "Show Better-T-Stack sponsors" }).handler(async () => {
6999
7037
  try {
7000
7038
  renderTitle();
7001
7039
  intro(pc.magenta("Better-T-Stack Sponsors"));
@@ -7005,7 +7043,7 @@ const router = t.router({
7005
7043
  handleError(error, "Failed to display sponsors");
7006
7044
  }
7007
7045
  }),
7008
- docs: t.procedure.meta({ description: "Open Better-T-Stack documentation" }).mutation(async () => {
7046
+ docs: os.meta({ description: "Open Better-T-Stack documentation" }).handler(async () => {
7009
7047
  const DOCS_URL = "https://better-t-stack.dev/docs";
7010
7048
  try {
7011
7049
  await openUrl(DOCS_URL);
@@ -7014,7 +7052,7 @@ const router = t.router({
7014
7052
  log.message(`Please visit ${DOCS_URL}`);
7015
7053
  }
7016
7054
  }),
7017
- builder: t.procedure.meta({ description: "Open the web-based stack builder" }).mutation(async () => {
7055
+ builder: os.meta({ description: "Open the web-based stack builder" }).handler(async () => {
7018
7056
  const BUILDER_URL = "https://better-t-stack.dev/new";
7019
7057
  try {
7020
7058
  await openUrl(BUILDER_URL);
@@ -7024,7 +7062,7 @@ const router = t.router({
7024
7062
  }
7025
7063
  })
7026
7064
  });
7027
- const caller = t.createCallerFactory(router)({});
7065
+ const caller = createRouterClient(router, { context: {} });
7028
7066
  function createBtsCli() {
7029
7067
  return createCli({
7030
7068
  router,
@@ -7066,9 +7104,8 @@ function createBtsCli() {
7066
7104
  * ```
7067
7105
  */
7068
7106
  async function init(projectName, options) {
7069
- const opts = options ?? {};
7070
7107
  const programmaticOpts = {
7071
- ...opts,
7108
+ ...options ?? {},
7072
7109
  verbose: true
7073
7110
  };
7074
7111
  const prev = process.env.BTS_PROGRAMMATIC;