create-better-t-stack 2.50.0 → 2.50.1-canary.58bbe5f6

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 (110) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.d.ts +8 -9
  3. package/dist/index.js +1 -1
  4. package/dist/{src-BFx0Xu3C.js → src-CulfT5QB.js} +814 -425
  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/server/_gitignore +34 -0
  9. package/templates/api/orpc/server/package.json.hbs +22 -0
  10. package/templates/api/orpc/server/{base/src/lib → src}/context.ts.hbs +6 -6
  11. package/templates/{backend/server/server-base → api/orpc/server}/src/routers/index.ts.hbs +2 -2
  12. package/templates/api/orpc/server/tsconfig.json.hbs +10 -0
  13. package/templates/api/orpc/server/tsdown.config.ts.hbs +7 -0
  14. package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +1 -1
  15. package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +4 -2
  16. package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +1 -1
  17. package/templates/api/orpc/web/svelte/src/lib/orpc.ts.hbs +1 -1
  18. package/templates/api/trpc/fullstack/next/src/app/api/trpc/[trpc]/route.ts.hbs +14 -0
  19. package/templates/api/trpc/server/_gitignore +34 -0
  20. package/templates/api/trpc/server/package.json.hbs +21 -0
  21. package/templates/api/trpc/server/{base/src/lib → src}/context.ts.hbs +6 -6
  22. package/templates/api/trpc/server/src/routers/index.ts.hbs +55 -0
  23. package/templates/api/trpc/server/tsconfig.json.hbs +10 -0
  24. package/templates/api/trpc/server/tsdown.config.ts.hbs +7 -0
  25. package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +6 -4
  26. 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
  27. package/templates/auth/better-auth/server/base/_gitignore +34 -0
  28. package/templates/auth/better-auth/server/base/package.json.hbs +21 -0
  29. package/templates/auth/better-auth/server/base/src/{lib/auth.ts.hbs → index.ts.hbs} +12 -12
  30. package/templates/auth/better-auth/server/base/tsconfig.json.hbs +10 -0
  31. package/templates/auth/better-auth/server/base/tsdown.config.ts.hbs +7 -0
  32. package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +2 -0
  33. package/templates/auth/better-auth/web/react/next/src/app/dashboard/page.tsx.hbs +31 -0
  34. package/templates/backend/server/{server-base → base}/package.json.hbs +1 -5
  35. package/templates/backend/server/base/tsconfig.json.hbs +13 -0
  36. package/templates/backend/server/base/tsdown.config.ts.hbs +9 -0
  37. package/templates/backend/server/elysia/src/index.ts.hbs +6 -6
  38. package/templates/backend/server/express/src/index.ts.hbs +6 -6
  39. package/templates/backend/server/fastify/src/index.ts.hbs +6 -6
  40. package/templates/backend/server/hono/src/index.ts.hbs +7 -7
  41. package/templates/base/_gitignore +47 -1
  42. package/templates/base/package.json.hbs +1 -3
  43. package/templates/{backend/server/server-base/tsconfig.json.hbs → base/tsconfig.base.json.hbs} +13 -11
  44. package/templates/base/tsconfig.json.hbs +3 -0
  45. package/templates/db/base/_gitignore +34 -0
  46. package/templates/db/base/package.json.hbs +21 -0
  47. package/templates/db/base/tsconfig.json.hbs +10 -0
  48. package/templates/db/base/tsdown.config.ts.hbs +7 -0
  49. package/templates/db/drizzle/mysql/drizzle.config.ts.hbs +11 -2
  50. package/templates/db/drizzle/mysql/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  51. package/templates/db/drizzle/postgres/drizzle.config.ts.hbs +11 -2
  52. package/templates/db/drizzle/postgres/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  53. package/templates/db/drizzle/sqlite/drizzle.config.ts.hbs +11 -2
  54. package/templates/db/drizzle/sqlite/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  55. package/templates/db/prisma/mongodb/prisma.config.ts.hbs +9 -1
  56. package/templates/db/prisma/mongodb/src/index.ts.hbs +5 -0
  57. package/templates/db/prisma/mysql/prisma.config.ts.hbs +9 -1
  58. package/templates/db/prisma/mysql/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  59. package/templates/db/prisma/postgres/prisma.config.ts.hbs +11 -3
  60. package/templates/db/prisma/postgres/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  61. package/templates/db/prisma/sqlite/prisma.config.ts.hbs +9 -1
  62. package/templates/db/prisma/sqlite/src/{db/index.ts.hbs → index.ts.hbs} +3 -3
  63. package/templates/deploy/alchemy/alchemy.run.ts.hbs +3 -3
  64. package/templates/examples/ai/fullstack/next/src/app/api/ai/route.ts.hbs +15 -0
  65. package/templates/examples/todo/server/drizzle/base/src/routers/todo.ts.hbs +6 -6
  66. package/templates/examples/todo/server/mongoose/base/src/routers/todo.ts.hbs +4 -4
  67. package/templates/examples/todo/server/prisma/base/src/routers/todo.ts.hbs +4 -4
  68. package/templates/frontend/native/nativewind/tsconfig.json.hbs +1 -6
  69. package/templates/frontend/native/unistyles/tsconfig.json.hbs +1 -6
  70. package/templates/frontend/nuxt/tsconfig.json.hbs +0 -4
  71. package/templates/frontend/react/next/package.json.hbs +1 -1
  72. package/templates/frontend/react/next/tsconfig.json.hbs +0 -7
  73. package/templates/frontend/react/react-router/tsconfig.json.hbs +1 -6
  74. package/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs +1 -1
  75. package/templates/frontend/react/tanstack-router/tsconfig.json.hbs +1 -6
  76. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +1 -1
  77. package/templates/frontend/react/tanstack-start/tsconfig.json.hbs +1 -6
  78. package/templates/frontend/solid/tsconfig.json.hbs +1 -6
  79. package/templates/frontend/svelte/tsconfig.json.hbs +1 -6
  80. package/templates/api/orpc/server/next/src/app/rpc/[...all]/route.ts.hbs +0 -52
  81. package/templates/api/trpc/server/next/src/app/trpc/[trpc]/route.ts +0 -14
  82. package/templates/backend/server/next/next-env.d.ts +0 -5
  83. package/templates/backend/server/next/next.config.ts +0 -7
  84. package/templates/backend/server/next/package.json.hbs +0 -27
  85. package/templates/backend/server/next/src/app/route.ts +0 -5
  86. package/templates/backend/server/next/src/middleware.ts +0 -19
  87. package/templates/backend/server/next/tsconfig.json.hbs +0 -33
  88. package/templates/db/prisma/mongodb/src/db/index.ts.hbs +0 -5
  89. package/templates/examples/ai/server/next/src/app/ai/route.ts.hbs +0 -15
  90. /package/templates/api/orpc/server/{base/src/lib/orpc.ts.hbs → src/index.ts.hbs} +0 -0
  91. /package/templates/api/trpc/server/{base/src/lib/trpc.ts.hbs → src/index.ts.hbs} +0 -0
  92. /package/templates/auth/better-auth/server/db/drizzle/mysql/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
  93. /package/templates/auth/better-auth/server/db/drizzle/postgres/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
  94. /package/templates/auth/better-auth/server/db/drizzle/sqlite/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
  95. /package/templates/auth/better-auth/server/db/mongoose/mongodb/src/{db/models/auth.model.ts → models/auth.model.ts.hbs} +0 -0
  96. /package/templates/auth/better-auth/server/db/prisma/mongodb/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  97. /package/templates/auth/better-auth/server/db/prisma/mysql/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  98. /package/templates/auth/better-auth/server/db/prisma/postgres/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  99. /package/templates/auth/better-auth/server/db/prisma/sqlite/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  100. /package/templates/auth/better-auth/web/nuxt/app/middleware/{auth.ts → auth.ts.hbs} +0 -0
  101. /package/templates/backend/server/{server-base → base}/_gitignore +0 -0
  102. /package/templates/db/mongoose/mongodb/src/{db/index.ts.hbs → index.ts.hbs} +0 -0
  103. /package/templates/examples/todo/server/drizzle/mysql/src/{db/schema → schema}/todo.ts +0 -0
  104. /package/templates/examples/todo/server/drizzle/postgres/src/{db/schema → schema}/todo.ts +0 -0
  105. /package/templates/examples/todo/server/drizzle/sqlite/src/{db/schema → schema}/todo.ts +0 -0
  106. /package/templates/examples/todo/server/mongoose/mongodb/src/{db/models/todo.model.ts → models/todo.model.ts.hbs} +0 -0
  107. /package/templates/examples/todo/server/prisma/mongodb/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  108. /package/templates/examples/todo/server/prisma/mysql/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  109. /package/templates/examples/todo/server/prisma/postgres/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  110. /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,17 @@ 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
+ if (!frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f))) 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.");
356
+ if (frontends.length > 1) exitWithError("Backend 'self' (fullstack) can only be used with a single frontend framework.");
357
+ }
358
+ const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
359
+ 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.");
360
+ }
346
361
  function validateWorkersCompatibility(providedFlags, options, config) {
347
362
  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
363
  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 +679,34 @@ async function getAuthChoice(auth, hasDatabase, backend, frontend) {
664
679
 
665
680
  //#endregion
666
681
  //#region src/prompts/backend.ts
682
+ const FULLSTACK_FRONTENDS = ["next"];
667
683
  async function getBackendFrameworkChoice(backendFramework, frontends) {
668
684
  if (backendFramework !== void 0) return backendFramework;
669
685
  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
- ];
686
+ const hasFullstackFrontend = frontends?.some((f) => FULLSTACK_FRONTENDS.includes(f));
687
+ const backendOptions = [];
688
+ if (hasFullstackFrontend) backendOptions.push({
689
+ value: "self",
690
+ label: "Self (Fullstack)",
691
+ hint: "Use frontend's built-in api routes"
692
+ });
693
+ backendOptions.push({
694
+ value: "hono",
695
+ label: "Hono",
696
+ hint: "Lightweight, ultrafast web framework"
697
+ }, {
698
+ value: "express",
699
+ label: "Express",
700
+ hint: "Fast, unopinionated, minimalist web framework for Node.js"
701
+ }, {
702
+ value: "fastify",
703
+ label: "Fastify",
704
+ hint: "Fast, low-overhead web framework for Node.js"
705
+ }, {
706
+ value: "elysia",
707
+ label: "Elysia",
708
+ hint: "Ergonomic web framework for building backend servers"
709
+ });
697
710
  if (!hasIncompatibleFrontend) backendOptions.push({
698
711
  value: "convex",
699
712
  label: "Convex",
@@ -707,7 +720,7 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
707
720
  const response = await select({
708
721
  message: "Select backend",
709
722
  options: backendOptions,
710
- initialValue: DEFAULT_CONFIG.backend
723
+ initialValue: hasFullstackFrontend ? "self" : DEFAULT_CONFIG.backend
711
724
  });
712
725
  if (isCancel(response)) return exitCancelled("Operation cancelled");
713
726
  return response;
@@ -1086,9 +1099,8 @@ async function getPaymentsChoice(payments, auth, backend, frontends) {
1086
1099
  //#endregion
1087
1100
  //#region src/prompts/runtime.ts
1088
1101
  async function getRuntimeChoice(runtime, backend) {
1089
- if (backend === "convex" || backend === "none") return "none";
1102
+ if (backend === "convex" || backend === "none" || backend === "self") return "none";
1090
1103
  if (runtime !== void 0) return runtime;
1091
- if (backend === "next") return "node";
1092
1104
  const runtimeOptions = [{
1093
1105
  value: "bun",
1094
1106
  label: "Bun",
@@ -1375,7 +1387,7 @@ const getLatestCLIVersion = () => {
1375
1387
  */
1376
1388
  function isTelemetryEnabled() {
1377
1389
  const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
1378
- const BTS_TELEMETRY = "1";
1390
+ const BTS_TELEMETRY = "0";
1379
1391
  if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
1380
1392
  if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
1381
1393
  return true;
@@ -1383,8 +1395,8 @@ function isTelemetryEnabled() {
1383
1395
 
1384
1396
  //#endregion
1385
1397
  //#region src/utils/analytics.ts
1386
- const POSTHOG_API_KEY = "phc_8ZUxEwwfKMajJLvxz1daGd931dYbQrwKNficBmsdIrs";
1387
- const POSTHOG_HOST = "https://us.i.posthog.com";
1398
+ const POSTHOG_API_KEY = "random";
1399
+ const POSTHOG_HOST = "random";
1388
1400
  function generateSessionId() {
1389
1401
  const rand = Math.random().toString(36).slice(2);
1390
1402
  return `cli_${Date.now().toString(36)}${rand}`;
@@ -1765,8 +1777,8 @@ function validateBackendConstraints(config, providedFlags, options) {
1765
1777
  ].includes(f));
1766
1778
  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
1779
  }
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.");
1780
+ if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none" && backend !== "self") {
1781
+ 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
1782
  }
1771
1783
  if (backend === "convex" && providedFlags.has("frontend") && options.frontend) {
1772
1784
  const incompatibleFrontends = options.frontend.filter((f) => f === "solid");
@@ -1796,6 +1808,7 @@ function validateFullConfig(config, providedFlags, options) {
1796
1808
  validateFrontendConstraints(config, providedFlags);
1797
1809
  validateApiConstraints(config, options);
1798
1810
  validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
1811
+ validateSelfBackendCompatibility(providedFlags, options, config);
1799
1812
  validateWorkersCompatibility(providedFlags, options, config);
1800
1813
  if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose 'wrangler' or 'alchemy' for --server-deploy.");
1801
1814
  if (config.addons && config.addons.length > 0) {
@@ -1849,6 +1862,7 @@ const CORE_STACK_FLAGS = new Set([
1849
1862
  "examples",
1850
1863
  "auth",
1851
1864
  "dbSetup",
1865
+ "payments",
1852
1866
  "api",
1853
1867
  "webDeploy",
1854
1868
  "serverDeploy"
@@ -1992,15 +2006,17 @@ const addPackageDependency = async (opts) => {
1992
2006
  if (!pkgJson.dependencies) pkgJson.dependencies = {};
1993
2007
  if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
1994
2008
  for (const pkgName of dependencies) {
1995
- const version = customDependencies[pkgName] || dependencyVersionMap[pkgName];
2009
+ const version = dependencyVersionMap[pkgName];
1996
2010
  if (version) pkgJson.dependencies[pkgName] = version;
1997
2011
  else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
1998
2012
  }
1999
2013
  for (const pkgName of devDependencies) {
2000
- const version = customDevDependencies[pkgName] || dependencyVersionMap[pkgName];
2014
+ const version = dependencyVersionMap[pkgName];
2001
2015
  if (version) pkgJson.devDependencies[pkgName] = version;
2002
2016
  else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
2003
2017
  }
2018
+ for (const [pkgName, version] of Object.entries(customDependencies)) pkgJson.dependencies[pkgName] = version;
2019
+ for (const [pkgName, version] of Object.entries(customDevDependencies)) pkgJson.devDependencies[pkgName] = version;
2004
2020
  await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
2005
2021
  };
2006
2022
 
@@ -2062,11 +2078,14 @@ async function setupFumadocs(config) {
2062
2078
  if (isCancel(template)) return exitCancelled("Operation cancelled");
2063
2079
  const commandWithArgs = `create-fumadocs-app@latest fumadocs --template ${TEMPLATES[template].value} --src --no-install --pm ${packageManager} --no-eslint --no-git`;
2064
2080
  const fumadocsInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2081
+ const s = spinner();
2082
+ s.start("Setting up Fumadocs...");
2065
2083
  await execa(fumadocsInitCommand, {
2066
2084
  cwd: path.join(projectDir, "apps"),
2067
2085
  env: { CI: "true" },
2068
2086
  shell: true
2069
2087
  });
2088
+ s.stop("Fumadocs setup complete!");
2070
2089
  const fumadocsDir = path.join(projectDir, "apps", "fumadocs");
2071
2090
  const packageJsonPath = path.join(fumadocsDir, "package.json");
2072
2091
  if (await fs.pathExists(packageJsonPath)) {
@@ -2307,11 +2326,14 @@ async function setupUltracite(config, hasHusky) {
2307
2326
  if (hasHusky) ultraciteArgs.push("--integrations", "husky", "lint-staged");
2308
2327
  const commandWithArgs = `ultracite@latest ${ultraciteArgs.join(" ")} --skip-install`;
2309
2328
  const ultraciteInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2329
+ const s = spinner();
2330
+ s.start("Setting up Ultracite...");
2310
2331
  await execa(ultraciteInitCommand, {
2311
2332
  cwd: projectDir,
2312
2333
  env: { CI: "true" },
2313
2334
  shell: true
2314
2335
  });
2336
+ s.stop("Ultracite setup complete!");
2315
2337
  if (hasHusky) await addPackageDependency({
2316
2338
  devDependencies: ["husky", "lint-staged"],
2317
2339
  projectDir
@@ -2655,8 +2677,12 @@ async function processTemplate(srcPath, destPath, context) {
2655
2677
  }
2656
2678
  handlebars.registerHelper("eq", (a, b) => a === b);
2657
2679
  handlebars.registerHelper("ne", (a, b) => a !== b);
2658
- handlebars.registerHelper("and", (a, b) => a && b);
2659
- handlebars.registerHelper("or", (a, b) => a || b);
2680
+ handlebars.registerHelper("and", (...args) => {
2681
+ return args.slice(0, -1).every((value) => value);
2682
+ });
2683
+ handlebars.registerHelper("or", (...args) => {
2684
+ return args.slice(0, -1).some((value) => value);
2685
+ });
2660
2686
  handlebars.registerHelper("includes", (array, value) => Array.isArray(array) && array.includes(value));
2661
2687
 
2662
2688
  //#endregion
@@ -2718,6 +2744,10 @@ async function setupFrontendTemplates(projectDir, context) {
2718
2744
  const apiWebBaseDir = path.join(PKG_ROOT, `templates/api/${context.api}/web/react/base`);
2719
2745
  if (await fs.pathExists(apiWebBaseDir)) await processAndCopyFiles("**/*", apiWebBaseDir, webAppDir, context);
2720
2746
  }
2747
+ if (context.backend === "self" && reactFramework === "next" && context.api !== "none") {
2748
+ const apiFullstackDir = path.join(PKG_ROOT, `templates/api/${context.api}/fullstack/next`);
2749
+ if (await fs.pathExists(apiFullstackDir)) await processAndCopyFiles("**/*", apiFullstackDir, webAppDir, context);
2750
+ }
2721
2751
  }
2722
2752
  } else if (hasNuxtWeb) {
2723
2753
  const nuxtBaseDir = path.join(PKG_ROOT, "templates/frontend/nuxt");
@@ -2758,35 +2788,52 @@ async function setupFrontendTemplates(projectDir, context) {
2758
2788
  }
2759
2789
  }
2760
2790
  }
2761
- async function setupBackendFramework(projectDir, context) {
2762
- if (context.backend === "none") return;
2791
+ async function setupApiPackage(projectDir, context) {
2792
+ if (context.api === "none") return;
2793
+ const apiPackageDir = path.join(projectDir, "packages/api");
2794
+ await fs.ensureDir(apiPackageDir);
2795
+ const apiServerDir = path.join(PKG_ROOT, `templates/api/${context.api}/server`);
2796
+ if (await fs.pathExists(apiServerDir)) await processAndCopyFiles("**/*", apiServerDir, apiPackageDir, context);
2797
+ }
2798
+ async function setupDbPackage(projectDir, context) {
2799
+ if (context.database === "none" || context.orm === "none") return;
2800
+ const dbPackageDir = path.join(projectDir, "packages/db");
2801
+ await fs.ensureDir(dbPackageDir);
2802
+ const dbBaseDir = path.join(PKG_ROOT, "templates/db/base");
2803
+ if (await fs.pathExists(dbBaseDir)) await processAndCopyFiles("**/*", dbBaseDir, dbPackageDir, context);
2804
+ const dbOrmSrcDir = path.join(PKG_ROOT, `templates/db/${context.orm}/${context.database}`);
2805
+ if (await fs.pathExists(dbOrmSrcDir)) await processAndCopyFiles("**/*", dbOrmSrcDir, dbPackageDir, context);
2806
+ }
2807
+ async function setupConvexBackend(projectDir, context) {
2808
+ const serverAppDir = path.join(projectDir, "apps/server");
2809
+ if (await fs.pathExists(serverAppDir)) await fs.remove(serverAppDir);
2810
+ const convexBackendDestDir = path.join(projectDir, "packages/backend");
2811
+ const convexSrcDir = path.join(PKG_ROOT, "templates/backend/convex/packages/backend");
2812
+ await fs.ensureDir(convexBackendDestDir);
2813
+ if (await fs.pathExists(convexSrcDir)) await processAndCopyFiles("**/*", convexSrcDir, convexBackendDestDir, context);
2814
+ }
2815
+ async function setupServerApp(projectDir, context) {
2763
2816
  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
2817
  await fs.ensureDir(serverAppDir);
2773
- const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/server-base");
2818
+ const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/base");
2774
2819
  if (await fs.pathExists(serverBaseDir)) await processAndCopyFiles("**/*", serverBaseDir, serverAppDir, context);
2775
2820
  const frameworkSrcDir = path.join(PKG_ROOT, `templates/backend/server/${context.backend}`);
2776
2821
  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
2822
  }
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);
2823
+ async function setupBackendFramework(projectDir, context) {
2824
+ if (context.backend === "none") return;
2825
+ if (context.backend === "convex") {
2826
+ await setupConvexBackend(projectDir, context);
2827
+ return;
2828
+ }
2829
+ if (context.backend === "self") {
2830
+ await setupApiPackage(projectDir, context);
2831
+ await setupDbPackage(projectDir, context);
2832
+ return;
2833
+ }
2834
+ await setupServerApp(projectDir, context);
2835
+ await setupApiPackage(projectDir, context);
2836
+ await setupDbPackage(projectDir, context);
2790
2837
  }
2791
2838
  async function setupAuthTemplate(projectDir, context) {
2792
2839
  if (!context.auth || context.auth === "none") return;
@@ -2866,21 +2913,21 @@ async function setupAuthTemplate(projectDir, context) {
2866
2913
  }
2867
2914
  return;
2868
2915
  }
2869
- if (serverAppDirExists && context.backend !== "convex") {
2916
+ if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
2917
+ const authPackageDir = path.join(projectDir, "packages/auth");
2918
+ await fs.ensureDir(authPackageDir);
2870
2919
  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
- }
2920
+ if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc, authPackageDir, context);
2876
2921
  if (context.orm !== "none" && context.database !== "none") {
2922
+ const dbPackageDir = path.join(projectDir, "packages/db");
2923
+ await fs.ensureDir(dbPackageDir);
2877
2924
  const orm = context.orm;
2878
2925
  const db = context.database;
2879
2926
  let authDbSrc = "";
2880
2927
  if (orm === "drizzle") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/drizzle/${db}`);
2881
2928
  else if (orm === "prisma") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/prisma/${db}`);
2882
2929
  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);
2930
+ if (authDbSrc && await fs.pathExists(authDbSrc)) await processAndCopyFiles("**/*", authDbSrc, dbPackageDir, context);
2884
2931
  }
2885
2932
  }
2886
2933
  if ((hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb) && webAppDirExists) {
@@ -2896,6 +2943,10 @@ async function setupAuthTemplate(projectDir, context) {
2896
2943
  if (reactFramework) {
2897
2944
  const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/${reactFramework}`);
2898
2945
  if (await fs.pathExists(authWebFrameworkSrc)) await processAndCopyFiles("**/*", authWebFrameworkSrc, webAppDir, context);
2946
+ if (context.backend === "self" && reactFramework === "next") {
2947
+ const authFullstackSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/fullstack/next`);
2948
+ if (await fs.pathExists(authFullstackSrc)) await processAndCopyFiles("**/*", authFullstackSrc, webAppDir, context);
2949
+ }
2899
2950
  }
2900
2951
  } else if (hasNuxtWeb) {
2901
2952
  const authWebNuxtSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/nuxt`);
@@ -2926,9 +2977,11 @@ async function setupPaymentsTemplate(projectDir, context) {
2926
2977
  const webAppDir = path.join(projectDir, "apps/web");
2927
2978
  const serverAppDirExists = await fs.pathExists(serverAppDir);
2928
2979
  const webAppDirExists = await fs.pathExists(webAppDir);
2929
- if (serverAppDirExists && context.backend !== "convex") {
2980
+ if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
2981
+ const authPackageDir = path.join(projectDir, "packages/auth");
2982
+ await fs.ensureDir(authPackageDir);
2930
2983
  const paymentsServerSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/server/base`);
2931
- if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc, serverAppDir, context);
2984
+ if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc, authPackageDir, context);
2932
2985
  }
2933
2986
  const hasReactWeb = context.frontend.some((f) => [
2934
2987
  "tanstack-router",
@@ -3004,17 +3057,19 @@ async function setupExamplesTemplate(projectDir, context) {
3004
3057
  for (const example of context.examples) {
3005
3058
  if (example === "none") continue;
3006
3059
  const exampleBaseDir = path.join(PKG_ROOT, `templates/examples/${example}`);
3007
- if (serverAppDirExists && context.backend !== "convex" && context.backend !== "none") {
3060
+ if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex" && context.backend !== "none") {
3008
3061
  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);
3062
+ if (context.api !== "none") {
3063
+ const apiPackageDir = path.join(projectDir, "packages/api");
3064
+ await fs.ensureDir(apiPackageDir);
3065
+ const exampleOrmBaseSrc = path.join(exampleServerSrc, context.orm, "base");
3066
+ if (await fs.pathExists(exampleOrmBaseSrc)) await processAndCopyFiles("**/*", exampleOrmBaseSrc, apiPackageDir, context, false);
3012
3067
  }
3013
3068
  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);
3069
+ const dbPackageDir = path.join(projectDir, "packages/db");
3070
+ await fs.ensureDir(dbPackageDir);
3016
3071
  const exampleDbSchemaSrc = path.join(exampleServerSrc, context.orm, context.database);
3017
- if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc, serverAppDir, context, false);
3072
+ if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc, dbPackageDir, context, false);
3018
3073
  }
3019
3074
  }
3020
3075
  if (webAppDirExists) {
@@ -3034,6 +3089,10 @@ async function setupExamplesTemplate(projectDir, context) {
3034
3089
  if (reactFramework) {
3035
3090
  const exampleWebFrameworkSrc = path.join(exampleWebSrc, reactFramework);
3036
3091
  if (await fs.pathExists(exampleWebFrameworkSrc)) await processAndCopyFiles("**/*", exampleWebFrameworkSrc, webAppDir, context, false);
3092
+ if (context.backend === "self" && reactFramework === "next") {
3093
+ const exampleFullstackSrc = path.join(exampleBaseDir, "fullstack/next");
3094
+ if (await fs.pathExists(exampleFullstackSrc)) await processAndCopyFiles("**/*", exampleFullstackSrc, webAppDir, context, false);
3095
+ }
3037
3096
  }
3038
3097
  }
3039
3098
  } else if (hasNuxtWeb) {
@@ -3081,30 +3140,34 @@ async function handleExtras(projectDir, context) {
3081
3140
  }
3082
3141
  async function setupDockerComposeTemplates(projectDir, context) {
3083
3142
  if (context.dbSetup !== "docker" || context.database === "none") return;
3084
- const serverAppDir = path.join(projectDir, "apps/server");
3143
+ const dbPackageDir = path.join(projectDir, "packages/db");
3085
3144
  const dockerSrcDir = path.join(PKG_ROOT, `templates/db-setup/docker-compose/${context.database}`);
3086
- if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, serverAppDir, context);
3145
+ if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, dbPackageDir, context);
3087
3146
  }
3088
3147
  async function setupDeploymentTemplates(projectDir, context) {
3089
- if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") if (context.webDeploy === "alchemy" && context.serverDeploy === "alchemy") {
3148
+ const isBackendSelf = context.backend === "self";
3149
+ if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") {
3090
3150
  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);
3151
+ if (context.webDeploy === "alchemy" && (context.serverDeploy === "alchemy" || isBackendSelf)) {
3152
+ if (await fs.pathExists(alchemyTemplateSrc)) {
3153
+ await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, projectDir, context);
3154
+ await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3155
+ }
3156
+ } else {
3157
+ if (context.webDeploy === "alchemy") {
3158
+ const webAppDir = path.join(projectDir, "apps/web");
3159
+ if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(webAppDir)) {
3160
+ await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, webAppDir, context);
3161
+ await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3162
+ }
3163
+ }
3164
+ if (context.serverDeploy === "alchemy" && !isBackendSelf) {
3165
+ const serverAppDir = path.join(projectDir, "apps/server");
3166
+ if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(serverAppDir)) {
3167
+ await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3168
+ await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3169
+ await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3170
+ }
3108
3171
  }
3109
3172
  }
3110
3173
  }
@@ -3127,7 +3190,7 @@ async function setupDeploymentTemplates(projectDir, context) {
3127
3190
  }
3128
3191
  }
3129
3192
  }
3130
- if (context.serverDeploy !== "none" && context.serverDeploy !== "alchemy") {
3193
+ if (context.serverDeploy !== "none" && context.serverDeploy !== "alchemy" && !isBackendSelf) {
3131
3194
  const serverAppDir = path.join(projectDir, "apps/server");
3132
3195
  if (await fs.pathExists(serverAppDir)) {
3133
3196
  const deployTemplateSrc = path.join(PKG_ROOT, `templates/deploy/${context.serverDeploy}/server`);
@@ -3135,6 +3198,18 @@ async function setupDeploymentTemplates(projectDir, context) {
3135
3198
  }
3136
3199
  }
3137
3200
  }
3201
+ async function addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc) {
3202
+ for (const packageName of [
3203
+ "packages/api",
3204
+ "packages/auth",
3205
+ "packages/db"
3206
+ ]) {
3207
+ const packageDir = path.join(projectDir, packageName);
3208
+ if (await fs.pathExists(packageDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, packageDir, context);
3209
+ }
3210
+ const serverAppDir = path.join(projectDir, "apps/server");
3211
+ if (await fs.pathExists(serverAppDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3212
+ }
3138
3213
 
3139
3214
  //#endregion
3140
3215
  //#region src/helpers/core/add-addons.ts
@@ -3200,7 +3275,7 @@ async function setupServerDeploy(config) {
3200
3275
  serverDir,
3201
3276
  packageManager
3202
3277
  });
3203
- } else if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, packageManager);
3278
+ } else if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, packageManager, projectDir);
3204
3279
  }
3205
3280
  async function setupWorkersServerDeploy(serverDir, _packageManager) {
3206
3281
  const packageJsonPath = path.join(serverDir, "package.json");
@@ -3237,18 +3312,18 @@ async function generateCloudflareWorkerTypes({ serverDir, packageManager }) {
3237
3312
  log.warn(`Note: You can manually run 'cd apps/server && ${managerCmd} cf-typegen' in the project directory later`);
3238
3313
  }
3239
3314
  }
3240
- async function setupAlchemyServerDeploy(serverDir, _packageManager) {
3315
+ async function setupAlchemyServerDeploy(serverDir, _packageManager, projectDir) {
3241
3316
  if (!await fs.pathExists(serverDir)) return;
3242
3317
  await addPackageDependency({
3243
3318
  devDependencies: [
3244
3319
  "alchemy",
3245
3320
  "wrangler",
3246
3321
  "@types/node",
3247
- "dotenv",
3248
3322
  "@cloudflare/workers-types"
3249
3323
  ],
3250
3324
  projectDir: serverDir
3251
3325
  });
3326
+ if (projectDir) await addAlchemyPackagesDependencies$1(projectDir);
3252
3327
  const packageJsonPath = path.join(serverDir, "package.json");
3253
3328
  if (await fs.pathExists(packageJsonPath)) {
3254
3329
  const packageJson = await fs.readJson(packageJsonPath);
@@ -3261,6 +3336,19 @@ async function setupAlchemyServerDeploy(serverDir, _packageManager) {
3261
3336
  await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
3262
3337
  }
3263
3338
  }
3339
+ async function addAlchemyPackagesDependencies$1(projectDir) {
3340
+ for (const packageName of [
3341
+ "packages/api",
3342
+ "packages/auth",
3343
+ "packages/db"
3344
+ ]) {
3345
+ const packageDir = path.join(projectDir, packageName);
3346
+ if (await fs.pathExists(packageDir)) await addPackageDependency({
3347
+ devDependencies: ["@cloudflare/workers-types"],
3348
+ projectDir: packageDir
3349
+ });
3350
+ }
3351
+ }
3264
3352
 
3265
3353
  //#endregion
3266
3354
  //#region src/helpers/deployment/alchemy/alchemy-next-setup.ts
@@ -3271,7 +3359,6 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
3271
3359
  dependencies: ["@opennextjs/cloudflare"],
3272
3360
  devDependencies: [
3273
3361
  "alchemy",
3274
- "dotenv",
3275
3362
  "wrangler",
3276
3363
  "@cloudflare/workers-types"
3277
3364
  ],
@@ -3307,7 +3394,6 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
3307
3394
  devDependencies: [
3308
3395
  "alchemy",
3309
3396
  "nitro-cloudflare-dev",
3310
- "dotenv",
3311
3397
  "wrangler"
3312
3398
  ],
3313
3399
  projectDir: webAppDir
@@ -3370,7 +3456,7 @@ async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, option
3370
3456
  const webAppDir = path.join(projectDir, "apps/web");
3371
3457
  if (!await fs.pathExists(webAppDir)) return;
3372
3458
  await addPackageDependency({
3373
- devDependencies: ["alchemy", "dotenv"],
3459
+ devDependencies: ["alchemy"],
3374
3460
  projectDir: webAppDir
3375
3461
  });
3376
3462
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3391,7 +3477,7 @@ async function setupSolidAlchemyDeploy(projectDir, _packageManager, options) {
3391
3477
  const webAppDir = path.join(projectDir, "apps/web");
3392
3478
  if (!await fs.pathExists(webAppDir)) return;
3393
3479
  await addPackageDependency({
3394
- devDependencies: ["alchemy", "dotenv"],
3480
+ devDependencies: ["alchemy"],
3395
3481
  projectDir: webAppDir
3396
3482
  });
3397
3483
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3412,11 +3498,7 @@ async function setupSvelteAlchemyDeploy(projectDir, _packageManager, options) {
3412
3498
  const webAppDir = path.join(projectDir, "apps/web");
3413
3499
  if (!await fs.pathExists(webAppDir)) return;
3414
3500
  await addPackageDependency({
3415
- devDependencies: [
3416
- "alchemy",
3417
- "@sveltejs/adapter-cloudflare",
3418
- "dotenv"
3419
- ],
3501
+ devDependencies: ["alchemy", "@sveltejs/adapter-cloudflare"],
3420
3502
  projectDir: webAppDir
3421
3503
  });
3422
3504
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3481,7 +3563,7 @@ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, opt
3481
3563
  const webAppDir = path.join(projectDir, "apps/web");
3482
3564
  if (!await fs.pathExists(webAppDir)) return;
3483
3565
  await addPackageDependency({
3484
- devDependencies: ["alchemy", "dotenv"],
3566
+ devDependencies: ["alchemy"],
3485
3567
  projectDir: webAppDir
3486
3568
  });
3487
3569
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3502,11 +3584,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3502
3584
  const webAppDir = path.join(projectDir, "apps/web");
3503
3585
  if (!await fs.pathExists(webAppDir)) return;
3504
3586
  await addPackageDependency({
3505
- devDependencies: [
3506
- "alchemy",
3507
- "dotenv",
3508
- "@cloudflare/vite-plugin"
3509
- ],
3587
+ devDependencies: ["alchemy", "@cloudflare/vite-plugin"],
3510
3588
  projectDir: webAppDir
3511
3589
  });
3512
3590
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3561,7 +3639,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3561
3639
  //#region src/helpers/deployment/alchemy/alchemy-combined-setup.ts
3562
3640
  async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
3563
3641
  await addPackageDependency({
3564
- devDependencies: ["alchemy", "dotenv"],
3642
+ devDependencies: ["alchemy"],
3565
3643
  projectDir
3566
3644
  });
3567
3645
  const rootPkgPath = path.join(projectDir, "package.json");
@@ -3576,7 +3654,7 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
3576
3654
  await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
3577
3655
  }
3578
3656
  const serverDir = path.join(projectDir, "apps/server");
3579
- if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, packageManager);
3657
+ if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, packageManager, projectDir);
3580
3658
  const frontend = config.frontend;
3581
3659
  const isNext = frontend.includes("next");
3582
3660
  const isNuxt = frontend.includes("nuxt");
@@ -3809,6 +3887,7 @@ async function setupWebDeploy(config) {
3809
3887
  if (webDeploy !== "wrangler" && webDeploy !== "alchemy") return;
3810
3888
  if (webDeploy === "alchemy" && serverDeploy === "alchemy") {
3811
3889
  await setupCombinedAlchemyDeploy(projectDir, packageManager, config);
3890
+ await addAlchemyPackagesDependencies(projectDir);
3812
3891
  return;
3813
3892
  }
3814
3893
  const isNext = frontend.includes("next");
@@ -3832,6 +3911,7 @@ async function setupWebDeploy(config) {
3832
3911
  else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager);
3833
3912
  else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager);
3834
3913
  else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager);
3914
+ await addAlchemyPackagesDependencies(projectDir);
3835
3915
  }
3836
3916
  }
3837
3917
  async function setupWorkersWebDeploy(projectDir, pkgManager) {
@@ -3849,6 +3929,19 @@ async function setupWorkersWebDeploy(projectDir, pkgManager) {
3849
3929
  }
3850
3930
  await setupWorkersVitePlugin(projectDir);
3851
3931
  }
3932
+ async function addAlchemyPackagesDependencies(projectDir) {
3933
+ for (const packageName of [
3934
+ "packages/api",
3935
+ "packages/auth",
3936
+ "packages/db"
3937
+ ]) {
3938
+ const packageDir = path.join(projectDir, packageName);
3939
+ if (await fs.pathExists(packageDir)) await addPackageDependency({
3940
+ devDependencies: ["@cloudflare/workers-types"],
3941
+ projectDir: packageDir
3942
+ });
3943
+ }
3944
+ }
3852
3945
 
3853
3946
  //#endregion
3854
3947
  //#region src/helpers/core/add-deployment.ts
@@ -3901,18 +3994,137 @@ async function addDeploymentToProject(input) {
3901
3994
  }
3902
3995
  }
3903
3996
 
3997
+ //#endregion
3998
+ //#region src/utils/setup-catalogs.ts
3999
+ async function setupCatalogs(projectDir, options) {
4000
+ if (options.packageManager === "npm") return;
4001
+ const packagePaths = [
4002
+ "apps/server",
4003
+ "apps/web",
4004
+ "packages/api",
4005
+ "packages/db",
4006
+ "packages/auth",
4007
+ "packages/backend"
4008
+ ];
4009
+ const packagesInfo = [];
4010
+ for (const pkgPath of packagePaths) {
4011
+ const fullPath = path.join(projectDir, pkgPath);
4012
+ const pkgJsonPath = path.join(fullPath, "package.json");
4013
+ if (await fs.pathExists(pkgJsonPath)) {
4014
+ const pkgJson = await fs.readJson(pkgJsonPath);
4015
+ packagesInfo.push({
4016
+ path: fullPath,
4017
+ dependencies: pkgJson.dependencies || {},
4018
+ devDependencies: pkgJson.devDependencies || {}
4019
+ });
4020
+ }
4021
+ }
4022
+ const catalog = findDuplicateDependencies(packagesInfo, options.projectName);
4023
+ if (Object.keys(catalog).length === 0) return;
4024
+ if (options.packageManager === "bun") await setupBunCatalogs(projectDir, catalog);
4025
+ else if (options.packageManager === "pnpm") await setupPnpmCatalogs(projectDir, catalog);
4026
+ await updatePackageJsonsWithCatalogs(packagesInfo, catalog);
4027
+ }
4028
+ function findDuplicateDependencies(packagesInfo, projectName) {
4029
+ const depCount = /* @__PURE__ */ new Map();
4030
+ const projectScope = `@${projectName}/`;
4031
+ for (const pkg of packagesInfo) {
4032
+ const allDeps = {
4033
+ ...pkg.dependencies,
4034
+ ...pkg.devDependencies
4035
+ };
4036
+ for (const [depName, version] of Object.entries(allDeps)) {
4037
+ if (depName.startsWith(projectScope)) continue;
4038
+ if (version.startsWith("workspace:")) continue;
4039
+ const existing = depCount.get(depName);
4040
+ if (existing) existing.packages.push(pkg.path);
4041
+ else depCount.set(depName, {
4042
+ version,
4043
+ packages: [pkg.path]
4044
+ });
4045
+ }
4046
+ }
4047
+ const catalog = {};
4048
+ for (const [depName, info] of depCount.entries()) if (info.packages.length > 1) catalog[depName] = info.version;
4049
+ return catalog;
4050
+ }
4051
+ async function setupBunCatalogs(projectDir, catalog) {
4052
+ const rootPkgJsonPath = path.join(projectDir, "package.json");
4053
+ const rootPkgJson = await fs.readJson(rootPkgJsonPath);
4054
+ if (!rootPkgJson.workspaces) rootPkgJson.workspaces = {};
4055
+ if (Array.isArray(rootPkgJson.workspaces)) rootPkgJson.workspaces = {
4056
+ packages: rootPkgJson.workspaces,
4057
+ catalog
4058
+ };
4059
+ else if (typeof rootPkgJson.workspaces === "object") {
4060
+ if (!rootPkgJson.workspaces.catalog) rootPkgJson.workspaces.catalog = {};
4061
+ rootPkgJson.workspaces.catalog = {
4062
+ ...rootPkgJson.workspaces.catalog,
4063
+ ...catalog
4064
+ };
4065
+ }
4066
+ await fs.writeJson(rootPkgJsonPath, rootPkgJson, { spaces: 2 });
4067
+ }
4068
+ async function setupPnpmCatalogs(projectDir, catalog) {
4069
+ const workspaceYamlPath = path.join(projectDir, "pnpm-workspace.yaml");
4070
+ if (!await fs.pathExists(workspaceYamlPath)) return;
4071
+ const workspaceContent = await fs.readFile(workspaceYamlPath, "utf-8");
4072
+ const workspaceYaml = yaml.parse(workspaceContent);
4073
+ if (!workspaceYaml.catalog) workspaceYaml.catalog = {};
4074
+ workspaceYaml.catalog = {
4075
+ ...workspaceYaml.catalog,
4076
+ ...catalog
4077
+ };
4078
+ await fs.writeFile(workspaceYamlPath, yaml.stringify(workspaceYaml));
4079
+ }
4080
+ async function updatePackageJsonsWithCatalogs(packagesInfo, catalog) {
4081
+ for (const pkg of packagesInfo) {
4082
+ const pkgJsonPath = path.join(pkg.path, "package.json");
4083
+ const pkgJson = await fs.readJson(pkgJsonPath);
4084
+ let updated = false;
4085
+ if (pkgJson.dependencies) {
4086
+ for (const depName of Object.keys(pkgJson.dependencies)) if (catalog[depName]) {
4087
+ pkgJson.dependencies[depName] = "catalog:";
4088
+ updated = true;
4089
+ }
4090
+ }
4091
+ if (pkgJson.devDependencies) {
4092
+ for (const depName of Object.keys(pkgJson.devDependencies)) if (catalog[depName]) {
4093
+ pkgJson.devDependencies[depName] = "catalog:";
4094
+ updated = true;
4095
+ }
4096
+ }
4097
+ if (updated) await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
4098
+ }
4099
+ }
4100
+
3904
4101
  //#endregion
3905
4102
  //#region src/helpers/addons/examples-setup.ts
3906
4103
  async function setupExamples(config) {
3907
- const { examples, frontend, backend, projectDir } = config;
4104
+ const { examples, frontend, backend, projectDir, orm } = config;
3908
4105
  if (backend === "convex" || !examples || examples.length === 0 || examples[0] === "none") return;
4106
+ const apiDir = path.join(projectDir, "packages/api");
4107
+ if (await fs.pathExists(apiDir) && backend !== "none") {
4108
+ if (orm === "drizzle") await addPackageDependency({
4109
+ dependencies: ["drizzle-orm"],
4110
+ projectDir: apiDir
4111
+ });
4112
+ else if (orm === "prisma") await addPackageDependency({
4113
+ dependencies: ["@prisma/client"],
4114
+ projectDir: apiDir
4115
+ });
4116
+ else if (orm === "mongoose") await addPackageDependency({
4117
+ dependencies: ["mongoose"],
4118
+ projectDir: apiDir
4119
+ });
4120
+ }
3909
4121
  if (examples.includes("ai")) {
3910
4122
  const webClientDir = path.join(projectDir, "apps/web");
3911
4123
  const nativeClientDir = path.join(projectDir, "apps/native");
3912
- const serverDir = path.join(projectDir, "apps/server");
4124
+ const apiDir$1 = path.join(projectDir, "packages/api");
3913
4125
  const webClientDirExists = await fs.pathExists(webClientDir);
3914
4126
  const nativeClientDirExists = await fs.pathExists(nativeClientDir);
3915
- const serverDirExists = await fs.pathExists(serverDir);
4127
+ const apiDirExists = await fs.pathExists(apiDir$1);
3916
4128
  const hasNuxt = frontend.includes("nuxt");
3917
4129
  const hasSvelte = frontend.includes("svelte");
3918
4130
  const hasReactWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next") || frontend.includes("tanstack-start");
@@ -3933,9 +4145,13 @@ async function setupExamples(config) {
3933
4145
  dependencies: ["ai", "@ai-sdk/react"],
3934
4146
  projectDir: nativeClientDir
3935
4147
  });
3936
- if (serverDirExists && backend !== "none") await addPackageDependency({
4148
+ if (apiDirExists && backend !== "none") await addPackageDependency({
3937
4149
  dependencies: ["ai", "@ai-sdk/google"],
3938
- projectDir: serverDir
4150
+ projectDir: apiDir$1
4151
+ });
4152
+ if (backend === "self" && webClientDirExists) await addPackageDependency({
4153
+ dependencies: ["ai", "@ai-sdk/google"],
4154
+ projectDir: webClientDir
3939
4155
  });
3940
4156
  }
3941
4157
  }
@@ -4058,23 +4274,26 @@ async function setupApi(config) {
4058
4274
  const serverDir = path.join(projectDir, "apps/server");
4059
4275
  const webDirExists = await fs.pathExists(webDir);
4060
4276
  const nativeDirExists = await fs.pathExists(nativeDir);
4061
- const serverDirExists = await fs.pathExists(serverDir);
4277
+ await fs.pathExists(serverDir);
4062
4278
  const frontendType = getFrontendType(frontend);
4063
4279
  if (!isConvex && api !== "none") {
4064
4280
  const apiDeps = getApiDependencies(api, frontendType);
4065
- if (serverDirExists && apiDeps.server) {
4281
+ const apiPackageDir = path.join(projectDir, "packages/api");
4282
+ if (apiDeps.server) {
4066
4283
  await addPackageDependency({
4067
4284
  dependencies: apiDeps.server.dependencies,
4068
- projectDir: serverDir
4285
+ projectDir: apiPackageDir
4069
4286
  });
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
4287
+ if (backend === "self" && webDirExists) await addPackageDependency({
4288
+ dependencies: apiDeps.server.dependencies,
4289
+ projectDir: webDir
4290
+ });
4291
+ if (backend === "self") {
4292
+ const frameworkDeps = [];
4293
+ if (frontend.includes("next")) frameworkDeps.push("next");
4294
+ if (frameworkDeps.length > 0) await addPackageDependency({
4295
+ dependencies: frameworkDeps,
4296
+ projectDir: apiPackageDir
4078
4297
  });
4079
4298
  }
4080
4299
  }
@@ -4120,7 +4339,7 @@ async function setupApi(config) {
4120
4339
  //#endregion
4121
4340
  //#region src/helpers/core/backend-setup.ts
4122
4341
  async function setupBackendDependencies(config) {
4123
- const { backend, runtime, api, projectDir } = config;
4342
+ const { backend, runtime, api, auth, examples, projectDir } = config;
4124
4343
  if (backend === "convex") return;
4125
4344
  const framework = backend;
4126
4345
  const serverDir = path.join(projectDir, "apps/server");
@@ -4128,27 +4347,23 @@ async function setupBackendDependencies(config) {
4128
4347
  const devDependencies = [];
4129
4348
  if (framework === "hono") {
4130
4349
  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
- }
4350
+ if (runtime === "node") dependencies.push("@hono/node-server");
4136
4351
  } else if (framework === "elysia") {
4137
4352
  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
- }
4353
+ if (runtime === "node") dependencies.push("@elysiajs/node");
4143
4354
  } else if (framework === "express") {
4144
4355
  dependencies.push("express", "cors");
4145
4356
  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");
4357
+ } else if (framework === "fastify") dependencies.push("fastify", "@fastify/cors");
4358
+ if (api === "trpc") {
4359
+ dependencies.push("@trpc/server");
4360
+ if (framework === "hono") dependencies.push("@hono/trpc-server");
4361
+ else if (framework === "elysia") dependencies.push("@elysiajs/trpc");
4362
+ } else if (api === "orpc") dependencies.push("@orpc/server", "@orpc/openapi", "@orpc/zod");
4363
+ if (auth === "better-auth") dependencies.push("better-auth");
4364
+ if (examples.includes("ai")) dependencies.push("ai", "@ai-sdk/google");
4365
+ if (runtime === "node") devDependencies.push("tsx", "@types/node");
4366
+ else if (runtime === "bun") devDependencies.push("@types/bun");
4152
4367
  if (dependencies.length > 0 || devDependencies.length > 0) await addPackageDependency({
4153
4368
  dependencies,
4154
4369
  devDependencies,
@@ -4166,7 +4381,7 @@ async function setupAuth(config) {
4166
4381
  const nativeDir = path.join(projectDir, "apps/native");
4167
4382
  const clientDirExists = await fs.pathExists(clientDir);
4168
4383
  const nativeDirExists = await fs.pathExists(nativeDir);
4169
- const serverDirExists = await fs.pathExists(serverDir);
4384
+ await fs.pathExists(serverDir);
4170
4385
  try {
4171
4386
  if (backend === "convex") {
4172
4387
  if (auth === "clerk" && clientDirExists) {
@@ -4222,9 +4437,11 @@ async function setupAuth(config) {
4222
4437
  });
4223
4438
  return;
4224
4439
  }
4225
- if (serverDirExists && auth === "better-auth") await addPackageDependency({
4440
+ const authPackageDir = path.join(projectDir, "packages/auth");
4441
+ const authPackageDirExists = await fs.pathExists(authPackageDir);
4442
+ if (authPackageDirExists && auth === "better-auth") await addPackageDependency({
4226
4443
  dependencies: ["better-auth"],
4227
- projectDir: serverDir
4444
+ projectDir: authPackageDir
4228
4445
  });
4229
4446
  if (frontend.some((f) => [
4230
4447
  "react-router",
@@ -4246,9 +4463,9 @@ async function setupAuth(config) {
4246
4463
  dependencies: ["better-auth", "@better-auth/expo"],
4247
4464
  projectDir: nativeDir
4248
4465
  });
4249
- if (serverDirExists) await addPackageDependency({
4466
+ if (authPackageDirExists) await addPackageDependency({
4250
4467
  dependencies: ["@better-auth/expo"],
4251
- projectDir: serverDir
4468
+ projectDir: authPackageDir
4252
4469
  });
4253
4470
  }
4254
4471
  }
@@ -4267,6 +4484,34 @@ function generateAuthSecret(length = 32) {
4267
4484
 
4268
4485
  //#endregion
4269
4486
  //#region src/helpers/core/env-setup.ts
4487
+ function getClientServerVar(frontend, backend) {
4488
+ const hasNextJs = frontend.includes("next");
4489
+ const hasNuxt = frontend.includes("nuxt");
4490
+ const hasSvelte = frontend.includes("svelte");
4491
+ if (backend === "self") return {
4492
+ key: "",
4493
+ value: "",
4494
+ write: false
4495
+ };
4496
+ let key = "VITE_SERVER_URL";
4497
+ if (hasNextJs) key = "NEXT_PUBLIC_SERVER_URL";
4498
+ else if (hasNuxt) key = "NUXT_PUBLIC_SERVER_URL";
4499
+ else if (hasSvelte) key = "PUBLIC_SERVER_URL";
4500
+ return {
4501
+ key,
4502
+ value: "http://localhost:3000",
4503
+ write: true
4504
+ };
4505
+ }
4506
+ function getConvexVar(frontend) {
4507
+ const hasNextJs = frontend.includes("next");
4508
+ const hasNuxt = frontend.includes("nuxt");
4509
+ const hasSvelte = frontend.includes("svelte");
4510
+ if (hasNextJs) return "NEXT_PUBLIC_CONVEX_URL";
4511
+ if (hasNuxt) return "NUXT_PUBLIC_CONVEX_URL";
4512
+ if (hasSvelte) return "PUBLIC_CONVEX_URL";
4513
+ return "VITE_CONVEX_URL";
4514
+ }
4270
4515
  async function addEnvVariablesToFile(filePath, variables) {
4271
4516
  await fs.ensureDir(path.dirname(filePath));
4272
4517
  let envContent = "";
@@ -4325,22 +4570,13 @@ async function setupEnvironmentVariables(config) {
4325
4570
  if (hasReactRouter || hasTanStackRouter || hasTanStackStart || hasNextJs || hasNuxt || hasSolid || hasSvelte) {
4326
4571
  const clientDir = path.join(projectDir, "apps/web");
4327
4572
  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
- }
4573
+ const baseVar = getClientServerVar(frontend, backend);
4574
+ const envVarName = backend === "convex" ? getConvexVar(frontend) : baseVar.key;
4575
+ const serverUrl = backend === "convex" ? "https://<YOUR_CONVEX_URL>" : baseVar.value;
4340
4576
  const clientVars = [{
4341
4577
  key: envVarName,
4342
4578
  value: serverUrl,
4343
- condition: true
4579
+ condition: backend === "convex" ? true : baseVar.write
4344
4580
  }];
4345
4581
  if (backend === "convex" && auth === "clerk") {
4346
4582
  if (hasNextJs) clientVars.push({
@@ -4431,10 +4667,9 @@ async function setupEnvironmentVariables(config) {
4431
4667
  return;
4432
4668
  }
4433
4669
  const serverDir = path.join(projectDir, "apps/server");
4434
- if (!await fs.pathExists(serverDir)) return;
4435
- const envPath = path.join(serverDir, ".env");
4436
4670
  let corsOrigin = "http://localhost:3001";
4437
- if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
4671
+ if (backend === "self") corsOrigin = "http://localhost:3001";
4672
+ else if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
4438
4673
  let databaseUrl = null;
4439
4674
  if (database !== "none" && dbSetup === "none") switch (database) {
4440
4675
  case "postgres":
@@ -4448,15 +4683,13 @@ async function setupEnvironmentVariables(config) {
4448
4683
  break;
4449
4684
  case "sqlite":
4450
4685
  if (config.runtime === "workers") databaseUrl = "http://127.0.0.1:8080";
4451
- else databaseUrl = "file:./local.db";
4686
+ else {
4687
+ const dbAppDir = backend === "self" ? "apps/web" : "apps/server";
4688
+ databaseUrl = `file:${path.join(config.projectDir, dbAppDir, "local.db")}`;
4689
+ }
4452
4690
  break;
4453
4691
  }
4454
4692
  const serverVars = [
4455
- {
4456
- key: "CORS_ORIGIN",
4457
- value: corsOrigin,
4458
- condition: true
4459
- },
4460
4693
  {
4461
4694
  key: "BETTER_AUTH_SECRET",
4462
4695
  value: generateAuthSecret(),
@@ -4464,19 +4697,9 @@ async function setupEnvironmentVariables(config) {
4464
4697
  },
4465
4698
  {
4466
4699
  key: "BETTER_AUTH_URL",
4467
- value: "http://localhost:3000",
4700
+ value: backend === "self" ? "http://localhost:3001" : "http://localhost:3000",
4468
4701
  condition: !!auth
4469
4702
  },
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
4703
  {
4481
4704
  key: "POLAR_ACCESS_TOKEN",
4482
4705
  value: "",
@@ -4486,9 +4709,27 @@ async function setupEnvironmentVariables(config) {
4486
4709
  key: "POLAR_SUCCESS_URL",
4487
4710
  value: `${corsOrigin}/success?checkout_id={CHECKOUT_ID}`,
4488
4711
  condition: config.payments === "polar"
4712
+ },
4713
+ {
4714
+ key: "CORS_ORIGIN",
4715
+ value: corsOrigin,
4716
+ condition: true
4717
+ },
4718
+ {
4719
+ key: "GOOGLE_GENERATIVE_AI_API_KEY",
4720
+ value: "",
4721
+ condition: examples?.includes("ai") || false
4722
+ },
4723
+ {
4724
+ key: "DATABASE_URL",
4725
+ value: databaseUrl,
4726
+ condition: database !== "none" && dbSetup === "none"
4489
4727
  }
4490
4728
  ];
4491
- await addEnvVariablesToFile(envPath, serverVars);
4729
+ if (backend === "self") {
4730
+ const webDir = path.join(projectDir, "apps/web");
4731
+ if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverVars);
4732
+ } else if (await fs.pathExists(serverDir)) await addEnvVariablesToFile(path.join(serverDir, ".env"), serverVars);
4492
4733
  const isUnifiedAlchemy = webDeploy === "alchemy" && serverDeploy === "alchemy";
4493
4734
  const isIndividualAlchemy = webDeploy === "alchemy" || serverDeploy === "alchemy";
4494
4735
  if (isUnifiedAlchemy) {
@@ -4508,12 +4749,15 @@ async function setupEnvironmentVariables(config) {
4508
4749
  }]);
4509
4750
  }
4510
4751
  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"), [{
4752
+ const serverAlchemyVars = [{
4513
4753
  key: "ALCHEMY_PASSWORD",
4514
4754
  value: "please-change-this",
4515
4755
  condition: true
4516
- }]);
4756
+ }];
4757
+ if (backend === "self") {
4758
+ const webDir = path.join(projectDir, "apps/web");
4759
+ if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverAlchemyVars);
4760
+ } else await addEnvVariablesToFile(path.join(serverDir, ".env"), serverAlchemyVars);
4517
4761
  }
4518
4762
  }
4519
4763
  }
@@ -4521,9 +4765,10 @@ async function setupEnvironmentVariables(config) {
4521
4765
  //#endregion
4522
4766
  //#region src/helpers/database-providers/d1-setup.ts
4523
4767
  async function setupCloudflareD1(config) {
4524
- const { projectDir, serverDeploy, orm } = config;
4768
+ const { projectDir, serverDeploy, orm, backend } = config;
4525
4769
  if (serverDeploy === "wrangler") {
4526
- const envPath = path.join(projectDir, "apps/server", ".env");
4770
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
4771
+ const envPath = path.join(projectDir, targetApp, ".env");
4527
4772
  const variables = [
4528
4773
  {
4529
4774
  key: "CLOUDFLARE_ACCOUNT_ID",
@@ -4546,16 +4791,17 @@ async function setupCloudflareD1(config) {
4546
4791
  } catch (_err) {}
4547
4792
  }
4548
4793
  if ((serverDeploy === "wrangler" || serverDeploy === "alchemy") && orm === "prisma") {
4549
- const envPath = path.join(projectDir, "apps/server", ".env");
4794
+ const targetApp2 = backend === "self" ? "apps/web" : "apps/server";
4795
+ const envPath = path.join(projectDir, targetApp2, ".env");
4550
4796
  const variables = [{
4551
4797
  key: "DATABASE_URL",
4552
- value: "file:./local.db",
4798
+ value: `file:${path.join(projectDir, "apps/server", "local.db")}`,
4553
4799
  condition: true
4554
4800
  }];
4555
4801
  try {
4556
4802
  await addEnvVariablesToFile(envPath, variables);
4557
4803
  } catch (_err) {}
4558
- const serverDir = path.join(projectDir, "apps/server");
4804
+ const serverDir = path.join(projectDir, backend === "self" ? "apps/web" : "apps/server");
4559
4805
  await addPackageDependency({
4560
4806
  dependencies: ["@prisma/adapter-d1"],
4561
4807
  projectDir: serverDir
@@ -4606,14 +4852,13 @@ async function commandExists(command) {
4606
4852
  //#endregion
4607
4853
  //#region src/helpers/database-providers/mongodb-atlas-setup.ts
4608
4854
  async function checkAtlasCLI() {
4609
- const s = spinner();
4610
- s.start("Checking for MongoDB Atlas CLI...");
4611
4855
  try {
4612
4856
  const exists = await commandExists("atlas");
4613
- s.stop(exists ? "MongoDB Atlas CLI found" : pc.yellow("MongoDB Atlas CLI not found"));
4857
+ if (exists) log.info("MongoDB Atlas CLI found");
4858
+ else log.warn(pc.yellow("MongoDB Atlas CLI not found"));
4614
4859
  return exists;
4615
4860
  } catch (_error) {
4616
- s.stop(pc.red("Error checking MongoDB Atlas CLI"));
4861
+ log.error(pc.red("Error checking MongoDB Atlas CLI"));
4617
4862
  return false;
4618
4863
  }
4619
4864
  }
@@ -4624,12 +4869,13 @@ async function initMongoDBAtlas(serverDir) {
4624
4869
  log.info(pc.yellow("Please install it from: https://www.mongodb.com/docs/atlas/cli/current/install-atlas-cli/"));
4625
4870
  return null;
4626
4871
  }
4627
- log.info(pc.blue("Running MongoDB Atlas setup..."));
4872
+ log.info("Running MongoDB Atlas setup...");
4628
4873
  await execa("atlas", ["deployments", "setup"], {
4629
4874
  cwd: serverDir,
4875
+ shell: true,
4630
4876
  stdio: "inherit"
4631
4877
  });
4632
- log.info(pc.green("MongoDB Atlas deployment ready"));
4878
+ log.success("MongoDB Atlas deployment ready");
4633
4879
  const connectionString = await text({
4634
4880
  message: "Enter your MongoDB connection string:",
4635
4881
  placeholder: "mongodb+srv://username:password@cluster.mongodb.net/database",
@@ -4648,9 +4894,10 @@ async function initMongoDBAtlas(serverDir) {
4648
4894
  return null;
4649
4895
  }
4650
4896
  }
4651
- async function writeEnvFile$3(projectDir, config) {
4897
+ async function writeEnvFile$3(projectDir, backend, config) {
4652
4898
  try {
4653
- const envPath = path.join(projectDir, "apps/server", ".env");
4899
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
4900
+ const envPath = path.join(projectDir, targetApp, ".env");
4654
4901
  const variables = [{
4655
4902
  key: "DATABASE_URL",
4656
4903
  value: config?.connectionString ?? "mongodb://localhost:27017/mydb",
@@ -4679,16 +4926,14 @@ ${pc.green("MongoDB Atlas Manual Setup Instructions:")}
4679
4926
  `);
4680
4927
  }
4681
4928
  async function setupMongoDBAtlas(config, cliInput) {
4682
- const { projectDir } = config;
4929
+ const { projectDir, backend } = config;
4683
4930
  const manualDb = cliInput?.manualDb ?? false;
4684
- const mainSpinner = spinner();
4685
- mainSpinner.start("Setting up MongoDB Atlas...");
4686
- const serverDir = path.join(projectDir, "apps/server");
4931
+ const serverDir = path.join(projectDir, "packages/db");
4687
4932
  try {
4688
4933
  await fs.ensureDir(serverDir);
4689
4934
  if (manualDb) {
4690
- mainSpinner.stop("MongoDB Atlas manual setup selected");
4691
- await writeEnvFile$3(projectDir);
4935
+ log.info("MongoDB Atlas manual setup selected");
4936
+ await writeEnvFile$3(projectDir, backend);
4692
4937
  displayManualSetupInstructions$3();
4693
4938
  return;
4694
4939
  }
@@ -4707,26 +4952,24 @@ async function setupMongoDBAtlas(config, cliInput) {
4707
4952
  });
4708
4953
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
4709
4954
  if (mode === "manual") {
4710
- mainSpinner.stop("MongoDB Atlas manual setup selected");
4711
- await writeEnvFile$3(projectDir);
4955
+ log.info("MongoDB Atlas manual setup selected");
4956
+ await writeEnvFile$3(projectDir, backend);
4712
4957
  displayManualSetupInstructions$3();
4713
4958
  return;
4714
4959
  }
4715
- mainSpinner.stop("MongoDB Atlas setup ready");
4716
4960
  const config$1 = await initMongoDBAtlas(serverDir);
4717
4961
  if (config$1) {
4718
- await writeEnvFile$3(projectDir, config$1);
4962
+ await writeEnvFile$3(projectDir, backend, config$1);
4719
4963
  log.success(pc.green("MongoDB Atlas setup complete! Connection saved to .env file."));
4720
4964
  } else {
4721
4965
  log.warn(pc.yellow("Falling back to local MongoDB configuration"));
4722
- await writeEnvFile$3(projectDir);
4966
+ await writeEnvFile$3(projectDir, backend);
4723
4967
  displayManualSetupInstructions$3();
4724
4968
  }
4725
4969
  } catch (error) {
4726
- mainSpinner.stop(pc.red("MongoDB Atlas setup failed"));
4727
4970
  consola.error(pc.red(`Error during MongoDB Atlas setup: ${error instanceof Error ? error.message : String(error)}`));
4728
4971
  try {
4729
- await writeEnvFile$3(projectDir);
4972
+ await writeEnvFile$3(projectDir, backend);
4730
4973
  displayManualSetupInstructions$3();
4731
4974
  } catch {}
4732
4975
  }
@@ -4783,7 +5026,7 @@ async function executeNeonCommand(packageManager, commandArgsString, spinnerText
4783
5026
  }
4784
5027
  async function createNeonProject(projectName, regionId, packageManager) {
4785
5028
  try {
4786
- const commandArgsString = `neonctl projects create --name ${projectName} --region-id ${regionId} --output json`;
5029
+ const commandArgsString = `neonctl@latest projects create --name ${projectName} --region-id ${regionId} --output json`;
4787
5030
  const { stdout } = await executeNeonCommand(packageManager, commandArgsString, `Creating Neon project "${projectName}"...`);
4788
5031
  const response = JSON.parse(stdout);
4789
5032
  if (response.project && response.connection_uris && response.connection_uris.length > 0) {
@@ -4803,8 +5046,9 @@ async function createNeonProject(projectName, regionId, packageManager) {
4803
5046
  consola$1.error(pc.red("Failed to create Neon project"));
4804
5047
  }
4805
5048
  }
4806
- async function writeEnvFile$2(projectDir, config) {
4807
- const envPath = path.join(projectDir, "apps/server", ".env");
5049
+ async function writeEnvFile$2(projectDir, backend, config) {
5050
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5051
+ const envPath = path.join(projectDir, targetApp, ".env");
4808
5052
  const variables = [{
4809
5053
  key: "DATABASE_URL",
4810
5054
  value: config?.connectionString ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
@@ -4813,16 +5057,17 @@ async function writeEnvFile$2(projectDir, config) {
4813
5057
  await addEnvVariablesToFile(envPath, variables);
4814
5058
  return true;
4815
5059
  }
4816
- async function setupWithNeonDb(projectDir, packageManager) {
5060
+ async function setupWithNeonDb(projectDir, packageManager, backend) {
4817
5061
  try {
4818
5062
  const s = spinner();
4819
5063
  s.start("Creating Neon database using neondb...");
4820
- const serverDir = path.join(projectDir, "apps/server");
4821
- await fs.ensureDir(serverDir);
4822
- const packageCmd = getPackageExecutionCommand(packageManager, "neondb --yes");
5064
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5065
+ const targetDir = path.join(projectDir, targetApp);
5066
+ await fs.ensureDir(targetDir);
5067
+ const packageCmd = getPackageExecutionCommand(packageManager, "neondb@latest --yes");
4823
5068
  await execa(packageCmd, {
4824
5069
  shell: true,
4825
- cwd: serverDir
5070
+ cwd: targetDir
4826
5071
  });
4827
5072
  s.stop(pc.green("Neon database created successfully!"));
4828
5073
  return true;
@@ -4831,23 +5076,23 @@ async function setupWithNeonDb(projectDir, packageManager) {
4831
5076
  throw error;
4832
5077
  }
4833
5078
  }
4834
- function displayManualSetupInstructions$2() {
5079
+ function displayManualSetupInstructions$2(target) {
4835
5080
  log.info(`Manual Neon PostgreSQL Setup Instructions:
4836
5081
 
4837
5082
  1. Visit https://neon.tech and create an account
4838
5083
  2. Create a new project from the dashboard
4839
5084
  3. Get your connection string
4840
- 4. Add the database URL to the .env file in apps/server/.env
5085
+ 4. Add the database URL to the .env file in ${target}/.env
4841
5086
 
4842
5087
  DATABASE_URL="your_connection_string"`);
4843
5088
  }
4844
5089
  async function setupNeonPostgres(config, cliInput) {
4845
- const { packageManager, projectDir } = config;
5090
+ const { packageManager, projectDir, backend } = config;
4846
5091
  const manualDb = cliInput?.manualDb ?? false;
4847
5092
  try {
4848
5093
  if (manualDb) {
4849
- await writeEnvFile$2(projectDir);
4850
- displayManualSetupInstructions$2();
5094
+ await writeEnvFile$2(projectDir, backend);
5095
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
4851
5096
  return;
4852
5097
  }
4853
5098
  const mode = await select({
@@ -4865,8 +5110,8 @@ async function setupNeonPostgres(config, cliInput) {
4865
5110
  });
4866
5111
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
4867
5112
  if (mode === "manual") {
4868
- await writeEnvFile$2(projectDir);
4869
- displayManualSetupInstructions$2();
5113
+ await writeEnvFile$2(projectDir, backend);
5114
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
4870
5115
  return;
4871
5116
  }
4872
5117
  const setupMethod = await select({
@@ -4883,7 +5128,7 @@ async function setupNeonPostgres(config, cliInput) {
4883
5128
  initialValue: "neondb"
4884
5129
  });
4885
5130
  if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
4886
- if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager);
5131
+ if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager, backend);
4887
5132
  else {
4888
5133
  const suggestedProjectName = path.basename(projectDir);
4889
5134
  const projectName = await text({
@@ -4901,22 +5146,22 @@ async function setupNeonPostgres(config, cliInput) {
4901
5146
  if (!neonConfig) throw new Error("Failed to create project - couldn't get connection information");
4902
5147
  const finalSpinner = spinner();
4903
5148
  finalSpinner.start("Configuring database connection");
4904
- await fs.ensureDir(path.join(projectDir, "apps/server"));
4905
- await writeEnvFile$2(projectDir, neonConfig);
5149
+ await writeEnvFile$2(projectDir, backend, neonConfig);
4906
5150
  finalSpinner.stop("Neon database configured!");
4907
5151
  }
4908
5152
  } catch (error) {
4909
5153
  if (error instanceof Error) consola$1.error(pc.red(error.message));
4910
- await writeEnvFile$2(projectDir);
4911
- displayManualSetupInstructions$2();
5154
+ await writeEnvFile$2(projectDir, backend);
5155
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
4912
5156
  }
4913
5157
  }
4914
5158
 
4915
5159
  //#endregion
4916
5160
  //#region src/helpers/database-providers/planetscale-setup.ts
4917
5161
  async function setupPlanetScale(config) {
4918
- const { projectDir, database, orm } = config;
4919
- const envPath = path.join(projectDir, "apps/server", ".env");
5162
+ const { projectDir, database, orm, backend } = config;
5163
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5164
+ const envPath = path.join(projectDir, targetApp, ".env");
4920
5165
  if (database === "mysql" && orm === "drizzle") {
4921
5166
  const variables = [
4922
5167
  {
@@ -4940,7 +5185,7 @@ async function setupPlanetScale(config) {
4940
5185
  condition: true
4941
5186
  }
4942
5187
  ];
4943
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5188
+ await fs.ensureDir(path.join(projectDir, targetApp));
4944
5189
  await addEnvVariablesToFile(envPath, variables);
4945
5190
  }
4946
5191
  if (database === "postgres" && orm === "prisma") {
@@ -4949,7 +5194,7 @@ async function setupPlanetScale(config) {
4949
5194
  value: "postgresql://username:password@host/database?sslaccept=strict",
4950
5195
  condition: true
4951
5196
  }];
4952
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5197
+ await fs.ensureDir(path.join(projectDir, targetApp));
4953
5198
  await addEnvVariablesToFile(envPath, variables);
4954
5199
  }
4955
5200
  if (database === "postgres" && orm === "drizzle") {
@@ -4958,7 +5203,7 @@ async function setupPlanetScale(config) {
4958
5203
  value: "postgresql://username:password@host/database?sslmode=verify-full",
4959
5204
  condition: true
4960
5205
  }];
4961
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5206
+ await fs.ensureDir(path.join(projectDir, targetApp));
4962
5207
  await addEnvVariablesToFile(envPath, variables);
4963
5208
  }
4964
5209
  if (database === "mysql" && orm === "prisma") {
@@ -4967,7 +5212,7 @@ async function setupPlanetScale(config) {
4967
5212
  value: "mysql://username:password@host/database?sslaccept=strict",
4968
5213
  condition: true
4969
5214
  }];
4970
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5215
+ await fs.ensureDir(path.join(projectDir, targetApp));
4971
5216
  await addEnvVariablesToFile(envPath, variables);
4972
5217
  }
4973
5218
  }
@@ -5037,7 +5282,7 @@ async function initPrismaDatabase(serverDir, packageManager) {
5037
5282
  try {
5038
5283
  const prismaDir = path.join(serverDir, "prisma");
5039
5284
  await fs.ensureDir(prismaDir);
5040
- log.info("Starting Prisma PostgreSQL setup. Please follow the instructions below:");
5285
+ log.info("Starting Prisma PostgreSQL setup.");
5041
5286
  const prismaInitCommand = getPackageExecutionCommand(packageManager, "prisma init --db");
5042
5287
  await execa(prismaInitCommand, {
5043
5288
  cwd: serverDir,
@@ -5059,9 +5304,10 @@ async function initPrismaDatabase(serverDir, packageManager) {
5059
5304
  return null;
5060
5305
  }
5061
5306
  }
5062
- async function writeEnvFile$1(projectDir, config) {
5307
+ async function writeEnvFile$1(projectDir, backend, config) {
5063
5308
  try {
5064
- const envPath = path.join(projectDir, "apps/server", ".env");
5309
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5310
+ const envPath = path.join(projectDir, targetApp, ".env");
5065
5311
  const variables = [{
5066
5312
  key: "DATABASE_URL",
5067
5313
  value: config?.databaseUrl ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
@@ -5077,31 +5323,32 @@ async function writeEnvFile$1(projectDir, config) {
5077
5323
  consola$1.error("Failed to update environment configuration");
5078
5324
  }
5079
5325
  }
5080
- async function addDotenvImportToPrismaConfig(projectDir) {
5326
+ async function addDotenvImportToPrismaConfig(projectDir, backend) {
5081
5327
  try {
5082
- const prismaConfigPath = path.join(projectDir, "apps/server/prisma.config.ts");
5328
+ const prismaConfigPath = path.join(projectDir, "packages/db/prisma.config.ts");
5083
5329
  let content = await fs.readFile(prismaConfigPath, "utf8");
5084
- content = `import "dotenv/config";\n${content}`;
5330
+ content = `import dotenv from "dotenv";\ndotenv.config({ path: "${backend === "self" ? "../../apps/web/.env" : "../../apps/server/.env"}" });\n${content}`;
5085
5331
  await fs.writeFile(prismaConfigPath, content);
5086
5332
  } catch (_error) {
5087
5333
  consola$1.error("Failed to update prisma.config.ts");
5088
5334
  }
5089
5335
  }
5090
- function displayManualSetupInstructions$1() {
5336
+ function displayManualSetupInstructions$1(target) {
5091
5337
  log.info(`Manual Prisma PostgreSQL Setup Instructions:
5092
5338
 
5093
5339
  1. Visit https://console.prisma.io and create an account
5094
5340
  2. Create a new PostgreSQL database from the dashboard
5095
5341
  3. Get your database URL
5096
- 4. Add the database URL to the .env file in apps/server/.env
5342
+ 4. Add the database URL to the .env file in ${target}/.env
5097
5343
 
5098
5344
  DATABASE_URL="your_database_url"`);
5099
5345
  }
5100
- async function addPrismaAccelerateExtension(serverDir) {
5346
+ async function addPrismaAccelerateExtension(projectDir) {
5101
5347
  try {
5348
+ const dbPackageDir = path.join(projectDir, "packages/db");
5102
5349
  await addPackageDependency({
5103
5350
  dependencies: ["@prisma/extension-accelerate"],
5104
- projectDir: serverDir
5351
+ projectDir: dbPackageDir
5105
5352
  });
5106
5353
  return true;
5107
5354
  } catch (_error) {
@@ -5110,14 +5357,14 @@ async function addPrismaAccelerateExtension(serverDir) {
5110
5357
  }
5111
5358
  }
5112
5359
  async function setupPrismaPostgres(config, cliInput) {
5113
- const { packageManager, projectDir, orm } = config;
5360
+ const { packageManager, projectDir, orm, backend } = config;
5114
5361
  const manualDb = cliInput?.manualDb ?? false;
5115
- const serverDir = path.join(projectDir, "apps/server");
5362
+ const dbDir = path.join(projectDir, "packages/db");
5116
5363
  try {
5117
- await fs.ensureDir(serverDir);
5364
+ await fs.ensureDir(dbDir);
5118
5365
  if (manualDb) {
5119
- await writeEnvFile$1(projectDir);
5120
- displayManualSetupInstructions$1();
5366
+ await writeEnvFile$1(projectDir, backend);
5367
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5121
5368
  return;
5122
5369
  }
5123
5370
  const mode = await select({
@@ -5135,8 +5382,8 @@ async function setupPrismaPostgres(config, cliInput) {
5135
5382
  });
5136
5383
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5137
5384
  if (mode === "manual") {
5138
- await writeEnvFile$1(projectDir);
5139
- displayManualSetupInstructions$1();
5385
+ await writeEnvFile$1(projectDir, backend);
5386
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5140
5387
  return;
5141
5388
  }
5142
5389
  const setupOptions = [{
@@ -5156,26 +5403,26 @@ async function setupPrismaPostgres(config, cliInput) {
5156
5403
  });
5157
5404
  if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
5158
5405
  let prismaConfig = null;
5159
- if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(serverDir, packageManager, orm);
5160
- else prismaConfig = await initPrismaDatabase(serverDir, packageManager);
5406
+ if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(dbDir, packageManager, orm);
5407
+ else prismaConfig = await initPrismaDatabase(dbDir, packageManager);
5161
5408
  if (prismaConfig) {
5162
- await writeEnvFile$1(projectDir, prismaConfig);
5409
+ await writeEnvFile$1(projectDir, backend, prismaConfig);
5163
5410
  if (orm === "prisma") {
5164
- await addDotenvImportToPrismaConfig(projectDir);
5165
- await addPrismaAccelerateExtension(serverDir);
5411
+ await addDotenvImportToPrismaConfig(projectDir, backend);
5412
+ await addPrismaAccelerateExtension(projectDir);
5166
5413
  }
5167
5414
  const connectionType = orm === "drizzle" ? "direct connection" : "Prisma Accelerate";
5168
5415
  log.success(pc.green(`Prisma Postgres database configured successfully with ${connectionType}!`));
5169
5416
  if (prismaConfig.claimUrl) log.info(pc.blue(`Claim URL saved to .env: ${prismaConfig.claimUrl}`));
5170
5417
  } else {
5171
- await writeEnvFile$1(projectDir);
5172
- displayManualSetupInstructions$1();
5418
+ await writeEnvFile$1(projectDir, backend);
5419
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5173
5420
  }
5174
5421
  } catch (error) {
5175
5422
  consola$1.error(pc.red(`Error during Prisma Postgres setup: ${error instanceof Error ? error.message : String(error)}`));
5176
5423
  try {
5177
- await writeEnvFile$1(projectDir);
5178
- displayManualSetupInstructions$1();
5424
+ await writeEnvFile$1(projectDir, backend);
5425
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5179
5426
  } catch {}
5180
5427
  log.info("Setup completed with manual configuration required.");
5181
5428
  }
@@ -5183,9 +5430,10 @@ async function setupPrismaPostgres(config, cliInput) {
5183
5430
 
5184
5431
  //#endregion
5185
5432
  //#region src/helpers/database-providers/supabase-setup.ts
5186
- async function writeSupabaseEnvFile(projectDir, databaseUrl) {
5433
+ async function writeSupabaseEnvFile(projectDir, backend, databaseUrl) {
5187
5434
  try {
5188
- const envPath = path.join(projectDir, "apps/server", ".env");
5435
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5436
+ const envPath = path.join(projectDir, targetApp, ".env");
5189
5437
  const dbUrlToUse = databaseUrl || "postgresql://postgres:postgres@127.0.0.1:54322/postgres";
5190
5438
  await addEnvVariablesToFile(envPath, [{
5191
5439
  key: "DATABASE_URL",
@@ -5262,23 +5510,23 @@ function displayManualSupabaseInstructions(output) {
5262
5510
  log.info(`"Manual Supabase Setup Instructions:"
5263
5511
  1. Ensure Docker is installed and running.
5264
5512
  2. Install the Supabase CLI (e.g., \`npm install -g supabase\`).
5265
- 3. Run \`supabase init\` in your project's \`apps/server\` directory.
5266
- 4. Run \`supabase start\` in your project's \`apps/server\` directory.
5513
+ 3. Run \`supabase init\` in your project's \`packages/db\` directory.
5514
+ 4. Run \`supabase start\` in your project's \`packages/db\` directory.
5267
5515
  5. Copy the 'DB URL' from the output.${output ? `
5268
5516
  ${pc.bold("Relevant output from `supabase start`:")}
5269
5517
  ${pc.dim(output)}` : ""}
5270
- 6. Add the DB URL to the .env file in \`apps/server/.env\` as \`DATABASE_URL\`:
5518
+ 6. Add the DB URL to the .env file in \`packages/db/.env\` as \`DATABASE_URL\`:
5271
5519
  ${pc.gray("DATABASE_URL=\"your_supabase_db_url\"")}`);
5272
5520
  }
5273
5521
  async function setupSupabase(config, cliInput) {
5274
- const { projectDir, packageManager } = config;
5522
+ const { projectDir, packageManager, backend } = config;
5275
5523
  const manualDb = cliInput?.manualDb ?? false;
5276
- const serverDir = path.join(projectDir, "apps", "server");
5524
+ const serverDir = path.join(projectDir, "packages", "db");
5277
5525
  try {
5278
5526
  await fs.ensureDir(serverDir);
5279
5527
  if (manualDb) {
5280
5528
  displayManualSupabaseInstructions();
5281
- await writeSupabaseEnvFile(projectDir, "");
5529
+ await writeSupabaseEnvFile(projectDir, backend, "");
5282
5530
  return;
5283
5531
  }
5284
5532
  const mode = await select({
@@ -5297,7 +5545,7 @@ async function setupSupabase(config, cliInput) {
5297
5545
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5298
5546
  if (mode === "manual") {
5299
5547
  displayManualSupabaseInstructions();
5300
- await writeSupabaseEnvFile(projectDir, "");
5548
+ await writeSupabaseEnvFile(projectDir, backend, "");
5301
5549
  return;
5302
5550
  }
5303
5551
  if (!await initializeSupabase(serverDir, packageManager)) {
@@ -5310,7 +5558,7 @@ async function setupSupabase(config, cliInput) {
5310
5558
  return;
5311
5559
  }
5312
5560
  const dbUrl = extractDbUrl(supabaseOutput);
5313
- if (dbUrl) if (await writeSupabaseEnvFile(projectDir, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
5561
+ if (dbUrl) if (await writeSupabaseEnvFile(projectDir, backend, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
5314
5562
  else {
5315
5563
  log.error(pc.red("Supabase setup completed, but failed to update .env automatically."));
5316
5564
  displayManualSupabaseInstructions(supabaseOutput);
@@ -5438,8 +5686,9 @@ async function createTursoDatabase(dbName, groupName) {
5438
5686
  s.stop(pc.red("Failed to retrieve database connection details"));
5439
5687
  }
5440
5688
  }
5441
- async function writeEnvFile(projectDir, config) {
5442
- const envPath = path.join(projectDir, "apps/server", ".env");
5689
+ async function writeEnvFile(projectDir, backend, config) {
5690
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5691
+ const envPath = path.join(projectDir, targetApp, ".env");
5443
5692
  const variables = [{
5444
5693
  key: "DATABASE_URL",
5445
5694
  value: config?.dbUrl ?? "",
@@ -5463,12 +5712,12 @@ DATABASE_URL=your_database_url
5463
5712
  DATABASE_AUTH_TOKEN=your_auth_token`);
5464
5713
  }
5465
5714
  async function setupTurso(config, cliInput) {
5466
- const { orm, projectDir } = config;
5715
+ const { orm, projectDir, backend } = config;
5467
5716
  const manualDb = cliInput?.manualDb ?? false;
5468
5717
  const setupSpinner = spinner();
5469
5718
  try {
5470
5719
  if (manualDb) {
5471
- await writeEnvFile(projectDir);
5720
+ await writeEnvFile(projectDir, backend);
5472
5721
  displayManualSetupInstructions();
5473
5722
  return;
5474
5723
  }
@@ -5487,7 +5736,7 @@ async function setupTurso(config, cliInput) {
5487
5736
  });
5488
5737
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5489
5738
  if (mode === "manual") {
5490
- await writeEnvFile(projectDir);
5739
+ await writeEnvFile(projectDir, backend);
5491
5740
  displayManualSetupInstructions();
5492
5741
  return;
5493
5742
  }
@@ -5497,7 +5746,7 @@ async function setupTurso(config, cliInput) {
5497
5746
  if (platform === "win32") {
5498
5747
  if (setupSpinner) setupSpinner.stop(pc.yellow("Turso setup not supported on Windows"));
5499
5748
  log.warn(pc.yellow("Automatic Turso setup is not supported on Windows."));
5500
- await writeEnvFile(projectDir);
5749
+ await writeEnvFile(projectDir, backend);
5501
5750
  displayManualSetupInstructions();
5502
5751
  return;
5503
5752
  }
@@ -5509,7 +5758,7 @@ async function setupTurso(config, cliInput) {
5509
5758
  });
5510
5759
  if (isCancel(shouldInstall)) return exitCancelled("Operation cancelled");
5511
5760
  if (!shouldInstall) {
5512
- await writeEnvFile(projectDir);
5761
+ await writeEnvFile(projectDir, backend);
5513
5762
  displayManualSetupInstructions();
5514
5763
  return;
5515
5764
  }
@@ -5531,7 +5780,7 @@ async function setupTurso(config, cliInput) {
5531
5780
  dbName = dbNameResponse;
5532
5781
  try {
5533
5782
  const config$1 = await createTursoDatabase(dbName, selectedGroup);
5534
- await writeEnvFile(projectDir, config$1);
5783
+ await writeEnvFile(projectDir, backend, config$1);
5535
5784
  success = true;
5536
5785
  } catch (error) {
5537
5786
  if (error instanceof Error && error.message === "DATABASE_EXISTS") {
@@ -5544,7 +5793,7 @@ async function setupTurso(config, cliInput) {
5544
5793
  } catch (error) {
5545
5794
  if (setupSpinner) setupSpinner.stop(pc.red("Turso CLI availability check failed"));
5546
5795
  consola.error(pc.red(`Error during Turso setup: ${error instanceof Error ? error.message : String(error)}`));
5547
- await writeEnvFile(projectDir);
5796
+ await writeEnvFile(projectDir, backend);
5548
5797
  displayManualSetupInstructions();
5549
5798
  log.success("Setup completed with manual configuration required.");
5550
5799
  }
@@ -5556,40 +5805,48 @@ async function setupDatabase(config, cliInput) {
5556
5805
  const { database, orm, dbSetup, backend, projectDir } = config;
5557
5806
  if (backend === "convex" || database === "none") {
5558
5807
  if (backend !== "convex") {
5559
- const serverDir$1 = path.join(projectDir, "apps/server");
5560
- const serverDbDir = path.join(serverDir$1, "src/db");
5808
+ const serverDir = path.join(projectDir, "apps/server");
5809
+ const serverDbDir = path.join(serverDir, "src/db");
5561
5810
  if (await fs.pathExists(serverDbDir)) await fs.remove(serverDbDir);
5562
5811
  }
5563
5812
  return;
5564
5813
  }
5565
5814
  const s = spinner();
5566
- const serverDir = path.join(projectDir, "apps/server");
5567
- if (!await fs.pathExists(serverDir)) return;
5815
+ const dbPackageDir = path.join(projectDir, "packages/db");
5816
+ if (!await fs.pathExists(dbPackageDir)) return;
5568
5817
  try {
5569
- if (orm === "prisma") if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
5570
- dependencies: [
5571
- "@prisma/client",
5572
- "@prisma/adapter-planetscale",
5573
- "@planetscale/database"
5574
- ],
5575
- devDependencies: ["prisma"],
5576
- projectDir: serverDir
5577
- });
5578
- else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
5579
- dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
5580
- devDependencies: ["prisma"],
5581
- projectDir: serverDir
5582
- });
5583
- else await addPackageDependency({
5584
- dependencies: ["@prisma/client"],
5585
- devDependencies: ["prisma"],
5586
- projectDir: serverDir
5587
- });
5588
- else if (orm === "drizzle") {
5818
+ if (orm === "prisma") {
5819
+ if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
5820
+ dependencies: [
5821
+ "@prisma/client",
5822
+ "@prisma/adapter-planetscale",
5823
+ "@planetscale/database"
5824
+ ],
5825
+ devDependencies: ["prisma"],
5826
+ projectDir: dbPackageDir
5827
+ });
5828
+ else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
5829
+ dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
5830
+ devDependencies: ["prisma"],
5831
+ projectDir: dbPackageDir
5832
+ });
5833
+ else await addPackageDependency({
5834
+ dependencies: ["@prisma/client"],
5835
+ devDependencies: ["prisma"],
5836
+ projectDir: dbPackageDir
5837
+ });
5838
+ if (backend === "self") {
5839
+ const webDir = path.join(projectDir, "apps/web");
5840
+ if (await fs.pathExists(webDir)) await addPackageDependency({
5841
+ dependencies: ["@prisma/client"],
5842
+ projectDir: webDir
5843
+ });
5844
+ }
5845
+ } else if (orm === "drizzle") {
5589
5846
  if (database === "sqlite") await addPackageDependency({
5590
5847
  dependencies: ["drizzle-orm", "@libsql/client"],
5591
5848
  devDependencies: ["drizzle-kit"],
5592
- projectDir: serverDir
5849
+ projectDir: dbPackageDir
5593
5850
  });
5594
5851
  else if (database === "postgres") if (dbSetup === "neon") await addPackageDependency({
5595
5852
  dependencies: [
@@ -5598,32 +5855,32 @@ async function setupDatabase(config, cliInput) {
5598
5855
  "ws"
5599
5856
  ],
5600
5857
  devDependencies: ["drizzle-kit", "@types/ws"],
5601
- projectDir: serverDir
5858
+ projectDir: dbPackageDir
5602
5859
  });
5603
5860
  else if (dbSetup === "planetscale") await addPackageDependency({
5604
5861
  dependencies: ["drizzle-orm", "pg"],
5605
5862
  devDependencies: ["drizzle-kit", "@types/pg"],
5606
- projectDir: serverDir
5863
+ projectDir: dbPackageDir
5607
5864
  });
5608
5865
  else await addPackageDependency({
5609
5866
  dependencies: ["drizzle-orm", "pg"],
5610
5867
  devDependencies: ["drizzle-kit", "@types/pg"],
5611
- projectDir: serverDir
5868
+ projectDir: dbPackageDir
5612
5869
  });
5613
5870
  else if (database === "mysql") if (dbSetup === "planetscale") await addPackageDependency({
5614
5871
  dependencies: ["drizzle-orm", "@planetscale/database"],
5615
5872
  devDependencies: ["drizzle-kit"],
5616
- projectDir: serverDir
5873
+ projectDir: dbPackageDir
5617
5874
  });
5618
5875
  else await addPackageDependency({
5619
5876
  dependencies: ["drizzle-orm", "mysql2"],
5620
5877
  devDependencies: ["drizzle-kit"],
5621
- projectDir: serverDir
5878
+ projectDir: dbPackageDir
5622
5879
  });
5623
5880
  } else if (orm === "mongoose") await addPackageDependency({
5624
5881
  dependencies: ["mongoose"],
5625
5882
  devDependencies: [],
5626
- projectDir: serverDir
5883
+ projectDir: dbPackageDir
5627
5884
  });
5628
5885
  if (dbSetup === "docker") await setupDockerCompose(config);
5629
5886
  else if (database === "sqlite" && dbSetup === "turso") await setupTurso(config, cliInput);
@@ -5646,7 +5903,7 @@ async function setupDatabase(config, cliInput) {
5646
5903
  //#region src/helpers/core/runtime-setup.ts
5647
5904
  async function setupRuntime(config) {
5648
5905
  const { runtime, backend, projectDir } = config;
5649
- if (backend === "convex" || backend === "next" || runtime === "none") return;
5906
+ if (backend === "convex" || backend === "self" || runtime === "none") return;
5650
5907
  const serverDir = path.join(projectDir, "apps/server");
5651
5908
  if (!await fs.pathExists(serverDir)) return;
5652
5909
  if (runtime === "bun") await setupBunRuntime(serverDir, backend);
@@ -5736,7 +5993,7 @@ This project uses Convex as a backend. You'll need to set up Convex before runni
5736
5993
  ${packageManagerRunCmd} dev:setup
5737
5994
  \`\`\`
5738
5995
 
5739
- 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)}
5996
+ 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)}
5740
5997
 
5741
5998
  Then, run the development server:
5742
5999
 
@@ -5789,6 +6046,7 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
5789
6046
  const instructions = [];
5790
6047
  const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
5791
6048
  const isBackendNone = backend === "none";
6049
+ const isBackendSelf = backend === "self";
5792
6050
  if (!hasFrontendNone) {
5793
6051
  const hasTanstackRouter = frontend.includes("tanstack-router");
5794
6052
  const hasReactRouter = frontend.includes("react-router");
@@ -5797,17 +6055,19 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
5797
6055
  const hasSvelte = frontend.includes("svelte");
5798
6056
  const hasNuxt = frontend.includes("nuxt");
5799
6057
  const hasSolid = frontend.includes("solid");
5800
- 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.`);
6058
+ 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.`);
6059
+ else instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see the web application.`);
5801
6060
  }
5802
6061
  if (hasNative) instructions.push("Use the Expo Go app to run the mobile application.");
5803
6062
  if (isConvex) instructions.push("Your app will connect to the Convex cloud backend automatically.");
5804
- else if (!isBackendNone) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
6063
+ else if (!isBackendNone && !isBackendSelf) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
5805
6064
  return instructions.join("\n");
5806
6065
  }
5807
6066
  function generateProjectStructure(projectName, frontend, backend, addons, isConvex, api, auth) {
5808
6067
  const structure = [`${projectName}/`, "├── apps/"];
5809
6068
  const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
5810
6069
  const isBackendNone = backend === "none";
6070
+ const isBackendSelf = backend === "self";
5811
6071
  if (!hasFrontendNone) {
5812
6072
  const hasTanstackRouter = frontend.includes("tanstack-router");
5813
6073
  const hasReactRouter = frontend.includes("react-router");
@@ -5825,21 +6085,32 @@ function generateProjectStructure(projectName, frontend, backend, addons, isConv
5825
6085
  else if (hasSvelte) frontendType = "SvelteKit";
5826
6086
  else if (hasNuxt) frontendType = "Nuxt";
5827
6087
  else if (hasSolid) frontendType = "SolidJS";
5828
- structure.push(`│ ├── web/ # Frontend application (${frontendType})`);
6088
+ if (isBackendSelf) structure.push(`│ └── web/ # Fullstack application (${frontendType})`);
6089
+ else structure.push(`│ ├── web/ # Frontend application (${frontendType})`);
5829
6090
  }
5830
6091
  }
5831
- if (frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
5832
- if (addons.includes("starlight")) structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
5833
- if (isConvex) {
5834
- structure.push("├── packages/");
5835
- structure.push("│ └── backend/ # Convex backend functions and schema");
5836
- if (auth === "clerk") structure.push("│ ├── convex/ # Convex functions and schema", "│ └── .env.local # Convex environment variables");
5837
- } else if (!isBackendNone) {
6092
+ if (frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) if (isBackendSelf) structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
6093
+ else structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
6094
+ if (addons.includes("starlight")) if (isBackendSelf) structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
6095
+ else structure.push("├── docs/ # Documentation site (Astro Starlight)");
6096
+ if (!isBackendSelf && !isBackendNone && !isConvex) {
5838
6097
  const backendName = backend[0].toUpperCase() + backend.slice(1);
5839
6098
  const apiName = api !== "none" ? api.toUpperCase() : "";
5840
6099
  const backendDesc = apiName ? `${backendName}, ${apiName}` : backendName;
5841
6100
  structure.push(`│ └── server/ # Backend API (${backendDesc})`);
5842
6101
  }
6102
+ if (isConvex || !isBackendNone) {
6103
+ structure.push("├── packages/");
6104
+ if (isConvex) {
6105
+ structure.push("│ ├── backend/ # Convex backend functions and schema");
6106
+ if (auth === "clerk") structure.push("│ │ ├── convex/ # Convex functions and schema", "│ │ └── .env.local # Convex environment variables");
6107
+ }
6108
+ if (!isConvex) {
6109
+ structure.push("│ ├── api/ # API layer / business logic");
6110
+ if (auth !== "none") structure.push("│ ├── auth/ # Authentication configuration & logic");
6111
+ if (api !== "none" || auth !== "none") structure.push("│ └── db/ # Database schema & queries");
6112
+ }
6113
+ }
5843
6114
  return structure.join("\n");
5844
6115
  }
5845
6116
  function generateFeaturesList(database, auth, addons, orm, runtime, frontend, backend, api) {
@@ -5897,33 +6168,36 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
5897
6168
  else if (addon === "turborepo") addonsList.push("- **Turborepo** - Optimized monorepo build system");
5898
6169
  return addonsList.join("\n");
5899
6170
  }
5900
- function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, serverDeploy) {
6171
+ function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, serverDeploy, backend) {
5901
6172
  if (database === "none") return "";
6173
+ const isBackendSelf = backend === "self";
6174
+ const envPath = isBackendSelf ? "apps/web/.env" : "apps/server/.env";
6175
+ const dbLocalPath = isBackendSelf ? "apps/web" : "apps/server";
5902
6176
  let setup = "## Database Setup\n\n";
5903
6177
  if (database === "sqlite") setup += `This project uses SQLite${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
5904
6178
 
5905
6179
  1. Start the local SQLite database:
5906
6180
  ${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
5907
- cd apps/server && ${packageManagerRunCmd} db:local
6181
+ cd ${dbLocalPath} && ${packageManagerRunCmd} db:local
5908
6182
  \`\`\`
5909
6183
  `}
5910
6184
 
5911
- 2. Update your \`.env\` file in the \`apps/server\` directory with the appropriate connection details if needed.
6185
+ 2. Update your \`.env\` file in the \`${isBackendSelf ? "apps/web" : "apps/server"}\` directory with the appropriate connection details if needed.
5912
6186
  `;
5913
6187
  else if (database === "postgres") setup += `This project uses PostgreSQL${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
5914
6188
 
5915
6189
  1. Make sure you have a PostgreSQL database set up.
5916
- 2. Update your \`apps/server/.env\` file with your PostgreSQL connection details.
6190
+ 2. Update your \`${envPath}\` file with your PostgreSQL connection details.
5917
6191
  `;
5918
6192
  else if (database === "mysql") setup += `This project uses MySQL${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
5919
6193
 
5920
6194
  1. Make sure you have a MySQL database set up.
5921
- 2. Update your \`apps/server/.env\` file with your MySQL connection details.
6195
+ 2. Update your \`${envPath}\` file with your MySQL connection details.
5922
6196
  `;
5923
6197
  else if (database === "mongodb") setup += `This project uses MongoDB ${orm === "mongoose" ? "with Mongoose" : orm === "prisma" ? "with Prisma ORM" : `with ${orm}`}.
5924
6198
 
5925
6199
  1. Make sure you have MongoDB set up.
5926
- 2. Update your \`apps/server/.env\` file with your MongoDB connection URI.
6200
+ 2. Update your \`${envPath}\` file with your MongoDB connection URI.
5927
6201
  `;
5928
6202
  setup += `
5929
6203
  3. ${orm === "prisma" ? `Generate the Prisma client and push the schema:
@@ -5942,13 +6216,14 @@ ${packageManagerRunCmd} db:push
5942
6216
  function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNative, addons, backend) {
5943
6217
  const isConvex = backend === "convex";
5944
6218
  const isBackendNone = backend === "none";
6219
+ const isBackendSelf = backend === "self";
5945
6220
  let scripts = `- \`${packageManagerRunCmd} dev\`: Start all applications in development mode
5946
6221
  - \`${packageManagerRunCmd} build\`: Build all applications`;
5947
- scripts += `
6222
+ if (!isBackendSelf) scripts += `
5948
6223
  - \`${packageManagerRunCmd} dev:web\`: Start only the web application`;
5949
6224
  if (isConvex) scripts += `
5950
6225
  - \`${packageManagerRunCmd} dev:setup\`: Setup and configure your Convex project`;
5951
- else if (!isBackendNone) scripts += `
6226
+ else if (!isBackendNone && !isBackendSelf) scripts += `
5952
6227
  - \`${packageManagerRunCmd} dev:server\`: Start only the server`;
5953
6228
  scripts += `
5954
6229
  - \`${packageManagerRunCmd} check-types\`: Check TypeScript types across all apps`;
@@ -5959,7 +6234,7 @@ function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNati
5959
6234
  - \`${packageManagerRunCmd} db:push\`: Push schema changes to database
5960
6235
  - \`${packageManagerRunCmd} db:studio\`: Open database studio UI`;
5961
6236
  if (database === "sqlite" && orm === "drizzle") scripts += `
5962
- - \`cd apps/server && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
6237
+ - \`cd ${isBackendSelf ? "apps/web" : "apps/server"} && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
5963
6238
  }
5964
6239
  if (addons.includes("biome")) scripts += `
5965
6240
  - \`${packageManagerRunCmd} check\`: Run Biome formatting and linting`;
@@ -6035,15 +6310,14 @@ async function initializeGit(projectDir, useGit) {
6035
6310
  async function setupPayments(config) {
6036
6311
  const { payments, projectDir, frontend } = config;
6037
6312
  if (!payments || payments === "none") return;
6038
- const serverDir = path.join(projectDir, "apps/server");
6039
6313
  const clientDir = path.join(projectDir, "apps/web");
6040
- const serverDirExists = await fs.pathExists(serverDir);
6314
+ const authDir = path.join(projectDir, "packages/auth");
6041
6315
  const clientDirExists = await fs.pathExists(clientDir);
6042
- if (!serverDirExists) return;
6316
+ const authDirExists = await fs.pathExists(authDir);
6043
6317
  if (payments === "polar") {
6044
- await addPackageDependency({
6318
+ if (authDirExists) await addPackageDependency({
6045
6319
  dependencies: ["@polar-sh/better-auth", "@polar-sh/sdk"],
6046
- projectDir: serverDir
6320
+ projectDir: authDir
6047
6321
  });
6048
6322
  if (clientDirExists) {
6049
6323
  if (frontend.some((f) => [
@@ -6117,19 +6391,20 @@ async function getDockerStatus(database) {
6117
6391
  async function displayPostInstallInstructions(config) {
6118
6392
  const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
6119
6393
  const isConvex = backend === "convex";
6394
+ const isBackendSelf = backend === "self";
6120
6395
  const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : "bun run";
6121
6396
  const cdCmd = `cd ${relativePath}`;
6122
6397
  const hasHuskyOrBiome = addons?.includes("husky") || addons?.includes("biome");
6123
- const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy) : "";
6398
+ const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
6124
6399
  const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
6125
6400
  const lintingInstructions = hasHuskyOrBiome ? getLintingInstructions(runCmd) : "";
6126
6401
  const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex) : "";
6127
6402
  const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
6128
6403
  const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
6129
6404
  const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
6130
- const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions() : "";
6131
- const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy);
6132
- const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy);
6405
+ const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions(backend) : "";
6406
+ const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
6407
+ const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
6133
6408
  const hasWeb = frontend?.some((f) => [
6134
6409
  "tanstack-router",
6135
6410
  "react-router",
@@ -6153,7 +6428,8 @@ async function displayPostInstallInstructions(config) {
6153
6428
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup\n${pc.dim(" (this will guide you through Convex project setup)")}\n`;
6154
6429
  output += `${pc.cyan(`${stepCounter++}.`)} Copy environment variables from\n${pc.white(" packages/backend/.env.local")} to ${pc.white("apps/*/.env")}\n`;
6155
6430
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
6156
- } else {
6431
+ } else if (isBackendSelf) output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
6432
+ else {
6157
6433
  if (runtime !== "workers") output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
6158
6434
  if (runtime === "workers") {
6159
6435
  if (dbSetup === "d1") output += `${pc.yellow("IMPORTANT:")} Complete D1 database setup first\n (see Database commands below)\n`;
@@ -6164,11 +6440,11 @@ async function displayPostInstallInstructions(config) {
6164
6440
  output += `${pc.bold("Your project will be available at:")}\n`;
6165
6441
  if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
6166
6442
  else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
6167
- if (!isConvex) {
6443
+ if (!isConvex && !isBackendSelf) {
6168
6444
  output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
6169
- if (api === "orpc") if (backend === "next") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/rpc/api\n`;
6170
- else output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
6445
+ if (api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
6171
6446
  }
6447
+ if (isBackendSelf && api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:${webPort}/rpc/api\n`;
6172
6448
  if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
6173
6449
  if (addons?.includes("fumadocs")) output += `${pc.cyan("•")} Fumadocs: http://localhost:4000\n`;
6174
6450
  if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
@@ -6200,7 +6476,7 @@ function getNativeInstructions(isConvex) {
6200
6476
  function getLintingInstructions(runCmd) {
6201
6477
  return `${pc.bold("Linting and formatting:")}\n${pc.cyan("•")} Format and lint fix: ${`${runCmd} check`}\n`;
6202
6478
  }
6203
- async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy) {
6479
+ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) {
6204
6480
  const instructions = [];
6205
6481
  if (dbSetup === "docker") {
6206
6482
  const dockerStatus = await getDockerStatus(database);
@@ -6214,8 +6490,9 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup,
6214
6490
  const packageManager = runCmd === "npm run" ? "npm" : runCmd || "npm";
6215
6491
  instructions.push(`${pc.cyan("1.")} Login to Cloudflare: ${pc.white(`${packageManager} wrangler login`)}`);
6216
6492
  instructions.push(`${pc.cyan("2.")} Create D1 database: ${pc.white(`${packageManager} wrangler d1 create your-database-name`)}`);
6217
- instructions.push(`${pc.cyan("3.")} Update apps/server/wrangler.jsonc with database_id and database_name`);
6218
- instructions.push(`${pc.cyan("4.")} Generate migrations: ${pc.white(`cd apps/server && ${runCmd} db:generate`)}`);
6493
+ const wranglerPath = backend === "self" ? "apps/web" : "apps/server";
6494
+ instructions.push(`${pc.cyan("3.")} Update ${wranglerPath}/wrangler.jsonc with database_id and database_name`);
6495
+ instructions.push(`${pc.cyan("4.")} Generate migrations: ${pc.white(`cd ${wranglerPath} && ${runCmd} db:generate`)}`);
6219
6496
  instructions.push(`${pc.cyan("5.")} Apply migrations locally: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME --local`)}`);
6220
6497
  instructions.push(`${pc.cyan("6.")} Apply migrations to production: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME`)}`);
6221
6498
  }
@@ -6239,7 +6516,10 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup,
6239
6516
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6240
6517
  if (dbSetup !== "d1") instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
6241
6518
  if (!(dbSetup === "d1" && serverDeploy === "alchemy")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
6242
- if (database === "sqlite" && dbSetup !== "d1") instructions.push(`${pc.cyan("•")} Start local DB (if needed): ${`cd apps/server && ${runCmd} db:local`}`);
6519
+ if (database === "sqlite" && dbSetup !== "d1") {
6520
+ const dbLocalPath = backend === "self" ? "apps/web" : "apps/server";
6521
+ instructions.push(`${pc.cyan("•")} Start local DB (if needed): ${`cd ${dbLocalPath} && ${runCmd} db:local`}`);
6522
+ }
6243
6523
  } else if (orm === "mongoose") {
6244
6524
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6245
6525
  } else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
@@ -6260,32 +6540,115 @@ function getNoOrmWarning() {
6260
6540
  function getBunWebNativeWarning() {
6261
6541
  return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
6262
6542
  }
6263
- function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy) {
6543
+ function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
6264
6544
  const instructions = [];
6265
- if (webDeploy === "wrangler") instructions.push(`${pc.bold("Deploy web to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} run deploy`}`);
6266
- if (serverDeploy === "wrangler") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} run deploy`}`);
6545
+ if (webDeploy === "wrangler") {
6546
+ const deployPath = backend === "self" ? "apps/web" : "apps/web";
6547
+ instructions.push(`${pc.bold("Deploy web to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd ${deployPath} && ${runCmd} run deploy`}`);
6548
+ }
6549
+ if (serverDeploy === "wrangler" && backend !== "self") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} run deploy`}`);
6267
6550
  return instructions.length ? `\n${instructions.join("\n")}` : "";
6268
6551
  }
6269
6552
  function getClerkInstructions() {
6270
6553
  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`;
6271
6554
  }
6272
- function getPolarInstructions() {
6273
- 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`;
6555
+ function getPolarInstructions(backend) {
6556
+ const envPath = backend === "self" ? "apps/web/.env" : "apps/server/.env";
6557
+ 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}`;
6274
6558
  }
6275
- function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
6559
+ function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
6276
6560
  const instructions = [];
6561
+ const isBackendSelf = backend === "self";
6277
6562
  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`}`);
6278
- 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`}`);
6279
- 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`}`);
6563
+ 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`}`);
6564
+ 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`}`);
6280
6565
  return instructions.length ? `\n${instructions.join("\n")}` : "";
6281
6566
  }
6282
6567
 
6568
+ //#endregion
6569
+ //#region src/helpers/core/workspace-setup.ts
6570
+ async function setupWorkspaceDependencies(projectDir, options) {
6571
+ const projectName = options.projectName;
6572
+ const workspaceVersion = options.packageManager === "npm" ? "*" : "workspace:*";
6573
+ const commonDeps = ["dotenv", "zod"];
6574
+ const commonDevDeps = ["tsdown"];
6575
+ const dbPackageDir = path.join(projectDir, "packages/db");
6576
+ if (await fs.pathExists(dbPackageDir)) await addPackageDependency({
6577
+ dependencies: commonDeps,
6578
+ devDependencies: commonDevDeps,
6579
+ projectDir: dbPackageDir
6580
+ });
6581
+ const authPackageDir = path.join(projectDir, "packages/auth");
6582
+ if (await fs.pathExists(authPackageDir)) await addPackageDependency({
6583
+ dependencies: commonDeps,
6584
+ devDependencies: commonDevDeps,
6585
+ customDependencies: { [`@${projectName}/db`]: workspaceVersion },
6586
+ projectDir: authPackageDir
6587
+ });
6588
+ const apiPackageDir = path.join(projectDir, "packages/api");
6589
+ if (await fs.pathExists(apiPackageDir)) await addPackageDependency({
6590
+ dependencies: commonDeps,
6591
+ devDependencies: commonDevDeps,
6592
+ customDependencies: {
6593
+ [`@${projectName}/auth`]: workspaceVersion,
6594
+ [`@${projectName}/db`]: workspaceVersion
6595
+ },
6596
+ projectDir: apiPackageDir
6597
+ });
6598
+ const serverPackageDir = path.join(projectDir, "apps/server");
6599
+ if (await fs.pathExists(serverPackageDir)) await addPackageDependency({
6600
+ dependencies: commonDeps,
6601
+ devDependencies: commonDevDeps,
6602
+ customDependencies: {
6603
+ [`@${projectName}/api`]: workspaceVersion,
6604
+ [`@${projectName}/auth`]: workspaceVersion,
6605
+ [`@${projectName}/db`]: workspaceVersion
6606
+ },
6607
+ projectDir: serverPackageDir
6608
+ });
6609
+ const webPackageDir = path.join(projectDir, "apps/web");
6610
+ if (await fs.pathExists(webPackageDir)) {
6611
+ const webDeps = {};
6612
+ webDeps[`@${projectName}/api`] = workspaceVersion;
6613
+ webDeps[`@${projectName}/auth`] = workspaceVersion;
6614
+ webDeps[`@${projectName}/db`] = workspaceVersion;
6615
+ if (Object.keys(webDeps).length > 0) await addPackageDependency({
6616
+ customDependencies: webDeps,
6617
+ projectDir: webPackageDir
6618
+ });
6619
+ }
6620
+ const runtimeDevDeps = getRuntimeDevDeps(options);
6621
+ await addPackageDependency({
6622
+ dependencies: commonDeps,
6623
+ devDependencies: [...commonDevDeps, ...runtimeDevDeps],
6624
+ projectDir
6625
+ });
6626
+ }
6627
+ function getRuntimeDevDeps(options) {
6628
+ const { runtime, backend } = options;
6629
+ if (runtime === "none" && backend === "self") return ["@types/node"];
6630
+ if (runtime === "node") return ["@types/node"];
6631
+ if (runtime === "bun") return ["@types/bun"];
6632
+ if (runtime === "workers") return ["@types/node"];
6633
+ return [];
6634
+ }
6635
+
6283
6636
  //#endregion
6284
6637
  //#region src/helpers/core/project-config.ts
6285
6638
  async function updatePackageConfigurations(projectDir, options) {
6286
6639
  await updateRootPackageJson(projectDir, options);
6287
- if (options.backend !== "convex") await updateServerPackageJson(projectDir, options);
6288
- else await updateConvexPackageJson(projectDir, options);
6640
+ if (options.backend === "convex") await updateConvexPackageJson(projectDir, options);
6641
+ else if (options.backend === "self") {
6642
+ await updateDbPackageJson(projectDir, options);
6643
+ await updateAuthPackageJson(projectDir, options);
6644
+ await updateApiPackageJson(projectDir, options);
6645
+ await setupWorkspaceDependencies(projectDir, options);
6646
+ } else if (options.backend !== "none") {
6647
+ await updateServerPackageJson(projectDir, options);
6648
+ await updateAuthPackageJson(projectDir, options);
6649
+ await updateApiPackageJson(projectDir, options);
6650
+ await setupWorkspaceDependencies(projectDir, options);
6651
+ }
6289
6652
  }
6290
6653
  async function updateRootPackageJson(projectDir, options) {
6291
6654
  const rootPackageJsonPath = path.join(projectDir, "package.json");
@@ -6295,6 +6658,7 @@ async function updateRootPackageJson(projectDir, options) {
6295
6658
  if (!packageJson.scripts) packageJson.scripts = {};
6296
6659
  const scripts = packageJson.scripts;
6297
6660
  const backendPackageName = options.backend === "convex" ? `@${options.projectName}/backend` : "server";
6661
+ const dbPackageName = `@${options.projectName}/db`;
6298
6662
  let serverDevScript = "";
6299
6663
  if (options.addons.includes("turborepo")) serverDevScript = `turbo -F ${backendPackageName} dev`;
6300
6664
  else if (options.packageManager === "bun") serverDevScript = `bun run --filter ${backendPackageName} dev`;
@@ -6311,24 +6675,24 @@ async function updateRootPackageJson(projectDir, options) {
6311
6675
  scripts["check-types"] = "turbo check-types";
6312
6676
  scripts["dev:native"] = "turbo -F native dev";
6313
6677
  scripts["dev:web"] = "turbo -F web dev";
6314
- scripts["dev:server"] = serverDevScript;
6678
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6315
6679
  if (options.backend === "convex") scripts["dev:setup"] = `turbo -F ${backendPackageName} dev:setup`;
6316
6680
  if (needsDbScripts) {
6317
- scripts["db:push"] = `turbo -F ${backendPackageName} db:push`;
6318
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${backendPackageName} db:studio`;
6681
+ scripts["db:push"] = `turbo -F ${dbPackageName} db:push`;
6682
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${dbPackageName} db:studio`;
6319
6683
  if (options.orm === "prisma") {
6320
- scripts["db:generate"] = `turbo -F ${backendPackageName} db:generate`;
6321
- scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
6684
+ scripts["db:generate"] = `turbo -F ${dbPackageName} db:generate`;
6685
+ scripts["db:migrate"] = `turbo -F ${dbPackageName} db:migrate`;
6322
6686
  } else if (options.orm === "drizzle") {
6323
- scripts["db:generate"] = `turbo -F ${backendPackageName} db:generate`;
6324
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
6687
+ scripts["db:generate"] = `turbo -F ${dbPackageName} db:generate`;
6688
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${dbPackageName} db:migrate`;
6325
6689
  }
6326
6690
  }
6327
6691
  if (options.dbSetup === "docker") {
6328
- scripts["db:start"] = `turbo -F ${backendPackageName} db:start`;
6329
- scripts["db:watch"] = `turbo -F ${backendPackageName} db:watch`;
6330
- scripts["db:stop"] = `turbo -F ${backendPackageName} db:stop`;
6331
- scripts["db:down"] = `turbo -F ${backendPackageName} db:down`;
6692
+ scripts["db:start"] = `turbo -F ${dbPackageName} db:start`;
6693
+ scripts["db:watch"] = `turbo -F ${dbPackageName} db:watch`;
6694
+ scripts["db:stop"] = `turbo -F ${dbPackageName} db:stop`;
6695
+ scripts["db:down"] = `turbo -F ${dbPackageName} db:down`;
6332
6696
  }
6333
6697
  } else if (options.packageManager === "pnpm") {
6334
6698
  scripts.dev = devScript;
@@ -6336,24 +6700,24 @@ async function updateRootPackageJson(projectDir, options) {
6336
6700
  scripts["check-types"] = "pnpm -r check-types";
6337
6701
  scripts["dev:native"] = "pnpm --filter native dev";
6338
6702
  scripts["dev:web"] = "pnpm --filter web dev";
6339
- scripts["dev:server"] = serverDevScript;
6703
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6340
6704
  if (options.backend === "convex") scripts["dev:setup"] = `pnpm --filter ${backendPackageName} dev:setup`;
6341
6705
  if (needsDbScripts) {
6342
- scripts["db:push"] = `pnpm --filter ${backendPackageName} db:push`;
6343
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${backendPackageName} db:studio`;
6706
+ scripts["db:push"] = `pnpm --filter ${dbPackageName} db:push`;
6707
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${dbPackageName} db:studio`;
6344
6708
  if (options.orm === "prisma") {
6345
- scripts["db:generate"] = `pnpm --filter ${backendPackageName} db:generate`;
6346
- scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
6709
+ scripts["db:generate"] = `pnpm --filter ${dbPackageName} db:generate`;
6710
+ scripts["db:migrate"] = `pnpm --filter ${dbPackageName} db:migrate`;
6347
6711
  } else if (options.orm === "drizzle") {
6348
- scripts["db:generate"] = `pnpm --filter ${backendPackageName} db:generate`;
6349
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
6712
+ scripts["db:generate"] = `pnpm --filter ${dbPackageName} db:generate`;
6713
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${dbPackageName} db:migrate`;
6350
6714
  }
6351
6715
  }
6352
6716
  if (options.dbSetup === "docker") {
6353
- scripts["db:start"] = `pnpm --filter ${backendPackageName} db:start`;
6354
- scripts["db:watch"] = `pnpm --filter ${backendPackageName} db:watch`;
6355
- scripts["db:stop"] = `pnpm --filter ${backendPackageName} db:stop`;
6356
- scripts["db:down"] = `pnpm --filter ${backendPackageName} db:down`;
6717
+ scripts["db:start"] = `pnpm --filter ${dbPackageName} db:start`;
6718
+ scripts["db:watch"] = `pnpm --filter ${dbPackageName} db:watch`;
6719
+ scripts["db:stop"] = `pnpm --filter ${dbPackageName} db:stop`;
6720
+ scripts["db:down"] = `pnpm --filter ${dbPackageName} db:down`;
6357
6721
  }
6358
6722
  } else if (options.packageManager === "npm") {
6359
6723
  scripts.dev = devScript;
@@ -6361,24 +6725,24 @@ async function updateRootPackageJson(projectDir, options) {
6361
6725
  scripts["check-types"] = "npm run check-types --workspaces";
6362
6726
  scripts["dev:native"] = "npm run dev --workspace native";
6363
6727
  scripts["dev:web"] = "npm run dev --workspace web";
6364
- scripts["dev:server"] = serverDevScript;
6728
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6365
6729
  if (options.backend === "convex") scripts["dev:setup"] = `npm run dev:setup --workspace ${backendPackageName}`;
6366
6730
  if (needsDbScripts) {
6367
- scripts["db:push"] = `npm run db:push --workspace ${backendPackageName}`;
6368
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${backendPackageName}`;
6731
+ scripts["db:push"] = `npm run db:push --workspace ${dbPackageName}`;
6732
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${dbPackageName}`;
6369
6733
  if (options.orm === "prisma") {
6370
- scripts["db:generate"] = `npm run db:generate --workspace ${backendPackageName}`;
6371
- scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
6734
+ scripts["db:generate"] = `npm run db:generate --workspace ${dbPackageName}`;
6735
+ scripts["db:migrate"] = `npm run db:migrate --workspace ${dbPackageName}`;
6372
6736
  } else if (options.orm === "drizzle") {
6373
- scripts["db:generate"] = `npm run db:generate --workspace ${backendPackageName}`;
6374
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
6737
+ scripts["db:generate"] = `npm run db:generate --workspace ${dbPackageName}`;
6738
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${dbPackageName}`;
6375
6739
  }
6376
6740
  }
6377
6741
  if (options.dbSetup === "docker") {
6378
- scripts["db:start"] = `npm run db:start --workspace ${backendPackageName}`;
6379
- scripts["db:watch"] = `npm run db:watch --workspace ${backendPackageName}`;
6380
- scripts["db:stop"] = `npm run db:stop --workspace ${backendPackageName}`;
6381
- scripts["db:down"] = `npm run db:down --workspace ${backendPackageName}`;
6742
+ scripts["db:start"] = `npm run db:start --workspace ${dbPackageName}`;
6743
+ scripts["db:watch"] = `npm run db:watch --workspace ${dbPackageName}`;
6744
+ scripts["db:stop"] = `npm run db:stop --workspace ${dbPackageName}`;
6745
+ scripts["db:down"] = `npm run db:down --workspace ${dbPackageName}`;
6382
6746
  }
6383
6747
  } else if (options.packageManager === "bun") {
6384
6748
  scripts.dev = devScript;
@@ -6386,24 +6750,24 @@ async function updateRootPackageJson(projectDir, options) {
6386
6750
  scripts["check-types"] = "bun run --filter '*' check-types";
6387
6751
  scripts["dev:native"] = "bun run --filter native dev";
6388
6752
  scripts["dev:web"] = "bun run --filter web dev";
6389
- scripts["dev:server"] = serverDevScript;
6753
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6390
6754
  if (options.backend === "convex") scripts["dev:setup"] = `bun run --filter ${backendPackageName} dev:setup`;
6391
6755
  if (needsDbScripts) {
6392
- scripts["db:push"] = `bun run --filter ${backendPackageName} db:push`;
6393
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${backendPackageName} db:studio`;
6756
+ scripts["db:push"] = `bun run --filter ${dbPackageName} db:push`;
6757
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${dbPackageName} db:studio`;
6394
6758
  if (options.orm === "prisma") {
6395
- scripts["db:generate"] = `bun run --filter ${backendPackageName} db:generate`;
6396
- scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
6759
+ scripts["db:generate"] = `bun run --filter ${dbPackageName} db:generate`;
6760
+ scripts["db:migrate"] = `bun run --filter ${dbPackageName} db:migrate`;
6397
6761
  } else if (options.orm === "drizzle") {
6398
- scripts["db:generate"] = `bun run --filter ${backendPackageName} db:generate`;
6399
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
6762
+ scripts["db:generate"] = `bun run --filter ${dbPackageName} db:generate`;
6763
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${dbPackageName} db:migrate`;
6400
6764
  }
6401
6765
  }
6402
6766
  if (options.dbSetup === "docker") {
6403
- scripts["db:start"] = `bun run --filter ${backendPackageName} db:start`;
6404
- scripts["db:watch"] = `bun run --filter ${backendPackageName} db:watch`;
6405
- scripts["db:stop"] = `bun run --filter ${backendPackageName} db:stop`;
6406
- scripts["db:down"] = `bun run --filter ${backendPackageName} db:down`;
6767
+ scripts["db:start"] = `bun run --filter ${dbPackageName} db:start`;
6768
+ scripts["db:watch"] = `bun run --filter ${dbPackageName} db:watch`;
6769
+ scripts["db:stop"] = `bun run --filter ${dbPackageName} db:stop`;
6770
+ scripts["db:down"] = `bun run --filter ${dbPackageName} db:down`;
6407
6771
  }
6408
6772
  }
6409
6773
  try {
@@ -6428,7 +6792,16 @@ async function updateServerPackageJson(projectDir, options) {
6428
6792
  if (!await fs.pathExists(serverPackageJsonPath)) return;
6429
6793
  const serverPackageJson = await fs.readJson(serverPackageJsonPath);
6430
6794
  if (!serverPackageJson.scripts) serverPackageJson.scripts = {};
6431
- const scripts = serverPackageJson.scripts;
6795
+ await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
6796
+ await updateDbPackageJson(projectDir, options);
6797
+ }
6798
+ async function updateDbPackageJson(projectDir, options) {
6799
+ const dbPackageJsonPath = path.join(projectDir, "packages/db/package.json");
6800
+ if (!await fs.pathExists(dbPackageJsonPath)) return;
6801
+ const dbPackageJson = await fs.readJson(dbPackageJsonPath);
6802
+ dbPackageJson.name = `@${options.projectName}/db`;
6803
+ if (!dbPackageJson.scripts) dbPackageJson.scripts = {};
6804
+ const scripts = dbPackageJson.scripts;
6432
6805
  if (options.database !== "none") {
6433
6806
  if (options.database === "sqlite" && options.orm === "drizzle" && options.dbSetup !== "d1") scripts["db:local"] = "turso dev --db-file local.db";
6434
6807
  if (options.orm === "prisma") {
@@ -6449,7 +6822,21 @@ async function updateServerPackageJson(projectDir, options) {
6449
6822
  scripts["db:stop"] = "docker compose stop";
6450
6823
  scripts["db:down"] = "docker compose down";
6451
6824
  }
6452
- await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
6825
+ await fs.writeJson(dbPackageJsonPath, dbPackageJson, { spaces: 2 });
6826
+ }
6827
+ async function updateAuthPackageJson(projectDir, options) {
6828
+ const authPackageJsonPath = path.join(projectDir, "packages/auth/package.json");
6829
+ if (!await fs.pathExists(authPackageJsonPath)) return;
6830
+ const authPackageJson = await fs.readJson(authPackageJsonPath);
6831
+ authPackageJson.name = `@${options.projectName}/auth`;
6832
+ await fs.writeJson(authPackageJsonPath, authPackageJson, { spaces: 2 });
6833
+ }
6834
+ async function updateApiPackageJson(projectDir, options) {
6835
+ const apiPackageJsonPath = path.join(projectDir, "packages/api/package.json");
6836
+ if (!await fs.pathExists(apiPackageJsonPath)) return;
6837
+ const apiPackageJson = await fs.readJson(apiPackageJsonPath);
6838
+ apiPackageJson.name = `@${options.projectName}/api`;
6839
+ await fs.writeJson(apiPackageJsonPath, apiPackageJson, { spaces: 2 });
6453
6840
  }
6454
6841
  async function updateConvexPackageJson(projectDir, options) {
6455
6842
  const convexPackageJsonPath = path.join(projectDir, "packages/backend/package.json");
@@ -6465,15 +6852,14 @@ async function updateConvexPackageJson(projectDir, options) {
6465
6852
  async function createProject(options, cliInput) {
6466
6853
  const projectDir = options.projectDir;
6467
6854
  const isConvex = options.backend === "convex";
6855
+ const isSelfBackend = options.backend === "self";
6856
+ const needsServerSetup = !isConvex && !isSelfBackend;
6468
6857
  try {
6469
6858
  await fs.ensureDir(projectDir);
6470
6859
  await copyBaseTemplate(projectDir, options);
6471
6860
  await setupFrontendTemplates(projectDir, options);
6472
6861
  await setupBackendFramework(projectDir, options);
6473
- if (!isConvex) {
6474
- await setupDbOrmTemplates(projectDir, options);
6475
- await setupDockerComposeTemplates(projectDir, options);
6476
- }
6862
+ if (needsServerSetup) await setupDockerComposeTemplates(projectDir, options);
6477
6863
  await setupAuthTemplate(projectDir, options);
6478
6864
  if (options.payments && options.payments !== "none") await setupPaymentsTemplate(projectDir, options);
6479
6865
  if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamplesTemplate(projectDir, options);
@@ -6481,9 +6867,11 @@ async function createProject(options, cliInput) {
6481
6867
  await setupDeploymentTemplates(projectDir, options);
6482
6868
  await setupApi(options);
6483
6869
  if (!isConvex) {
6484
- await setupBackendDependencies(options);
6870
+ if (needsServerSetup) {
6871
+ await setupBackendDependencies(options);
6872
+ await setupRuntime(options);
6873
+ }
6485
6874
  await setupDatabase(options, cliInput);
6486
- await setupRuntime(options);
6487
6875
  if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamples(options);
6488
6876
  }
6489
6877
  if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
@@ -6492,6 +6880,7 @@ async function createProject(options, cliInput) {
6492
6880
  await handleExtras(projectDir, options);
6493
6881
  await setupEnvironmentVariables(options);
6494
6882
  await updatePackageConfigurations(projectDir, options);
6883
+ await setupCatalogs(projectDir, options);
6495
6884
  await setupWebDeploy(options);
6496
6885
  await setupServerDeploy(options);
6497
6886
  await createReadme(projectDir, options);