create-better-t-stack 2.50.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) 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-B8TD9m4n.js → src-BywywK-y.js} +825 -418
  5. package/package.json +2 -1
  6. package/templates/addons/ruler/.ruler/bts.md.hbs +24 -14
  7. package/templates/api/orpc/fullstack/next/src/app/api/rpc/[[...rest]]/route.ts.hbs +50 -0
  8. package/templates/api/orpc/native/utils/orpc.ts.hbs +5 -1
  9. package/templates/api/orpc/server/_gitignore +34 -0
  10. package/templates/api/orpc/server/package.json.hbs +22 -0
  11. package/templates/api/orpc/server/{base/src/lib → src}/context.ts.hbs +6 -6
  12. package/templates/{backend/server/server-base → api/orpc/server}/src/routers/index.ts.hbs +2 -2
  13. package/templates/api/orpc/server/tsconfig.json.hbs +10 -0
  14. package/templates/api/orpc/server/tsdown.config.ts.hbs +7 -0
  15. package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +1 -1
  16. package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +4 -2
  17. package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +1 -1
  18. package/templates/api/orpc/web/svelte/src/lib/orpc.ts.hbs +1 -1
  19. package/templates/api/trpc/fullstack/next/src/app/api/trpc/[trpc]/route.ts.hbs +14 -0
  20. package/templates/api/trpc/native/utils/trpc.ts.hbs +5 -1
  21. package/templates/api/trpc/server/_gitignore +34 -0
  22. package/templates/api/trpc/server/package.json.hbs +21 -0
  23. package/templates/api/trpc/server/{base/src/lib → src}/context.ts.hbs +6 -6
  24. package/templates/api/trpc/server/src/routers/index.ts.hbs +55 -0
  25. package/templates/api/trpc/server/tsconfig.json.hbs +10 -0
  26. package/templates/api/trpc/server/tsdown.config.ts.hbs +7 -0
  27. package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +6 -4
  28. 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
  29. package/templates/auth/better-auth/server/base/_gitignore +34 -0
  30. package/templates/auth/better-auth/server/base/package.json.hbs +21 -0
  31. package/templates/auth/better-auth/server/base/src/{lib/auth.ts.hbs → index.ts.hbs} +12 -12
  32. package/templates/auth/better-auth/server/base/tsconfig.json.hbs +10 -0
  33. package/templates/auth/better-auth/server/base/tsdown.config.ts.hbs +7 -0
  34. package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +2 -0
  35. package/templates/auth/better-auth/web/react/next/src/app/dashboard/page.tsx.hbs +8 -17
  36. package/templates/backend/server/{server-base → base}/package.json.hbs +1 -5
  37. package/templates/backend/server/base/tsconfig.json.hbs +13 -0
  38. package/templates/backend/server/base/tsdown.config.ts.hbs +9 -0
  39. package/templates/backend/server/elysia/src/index.ts.hbs +6 -6
  40. package/templates/backend/server/express/src/index.ts.hbs +6 -6
  41. package/templates/backend/server/fastify/src/index.ts.hbs +6 -6
  42. package/templates/backend/server/hono/src/index.ts.hbs +7 -7
  43. package/templates/base/_gitignore +47 -1
  44. package/templates/base/package.json.hbs +1 -3
  45. package/templates/{backend/server/server-base/tsconfig.json.hbs → base/tsconfig.base.json.hbs} +14 -13
  46. package/templates/base/tsconfig.json.hbs +3 -0
  47. package/templates/db/base/_gitignore +34 -0
  48. package/templates/db/base/package.json.hbs +21 -0
  49. package/templates/db/base/tsconfig.json.hbs +10 -0
  50. package/templates/db/base/tsdown.config.ts.hbs +7 -0
  51. package/templates/db/drizzle/mysql/drizzle.config.ts.hbs +11 -2
  52. package/templates/db/drizzle/mysql/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  53. package/templates/db/drizzle/postgres/drizzle.config.ts.hbs +11 -2
  54. package/templates/db/drizzle/postgres/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  55. package/templates/db/drizzle/sqlite/drizzle.config.ts.hbs +11 -2
  56. package/templates/db/drizzle/sqlite/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  57. package/templates/db/prisma/mongodb/prisma.config.ts.hbs +9 -1
  58. package/templates/db/prisma/mongodb/src/index.ts.hbs +5 -0
  59. package/templates/db/prisma/mysql/prisma.config.ts.hbs +9 -1
  60. package/templates/db/prisma/mysql/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  61. package/templates/db/prisma/postgres/prisma.config.ts.hbs +11 -3
  62. package/templates/db/prisma/postgres/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  63. package/templates/db/prisma/sqlite/prisma.config.ts.hbs +9 -1
  64. package/templates/db/prisma/sqlite/src/{db/index.ts.hbs → index.ts.hbs} +3 -3
  65. package/templates/deploy/alchemy/alchemy.run.ts.hbs +3 -3
  66. package/templates/examples/ai/fullstack/next/src/app/api/ai/route.ts.hbs +15 -0
  67. package/templates/examples/todo/server/drizzle/base/src/routers/todo.ts.hbs +6 -6
  68. package/templates/examples/todo/server/mongoose/base/src/routers/todo.ts.hbs +4 -4
  69. package/templates/examples/todo/server/prisma/base/src/routers/todo.ts.hbs +4 -4
  70. package/templates/extras/bunfig.toml.hbs +2 -2
  71. package/templates/frontend/native/nativewind/tsconfig.json.hbs +1 -6
  72. package/templates/frontend/native/unistyles/tsconfig.json.hbs +1 -6
  73. package/templates/frontend/nuxt/tsconfig.json.hbs +0 -4
  74. package/templates/frontend/react/next/package.json.hbs +1 -1
  75. package/templates/frontend/react/next/tsconfig.json.hbs +0 -7
  76. package/templates/frontend/react/react-router/tsconfig.json.hbs +1 -6
  77. package/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs +1 -1
  78. package/templates/frontend/react/tanstack-router/tsconfig.json.hbs +1 -6
  79. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +1 -1
  80. package/templates/frontend/react/tanstack-start/tsconfig.json.hbs +1 -6
  81. package/templates/frontend/solid/tsconfig.json.hbs +1 -6
  82. package/templates/frontend/svelte/tsconfig.json.hbs +1 -6
  83. package/templates/api/orpc/server/next/src/app/rpc/[...all]/route.ts.hbs +0 -52
  84. package/templates/api/trpc/server/next/src/app/trpc/[trpc]/route.ts +0 -14
  85. package/templates/backend/server/next/next-env.d.ts +0 -5
  86. package/templates/backend/server/next/next.config.ts +0 -7
  87. package/templates/backend/server/next/package.json.hbs +0 -27
  88. package/templates/backend/server/next/src/app/route.ts +0 -5
  89. package/templates/backend/server/next/src/middleware.ts +0 -19
  90. package/templates/backend/server/next/tsconfig.json.hbs +0 -33
  91. package/templates/db/prisma/mongodb/src/db/index.ts.hbs +0 -5
  92. package/templates/examples/ai/server/next/src/app/ai/route.ts.hbs +0 -15
  93. /package/templates/api/orpc/server/{base/src/lib/orpc.ts.hbs → src/index.ts.hbs} +0 -0
  94. /package/templates/api/trpc/server/{base/src/lib/trpc.ts.hbs → src/index.ts.hbs} +0 -0
  95. /package/templates/auth/better-auth/server/db/drizzle/mysql/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
  96. /package/templates/auth/better-auth/server/db/drizzle/postgres/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
  97. /package/templates/auth/better-auth/server/db/drizzle/sqlite/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
  98. /package/templates/auth/better-auth/server/db/mongoose/mongodb/src/{db/models/auth.model.ts → models/auth.model.ts.hbs} +0 -0
  99. /package/templates/auth/better-auth/server/db/prisma/mongodb/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  100. /package/templates/auth/better-auth/server/db/prisma/mysql/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  101. /package/templates/auth/better-auth/server/db/prisma/postgres/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  102. /package/templates/auth/better-auth/server/db/prisma/sqlite/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  103. /package/templates/auth/better-auth/web/nuxt/app/middleware/{auth.ts → auth.ts.hbs} +0 -0
  104. /package/templates/backend/server/{server-base → base}/_gitignore +0 -0
  105. /package/templates/db/mongoose/mongodb/src/{db/index.ts.hbs → index.ts.hbs} +0 -0
  106. /package/templates/examples/todo/server/drizzle/mysql/src/{db/schema → schema}/todo.ts +0 -0
  107. /package/templates/examples/todo/server/drizzle/postgres/src/{db/schema → schema}/todo.ts +0 -0
  108. /package/templates/examples/todo/server/drizzle/sqlite/src/{db/schema → schema}/todo.ts +0 -0
  109. /package/templates/examples/todo/server/mongoose/mongodb/src/{db/models/todo.model.ts → models/todo.model.ts.hbs} +0 -0
  110. /package/templates/examples/todo/server/prisma/mongodb/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  111. /package/templates/examples/todo/server/prisma/mysql/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  112. /package/templates/examples/todo/server/prisma/postgres/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  113. /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,18 @@ 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 = ["next"];
351
+ function validateSelfBackendCompatibility(providedFlags, options, config) {
352
+ const backend = config.backend || options.backend;
353
+ const frontends = config.frontend || options.frontend || [];
354
+ if (backend === "self") {
355
+ const { web, native } = splitFrontends(frontends);
356
+ if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) exitWithError("Backend 'self' (fullstack) currently only supports Next.js frontend. Please use --frontend next. Support for Nuxt, SvelteKit, and TanStack Start will be added in a future update.");
357
+ if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles");
358
+ }
359
+ const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
360
+ if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) currently only supports Next.js frontend. Please use --frontend next or choose a different backend. Support for Nuxt, SvelteKit, and TanStack Start will be added in a future update.");
361
+ }
346
362
  function validateWorkersCompatibility(providedFlags, options, config) {
347
363
  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
364
  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 +680,34 @@ async function getAuthChoice(auth, hasDatabase, backend, frontend) {
664
680
 
665
681
  //#endregion
666
682
  //#region src/prompts/backend.ts
683
+ const FULLSTACK_FRONTENDS = ["next"];
667
684
  async function getBackendFrameworkChoice(backendFramework, frontends) {
668
685
  if (backendFramework !== void 0) return backendFramework;
669
686
  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
- ];
687
+ const hasFullstackFrontend = frontends?.some((f) => FULLSTACK_FRONTENDS.includes(f));
688
+ const backendOptions = [];
689
+ if (hasFullstackFrontend) backendOptions.push({
690
+ value: "self",
691
+ label: "Self (Fullstack)",
692
+ hint: "Use frontend's built-in api routes"
693
+ });
694
+ backendOptions.push({
695
+ value: "hono",
696
+ label: "Hono",
697
+ hint: "Lightweight, ultrafast web framework"
698
+ }, {
699
+ value: "express",
700
+ label: "Express",
701
+ hint: "Fast, unopinionated, minimalist web framework for Node.js"
702
+ }, {
703
+ value: "fastify",
704
+ label: "Fastify",
705
+ hint: "Fast, low-overhead web framework for Node.js"
706
+ }, {
707
+ value: "elysia",
708
+ label: "Elysia",
709
+ hint: "Ergonomic web framework for building backend servers"
710
+ });
697
711
  if (!hasIncompatibleFrontend) backendOptions.push({
698
712
  value: "convex",
699
713
  label: "Convex",
@@ -707,7 +721,7 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
707
721
  const response = await select({
708
722
  message: "Select backend",
709
723
  options: backendOptions,
710
- initialValue: DEFAULT_CONFIG.backend
724
+ initialValue: hasFullstackFrontend ? "self" : DEFAULT_CONFIG.backend
711
725
  });
712
726
  if (isCancel(response)) return exitCancelled("Operation cancelled");
713
727
  return response;
@@ -1086,9 +1100,8 @@ async function getPaymentsChoice(payments, auth, backend, frontends) {
1086
1100
  //#endregion
1087
1101
  //#region src/prompts/runtime.ts
1088
1102
  async function getRuntimeChoice(runtime, backend) {
1089
- if (backend === "convex" || backend === "none") return "none";
1103
+ if (backend === "convex" || backend === "none" || backend === "self") return "none";
1090
1104
  if (runtime !== void 0) return runtime;
1091
- if (backend === "next") return "node";
1092
1105
  const runtimeOptions = [{
1093
1106
  value: "bun",
1094
1107
  label: "Bun",
@@ -1754,6 +1767,12 @@ function validateBackendNoneConstraints(config, providedFlags) {
1754
1767
  if (has("dbSetup") && config.dbSetup !== "none") exitWithError("Backend 'none' requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.");
1755
1768
  if (has("serverDeploy") && config.serverDeploy !== "none") exitWithError("Backend 'none' requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.");
1756
1769
  }
1770
+ function validateSelfBackendConstraints(config, providedFlags) {
1771
+ const { backend } = config;
1772
+ if (backend !== "self") return;
1773
+ const has = (k) => providedFlags.has(k);
1774
+ if (has("runtime") && config.runtime !== "none") exitWithError("Backend 'self' (fullstack) requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.");
1775
+ }
1757
1776
  function validateBackendConstraints(config, providedFlags, options) {
1758
1777
  const { backend } = config;
1759
1778
  if (config.auth === "clerk" && backend !== "convex") exitWithError("Clerk authentication is only supported with the Convex backend. Please use '--backend convex' or choose a different auth provider.");
@@ -1765,8 +1784,8 @@ function validateBackendConstraints(config, providedFlags, options) {
1765
1784
  ].includes(f));
1766
1785
  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
1786
  }
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.");
1787
+ if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none" && backend !== "self") {
1788
+ 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
1789
  }
1771
1790
  if (backend === "convex" && providedFlags.has("frontend") && options.frontend) {
1772
1791
  const incompatibleFrontends = options.frontend.filter((f) => f === "solid");
@@ -1792,10 +1811,12 @@ function validateFullConfig(config, providedFlags, options) {
1792
1811
  validateDatabaseSetup(config, providedFlags);
1793
1812
  validateConvexConstraints(config, providedFlags);
1794
1813
  validateBackendNoneConstraints(config, providedFlags);
1814
+ validateSelfBackendConstraints(config, providedFlags);
1795
1815
  validateBackendConstraints(config, providedFlags, options);
1796
1816
  validateFrontendConstraints(config, providedFlags);
1797
1817
  validateApiConstraints(config, options);
1798
1818
  validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
1819
+ validateSelfBackendCompatibility(providedFlags, options, config);
1799
1820
  validateWorkersCompatibility(providedFlags, options, config);
1800
1821
  if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose 'wrangler' or 'alchemy' for --server-deploy.");
1801
1822
  if (config.addons && config.addons.length > 0) {
@@ -1849,6 +1870,7 @@ const CORE_STACK_FLAGS = new Set([
1849
1870
  "examples",
1850
1871
  "auth",
1851
1872
  "dbSetup",
1873
+ "payments",
1852
1874
  "api",
1853
1875
  "webDeploy",
1854
1876
  "serverDeploy"
@@ -1992,15 +2014,17 @@ const addPackageDependency = async (opts) => {
1992
2014
  if (!pkgJson.dependencies) pkgJson.dependencies = {};
1993
2015
  if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
1994
2016
  for (const pkgName of dependencies) {
1995
- const version = customDependencies[pkgName] || dependencyVersionMap[pkgName];
2017
+ const version = dependencyVersionMap[pkgName];
1996
2018
  if (version) pkgJson.dependencies[pkgName] = version;
1997
2019
  else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
1998
2020
  }
1999
2021
  for (const pkgName of devDependencies) {
2000
- const version = customDevDependencies[pkgName] || dependencyVersionMap[pkgName];
2022
+ const version = dependencyVersionMap[pkgName];
2001
2023
  if (version) pkgJson.devDependencies[pkgName] = version;
2002
2024
  else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
2003
2025
  }
2026
+ for (const [pkgName, version] of Object.entries(customDependencies)) pkgJson.dependencies[pkgName] = version;
2027
+ for (const [pkgName, version] of Object.entries(customDevDependencies)) pkgJson.devDependencies[pkgName] = version;
2004
2028
  await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
2005
2029
  };
2006
2030
 
@@ -2062,11 +2086,14 @@ async function setupFumadocs(config) {
2062
2086
  if (isCancel(template)) return exitCancelled("Operation cancelled");
2063
2087
  const commandWithArgs = `create-fumadocs-app@latest fumadocs --template ${TEMPLATES[template].value} --src --no-install --pm ${packageManager} --no-eslint --no-git`;
2064
2088
  const fumadocsInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2089
+ const s = spinner();
2090
+ s.start("Setting up Fumadocs...");
2065
2091
  await execa(fumadocsInitCommand, {
2066
2092
  cwd: path.join(projectDir, "apps"),
2067
2093
  env: { CI: "true" },
2068
2094
  shell: true
2069
2095
  });
2096
+ s.stop("Fumadocs setup complete!");
2070
2097
  const fumadocsDir = path.join(projectDir, "apps", "fumadocs");
2071
2098
  const packageJsonPath = path.join(fumadocsDir, "package.json");
2072
2099
  if (await fs.pathExists(packageJsonPath)) {
@@ -2307,11 +2334,14 @@ async function setupUltracite(config, hasHusky) {
2307
2334
  if (hasHusky) ultraciteArgs.push("--integrations", "husky", "lint-staged");
2308
2335
  const commandWithArgs = `ultracite@latest ${ultraciteArgs.join(" ")} --skip-install`;
2309
2336
  const ultraciteInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2337
+ const s = spinner();
2338
+ s.start("Setting up Ultracite...");
2310
2339
  await execa(ultraciteInitCommand, {
2311
2340
  cwd: projectDir,
2312
2341
  env: { CI: "true" },
2313
2342
  shell: true
2314
2343
  });
2344
+ s.stop("Ultracite setup complete!");
2315
2345
  if (hasHusky) await addPackageDependency({
2316
2346
  devDependencies: ["husky", "lint-staged"],
2317
2347
  projectDir
@@ -2655,8 +2685,12 @@ async function processTemplate(srcPath, destPath, context) {
2655
2685
  }
2656
2686
  handlebars.registerHelper("eq", (a, b) => a === b);
2657
2687
  handlebars.registerHelper("ne", (a, b) => a !== b);
2658
- handlebars.registerHelper("and", (a, b) => a && b);
2659
- handlebars.registerHelper("or", (a, b) => a || b);
2688
+ handlebars.registerHelper("and", (...args) => {
2689
+ return args.slice(0, -1).every((value) => value);
2690
+ });
2691
+ handlebars.registerHelper("or", (...args) => {
2692
+ return args.slice(0, -1).some((value) => value);
2693
+ });
2660
2694
  handlebars.registerHelper("includes", (array, value) => Array.isArray(array) && array.includes(value));
2661
2695
 
2662
2696
  //#endregion
@@ -2718,6 +2752,10 @@ async function setupFrontendTemplates(projectDir, context) {
2718
2752
  const apiWebBaseDir = path.join(PKG_ROOT, `templates/api/${context.api}/web/react/base`);
2719
2753
  if (await fs.pathExists(apiWebBaseDir)) await processAndCopyFiles("**/*", apiWebBaseDir, webAppDir, context);
2720
2754
  }
2755
+ if (context.backend === "self" && reactFramework === "next" && context.api !== "none") {
2756
+ const apiFullstackDir = path.join(PKG_ROOT, `templates/api/${context.api}/fullstack/next`);
2757
+ if (await fs.pathExists(apiFullstackDir)) await processAndCopyFiles("**/*", apiFullstackDir, webAppDir, context);
2758
+ }
2721
2759
  }
2722
2760
  } else if (hasNuxtWeb) {
2723
2761
  const nuxtBaseDir = path.join(PKG_ROOT, "templates/frontend/nuxt");
@@ -2758,35 +2796,52 @@ async function setupFrontendTemplates(projectDir, context) {
2758
2796
  }
2759
2797
  }
2760
2798
  }
2761
- async function setupBackendFramework(projectDir, context) {
2762
- if (context.backend === "none") return;
2799
+ async function setupApiPackage(projectDir, context) {
2800
+ if (context.api === "none") return;
2801
+ const apiPackageDir = path.join(projectDir, "packages/api");
2802
+ await fs.ensureDir(apiPackageDir);
2803
+ const apiServerDir = path.join(PKG_ROOT, `templates/api/${context.api}/server`);
2804
+ if (await fs.pathExists(apiServerDir)) await processAndCopyFiles("**/*", apiServerDir, apiPackageDir, context);
2805
+ }
2806
+ async function setupDbPackage(projectDir, context) {
2807
+ if (context.database === "none" || context.orm === "none") return;
2808
+ const dbPackageDir = path.join(projectDir, "packages/db");
2809
+ await fs.ensureDir(dbPackageDir);
2810
+ const dbBaseDir = path.join(PKG_ROOT, "templates/db/base");
2811
+ if (await fs.pathExists(dbBaseDir)) await processAndCopyFiles("**/*", dbBaseDir, dbPackageDir, context);
2812
+ const dbOrmSrcDir = path.join(PKG_ROOT, `templates/db/${context.orm}/${context.database}`);
2813
+ if (await fs.pathExists(dbOrmSrcDir)) await processAndCopyFiles("**/*", dbOrmSrcDir, dbPackageDir, context);
2814
+ }
2815
+ async function setupConvexBackend(projectDir, context) {
2816
+ const serverAppDir = path.join(projectDir, "apps/server");
2817
+ if (await fs.pathExists(serverAppDir)) await fs.remove(serverAppDir);
2818
+ const convexBackendDestDir = path.join(projectDir, "packages/backend");
2819
+ const convexSrcDir = path.join(PKG_ROOT, "templates/backend/convex/packages/backend");
2820
+ await fs.ensureDir(convexBackendDestDir);
2821
+ if (await fs.pathExists(convexSrcDir)) await processAndCopyFiles("**/*", convexSrcDir, convexBackendDestDir, context);
2822
+ }
2823
+ async function setupServerApp(projectDir, context) {
2763
2824
  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
2825
  await fs.ensureDir(serverAppDir);
2773
- const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/server-base");
2826
+ const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/base");
2774
2827
  if (await fs.pathExists(serverBaseDir)) await processAndCopyFiles("**/*", serverBaseDir, serverAppDir, context);
2775
2828
  const frameworkSrcDir = path.join(PKG_ROOT, `templates/backend/server/${context.backend}`);
2776
2829
  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
2830
  }
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);
2831
+ async function setupBackendFramework(projectDir, context) {
2832
+ if (context.backend === "none") return;
2833
+ if (context.backend === "convex") {
2834
+ await setupConvexBackend(projectDir, context);
2835
+ return;
2836
+ }
2837
+ if (context.backend === "self") {
2838
+ await setupApiPackage(projectDir, context);
2839
+ await setupDbPackage(projectDir, context);
2840
+ return;
2841
+ }
2842
+ await setupServerApp(projectDir, context);
2843
+ await setupApiPackage(projectDir, context);
2844
+ await setupDbPackage(projectDir, context);
2790
2845
  }
2791
2846
  async function setupAuthTemplate(projectDir, context) {
2792
2847
  if (!context.auth || context.auth === "none") return;
@@ -2866,21 +2921,21 @@ async function setupAuthTemplate(projectDir, context) {
2866
2921
  }
2867
2922
  return;
2868
2923
  }
2869
- if (serverAppDirExists && context.backend !== "convex") {
2924
+ if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
2925
+ const authPackageDir = path.join(projectDir, "packages/auth");
2926
+ await fs.ensureDir(authPackageDir);
2870
2927
  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
- }
2928
+ if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc, authPackageDir, context);
2876
2929
  if (context.orm !== "none" && context.database !== "none") {
2930
+ const dbPackageDir = path.join(projectDir, "packages/db");
2931
+ await fs.ensureDir(dbPackageDir);
2877
2932
  const orm = context.orm;
2878
2933
  const db = context.database;
2879
2934
  let authDbSrc = "";
2880
2935
  if (orm === "drizzle") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/drizzle/${db}`);
2881
2936
  else if (orm === "prisma") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/prisma/${db}`);
2882
2937
  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);
2938
+ if (authDbSrc && await fs.pathExists(authDbSrc)) await processAndCopyFiles("**/*", authDbSrc, dbPackageDir, context);
2884
2939
  }
2885
2940
  }
2886
2941
  if ((hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb) && webAppDirExists) {
@@ -2896,6 +2951,10 @@ async function setupAuthTemplate(projectDir, context) {
2896
2951
  if (reactFramework) {
2897
2952
  const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/${reactFramework}`);
2898
2953
  if (await fs.pathExists(authWebFrameworkSrc)) await processAndCopyFiles("**/*", authWebFrameworkSrc, webAppDir, context);
2954
+ if (context.backend === "self" && reactFramework === "next") {
2955
+ const authFullstackSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/fullstack/next`);
2956
+ if (await fs.pathExists(authFullstackSrc)) await processAndCopyFiles("**/*", authFullstackSrc, webAppDir, context);
2957
+ }
2899
2958
  }
2900
2959
  } else if (hasNuxtWeb) {
2901
2960
  const authWebNuxtSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/nuxt`);
@@ -2926,9 +2985,11 @@ async function setupPaymentsTemplate(projectDir, context) {
2926
2985
  const webAppDir = path.join(projectDir, "apps/web");
2927
2986
  const serverAppDirExists = await fs.pathExists(serverAppDir);
2928
2987
  const webAppDirExists = await fs.pathExists(webAppDir);
2929
- if (serverAppDirExists && context.backend !== "convex") {
2988
+ if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
2989
+ const authPackageDir = path.join(projectDir, "packages/auth");
2990
+ await fs.ensureDir(authPackageDir);
2930
2991
  const paymentsServerSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/server/base`);
2931
- if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc, serverAppDir, context);
2992
+ if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc, authPackageDir, context);
2932
2993
  }
2933
2994
  const hasReactWeb = context.frontend.some((f) => [
2934
2995
  "tanstack-router",
@@ -3004,17 +3065,19 @@ async function setupExamplesTemplate(projectDir, context) {
3004
3065
  for (const example of context.examples) {
3005
3066
  if (example === "none") continue;
3006
3067
  const exampleBaseDir = path.join(PKG_ROOT, `templates/examples/${example}`);
3007
- if (serverAppDirExists && context.backend !== "convex" && context.backend !== "none") {
3068
+ if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex" && context.backend !== "none") {
3008
3069
  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);
3070
+ if (context.api !== "none") {
3071
+ const apiPackageDir = path.join(projectDir, "packages/api");
3072
+ await fs.ensureDir(apiPackageDir);
3073
+ const exampleOrmBaseSrc = path.join(exampleServerSrc, context.orm, "base");
3074
+ if (await fs.pathExists(exampleOrmBaseSrc)) await processAndCopyFiles("**/*", exampleOrmBaseSrc, apiPackageDir, context, false);
3012
3075
  }
3013
3076
  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);
3077
+ const dbPackageDir = path.join(projectDir, "packages/db");
3078
+ await fs.ensureDir(dbPackageDir);
3016
3079
  const exampleDbSchemaSrc = path.join(exampleServerSrc, context.orm, context.database);
3017
- if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc, serverAppDir, context, false);
3080
+ if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc, dbPackageDir, context, false);
3018
3081
  }
3019
3082
  }
3020
3083
  if (webAppDirExists) {
@@ -3034,6 +3097,10 @@ async function setupExamplesTemplate(projectDir, context) {
3034
3097
  if (reactFramework) {
3035
3098
  const exampleWebFrameworkSrc = path.join(exampleWebSrc, reactFramework);
3036
3099
  if (await fs.pathExists(exampleWebFrameworkSrc)) await processAndCopyFiles("**/*", exampleWebFrameworkSrc, webAppDir, context, false);
3100
+ if (context.backend === "self" && reactFramework === "next") {
3101
+ const exampleFullstackSrc = path.join(exampleBaseDir, "fullstack/next");
3102
+ if (await fs.pathExists(exampleFullstackSrc)) await processAndCopyFiles("**/*", exampleFullstackSrc, webAppDir, context, false);
3103
+ }
3037
3104
  }
3038
3105
  }
3039
3106
  } else if (hasNuxtWeb) {
@@ -3081,30 +3148,34 @@ async function handleExtras(projectDir, context) {
3081
3148
  }
3082
3149
  async function setupDockerComposeTemplates(projectDir, context) {
3083
3150
  if (context.dbSetup !== "docker" || context.database === "none") return;
3084
- const serverAppDir = path.join(projectDir, "apps/server");
3151
+ const dbPackageDir = path.join(projectDir, "packages/db");
3085
3152
  const dockerSrcDir = path.join(PKG_ROOT, `templates/db-setup/docker-compose/${context.database}`);
3086
- if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, serverAppDir, context);
3153
+ if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, dbPackageDir, context);
3087
3154
  }
3088
3155
  async function setupDeploymentTemplates(projectDir, context) {
3089
- if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") if (context.webDeploy === "alchemy" && context.serverDeploy === "alchemy") {
3156
+ const isBackendSelf = context.backend === "self";
3157
+ if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") {
3090
3158
  const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
3091
- if (await fs.pathExists(alchemyTemplateSrc)) {
3092
- await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, projectDir, context);
3093
- const serverAppDir = path.join(projectDir, "apps/server");
3094
- if (await fs.pathExists(serverAppDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3095
- }
3096
- } else {
3097
- if (context.webDeploy === "alchemy") {
3098
- const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
3099
- const webAppDir = path.join(projectDir, "apps/web");
3100
- if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(webAppDir)) await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, webAppDir, context);
3101
- }
3102
- if (context.serverDeploy === "alchemy") {
3103
- const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
3104
- const serverAppDir = path.join(projectDir, "apps/server");
3105
- if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(serverAppDir)) {
3106
- await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3107
- await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3159
+ if (context.webDeploy === "alchemy" && (context.serverDeploy === "alchemy" || isBackendSelf)) {
3160
+ if (await fs.pathExists(alchemyTemplateSrc)) {
3161
+ await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, projectDir, context);
3162
+ await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3163
+ }
3164
+ } else {
3165
+ if (context.webDeploy === "alchemy") {
3166
+ const webAppDir = path.join(projectDir, "apps/web");
3167
+ if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(webAppDir)) {
3168
+ await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, webAppDir, context);
3169
+ await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3170
+ }
3171
+ }
3172
+ if (context.serverDeploy === "alchemy" && !isBackendSelf) {
3173
+ const serverAppDir = path.join(projectDir, "apps/server");
3174
+ if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(serverAppDir)) {
3175
+ await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3176
+ await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3177
+ await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3178
+ }
3108
3179
  }
3109
3180
  }
3110
3181
  }
@@ -3127,7 +3198,7 @@ async function setupDeploymentTemplates(projectDir, context) {
3127
3198
  }
3128
3199
  }
3129
3200
  }
3130
- if (context.serverDeploy !== "none" && context.serverDeploy !== "alchemy") {
3201
+ if (context.serverDeploy !== "none" && context.serverDeploy !== "alchemy" && !isBackendSelf) {
3131
3202
  const serverAppDir = path.join(projectDir, "apps/server");
3132
3203
  if (await fs.pathExists(serverAppDir)) {
3133
3204
  const deployTemplateSrc = path.join(PKG_ROOT, `templates/deploy/${context.serverDeploy}/server`);
@@ -3135,6 +3206,18 @@ async function setupDeploymentTemplates(projectDir, context) {
3135
3206
  }
3136
3207
  }
3137
3208
  }
3209
+ async function addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc) {
3210
+ for (const packageName of [
3211
+ "packages/api",
3212
+ "packages/auth",
3213
+ "packages/db"
3214
+ ]) {
3215
+ const packageDir = path.join(projectDir, packageName);
3216
+ if (await fs.pathExists(packageDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, packageDir, context);
3217
+ }
3218
+ const serverAppDir = path.join(projectDir, "apps/server");
3219
+ if (await fs.pathExists(serverAppDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3220
+ }
3138
3221
 
3139
3222
  //#endregion
3140
3223
  //#region src/helpers/core/add-addons.ts
@@ -3200,7 +3283,7 @@ async function setupServerDeploy(config) {
3200
3283
  serverDir,
3201
3284
  packageManager
3202
3285
  });
3203
- } else if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, packageManager);
3286
+ } else if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, packageManager, projectDir);
3204
3287
  }
3205
3288
  async function setupWorkersServerDeploy(serverDir, _packageManager) {
3206
3289
  const packageJsonPath = path.join(serverDir, "package.json");
@@ -3237,18 +3320,18 @@ async function generateCloudflareWorkerTypes({ serverDir, packageManager }) {
3237
3320
  log.warn(`Note: You can manually run 'cd apps/server && ${managerCmd} cf-typegen' in the project directory later`);
3238
3321
  }
3239
3322
  }
3240
- async function setupAlchemyServerDeploy(serverDir, _packageManager) {
3323
+ async function setupAlchemyServerDeploy(serverDir, _packageManager, projectDir) {
3241
3324
  if (!await fs.pathExists(serverDir)) return;
3242
3325
  await addPackageDependency({
3243
3326
  devDependencies: [
3244
3327
  "alchemy",
3245
3328
  "wrangler",
3246
3329
  "@types/node",
3247
- "dotenv",
3248
3330
  "@cloudflare/workers-types"
3249
3331
  ],
3250
3332
  projectDir: serverDir
3251
3333
  });
3334
+ if (projectDir) await addAlchemyPackagesDependencies$1(projectDir);
3252
3335
  const packageJsonPath = path.join(serverDir, "package.json");
3253
3336
  if (await fs.pathExists(packageJsonPath)) {
3254
3337
  const packageJson = await fs.readJson(packageJsonPath);
@@ -3261,6 +3344,19 @@ async function setupAlchemyServerDeploy(serverDir, _packageManager) {
3261
3344
  await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
3262
3345
  }
3263
3346
  }
3347
+ async function addAlchemyPackagesDependencies$1(projectDir) {
3348
+ for (const packageName of [
3349
+ "packages/api",
3350
+ "packages/auth",
3351
+ "packages/db"
3352
+ ]) {
3353
+ const packageDir = path.join(projectDir, packageName);
3354
+ if (await fs.pathExists(packageDir)) await addPackageDependency({
3355
+ devDependencies: ["@cloudflare/workers-types"],
3356
+ projectDir: packageDir
3357
+ });
3358
+ }
3359
+ }
3264
3360
 
3265
3361
  //#endregion
3266
3362
  //#region src/helpers/deployment/alchemy/alchemy-next-setup.ts
@@ -3271,7 +3367,6 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
3271
3367
  dependencies: ["@opennextjs/cloudflare"],
3272
3368
  devDependencies: [
3273
3369
  "alchemy",
3274
- "dotenv",
3275
3370
  "wrangler",
3276
3371
  "@cloudflare/workers-types"
3277
3372
  ],
@@ -3307,7 +3402,6 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
3307
3402
  devDependencies: [
3308
3403
  "alchemy",
3309
3404
  "nitro-cloudflare-dev",
3310
- "dotenv",
3311
3405
  "wrangler"
3312
3406
  ],
3313
3407
  projectDir: webAppDir
@@ -3370,7 +3464,7 @@ async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, option
3370
3464
  const webAppDir = path.join(projectDir, "apps/web");
3371
3465
  if (!await fs.pathExists(webAppDir)) return;
3372
3466
  await addPackageDependency({
3373
- devDependencies: ["alchemy", "dotenv"],
3467
+ devDependencies: ["alchemy"],
3374
3468
  projectDir: webAppDir
3375
3469
  });
3376
3470
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3391,7 +3485,7 @@ async function setupSolidAlchemyDeploy(projectDir, _packageManager, options) {
3391
3485
  const webAppDir = path.join(projectDir, "apps/web");
3392
3486
  if (!await fs.pathExists(webAppDir)) return;
3393
3487
  await addPackageDependency({
3394
- devDependencies: ["alchemy", "dotenv"],
3488
+ devDependencies: ["alchemy"],
3395
3489
  projectDir: webAppDir
3396
3490
  });
3397
3491
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3412,11 +3506,7 @@ async function setupSvelteAlchemyDeploy(projectDir, _packageManager, options) {
3412
3506
  const webAppDir = path.join(projectDir, "apps/web");
3413
3507
  if (!await fs.pathExists(webAppDir)) return;
3414
3508
  await addPackageDependency({
3415
- devDependencies: [
3416
- "alchemy",
3417
- "@sveltejs/adapter-cloudflare",
3418
- "dotenv"
3419
- ],
3509
+ devDependencies: ["alchemy", "@sveltejs/adapter-cloudflare"],
3420
3510
  projectDir: webAppDir
3421
3511
  });
3422
3512
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3481,7 +3571,7 @@ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, opt
3481
3571
  const webAppDir = path.join(projectDir, "apps/web");
3482
3572
  if (!await fs.pathExists(webAppDir)) return;
3483
3573
  await addPackageDependency({
3484
- devDependencies: ["alchemy", "dotenv"],
3574
+ devDependencies: ["alchemy"],
3485
3575
  projectDir: webAppDir
3486
3576
  });
3487
3577
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3502,11 +3592,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3502
3592
  const webAppDir = path.join(projectDir, "apps/web");
3503
3593
  if (!await fs.pathExists(webAppDir)) return;
3504
3594
  await addPackageDependency({
3505
- devDependencies: [
3506
- "alchemy",
3507
- "dotenv",
3508
- "@cloudflare/vite-plugin"
3509
- ],
3595
+ devDependencies: ["alchemy", "@cloudflare/vite-plugin"],
3510
3596
  projectDir: webAppDir
3511
3597
  });
3512
3598
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3561,7 +3647,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3561
3647
  //#region src/helpers/deployment/alchemy/alchemy-combined-setup.ts
3562
3648
  async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
3563
3649
  await addPackageDependency({
3564
- devDependencies: ["alchemy", "dotenv"],
3650
+ devDependencies: ["alchemy"],
3565
3651
  projectDir
3566
3652
  });
3567
3653
  const rootPkgPath = path.join(projectDir, "package.json");
@@ -3576,7 +3662,7 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
3576
3662
  await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
3577
3663
  }
3578
3664
  const serverDir = path.join(projectDir, "apps/server");
3579
- if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, packageManager);
3665
+ if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, packageManager, projectDir);
3580
3666
  const frontend = config.frontend;
3581
3667
  const isNext = frontend.includes("next");
3582
3668
  const isNuxt = frontend.includes("nuxt");
@@ -3809,6 +3895,7 @@ async function setupWebDeploy(config) {
3809
3895
  if (webDeploy !== "wrangler" && webDeploy !== "alchemy") return;
3810
3896
  if (webDeploy === "alchemy" && serverDeploy === "alchemy") {
3811
3897
  await setupCombinedAlchemyDeploy(projectDir, packageManager, config);
3898
+ await addAlchemyPackagesDependencies(projectDir);
3812
3899
  return;
3813
3900
  }
3814
3901
  const isNext = frontend.includes("next");
@@ -3832,6 +3919,7 @@ async function setupWebDeploy(config) {
3832
3919
  else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager);
3833
3920
  else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager);
3834
3921
  else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager);
3922
+ await addAlchemyPackagesDependencies(projectDir);
3835
3923
  }
3836
3924
  }
3837
3925
  async function setupWorkersWebDeploy(projectDir, pkgManager) {
@@ -3849,6 +3937,19 @@ async function setupWorkersWebDeploy(projectDir, pkgManager) {
3849
3937
  }
3850
3938
  await setupWorkersVitePlugin(projectDir);
3851
3939
  }
3940
+ async function addAlchemyPackagesDependencies(projectDir) {
3941
+ for (const packageName of [
3942
+ "packages/api",
3943
+ "packages/auth",
3944
+ "packages/db"
3945
+ ]) {
3946
+ const packageDir = path.join(projectDir, packageName);
3947
+ if (await fs.pathExists(packageDir)) await addPackageDependency({
3948
+ devDependencies: ["@cloudflare/workers-types"],
3949
+ projectDir: packageDir
3950
+ });
3951
+ }
3952
+ }
3852
3953
 
3853
3954
  //#endregion
3854
3955
  //#region src/helpers/core/add-deployment.ts
@@ -3901,18 +4002,137 @@ async function addDeploymentToProject(input) {
3901
4002
  }
3902
4003
  }
3903
4004
 
4005
+ //#endregion
4006
+ //#region src/utils/setup-catalogs.ts
4007
+ async function setupCatalogs(projectDir, options) {
4008
+ if (options.packageManager === "npm") return;
4009
+ const packagePaths = [
4010
+ "apps/server",
4011
+ "apps/web",
4012
+ "packages/api",
4013
+ "packages/db",
4014
+ "packages/auth",
4015
+ "packages/backend"
4016
+ ];
4017
+ const packagesInfo = [];
4018
+ for (const pkgPath of packagePaths) {
4019
+ const fullPath = path.join(projectDir, pkgPath);
4020
+ const pkgJsonPath = path.join(fullPath, "package.json");
4021
+ if (await fs.pathExists(pkgJsonPath)) {
4022
+ const pkgJson = await fs.readJson(pkgJsonPath);
4023
+ packagesInfo.push({
4024
+ path: fullPath,
4025
+ dependencies: pkgJson.dependencies || {},
4026
+ devDependencies: pkgJson.devDependencies || {}
4027
+ });
4028
+ }
4029
+ }
4030
+ const catalog = findDuplicateDependencies(packagesInfo, options.projectName);
4031
+ if (Object.keys(catalog).length === 0) return;
4032
+ if (options.packageManager === "bun") await setupBunCatalogs(projectDir, catalog);
4033
+ else if (options.packageManager === "pnpm") await setupPnpmCatalogs(projectDir, catalog);
4034
+ await updatePackageJsonsWithCatalogs(packagesInfo, catalog);
4035
+ }
4036
+ function findDuplicateDependencies(packagesInfo, projectName) {
4037
+ const depCount = /* @__PURE__ */ new Map();
4038
+ const projectScope = `@${projectName}/`;
4039
+ for (const pkg of packagesInfo) {
4040
+ const allDeps = {
4041
+ ...pkg.dependencies,
4042
+ ...pkg.devDependencies
4043
+ };
4044
+ for (const [depName, version] of Object.entries(allDeps)) {
4045
+ if (depName.startsWith(projectScope)) continue;
4046
+ if (version.startsWith("workspace:")) continue;
4047
+ const existing = depCount.get(depName);
4048
+ if (existing) existing.packages.push(pkg.path);
4049
+ else depCount.set(depName, {
4050
+ version,
4051
+ packages: [pkg.path]
4052
+ });
4053
+ }
4054
+ }
4055
+ const catalog = {};
4056
+ for (const [depName, info] of depCount.entries()) if (info.packages.length > 1) catalog[depName] = info.version;
4057
+ return catalog;
4058
+ }
4059
+ async function setupBunCatalogs(projectDir, catalog) {
4060
+ const rootPkgJsonPath = path.join(projectDir, "package.json");
4061
+ const rootPkgJson = await fs.readJson(rootPkgJsonPath);
4062
+ if (!rootPkgJson.workspaces) rootPkgJson.workspaces = {};
4063
+ if (Array.isArray(rootPkgJson.workspaces)) rootPkgJson.workspaces = {
4064
+ packages: rootPkgJson.workspaces,
4065
+ catalog
4066
+ };
4067
+ else if (typeof rootPkgJson.workspaces === "object") {
4068
+ if (!rootPkgJson.workspaces.catalog) rootPkgJson.workspaces.catalog = {};
4069
+ rootPkgJson.workspaces.catalog = {
4070
+ ...rootPkgJson.workspaces.catalog,
4071
+ ...catalog
4072
+ };
4073
+ }
4074
+ await fs.writeJson(rootPkgJsonPath, rootPkgJson, { spaces: 2 });
4075
+ }
4076
+ async function setupPnpmCatalogs(projectDir, catalog) {
4077
+ const workspaceYamlPath = path.join(projectDir, "pnpm-workspace.yaml");
4078
+ if (!await fs.pathExists(workspaceYamlPath)) return;
4079
+ const workspaceContent = await fs.readFile(workspaceYamlPath, "utf-8");
4080
+ const workspaceYaml = yaml.parse(workspaceContent);
4081
+ if (!workspaceYaml.catalog) workspaceYaml.catalog = {};
4082
+ workspaceYaml.catalog = {
4083
+ ...workspaceYaml.catalog,
4084
+ ...catalog
4085
+ };
4086
+ await fs.writeFile(workspaceYamlPath, yaml.stringify(workspaceYaml));
4087
+ }
4088
+ async function updatePackageJsonsWithCatalogs(packagesInfo, catalog) {
4089
+ for (const pkg of packagesInfo) {
4090
+ const pkgJsonPath = path.join(pkg.path, "package.json");
4091
+ const pkgJson = await fs.readJson(pkgJsonPath);
4092
+ let updated = false;
4093
+ if (pkgJson.dependencies) {
4094
+ for (const depName of Object.keys(pkgJson.dependencies)) if (catalog[depName]) {
4095
+ pkgJson.dependencies[depName] = "catalog:";
4096
+ updated = true;
4097
+ }
4098
+ }
4099
+ if (pkgJson.devDependencies) {
4100
+ for (const depName of Object.keys(pkgJson.devDependencies)) if (catalog[depName]) {
4101
+ pkgJson.devDependencies[depName] = "catalog:";
4102
+ updated = true;
4103
+ }
4104
+ }
4105
+ if (updated) await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
4106
+ }
4107
+ }
4108
+
3904
4109
  //#endregion
3905
4110
  //#region src/helpers/addons/examples-setup.ts
3906
4111
  async function setupExamples(config) {
3907
- const { examples, frontend, backend, projectDir } = config;
4112
+ const { examples, frontend, backend, projectDir, orm } = config;
3908
4113
  if (backend === "convex" || !examples || examples.length === 0 || examples[0] === "none") return;
4114
+ const apiDir = path.join(projectDir, "packages/api");
4115
+ if (await fs.pathExists(apiDir) && backend !== "none") {
4116
+ if (orm === "drizzle") await addPackageDependency({
4117
+ dependencies: ["drizzle-orm"],
4118
+ projectDir: apiDir
4119
+ });
4120
+ else if (orm === "prisma") await addPackageDependency({
4121
+ dependencies: ["@prisma/client"],
4122
+ projectDir: apiDir
4123
+ });
4124
+ else if (orm === "mongoose") await addPackageDependency({
4125
+ dependencies: ["mongoose"],
4126
+ projectDir: apiDir
4127
+ });
4128
+ }
3909
4129
  if (examples.includes("ai")) {
3910
4130
  const webClientDir = path.join(projectDir, "apps/web");
3911
4131
  const nativeClientDir = path.join(projectDir, "apps/native");
3912
- const serverDir = path.join(projectDir, "apps/server");
4132
+ const apiDir$1 = path.join(projectDir, "packages/api");
3913
4133
  const webClientDirExists = await fs.pathExists(webClientDir);
3914
4134
  const nativeClientDirExists = await fs.pathExists(nativeClientDir);
3915
- const serverDirExists = await fs.pathExists(serverDir);
4135
+ const apiDirExists = await fs.pathExists(apiDir$1);
3916
4136
  const hasNuxt = frontend.includes("nuxt");
3917
4137
  const hasSvelte = frontend.includes("svelte");
3918
4138
  const hasReactWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next") || frontend.includes("tanstack-start");
@@ -3933,9 +4153,13 @@ async function setupExamples(config) {
3933
4153
  dependencies: ["ai", "@ai-sdk/react"],
3934
4154
  projectDir: nativeClientDir
3935
4155
  });
3936
- if (serverDirExists && backend !== "none") await addPackageDependency({
4156
+ if (apiDirExists && backend !== "none") await addPackageDependency({
3937
4157
  dependencies: ["ai", "@ai-sdk/google"],
3938
- projectDir: serverDir
4158
+ projectDir: apiDir$1
4159
+ });
4160
+ if (backend === "self" && webClientDirExists) await addPackageDependency({
4161
+ dependencies: ["ai", "@ai-sdk/google"],
4162
+ projectDir: webClientDir
3939
4163
  });
3940
4164
  }
3941
4165
  }
@@ -4058,23 +4282,26 @@ async function setupApi(config) {
4058
4282
  const serverDir = path.join(projectDir, "apps/server");
4059
4283
  const webDirExists = await fs.pathExists(webDir);
4060
4284
  const nativeDirExists = await fs.pathExists(nativeDir);
4061
- const serverDirExists = await fs.pathExists(serverDir);
4285
+ await fs.pathExists(serverDir);
4062
4286
  const frontendType = getFrontendType(frontend);
4063
4287
  if (!isConvex && api !== "none") {
4064
4288
  const apiDeps = getApiDependencies(api, frontendType);
4065
- if (serverDirExists && apiDeps.server) {
4289
+ const apiPackageDir = path.join(projectDir, "packages/api");
4290
+ if (apiDeps.server) {
4066
4291
  await addPackageDependency({
4067
4292
  dependencies: apiDeps.server.dependencies,
4068
- projectDir: serverDir
4293
+ projectDir: apiPackageDir
4069
4294
  });
4070
- if (api === "trpc") {
4071
- if (backend === "hono") await addPackageDependency({
4072
- dependencies: ["@hono/trpc-server"],
4073
- projectDir: serverDir
4074
- });
4075
- else if (backend === "elysia") await addPackageDependency({
4076
- dependencies: ["@elysiajs/trpc"],
4077
- projectDir: serverDir
4295
+ if (backend === "self" && webDirExists) await addPackageDependency({
4296
+ dependencies: apiDeps.server.dependencies,
4297
+ projectDir: webDir
4298
+ });
4299
+ if (backend === "self") {
4300
+ const frameworkDeps = [];
4301
+ if (frontend.includes("next")) frameworkDeps.push("next");
4302
+ if (frameworkDeps.length > 0) await addPackageDependency({
4303
+ dependencies: frameworkDeps,
4304
+ projectDir: apiPackageDir
4078
4305
  });
4079
4306
  }
4080
4307
  }
@@ -4120,7 +4347,7 @@ async function setupApi(config) {
4120
4347
  //#endregion
4121
4348
  //#region src/helpers/core/backend-setup.ts
4122
4349
  async function setupBackendDependencies(config) {
4123
- const { backend, runtime, api, projectDir } = config;
4350
+ const { backend, runtime, api, auth, examples, projectDir } = config;
4124
4351
  if (backend === "convex") return;
4125
4352
  const framework = backend;
4126
4353
  const serverDir = path.join(projectDir, "apps/server");
@@ -4128,27 +4355,23 @@ async function setupBackendDependencies(config) {
4128
4355
  const devDependencies = [];
4129
4356
  if (framework === "hono") {
4130
4357
  dependencies.push("hono");
4131
- if (api === "trpc") dependencies.push("@hono/trpc-server");
4132
- if (runtime === "node") {
4133
- dependencies.push("@hono/node-server");
4134
- devDependencies.push("tsx", "@types/node");
4135
- }
4358
+ if (runtime === "node") dependencies.push("@hono/node-server");
4136
4359
  } else if (framework === "elysia") {
4137
4360
  dependencies.push("elysia", "@elysiajs/cors");
4138
- if (api === "trpc") dependencies.push("@elysiajs/trpc");
4139
- if (runtime === "node") {
4140
- dependencies.push("@elysiajs/node");
4141
- devDependencies.push("tsx", "@types/node");
4142
- }
4361
+ if (runtime === "node") dependencies.push("@elysiajs/node");
4143
4362
  } else if (framework === "express") {
4144
4363
  dependencies.push("express", "cors");
4145
4364
  devDependencies.push("@types/express", "@types/cors");
4146
- if (runtime === "node") devDependencies.push("tsx", "@types/node");
4147
- } else if (framework === "fastify") {
4148
- dependencies.push("fastify", "@fastify/cors");
4149
- if (runtime === "node") devDependencies.push("tsx", "@types/node");
4150
- }
4151
- if (runtime === "bun") devDependencies.push("@types/bun");
4365
+ } else if (framework === "fastify") dependencies.push("fastify", "@fastify/cors");
4366
+ if (api === "trpc") {
4367
+ dependencies.push("@trpc/server");
4368
+ if (framework === "hono") dependencies.push("@hono/trpc-server");
4369
+ else if (framework === "elysia") dependencies.push("@elysiajs/trpc");
4370
+ } else if (api === "orpc") dependencies.push("@orpc/server", "@orpc/openapi", "@orpc/zod");
4371
+ if (auth === "better-auth") dependencies.push("better-auth");
4372
+ if (examples.includes("ai")) dependencies.push("ai", "@ai-sdk/google");
4373
+ if (runtime === "node") devDependencies.push("tsx", "@types/node");
4374
+ else if (runtime === "bun") devDependencies.push("@types/bun");
4152
4375
  if (dependencies.length > 0 || devDependencies.length > 0) await addPackageDependency({
4153
4376
  dependencies,
4154
4377
  devDependencies,
@@ -4166,7 +4389,7 @@ async function setupAuth(config) {
4166
4389
  const nativeDir = path.join(projectDir, "apps/native");
4167
4390
  const clientDirExists = await fs.pathExists(clientDir);
4168
4391
  const nativeDirExists = await fs.pathExists(nativeDir);
4169
- const serverDirExists = await fs.pathExists(serverDir);
4392
+ await fs.pathExists(serverDir);
4170
4393
  try {
4171
4394
  if (backend === "convex") {
4172
4395
  if (auth === "clerk" && clientDirExists) {
@@ -4222,9 +4445,11 @@ async function setupAuth(config) {
4222
4445
  });
4223
4446
  return;
4224
4447
  }
4225
- if (serverDirExists && auth === "better-auth") await addPackageDependency({
4448
+ const authPackageDir = path.join(projectDir, "packages/auth");
4449
+ const authPackageDirExists = await fs.pathExists(authPackageDir);
4450
+ if (authPackageDirExists && auth === "better-auth") await addPackageDependency({
4226
4451
  dependencies: ["better-auth"],
4227
- projectDir: serverDir
4452
+ projectDir: authPackageDir
4228
4453
  });
4229
4454
  if (frontend.some((f) => [
4230
4455
  "react-router",
@@ -4246,9 +4471,9 @@ async function setupAuth(config) {
4246
4471
  dependencies: ["better-auth", "@better-auth/expo"],
4247
4472
  projectDir: nativeDir
4248
4473
  });
4249
- if (serverDirExists) await addPackageDependency({
4474
+ if (authPackageDirExists) await addPackageDependency({
4250
4475
  dependencies: ["@better-auth/expo"],
4251
- projectDir: serverDir
4476
+ projectDir: authPackageDir
4252
4477
  });
4253
4478
  }
4254
4479
  }
@@ -4267,6 +4492,34 @@ function generateAuthSecret(length = 32) {
4267
4492
 
4268
4493
  //#endregion
4269
4494
  //#region src/helpers/core/env-setup.ts
4495
+ function getClientServerVar(frontend, backend) {
4496
+ const hasNextJs = frontend.includes("next");
4497
+ const hasNuxt = frontend.includes("nuxt");
4498
+ const hasSvelte = frontend.includes("svelte");
4499
+ if (backend === "self") return {
4500
+ key: "",
4501
+ value: "",
4502
+ write: false
4503
+ };
4504
+ let key = "VITE_SERVER_URL";
4505
+ if (hasNextJs) key = "NEXT_PUBLIC_SERVER_URL";
4506
+ else if (hasNuxt) key = "NUXT_PUBLIC_SERVER_URL";
4507
+ else if (hasSvelte) key = "PUBLIC_SERVER_URL";
4508
+ return {
4509
+ key,
4510
+ value: "http://localhost:3000",
4511
+ write: true
4512
+ };
4513
+ }
4514
+ function getConvexVar(frontend) {
4515
+ const hasNextJs = frontend.includes("next");
4516
+ const hasNuxt = frontend.includes("nuxt");
4517
+ const hasSvelte = frontend.includes("svelte");
4518
+ if (hasNextJs) return "NEXT_PUBLIC_CONVEX_URL";
4519
+ if (hasNuxt) return "NUXT_PUBLIC_CONVEX_URL";
4520
+ if (hasSvelte) return "PUBLIC_CONVEX_URL";
4521
+ return "VITE_CONVEX_URL";
4522
+ }
4270
4523
  async function addEnvVariablesToFile(filePath, variables) {
4271
4524
  await fs.ensureDir(path.dirname(filePath));
4272
4525
  let envContent = "";
@@ -4325,22 +4578,13 @@ async function setupEnvironmentVariables(config) {
4325
4578
  if (hasReactRouter || hasTanStackRouter || hasTanStackStart || hasNextJs || hasNuxt || hasSolid || hasSvelte) {
4326
4579
  const clientDir = path.join(projectDir, "apps/web");
4327
4580
  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
- }
4581
+ const baseVar = getClientServerVar(frontend, backend);
4582
+ const envVarName = backend === "convex" ? getConvexVar(frontend) : baseVar.key;
4583
+ const serverUrl = backend === "convex" ? "https://<YOUR_CONVEX_URL>" : baseVar.value;
4340
4584
  const clientVars = [{
4341
4585
  key: envVarName,
4342
4586
  value: serverUrl,
4343
- condition: true
4587
+ condition: backend === "convex" ? true : baseVar.write
4344
4588
  }];
4345
4589
  if (backend === "convex" && auth === "clerk") {
4346
4590
  if (hasNextJs) clientVars.push({
@@ -4388,7 +4632,7 @@ async function setupEnvironmentVariables(config) {
4388
4632
  const nativeDir = path.join(projectDir, "apps/native");
4389
4633
  if (await fs.pathExists(nativeDir)) {
4390
4634
  let envVarName = "EXPO_PUBLIC_SERVER_URL";
4391
- let serverUrl = "http://localhost:3000";
4635
+ let serverUrl = backend === "self" ? "http://localhost:3001" : "http://localhost:3000";
4392
4636
  if (backend === "convex") {
4393
4637
  envVarName = "EXPO_PUBLIC_CONVEX_URL";
4394
4638
  serverUrl = "https://<YOUR_CONVEX_URL>";
@@ -4431,10 +4675,9 @@ async function setupEnvironmentVariables(config) {
4431
4675
  return;
4432
4676
  }
4433
4677
  const serverDir = path.join(projectDir, "apps/server");
4434
- if (!await fs.pathExists(serverDir)) return;
4435
- const envPath = path.join(serverDir, ".env");
4436
4678
  let corsOrigin = "http://localhost:3001";
4437
- if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
4679
+ if (backend === "self") corsOrigin = "http://localhost:3001";
4680
+ else if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
4438
4681
  let databaseUrl = null;
4439
4682
  if (database !== "none" && dbSetup === "none") switch (database) {
4440
4683
  case "postgres":
@@ -4448,15 +4691,13 @@ async function setupEnvironmentVariables(config) {
4448
4691
  break;
4449
4692
  case "sqlite":
4450
4693
  if (config.runtime === "workers") databaseUrl = "http://127.0.0.1:8080";
4451
- else databaseUrl = "file:./local.db";
4694
+ else {
4695
+ const dbAppDir = backend === "self" ? "apps/web" : "apps/server";
4696
+ databaseUrl = `file:${path.join(config.projectDir, dbAppDir, "local.db")}`;
4697
+ }
4452
4698
  break;
4453
4699
  }
4454
4700
  const serverVars = [
4455
- {
4456
- key: "CORS_ORIGIN",
4457
- value: corsOrigin,
4458
- condition: true
4459
- },
4460
4701
  {
4461
4702
  key: "BETTER_AUTH_SECRET",
4462
4703
  value: generateAuthSecret(),
@@ -4464,19 +4705,9 @@ async function setupEnvironmentVariables(config) {
4464
4705
  },
4465
4706
  {
4466
4707
  key: "BETTER_AUTH_URL",
4467
- value: "http://localhost:3000",
4708
+ value: backend === "self" ? "http://localhost:3001" : "http://localhost:3000",
4468
4709
  condition: !!auth
4469
4710
  },
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
4711
  {
4481
4712
  key: "POLAR_ACCESS_TOKEN",
4482
4713
  value: "",
@@ -4486,9 +4717,27 @@ async function setupEnvironmentVariables(config) {
4486
4717
  key: "POLAR_SUCCESS_URL",
4487
4718
  value: `${corsOrigin}/success?checkout_id={CHECKOUT_ID}`,
4488
4719
  condition: config.payments === "polar"
4720
+ },
4721
+ {
4722
+ key: "CORS_ORIGIN",
4723
+ value: corsOrigin,
4724
+ condition: true
4725
+ },
4726
+ {
4727
+ key: "GOOGLE_GENERATIVE_AI_API_KEY",
4728
+ value: "",
4729
+ condition: examples?.includes("ai") || false
4730
+ },
4731
+ {
4732
+ key: "DATABASE_URL",
4733
+ value: databaseUrl,
4734
+ condition: database !== "none" && dbSetup === "none"
4489
4735
  }
4490
4736
  ];
4491
- await addEnvVariablesToFile(envPath, serverVars);
4737
+ if (backend === "self") {
4738
+ const webDir = path.join(projectDir, "apps/web");
4739
+ if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverVars);
4740
+ } else if (await fs.pathExists(serverDir)) await addEnvVariablesToFile(path.join(serverDir, ".env"), serverVars);
4492
4741
  const isUnifiedAlchemy = webDeploy === "alchemy" && serverDeploy === "alchemy";
4493
4742
  const isIndividualAlchemy = webDeploy === "alchemy" || serverDeploy === "alchemy";
4494
4743
  if (isUnifiedAlchemy) {
@@ -4508,12 +4757,15 @@ async function setupEnvironmentVariables(config) {
4508
4757
  }]);
4509
4758
  }
4510
4759
  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"), [{
4760
+ const serverAlchemyVars = [{
4513
4761
  key: "ALCHEMY_PASSWORD",
4514
4762
  value: "please-change-this",
4515
4763
  condition: true
4516
- }]);
4764
+ }];
4765
+ if (backend === "self") {
4766
+ const webDir = path.join(projectDir, "apps/web");
4767
+ if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverAlchemyVars);
4768
+ } else await addEnvVariablesToFile(path.join(serverDir, ".env"), serverAlchemyVars);
4517
4769
  }
4518
4770
  }
4519
4771
  }
@@ -4521,9 +4773,10 @@ async function setupEnvironmentVariables(config) {
4521
4773
  //#endregion
4522
4774
  //#region src/helpers/database-providers/d1-setup.ts
4523
4775
  async function setupCloudflareD1(config) {
4524
- const { projectDir, serverDeploy, orm } = config;
4776
+ const { projectDir, serverDeploy, orm, backend } = config;
4525
4777
  if (serverDeploy === "wrangler") {
4526
- const envPath = path.join(projectDir, "apps/server", ".env");
4778
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
4779
+ const envPath = path.join(projectDir, targetApp, ".env");
4527
4780
  const variables = [
4528
4781
  {
4529
4782
  key: "CLOUDFLARE_ACCOUNT_ID",
@@ -4546,16 +4799,17 @@ async function setupCloudflareD1(config) {
4546
4799
  } catch (_err) {}
4547
4800
  }
4548
4801
  if ((serverDeploy === "wrangler" || serverDeploy === "alchemy") && orm === "prisma") {
4549
- const envPath = path.join(projectDir, "apps/server", ".env");
4802
+ const targetApp2 = backend === "self" ? "apps/web" : "apps/server";
4803
+ const envPath = path.join(projectDir, targetApp2, ".env");
4550
4804
  const variables = [{
4551
4805
  key: "DATABASE_URL",
4552
- value: "file:./local.db",
4806
+ value: `file:${path.join(projectDir, "apps/server", "local.db")}`,
4553
4807
  condition: true
4554
4808
  }];
4555
4809
  try {
4556
4810
  await addEnvVariablesToFile(envPath, variables);
4557
4811
  } catch (_err) {}
4558
- const serverDir = path.join(projectDir, "apps/server");
4812
+ const serverDir = path.join(projectDir, backend === "self" ? "apps/web" : "apps/server");
4559
4813
  await addPackageDependency({
4560
4814
  dependencies: ["@prisma/adapter-d1"],
4561
4815
  projectDir: serverDir
@@ -4648,9 +4902,10 @@ async function initMongoDBAtlas(serverDir) {
4648
4902
  return null;
4649
4903
  }
4650
4904
  }
4651
- async function writeEnvFile$3(projectDir, config) {
4905
+ async function writeEnvFile$3(projectDir, backend, config) {
4652
4906
  try {
4653
- const envPath = path.join(projectDir, "apps/server", ".env");
4907
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
4908
+ const envPath = path.join(projectDir, targetApp, ".env");
4654
4909
  const variables = [{
4655
4910
  key: "DATABASE_URL",
4656
4911
  value: config?.connectionString ?? "mongodb://localhost:27017/mydb",
@@ -4679,14 +4934,14 @@ ${pc.green("MongoDB Atlas Manual Setup Instructions:")}
4679
4934
  `);
4680
4935
  }
4681
4936
  async function setupMongoDBAtlas(config, cliInput) {
4682
- const { projectDir } = config;
4937
+ const { projectDir, backend } = config;
4683
4938
  const manualDb = cliInput?.manualDb ?? false;
4684
- const serverDir = path.join(projectDir, "apps/server");
4939
+ const serverDir = path.join(projectDir, "packages/db");
4685
4940
  try {
4686
4941
  await fs.ensureDir(serverDir);
4687
4942
  if (manualDb) {
4688
4943
  log.info("MongoDB Atlas manual setup selected");
4689
- await writeEnvFile$3(projectDir);
4944
+ await writeEnvFile$3(projectDir, backend);
4690
4945
  displayManualSetupInstructions$3();
4691
4946
  return;
4692
4947
  }
@@ -4706,23 +4961,23 @@ async function setupMongoDBAtlas(config, cliInput) {
4706
4961
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
4707
4962
  if (mode === "manual") {
4708
4963
  log.info("MongoDB Atlas manual setup selected");
4709
- await writeEnvFile$3(projectDir);
4964
+ await writeEnvFile$3(projectDir, backend);
4710
4965
  displayManualSetupInstructions$3();
4711
4966
  return;
4712
4967
  }
4713
- const atlasConfig = await initMongoDBAtlas(serverDir);
4714
- if (atlasConfig) {
4715
- await writeEnvFile$3(projectDir, atlasConfig);
4968
+ const config$1 = await initMongoDBAtlas(serverDir);
4969
+ if (config$1) {
4970
+ await writeEnvFile$3(projectDir, backend, config$1);
4716
4971
  log.success(pc.green("MongoDB Atlas setup complete! Connection saved to .env file."));
4717
4972
  } else {
4718
4973
  log.warn(pc.yellow("Falling back to local MongoDB configuration"));
4719
- await writeEnvFile$3(projectDir);
4974
+ await writeEnvFile$3(projectDir, backend);
4720
4975
  displayManualSetupInstructions$3();
4721
4976
  }
4722
4977
  } catch (error) {
4723
4978
  consola.error(pc.red(`Error during MongoDB Atlas setup: ${error instanceof Error ? error.message : String(error)}`));
4724
4979
  try {
4725
- await writeEnvFile$3(projectDir);
4980
+ await writeEnvFile$3(projectDir, backend);
4726
4981
  displayManualSetupInstructions$3();
4727
4982
  } catch {}
4728
4983
  }
@@ -4779,7 +5034,7 @@ async function executeNeonCommand(packageManager, commandArgsString, spinnerText
4779
5034
  }
4780
5035
  async function createNeonProject(projectName, regionId, packageManager) {
4781
5036
  try {
4782
- const commandArgsString = `neonctl projects create --name ${projectName} --region-id ${regionId} --output json`;
5037
+ const commandArgsString = `neonctl@latest projects create --name ${projectName} --region-id ${regionId} --output json`;
4783
5038
  const { stdout } = await executeNeonCommand(packageManager, commandArgsString, `Creating Neon project "${projectName}"...`);
4784
5039
  const response = JSON.parse(stdout);
4785
5040
  if (response.project && response.connection_uris && response.connection_uris.length > 0) {
@@ -4799,8 +5054,9 @@ async function createNeonProject(projectName, regionId, packageManager) {
4799
5054
  consola$1.error(pc.red("Failed to create Neon project"));
4800
5055
  }
4801
5056
  }
4802
- async function writeEnvFile$2(projectDir, config) {
4803
- const envPath = path.join(projectDir, "apps/server", ".env");
5057
+ async function writeEnvFile$2(projectDir, backend, config) {
5058
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5059
+ const envPath = path.join(projectDir, targetApp, ".env");
4804
5060
  const variables = [{
4805
5061
  key: "DATABASE_URL",
4806
5062
  value: config?.connectionString ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
@@ -4809,16 +5065,17 @@ async function writeEnvFile$2(projectDir, config) {
4809
5065
  await addEnvVariablesToFile(envPath, variables);
4810
5066
  return true;
4811
5067
  }
4812
- async function setupWithNeonDb(projectDir, packageManager) {
5068
+ async function setupWithNeonDb(projectDir, packageManager, backend) {
4813
5069
  try {
4814
5070
  const s = spinner();
4815
5071
  s.start("Creating Neon database using neondb...");
4816
- const serverDir = path.join(projectDir, "apps/server");
4817
- await fs.ensureDir(serverDir);
4818
- const packageCmd = getPackageExecutionCommand(packageManager, "neondb --yes");
5072
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5073
+ const targetDir = path.join(projectDir, targetApp);
5074
+ await fs.ensureDir(targetDir);
5075
+ const packageCmd = getPackageExecutionCommand(packageManager, "neondb@latest --yes");
4819
5076
  await execa(packageCmd, {
4820
5077
  shell: true,
4821
- cwd: serverDir
5078
+ cwd: targetDir
4822
5079
  });
4823
5080
  s.stop(pc.green("Neon database created successfully!"));
4824
5081
  return true;
@@ -4827,23 +5084,23 @@ async function setupWithNeonDb(projectDir, packageManager) {
4827
5084
  throw error;
4828
5085
  }
4829
5086
  }
4830
- function displayManualSetupInstructions$2() {
5087
+ function displayManualSetupInstructions$2(target) {
4831
5088
  log.info(`Manual Neon PostgreSQL Setup Instructions:
4832
5089
 
4833
5090
  1. Visit https://neon.tech and create an account
4834
5091
  2. Create a new project from the dashboard
4835
5092
  3. Get your connection string
4836
- 4. Add the database URL to the .env file in apps/server/.env
5093
+ 4. Add the database URL to the .env file in ${target}/.env
4837
5094
 
4838
5095
  DATABASE_URL="your_connection_string"`);
4839
5096
  }
4840
5097
  async function setupNeonPostgres(config, cliInput) {
4841
- const { packageManager, projectDir } = config;
5098
+ const { packageManager, projectDir, backend } = config;
4842
5099
  const manualDb = cliInput?.manualDb ?? false;
4843
5100
  try {
4844
5101
  if (manualDb) {
4845
- await writeEnvFile$2(projectDir);
4846
- displayManualSetupInstructions$2();
5102
+ await writeEnvFile$2(projectDir, backend);
5103
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
4847
5104
  return;
4848
5105
  }
4849
5106
  const mode = await select({
@@ -4861,8 +5118,8 @@ async function setupNeonPostgres(config, cliInput) {
4861
5118
  });
4862
5119
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
4863
5120
  if (mode === "manual") {
4864
- await writeEnvFile$2(projectDir);
4865
- displayManualSetupInstructions$2();
5121
+ await writeEnvFile$2(projectDir, backend);
5122
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
4866
5123
  return;
4867
5124
  }
4868
5125
  const setupMethod = await select({
@@ -4879,7 +5136,7 @@ async function setupNeonPostgres(config, cliInput) {
4879
5136
  initialValue: "neondb"
4880
5137
  });
4881
5138
  if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
4882
- if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager);
5139
+ if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager, backend);
4883
5140
  else {
4884
5141
  const suggestedProjectName = path.basename(projectDir);
4885
5142
  const projectName = await text({
@@ -4897,22 +5154,22 @@ async function setupNeonPostgres(config, cliInput) {
4897
5154
  if (!neonConfig) throw new Error("Failed to create project - couldn't get connection information");
4898
5155
  const finalSpinner = spinner();
4899
5156
  finalSpinner.start("Configuring database connection");
4900
- await fs.ensureDir(path.join(projectDir, "apps/server"));
4901
- await writeEnvFile$2(projectDir, neonConfig);
5157
+ await writeEnvFile$2(projectDir, backend, neonConfig);
4902
5158
  finalSpinner.stop("Neon database configured!");
4903
5159
  }
4904
5160
  } catch (error) {
4905
5161
  if (error instanceof Error) consola$1.error(pc.red(error.message));
4906
- await writeEnvFile$2(projectDir);
4907
- displayManualSetupInstructions$2();
5162
+ await writeEnvFile$2(projectDir, backend);
5163
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
4908
5164
  }
4909
5165
  }
4910
5166
 
4911
5167
  //#endregion
4912
5168
  //#region src/helpers/database-providers/planetscale-setup.ts
4913
5169
  async function setupPlanetScale(config) {
4914
- const { projectDir, database, orm } = config;
4915
- const envPath = path.join(projectDir, "apps/server", ".env");
5170
+ const { projectDir, database, orm, backend } = config;
5171
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5172
+ const envPath = path.join(projectDir, targetApp, ".env");
4916
5173
  if (database === "mysql" && orm === "drizzle") {
4917
5174
  const variables = [
4918
5175
  {
@@ -4936,7 +5193,7 @@ async function setupPlanetScale(config) {
4936
5193
  condition: true
4937
5194
  }
4938
5195
  ];
4939
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5196
+ await fs.ensureDir(path.join(projectDir, targetApp));
4940
5197
  await addEnvVariablesToFile(envPath, variables);
4941
5198
  }
4942
5199
  if (database === "postgres" && orm === "prisma") {
@@ -4945,7 +5202,7 @@ async function setupPlanetScale(config) {
4945
5202
  value: "postgresql://username:password@host/database?sslaccept=strict",
4946
5203
  condition: true
4947
5204
  }];
4948
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5205
+ await fs.ensureDir(path.join(projectDir, targetApp));
4949
5206
  await addEnvVariablesToFile(envPath, variables);
4950
5207
  }
4951
5208
  if (database === "postgres" && orm === "drizzle") {
@@ -4954,7 +5211,7 @@ async function setupPlanetScale(config) {
4954
5211
  value: "postgresql://username:password@host/database?sslmode=verify-full",
4955
5212
  condition: true
4956
5213
  }];
4957
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5214
+ await fs.ensureDir(path.join(projectDir, targetApp));
4958
5215
  await addEnvVariablesToFile(envPath, variables);
4959
5216
  }
4960
5217
  if (database === "mysql" && orm === "prisma") {
@@ -4963,7 +5220,7 @@ async function setupPlanetScale(config) {
4963
5220
  value: "mysql://username:password@host/database?sslaccept=strict",
4964
5221
  condition: true
4965
5222
  }];
4966
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5223
+ await fs.ensureDir(path.join(projectDir, targetApp));
4967
5224
  await addEnvVariablesToFile(envPath, variables);
4968
5225
  }
4969
5226
  }
@@ -4998,7 +5255,7 @@ const AVAILABLE_REGIONS = [
4998
5255
  ];
4999
5256
  async function setupWithCreateDb(serverDir, packageManager, orm) {
5000
5257
  try {
5001
- log.info("Starting Prisma Postgres setup with create-db. Please follow the instructions below:");
5258
+ log.info("Starting Prisma Postgres setup with create-db.");
5002
5259
  const selectedRegion = await select({
5003
5260
  message: "Select your preferred region:",
5004
5261
  options: AVAILABLE_REGIONS,
@@ -5033,7 +5290,7 @@ async function initPrismaDatabase(serverDir, packageManager) {
5033
5290
  try {
5034
5291
  const prismaDir = path.join(serverDir, "prisma");
5035
5292
  await fs.ensureDir(prismaDir);
5036
- log.info("Starting Prisma PostgreSQL setup. Please follow the instructions below:");
5293
+ log.info("Starting Prisma PostgreSQL setup.");
5037
5294
  const prismaInitCommand = getPackageExecutionCommand(packageManager, "prisma init --db");
5038
5295
  await execa(prismaInitCommand, {
5039
5296
  cwd: serverDir,
@@ -5055,9 +5312,10 @@ async function initPrismaDatabase(serverDir, packageManager) {
5055
5312
  return null;
5056
5313
  }
5057
5314
  }
5058
- async function writeEnvFile$1(projectDir, config) {
5315
+ async function writeEnvFile$1(projectDir, backend, config) {
5059
5316
  try {
5060
- const envPath = path.join(projectDir, "apps/server", ".env");
5317
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5318
+ const envPath = path.join(projectDir, targetApp, ".env");
5061
5319
  const variables = [{
5062
5320
  key: "DATABASE_URL",
5063
5321
  value: config?.databaseUrl ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
@@ -5073,31 +5331,32 @@ async function writeEnvFile$1(projectDir, config) {
5073
5331
  consola$1.error("Failed to update environment configuration");
5074
5332
  }
5075
5333
  }
5076
- async function addDotenvImportToPrismaConfig(projectDir) {
5334
+ async function addDotenvImportToPrismaConfig(projectDir, backend) {
5077
5335
  try {
5078
- const prismaConfigPath = path.join(projectDir, "apps/server/prisma.config.ts");
5336
+ const prismaConfigPath = path.join(projectDir, "packages/db/prisma.config.ts");
5079
5337
  let content = await fs.readFile(prismaConfigPath, "utf8");
5080
- content = `import "dotenv/config";\n${content}`;
5338
+ content = `import dotenv from "dotenv";\ndotenv.config({ path: "${backend === "self" ? "../../apps/web/.env" : "../../apps/server/.env"}" });\n${content}`;
5081
5339
  await fs.writeFile(prismaConfigPath, content);
5082
5340
  } catch (_error) {
5083
5341
  consola$1.error("Failed to update prisma.config.ts");
5084
5342
  }
5085
5343
  }
5086
- function displayManualSetupInstructions$1() {
5344
+ function displayManualSetupInstructions$1(target) {
5087
5345
  log.info(`Manual Prisma PostgreSQL Setup Instructions:
5088
5346
 
5089
5347
  1. Visit https://console.prisma.io and create an account
5090
5348
  2. Create a new PostgreSQL database from the dashboard
5091
5349
  3. Get your database URL
5092
- 4. Add the database URL to the .env file in apps/server/.env
5350
+ 4. Add the database URL to the .env file in ${target}/.env
5093
5351
 
5094
5352
  DATABASE_URL="your_database_url"`);
5095
5353
  }
5096
- async function addPrismaAccelerateExtension(serverDir) {
5354
+ async function addPrismaAccelerateExtension(projectDir) {
5097
5355
  try {
5356
+ const dbPackageDir = path.join(projectDir, "packages/db");
5098
5357
  await addPackageDependency({
5099
5358
  dependencies: ["@prisma/extension-accelerate"],
5100
- projectDir: serverDir
5359
+ projectDir: dbPackageDir
5101
5360
  });
5102
5361
  return true;
5103
5362
  } catch (_error) {
@@ -5106,14 +5365,14 @@ async function addPrismaAccelerateExtension(serverDir) {
5106
5365
  }
5107
5366
  }
5108
5367
  async function setupPrismaPostgres(config, cliInput) {
5109
- const { packageManager, projectDir, orm } = config;
5368
+ const { packageManager, projectDir, orm, backend } = config;
5110
5369
  const manualDb = cliInput?.manualDb ?? false;
5111
- const serverDir = path.join(projectDir, "apps/server");
5370
+ const dbDir = path.join(projectDir, "packages/db");
5112
5371
  try {
5113
- await fs.ensureDir(serverDir);
5372
+ await fs.ensureDir(dbDir);
5114
5373
  if (manualDb) {
5115
- await writeEnvFile$1(projectDir);
5116
- displayManualSetupInstructions$1();
5374
+ await writeEnvFile$1(projectDir, backend);
5375
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5117
5376
  return;
5118
5377
  }
5119
5378
  const mode = await select({
@@ -5131,8 +5390,8 @@ async function setupPrismaPostgres(config, cliInput) {
5131
5390
  });
5132
5391
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5133
5392
  if (mode === "manual") {
5134
- await writeEnvFile$1(projectDir);
5135
- displayManualSetupInstructions$1();
5393
+ await writeEnvFile$1(projectDir, backend);
5394
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5136
5395
  return;
5137
5396
  }
5138
5397
  const setupOptions = [{
@@ -5152,26 +5411,26 @@ async function setupPrismaPostgres(config, cliInput) {
5152
5411
  });
5153
5412
  if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
5154
5413
  let prismaConfig = null;
5155
- if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(serverDir, packageManager, orm);
5156
- else prismaConfig = await initPrismaDatabase(serverDir, packageManager);
5414
+ if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(dbDir, packageManager, orm);
5415
+ else prismaConfig = await initPrismaDatabase(dbDir, packageManager);
5157
5416
  if (prismaConfig) {
5158
- await writeEnvFile$1(projectDir, prismaConfig);
5417
+ await writeEnvFile$1(projectDir, backend, prismaConfig);
5159
5418
  if (orm === "prisma") {
5160
- await addDotenvImportToPrismaConfig(projectDir);
5161
- await addPrismaAccelerateExtension(serverDir);
5419
+ await addDotenvImportToPrismaConfig(projectDir, backend);
5420
+ await addPrismaAccelerateExtension(projectDir);
5162
5421
  }
5163
5422
  const connectionType = orm === "drizzle" ? "direct connection" : "Prisma Accelerate";
5164
5423
  log.success(pc.green(`Prisma Postgres database configured successfully with ${connectionType}!`));
5165
5424
  if (prismaConfig.claimUrl) log.info(pc.blue(`Claim URL saved to .env: ${prismaConfig.claimUrl}`));
5166
5425
  } else {
5167
- await writeEnvFile$1(projectDir);
5168
- displayManualSetupInstructions$1();
5426
+ await writeEnvFile$1(projectDir, backend);
5427
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5169
5428
  }
5170
5429
  } catch (error) {
5171
5430
  consola$1.error(pc.red(`Error during Prisma Postgres setup: ${error instanceof Error ? error.message : String(error)}`));
5172
5431
  try {
5173
- await writeEnvFile$1(projectDir);
5174
- displayManualSetupInstructions$1();
5432
+ await writeEnvFile$1(projectDir, backend);
5433
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5175
5434
  } catch {}
5176
5435
  log.info("Setup completed with manual configuration required.");
5177
5436
  }
@@ -5179,9 +5438,10 @@ async function setupPrismaPostgres(config, cliInput) {
5179
5438
 
5180
5439
  //#endregion
5181
5440
  //#region src/helpers/database-providers/supabase-setup.ts
5182
- async function writeSupabaseEnvFile(projectDir, databaseUrl) {
5441
+ async function writeSupabaseEnvFile(projectDir, backend, databaseUrl) {
5183
5442
  try {
5184
- const envPath = path.join(projectDir, "apps/server", ".env");
5443
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5444
+ const envPath = path.join(projectDir, targetApp, ".env");
5185
5445
  const dbUrlToUse = databaseUrl || "postgresql://postgres:postgres@127.0.0.1:54322/postgres";
5186
5446
  await addEnvVariablesToFile(envPath, [{
5187
5447
  key: "DATABASE_URL",
@@ -5258,23 +5518,23 @@ function displayManualSupabaseInstructions(output) {
5258
5518
  log.info(`"Manual Supabase Setup Instructions:"
5259
5519
  1. Ensure Docker is installed and running.
5260
5520
  2. Install the Supabase CLI (e.g., \`npm install -g supabase\`).
5261
- 3. Run \`supabase init\` in your project's \`apps/server\` directory.
5262
- 4. Run \`supabase start\` in your project's \`apps/server\` directory.
5521
+ 3. Run \`supabase init\` in your project's \`packages/db\` directory.
5522
+ 4. Run \`supabase start\` in your project's \`packages/db\` directory.
5263
5523
  5. Copy the 'DB URL' from the output.${output ? `
5264
5524
  ${pc.bold("Relevant output from `supabase start`:")}
5265
5525
  ${pc.dim(output)}` : ""}
5266
- 6. Add the DB URL to the .env file in \`apps/server/.env\` as \`DATABASE_URL\`:
5526
+ 6. Add the DB URL to the .env file in \`packages/db/.env\` as \`DATABASE_URL\`:
5267
5527
  ${pc.gray("DATABASE_URL=\"your_supabase_db_url\"")}`);
5268
5528
  }
5269
5529
  async function setupSupabase(config, cliInput) {
5270
- const { projectDir, packageManager } = config;
5530
+ const { projectDir, packageManager, backend } = config;
5271
5531
  const manualDb = cliInput?.manualDb ?? false;
5272
- const serverDir = path.join(projectDir, "apps", "server");
5532
+ const serverDir = path.join(projectDir, "packages", "db");
5273
5533
  try {
5274
5534
  await fs.ensureDir(serverDir);
5275
5535
  if (manualDb) {
5276
5536
  displayManualSupabaseInstructions();
5277
- await writeSupabaseEnvFile(projectDir, "");
5537
+ await writeSupabaseEnvFile(projectDir, backend, "");
5278
5538
  return;
5279
5539
  }
5280
5540
  const mode = await select({
@@ -5293,7 +5553,7 @@ async function setupSupabase(config, cliInput) {
5293
5553
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5294
5554
  if (mode === "manual") {
5295
5555
  displayManualSupabaseInstructions();
5296
- await writeSupabaseEnvFile(projectDir, "");
5556
+ await writeSupabaseEnvFile(projectDir, backend, "");
5297
5557
  return;
5298
5558
  }
5299
5559
  if (!await initializeSupabase(serverDir, packageManager)) {
@@ -5306,7 +5566,7 @@ async function setupSupabase(config, cliInput) {
5306
5566
  return;
5307
5567
  }
5308
5568
  const dbUrl = extractDbUrl(supabaseOutput);
5309
- if (dbUrl) if (await writeSupabaseEnvFile(projectDir, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
5569
+ if (dbUrl) if (await writeSupabaseEnvFile(projectDir, backend, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
5310
5570
  else {
5311
5571
  log.error(pc.red("Supabase setup completed, but failed to update .env automatically."));
5312
5572
  displayManualSupabaseInstructions(supabaseOutput);
@@ -5434,8 +5694,9 @@ async function createTursoDatabase(dbName, groupName) {
5434
5694
  s.stop(pc.red("Failed to retrieve database connection details"));
5435
5695
  }
5436
5696
  }
5437
- async function writeEnvFile(projectDir, config) {
5438
- const envPath = path.join(projectDir, "apps/server", ".env");
5697
+ async function writeEnvFile(projectDir, backend, config) {
5698
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5699
+ const envPath = path.join(projectDir, targetApp, ".env");
5439
5700
  const variables = [{
5440
5701
  key: "DATABASE_URL",
5441
5702
  value: config?.dbUrl ?? "",
@@ -5459,12 +5720,12 @@ DATABASE_URL=your_database_url
5459
5720
  DATABASE_AUTH_TOKEN=your_auth_token`);
5460
5721
  }
5461
5722
  async function setupTurso(config, cliInput) {
5462
- const { orm, projectDir } = config;
5723
+ const { orm, projectDir, backend } = config;
5463
5724
  const manualDb = cliInput?.manualDb ?? false;
5464
5725
  const setupSpinner = spinner();
5465
5726
  try {
5466
5727
  if (manualDb) {
5467
- await writeEnvFile(projectDir);
5728
+ await writeEnvFile(projectDir, backend);
5468
5729
  displayManualSetupInstructions();
5469
5730
  return;
5470
5731
  }
@@ -5483,7 +5744,7 @@ async function setupTurso(config, cliInput) {
5483
5744
  });
5484
5745
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5485
5746
  if (mode === "manual") {
5486
- await writeEnvFile(projectDir);
5747
+ await writeEnvFile(projectDir, backend);
5487
5748
  displayManualSetupInstructions();
5488
5749
  return;
5489
5750
  }
@@ -5493,7 +5754,7 @@ async function setupTurso(config, cliInput) {
5493
5754
  if (platform === "win32") {
5494
5755
  if (setupSpinner) setupSpinner.stop(pc.yellow("Turso setup not supported on Windows"));
5495
5756
  log.warn(pc.yellow("Automatic Turso setup is not supported on Windows."));
5496
- await writeEnvFile(projectDir);
5757
+ await writeEnvFile(projectDir, backend);
5497
5758
  displayManualSetupInstructions();
5498
5759
  return;
5499
5760
  }
@@ -5505,7 +5766,7 @@ async function setupTurso(config, cliInput) {
5505
5766
  });
5506
5767
  if (isCancel(shouldInstall)) return exitCancelled("Operation cancelled");
5507
5768
  if (!shouldInstall) {
5508
- await writeEnvFile(projectDir);
5769
+ await writeEnvFile(projectDir, backend);
5509
5770
  displayManualSetupInstructions();
5510
5771
  return;
5511
5772
  }
@@ -5527,7 +5788,7 @@ async function setupTurso(config, cliInput) {
5527
5788
  dbName = dbNameResponse;
5528
5789
  try {
5529
5790
  const config$1 = await createTursoDatabase(dbName, selectedGroup);
5530
- await writeEnvFile(projectDir, config$1);
5791
+ await writeEnvFile(projectDir, backend, config$1);
5531
5792
  success = true;
5532
5793
  } catch (error) {
5533
5794
  if (error instanceof Error && error.message === "DATABASE_EXISTS") {
@@ -5540,7 +5801,7 @@ async function setupTurso(config, cliInput) {
5540
5801
  } catch (error) {
5541
5802
  if (setupSpinner) setupSpinner.stop(pc.red("Turso CLI availability check failed"));
5542
5803
  consola.error(pc.red(`Error during Turso setup: ${error instanceof Error ? error.message : String(error)}`));
5543
- await writeEnvFile(projectDir);
5804
+ await writeEnvFile(projectDir, backend);
5544
5805
  displayManualSetupInstructions();
5545
5806
  log.success("Setup completed with manual configuration required.");
5546
5807
  }
@@ -5552,40 +5813,46 @@ async function setupDatabase(config, cliInput) {
5552
5813
  const { database, orm, dbSetup, backend, projectDir } = config;
5553
5814
  if (backend === "convex" || database === "none") {
5554
5815
  if (backend !== "convex") {
5555
- const serverDir$1 = path.join(projectDir, "apps/server");
5556
- const serverDbDir = path.join(serverDir$1, "src/db");
5816
+ const serverDir = path.join(projectDir, "apps/server");
5817
+ const serverDbDir = path.join(serverDir, "src/db");
5557
5818
  if (await fs.pathExists(serverDbDir)) await fs.remove(serverDbDir);
5558
5819
  }
5559
5820
  return;
5560
5821
  }
5561
5822
  const s = spinner();
5562
- const serverDir = path.join(projectDir, "apps/server");
5563
- if (!await fs.pathExists(serverDir)) return;
5823
+ const dbPackageDir = path.join(projectDir, "packages/db");
5824
+ if (!await fs.pathExists(dbPackageDir)) return;
5564
5825
  try {
5565
- if (orm === "prisma") if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
5566
- dependencies: [
5567
- "@prisma/client",
5568
- "@prisma/adapter-planetscale",
5569
- "@planetscale/database"
5570
- ],
5571
- devDependencies: ["prisma"],
5572
- projectDir: serverDir
5573
- });
5574
- else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
5575
- dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
5576
- devDependencies: ["prisma"],
5577
- projectDir: serverDir
5578
- });
5579
- else await addPackageDependency({
5580
- dependencies: ["@prisma/client"],
5581
- devDependencies: ["prisma"],
5582
- projectDir: serverDir
5583
- });
5584
- else if (orm === "drizzle") {
5826
+ if (orm === "prisma") {
5827
+ if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
5828
+ dependencies: [
5829
+ "@prisma/client",
5830
+ "@prisma/adapter-planetscale",
5831
+ "@planetscale/database"
5832
+ ],
5833
+ devDependencies: ["prisma"],
5834
+ projectDir: dbPackageDir
5835
+ });
5836
+ else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
5837
+ dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
5838
+ devDependencies: ["prisma"],
5839
+ projectDir: dbPackageDir
5840
+ });
5841
+ else await addPackageDependency({
5842
+ dependencies: ["@prisma/client"],
5843
+ devDependencies: ["prisma"],
5844
+ projectDir: dbPackageDir
5845
+ });
5846
+ const webDir = path.join(projectDir, "apps/web");
5847
+ if (await fs.pathExists(webDir)) await addPackageDependency({
5848
+ dependencies: ["@prisma/client"],
5849
+ projectDir: webDir
5850
+ });
5851
+ } else if (orm === "drizzle") {
5585
5852
  if (database === "sqlite") await addPackageDependency({
5586
5853
  dependencies: ["drizzle-orm", "@libsql/client"],
5587
5854
  devDependencies: ["drizzle-kit"],
5588
- projectDir: serverDir
5855
+ projectDir: dbPackageDir
5589
5856
  });
5590
5857
  else if (database === "postgres") if (dbSetup === "neon") await addPackageDependency({
5591
5858
  dependencies: [
@@ -5594,32 +5861,32 @@ async function setupDatabase(config, cliInput) {
5594
5861
  "ws"
5595
5862
  ],
5596
5863
  devDependencies: ["drizzle-kit", "@types/ws"],
5597
- projectDir: serverDir
5864
+ projectDir: dbPackageDir
5598
5865
  });
5599
5866
  else if (dbSetup === "planetscale") await addPackageDependency({
5600
5867
  dependencies: ["drizzle-orm", "pg"],
5601
5868
  devDependencies: ["drizzle-kit", "@types/pg"],
5602
- projectDir: serverDir
5869
+ projectDir: dbPackageDir
5603
5870
  });
5604
5871
  else await addPackageDependency({
5605
5872
  dependencies: ["drizzle-orm", "pg"],
5606
5873
  devDependencies: ["drizzle-kit", "@types/pg"],
5607
- projectDir: serverDir
5874
+ projectDir: dbPackageDir
5608
5875
  });
5609
5876
  else if (database === "mysql") if (dbSetup === "planetscale") await addPackageDependency({
5610
5877
  dependencies: ["drizzle-orm", "@planetscale/database"],
5611
5878
  devDependencies: ["drizzle-kit"],
5612
- projectDir: serverDir
5879
+ projectDir: dbPackageDir
5613
5880
  });
5614
5881
  else await addPackageDependency({
5615
5882
  dependencies: ["drizzle-orm", "mysql2"],
5616
5883
  devDependencies: ["drizzle-kit"],
5617
- projectDir: serverDir
5884
+ projectDir: dbPackageDir
5618
5885
  });
5619
5886
  } else if (orm === "mongoose") await addPackageDependency({
5620
5887
  dependencies: ["mongoose"],
5621
5888
  devDependencies: [],
5622
- projectDir: serverDir
5889
+ projectDir: dbPackageDir
5623
5890
  });
5624
5891
  if (dbSetup === "docker") await setupDockerCompose(config);
5625
5892
  else if (database === "sqlite" && dbSetup === "turso") await setupTurso(config, cliInput);
@@ -5642,7 +5909,7 @@ async function setupDatabase(config, cliInput) {
5642
5909
  //#region src/helpers/core/runtime-setup.ts
5643
5910
  async function setupRuntime(config) {
5644
5911
  const { runtime, backend, projectDir } = config;
5645
- if (backend === "convex" || backend === "next" || runtime === "none") return;
5912
+ if (backend === "convex" || backend === "self" || runtime === "none") return;
5646
5913
  const serverDir = path.join(projectDir, "apps/server");
5647
5914
  if (!await fs.pathExists(serverDir)) return;
5648
5915
  if (runtime === "bun") await setupBunRuntime(serverDir, backend);
@@ -5732,7 +5999,7 @@ This project uses Convex as a backend. You'll need to set up Convex before runni
5732
5999
  ${packageManagerRunCmd} dev:setup
5733
6000
  \`\`\`
5734
6001
 
5735
- Follow the prompts to create a new Convex project and connect it to your application.${auth === "clerk" ? " See [Convex + Clerk guide](https://docs.convex.dev/auth/clerk) for auth setup." : ""}` : generateDatabaseSetup(database, auth, packageManagerRunCmd, orm, options.dbSetup, options.serverDeploy)}
6002
+ Follow the prompts to create a new Convex project and connect it to your application.${auth === "clerk" ? " See [Convex + Clerk guide](https://docs.convex.dev/auth/clerk) for auth setup." : ""}` : generateDatabaseSetup(database, auth, packageManagerRunCmd, orm, options.dbSetup, options.serverDeploy, options.backend)}
5736
6003
 
5737
6004
  Then, run the development server:
5738
6005
 
@@ -5785,6 +6052,7 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
5785
6052
  const instructions = [];
5786
6053
  const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
5787
6054
  const isBackendNone = backend === "none";
6055
+ const isBackendSelf = backend === "self";
5788
6056
  if (!hasFrontendNone) {
5789
6057
  const hasTanstackRouter = frontend.includes("tanstack-router");
5790
6058
  const hasReactRouter = frontend.includes("react-router");
@@ -5793,17 +6061,19 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
5793
6061
  const hasSvelte = frontend.includes("svelte");
5794
6062
  const hasNuxt = frontend.includes("nuxt");
5795
6063
  const hasSolid = frontend.includes("solid");
5796
- if (hasTanstackRouter || hasReactRouter || hasNext || hasTanstackStart || hasSvelte || hasNuxt || hasSolid) instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see the web application.`);
6064
+ if (hasTanstackRouter || hasReactRouter || hasNext || hasTanstackStart || hasSvelte || hasNuxt || hasSolid) if (isBackendSelf) instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see your fullstack application.`);
6065
+ else instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see the web application.`);
5797
6066
  }
5798
6067
  if (hasNative) instructions.push("Use the Expo Go app to run the mobile application.");
5799
6068
  if (isConvex) instructions.push("Your app will connect to the Convex cloud backend automatically.");
5800
- else if (!isBackendNone) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
6069
+ else if (!isBackendNone && !isBackendSelf) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
5801
6070
  return instructions.join("\n");
5802
6071
  }
5803
6072
  function generateProjectStructure(projectName, frontend, backend, addons, isConvex, api, auth) {
5804
6073
  const structure = [`${projectName}/`, "├── apps/"];
5805
6074
  const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
5806
6075
  const isBackendNone = backend === "none";
6076
+ const isBackendSelf = backend === "self";
5807
6077
  if (!hasFrontendNone) {
5808
6078
  const hasTanstackRouter = frontend.includes("tanstack-router");
5809
6079
  const hasReactRouter = frontend.includes("react-router");
@@ -5821,21 +6091,32 @@ function generateProjectStructure(projectName, frontend, backend, addons, isConv
5821
6091
  else if (hasSvelte) frontendType = "SvelteKit";
5822
6092
  else if (hasNuxt) frontendType = "Nuxt";
5823
6093
  else if (hasSolid) frontendType = "SolidJS";
5824
- structure.push(`│ ├── web/ # Frontend application (${frontendType})`);
6094
+ if (isBackendSelf) structure.push(`│ └── web/ # Fullstack application (${frontendType})`);
6095
+ else structure.push(`│ ├── web/ # Frontend application (${frontendType})`);
5825
6096
  }
5826
6097
  }
5827
- if (frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
5828
- if (addons.includes("starlight")) structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
5829
- if (isConvex) {
5830
- structure.push("├── packages/");
5831
- structure.push("│ └── backend/ # Convex backend functions and schema");
5832
- if (auth === "clerk") structure.push("│ ├── convex/ # Convex functions and schema", "│ └── .env.local # Convex environment variables");
5833
- } else if (!isBackendNone) {
6098
+ if (frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) if (isBackendSelf) structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
6099
+ else structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
6100
+ if (addons.includes("starlight")) if (isBackendSelf) structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
6101
+ else structure.push("├── docs/ # Documentation site (Astro Starlight)");
6102
+ if (!isBackendSelf && !isBackendNone && !isConvex) {
5834
6103
  const backendName = backend[0].toUpperCase() + backend.slice(1);
5835
6104
  const apiName = api !== "none" ? api.toUpperCase() : "";
5836
6105
  const backendDesc = apiName ? `${backendName}, ${apiName}` : backendName;
5837
6106
  structure.push(`│ └── server/ # Backend API (${backendDesc})`);
5838
6107
  }
6108
+ if (isConvex || !isBackendNone) {
6109
+ structure.push("├── packages/");
6110
+ if (isConvex) {
6111
+ structure.push("│ ├── backend/ # Convex backend functions and schema");
6112
+ if (auth === "clerk") structure.push("│ │ ├── convex/ # Convex functions and schema", "│ │ └── .env.local # Convex environment variables");
6113
+ }
6114
+ if (!isConvex) {
6115
+ structure.push("│ ├── api/ # API layer / business logic");
6116
+ if (auth !== "none") structure.push("│ ├── auth/ # Authentication configuration & logic");
6117
+ if (api !== "none" || auth !== "none") structure.push("│ └── db/ # Database schema & queries");
6118
+ }
6119
+ }
5839
6120
  return structure.join("\n");
5840
6121
  }
5841
6122
  function generateFeaturesList(database, auth, addons, orm, runtime, frontend, backend, api) {
@@ -5871,7 +6152,6 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
5871
6152
  else if (backend === "express") addonsList.push("- **Express** - Fast, unopinionated web framework");
5872
6153
  else if (backend === "fastify") addonsList.push("- **Fastify** - Fast, low-overhead web framework");
5873
6154
  else if (backend === "elysia") addonsList.push("- **Elysia** - Type-safe, high-performance framework");
5874
- else if (backend === "next") addonsList.push("- **Next.js** - Full-stack React framework");
5875
6155
  if (api === "trpc") addonsList.push("- **tRPC** - End-to-end type-safe APIs");
5876
6156
  else if (api === "orpc") addonsList.push("- **oRPC** - End-to-end type-safe APIs with OpenAPI integration");
5877
6157
  if (runtime !== "none") addonsList.push(`- **${runtime === "bun" ? "Bun" : runtime === "node" ? "Node.js" : runtime}** - Runtime environment`);
@@ -5893,33 +6173,36 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
5893
6173
  else if (addon === "turborepo") addonsList.push("- **Turborepo** - Optimized monorepo build system");
5894
6174
  return addonsList.join("\n");
5895
6175
  }
5896
- function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, serverDeploy) {
6176
+ function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, serverDeploy, backend) {
5897
6177
  if (database === "none") return "";
6178
+ const isBackendSelf = backend === "self";
6179
+ const envPath = isBackendSelf ? "apps/web/.env" : "apps/server/.env";
6180
+ const dbLocalPath = isBackendSelf ? "apps/web" : "apps/server";
5898
6181
  let setup = "## Database Setup\n\n";
5899
6182
  if (database === "sqlite") setup += `This project uses SQLite${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
5900
6183
 
5901
6184
  1. Start the local SQLite database:
5902
6185
  ${dbSetup === "d1" ? serverDeploy === "alchemy" ? "D1 local development and migrations are handled automatically by Alchemy during dev and deploy." : "Local development for a Cloudflare D1 database will already be running as part of the `wrangler dev` command." : `\`\`\`bash
5903
- cd apps/server && ${packageManagerRunCmd} db:local
6186
+ cd ${dbLocalPath} && ${packageManagerRunCmd} db:local
5904
6187
  \`\`\`
5905
6188
  `}
5906
6189
 
5907
- 2. Update your \`.env\` file in the \`apps/server\` directory with the appropriate connection details if needed.
6190
+ 2. Update your \`.env\` file in the \`${isBackendSelf ? "apps/web" : "apps/server"}\` directory with the appropriate connection details if needed.
5908
6191
  `;
5909
6192
  else if (database === "postgres") setup += `This project uses PostgreSQL${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
5910
6193
 
5911
6194
  1. Make sure you have a PostgreSQL database set up.
5912
- 2. Update your \`apps/server/.env\` file with your PostgreSQL connection details.
6195
+ 2. Update your \`${envPath}\` file with your PostgreSQL connection details.
5913
6196
  `;
5914
6197
  else if (database === "mysql") setup += `This project uses MySQL${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
5915
6198
 
5916
6199
  1. Make sure you have a MySQL database set up.
5917
- 2. Update your \`apps/server/.env\` file with your MySQL connection details.
6200
+ 2. Update your \`${envPath}\` file with your MySQL connection details.
5918
6201
  `;
5919
6202
  else if (database === "mongodb") setup += `This project uses MongoDB ${orm === "mongoose" ? "with Mongoose" : orm === "prisma" ? "with Prisma ORM" : `with ${orm}`}.
5920
6203
 
5921
6204
  1. Make sure you have MongoDB set up.
5922
- 2. Update your \`apps/server/.env\` file with your MongoDB connection URI.
6205
+ 2. Update your \`${envPath}\` file with your MongoDB connection URI.
5923
6206
  `;
5924
6207
  setup += `
5925
6208
  3. ${orm === "prisma" ? `Generate the Prisma client and push the schema:
@@ -5938,13 +6221,14 @@ ${packageManagerRunCmd} db:push
5938
6221
  function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNative, addons, backend) {
5939
6222
  const isConvex = backend === "convex";
5940
6223
  const isBackendNone = backend === "none";
6224
+ const isBackendSelf = backend === "self";
5941
6225
  let scripts = `- \`${packageManagerRunCmd} dev\`: Start all applications in development mode
5942
6226
  - \`${packageManagerRunCmd} build\`: Build all applications`;
5943
- scripts += `
6227
+ if (!isBackendSelf) scripts += `
5944
6228
  - \`${packageManagerRunCmd} dev:web\`: Start only the web application`;
5945
6229
  if (isConvex) scripts += `
5946
6230
  - \`${packageManagerRunCmd} dev:setup\`: Setup and configure your Convex project`;
5947
- else if (!isBackendNone) scripts += `
6231
+ else if (!isBackendNone && !isBackendSelf) scripts += `
5948
6232
  - \`${packageManagerRunCmd} dev:server\`: Start only the server`;
5949
6233
  scripts += `
5950
6234
  - \`${packageManagerRunCmd} check-types\`: Check TypeScript types across all apps`;
@@ -5955,7 +6239,7 @@ function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNati
5955
6239
  - \`${packageManagerRunCmd} db:push\`: Push schema changes to database
5956
6240
  - \`${packageManagerRunCmd} db:studio\`: Open database studio UI`;
5957
6241
  if (database === "sqlite" && orm === "drizzle") scripts += `
5958
- - \`cd apps/server && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
6242
+ - \`cd ${isBackendSelf ? "apps/web" : "apps/server"} && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
5959
6243
  }
5960
6244
  if (addons.includes("biome")) scripts += `
5961
6245
  - \`${packageManagerRunCmd} check\`: Run Biome formatting and linting`;
@@ -6031,15 +6315,14 @@ async function initializeGit(projectDir, useGit) {
6031
6315
  async function setupPayments(config) {
6032
6316
  const { payments, projectDir, frontend } = config;
6033
6317
  if (!payments || payments === "none") return;
6034
- const serverDir = path.join(projectDir, "apps/server");
6035
6318
  const clientDir = path.join(projectDir, "apps/web");
6036
- const serverDirExists = await fs.pathExists(serverDir);
6319
+ const authDir = path.join(projectDir, "packages/auth");
6037
6320
  const clientDirExists = await fs.pathExists(clientDir);
6038
- if (!serverDirExists) return;
6321
+ const authDirExists = await fs.pathExists(authDir);
6039
6322
  if (payments === "polar") {
6040
- await addPackageDependency({
6323
+ if (authDirExists) await addPackageDependency({
6041
6324
  dependencies: ["@polar-sh/better-auth", "@polar-sh/sdk"],
6042
- projectDir: serverDir
6325
+ projectDir: authDir
6043
6326
  });
6044
6327
  if (clientDirExists) {
6045
6328
  if (frontend.some((f) => [
@@ -6113,19 +6396,20 @@ async function getDockerStatus(database) {
6113
6396
  async function displayPostInstallInstructions(config) {
6114
6397
  const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
6115
6398
  const isConvex = backend === "convex";
6399
+ const isBackendSelf = backend === "self";
6116
6400
  const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : "bun run";
6117
6401
  const cdCmd = `cd ${relativePath}`;
6118
6402
  const hasHuskyOrBiome = addons?.includes("husky") || addons?.includes("biome");
6119
- const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy) : "";
6403
+ const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
6120
6404
  const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
6121
6405
  const lintingInstructions = hasHuskyOrBiome ? getLintingInstructions(runCmd) : "";
6122
- const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex) : "";
6406
+ const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex, isBackendSelf) : "";
6123
6407
  const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
6124
6408
  const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
6125
6409
  const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
6126
- const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions() : "";
6127
- const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy);
6128
- const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy);
6410
+ const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions(backend) : "";
6411
+ const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
6412
+ const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
6129
6413
  const hasWeb = frontend?.some((f) => [
6130
6414
  "tanstack-router",
6131
6415
  "react-router",
@@ -6149,7 +6433,8 @@ async function displayPostInstallInstructions(config) {
6149
6433
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup\n${pc.dim(" (this will guide you through Convex project setup)")}\n`;
6150
6434
  output += `${pc.cyan(`${stepCounter++}.`)} Copy environment variables from\n${pc.white(" packages/backend/.env.local")} to ${pc.white("apps/*/.env")}\n`;
6151
6435
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
6152
- } else {
6436
+ } else if (isBackendSelf) output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
6437
+ else {
6153
6438
  if (runtime !== "workers") output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
6154
6439
  if (runtime === "workers") {
6155
6440
  if (dbSetup === "d1") output += `${pc.yellow("IMPORTANT:")} Complete D1 database setup first\n (see Database commands below)\n`;
@@ -6160,11 +6445,11 @@ async function displayPostInstallInstructions(config) {
6160
6445
  output += `${pc.bold("Your project will be available at:")}\n`;
6161
6446
  if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
6162
6447
  else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
6163
- if (!isConvex) {
6448
+ if (!isConvex && !isBackendSelf) {
6164
6449
  output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
6165
- if (api === "orpc") if (backend === "next") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/rpc/api\n`;
6166
- else output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
6450
+ if (api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
6167
6451
  }
6452
+ if (isBackendSelf && api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:${webPort}/rpc/api\n`;
6168
6453
  if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
6169
6454
  if (addons?.includes("fumadocs")) output += `${pc.cyan("•")} Fumadocs: http://localhost:4000\n`;
6170
6455
  if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
@@ -6184,9 +6469,9 @@ async function displayPostInstallInstructions(config) {
6184
6469
  output += pc.cyan("https://github.com/AmanVarshney01/create-better-t-stack");
6185
6470
  consola$1.box(output);
6186
6471
  }
6187
- function getNativeInstructions(isConvex) {
6472
+ function getNativeInstructions(isConvex, isBackendSelf) {
6188
6473
  const envVar = isConvex ? "EXPO_PUBLIC_CONVEX_URL" : "EXPO_PUBLIC_SERVER_URL";
6189
- const exampleUrl = isConvex ? "https://<YOUR_CONVEX_URL>" : "http://<YOUR_LOCAL_IP>:3000";
6474
+ const exampleUrl = isConvex ? "https://<YOUR_CONVEX_URL>" : isBackendSelf ? "http://<YOUR_LOCAL_IP>:3001" : "http://<YOUR_LOCAL_IP>:3000";
6190
6475
  const envFileName = ".env";
6191
6476
  const ipNote = isConvex ? "your Convex deployment URL (find after running 'dev:setup')" : "your local IP address";
6192
6477
  let instructions = `${pc.yellow("NOTE:")} For Expo connectivity issues, update\n apps/native/${envFileName} with ${ipNote}:\n ${`${envVar}=${exampleUrl}`}\n`;
@@ -6196,7 +6481,7 @@ function getNativeInstructions(isConvex) {
6196
6481
  function getLintingInstructions(runCmd) {
6197
6482
  return `${pc.bold("Linting and formatting:")}\n${pc.cyan("•")} Format and lint fix: ${`${runCmd} check`}\n`;
6198
6483
  }
6199
- async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy) {
6484
+ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) {
6200
6485
  const instructions = [];
6201
6486
  if (dbSetup === "docker") {
6202
6487
  const dockerStatus = await getDockerStatus(database);
@@ -6210,8 +6495,9 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup,
6210
6495
  const packageManager = runCmd === "npm run" ? "npm" : runCmd || "npm";
6211
6496
  instructions.push(`${pc.cyan("1.")} Login to Cloudflare: ${pc.white(`${packageManager} wrangler login`)}`);
6212
6497
  instructions.push(`${pc.cyan("2.")} Create D1 database: ${pc.white(`${packageManager} wrangler d1 create your-database-name`)}`);
6213
- instructions.push(`${pc.cyan("3.")} Update apps/server/wrangler.jsonc with database_id and database_name`);
6214
- instructions.push(`${pc.cyan("4.")} Generate migrations: ${pc.white(`cd apps/server && ${runCmd} db:generate`)}`);
6498
+ const wranglerPath = backend === "self" ? "apps/web" : "apps/server";
6499
+ instructions.push(`${pc.cyan("3.")} Update ${wranglerPath}/wrangler.jsonc with database_id and database_name`);
6500
+ instructions.push(`${pc.cyan("4.")} Generate migrations: ${pc.white(`cd ${wranglerPath} && ${runCmd} db:generate`)}`);
6215
6501
  instructions.push(`${pc.cyan("5.")} Apply migrations locally: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME --local`)}`);
6216
6502
  instructions.push(`${pc.cyan("6.")} Apply migrations to production: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME`)}`);
6217
6503
  }
@@ -6235,7 +6521,10 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup,
6235
6521
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6236
6522
  if (dbSetup !== "d1") instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
6237
6523
  if (!(dbSetup === "d1" && serverDeploy === "alchemy")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
6238
- if (database === "sqlite" && dbSetup !== "d1") instructions.push(`${pc.cyan("•")} Start local DB (if needed): ${`cd apps/server && ${runCmd} db:local`}`);
6524
+ if (database === "sqlite" && dbSetup !== "d1") {
6525
+ const dbLocalPath = backend === "self" ? "apps/web" : "apps/server";
6526
+ instructions.push(`${pc.cyan("•")} Start local DB (if needed): ${`cd ${dbLocalPath} && ${runCmd} db:local`}`);
6527
+ }
6239
6528
  } else if (orm === "mongoose") {
6240
6529
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6241
6530
  } else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
@@ -6256,32 +6545,124 @@ function getNoOrmWarning() {
6256
6545
  function getBunWebNativeWarning() {
6257
6546
  return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
6258
6547
  }
6259
- function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy) {
6548
+ function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
6260
6549
  const instructions = [];
6261
- if (webDeploy === "wrangler") instructions.push(`${pc.bold("Deploy web to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} run deploy`}`);
6262
- if (serverDeploy === "wrangler") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} run deploy`}`);
6550
+ if (webDeploy === "wrangler") {
6551
+ const deployPath = backend === "self" ? "apps/web" : "apps/web";
6552
+ instructions.push(`${pc.bold("Deploy web to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd ${deployPath} && ${runCmd} run deploy`}`);
6553
+ }
6554
+ if (serverDeploy === "wrangler" && backend !== "self") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} run deploy`}`);
6263
6555
  return instructions.length ? `\n${instructions.join("\n")}` : "";
6264
6556
  }
6265
6557
  function getClerkInstructions() {
6266
6558
  return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Follow the guide: ${pc.underline("https://docs.convex.dev/auth/clerk")}\n${pc.cyan("•")} Set CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard\n${pc.cyan("•")} Set CLERK_PUBLISHABLE_KEY in apps/*/.env`;
6267
6559
  }
6268
- function getPolarInstructions() {
6269
- return `${pc.bold("Polar Payments Setup:")}\n${pc.cyan("•")} Get access token & product ID from ${pc.underline("https://sandbox.polar.sh/")}\n${pc.cyan("•")} Set POLAR_ACCESS_TOKEN in apps/server/.env`;
6560
+ function getPolarInstructions(backend) {
6561
+ const envPath = backend === "self" ? "apps/web/.env" : "apps/server/.env";
6562
+ return `${pc.bold("Polar Payments Setup:")}\n${pc.cyan("•")} Get access token & product ID from ${pc.underline("https://sandbox.polar.sh/")}\n${pc.cyan("•")} Set POLAR_ACCESS_TOKEN in ${envPath}`;
6270
6563
  }
6271
- function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
6564
+ function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
6272
6565
  const instructions = [];
6566
+ const isBackendSelf = backend === "self";
6273
6567
  if (webDeploy === "alchemy" && serverDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy web with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/web && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/web && ${runCmd} destroy`}`);
6274
- else if (serverDeploy === "alchemy" && webDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/server && ${runCmd} destroy`}`);
6275
- else if (webDeploy === "alchemy" && serverDeploy === "alchemy") instructions.push(`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`);
6568
+ else if (serverDeploy === "alchemy" && webDeploy !== "alchemy" && !isBackendSelf) instructions.push(`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/server && ${runCmd} destroy`}`);
6569
+ else if (webDeploy === "alchemy" && (serverDeploy === "alchemy" || isBackendSelf)) instructions.push(`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`);
6276
6570
  return instructions.length ? `\n${instructions.join("\n")}` : "";
6277
6571
  }
6278
6572
 
6573
+ //#endregion
6574
+ //#region src/helpers/core/workspace-setup.ts
6575
+ async function setupWorkspaceDependencies(projectDir, options) {
6576
+ const projectName = options.projectName;
6577
+ const workspaceVersion = options.packageManager === "npm" ? "*" : "workspace:*";
6578
+ const commonDeps = ["dotenv", "zod"];
6579
+ const commonDevDeps = ["tsdown"];
6580
+ const dbPackageDir = path.join(projectDir, "packages/db");
6581
+ if (await fs.pathExists(dbPackageDir)) await addPackageDependency({
6582
+ dependencies: commonDeps,
6583
+ devDependencies: commonDevDeps,
6584
+ projectDir: dbPackageDir
6585
+ });
6586
+ const authPackageDir = path.join(projectDir, "packages/auth");
6587
+ if (await fs.pathExists(authPackageDir)) await addPackageDependency({
6588
+ dependencies: commonDeps,
6589
+ devDependencies: commonDevDeps,
6590
+ customDependencies: { [`@${projectName}/db`]: workspaceVersion },
6591
+ projectDir: authPackageDir
6592
+ });
6593
+ const apiPackageDir = path.join(projectDir, "packages/api");
6594
+ if (await fs.pathExists(apiPackageDir)) await addPackageDependency({
6595
+ dependencies: commonDeps,
6596
+ devDependencies: commonDevDeps,
6597
+ customDependencies: {
6598
+ [`@${projectName}/auth`]: workspaceVersion,
6599
+ [`@${projectName}/db`]: workspaceVersion
6600
+ },
6601
+ projectDir: apiPackageDir
6602
+ });
6603
+ const serverPackageDir = path.join(projectDir, "apps/server");
6604
+ if (await fs.pathExists(serverPackageDir)) await addPackageDependency({
6605
+ dependencies: commonDeps,
6606
+ devDependencies: commonDevDeps,
6607
+ customDependencies: {
6608
+ [`@${projectName}/api`]: workspaceVersion,
6609
+ [`@${projectName}/auth`]: workspaceVersion,
6610
+ [`@${projectName}/db`]: workspaceVersion
6611
+ },
6612
+ projectDir: serverPackageDir
6613
+ });
6614
+ const webPackageDir = path.join(projectDir, "apps/web");
6615
+ if (await fs.pathExists(webPackageDir)) {
6616
+ const webDeps = {};
6617
+ webDeps[`@${projectName}/api`] = workspaceVersion;
6618
+ webDeps[`@${projectName}/auth`] = workspaceVersion;
6619
+ webDeps[`@${projectName}/db`] = workspaceVersion;
6620
+ if (Object.keys(webDeps).length > 0) await addPackageDependency({
6621
+ customDependencies: webDeps,
6622
+ projectDir: webPackageDir
6623
+ });
6624
+ }
6625
+ const nativePackageDir = path.join(projectDir, "apps/native");
6626
+ if (await fs.pathExists(nativePackageDir)) {
6627
+ const nativeDeps = {};
6628
+ nativeDeps[`@${projectName}/api`] = workspaceVersion;
6629
+ if (Object.keys(nativeDeps).length > 0) await addPackageDependency({
6630
+ customDependencies: nativeDeps,
6631
+ projectDir: nativePackageDir
6632
+ });
6633
+ }
6634
+ const runtimeDevDeps = getRuntimeDevDeps(options);
6635
+ await addPackageDependency({
6636
+ dependencies: commonDeps,
6637
+ devDependencies: [...commonDevDeps, ...runtimeDevDeps],
6638
+ projectDir
6639
+ });
6640
+ }
6641
+ function getRuntimeDevDeps(options) {
6642
+ const { runtime, backend } = options;
6643
+ if (runtime === "none" && backend === "self") return ["@types/node"];
6644
+ if (runtime === "node") return ["@types/node"];
6645
+ if (runtime === "bun") return ["@types/bun"];
6646
+ if (runtime === "workers") return ["@types/node"];
6647
+ return [];
6648
+ }
6649
+
6279
6650
  //#endregion
6280
6651
  //#region src/helpers/core/project-config.ts
6281
6652
  async function updatePackageConfigurations(projectDir, options) {
6282
6653
  await updateRootPackageJson(projectDir, options);
6283
- if (options.backend !== "convex") await updateServerPackageJson(projectDir, options);
6284
- else await updateConvexPackageJson(projectDir, options);
6654
+ if (options.backend === "convex") await updateConvexPackageJson(projectDir, options);
6655
+ else if (options.backend === "self") {
6656
+ await updateDbPackageJson(projectDir, options);
6657
+ await updateAuthPackageJson(projectDir, options);
6658
+ await updateApiPackageJson(projectDir, options);
6659
+ await setupWorkspaceDependencies(projectDir, options);
6660
+ } else if (options.backend !== "none") {
6661
+ await updateServerPackageJson(projectDir, options);
6662
+ await updateAuthPackageJson(projectDir, options);
6663
+ await updateApiPackageJson(projectDir, options);
6664
+ await setupWorkspaceDependencies(projectDir, options);
6665
+ }
6285
6666
  }
6286
6667
  async function updateRootPackageJson(projectDir, options) {
6287
6668
  const rootPackageJsonPath = path.join(projectDir, "package.json");
@@ -6291,6 +6672,7 @@ async function updateRootPackageJson(projectDir, options) {
6291
6672
  if (!packageJson.scripts) packageJson.scripts = {};
6292
6673
  const scripts = packageJson.scripts;
6293
6674
  const backendPackageName = options.backend === "convex" ? `@${options.projectName}/backend` : "server";
6675
+ const dbPackageName = `@${options.projectName}/db`;
6294
6676
  let serverDevScript = "";
6295
6677
  if (options.addons.includes("turborepo")) serverDevScript = `turbo -F ${backendPackageName} dev`;
6296
6678
  else if (options.packageManager === "bun") serverDevScript = `bun run --filter ${backendPackageName} dev`;
@@ -6307,24 +6689,24 @@ async function updateRootPackageJson(projectDir, options) {
6307
6689
  scripts["check-types"] = "turbo check-types";
6308
6690
  scripts["dev:native"] = "turbo -F native dev";
6309
6691
  scripts["dev:web"] = "turbo -F web dev";
6310
- scripts["dev:server"] = serverDevScript;
6692
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6311
6693
  if (options.backend === "convex") scripts["dev:setup"] = `turbo -F ${backendPackageName} dev:setup`;
6312
6694
  if (needsDbScripts) {
6313
- scripts["db:push"] = `turbo -F ${backendPackageName} db:push`;
6314
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${backendPackageName} db:studio`;
6695
+ scripts["db:push"] = `turbo -F ${dbPackageName} db:push`;
6696
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${dbPackageName} db:studio`;
6315
6697
  if (options.orm === "prisma") {
6316
- scripts["db:generate"] = `turbo -F ${backendPackageName} db:generate`;
6317
- scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
6698
+ scripts["db:generate"] = `turbo -F ${dbPackageName} db:generate`;
6699
+ scripts["db:migrate"] = `turbo -F ${dbPackageName} db:migrate`;
6318
6700
  } else if (options.orm === "drizzle") {
6319
- scripts["db:generate"] = `turbo -F ${backendPackageName} db:generate`;
6320
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
6701
+ scripts["db:generate"] = `turbo -F ${dbPackageName} db:generate`;
6702
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${dbPackageName} db:migrate`;
6321
6703
  }
6322
6704
  }
6323
6705
  if (options.dbSetup === "docker") {
6324
- scripts["db:start"] = `turbo -F ${backendPackageName} db:start`;
6325
- scripts["db:watch"] = `turbo -F ${backendPackageName} db:watch`;
6326
- scripts["db:stop"] = `turbo -F ${backendPackageName} db:stop`;
6327
- scripts["db:down"] = `turbo -F ${backendPackageName} db:down`;
6706
+ scripts["db:start"] = `turbo -F ${dbPackageName} db:start`;
6707
+ scripts["db:watch"] = `turbo -F ${dbPackageName} db:watch`;
6708
+ scripts["db:stop"] = `turbo -F ${dbPackageName} db:stop`;
6709
+ scripts["db:down"] = `turbo -F ${dbPackageName} db:down`;
6328
6710
  }
6329
6711
  } else if (options.packageManager === "pnpm") {
6330
6712
  scripts.dev = devScript;
@@ -6332,24 +6714,24 @@ async function updateRootPackageJson(projectDir, options) {
6332
6714
  scripts["check-types"] = "pnpm -r check-types";
6333
6715
  scripts["dev:native"] = "pnpm --filter native dev";
6334
6716
  scripts["dev:web"] = "pnpm --filter web dev";
6335
- scripts["dev:server"] = serverDevScript;
6717
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6336
6718
  if (options.backend === "convex") scripts["dev:setup"] = `pnpm --filter ${backendPackageName} dev:setup`;
6337
6719
  if (needsDbScripts) {
6338
- scripts["db:push"] = `pnpm --filter ${backendPackageName} db:push`;
6339
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${backendPackageName} db:studio`;
6720
+ scripts["db:push"] = `pnpm --filter ${dbPackageName} db:push`;
6721
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${dbPackageName} db:studio`;
6340
6722
  if (options.orm === "prisma") {
6341
- scripts["db:generate"] = `pnpm --filter ${backendPackageName} db:generate`;
6342
- scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
6723
+ scripts["db:generate"] = `pnpm --filter ${dbPackageName} db:generate`;
6724
+ scripts["db:migrate"] = `pnpm --filter ${dbPackageName} db:migrate`;
6343
6725
  } else if (options.orm === "drizzle") {
6344
- scripts["db:generate"] = `pnpm --filter ${backendPackageName} db:generate`;
6345
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
6726
+ scripts["db:generate"] = `pnpm --filter ${dbPackageName} db:generate`;
6727
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${dbPackageName} db:migrate`;
6346
6728
  }
6347
6729
  }
6348
6730
  if (options.dbSetup === "docker") {
6349
- scripts["db:start"] = `pnpm --filter ${backendPackageName} db:start`;
6350
- scripts["db:watch"] = `pnpm --filter ${backendPackageName} db:watch`;
6351
- scripts["db:stop"] = `pnpm --filter ${backendPackageName} db:stop`;
6352
- scripts["db:down"] = `pnpm --filter ${backendPackageName} db:down`;
6731
+ scripts["db:start"] = `pnpm --filter ${dbPackageName} db:start`;
6732
+ scripts["db:watch"] = `pnpm --filter ${dbPackageName} db:watch`;
6733
+ scripts["db:stop"] = `pnpm --filter ${dbPackageName} db:stop`;
6734
+ scripts["db:down"] = `pnpm --filter ${dbPackageName} db:down`;
6353
6735
  }
6354
6736
  } else if (options.packageManager === "npm") {
6355
6737
  scripts.dev = devScript;
@@ -6357,24 +6739,24 @@ async function updateRootPackageJson(projectDir, options) {
6357
6739
  scripts["check-types"] = "npm run check-types --workspaces";
6358
6740
  scripts["dev:native"] = "npm run dev --workspace native";
6359
6741
  scripts["dev:web"] = "npm run dev --workspace web";
6360
- scripts["dev:server"] = serverDevScript;
6742
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6361
6743
  if (options.backend === "convex") scripts["dev:setup"] = `npm run dev:setup --workspace ${backendPackageName}`;
6362
6744
  if (needsDbScripts) {
6363
- scripts["db:push"] = `npm run db:push --workspace ${backendPackageName}`;
6364
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${backendPackageName}`;
6745
+ scripts["db:push"] = `npm run db:push --workspace ${dbPackageName}`;
6746
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${dbPackageName}`;
6365
6747
  if (options.orm === "prisma") {
6366
- scripts["db:generate"] = `npm run db:generate --workspace ${backendPackageName}`;
6367
- scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
6748
+ scripts["db:generate"] = `npm run db:generate --workspace ${dbPackageName}`;
6749
+ scripts["db:migrate"] = `npm run db:migrate --workspace ${dbPackageName}`;
6368
6750
  } else if (options.orm === "drizzle") {
6369
- scripts["db:generate"] = `npm run db:generate --workspace ${backendPackageName}`;
6370
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
6751
+ scripts["db:generate"] = `npm run db:generate --workspace ${dbPackageName}`;
6752
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${dbPackageName}`;
6371
6753
  }
6372
6754
  }
6373
6755
  if (options.dbSetup === "docker") {
6374
- scripts["db:start"] = `npm run db:start --workspace ${backendPackageName}`;
6375
- scripts["db:watch"] = `npm run db:watch --workspace ${backendPackageName}`;
6376
- scripts["db:stop"] = `npm run db:stop --workspace ${backendPackageName}`;
6377
- scripts["db:down"] = `npm run db:down --workspace ${backendPackageName}`;
6756
+ scripts["db:start"] = `npm run db:start --workspace ${dbPackageName}`;
6757
+ scripts["db:watch"] = `npm run db:watch --workspace ${dbPackageName}`;
6758
+ scripts["db:stop"] = `npm run db:stop --workspace ${dbPackageName}`;
6759
+ scripts["db:down"] = `npm run db:down --workspace ${dbPackageName}`;
6378
6760
  }
6379
6761
  } else if (options.packageManager === "bun") {
6380
6762
  scripts.dev = devScript;
@@ -6382,24 +6764,24 @@ async function updateRootPackageJson(projectDir, options) {
6382
6764
  scripts["check-types"] = "bun run --filter '*' check-types";
6383
6765
  scripts["dev:native"] = "bun run --filter native dev";
6384
6766
  scripts["dev:web"] = "bun run --filter web dev";
6385
- scripts["dev:server"] = serverDevScript;
6767
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6386
6768
  if (options.backend === "convex") scripts["dev:setup"] = `bun run --filter ${backendPackageName} dev:setup`;
6387
6769
  if (needsDbScripts) {
6388
- scripts["db:push"] = `bun run --filter ${backendPackageName} db:push`;
6389
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${backendPackageName} db:studio`;
6770
+ scripts["db:push"] = `bun run --filter ${dbPackageName} db:push`;
6771
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${dbPackageName} db:studio`;
6390
6772
  if (options.orm === "prisma") {
6391
- scripts["db:generate"] = `bun run --filter ${backendPackageName} db:generate`;
6392
- scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
6773
+ scripts["db:generate"] = `bun run --filter ${dbPackageName} db:generate`;
6774
+ scripts["db:migrate"] = `bun run --filter ${dbPackageName} db:migrate`;
6393
6775
  } else if (options.orm === "drizzle") {
6394
- scripts["db:generate"] = `bun run --filter ${backendPackageName} db:generate`;
6395
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
6776
+ scripts["db:generate"] = `bun run --filter ${dbPackageName} db:generate`;
6777
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${dbPackageName} db:migrate`;
6396
6778
  }
6397
6779
  }
6398
6780
  if (options.dbSetup === "docker") {
6399
- scripts["db:start"] = `bun run --filter ${backendPackageName} db:start`;
6400
- scripts["db:watch"] = `bun run --filter ${backendPackageName} db:watch`;
6401
- scripts["db:stop"] = `bun run --filter ${backendPackageName} db:stop`;
6402
- scripts["db:down"] = `bun run --filter ${backendPackageName} db:down`;
6781
+ scripts["db:start"] = `bun run --filter ${dbPackageName} db:start`;
6782
+ scripts["db:watch"] = `bun run --filter ${dbPackageName} db:watch`;
6783
+ scripts["db:stop"] = `bun run --filter ${dbPackageName} db:stop`;
6784
+ scripts["db:down"] = `bun run --filter ${dbPackageName} db:down`;
6403
6785
  }
6404
6786
  }
6405
6787
  try {
@@ -6424,7 +6806,16 @@ async function updateServerPackageJson(projectDir, options) {
6424
6806
  if (!await fs.pathExists(serverPackageJsonPath)) return;
6425
6807
  const serverPackageJson = await fs.readJson(serverPackageJsonPath);
6426
6808
  if (!serverPackageJson.scripts) serverPackageJson.scripts = {};
6427
- const scripts = serverPackageJson.scripts;
6809
+ await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
6810
+ await updateDbPackageJson(projectDir, options);
6811
+ }
6812
+ async function updateDbPackageJson(projectDir, options) {
6813
+ const dbPackageJsonPath = path.join(projectDir, "packages/db/package.json");
6814
+ if (!await fs.pathExists(dbPackageJsonPath)) return;
6815
+ const dbPackageJson = await fs.readJson(dbPackageJsonPath);
6816
+ dbPackageJson.name = `@${options.projectName}/db`;
6817
+ if (!dbPackageJson.scripts) dbPackageJson.scripts = {};
6818
+ const scripts = dbPackageJson.scripts;
6428
6819
  if (options.database !== "none") {
6429
6820
  if (options.database === "sqlite" && options.orm === "drizzle" && options.dbSetup !== "d1") scripts["db:local"] = "turso dev --db-file local.db";
6430
6821
  if (options.orm === "prisma") {
@@ -6445,7 +6836,21 @@ async function updateServerPackageJson(projectDir, options) {
6445
6836
  scripts["db:stop"] = "docker compose stop";
6446
6837
  scripts["db:down"] = "docker compose down";
6447
6838
  }
6448
- await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
6839
+ await fs.writeJson(dbPackageJsonPath, dbPackageJson, { spaces: 2 });
6840
+ }
6841
+ async function updateAuthPackageJson(projectDir, options) {
6842
+ const authPackageJsonPath = path.join(projectDir, "packages/auth/package.json");
6843
+ if (!await fs.pathExists(authPackageJsonPath)) return;
6844
+ const authPackageJson = await fs.readJson(authPackageJsonPath);
6845
+ authPackageJson.name = `@${options.projectName}/auth`;
6846
+ await fs.writeJson(authPackageJsonPath, authPackageJson, { spaces: 2 });
6847
+ }
6848
+ async function updateApiPackageJson(projectDir, options) {
6849
+ const apiPackageJsonPath = path.join(projectDir, "packages/api/package.json");
6850
+ if (!await fs.pathExists(apiPackageJsonPath)) return;
6851
+ const apiPackageJson = await fs.readJson(apiPackageJsonPath);
6852
+ apiPackageJson.name = `@${options.projectName}/api`;
6853
+ await fs.writeJson(apiPackageJsonPath, apiPackageJson, { spaces: 2 });
6449
6854
  }
6450
6855
  async function updateConvexPackageJson(projectDir, options) {
6451
6856
  const convexPackageJsonPath = path.join(projectDir, "packages/backend/package.json");
@@ -6461,15 +6866,14 @@ async function updateConvexPackageJson(projectDir, options) {
6461
6866
  async function createProject(options, cliInput) {
6462
6867
  const projectDir = options.projectDir;
6463
6868
  const isConvex = options.backend === "convex";
6869
+ const isSelfBackend = options.backend === "self";
6870
+ const needsServerSetup = !isConvex && !isSelfBackend;
6464
6871
  try {
6465
6872
  await fs.ensureDir(projectDir);
6466
6873
  await copyBaseTemplate(projectDir, options);
6467
6874
  await setupFrontendTemplates(projectDir, options);
6468
6875
  await setupBackendFramework(projectDir, options);
6469
- if (!isConvex) {
6470
- await setupDbOrmTemplates(projectDir, options);
6471
- await setupDockerComposeTemplates(projectDir, options);
6472
- }
6876
+ if (needsServerSetup) await setupDockerComposeTemplates(projectDir, options);
6473
6877
  await setupAuthTemplate(projectDir, options);
6474
6878
  if (options.payments && options.payments !== "none") await setupPaymentsTemplate(projectDir, options);
6475
6879
  if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamplesTemplate(projectDir, options);
@@ -6477,9 +6881,11 @@ async function createProject(options, cliInput) {
6477
6881
  await setupDeploymentTemplates(projectDir, options);
6478
6882
  await setupApi(options);
6479
6883
  if (!isConvex) {
6480
- await setupBackendDependencies(options);
6884
+ if (needsServerSetup) {
6885
+ await setupBackendDependencies(options);
6886
+ await setupRuntime(options);
6887
+ }
6481
6888
  await setupDatabase(options, cliInput);
6482
- await setupRuntime(options);
6483
6889
  if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamples(options);
6484
6890
  }
6485
6891
  if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
@@ -6488,6 +6894,7 @@ async function createProject(options, cliInput) {
6488
6894
  await handleExtras(projectDir, options);
6489
6895
  await setupEnvironmentVariables(options);
6490
6896
  await updatePackageConfigurations(projectDir, options);
6897
+ await setupCatalogs(projectDir, options);
6491
6898
  await setupWebDeploy(options);
6492
6899
  await setupServerDeploy(options);
6493
6900
  await createReadme(projectDir, options);