create-better-t-stack 2.50.0 → 2.50.1-canary.7ebd503f

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 (109) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.d.ts +8 -9
  3. package/dist/index.js +1 -1
  4. package/dist/{src-BFx0Xu3C.js → src-DG05Vcnx.js} +696 -343
  5. package/package.json +2 -1
  6. package/templates/api/orpc/fullstack/next/src/app/api/rpc/[[...rest]]/route.ts.hbs +50 -0
  7. package/templates/api/orpc/server/_gitignore +34 -0
  8. package/templates/api/orpc/server/package.json.hbs +24 -0
  9. package/templates/api/orpc/server/{base/src/lib → src}/context.ts.hbs +6 -6
  10. package/templates/{backend/server/server-base → api/orpc/server}/src/routers/index.ts.hbs +2 -2
  11. package/templates/api/orpc/server/tsconfig.json.hbs +10 -0
  12. package/templates/api/orpc/server/tsdown.config.ts.hbs +7 -0
  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 +4 -2
  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/fullstack/next/src/app/api/trpc/[trpc]/route.ts.hbs +14 -0
  18. package/templates/api/trpc/server/_gitignore +34 -0
  19. package/templates/api/trpc/server/package.json.hbs +23 -0
  20. package/templates/api/trpc/server/{base/src/lib → src}/context.ts.hbs +6 -6
  21. package/templates/api/trpc/server/src/routers/index.ts.hbs +55 -0
  22. package/templates/api/trpc/server/tsconfig.json.hbs +13 -0
  23. package/templates/api/trpc/server/tsdown.config.ts.hbs +7 -0
  24. package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +6 -4
  25. package/templates/auth/better-auth/{server/next/src/app/api/auth/[...all]/route.ts → fullstack/next/src/app/api/auth/[...all]/route.ts.hbs} +1 -1
  26. package/templates/auth/better-auth/server/base/_gitignore +34 -0
  27. package/templates/auth/better-auth/server/base/package.json.hbs +24 -0
  28. package/templates/auth/better-auth/server/base/src/{lib/auth.ts.hbs → index.ts.hbs} +12 -12
  29. package/templates/auth/better-auth/server/base/tsconfig.json.hbs +13 -0
  30. package/templates/auth/better-auth/server/base/tsdown.config.ts.hbs +7 -0
  31. package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +2 -0
  32. package/templates/auth/better-auth/web/react/next/src/app/dashboard/page.tsx.hbs +31 -0
  33. package/templates/backend/server/{server-base → base}/package.json.hbs +1 -5
  34. package/templates/backend/server/{server-base → base}/tsconfig.json.hbs +5 -10
  35. package/templates/backend/server/base/tsdown.config.ts.hbs +9 -0
  36. package/templates/backend/server/elysia/src/index.ts.hbs +6 -6
  37. package/templates/backend/server/express/src/index.ts.hbs +6 -6
  38. package/templates/backend/server/fastify/src/index.ts.hbs +6 -6
  39. package/templates/backend/server/hono/src/index.ts.hbs +7 -7
  40. package/templates/base/_gitignore +47 -1
  41. package/templates/base/package.json.hbs +1 -3
  42. package/templates/base/tsconfig.base.json.hbs +23 -0
  43. package/templates/base/tsconfig.json.hbs +3 -0
  44. package/templates/db/base/_gitignore +34 -0
  45. package/templates/db/base/package.json.hbs +23 -0
  46. package/templates/db/base/tsconfig.json.hbs +13 -0
  47. package/templates/db/base/tsdown.config.ts.hbs +7 -0
  48. package/templates/db/drizzle/mysql/drizzle.config.ts.hbs +11 -2
  49. package/templates/db/drizzle/mysql/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  50. package/templates/db/drizzle/postgres/drizzle.config.ts.hbs +11 -2
  51. package/templates/db/drizzle/postgres/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  52. package/templates/db/drizzle/sqlite/drizzle.config.ts.hbs +11 -2
  53. package/templates/db/drizzle/sqlite/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  54. package/templates/db/prisma/mongodb/prisma.config.ts.hbs +9 -1
  55. package/templates/db/prisma/mongodb/src/index.ts.hbs +5 -0
  56. package/templates/db/prisma/mysql/prisma.config.ts.hbs +9 -1
  57. package/templates/db/prisma/mysql/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  58. package/templates/db/prisma/postgres/prisma.config.ts.hbs +11 -3
  59. package/templates/db/prisma/postgres/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  60. package/templates/db/prisma/sqlite/prisma.config.ts.hbs +9 -1
  61. package/templates/db/prisma/sqlite/src/{db/index.ts.hbs → index.ts.hbs} +3 -3
  62. package/templates/deploy/alchemy/alchemy.run.ts.hbs +3 -3
  63. package/templates/examples/ai/fullstack/next/src/app/api/ai/route.ts.hbs +15 -0
  64. package/templates/examples/todo/server/drizzle/base/src/routers/todo.ts.hbs +6 -6
  65. package/templates/examples/todo/server/mongoose/base/src/routers/todo.ts.hbs +4 -4
  66. package/templates/examples/todo/server/prisma/base/src/routers/todo.ts.hbs +4 -4
  67. package/templates/frontend/native/nativewind/tsconfig.json.hbs +1 -6
  68. package/templates/frontend/native/unistyles/tsconfig.json.hbs +1 -6
  69. package/templates/frontend/nuxt/tsconfig.json.hbs +0 -4
  70. package/templates/frontend/react/next/package.json.hbs +1 -1
  71. package/templates/frontend/react/next/tsconfig.json.hbs +0 -7
  72. package/templates/frontend/react/react-router/tsconfig.json.hbs +1 -6
  73. package/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs +1 -1
  74. package/templates/frontend/react/tanstack-router/tsconfig.json.hbs +1 -6
  75. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +1 -1
  76. package/templates/frontend/react/tanstack-start/tsconfig.json.hbs +1 -6
  77. package/templates/frontend/solid/tsconfig.json.hbs +1 -6
  78. package/templates/frontend/svelte/tsconfig.json.hbs +1 -6
  79. package/templates/api/orpc/server/next/src/app/rpc/[...all]/route.ts.hbs +0 -52
  80. package/templates/api/trpc/server/next/src/app/trpc/[trpc]/route.ts +0 -14
  81. package/templates/backend/server/next/next-env.d.ts +0 -5
  82. package/templates/backend/server/next/next.config.ts +0 -7
  83. package/templates/backend/server/next/package.json.hbs +0 -27
  84. package/templates/backend/server/next/src/app/route.ts +0 -5
  85. package/templates/backend/server/next/src/middleware.ts +0 -19
  86. package/templates/backend/server/next/tsconfig.json.hbs +0 -33
  87. package/templates/db/prisma/mongodb/src/db/index.ts.hbs +0 -5
  88. package/templates/examples/ai/server/next/src/app/ai/route.ts.hbs +0 -15
  89. /package/templates/api/orpc/server/{base/src/lib/orpc.ts.hbs → src/index.ts.hbs} +0 -0
  90. /package/templates/api/trpc/server/{base/src/lib/trpc.ts.hbs → src/index.ts.hbs} +0 -0
  91. /package/templates/auth/better-auth/server/db/drizzle/mysql/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
  92. /package/templates/auth/better-auth/server/db/drizzle/postgres/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
  93. /package/templates/auth/better-auth/server/db/drizzle/sqlite/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
  94. /package/templates/auth/better-auth/server/db/mongoose/mongodb/src/{db/models/auth.model.ts → models/auth.model.ts.hbs} +0 -0
  95. /package/templates/auth/better-auth/server/db/prisma/mongodb/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  96. /package/templates/auth/better-auth/server/db/prisma/mysql/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  97. /package/templates/auth/better-auth/server/db/prisma/postgres/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  98. /package/templates/auth/better-auth/server/db/prisma/sqlite/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  99. /package/templates/auth/better-auth/web/nuxt/app/middleware/{auth.ts → auth.ts.hbs} +0 -0
  100. /package/templates/backend/server/{server-base → base}/_gitignore +0 -0
  101. /package/templates/db/mongoose/mongodb/src/{db/index.ts.hbs → index.ts.hbs} +0 -0
  102. /package/templates/examples/todo/server/drizzle/mysql/src/{db/schema → schema}/todo.ts +0 -0
  103. /package/templates/examples/todo/server/drizzle/postgres/src/{db/schema → schema}/todo.ts +0 -0
  104. /package/templates/examples/todo/server/drizzle/sqlite/src/{db/schema → schema}/todo.ts +0 -0
  105. /package/templates/examples/todo/server/mongoose/mongodb/src/{db/models/todo.model.ts → models/todo.model.ts.hbs} +0 -0
  106. /package/templates/examples/todo/server/prisma/mongodb/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  107. /package/templates/examples/todo/server/prisma/mysql/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  108. /package/templates/examples/todo/server/prisma/postgres/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  109. /package/templates/examples/todo/server/prisma/sqlite/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
@@ -15,6 +15,7 @@ import { IndentationText, Node, Project, QuoteKind, SyntaxKind } from "ts-morph"
15
15
  import { glob } from "tinyglobby";
16
16
  import handlebars from "handlebars";
17
17
  import { Biome } from "@biomejs/js-api/nodejs";
18
+ import yaml from "yaml";
18
19
  import os$1 from "node:os";
19
20
 
20
21
  //#region src/utils/get-package-manager.ts
@@ -70,7 +71,7 @@ const dependencyVersionMap = {
70
71
  "drizzle-orm": "^0.44.2",
71
72
  "drizzle-kit": "^0.31.2",
72
73
  "@planetscale/database": "^1.19.0",
73
- "@libsql/client": "^0.15.9",
74
+ "@libsql/client": "^0.14.0",
74
75
  "@neondatabase/serverless": "^1.0.1",
75
76
  pg: "^8.14.1",
76
77
  "@types/pg": "^8.11.11",
@@ -123,6 +124,7 @@ const dependencyVersionMap = {
123
124
  "@trpc/tanstack-react-query": "^11.5.0",
124
125
  "@trpc/server": "^11.5.0",
125
126
  "@trpc/client": "^11.5.0",
127
+ next: "15.5.4",
126
128
  convex: "^1.27.0",
127
129
  "@convex-dev/react-query": "^0.0.0-alpha.8",
128
130
  "convex-svelte": "^0.0.11",
@@ -146,7 +148,9 @@ const dependencyVersionMap = {
146
148
  "@cloudflare/workers-types": "^4.20250822.0",
147
149
  alchemy: "^0.70.0",
148
150
  nitropack: "^2.12.4",
149
- dotenv: "^17.2.1",
151
+ dotenv: "^17.2.2",
152
+ tsdown: "^0.15.5",
153
+ zod: "^4.1.11",
150
154
  "@polar-sh/better-auth": "^1.1.3",
151
155
  "@polar-sh/sdk": "^0.34.16"
152
156
  };
@@ -195,9 +199,9 @@ const BackendSchema = z.enum([
195
199
  "hono",
196
200
  "express",
197
201
  "fastify",
198
- "next",
199
202
  "elysia",
200
203
  "convex",
204
+ "self",
201
205
  "none"
202
206
  ]).describe("Backend framework");
203
207
  const RuntimeSchema = z.enum([
@@ -343,6 +347,22 @@ function ensureSingleWebAndNative(frontends) {
343
347
  if (web.length > 1) exitWithError("Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next, nuxt, svelte, solid");
344
348
  if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles");
345
349
  }
350
+ const FULLSTACK_FRONTENDS$1 = [
351
+ "next",
352
+ "nuxt",
353
+ "svelte",
354
+ "tanstack-start"
355
+ ];
356
+ function validateSelfBackendCompatibility(providedFlags, options, config) {
357
+ const backend = config.backend || options.backend;
358
+ const frontends = config.frontend || options.frontend || [];
359
+ if (backend === "self") {
360
+ if (!frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f))) exitWithError("Backend 'self' (fullstack) requires a fullstack-capable frontend. Please use --frontend with one of: next, nuxt, svelte, tanstack-start");
361
+ if (frontends.length > 1) exitWithError("Backend 'self' (fullstack) can only be used with a single frontend framework.");
362
+ }
363
+ const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
364
+ if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) is only compatible with fullstack-capable frontends: next, nuxt, svelte, tanstack-start. Please choose a different backend or use a fullstack frontend.");
365
+ }
346
366
  function validateWorkersCompatibility(providedFlags, options, config) {
347
367
  if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && config.backend !== "hono") exitWithError(`Cloudflare Workers runtime (--runtime workers) is only supported with Hono backend (--backend hono). Current backend: ${config.backend}. Please use '--backend hono' or choose a different runtime.`);
348
368
  if (providedFlags.has("backend") && config.backend && config.backend !== "hono" && config.runtime === "workers") exitWithError(`Backend '${config.backend}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Hono backend. Please use '--backend hono' or choose a different runtime.`);
@@ -664,36 +684,39 @@ async function getAuthChoice(auth, hasDatabase, backend, frontend) {
664
684
 
665
685
  //#endregion
666
686
  //#region src/prompts/backend.ts
687
+ const FULLSTACK_FRONTENDS = [
688
+ "next",
689
+ "nuxt",
690
+ "svelte",
691
+ "tanstack-start"
692
+ ];
667
693
  async function getBackendFrameworkChoice(backendFramework, frontends) {
668
694
  if (backendFramework !== void 0) return backendFramework;
669
695
  const hasIncompatibleFrontend = frontends?.some((f) => f === "solid");
670
- const backendOptions = [
671
- {
672
- value: "hono",
673
- label: "Hono",
674
- hint: "Lightweight, ultrafast web framework"
675
- },
676
- {
677
- value: "next",
678
- label: "Next.js",
679
- hint: "separate api routes only backend"
680
- },
681
- {
682
- value: "express",
683
- label: "Express",
684
- hint: "Fast, unopinionated, minimalist web framework for Node.js"
685
- },
686
- {
687
- value: "fastify",
688
- label: "Fastify",
689
- hint: "Fast, low-overhead web framework for Node.js"
690
- },
691
- {
692
- value: "elysia",
693
- label: "Elysia",
694
- hint: "Ergonomic web framework for building backend servers"
695
- }
696
- ];
696
+ const hasFullstackFrontend = frontends?.some((f) => FULLSTACK_FRONTENDS.includes(f));
697
+ const backendOptions = [];
698
+ if (hasFullstackFrontend) backendOptions.push({
699
+ value: "self",
700
+ label: "Self (Fullstack)",
701
+ hint: "Use frontend's built-in api routes"
702
+ });
703
+ backendOptions.push({
704
+ value: "hono",
705
+ label: "Hono",
706
+ hint: "Lightweight, ultrafast web framework"
707
+ }, {
708
+ value: "express",
709
+ label: "Express",
710
+ hint: "Fast, unopinionated, minimalist web framework for Node.js"
711
+ }, {
712
+ value: "fastify",
713
+ label: "Fastify",
714
+ hint: "Fast, low-overhead web framework for Node.js"
715
+ }, {
716
+ value: "elysia",
717
+ label: "Elysia",
718
+ hint: "Ergonomic web framework for building backend servers"
719
+ });
697
720
  if (!hasIncompatibleFrontend) backendOptions.push({
698
721
  value: "convex",
699
722
  label: "Convex",
@@ -707,7 +730,7 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
707
730
  const response = await select({
708
731
  message: "Select backend",
709
732
  options: backendOptions,
710
- initialValue: DEFAULT_CONFIG.backend
733
+ initialValue: hasFullstackFrontend ? "self" : DEFAULT_CONFIG.backend
711
734
  });
712
735
  if (isCancel(response)) return exitCancelled("Operation cancelled");
713
736
  return response;
@@ -1086,9 +1109,8 @@ async function getPaymentsChoice(payments, auth, backend, frontends) {
1086
1109
  //#endregion
1087
1110
  //#region src/prompts/runtime.ts
1088
1111
  async function getRuntimeChoice(runtime, backend) {
1089
- if (backend === "convex" || backend === "none") return "none";
1112
+ if (backend === "convex" || backend === "none" || backend === "self") return "none";
1090
1113
  if (runtime !== void 0) return runtime;
1091
- if (backend === "next") return "node";
1092
1114
  const runtimeOptions = [{
1093
1115
  value: "bun",
1094
1116
  label: "Bun",
@@ -1375,7 +1397,7 @@ const getLatestCLIVersion = () => {
1375
1397
  */
1376
1398
  function isTelemetryEnabled() {
1377
1399
  const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
1378
- const BTS_TELEMETRY = "1";
1400
+ const BTS_TELEMETRY = "0";
1379
1401
  if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
1380
1402
  if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
1381
1403
  return true;
@@ -1383,8 +1405,8 @@ function isTelemetryEnabled() {
1383
1405
 
1384
1406
  //#endregion
1385
1407
  //#region src/utils/analytics.ts
1386
- const POSTHOG_API_KEY = "phc_8ZUxEwwfKMajJLvxz1daGd931dYbQrwKNficBmsdIrs";
1387
- const POSTHOG_HOST = "https://us.i.posthog.com";
1408
+ const POSTHOG_API_KEY = "random";
1409
+ const POSTHOG_HOST = "random";
1388
1410
  function generateSessionId() {
1389
1411
  const rand = Math.random().toString(36).slice(2);
1390
1412
  return `cli_${Date.now().toString(36)}${rand}`;
@@ -1765,8 +1787,8 @@ function validateBackendConstraints(config, providedFlags, options) {
1765
1787
  ].includes(f));
1766
1788
  if (incompatibleFrontends.length > 0) exitWithError(`Clerk authentication is not compatible with the following frontends: ${incompatibleFrontends.join(", ")}. Please choose a different frontend or auth provider.`);
1767
1789
  }
1768
- if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none") {
1769
- if (providedFlags.has("runtime") && options.runtime === "none") exitWithError("'--runtime none' is only supported with '--backend convex' or '--backend none'. Please choose 'bun', 'node', or remove the --runtime flag.");
1790
+ if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none" && backend !== "self") {
1791
+ if (providedFlags.has("runtime") && options.runtime === "none") exitWithError("'--runtime none' is only supported with '--backend convex', '--backend none', or '--backend self'. Please choose 'bun', 'node', or remove the --runtime flag.");
1770
1792
  }
1771
1793
  if (backend === "convex" && providedFlags.has("frontend") && options.frontend) {
1772
1794
  const incompatibleFrontends = options.frontend.filter((f) => f === "solid");
@@ -1796,6 +1818,7 @@ function validateFullConfig(config, providedFlags, options) {
1796
1818
  validateFrontendConstraints(config, providedFlags);
1797
1819
  validateApiConstraints(config, options);
1798
1820
  validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
1821
+ validateSelfBackendCompatibility(providedFlags, options, config);
1799
1822
  validateWorkersCompatibility(providedFlags, options, config);
1800
1823
  if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose 'wrangler' or 'alchemy' for --server-deploy.");
1801
1824
  if (config.addons && config.addons.length > 0) {
@@ -1849,6 +1872,7 @@ const CORE_STACK_FLAGS = new Set([
1849
1872
  "examples",
1850
1873
  "auth",
1851
1874
  "dbSetup",
1875
+ "payments",
1852
1876
  "api",
1853
1877
  "webDeploy",
1854
1878
  "serverDeploy"
@@ -1992,15 +2016,17 @@ const addPackageDependency = async (opts) => {
1992
2016
  if (!pkgJson.dependencies) pkgJson.dependencies = {};
1993
2017
  if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
1994
2018
  for (const pkgName of dependencies) {
1995
- const version = customDependencies[pkgName] || dependencyVersionMap[pkgName];
2019
+ const version = dependencyVersionMap[pkgName];
1996
2020
  if (version) pkgJson.dependencies[pkgName] = version;
1997
2021
  else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
1998
2022
  }
1999
2023
  for (const pkgName of devDependencies) {
2000
- const version = customDevDependencies[pkgName] || dependencyVersionMap[pkgName];
2024
+ const version = dependencyVersionMap[pkgName];
2001
2025
  if (version) pkgJson.devDependencies[pkgName] = version;
2002
2026
  else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
2003
2027
  }
2028
+ for (const [pkgName, version] of Object.entries(customDependencies)) pkgJson.dependencies[pkgName] = version;
2029
+ for (const [pkgName, version] of Object.entries(customDevDependencies)) pkgJson.devDependencies[pkgName] = version;
2004
2030
  await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
2005
2031
  };
2006
2032
 
@@ -2062,11 +2088,14 @@ async function setupFumadocs(config) {
2062
2088
  if (isCancel(template)) return exitCancelled("Operation cancelled");
2063
2089
  const commandWithArgs = `create-fumadocs-app@latest fumadocs --template ${TEMPLATES[template].value} --src --no-install --pm ${packageManager} --no-eslint --no-git`;
2064
2090
  const fumadocsInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2091
+ const s = spinner();
2092
+ s.start("Setting up Fumadocs...");
2065
2093
  await execa(fumadocsInitCommand, {
2066
2094
  cwd: path.join(projectDir, "apps"),
2067
2095
  env: { CI: "true" },
2068
2096
  shell: true
2069
2097
  });
2098
+ s.stop("Fumadocs setup complete!");
2070
2099
  const fumadocsDir = path.join(projectDir, "apps", "fumadocs");
2071
2100
  const packageJsonPath = path.join(fumadocsDir, "package.json");
2072
2101
  if (await fs.pathExists(packageJsonPath)) {
@@ -2307,11 +2336,14 @@ async function setupUltracite(config, hasHusky) {
2307
2336
  if (hasHusky) ultraciteArgs.push("--integrations", "husky", "lint-staged");
2308
2337
  const commandWithArgs = `ultracite@latest ${ultraciteArgs.join(" ")} --skip-install`;
2309
2338
  const ultraciteInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2339
+ const s = spinner();
2340
+ s.start("Setting up Ultracite...");
2310
2341
  await execa(ultraciteInitCommand, {
2311
2342
  cwd: projectDir,
2312
2343
  env: { CI: "true" },
2313
2344
  shell: true
2314
2345
  });
2346
+ s.stop("Ultracite setup complete!");
2315
2347
  if (hasHusky) await addPackageDependency({
2316
2348
  devDependencies: ["husky", "lint-staged"],
2317
2349
  projectDir
@@ -2655,8 +2687,12 @@ async function processTemplate(srcPath, destPath, context) {
2655
2687
  }
2656
2688
  handlebars.registerHelper("eq", (a, b) => a === b);
2657
2689
  handlebars.registerHelper("ne", (a, b) => a !== b);
2658
- handlebars.registerHelper("and", (a, b) => a && b);
2659
- handlebars.registerHelper("or", (a, b) => a || b);
2690
+ handlebars.registerHelper("and", (...args) => {
2691
+ return args.slice(0, -1).every((value) => value);
2692
+ });
2693
+ handlebars.registerHelper("or", (...args) => {
2694
+ return args.slice(0, -1).some((value) => value);
2695
+ });
2660
2696
  handlebars.registerHelper("includes", (array, value) => Array.isArray(array) && array.includes(value));
2661
2697
 
2662
2698
  //#endregion
@@ -2718,6 +2754,10 @@ async function setupFrontendTemplates(projectDir, context) {
2718
2754
  const apiWebBaseDir = path.join(PKG_ROOT, `templates/api/${context.api}/web/react/base`);
2719
2755
  if (await fs.pathExists(apiWebBaseDir)) await processAndCopyFiles("**/*", apiWebBaseDir, webAppDir, context);
2720
2756
  }
2757
+ if (context.backend === "self" && reactFramework === "next" && context.api !== "none") {
2758
+ const apiFullstackDir = path.join(PKG_ROOT, `templates/api/${context.api}/fullstack/next`);
2759
+ if (await fs.pathExists(apiFullstackDir)) await processAndCopyFiles("**/*", apiFullstackDir, webAppDir, context);
2760
+ }
2721
2761
  }
2722
2762
  } else if (hasNuxtWeb) {
2723
2763
  const nuxtBaseDir = path.join(PKG_ROOT, "templates/frontend/nuxt");
@@ -2758,35 +2798,52 @@ async function setupFrontendTemplates(projectDir, context) {
2758
2798
  }
2759
2799
  }
2760
2800
  }
2761
- async function setupBackendFramework(projectDir, context) {
2762
- if (context.backend === "none") return;
2801
+ async function setupApiPackage(projectDir, context) {
2802
+ if (context.api === "none") return;
2803
+ const apiPackageDir = path.join(projectDir, "packages/api");
2804
+ await fs.ensureDir(apiPackageDir);
2805
+ const apiServerDir = path.join(PKG_ROOT, `templates/api/${context.api}/server`);
2806
+ if (await fs.pathExists(apiServerDir)) await processAndCopyFiles("**/*", apiServerDir, apiPackageDir, context);
2807
+ }
2808
+ async function setupDbPackage(projectDir, context) {
2809
+ if (context.database === "none" || context.orm === "none") return;
2810
+ const dbPackageDir = path.join(projectDir, "packages/db");
2811
+ await fs.ensureDir(dbPackageDir);
2812
+ const dbBaseDir = path.join(PKG_ROOT, "templates/db/base");
2813
+ if (await fs.pathExists(dbBaseDir)) await processAndCopyFiles("**/*", dbBaseDir, dbPackageDir, context);
2814
+ const dbOrmSrcDir = path.join(PKG_ROOT, `templates/db/${context.orm}/${context.database}`);
2815
+ if (await fs.pathExists(dbOrmSrcDir)) await processAndCopyFiles("**/*", dbOrmSrcDir, dbPackageDir, context);
2816
+ }
2817
+ async function setupConvexBackend(projectDir, context) {
2818
+ const serverAppDir = path.join(projectDir, "apps/server");
2819
+ if (await fs.pathExists(serverAppDir)) await fs.remove(serverAppDir);
2820
+ const convexBackendDestDir = path.join(projectDir, "packages/backend");
2821
+ const convexSrcDir = path.join(PKG_ROOT, "templates/backend/convex/packages/backend");
2822
+ await fs.ensureDir(convexBackendDestDir);
2823
+ if (await fs.pathExists(convexSrcDir)) await processAndCopyFiles("**/*", convexSrcDir, convexBackendDestDir, context);
2824
+ }
2825
+ async function setupServerApp(projectDir, context) {
2763
2826
  const serverAppDir = path.join(projectDir, "apps/server");
2764
- if (context.backend === "convex") {
2765
- if (await fs.pathExists(serverAppDir)) await fs.remove(serverAppDir);
2766
- const convexBackendDestDir = path.join(projectDir, "packages/backend");
2767
- const convexSrcDir = path.join(PKG_ROOT, "templates/backend/convex/packages/backend");
2768
- await fs.ensureDir(convexBackendDestDir);
2769
- if (await fs.pathExists(convexSrcDir)) await processAndCopyFiles("**/*", convexSrcDir, convexBackendDestDir, context);
2770
- return;
2771
- }
2772
2827
  await fs.ensureDir(serverAppDir);
2773
- const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/server-base");
2828
+ const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/base");
2774
2829
  if (await fs.pathExists(serverBaseDir)) await processAndCopyFiles("**/*", serverBaseDir, serverAppDir, context);
2775
2830
  const frameworkSrcDir = path.join(PKG_ROOT, `templates/backend/server/${context.backend}`);
2776
2831
  if (await fs.pathExists(frameworkSrcDir)) await processAndCopyFiles("**/*", frameworkSrcDir, serverAppDir, context, true);
2777
- if (context.api !== "none") {
2778
- const apiServerBaseDir = path.join(PKG_ROOT, `templates/api/${context.api}/server/base`);
2779
- if (await fs.pathExists(apiServerBaseDir)) await processAndCopyFiles("**/*", apiServerBaseDir, serverAppDir, context, true);
2780
- const apiServerFrameworkDir = path.join(PKG_ROOT, `templates/api/${context.api}/server/${context.backend}`);
2781
- if (await fs.pathExists(apiServerFrameworkDir)) await processAndCopyFiles("**/*", apiServerFrameworkDir, serverAppDir, context, true);
2782
- }
2783
2832
  }
2784
- async function setupDbOrmTemplates(projectDir, context) {
2785
- if (context.backend === "convex" || context.orm === "none" || context.database === "none") return;
2786
- const serverAppDir = path.join(projectDir, "apps/server");
2787
- await fs.ensureDir(serverAppDir);
2788
- const dbOrmSrcDir = path.join(PKG_ROOT, `templates/db/${context.orm}/${context.database}`);
2789
- if (await fs.pathExists(dbOrmSrcDir)) await processAndCopyFiles("**/*", dbOrmSrcDir, serverAppDir, context);
2833
+ async function setupBackendFramework(projectDir, context) {
2834
+ if (context.backend === "none") return;
2835
+ if (context.backend === "convex") {
2836
+ await setupConvexBackend(projectDir, context);
2837
+ return;
2838
+ }
2839
+ if (context.backend === "self") {
2840
+ await setupApiPackage(projectDir, context);
2841
+ await setupDbPackage(projectDir, context);
2842
+ return;
2843
+ }
2844
+ await setupServerApp(projectDir, context);
2845
+ await setupApiPackage(projectDir, context);
2846
+ await setupDbPackage(projectDir, context);
2790
2847
  }
2791
2848
  async function setupAuthTemplate(projectDir, context) {
2792
2849
  if (!context.auth || context.auth === "none") return;
@@ -2866,21 +2923,21 @@ async function setupAuthTemplate(projectDir, context) {
2866
2923
  }
2867
2924
  return;
2868
2925
  }
2869
- if (serverAppDirExists && context.backend !== "convex") {
2926
+ if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
2927
+ const authPackageDir = path.join(projectDir, "packages/auth");
2928
+ await fs.ensureDir(authPackageDir);
2870
2929
  const authServerBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/base`);
2871
- if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc, serverAppDir, context);
2872
- if (context.backend === "next") {
2873
- const authServerNextSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/next`);
2874
- if (await fs.pathExists(authServerNextSrc)) await processAndCopyFiles("**/*", authServerNextSrc, serverAppDir, context);
2875
- }
2930
+ if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc, authPackageDir, context);
2876
2931
  if (context.orm !== "none" && context.database !== "none") {
2932
+ const dbPackageDir = path.join(projectDir, "packages/db");
2933
+ await fs.ensureDir(dbPackageDir);
2877
2934
  const orm = context.orm;
2878
2935
  const db = context.database;
2879
2936
  let authDbSrc = "";
2880
2937
  if (orm === "drizzle") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/drizzle/${db}`);
2881
2938
  else if (orm === "prisma") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/prisma/${db}`);
2882
2939
  else if (orm === "mongoose") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/mongoose/${db}`);
2883
- if (authDbSrc && await fs.pathExists(authDbSrc)) await processAndCopyFiles("**/*", authDbSrc, serverAppDir, context);
2940
+ if (authDbSrc && await fs.pathExists(authDbSrc)) await processAndCopyFiles("**/*", authDbSrc, dbPackageDir, context);
2884
2941
  }
2885
2942
  }
2886
2943
  if ((hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb) && webAppDirExists) {
@@ -2896,6 +2953,10 @@ async function setupAuthTemplate(projectDir, context) {
2896
2953
  if (reactFramework) {
2897
2954
  const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/${reactFramework}`);
2898
2955
  if (await fs.pathExists(authWebFrameworkSrc)) await processAndCopyFiles("**/*", authWebFrameworkSrc, webAppDir, context);
2956
+ if (context.backend === "self" && reactFramework === "next") {
2957
+ const authFullstackSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/fullstack/next`);
2958
+ if (await fs.pathExists(authFullstackSrc)) await processAndCopyFiles("**/*", authFullstackSrc, webAppDir, context);
2959
+ }
2899
2960
  }
2900
2961
  } else if (hasNuxtWeb) {
2901
2962
  const authWebNuxtSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/nuxt`);
@@ -2926,9 +2987,11 @@ async function setupPaymentsTemplate(projectDir, context) {
2926
2987
  const webAppDir = path.join(projectDir, "apps/web");
2927
2988
  const serverAppDirExists = await fs.pathExists(serverAppDir);
2928
2989
  const webAppDirExists = await fs.pathExists(webAppDir);
2929
- if (serverAppDirExists && context.backend !== "convex") {
2990
+ if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
2991
+ const authPackageDir = path.join(projectDir, "packages/auth");
2992
+ await fs.ensureDir(authPackageDir);
2930
2993
  const paymentsServerSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/server/base`);
2931
- if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc, serverAppDir, context);
2994
+ if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc, authPackageDir, context);
2932
2995
  }
2933
2996
  const hasReactWeb = context.frontend.some((f) => [
2934
2997
  "tanstack-router",
@@ -3004,17 +3067,19 @@ async function setupExamplesTemplate(projectDir, context) {
3004
3067
  for (const example of context.examples) {
3005
3068
  if (example === "none") continue;
3006
3069
  const exampleBaseDir = path.join(PKG_ROOT, `templates/examples/${example}`);
3007
- if (serverAppDirExists && context.backend !== "convex" && context.backend !== "none") {
3070
+ if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex" && context.backend !== "none") {
3008
3071
  const exampleServerSrc = path.join(exampleBaseDir, "server");
3009
- if (example === "ai" && context.backend === "next") {
3010
- const aiNextServerSrc = path.join(exampleServerSrc, "next");
3011
- if (await fs.pathExists(aiNextServerSrc)) await processAndCopyFiles("**/*", aiNextServerSrc, serverAppDir, context, false);
3072
+ if (context.api !== "none") {
3073
+ const apiPackageDir = path.join(projectDir, "packages/api");
3074
+ await fs.ensureDir(apiPackageDir);
3075
+ const exampleOrmBaseSrc = path.join(exampleServerSrc, context.orm, "base");
3076
+ if (await fs.pathExists(exampleOrmBaseSrc)) await processAndCopyFiles("**/*", exampleOrmBaseSrc, apiPackageDir, context, false);
3012
3077
  }
3013
3078
  if (context.orm !== "none" && context.database !== "none") {
3014
- const exampleOrmBaseSrc = path.join(exampleServerSrc, context.orm, "base");
3015
- if (await fs.pathExists(exampleOrmBaseSrc)) await processAndCopyFiles("**/*", exampleOrmBaseSrc, serverAppDir, context, false);
3079
+ const dbPackageDir = path.join(projectDir, "packages/db");
3080
+ await fs.ensureDir(dbPackageDir);
3016
3081
  const exampleDbSchemaSrc = path.join(exampleServerSrc, context.orm, context.database);
3017
- if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc, serverAppDir, context, false);
3082
+ if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc, dbPackageDir, context, false);
3018
3083
  }
3019
3084
  }
3020
3085
  if (webAppDirExists) {
@@ -3034,6 +3099,10 @@ async function setupExamplesTemplate(projectDir, context) {
3034
3099
  if (reactFramework) {
3035
3100
  const exampleWebFrameworkSrc = path.join(exampleWebSrc, reactFramework);
3036
3101
  if (await fs.pathExists(exampleWebFrameworkSrc)) await processAndCopyFiles("**/*", exampleWebFrameworkSrc, webAppDir, context, false);
3102
+ if (context.backend === "self" && reactFramework === "next") {
3103
+ const exampleFullstackSrc = path.join(exampleBaseDir, "fullstack/next");
3104
+ if (await fs.pathExists(exampleFullstackSrc)) await processAndCopyFiles("**/*", exampleFullstackSrc, webAppDir, context, false);
3105
+ }
3037
3106
  }
3038
3107
  }
3039
3108
  } else if (hasNuxtWeb) {
@@ -3081,9 +3150,9 @@ async function handleExtras(projectDir, context) {
3081
3150
  }
3082
3151
  async function setupDockerComposeTemplates(projectDir, context) {
3083
3152
  if (context.dbSetup !== "docker" || context.database === "none") return;
3084
- const serverAppDir = path.join(projectDir, "apps/server");
3153
+ const dbPackageDir = path.join(projectDir, "packages/db");
3085
3154
  const dockerSrcDir = path.join(PKG_ROOT, `templates/db-setup/docker-compose/${context.database}`);
3086
- if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, serverAppDir, context);
3155
+ if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, dbPackageDir, context);
3087
3156
  }
3088
3157
  async function setupDeploymentTemplates(projectDir, context) {
3089
3158
  if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") if (context.webDeploy === "alchemy" && context.serverDeploy === "alchemy") {
@@ -3244,7 +3313,6 @@ async function setupAlchemyServerDeploy(serverDir, _packageManager) {
3244
3313
  "alchemy",
3245
3314
  "wrangler",
3246
3315
  "@types/node",
3247
- "dotenv",
3248
3316
  "@cloudflare/workers-types"
3249
3317
  ],
3250
3318
  projectDir: serverDir
@@ -3271,7 +3339,6 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
3271
3339
  dependencies: ["@opennextjs/cloudflare"],
3272
3340
  devDependencies: [
3273
3341
  "alchemy",
3274
- "dotenv",
3275
3342
  "wrangler",
3276
3343
  "@cloudflare/workers-types"
3277
3344
  ],
@@ -3307,7 +3374,6 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
3307
3374
  devDependencies: [
3308
3375
  "alchemy",
3309
3376
  "nitro-cloudflare-dev",
3310
- "dotenv",
3311
3377
  "wrangler"
3312
3378
  ],
3313
3379
  projectDir: webAppDir
@@ -3370,7 +3436,7 @@ async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, option
3370
3436
  const webAppDir = path.join(projectDir, "apps/web");
3371
3437
  if (!await fs.pathExists(webAppDir)) return;
3372
3438
  await addPackageDependency({
3373
- devDependencies: ["alchemy", "dotenv"],
3439
+ devDependencies: ["alchemy"],
3374
3440
  projectDir: webAppDir
3375
3441
  });
3376
3442
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3391,7 +3457,7 @@ async function setupSolidAlchemyDeploy(projectDir, _packageManager, options) {
3391
3457
  const webAppDir = path.join(projectDir, "apps/web");
3392
3458
  if (!await fs.pathExists(webAppDir)) return;
3393
3459
  await addPackageDependency({
3394
- devDependencies: ["alchemy", "dotenv"],
3460
+ devDependencies: ["alchemy"],
3395
3461
  projectDir: webAppDir
3396
3462
  });
3397
3463
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3412,11 +3478,7 @@ async function setupSvelteAlchemyDeploy(projectDir, _packageManager, options) {
3412
3478
  const webAppDir = path.join(projectDir, "apps/web");
3413
3479
  if (!await fs.pathExists(webAppDir)) return;
3414
3480
  await addPackageDependency({
3415
- devDependencies: [
3416
- "alchemy",
3417
- "@sveltejs/adapter-cloudflare",
3418
- "dotenv"
3419
- ],
3481
+ devDependencies: ["alchemy", "@sveltejs/adapter-cloudflare"],
3420
3482
  projectDir: webAppDir
3421
3483
  });
3422
3484
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3481,7 +3543,7 @@ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, opt
3481
3543
  const webAppDir = path.join(projectDir, "apps/web");
3482
3544
  if (!await fs.pathExists(webAppDir)) return;
3483
3545
  await addPackageDependency({
3484
- devDependencies: ["alchemy", "dotenv"],
3546
+ devDependencies: ["alchemy"],
3485
3547
  projectDir: webAppDir
3486
3548
  });
3487
3549
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3502,11 +3564,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3502
3564
  const webAppDir = path.join(projectDir, "apps/web");
3503
3565
  if (!await fs.pathExists(webAppDir)) return;
3504
3566
  await addPackageDependency({
3505
- devDependencies: [
3506
- "alchemy",
3507
- "dotenv",
3508
- "@cloudflare/vite-plugin"
3509
- ],
3567
+ devDependencies: ["alchemy", "@cloudflare/vite-plugin"],
3510
3568
  projectDir: webAppDir
3511
3569
  });
3512
3570
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3561,7 +3619,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3561
3619
  //#region src/helpers/deployment/alchemy/alchemy-combined-setup.ts
3562
3620
  async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
3563
3621
  await addPackageDependency({
3564
- devDependencies: ["alchemy", "dotenv"],
3622
+ devDependencies: ["alchemy"],
3565
3623
  projectDir
3566
3624
  });
3567
3625
  const rootPkgPath = path.join(projectDir, "package.json");
@@ -3901,18 +3959,137 @@ async function addDeploymentToProject(input) {
3901
3959
  }
3902
3960
  }
3903
3961
 
3962
+ //#endregion
3963
+ //#region src/utils/setup-catalogs.ts
3964
+ async function setupCatalogs(projectDir, options) {
3965
+ if (options.packageManager === "npm") return;
3966
+ const packagePaths = [
3967
+ "apps/server",
3968
+ "apps/web",
3969
+ "packages/api",
3970
+ "packages/db",
3971
+ "packages/auth",
3972
+ "packages/backend"
3973
+ ];
3974
+ const packagesInfo = [];
3975
+ for (const pkgPath of packagePaths) {
3976
+ const fullPath = path.join(projectDir, pkgPath);
3977
+ const pkgJsonPath = path.join(fullPath, "package.json");
3978
+ if (await fs.pathExists(pkgJsonPath)) {
3979
+ const pkgJson = await fs.readJson(pkgJsonPath);
3980
+ packagesInfo.push({
3981
+ path: fullPath,
3982
+ dependencies: pkgJson.dependencies || {},
3983
+ devDependencies: pkgJson.devDependencies || {}
3984
+ });
3985
+ }
3986
+ }
3987
+ const catalog = findDuplicateDependencies(packagesInfo, options.projectName);
3988
+ if (Object.keys(catalog).length === 0) return;
3989
+ if (options.packageManager === "bun") await setupBunCatalogs(projectDir, catalog);
3990
+ else if (options.packageManager === "pnpm") await setupPnpmCatalogs(projectDir, catalog);
3991
+ await updatePackageJsonsWithCatalogs(packagesInfo, catalog);
3992
+ }
3993
+ function findDuplicateDependencies(packagesInfo, projectName) {
3994
+ const depCount = /* @__PURE__ */ new Map();
3995
+ const projectScope = `@${projectName}/`;
3996
+ for (const pkg of packagesInfo) {
3997
+ const allDeps = {
3998
+ ...pkg.dependencies,
3999
+ ...pkg.devDependencies
4000
+ };
4001
+ for (const [depName, version] of Object.entries(allDeps)) {
4002
+ if (depName.startsWith(projectScope)) continue;
4003
+ if (version.startsWith("workspace:")) continue;
4004
+ const existing = depCount.get(depName);
4005
+ if (existing) existing.packages.push(pkg.path);
4006
+ else depCount.set(depName, {
4007
+ version,
4008
+ packages: [pkg.path]
4009
+ });
4010
+ }
4011
+ }
4012
+ const catalog = {};
4013
+ for (const [depName, info] of depCount.entries()) if (info.packages.length > 1) catalog[depName] = info.version;
4014
+ return catalog;
4015
+ }
4016
+ async function setupBunCatalogs(projectDir, catalog) {
4017
+ const rootPkgJsonPath = path.join(projectDir, "package.json");
4018
+ const rootPkgJson = await fs.readJson(rootPkgJsonPath);
4019
+ if (!rootPkgJson.workspaces) rootPkgJson.workspaces = {};
4020
+ if (Array.isArray(rootPkgJson.workspaces)) rootPkgJson.workspaces = {
4021
+ packages: rootPkgJson.workspaces,
4022
+ catalog
4023
+ };
4024
+ else if (typeof rootPkgJson.workspaces === "object") {
4025
+ if (!rootPkgJson.workspaces.catalog) rootPkgJson.workspaces.catalog = {};
4026
+ rootPkgJson.workspaces.catalog = {
4027
+ ...rootPkgJson.workspaces.catalog,
4028
+ ...catalog
4029
+ };
4030
+ }
4031
+ await fs.writeJson(rootPkgJsonPath, rootPkgJson, { spaces: 2 });
4032
+ }
4033
+ async function setupPnpmCatalogs(projectDir, catalog) {
4034
+ const workspaceYamlPath = path.join(projectDir, "pnpm-workspace.yaml");
4035
+ if (!await fs.pathExists(workspaceYamlPath)) return;
4036
+ const workspaceContent = await fs.readFile(workspaceYamlPath, "utf-8");
4037
+ const workspaceYaml = yaml.parse(workspaceContent);
4038
+ if (!workspaceYaml.catalog) workspaceYaml.catalog = {};
4039
+ workspaceYaml.catalog = {
4040
+ ...workspaceYaml.catalog,
4041
+ ...catalog
4042
+ };
4043
+ await fs.writeFile(workspaceYamlPath, yaml.stringify(workspaceYaml));
4044
+ }
4045
+ async function updatePackageJsonsWithCatalogs(packagesInfo, catalog) {
4046
+ for (const pkg of packagesInfo) {
4047
+ const pkgJsonPath = path.join(pkg.path, "package.json");
4048
+ const pkgJson = await fs.readJson(pkgJsonPath);
4049
+ let updated = false;
4050
+ if (pkgJson.dependencies) {
4051
+ for (const depName of Object.keys(pkgJson.dependencies)) if (catalog[depName]) {
4052
+ pkgJson.dependencies[depName] = "catalog:";
4053
+ updated = true;
4054
+ }
4055
+ }
4056
+ if (pkgJson.devDependencies) {
4057
+ for (const depName of Object.keys(pkgJson.devDependencies)) if (catalog[depName]) {
4058
+ pkgJson.devDependencies[depName] = "catalog:";
4059
+ updated = true;
4060
+ }
4061
+ }
4062
+ if (updated) await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
4063
+ }
4064
+ }
4065
+
3904
4066
  //#endregion
3905
4067
  //#region src/helpers/addons/examples-setup.ts
3906
4068
  async function setupExamples(config) {
3907
- const { examples, frontend, backend, projectDir } = config;
4069
+ const { examples, frontend, backend, projectDir, orm } = config;
3908
4070
  if (backend === "convex" || !examples || examples.length === 0 || examples[0] === "none") return;
4071
+ const apiDir = path.join(projectDir, "packages/api");
4072
+ if (await fs.pathExists(apiDir) && backend !== "none") {
4073
+ if (orm === "drizzle") await addPackageDependency({
4074
+ dependencies: ["drizzle-orm"],
4075
+ projectDir: apiDir
4076
+ });
4077
+ else if (orm === "prisma") await addPackageDependency({
4078
+ dependencies: ["@prisma/client"],
4079
+ projectDir: apiDir
4080
+ });
4081
+ else if (orm === "mongoose") await addPackageDependency({
4082
+ dependencies: ["mongoose"],
4083
+ projectDir: apiDir
4084
+ });
4085
+ }
3909
4086
  if (examples.includes("ai")) {
3910
4087
  const webClientDir = path.join(projectDir, "apps/web");
3911
4088
  const nativeClientDir = path.join(projectDir, "apps/native");
3912
- const serverDir = path.join(projectDir, "apps/server");
4089
+ const apiDir$1 = path.join(projectDir, "packages/api");
3913
4090
  const webClientDirExists = await fs.pathExists(webClientDir);
3914
4091
  const nativeClientDirExists = await fs.pathExists(nativeClientDir);
3915
- const serverDirExists = await fs.pathExists(serverDir);
4092
+ const apiDirExists = await fs.pathExists(apiDir$1);
3916
4093
  const hasNuxt = frontend.includes("nuxt");
3917
4094
  const hasSvelte = frontend.includes("svelte");
3918
4095
  const hasReactWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next") || frontend.includes("tanstack-start");
@@ -3933,9 +4110,13 @@ async function setupExamples(config) {
3933
4110
  dependencies: ["ai", "@ai-sdk/react"],
3934
4111
  projectDir: nativeClientDir
3935
4112
  });
3936
- if (serverDirExists && backend !== "none") await addPackageDependency({
4113
+ if (apiDirExists && backend !== "none") await addPackageDependency({
3937
4114
  dependencies: ["ai", "@ai-sdk/google"],
3938
- projectDir: serverDir
4115
+ projectDir: apiDir$1
4116
+ });
4117
+ if (backend === "self" && webClientDirExists) await addPackageDependency({
4118
+ dependencies: ["ai", "@ai-sdk/google"],
4119
+ projectDir: webClientDir
3939
4120
  });
3940
4121
  }
3941
4122
  }
@@ -4051,32 +4232,61 @@ function getConvexDependencies(frontend) {
4051
4232
  return deps;
4052
4233
  }
4053
4234
  async function setupApi(config) {
4054
- const { api, projectName, frontend, backend, packageManager, projectDir } = config;
4235
+ const { api, projectName, frontend, backend, packageManager, projectDir, auth } = config;
4055
4236
  const isConvex = backend === "convex";
4056
4237
  const webDir = path.join(projectDir, "apps/web");
4057
4238
  const nativeDir = path.join(projectDir, "apps/native");
4058
4239
  const serverDir = path.join(projectDir, "apps/server");
4059
4240
  const webDirExists = await fs.pathExists(webDir);
4060
4241
  const nativeDirExists = await fs.pathExists(nativeDir);
4061
- const serverDirExists = await fs.pathExists(serverDir);
4242
+ await fs.pathExists(serverDir);
4062
4243
  const frontendType = getFrontendType(frontend);
4063
4244
  if (!isConvex && api !== "none") {
4064
4245
  const apiDeps = getApiDependencies(api, frontendType);
4065
- if (serverDirExists && apiDeps.server) {
4246
+ const apiPackageDir = path.join(projectDir, "packages/api");
4247
+ if (apiDeps.server) {
4066
4248
  await addPackageDependency({
4067
4249
  dependencies: apiDeps.server.dependencies,
4068
- projectDir: serverDir
4250
+ projectDir: apiPackageDir
4251
+ });
4252
+ if (backend === "self" && webDirExists) await addPackageDependency({
4253
+ dependencies: apiDeps.server.dependencies,
4254
+ projectDir: webDir
4255
+ });
4256
+ const frameworkDeps = [];
4257
+ if (backend === "hono") frameworkDeps.push("hono");
4258
+ else if (backend === "elysia") frameworkDeps.push("elysia");
4259
+ else if (backend === "express") frameworkDeps.push("express", "@types/express");
4260
+ else if (backend === "fastify") frameworkDeps.push("fastify");
4261
+ else if (backend === "self") {
4262
+ if (frontend.includes("next")) frameworkDeps.push("next");
4263
+ }
4264
+ if (frameworkDeps.length > 0) await addPackageDependency({
4265
+ dependencies: frameworkDeps,
4266
+ projectDir: apiPackageDir
4069
4267
  });
4070
4268
  if (api === "trpc") {
4071
4269
  if (backend === "hono") await addPackageDependency({
4072
4270
  dependencies: ["@hono/trpc-server"],
4073
- projectDir: serverDir
4271
+ projectDir: apiPackageDir
4074
4272
  });
4075
4273
  else if (backend === "elysia") await addPackageDependency({
4076
4274
  dependencies: ["@elysiajs/trpc"],
4077
- projectDir: serverDir
4275
+ projectDir: apiPackageDir
4276
+ });
4277
+ else if (backend === "express") await addPackageDependency({
4278
+ dependencies: ["@trpc/server"],
4279
+ projectDir: apiPackageDir
4280
+ });
4281
+ else if (backend === "fastify") await addPackageDependency({
4282
+ dependencies: ["@trpc/server"],
4283
+ projectDir: apiPackageDir
4078
4284
  });
4079
4285
  }
4286
+ if (auth === "better-auth") await addPackageDependency({
4287
+ dependencies: ["better-auth"],
4288
+ projectDir: apiPackageDir
4289
+ });
4080
4290
  }
4081
4291
  if (webDirExists && apiDeps.web) await addPackageDependency({
4082
4292
  dependencies: apiDeps.web.dependencies,
@@ -4120,7 +4330,7 @@ async function setupApi(config) {
4120
4330
  //#endregion
4121
4331
  //#region src/helpers/core/backend-setup.ts
4122
4332
  async function setupBackendDependencies(config) {
4123
- const { backend, runtime, api, projectDir } = config;
4333
+ const { backend, runtime, api, auth, examples, projectDir } = config;
4124
4334
  if (backend === "convex") return;
4125
4335
  const framework = backend;
4126
4336
  const serverDir = path.join(projectDir, "apps/server");
@@ -4148,6 +4358,13 @@ async function setupBackendDependencies(config) {
4148
4358
  dependencies.push("fastify", "@fastify/cors");
4149
4359
  if (runtime === "node") devDependencies.push("tsx", "@types/node");
4150
4360
  }
4361
+ if (api === "trpc") {
4362
+ if (framework === "express") dependencies.push("@trpc/server");
4363
+ else if (framework === "fastify") dependencies.push("@trpc/server");
4364
+ else if (runtime === "workers") dependencies.push("@trpc/server");
4365
+ } else if (api === "orpc") dependencies.push("@orpc/server", "@orpc/openapi", "@orpc/zod");
4366
+ if (auth === "better-auth") dependencies.push("better-auth");
4367
+ if (examples.includes("ai")) dependencies.push("ai", "@ai-sdk/google");
4151
4368
  if (runtime === "bun") devDependencies.push("@types/bun");
4152
4369
  if (dependencies.length > 0 || devDependencies.length > 0) await addPackageDependency({
4153
4370
  dependencies,
@@ -4166,7 +4383,7 @@ async function setupAuth(config) {
4166
4383
  const nativeDir = path.join(projectDir, "apps/native");
4167
4384
  const clientDirExists = await fs.pathExists(clientDir);
4168
4385
  const nativeDirExists = await fs.pathExists(nativeDir);
4169
- const serverDirExists = await fs.pathExists(serverDir);
4386
+ await fs.pathExists(serverDir);
4170
4387
  try {
4171
4388
  if (backend === "convex") {
4172
4389
  if (auth === "clerk" && clientDirExists) {
@@ -4222,9 +4439,11 @@ async function setupAuth(config) {
4222
4439
  });
4223
4440
  return;
4224
4441
  }
4225
- if (serverDirExists && auth === "better-auth") await addPackageDependency({
4442
+ const authPackageDir = path.join(projectDir, "packages/auth");
4443
+ const authPackageDirExists = await fs.pathExists(authPackageDir);
4444
+ if (authPackageDirExists && auth === "better-auth") await addPackageDependency({
4226
4445
  dependencies: ["better-auth"],
4227
- projectDir: serverDir
4446
+ projectDir: authPackageDir
4228
4447
  });
4229
4448
  if (frontend.some((f) => [
4230
4449
  "react-router",
@@ -4246,9 +4465,9 @@ async function setupAuth(config) {
4246
4465
  dependencies: ["better-auth", "@better-auth/expo"],
4247
4466
  projectDir: nativeDir
4248
4467
  });
4249
- if (serverDirExists) await addPackageDependency({
4468
+ if (authPackageDirExists) await addPackageDependency({
4250
4469
  dependencies: ["@better-auth/expo"],
4251
- projectDir: serverDir
4470
+ projectDir: authPackageDir
4252
4471
  });
4253
4472
  }
4254
4473
  }
@@ -4267,6 +4486,34 @@ function generateAuthSecret(length = 32) {
4267
4486
 
4268
4487
  //#endregion
4269
4488
  //#region src/helpers/core/env-setup.ts
4489
+ function getClientServerVar(frontend, backend) {
4490
+ const hasNextJs = frontend.includes("next");
4491
+ const hasNuxt = frontend.includes("nuxt");
4492
+ const hasSvelte = frontend.includes("svelte");
4493
+ if (backend === "self") return {
4494
+ key: "",
4495
+ value: "",
4496
+ write: false
4497
+ };
4498
+ let key = "VITE_SERVER_URL";
4499
+ if (hasNextJs) key = "NEXT_PUBLIC_SERVER_URL";
4500
+ else if (hasNuxt) key = "NUXT_PUBLIC_SERVER_URL";
4501
+ else if (hasSvelte) key = "PUBLIC_SERVER_URL";
4502
+ return {
4503
+ key,
4504
+ value: "http://localhost:3000",
4505
+ write: true
4506
+ };
4507
+ }
4508
+ function getConvexVar(frontend) {
4509
+ const hasNextJs = frontend.includes("next");
4510
+ const hasNuxt = frontend.includes("nuxt");
4511
+ const hasSvelte = frontend.includes("svelte");
4512
+ if (hasNextJs) return "NEXT_PUBLIC_CONVEX_URL";
4513
+ if (hasNuxt) return "NUXT_PUBLIC_CONVEX_URL";
4514
+ if (hasSvelte) return "PUBLIC_CONVEX_URL";
4515
+ return "VITE_CONVEX_URL";
4516
+ }
4270
4517
  async function addEnvVariablesToFile(filePath, variables) {
4271
4518
  await fs.ensureDir(path.dirname(filePath));
4272
4519
  let envContent = "";
@@ -4325,22 +4572,13 @@ async function setupEnvironmentVariables(config) {
4325
4572
  if (hasReactRouter || hasTanStackRouter || hasTanStackStart || hasNextJs || hasNuxt || hasSolid || hasSvelte) {
4326
4573
  const clientDir = path.join(projectDir, "apps/web");
4327
4574
  if (await fs.pathExists(clientDir)) {
4328
- let envVarName = "VITE_SERVER_URL";
4329
- let serverUrl = "http://localhost:3000";
4330
- if (hasNextJs) envVarName = "NEXT_PUBLIC_SERVER_URL";
4331
- else if (hasNuxt) envVarName = "NUXT_PUBLIC_SERVER_URL";
4332
- else if (hasSvelte) envVarName = "PUBLIC_SERVER_URL";
4333
- if (backend === "convex") {
4334
- if (hasNextJs) envVarName = "NEXT_PUBLIC_CONVEX_URL";
4335
- else if (hasNuxt) envVarName = "NUXT_PUBLIC_CONVEX_URL";
4336
- else if (hasSvelte) envVarName = "PUBLIC_CONVEX_URL";
4337
- else envVarName = "VITE_CONVEX_URL";
4338
- serverUrl = "https://<YOUR_CONVEX_URL>";
4339
- }
4575
+ const baseVar = getClientServerVar(frontend, backend);
4576
+ const envVarName = backend === "convex" ? getConvexVar(frontend) : baseVar.key;
4577
+ const serverUrl = backend === "convex" ? "https://<YOUR_CONVEX_URL>" : baseVar.value;
4340
4578
  const clientVars = [{
4341
4579
  key: envVarName,
4342
4580
  value: serverUrl,
4343
- condition: true
4581
+ condition: backend === "convex" ? true : baseVar.write
4344
4582
  }];
4345
4583
  if (backend === "convex" && auth === "clerk") {
4346
4584
  if (hasNextJs) clientVars.push({
@@ -4431,10 +4669,9 @@ async function setupEnvironmentVariables(config) {
4431
4669
  return;
4432
4670
  }
4433
4671
  const serverDir = path.join(projectDir, "apps/server");
4434
- if (!await fs.pathExists(serverDir)) return;
4435
- const envPath = path.join(serverDir, ".env");
4436
4672
  let corsOrigin = "http://localhost:3001";
4437
- if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
4673
+ if (backend === "self") corsOrigin = "http://localhost:3001";
4674
+ else if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
4438
4675
  let databaseUrl = null;
4439
4676
  if (database !== "none" && dbSetup === "none") switch (database) {
4440
4677
  case "postgres":
@@ -4448,15 +4685,13 @@ async function setupEnvironmentVariables(config) {
4448
4685
  break;
4449
4686
  case "sqlite":
4450
4687
  if (config.runtime === "workers") databaseUrl = "http://127.0.0.1:8080";
4451
- else databaseUrl = "file:./local.db";
4688
+ else {
4689
+ const dbAppDir = backend === "self" ? "apps/web" : "apps/server";
4690
+ databaseUrl = `file:${path.join(config.projectDir, dbAppDir, "local.db")}`;
4691
+ }
4452
4692
  break;
4453
4693
  }
4454
4694
  const serverVars = [
4455
- {
4456
- key: "CORS_ORIGIN",
4457
- value: corsOrigin,
4458
- condition: true
4459
- },
4460
4695
  {
4461
4696
  key: "BETTER_AUTH_SECRET",
4462
4697
  value: generateAuthSecret(),
@@ -4464,19 +4699,9 @@ async function setupEnvironmentVariables(config) {
4464
4699
  },
4465
4700
  {
4466
4701
  key: "BETTER_AUTH_URL",
4467
- value: "http://localhost:3000",
4702
+ value: backend === "self" ? "http://localhost:3001" : "http://localhost:3000",
4468
4703
  condition: !!auth
4469
4704
  },
4470
- {
4471
- key: "DATABASE_URL",
4472
- value: databaseUrl,
4473
- condition: database !== "none" && dbSetup === "none"
4474
- },
4475
- {
4476
- key: "GOOGLE_GENERATIVE_AI_API_KEY",
4477
- value: "",
4478
- condition: examples?.includes("ai") || false
4479
- },
4480
4705
  {
4481
4706
  key: "POLAR_ACCESS_TOKEN",
4482
4707
  value: "",
@@ -4486,9 +4711,27 @@ async function setupEnvironmentVariables(config) {
4486
4711
  key: "POLAR_SUCCESS_URL",
4487
4712
  value: `${corsOrigin}/success?checkout_id={CHECKOUT_ID}`,
4488
4713
  condition: config.payments === "polar"
4714
+ },
4715
+ {
4716
+ key: "CORS_ORIGIN",
4717
+ value: corsOrigin,
4718
+ condition: true
4719
+ },
4720
+ {
4721
+ key: "GOOGLE_GENERATIVE_AI_API_KEY",
4722
+ value: "",
4723
+ condition: examples?.includes("ai") || false
4724
+ },
4725
+ {
4726
+ key: "DATABASE_URL",
4727
+ value: databaseUrl,
4728
+ condition: database !== "none" && dbSetup === "none"
4489
4729
  }
4490
4730
  ];
4491
- await addEnvVariablesToFile(envPath, serverVars);
4731
+ if (backend === "self") {
4732
+ const webDir = path.join(projectDir, "apps/web");
4733
+ if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverVars);
4734
+ } else if (await fs.pathExists(serverDir)) await addEnvVariablesToFile(path.join(serverDir, ".env"), serverVars);
4492
4735
  const isUnifiedAlchemy = webDeploy === "alchemy" && serverDeploy === "alchemy";
4493
4736
  const isIndividualAlchemy = webDeploy === "alchemy" || serverDeploy === "alchemy";
4494
4737
  if (isUnifiedAlchemy) {
@@ -4508,12 +4751,15 @@ async function setupEnvironmentVariables(config) {
4508
4751
  }]);
4509
4752
  }
4510
4753
  if (serverDeploy === "alchemy") {
4511
- const serverDir$1 = path.join(projectDir, "apps/server");
4512
- if (await fs.pathExists(serverDir$1)) await addEnvVariablesToFile(path.join(serverDir$1, ".env"), [{
4754
+ const serverAlchemyVars = [{
4513
4755
  key: "ALCHEMY_PASSWORD",
4514
4756
  value: "please-change-this",
4515
4757
  condition: true
4516
- }]);
4758
+ }];
4759
+ if (backend === "self") {
4760
+ const webDir = path.join(projectDir, "apps/web");
4761
+ if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverAlchemyVars);
4762
+ } else await addEnvVariablesToFile(path.join(serverDir, ".env"), serverAlchemyVars);
4517
4763
  }
4518
4764
  }
4519
4765
  }
@@ -4521,9 +4767,10 @@ async function setupEnvironmentVariables(config) {
4521
4767
  //#endregion
4522
4768
  //#region src/helpers/database-providers/d1-setup.ts
4523
4769
  async function setupCloudflareD1(config) {
4524
- const { projectDir, serverDeploy, orm } = config;
4770
+ const { projectDir, serverDeploy, orm, backend } = config;
4525
4771
  if (serverDeploy === "wrangler") {
4526
- const envPath = path.join(projectDir, "apps/server", ".env");
4772
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
4773
+ const envPath = path.join(projectDir, targetApp, ".env");
4527
4774
  const variables = [
4528
4775
  {
4529
4776
  key: "CLOUDFLARE_ACCOUNT_ID",
@@ -4546,16 +4793,17 @@ async function setupCloudflareD1(config) {
4546
4793
  } catch (_err) {}
4547
4794
  }
4548
4795
  if ((serverDeploy === "wrangler" || serverDeploy === "alchemy") && orm === "prisma") {
4549
- const envPath = path.join(projectDir, "apps/server", ".env");
4796
+ const targetApp2 = backend === "self" ? "apps/web" : "apps/server";
4797
+ const envPath = path.join(projectDir, targetApp2, ".env");
4550
4798
  const variables = [{
4551
4799
  key: "DATABASE_URL",
4552
- value: "file:./local.db",
4800
+ value: `file:${path.join(projectDir, "apps/server", "local.db")}`,
4553
4801
  condition: true
4554
4802
  }];
4555
4803
  try {
4556
4804
  await addEnvVariablesToFile(envPath, variables);
4557
4805
  } catch (_err) {}
4558
- const serverDir = path.join(projectDir, "apps/server");
4806
+ const serverDir = path.join(projectDir, backend === "self" ? "apps/web" : "apps/server");
4559
4807
  await addPackageDependency({
4560
4808
  dependencies: ["@prisma/adapter-d1"],
4561
4809
  projectDir: serverDir
@@ -4606,14 +4854,13 @@ async function commandExists(command) {
4606
4854
  //#endregion
4607
4855
  //#region src/helpers/database-providers/mongodb-atlas-setup.ts
4608
4856
  async function checkAtlasCLI() {
4609
- const s = spinner();
4610
- s.start("Checking for MongoDB Atlas CLI...");
4611
4857
  try {
4612
4858
  const exists = await commandExists("atlas");
4613
- s.stop(exists ? "MongoDB Atlas CLI found" : pc.yellow("MongoDB Atlas CLI not found"));
4859
+ if (exists) log.info("MongoDB Atlas CLI found");
4860
+ else log.warn(pc.yellow("MongoDB Atlas CLI not found"));
4614
4861
  return exists;
4615
4862
  } catch (_error) {
4616
- s.stop(pc.red("Error checking MongoDB Atlas CLI"));
4863
+ log.error(pc.red("Error checking MongoDB Atlas CLI"));
4617
4864
  return false;
4618
4865
  }
4619
4866
  }
@@ -4624,12 +4871,13 @@ async function initMongoDBAtlas(serverDir) {
4624
4871
  log.info(pc.yellow("Please install it from: https://www.mongodb.com/docs/atlas/cli/current/install-atlas-cli/"));
4625
4872
  return null;
4626
4873
  }
4627
- log.info(pc.blue("Running MongoDB Atlas setup..."));
4874
+ log.info("Running MongoDB Atlas setup...");
4628
4875
  await execa("atlas", ["deployments", "setup"], {
4629
4876
  cwd: serverDir,
4877
+ shell: true,
4630
4878
  stdio: "inherit"
4631
4879
  });
4632
- log.info(pc.green("MongoDB Atlas deployment ready"));
4880
+ log.success("MongoDB Atlas deployment ready");
4633
4881
  const connectionString = await text({
4634
4882
  message: "Enter your MongoDB connection string:",
4635
4883
  placeholder: "mongodb+srv://username:password@cluster.mongodb.net/database",
@@ -4648,9 +4896,10 @@ async function initMongoDBAtlas(serverDir) {
4648
4896
  return null;
4649
4897
  }
4650
4898
  }
4651
- async function writeEnvFile$3(projectDir, config) {
4899
+ async function writeEnvFile$3(projectDir, backend, config) {
4652
4900
  try {
4653
- const envPath = path.join(projectDir, "apps/server", ".env");
4901
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
4902
+ const envPath = path.join(projectDir, targetApp, ".env");
4654
4903
  const variables = [{
4655
4904
  key: "DATABASE_URL",
4656
4905
  value: config?.connectionString ?? "mongodb://localhost:27017/mydb",
@@ -4679,16 +4928,14 @@ ${pc.green("MongoDB Atlas Manual Setup Instructions:")}
4679
4928
  `);
4680
4929
  }
4681
4930
  async function setupMongoDBAtlas(config, cliInput) {
4682
- const { projectDir } = config;
4931
+ const { projectDir, backend } = config;
4683
4932
  const manualDb = cliInput?.manualDb ?? false;
4684
- const mainSpinner = spinner();
4685
- mainSpinner.start("Setting up MongoDB Atlas...");
4686
- const serverDir = path.join(projectDir, "apps/server");
4933
+ const serverDir = path.join(projectDir, "packages/db");
4687
4934
  try {
4688
4935
  await fs.ensureDir(serverDir);
4689
4936
  if (manualDb) {
4690
- mainSpinner.stop("MongoDB Atlas manual setup selected");
4691
- await writeEnvFile$3(projectDir);
4937
+ log.info("MongoDB Atlas manual setup selected");
4938
+ await writeEnvFile$3(projectDir, backend);
4692
4939
  displayManualSetupInstructions$3();
4693
4940
  return;
4694
4941
  }
@@ -4707,26 +4954,24 @@ async function setupMongoDBAtlas(config, cliInput) {
4707
4954
  });
4708
4955
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
4709
4956
  if (mode === "manual") {
4710
- mainSpinner.stop("MongoDB Atlas manual setup selected");
4711
- await writeEnvFile$3(projectDir);
4957
+ log.info("MongoDB Atlas manual setup selected");
4958
+ await writeEnvFile$3(projectDir, backend);
4712
4959
  displayManualSetupInstructions$3();
4713
4960
  return;
4714
4961
  }
4715
- mainSpinner.stop("MongoDB Atlas setup ready");
4716
4962
  const config$1 = await initMongoDBAtlas(serverDir);
4717
4963
  if (config$1) {
4718
- await writeEnvFile$3(projectDir, config$1);
4964
+ await writeEnvFile$3(projectDir, backend, config$1);
4719
4965
  log.success(pc.green("MongoDB Atlas setup complete! Connection saved to .env file."));
4720
4966
  } else {
4721
4967
  log.warn(pc.yellow("Falling back to local MongoDB configuration"));
4722
- await writeEnvFile$3(projectDir);
4968
+ await writeEnvFile$3(projectDir, backend);
4723
4969
  displayManualSetupInstructions$3();
4724
4970
  }
4725
4971
  } catch (error) {
4726
- mainSpinner.stop(pc.red("MongoDB Atlas setup failed"));
4727
4972
  consola.error(pc.red(`Error during MongoDB Atlas setup: ${error instanceof Error ? error.message : String(error)}`));
4728
4973
  try {
4729
- await writeEnvFile$3(projectDir);
4974
+ await writeEnvFile$3(projectDir, backend);
4730
4975
  displayManualSetupInstructions$3();
4731
4976
  } catch {}
4732
4977
  }
@@ -4783,7 +5028,7 @@ async function executeNeonCommand(packageManager, commandArgsString, spinnerText
4783
5028
  }
4784
5029
  async function createNeonProject(projectName, regionId, packageManager) {
4785
5030
  try {
4786
- const commandArgsString = `neonctl projects create --name ${projectName} --region-id ${regionId} --output json`;
5031
+ const commandArgsString = `neonctl@latest projects create --name ${projectName} --region-id ${regionId} --output json`;
4787
5032
  const { stdout } = await executeNeonCommand(packageManager, commandArgsString, `Creating Neon project "${projectName}"...`);
4788
5033
  const response = JSON.parse(stdout);
4789
5034
  if (response.project && response.connection_uris && response.connection_uris.length > 0) {
@@ -4803,8 +5048,9 @@ async function createNeonProject(projectName, regionId, packageManager) {
4803
5048
  consola$1.error(pc.red("Failed to create Neon project"));
4804
5049
  }
4805
5050
  }
4806
- async function writeEnvFile$2(projectDir, config) {
4807
- const envPath = path.join(projectDir, "apps/server", ".env");
5051
+ async function writeEnvFile$2(projectDir, backend, config) {
5052
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5053
+ const envPath = path.join(projectDir, targetApp, ".env");
4808
5054
  const variables = [{
4809
5055
  key: "DATABASE_URL",
4810
5056
  value: config?.connectionString ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
@@ -4813,16 +5059,17 @@ async function writeEnvFile$2(projectDir, config) {
4813
5059
  await addEnvVariablesToFile(envPath, variables);
4814
5060
  return true;
4815
5061
  }
4816
- async function setupWithNeonDb(projectDir, packageManager) {
5062
+ async function setupWithNeonDb(projectDir, packageManager, backend) {
4817
5063
  try {
4818
5064
  const s = spinner();
4819
5065
  s.start("Creating Neon database using neondb...");
4820
- const serverDir = path.join(projectDir, "apps/server");
4821
- await fs.ensureDir(serverDir);
4822
- const packageCmd = getPackageExecutionCommand(packageManager, "neondb --yes");
5066
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5067
+ const targetDir = path.join(projectDir, targetApp);
5068
+ await fs.ensureDir(targetDir);
5069
+ const packageCmd = getPackageExecutionCommand(packageManager, "neondb@latest --yes");
4823
5070
  await execa(packageCmd, {
4824
5071
  shell: true,
4825
- cwd: serverDir
5072
+ cwd: targetDir
4826
5073
  });
4827
5074
  s.stop(pc.green("Neon database created successfully!"));
4828
5075
  return true;
@@ -4831,23 +5078,23 @@ async function setupWithNeonDb(projectDir, packageManager) {
4831
5078
  throw error;
4832
5079
  }
4833
5080
  }
4834
- function displayManualSetupInstructions$2() {
5081
+ function displayManualSetupInstructions$2(target) {
4835
5082
  log.info(`Manual Neon PostgreSQL Setup Instructions:
4836
5083
 
4837
5084
  1. Visit https://neon.tech and create an account
4838
5085
  2. Create a new project from the dashboard
4839
5086
  3. Get your connection string
4840
- 4. Add the database URL to the .env file in apps/server/.env
5087
+ 4. Add the database URL to the .env file in ${target}/.env
4841
5088
 
4842
5089
  DATABASE_URL="your_connection_string"`);
4843
5090
  }
4844
5091
  async function setupNeonPostgres(config, cliInput) {
4845
- const { packageManager, projectDir } = config;
5092
+ const { packageManager, projectDir, backend } = config;
4846
5093
  const manualDb = cliInput?.manualDb ?? false;
4847
5094
  try {
4848
5095
  if (manualDb) {
4849
- await writeEnvFile$2(projectDir);
4850
- displayManualSetupInstructions$2();
5096
+ await writeEnvFile$2(projectDir, backend);
5097
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
4851
5098
  return;
4852
5099
  }
4853
5100
  const mode = await select({
@@ -4865,8 +5112,8 @@ async function setupNeonPostgres(config, cliInput) {
4865
5112
  });
4866
5113
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
4867
5114
  if (mode === "manual") {
4868
- await writeEnvFile$2(projectDir);
4869
- displayManualSetupInstructions$2();
5115
+ await writeEnvFile$2(projectDir, backend);
5116
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
4870
5117
  return;
4871
5118
  }
4872
5119
  const setupMethod = await select({
@@ -4883,7 +5130,7 @@ async function setupNeonPostgres(config, cliInput) {
4883
5130
  initialValue: "neondb"
4884
5131
  });
4885
5132
  if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
4886
- if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager);
5133
+ if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager, backend);
4887
5134
  else {
4888
5135
  const suggestedProjectName = path.basename(projectDir);
4889
5136
  const projectName = await text({
@@ -4901,22 +5148,22 @@ async function setupNeonPostgres(config, cliInput) {
4901
5148
  if (!neonConfig) throw new Error("Failed to create project - couldn't get connection information");
4902
5149
  const finalSpinner = spinner();
4903
5150
  finalSpinner.start("Configuring database connection");
4904
- await fs.ensureDir(path.join(projectDir, "apps/server"));
4905
- await writeEnvFile$2(projectDir, neonConfig);
5151
+ await writeEnvFile$2(projectDir, backend, neonConfig);
4906
5152
  finalSpinner.stop("Neon database configured!");
4907
5153
  }
4908
5154
  } catch (error) {
4909
5155
  if (error instanceof Error) consola$1.error(pc.red(error.message));
4910
- await writeEnvFile$2(projectDir);
4911
- displayManualSetupInstructions$2();
5156
+ await writeEnvFile$2(projectDir, backend);
5157
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
4912
5158
  }
4913
5159
  }
4914
5160
 
4915
5161
  //#endregion
4916
5162
  //#region src/helpers/database-providers/planetscale-setup.ts
4917
5163
  async function setupPlanetScale(config) {
4918
- const { projectDir, database, orm } = config;
4919
- const envPath = path.join(projectDir, "apps/server", ".env");
5164
+ const { projectDir, database, orm, backend } = config;
5165
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5166
+ const envPath = path.join(projectDir, targetApp, ".env");
4920
5167
  if (database === "mysql" && orm === "drizzle") {
4921
5168
  const variables = [
4922
5169
  {
@@ -4940,7 +5187,7 @@ async function setupPlanetScale(config) {
4940
5187
  condition: true
4941
5188
  }
4942
5189
  ];
4943
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5190
+ await fs.ensureDir(path.join(projectDir, targetApp));
4944
5191
  await addEnvVariablesToFile(envPath, variables);
4945
5192
  }
4946
5193
  if (database === "postgres" && orm === "prisma") {
@@ -4949,7 +5196,7 @@ async function setupPlanetScale(config) {
4949
5196
  value: "postgresql://username:password@host/database?sslaccept=strict",
4950
5197
  condition: true
4951
5198
  }];
4952
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5199
+ await fs.ensureDir(path.join(projectDir, targetApp));
4953
5200
  await addEnvVariablesToFile(envPath, variables);
4954
5201
  }
4955
5202
  if (database === "postgres" && orm === "drizzle") {
@@ -4958,7 +5205,7 @@ async function setupPlanetScale(config) {
4958
5205
  value: "postgresql://username:password@host/database?sslmode=verify-full",
4959
5206
  condition: true
4960
5207
  }];
4961
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5208
+ await fs.ensureDir(path.join(projectDir, targetApp));
4962
5209
  await addEnvVariablesToFile(envPath, variables);
4963
5210
  }
4964
5211
  if (database === "mysql" && orm === "prisma") {
@@ -4967,7 +5214,7 @@ async function setupPlanetScale(config) {
4967
5214
  value: "mysql://username:password@host/database?sslaccept=strict",
4968
5215
  condition: true
4969
5216
  }];
4970
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5217
+ await fs.ensureDir(path.join(projectDir, targetApp));
4971
5218
  await addEnvVariablesToFile(envPath, variables);
4972
5219
  }
4973
5220
  }
@@ -5037,7 +5284,7 @@ async function initPrismaDatabase(serverDir, packageManager) {
5037
5284
  try {
5038
5285
  const prismaDir = path.join(serverDir, "prisma");
5039
5286
  await fs.ensureDir(prismaDir);
5040
- log.info("Starting Prisma PostgreSQL setup. Please follow the instructions below:");
5287
+ log.info("Starting Prisma PostgreSQL setup.");
5041
5288
  const prismaInitCommand = getPackageExecutionCommand(packageManager, "prisma init --db");
5042
5289
  await execa(prismaInitCommand, {
5043
5290
  cwd: serverDir,
@@ -5059,9 +5306,10 @@ async function initPrismaDatabase(serverDir, packageManager) {
5059
5306
  return null;
5060
5307
  }
5061
5308
  }
5062
- async function writeEnvFile$1(projectDir, config) {
5309
+ async function writeEnvFile$1(projectDir, backend, config) {
5063
5310
  try {
5064
- const envPath = path.join(projectDir, "apps/server", ".env");
5311
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5312
+ const envPath = path.join(projectDir, targetApp, ".env");
5065
5313
  const variables = [{
5066
5314
  key: "DATABASE_URL",
5067
5315
  value: config?.databaseUrl ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
@@ -5077,31 +5325,32 @@ async function writeEnvFile$1(projectDir, config) {
5077
5325
  consola$1.error("Failed to update environment configuration");
5078
5326
  }
5079
5327
  }
5080
- async function addDotenvImportToPrismaConfig(projectDir) {
5328
+ async function addDotenvImportToPrismaConfig(projectDir, backend) {
5081
5329
  try {
5082
- const prismaConfigPath = path.join(projectDir, "apps/server/prisma.config.ts");
5330
+ const prismaConfigPath = path.join(projectDir, "packages/db/prisma.config.ts");
5083
5331
  let content = await fs.readFile(prismaConfigPath, "utf8");
5084
- content = `import "dotenv/config";\n${content}`;
5332
+ content = `import dotenv from "dotenv";\ndotenv.config({ path: "${backend === "self" ? "../../apps/web/.env" : "../../apps/server/.env"}" });\n${content}`;
5085
5333
  await fs.writeFile(prismaConfigPath, content);
5086
5334
  } catch (_error) {
5087
5335
  consola$1.error("Failed to update prisma.config.ts");
5088
5336
  }
5089
5337
  }
5090
- function displayManualSetupInstructions$1() {
5338
+ function displayManualSetupInstructions$1(target) {
5091
5339
  log.info(`Manual Prisma PostgreSQL Setup Instructions:
5092
5340
 
5093
5341
  1. Visit https://console.prisma.io and create an account
5094
5342
  2. Create a new PostgreSQL database from the dashboard
5095
5343
  3. Get your database URL
5096
- 4. Add the database URL to the .env file in apps/server/.env
5344
+ 4. Add the database URL to the .env file in ${target}/.env
5097
5345
 
5098
5346
  DATABASE_URL="your_database_url"`);
5099
5347
  }
5100
- async function addPrismaAccelerateExtension(serverDir) {
5348
+ async function addPrismaAccelerateExtension(projectDir) {
5101
5349
  try {
5350
+ const dbPackageDir = path.join(projectDir, "packages/db");
5102
5351
  await addPackageDependency({
5103
5352
  dependencies: ["@prisma/extension-accelerate"],
5104
- projectDir: serverDir
5353
+ projectDir: dbPackageDir
5105
5354
  });
5106
5355
  return true;
5107
5356
  } catch (_error) {
@@ -5110,14 +5359,14 @@ async function addPrismaAccelerateExtension(serverDir) {
5110
5359
  }
5111
5360
  }
5112
5361
  async function setupPrismaPostgres(config, cliInput) {
5113
- const { packageManager, projectDir, orm } = config;
5362
+ const { packageManager, projectDir, orm, backend } = config;
5114
5363
  const manualDb = cliInput?.manualDb ?? false;
5115
- const serverDir = path.join(projectDir, "apps/server");
5364
+ const dbDir = path.join(projectDir, "packages/db");
5116
5365
  try {
5117
- await fs.ensureDir(serverDir);
5366
+ await fs.ensureDir(dbDir);
5118
5367
  if (manualDb) {
5119
- await writeEnvFile$1(projectDir);
5120
- displayManualSetupInstructions$1();
5368
+ await writeEnvFile$1(projectDir, backend);
5369
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5121
5370
  return;
5122
5371
  }
5123
5372
  const mode = await select({
@@ -5135,8 +5384,8 @@ async function setupPrismaPostgres(config, cliInput) {
5135
5384
  });
5136
5385
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5137
5386
  if (mode === "manual") {
5138
- await writeEnvFile$1(projectDir);
5139
- displayManualSetupInstructions$1();
5387
+ await writeEnvFile$1(projectDir, backend);
5388
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5140
5389
  return;
5141
5390
  }
5142
5391
  const setupOptions = [{
@@ -5156,26 +5405,26 @@ async function setupPrismaPostgres(config, cliInput) {
5156
5405
  });
5157
5406
  if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
5158
5407
  let prismaConfig = null;
5159
- if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(serverDir, packageManager, orm);
5160
- else prismaConfig = await initPrismaDatabase(serverDir, packageManager);
5408
+ if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(dbDir, packageManager, orm);
5409
+ else prismaConfig = await initPrismaDatabase(dbDir, packageManager);
5161
5410
  if (prismaConfig) {
5162
- await writeEnvFile$1(projectDir, prismaConfig);
5411
+ await writeEnvFile$1(projectDir, backend, prismaConfig);
5163
5412
  if (orm === "prisma") {
5164
- await addDotenvImportToPrismaConfig(projectDir);
5165
- await addPrismaAccelerateExtension(serverDir);
5413
+ await addDotenvImportToPrismaConfig(projectDir, backend);
5414
+ await addPrismaAccelerateExtension(projectDir);
5166
5415
  }
5167
5416
  const connectionType = orm === "drizzle" ? "direct connection" : "Prisma Accelerate";
5168
5417
  log.success(pc.green(`Prisma Postgres database configured successfully with ${connectionType}!`));
5169
5418
  if (prismaConfig.claimUrl) log.info(pc.blue(`Claim URL saved to .env: ${prismaConfig.claimUrl}`));
5170
5419
  } else {
5171
- await writeEnvFile$1(projectDir);
5172
- displayManualSetupInstructions$1();
5420
+ await writeEnvFile$1(projectDir, backend);
5421
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5173
5422
  }
5174
5423
  } catch (error) {
5175
5424
  consola$1.error(pc.red(`Error during Prisma Postgres setup: ${error instanceof Error ? error.message : String(error)}`));
5176
5425
  try {
5177
- await writeEnvFile$1(projectDir);
5178
- displayManualSetupInstructions$1();
5426
+ await writeEnvFile$1(projectDir, backend);
5427
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5179
5428
  } catch {}
5180
5429
  log.info("Setup completed with manual configuration required.");
5181
5430
  }
@@ -5183,9 +5432,10 @@ async function setupPrismaPostgres(config, cliInput) {
5183
5432
 
5184
5433
  //#endregion
5185
5434
  //#region src/helpers/database-providers/supabase-setup.ts
5186
- async function writeSupabaseEnvFile(projectDir, databaseUrl) {
5435
+ async function writeSupabaseEnvFile(projectDir, backend, databaseUrl) {
5187
5436
  try {
5188
- const envPath = path.join(projectDir, "apps/server", ".env");
5437
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5438
+ const envPath = path.join(projectDir, targetApp, ".env");
5189
5439
  const dbUrlToUse = databaseUrl || "postgresql://postgres:postgres@127.0.0.1:54322/postgres";
5190
5440
  await addEnvVariablesToFile(envPath, [{
5191
5441
  key: "DATABASE_URL",
@@ -5262,23 +5512,23 @@ function displayManualSupabaseInstructions(output) {
5262
5512
  log.info(`"Manual Supabase Setup Instructions:"
5263
5513
  1. Ensure Docker is installed and running.
5264
5514
  2. Install the Supabase CLI (e.g., \`npm install -g supabase\`).
5265
- 3. Run \`supabase init\` in your project's \`apps/server\` directory.
5266
- 4. Run \`supabase start\` in your project's \`apps/server\` directory.
5515
+ 3. Run \`supabase init\` in your project's \`packages/db\` directory.
5516
+ 4. Run \`supabase start\` in your project's \`packages/db\` directory.
5267
5517
  5. Copy the 'DB URL' from the output.${output ? `
5268
5518
  ${pc.bold("Relevant output from `supabase start`:")}
5269
5519
  ${pc.dim(output)}` : ""}
5270
- 6. Add the DB URL to the .env file in \`apps/server/.env\` as \`DATABASE_URL\`:
5520
+ 6. Add the DB URL to the .env file in \`packages/db/.env\` as \`DATABASE_URL\`:
5271
5521
  ${pc.gray("DATABASE_URL=\"your_supabase_db_url\"")}`);
5272
5522
  }
5273
5523
  async function setupSupabase(config, cliInput) {
5274
- const { projectDir, packageManager } = config;
5524
+ const { projectDir, packageManager, backend } = config;
5275
5525
  const manualDb = cliInput?.manualDb ?? false;
5276
- const serverDir = path.join(projectDir, "apps", "server");
5526
+ const serverDir = path.join(projectDir, "packages", "db");
5277
5527
  try {
5278
5528
  await fs.ensureDir(serverDir);
5279
5529
  if (manualDb) {
5280
5530
  displayManualSupabaseInstructions();
5281
- await writeSupabaseEnvFile(projectDir, "");
5531
+ await writeSupabaseEnvFile(projectDir, backend, "");
5282
5532
  return;
5283
5533
  }
5284
5534
  const mode = await select({
@@ -5297,7 +5547,7 @@ async function setupSupabase(config, cliInput) {
5297
5547
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5298
5548
  if (mode === "manual") {
5299
5549
  displayManualSupabaseInstructions();
5300
- await writeSupabaseEnvFile(projectDir, "");
5550
+ await writeSupabaseEnvFile(projectDir, backend, "");
5301
5551
  return;
5302
5552
  }
5303
5553
  if (!await initializeSupabase(serverDir, packageManager)) {
@@ -5310,7 +5560,7 @@ async function setupSupabase(config, cliInput) {
5310
5560
  return;
5311
5561
  }
5312
5562
  const dbUrl = extractDbUrl(supabaseOutput);
5313
- if (dbUrl) if (await writeSupabaseEnvFile(projectDir, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
5563
+ if (dbUrl) if (await writeSupabaseEnvFile(projectDir, backend, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
5314
5564
  else {
5315
5565
  log.error(pc.red("Supabase setup completed, but failed to update .env automatically."));
5316
5566
  displayManualSupabaseInstructions(supabaseOutput);
@@ -5438,8 +5688,9 @@ async function createTursoDatabase(dbName, groupName) {
5438
5688
  s.stop(pc.red("Failed to retrieve database connection details"));
5439
5689
  }
5440
5690
  }
5441
- async function writeEnvFile(projectDir, config) {
5442
- const envPath = path.join(projectDir, "apps/server", ".env");
5691
+ async function writeEnvFile(projectDir, backend, config) {
5692
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5693
+ const envPath = path.join(projectDir, targetApp, ".env");
5443
5694
  const variables = [{
5444
5695
  key: "DATABASE_URL",
5445
5696
  value: config?.dbUrl ?? "",
@@ -5463,12 +5714,12 @@ DATABASE_URL=your_database_url
5463
5714
  DATABASE_AUTH_TOKEN=your_auth_token`);
5464
5715
  }
5465
5716
  async function setupTurso(config, cliInput) {
5466
- const { orm, projectDir } = config;
5717
+ const { orm, projectDir, backend } = config;
5467
5718
  const manualDb = cliInput?.manualDb ?? false;
5468
5719
  const setupSpinner = spinner();
5469
5720
  try {
5470
5721
  if (manualDb) {
5471
- await writeEnvFile(projectDir);
5722
+ await writeEnvFile(projectDir, backend);
5472
5723
  displayManualSetupInstructions();
5473
5724
  return;
5474
5725
  }
@@ -5487,7 +5738,7 @@ async function setupTurso(config, cliInput) {
5487
5738
  });
5488
5739
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5489
5740
  if (mode === "manual") {
5490
- await writeEnvFile(projectDir);
5741
+ await writeEnvFile(projectDir, backend);
5491
5742
  displayManualSetupInstructions();
5492
5743
  return;
5493
5744
  }
@@ -5497,7 +5748,7 @@ async function setupTurso(config, cliInput) {
5497
5748
  if (platform === "win32") {
5498
5749
  if (setupSpinner) setupSpinner.stop(pc.yellow("Turso setup not supported on Windows"));
5499
5750
  log.warn(pc.yellow("Automatic Turso setup is not supported on Windows."));
5500
- await writeEnvFile(projectDir);
5751
+ await writeEnvFile(projectDir, backend);
5501
5752
  displayManualSetupInstructions();
5502
5753
  return;
5503
5754
  }
@@ -5509,7 +5760,7 @@ async function setupTurso(config, cliInput) {
5509
5760
  });
5510
5761
  if (isCancel(shouldInstall)) return exitCancelled("Operation cancelled");
5511
5762
  if (!shouldInstall) {
5512
- await writeEnvFile(projectDir);
5763
+ await writeEnvFile(projectDir, backend);
5513
5764
  displayManualSetupInstructions();
5514
5765
  return;
5515
5766
  }
@@ -5531,7 +5782,7 @@ async function setupTurso(config, cliInput) {
5531
5782
  dbName = dbNameResponse;
5532
5783
  try {
5533
5784
  const config$1 = await createTursoDatabase(dbName, selectedGroup);
5534
- await writeEnvFile(projectDir, config$1);
5785
+ await writeEnvFile(projectDir, backend, config$1);
5535
5786
  success = true;
5536
5787
  } catch (error) {
5537
5788
  if (error instanceof Error && error.message === "DATABASE_EXISTS") {
@@ -5544,7 +5795,7 @@ async function setupTurso(config, cliInput) {
5544
5795
  } catch (error) {
5545
5796
  if (setupSpinner) setupSpinner.stop(pc.red("Turso CLI availability check failed"));
5546
5797
  consola.error(pc.red(`Error during Turso setup: ${error instanceof Error ? error.message : String(error)}`));
5547
- await writeEnvFile(projectDir);
5798
+ await writeEnvFile(projectDir, backend);
5548
5799
  displayManualSetupInstructions();
5549
5800
  log.success("Setup completed with manual configuration required.");
5550
5801
  }
@@ -5556,40 +5807,48 @@ async function setupDatabase(config, cliInput) {
5556
5807
  const { database, orm, dbSetup, backend, projectDir } = config;
5557
5808
  if (backend === "convex" || database === "none") {
5558
5809
  if (backend !== "convex") {
5559
- const serverDir$1 = path.join(projectDir, "apps/server");
5560
- const serverDbDir = path.join(serverDir$1, "src/db");
5810
+ const serverDir = path.join(projectDir, "apps/server");
5811
+ const serverDbDir = path.join(serverDir, "src/db");
5561
5812
  if (await fs.pathExists(serverDbDir)) await fs.remove(serverDbDir);
5562
5813
  }
5563
5814
  return;
5564
5815
  }
5565
5816
  const s = spinner();
5566
- const serverDir = path.join(projectDir, "apps/server");
5567
- if (!await fs.pathExists(serverDir)) return;
5817
+ const dbPackageDir = path.join(projectDir, "packages/db");
5818
+ if (!await fs.pathExists(dbPackageDir)) return;
5568
5819
  try {
5569
- if (orm === "prisma") if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
5570
- dependencies: [
5571
- "@prisma/client",
5572
- "@prisma/adapter-planetscale",
5573
- "@planetscale/database"
5574
- ],
5575
- devDependencies: ["prisma"],
5576
- projectDir: serverDir
5577
- });
5578
- else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
5579
- dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
5580
- devDependencies: ["prisma"],
5581
- projectDir: serverDir
5582
- });
5583
- else await addPackageDependency({
5584
- dependencies: ["@prisma/client"],
5585
- devDependencies: ["prisma"],
5586
- projectDir: serverDir
5587
- });
5588
- else if (orm === "drizzle") {
5820
+ if (orm === "prisma") {
5821
+ if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
5822
+ dependencies: [
5823
+ "@prisma/client",
5824
+ "@prisma/adapter-planetscale",
5825
+ "@planetscale/database"
5826
+ ],
5827
+ devDependencies: ["prisma"],
5828
+ projectDir: dbPackageDir
5829
+ });
5830
+ else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
5831
+ dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
5832
+ devDependencies: ["prisma"],
5833
+ projectDir: dbPackageDir
5834
+ });
5835
+ else await addPackageDependency({
5836
+ dependencies: ["@prisma/client"],
5837
+ devDependencies: ["prisma"],
5838
+ projectDir: dbPackageDir
5839
+ });
5840
+ if (backend === "self") {
5841
+ const webDir = path.join(projectDir, "apps/web");
5842
+ if (await fs.pathExists(webDir)) await addPackageDependency({
5843
+ dependencies: ["@prisma/client"],
5844
+ projectDir: webDir
5845
+ });
5846
+ }
5847
+ } else if (orm === "drizzle") {
5589
5848
  if (database === "sqlite") await addPackageDependency({
5590
5849
  dependencies: ["drizzle-orm", "@libsql/client"],
5591
5850
  devDependencies: ["drizzle-kit"],
5592
- projectDir: serverDir
5851
+ projectDir: dbPackageDir
5593
5852
  });
5594
5853
  else if (database === "postgres") if (dbSetup === "neon") await addPackageDependency({
5595
5854
  dependencies: [
@@ -5598,32 +5857,32 @@ async function setupDatabase(config, cliInput) {
5598
5857
  "ws"
5599
5858
  ],
5600
5859
  devDependencies: ["drizzle-kit", "@types/ws"],
5601
- projectDir: serverDir
5860
+ projectDir: dbPackageDir
5602
5861
  });
5603
5862
  else if (dbSetup === "planetscale") await addPackageDependency({
5604
5863
  dependencies: ["drizzle-orm", "pg"],
5605
5864
  devDependencies: ["drizzle-kit", "@types/pg"],
5606
- projectDir: serverDir
5865
+ projectDir: dbPackageDir
5607
5866
  });
5608
5867
  else await addPackageDependency({
5609
5868
  dependencies: ["drizzle-orm", "pg"],
5610
5869
  devDependencies: ["drizzle-kit", "@types/pg"],
5611
- projectDir: serverDir
5870
+ projectDir: dbPackageDir
5612
5871
  });
5613
5872
  else if (database === "mysql") if (dbSetup === "planetscale") await addPackageDependency({
5614
5873
  dependencies: ["drizzle-orm", "@planetscale/database"],
5615
5874
  devDependencies: ["drizzle-kit"],
5616
- projectDir: serverDir
5875
+ projectDir: dbPackageDir
5617
5876
  });
5618
5877
  else await addPackageDependency({
5619
5878
  dependencies: ["drizzle-orm", "mysql2"],
5620
5879
  devDependencies: ["drizzle-kit"],
5621
- projectDir: serverDir
5880
+ projectDir: dbPackageDir
5622
5881
  });
5623
5882
  } else if (orm === "mongoose") await addPackageDependency({
5624
5883
  dependencies: ["mongoose"],
5625
5884
  devDependencies: [],
5626
- projectDir: serverDir
5885
+ projectDir: dbPackageDir
5627
5886
  });
5628
5887
  if (dbSetup === "docker") await setupDockerCompose(config);
5629
5888
  else if (database === "sqlite" && dbSetup === "turso") await setupTurso(config, cliInput);
@@ -5646,7 +5905,7 @@ async function setupDatabase(config, cliInput) {
5646
5905
  //#region src/helpers/core/runtime-setup.ts
5647
5906
  async function setupRuntime(config) {
5648
5907
  const { runtime, backend, projectDir } = config;
5649
- if (backend === "convex" || backend === "next" || runtime === "none") return;
5908
+ if (backend === "convex" || backend === "self" || runtime === "none") return;
5650
5909
  const serverDir = path.join(projectDir, "apps/server");
5651
5910
  if (!await fs.pathExists(serverDir)) return;
5652
5911
  if (runtime === "bun") await setupBunRuntime(serverDir, backend);
@@ -6035,15 +6294,14 @@ async function initializeGit(projectDir, useGit) {
6035
6294
  async function setupPayments(config) {
6036
6295
  const { payments, projectDir, frontend } = config;
6037
6296
  if (!payments || payments === "none") return;
6038
- const serverDir = path.join(projectDir, "apps/server");
6039
6297
  const clientDir = path.join(projectDir, "apps/web");
6040
- const serverDirExists = await fs.pathExists(serverDir);
6298
+ const authDir = path.join(projectDir, "packages/auth");
6041
6299
  const clientDirExists = await fs.pathExists(clientDir);
6042
- if (!serverDirExists) return;
6300
+ const authDirExists = await fs.pathExists(authDir);
6043
6301
  if (payments === "polar") {
6044
- await addPackageDependency({
6302
+ if (authDirExists) await addPackageDependency({
6045
6303
  dependencies: ["@polar-sh/better-auth", "@polar-sh/sdk"],
6046
- projectDir: serverDir
6304
+ projectDir: authDir
6047
6305
  });
6048
6306
  if (clientDirExists) {
6049
6307
  if (frontend.some((f) => [
@@ -6166,7 +6424,7 @@ async function displayPostInstallInstructions(config) {
6166
6424
  else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
6167
6425
  if (!isConvex) {
6168
6426
  output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
6169
- if (api === "orpc") if (backend === "next") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/rpc/api\n`;
6427
+ if (api === "orpc") if (backend === "self") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/rpc/api\n`;
6170
6428
  else output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
6171
6429
  }
6172
6430
  if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
@@ -6280,12 +6538,81 @@ function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
6280
6538
  return instructions.length ? `\n${instructions.join("\n")}` : "";
6281
6539
  }
6282
6540
 
6541
+ //#endregion
6542
+ //#region src/helpers/core/workspace-setup.ts
6543
+ async function setupWorkspaceDependencies(projectDir, options) {
6544
+ const projectName = options.projectName;
6545
+ const workspaceVersion = options.packageManager === "npm" ? "*" : "workspace:*";
6546
+ const commonDeps = ["dotenv", "zod"];
6547
+ const commonDevDeps = ["tsdown"];
6548
+ const dbPackageDir = path.join(projectDir, "packages/db");
6549
+ if (await fs.pathExists(dbPackageDir)) await addPackageDependency({
6550
+ dependencies: commonDeps,
6551
+ devDependencies: commonDevDeps,
6552
+ projectDir: dbPackageDir
6553
+ });
6554
+ const authPackageDir = path.join(projectDir, "packages/auth");
6555
+ if (await fs.pathExists(authPackageDir)) await addPackageDependency({
6556
+ dependencies: commonDeps,
6557
+ devDependencies: commonDevDeps,
6558
+ customDependencies: { [`@${projectName}/db`]: workspaceVersion },
6559
+ projectDir: authPackageDir
6560
+ });
6561
+ const apiPackageDir = path.join(projectDir, "packages/api");
6562
+ if (await fs.pathExists(apiPackageDir)) await addPackageDependency({
6563
+ dependencies: commonDeps,
6564
+ devDependencies: commonDevDeps,
6565
+ customDependencies: {
6566
+ [`@${projectName}/auth`]: workspaceVersion,
6567
+ [`@${projectName}/db`]: workspaceVersion
6568
+ },
6569
+ projectDir: apiPackageDir
6570
+ });
6571
+ const serverPackageDir = path.join(projectDir, "apps/server");
6572
+ if (await fs.pathExists(serverPackageDir)) await addPackageDependency({
6573
+ dependencies: commonDeps,
6574
+ devDependencies: commonDevDeps,
6575
+ customDependencies: {
6576
+ [`@${projectName}/api`]: workspaceVersion,
6577
+ [`@${projectName}/auth`]: workspaceVersion,
6578
+ [`@${projectName}/db`]: workspaceVersion
6579
+ },
6580
+ projectDir: serverPackageDir
6581
+ });
6582
+ const webPackageDir = path.join(projectDir, "apps/web");
6583
+ if (await fs.pathExists(webPackageDir)) {
6584
+ const webDeps = {};
6585
+ webDeps[`@${projectName}/api`] = workspaceVersion;
6586
+ webDeps[`@${projectName}/auth`] = workspaceVersion;
6587
+ webDeps[`@${projectName}/db`] = workspaceVersion;
6588
+ if (Object.keys(webDeps).length > 0) await addPackageDependency({
6589
+ customDependencies: webDeps,
6590
+ projectDir: webPackageDir
6591
+ });
6592
+ }
6593
+ await addPackageDependency({
6594
+ dependencies: commonDeps,
6595
+ devDependencies: commonDevDeps,
6596
+ projectDir
6597
+ });
6598
+ }
6599
+
6283
6600
  //#endregion
6284
6601
  //#region src/helpers/core/project-config.ts
6285
6602
  async function updatePackageConfigurations(projectDir, options) {
6286
6603
  await updateRootPackageJson(projectDir, options);
6287
- if (options.backend !== "convex") await updateServerPackageJson(projectDir, options);
6288
- else await updateConvexPackageJson(projectDir, options);
6604
+ if (options.backend === "convex") await updateConvexPackageJson(projectDir, options);
6605
+ else if (options.backend === "self") {
6606
+ await updateDbPackageJson(projectDir, options);
6607
+ await updateAuthPackageJson(projectDir, options);
6608
+ await updateApiPackageJson(projectDir, options);
6609
+ await setupWorkspaceDependencies(projectDir, options);
6610
+ } else if (options.backend !== "none") {
6611
+ await updateServerPackageJson(projectDir, options);
6612
+ await updateAuthPackageJson(projectDir, options);
6613
+ await updateApiPackageJson(projectDir, options);
6614
+ await setupWorkspaceDependencies(projectDir, options);
6615
+ }
6289
6616
  }
6290
6617
  async function updateRootPackageJson(projectDir, options) {
6291
6618
  const rootPackageJsonPath = path.join(projectDir, "package.json");
@@ -6295,6 +6622,7 @@ async function updateRootPackageJson(projectDir, options) {
6295
6622
  if (!packageJson.scripts) packageJson.scripts = {};
6296
6623
  const scripts = packageJson.scripts;
6297
6624
  const backendPackageName = options.backend === "convex" ? `@${options.projectName}/backend` : "server";
6625
+ const dbPackageName = `@${options.projectName}/db`;
6298
6626
  let serverDevScript = "";
6299
6627
  if (options.addons.includes("turborepo")) serverDevScript = `turbo -F ${backendPackageName} dev`;
6300
6628
  else if (options.packageManager === "bun") serverDevScript = `bun run --filter ${backendPackageName} dev`;
@@ -6311,24 +6639,24 @@ async function updateRootPackageJson(projectDir, options) {
6311
6639
  scripts["check-types"] = "turbo check-types";
6312
6640
  scripts["dev:native"] = "turbo -F native dev";
6313
6641
  scripts["dev:web"] = "turbo -F web dev";
6314
- scripts["dev:server"] = serverDevScript;
6642
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6315
6643
  if (options.backend === "convex") scripts["dev:setup"] = `turbo -F ${backendPackageName} dev:setup`;
6316
6644
  if (needsDbScripts) {
6317
- scripts["db:push"] = `turbo -F ${backendPackageName} db:push`;
6318
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${backendPackageName} db:studio`;
6645
+ scripts["db:push"] = `turbo -F ${dbPackageName} db:push`;
6646
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${dbPackageName} db:studio`;
6319
6647
  if (options.orm === "prisma") {
6320
- scripts["db:generate"] = `turbo -F ${backendPackageName} db:generate`;
6321
- scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
6648
+ scripts["db:generate"] = `turbo -F ${dbPackageName} db:generate`;
6649
+ scripts["db:migrate"] = `turbo -F ${dbPackageName} db:migrate`;
6322
6650
  } else if (options.orm === "drizzle") {
6323
- scripts["db:generate"] = `turbo -F ${backendPackageName} db:generate`;
6324
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
6651
+ scripts["db:generate"] = `turbo -F ${dbPackageName} db:generate`;
6652
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${dbPackageName} db:migrate`;
6325
6653
  }
6326
6654
  }
6327
6655
  if (options.dbSetup === "docker") {
6328
- scripts["db:start"] = `turbo -F ${backendPackageName} db:start`;
6329
- scripts["db:watch"] = `turbo -F ${backendPackageName} db:watch`;
6330
- scripts["db:stop"] = `turbo -F ${backendPackageName} db:stop`;
6331
- scripts["db:down"] = `turbo -F ${backendPackageName} db:down`;
6656
+ scripts["db:start"] = `turbo -F ${dbPackageName} db:start`;
6657
+ scripts["db:watch"] = `turbo -F ${dbPackageName} db:watch`;
6658
+ scripts["db:stop"] = `turbo -F ${dbPackageName} db:stop`;
6659
+ scripts["db:down"] = `turbo -F ${dbPackageName} db:down`;
6332
6660
  }
6333
6661
  } else if (options.packageManager === "pnpm") {
6334
6662
  scripts.dev = devScript;
@@ -6336,24 +6664,24 @@ async function updateRootPackageJson(projectDir, options) {
6336
6664
  scripts["check-types"] = "pnpm -r check-types";
6337
6665
  scripts["dev:native"] = "pnpm --filter native dev";
6338
6666
  scripts["dev:web"] = "pnpm --filter web dev";
6339
- scripts["dev:server"] = serverDevScript;
6667
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6340
6668
  if (options.backend === "convex") scripts["dev:setup"] = `pnpm --filter ${backendPackageName} dev:setup`;
6341
6669
  if (needsDbScripts) {
6342
- scripts["db:push"] = `pnpm --filter ${backendPackageName} db:push`;
6343
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${backendPackageName} db:studio`;
6670
+ scripts["db:push"] = `pnpm --filter ${dbPackageName} db:push`;
6671
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${dbPackageName} db:studio`;
6344
6672
  if (options.orm === "prisma") {
6345
- scripts["db:generate"] = `pnpm --filter ${backendPackageName} db:generate`;
6346
- scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
6673
+ scripts["db:generate"] = `pnpm --filter ${dbPackageName} db:generate`;
6674
+ scripts["db:migrate"] = `pnpm --filter ${dbPackageName} db:migrate`;
6347
6675
  } else if (options.orm === "drizzle") {
6348
- scripts["db:generate"] = `pnpm --filter ${backendPackageName} db:generate`;
6349
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
6676
+ scripts["db:generate"] = `pnpm --filter ${dbPackageName} db:generate`;
6677
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${dbPackageName} db:migrate`;
6350
6678
  }
6351
6679
  }
6352
6680
  if (options.dbSetup === "docker") {
6353
- scripts["db:start"] = `pnpm --filter ${backendPackageName} db:start`;
6354
- scripts["db:watch"] = `pnpm --filter ${backendPackageName} db:watch`;
6355
- scripts["db:stop"] = `pnpm --filter ${backendPackageName} db:stop`;
6356
- scripts["db:down"] = `pnpm --filter ${backendPackageName} db:down`;
6681
+ scripts["db:start"] = `pnpm --filter ${dbPackageName} db:start`;
6682
+ scripts["db:watch"] = `pnpm --filter ${dbPackageName} db:watch`;
6683
+ scripts["db:stop"] = `pnpm --filter ${dbPackageName} db:stop`;
6684
+ scripts["db:down"] = `pnpm --filter ${dbPackageName} db:down`;
6357
6685
  }
6358
6686
  } else if (options.packageManager === "npm") {
6359
6687
  scripts.dev = devScript;
@@ -6361,24 +6689,24 @@ async function updateRootPackageJson(projectDir, options) {
6361
6689
  scripts["check-types"] = "npm run check-types --workspaces";
6362
6690
  scripts["dev:native"] = "npm run dev --workspace native";
6363
6691
  scripts["dev:web"] = "npm run dev --workspace web";
6364
- scripts["dev:server"] = serverDevScript;
6692
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6365
6693
  if (options.backend === "convex") scripts["dev:setup"] = `npm run dev:setup --workspace ${backendPackageName}`;
6366
6694
  if (needsDbScripts) {
6367
- scripts["db:push"] = `npm run db:push --workspace ${backendPackageName}`;
6368
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${backendPackageName}`;
6695
+ scripts["db:push"] = `npm run db:push --workspace ${dbPackageName}`;
6696
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${dbPackageName}`;
6369
6697
  if (options.orm === "prisma") {
6370
- scripts["db:generate"] = `npm run db:generate --workspace ${backendPackageName}`;
6371
- scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
6698
+ scripts["db:generate"] = `npm run db:generate --workspace ${dbPackageName}`;
6699
+ scripts["db:migrate"] = `npm run db:migrate --workspace ${dbPackageName}`;
6372
6700
  } else if (options.orm === "drizzle") {
6373
- scripts["db:generate"] = `npm run db:generate --workspace ${backendPackageName}`;
6374
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
6701
+ scripts["db:generate"] = `npm run db:generate --workspace ${dbPackageName}`;
6702
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${dbPackageName}`;
6375
6703
  }
6376
6704
  }
6377
6705
  if (options.dbSetup === "docker") {
6378
- scripts["db:start"] = `npm run db:start --workspace ${backendPackageName}`;
6379
- scripts["db:watch"] = `npm run db:watch --workspace ${backendPackageName}`;
6380
- scripts["db:stop"] = `npm run db:stop --workspace ${backendPackageName}`;
6381
- scripts["db:down"] = `npm run db:down --workspace ${backendPackageName}`;
6706
+ scripts["db:start"] = `npm run db:start --workspace ${dbPackageName}`;
6707
+ scripts["db:watch"] = `npm run db:watch --workspace ${dbPackageName}`;
6708
+ scripts["db:stop"] = `npm run db:stop --workspace ${dbPackageName}`;
6709
+ scripts["db:down"] = `npm run db:down --workspace ${dbPackageName}`;
6382
6710
  }
6383
6711
  } else if (options.packageManager === "bun") {
6384
6712
  scripts.dev = devScript;
@@ -6386,24 +6714,24 @@ async function updateRootPackageJson(projectDir, options) {
6386
6714
  scripts["check-types"] = "bun run --filter '*' check-types";
6387
6715
  scripts["dev:native"] = "bun run --filter native dev";
6388
6716
  scripts["dev:web"] = "bun run --filter web dev";
6389
- scripts["dev:server"] = serverDevScript;
6717
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6390
6718
  if (options.backend === "convex") scripts["dev:setup"] = `bun run --filter ${backendPackageName} dev:setup`;
6391
6719
  if (needsDbScripts) {
6392
- scripts["db:push"] = `bun run --filter ${backendPackageName} db:push`;
6393
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${backendPackageName} db:studio`;
6720
+ scripts["db:push"] = `bun run --filter ${dbPackageName} db:push`;
6721
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${dbPackageName} db:studio`;
6394
6722
  if (options.orm === "prisma") {
6395
- scripts["db:generate"] = `bun run --filter ${backendPackageName} db:generate`;
6396
- scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
6723
+ scripts["db:generate"] = `bun run --filter ${dbPackageName} db:generate`;
6724
+ scripts["db:migrate"] = `bun run --filter ${dbPackageName} db:migrate`;
6397
6725
  } else if (options.orm === "drizzle") {
6398
- scripts["db:generate"] = `bun run --filter ${backendPackageName} db:generate`;
6399
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
6726
+ scripts["db:generate"] = `bun run --filter ${dbPackageName} db:generate`;
6727
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${dbPackageName} db:migrate`;
6400
6728
  }
6401
6729
  }
6402
6730
  if (options.dbSetup === "docker") {
6403
- scripts["db:start"] = `bun run --filter ${backendPackageName} db:start`;
6404
- scripts["db:watch"] = `bun run --filter ${backendPackageName} db:watch`;
6405
- scripts["db:stop"] = `bun run --filter ${backendPackageName} db:stop`;
6406
- scripts["db:down"] = `bun run --filter ${backendPackageName} db:down`;
6731
+ scripts["db:start"] = `bun run --filter ${dbPackageName} db:start`;
6732
+ scripts["db:watch"] = `bun run --filter ${dbPackageName} db:watch`;
6733
+ scripts["db:stop"] = `bun run --filter ${dbPackageName} db:stop`;
6734
+ scripts["db:down"] = `bun run --filter ${dbPackageName} db:down`;
6407
6735
  }
6408
6736
  }
6409
6737
  try {
@@ -6428,7 +6756,16 @@ async function updateServerPackageJson(projectDir, options) {
6428
6756
  if (!await fs.pathExists(serverPackageJsonPath)) return;
6429
6757
  const serverPackageJson = await fs.readJson(serverPackageJsonPath);
6430
6758
  if (!serverPackageJson.scripts) serverPackageJson.scripts = {};
6431
- const scripts = serverPackageJson.scripts;
6759
+ await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
6760
+ await updateDbPackageJson(projectDir, options);
6761
+ }
6762
+ async function updateDbPackageJson(projectDir, options) {
6763
+ const dbPackageJsonPath = path.join(projectDir, "packages/db/package.json");
6764
+ if (!await fs.pathExists(dbPackageJsonPath)) return;
6765
+ const dbPackageJson = await fs.readJson(dbPackageJsonPath);
6766
+ dbPackageJson.name = `@${options.projectName}/db`;
6767
+ if (!dbPackageJson.scripts) dbPackageJson.scripts = {};
6768
+ const scripts = dbPackageJson.scripts;
6432
6769
  if (options.database !== "none") {
6433
6770
  if (options.database === "sqlite" && options.orm === "drizzle" && options.dbSetup !== "d1") scripts["db:local"] = "turso dev --db-file local.db";
6434
6771
  if (options.orm === "prisma") {
@@ -6449,7 +6786,21 @@ async function updateServerPackageJson(projectDir, options) {
6449
6786
  scripts["db:stop"] = "docker compose stop";
6450
6787
  scripts["db:down"] = "docker compose down";
6451
6788
  }
6452
- await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
6789
+ await fs.writeJson(dbPackageJsonPath, dbPackageJson, { spaces: 2 });
6790
+ }
6791
+ async function updateAuthPackageJson(projectDir, options) {
6792
+ const authPackageJsonPath = path.join(projectDir, "packages/auth/package.json");
6793
+ if (!await fs.pathExists(authPackageJsonPath)) return;
6794
+ const authPackageJson = await fs.readJson(authPackageJsonPath);
6795
+ authPackageJson.name = `@${options.projectName}/auth`;
6796
+ await fs.writeJson(authPackageJsonPath, authPackageJson, { spaces: 2 });
6797
+ }
6798
+ async function updateApiPackageJson(projectDir, options) {
6799
+ const apiPackageJsonPath = path.join(projectDir, "packages/api/package.json");
6800
+ if (!await fs.pathExists(apiPackageJsonPath)) return;
6801
+ const apiPackageJson = await fs.readJson(apiPackageJsonPath);
6802
+ apiPackageJson.name = `@${options.projectName}/api`;
6803
+ await fs.writeJson(apiPackageJsonPath, apiPackageJson, { spaces: 2 });
6453
6804
  }
6454
6805
  async function updateConvexPackageJson(projectDir, options) {
6455
6806
  const convexPackageJsonPath = path.join(projectDir, "packages/backend/package.json");
@@ -6465,15 +6816,14 @@ async function updateConvexPackageJson(projectDir, options) {
6465
6816
  async function createProject(options, cliInput) {
6466
6817
  const projectDir = options.projectDir;
6467
6818
  const isConvex = options.backend === "convex";
6819
+ const isSelfBackend = options.backend === "self";
6820
+ const needsServerSetup = !isConvex && !isSelfBackend;
6468
6821
  try {
6469
6822
  await fs.ensureDir(projectDir);
6470
6823
  await copyBaseTemplate(projectDir, options);
6471
6824
  await setupFrontendTemplates(projectDir, options);
6472
6825
  await setupBackendFramework(projectDir, options);
6473
- if (!isConvex) {
6474
- await setupDbOrmTemplates(projectDir, options);
6475
- await setupDockerComposeTemplates(projectDir, options);
6476
- }
6826
+ if (needsServerSetup) await setupDockerComposeTemplates(projectDir, options);
6477
6827
  await setupAuthTemplate(projectDir, options);
6478
6828
  if (options.payments && options.payments !== "none") await setupPaymentsTemplate(projectDir, options);
6479
6829
  if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamplesTemplate(projectDir, options);
@@ -6481,9 +6831,11 @@ async function createProject(options, cliInput) {
6481
6831
  await setupDeploymentTemplates(projectDir, options);
6482
6832
  await setupApi(options);
6483
6833
  if (!isConvex) {
6484
- await setupBackendDependencies(options);
6834
+ if (needsServerSetup) {
6835
+ await setupBackendDependencies(options);
6836
+ await setupRuntime(options);
6837
+ }
6485
6838
  await setupDatabase(options, cliInput);
6486
- await setupRuntime(options);
6487
6839
  if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamples(options);
6488
6840
  }
6489
6841
  if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
@@ -6492,6 +6844,7 @@ async function createProject(options, cliInput) {
6492
6844
  await handleExtras(projectDir, options);
6493
6845
  await setupEnvironmentVariables(options);
6494
6846
  await updatePackageConfigurations(projectDir, options);
6847
+ await setupCatalogs(projectDir, options);
6495
6848
  await setupWebDeploy(options);
6496
6849
  await setupServerDeploy(options);
6497
6850
  await createReadme(projectDir, options);