create-better-t-stack 2.49.1 → 2.50.0-canary.5b25d7db

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 (111) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.d.ts +2 -2
  3. package/dist/index.js +1 -1
  4. package/dist/{src-CKCxH6aF.js → src-Vtq3I-LI.js} +684 -372
  5. package/package.json +2 -1
  6. package/templates/api/orpc/fullstack/next/src/app/api/rpc/[[...rest]]/route.ts.hbs +50 -0
  7. package/templates/api/orpc/server/_gitignore +34 -0
  8. package/templates/api/orpc/server/package.json.hbs +24 -0
  9. package/templates/api/orpc/server/{base/src/lib → src}/context.ts.hbs +6 -6
  10. package/templates/{backend/server/server-base → api/orpc/server}/src/routers/index.ts.hbs +2 -2
  11. package/templates/api/orpc/server/tsconfig.json.hbs +10 -0
  12. package/templates/api/orpc/server/tsdown.config.ts.hbs +7 -0
  13. package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +1 -1
  14. package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +4 -2
  15. package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +1 -1
  16. package/templates/api/orpc/web/svelte/src/lib/orpc.ts.hbs +1 -1
  17. package/templates/api/trpc/fullstack/next/src/app/api/trpc/[trpc]/route.ts.hbs +14 -0
  18. package/templates/api/trpc/server/_gitignore +34 -0
  19. package/templates/api/trpc/server/package.json.hbs +23 -0
  20. package/templates/api/trpc/server/{base/src/lib → src}/context.ts.hbs +6 -6
  21. package/templates/api/trpc/server/src/routers/index.ts.hbs +55 -0
  22. package/templates/api/trpc/server/tsconfig.json.hbs +13 -0
  23. package/templates/api/trpc/server/tsdown.config.ts.hbs +7 -0
  24. package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +6 -4
  25. package/templates/auth/better-auth/{server/next/src/app/api/auth/[...all]/route.ts → fullstack/next/src/app/api/auth/[...all]/route.ts.hbs} +1 -1
  26. package/templates/auth/better-auth/server/base/_gitignore +34 -0
  27. package/templates/auth/better-auth/server/base/package.json.hbs +24 -0
  28. package/templates/auth/better-auth/server/base/src/{lib/auth.ts.hbs → index.ts.hbs} +12 -12
  29. package/templates/auth/better-auth/server/base/tsconfig.json.hbs +13 -0
  30. package/templates/auth/better-auth/server/base/tsdown.config.ts.hbs +7 -0
  31. package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +2 -0
  32. package/templates/auth/better-auth/web/react/next/src/app/dashboard/page.tsx.hbs +31 -0
  33. package/templates/auth/clerk/convex/web/react/tanstack-start/src/server.ts.hbs +1 -0
  34. package/templates/backend/server/{server-base → base}/package.json.hbs +0 -1
  35. package/templates/backend/server/{server-base → base}/tsconfig.json.hbs +5 -10
  36. package/templates/backend/server/base/tsdown.config.ts.hbs +14 -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/base/tsconfig.base.json +23 -0
  44. package/templates/db/base/_gitignore +34 -0
  45. package/templates/db/base/package.json.hbs +23 -0
  46. package/templates/db/base/tsconfig.json.hbs +13 -0
  47. package/templates/db/base/tsdown.config.ts.hbs +7 -0
  48. package/templates/db/drizzle/mysql/drizzle.config.ts.hbs +11 -2
  49. package/templates/db/drizzle/mysql/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  50. package/templates/db/drizzle/postgres/drizzle.config.ts.hbs +11 -2
  51. package/templates/db/drizzle/postgres/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  52. package/templates/db/drizzle/sqlite/drizzle.config.ts.hbs +11 -2
  53. package/templates/db/drizzle/sqlite/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  54. package/templates/db/prisma/mongodb/prisma.config.ts.hbs +9 -1
  55. package/templates/db/prisma/mongodb/src/index.ts.hbs +5 -0
  56. package/templates/db/prisma/mysql/prisma.config.ts.hbs +9 -1
  57. package/templates/db/prisma/mysql/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  58. package/templates/db/prisma/postgres/prisma.config.ts.hbs +11 -3
  59. package/templates/db/prisma/postgres/src/{db/index.ts.hbs → index.ts.hbs} +1 -1
  60. package/templates/db/prisma/sqlite/prisma.config.ts.hbs +9 -1
  61. package/templates/db/prisma/sqlite/src/{db/index.ts.hbs → index.ts.hbs} +3 -3
  62. package/templates/deploy/wrangler/web/react/tanstack-start/wrangler.jsonc.hbs +1 -1
  63. package/templates/examples/ai/fullstack/next/src/app/api/ai/route.ts.hbs +15 -0
  64. package/templates/examples/todo/server/drizzle/base/src/routers/todo.ts.hbs +7 -7
  65. package/templates/examples/todo/server/mongoose/base/src/routers/todo.ts.hbs +4 -4
  66. package/templates/examples/todo/server/prisma/base/src/routers/todo.ts.hbs +4 -4
  67. package/templates/frontend/native/nativewind/tsconfig.json.hbs +1 -6
  68. package/templates/frontend/native/unistyles/tsconfig.json.hbs +1 -6
  69. package/templates/frontend/nuxt/tsconfig.json.hbs +0 -4
  70. package/templates/frontend/react/next/package.json.hbs +1 -1
  71. package/templates/frontend/react/next/tsconfig.json.hbs +0 -7
  72. package/templates/frontend/react/react-router/tsconfig.json.hbs +1 -6
  73. package/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs +1 -1
  74. package/templates/frontend/react/tanstack-router/tsconfig.json.hbs +1 -6
  75. package/templates/frontend/react/tanstack-start/package.json.hbs +7 -7
  76. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +5 -5
  77. package/templates/frontend/react/tanstack-start/tsconfig.json.hbs +1 -6
  78. package/templates/frontend/react/tanstack-start/vite.config.ts.hbs +1 -1
  79. package/templates/frontend/solid/tsconfig.json.hbs +1 -6
  80. package/templates/frontend/svelte/tsconfig.json.hbs +1 -6
  81. package/templates/api/orpc/server/next/src/app/rpc/[...all]/route.ts.hbs +0 -52
  82. package/templates/api/trpc/server/next/src/app/trpc/[trpc]/route.ts +0 -14
  83. package/templates/backend/server/next/next-env.d.ts +0 -5
  84. package/templates/backend/server/next/next.config.ts +0 -7
  85. package/templates/backend/server/next/package.json.hbs +0 -27
  86. package/templates/backend/server/next/src/app/route.ts +0 -5
  87. package/templates/backend/server/next/src/middleware.ts +0 -19
  88. package/templates/backend/server/next/tsconfig.json.hbs +0 -33
  89. package/templates/db/prisma/mongodb/src/db/index.ts.hbs +0 -5
  90. package/templates/examples/ai/server/next/src/app/ai/route.ts.hbs +0 -15
  91. /package/templates/api/orpc/server/{base/src/lib/orpc.ts.hbs → src/index.ts.hbs} +0 -0
  92. /package/templates/api/trpc/server/{base/src/lib/trpc.ts.hbs → src/index.ts.hbs} +0 -0
  93. /package/templates/auth/better-auth/server/db/drizzle/mysql/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
  94. /package/templates/auth/better-auth/server/db/drizzle/postgres/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
  95. /package/templates/auth/better-auth/server/db/drizzle/sqlite/src/{db/schema/auth.ts → schema/auth.ts.hbs} +0 -0
  96. /package/templates/auth/better-auth/server/db/mongoose/mongodb/src/{db/models/auth.model.ts → models/auth.model.ts.hbs} +0 -0
  97. /package/templates/auth/better-auth/server/db/prisma/mongodb/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  98. /package/templates/auth/better-auth/server/db/prisma/mysql/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  99. /package/templates/auth/better-auth/server/db/prisma/postgres/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  100. /package/templates/auth/better-auth/server/db/prisma/sqlite/prisma/schema/{auth.prisma → auth.prisma.hbs} +0 -0
  101. /package/templates/auth/better-auth/web/nuxt/app/middleware/{auth.ts → auth.ts.hbs} +0 -0
  102. /package/templates/backend/server/{server-base → base}/_gitignore +0 -0
  103. /package/templates/db/mongoose/mongodb/src/{db/index.ts.hbs → index.ts.hbs} +0 -0
  104. /package/templates/examples/todo/server/drizzle/mysql/src/{db/schema → schema}/todo.ts +0 -0
  105. /package/templates/examples/todo/server/drizzle/postgres/src/{db/schema → schema}/todo.ts +0 -0
  106. /package/templates/examples/todo/server/drizzle/sqlite/src/{db/schema → schema}/todo.ts +0 -0
  107. /package/templates/examples/todo/server/mongoose/mongodb/src/{db/models/todo.model.ts → models/todo.model.ts.hbs} +0 -0
  108. /package/templates/examples/todo/server/prisma/mongodb/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  109. /package/templates/examples/todo/server/prisma/mysql/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  110. /package/templates/examples/todo/server/prisma/postgres/prisma/schema/{todo.prisma → todo.prisma.hbs} +0 -0
  111. /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
@@ -65,12 +66,13 @@ const dependencyVersionMap = {
65
66
  "@better-auth/expo": "^1.3.13",
66
67
  "@clerk/nextjs": "^6.31.5",
67
68
  "@clerk/clerk-react": "^5.45.0",
68
- "@clerk/tanstack-react-start": "^0.23.1",
69
+ "@clerk/tanstack-react-start": "^0.25.1",
69
70
  "@clerk/clerk-expo": "^2.14.25",
70
71
  "drizzle-orm": "^0.44.2",
71
72
  "drizzle-kit": "^0.31.2",
72
73
  "@planetscale/database": "^1.19.0",
73
74
  "@libsql/client": "^0.15.9",
75
+ libsql: "^0.5.22",
74
76
  "@neondatabase/serverless": "^1.0.1",
75
77
  pg: "^8.14.1",
76
78
  "@types/pg": "^8.11.11",
@@ -123,6 +125,7 @@ const dependencyVersionMap = {
123
125
  "@trpc/tanstack-react-query": "^11.5.0",
124
126
  "@trpc/server": "^11.5.0",
125
127
  "@trpc/client": "^11.5.0",
128
+ next: "15.5.4",
126
129
  convex: "^1.27.0",
127
130
  "@convex-dev/react-query": "^0.0.0-alpha.8",
128
131
  "convex-svelte": "^0.0.11",
@@ -138,15 +141,17 @@ const dependencyVersionMap = {
138
141
  "@tanstack/solid-query": "^5.87.4",
139
142
  "@tanstack/solid-query-devtools": "^5.87.4",
140
143
  "@tanstack/solid-router-devtools": "^1.131.44",
141
- wrangler: "^4.23.0",
142
- "@cloudflare/vite-plugin": "^1.9.0",
144
+ wrangler: "^4.40.3",
145
+ "@cloudflare/vite-plugin": "^1.13.8",
143
146
  "@opennextjs/cloudflare": "^1.6.5",
144
147
  "nitro-cloudflare-dev": "^0.2.2",
145
148
  "@sveltejs/adapter-cloudflare": "^7.2.1",
146
149
  "@cloudflare/workers-types": "^4.20250822.0",
147
- alchemy: "^0.67.0",
150
+ alchemy: "^0.70.0",
148
151
  nitropack: "^2.12.4",
149
- dotenv: "^17.2.1",
152
+ dotenv: "^17.2.2",
153
+ tsdown: "^0.15.5",
154
+ zod: "^4.1.11",
150
155
  "@polar-sh/better-auth": "^1.1.3",
151
156
  "@polar-sh/sdk": "^0.34.16"
152
157
  };
@@ -195,9 +200,9 @@ const BackendSchema = z.enum([
195
200
  "hono",
196
201
  "express",
197
202
  "fastify",
198
- "next",
199
203
  "elysia",
200
204
  "convex",
205
+ "self",
201
206
  "none"
202
207
  ]).describe("Backend framework");
203
208
  const RuntimeSchema = z.enum([
@@ -343,6 +348,22 @@ function ensureSingleWebAndNative(frontends) {
343
348
  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
349
  if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles");
345
350
  }
351
+ const FULLSTACK_FRONTENDS$1 = [
352
+ "next",
353
+ "nuxt",
354
+ "svelte",
355
+ "tanstack-start"
356
+ ];
357
+ function validateSelfBackendCompatibility(providedFlags, options, config) {
358
+ const backend = config.backend || options.backend;
359
+ const frontends = config.frontend || options.frontend || [];
360
+ if (backend === "self") {
361
+ if (!frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f))) exitWithError("Backend 'self' (fullstack) requires a fullstack-capable frontend. Please use --frontend with one of: next, nuxt, svelte, tanstack-start");
362
+ if (frontends.length > 1) exitWithError("Backend 'self' (fullstack) can only be used with a single frontend framework.");
363
+ }
364
+ const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
365
+ if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) is only compatible with fullstack-capable frontends: next, nuxt, svelte, tanstack-start. Please choose a different backend or use a fullstack frontend.");
366
+ }
346
367
  function validateWorkersCompatibility(providedFlags, options, config) {
347
368
  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
369
  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 +685,39 @@ async function getAuthChoice(auth, hasDatabase, backend, frontend) {
664
685
 
665
686
  //#endregion
666
687
  //#region src/prompts/backend.ts
688
+ const FULLSTACK_FRONTENDS = [
689
+ "next",
690
+ "nuxt",
691
+ "svelte",
692
+ "tanstack-start"
693
+ ];
667
694
  async function getBackendFrameworkChoice(backendFramework, frontends) {
668
695
  if (backendFramework !== void 0) return backendFramework;
669
696
  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
- ];
697
+ const hasFullstackFrontend = frontends?.some((f) => FULLSTACK_FRONTENDS.includes(f));
698
+ const backendOptions = [];
699
+ if (hasFullstackFrontend) backendOptions.push({
700
+ value: "self",
701
+ label: "Self (Fullstack)",
702
+ hint: "Use frontend's built-in api routes"
703
+ });
704
+ backendOptions.push({
705
+ value: "hono",
706
+ label: "Hono",
707
+ hint: "Lightweight, ultrafast web framework"
708
+ }, {
709
+ value: "express",
710
+ label: "Express",
711
+ hint: "Fast, unopinionated, minimalist web framework for Node.js"
712
+ }, {
713
+ value: "fastify",
714
+ label: "Fastify",
715
+ hint: "Fast, low-overhead web framework for Node.js"
716
+ }, {
717
+ value: "elysia",
718
+ label: "Elysia",
719
+ hint: "Ergonomic web framework for building backend servers"
720
+ });
697
721
  if (!hasIncompatibleFrontend) backendOptions.push({
698
722
  value: "convex",
699
723
  label: "Convex",
@@ -707,7 +731,7 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
707
731
  const response = await select({
708
732
  message: "Select backend",
709
733
  options: backendOptions,
710
- initialValue: DEFAULT_CONFIG.backend
734
+ initialValue: hasFullstackFrontend ? "self" : DEFAULT_CONFIG.backend
711
735
  });
712
736
  if (isCancel(response)) return exitCancelled("Operation cancelled");
713
737
  return response;
@@ -1086,9 +1110,8 @@ async function getPaymentsChoice(payments, auth, backend, frontends) {
1086
1110
  //#endregion
1087
1111
  //#region src/prompts/runtime.ts
1088
1112
  async function getRuntimeChoice(runtime, backend) {
1089
- if (backend === "convex" || backend === "none") return "none";
1113
+ if (backend === "convex" || backend === "none" || backend === "self") return "none";
1090
1114
  if (runtime !== void 0) return runtime;
1091
- if (backend === "next") return "node";
1092
1115
  const runtimeOptions = [{
1093
1116
  value: "bun",
1094
1117
  label: "Bun",
@@ -1375,7 +1398,7 @@ const getLatestCLIVersion = () => {
1375
1398
  */
1376
1399
  function isTelemetryEnabled() {
1377
1400
  const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
1378
- const BTS_TELEMETRY = "1";
1401
+ const BTS_TELEMETRY = "0";
1379
1402
  if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
1380
1403
  if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
1381
1404
  return true;
@@ -1383,8 +1406,8 @@ function isTelemetryEnabled() {
1383
1406
 
1384
1407
  //#endregion
1385
1408
  //#region src/utils/analytics.ts
1386
- const POSTHOG_API_KEY = "phc_8ZUxEwwfKMajJLvxz1daGd931dYbQrwKNficBmsdIrs";
1387
- const POSTHOG_HOST = "https://us.i.posthog.com";
1409
+ const POSTHOG_API_KEY = "random";
1410
+ const POSTHOG_HOST = "random";
1388
1411
  function generateSessionId() {
1389
1412
  const rand = Math.random().toString(36).slice(2);
1390
1413
  return `cli_${Date.now().toString(36)}${rand}`;
@@ -1765,8 +1788,8 @@ function validateBackendConstraints(config, providedFlags, options) {
1765
1788
  ].includes(f));
1766
1789
  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
1790
  }
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.");
1791
+ if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none" && backend !== "self") {
1792
+ 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
1793
  }
1771
1794
  if (backend === "convex" && providedFlags.has("frontend") && options.frontend) {
1772
1795
  const incompatibleFrontends = options.frontend.filter((f) => f === "solid");
@@ -1796,6 +1819,7 @@ function validateFullConfig(config, providedFlags, options) {
1796
1819
  validateFrontendConstraints(config, providedFlags);
1797
1820
  validateApiConstraints(config, options);
1798
1821
  validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
1822
+ validateSelfBackendCompatibility(providedFlags, options, config);
1799
1823
  validateWorkersCompatibility(providedFlags, options, config);
1800
1824
  if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose 'wrangler' or 'alchemy' for --server-deploy.");
1801
1825
  if (config.addons && config.addons.length > 0) {
@@ -1849,6 +1873,7 @@ const CORE_STACK_FLAGS = new Set([
1849
1873
  "examples",
1850
1874
  "auth",
1851
1875
  "dbSetup",
1876
+ "payments",
1852
1877
  "api",
1853
1878
  "webDeploy",
1854
1879
  "serverDeploy"
@@ -1992,15 +2017,17 @@ const addPackageDependency = async (opts) => {
1992
2017
  if (!pkgJson.dependencies) pkgJson.dependencies = {};
1993
2018
  if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
1994
2019
  for (const pkgName of dependencies) {
1995
- const version = customDependencies[pkgName] || dependencyVersionMap[pkgName];
2020
+ const version = dependencyVersionMap[pkgName];
1996
2021
  if (version) pkgJson.dependencies[pkgName] = version;
1997
2022
  else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
1998
2023
  }
1999
2024
  for (const pkgName of devDependencies) {
2000
- const version = customDevDependencies[pkgName] || dependencyVersionMap[pkgName];
2025
+ const version = dependencyVersionMap[pkgName];
2001
2026
  if (version) pkgJson.devDependencies[pkgName] = version;
2002
2027
  else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
2003
2028
  }
2029
+ for (const [pkgName, version] of Object.entries(customDependencies)) pkgJson.dependencies[pkgName] = version;
2030
+ for (const [pkgName, version] of Object.entries(customDevDependencies)) pkgJson.devDependencies[pkgName] = version;
2004
2031
  await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
2005
2032
  };
2006
2033
 
@@ -2655,8 +2682,12 @@ async function processTemplate(srcPath, destPath, context) {
2655
2682
  }
2656
2683
  handlebars.registerHelper("eq", (a, b) => a === b);
2657
2684
  handlebars.registerHelper("ne", (a, b) => a !== b);
2658
- handlebars.registerHelper("and", (a, b) => a && b);
2659
- handlebars.registerHelper("or", (a, b) => a || b);
2685
+ handlebars.registerHelper("and", (...args) => {
2686
+ return args.slice(0, -1).every((value) => value);
2687
+ });
2688
+ handlebars.registerHelper("or", (...args) => {
2689
+ return args.slice(0, -1).some((value) => value);
2690
+ });
2660
2691
  handlebars.registerHelper("includes", (array, value) => Array.isArray(array) && array.includes(value));
2661
2692
 
2662
2693
  //#endregion
@@ -2718,6 +2749,10 @@ async function setupFrontendTemplates(projectDir, context) {
2718
2749
  const apiWebBaseDir = path.join(PKG_ROOT, `templates/api/${context.api}/web/react/base`);
2719
2750
  if (await fs.pathExists(apiWebBaseDir)) await processAndCopyFiles("**/*", apiWebBaseDir, webAppDir, context);
2720
2751
  }
2752
+ if (context.backend === "self" && reactFramework === "next" && context.api !== "none") {
2753
+ const apiFullstackDir = path.join(PKG_ROOT, `templates/api/${context.api}/fullstack/next`);
2754
+ if (await fs.pathExists(apiFullstackDir)) await processAndCopyFiles("**/*", apiFullstackDir, webAppDir, context);
2755
+ }
2721
2756
  }
2722
2757
  } else if (hasNuxtWeb) {
2723
2758
  const nuxtBaseDir = path.join(PKG_ROOT, "templates/frontend/nuxt");
@@ -2758,35 +2793,52 @@ async function setupFrontendTemplates(projectDir, context) {
2758
2793
  }
2759
2794
  }
2760
2795
  }
2761
- async function setupBackendFramework(projectDir, context) {
2762
- if (context.backend === "none") return;
2796
+ async function setupApiPackage(projectDir, context) {
2797
+ if (context.api === "none") return;
2798
+ const apiPackageDir = path.join(projectDir, "packages/api");
2799
+ await fs.ensureDir(apiPackageDir);
2800
+ const apiServerDir = path.join(PKG_ROOT, `templates/api/${context.api}/server`);
2801
+ if (await fs.pathExists(apiServerDir)) await processAndCopyFiles("**/*", apiServerDir, apiPackageDir, context);
2802
+ }
2803
+ async function setupDbPackage(projectDir, context) {
2804
+ if (context.database === "none" || context.orm === "none") return;
2805
+ const dbPackageDir = path.join(projectDir, "packages/db");
2806
+ await fs.ensureDir(dbPackageDir);
2807
+ const dbBaseDir = path.join(PKG_ROOT, "templates/db/base");
2808
+ if (await fs.pathExists(dbBaseDir)) await processAndCopyFiles("**/*", dbBaseDir, dbPackageDir, context);
2809
+ const dbOrmSrcDir = path.join(PKG_ROOT, `templates/db/${context.orm}/${context.database}`);
2810
+ if (await fs.pathExists(dbOrmSrcDir)) await processAndCopyFiles("**/*", dbOrmSrcDir, dbPackageDir, context);
2811
+ }
2812
+ async function setupConvexBackend(projectDir, context) {
2813
+ const serverAppDir = path.join(projectDir, "apps/server");
2814
+ if (await fs.pathExists(serverAppDir)) await fs.remove(serverAppDir);
2815
+ const convexBackendDestDir = path.join(projectDir, "packages/backend");
2816
+ const convexSrcDir = path.join(PKG_ROOT, "templates/backend/convex/packages/backend");
2817
+ await fs.ensureDir(convexBackendDestDir);
2818
+ if (await fs.pathExists(convexSrcDir)) await processAndCopyFiles("**/*", convexSrcDir, convexBackendDestDir, context);
2819
+ }
2820
+ async function setupServerApp(projectDir, context) {
2763
2821
  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
2822
  await fs.ensureDir(serverAppDir);
2773
- const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/server-base");
2823
+ const serverBaseDir = path.join(PKG_ROOT, "templates/backend/server/base");
2774
2824
  if (await fs.pathExists(serverBaseDir)) await processAndCopyFiles("**/*", serverBaseDir, serverAppDir, context);
2775
2825
  const frameworkSrcDir = path.join(PKG_ROOT, `templates/backend/server/${context.backend}`);
2776
2826
  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
2827
  }
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);
2828
+ async function setupBackendFramework(projectDir, context) {
2829
+ if (context.backend === "none") return;
2830
+ if (context.backend === "convex") {
2831
+ await setupConvexBackend(projectDir, context);
2832
+ return;
2833
+ }
2834
+ if (context.backend === "self") {
2835
+ await setupApiPackage(projectDir, context);
2836
+ await setupDbPackage(projectDir, context);
2837
+ return;
2838
+ }
2839
+ await setupServerApp(projectDir, context);
2840
+ await setupApiPackage(projectDir, context);
2841
+ await setupDbPackage(projectDir, context);
2790
2842
  }
2791
2843
  async function setupAuthTemplate(projectDir, context) {
2792
2844
  if (!context.auth || context.auth === "none") return;
@@ -2866,21 +2918,21 @@ async function setupAuthTemplate(projectDir, context) {
2866
2918
  }
2867
2919
  return;
2868
2920
  }
2869
- if (serverAppDirExists && context.backend !== "convex") {
2921
+ if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
2922
+ const authPackageDir = path.join(projectDir, "packages/auth");
2923
+ await fs.ensureDir(authPackageDir);
2870
2924
  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
- }
2925
+ if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc, authPackageDir, context);
2876
2926
  if (context.orm !== "none" && context.database !== "none") {
2927
+ const dbPackageDir = path.join(projectDir, "packages/db");
2928
+ await fs.ensureDir(dbPackageDir);
2877
2929
  const orm = context.orm;
2878
2930
  const db = context.database;
2879
2931
  let authDbSrc = "";
2880
2932
  if (orm === "drizzle") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/drizzle/${db}`);
2881
2933
  else if (orm === "prisma") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/prisma/${db}`);
2882
2934
  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);
2935
+ if (authDbSrc && await fs.pathExists(authDbSrc)) await processAndCopyFiles("**/*", authDbSrc, dbPackageDir, context);
2884
2936
  }
2885
2937
  }
2886
2938
  if ((hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb) && webAppDirExists) {
@@ -2896,6 +2948,10 @@ async function setupAuthTemplate(projectDir, context) {
2896
2948
  if (reactFramework) {
2897
2949
  const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/${reactFramework}`);
2898
2950
  if (await fs.pathExists(authWebFrameworkSrc)) await processAndCopyFiles("**/*", authWebFrameworkSrc, webAppDir, context);
2951
+ if (context.backend === "self" && reactFramework === "next") {
2952
+ const authFullstackSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/fullstack/next`);
2953
+ if (await fs.pathExists(authFullstackSrc)) await processAndCopyFiles("**/*", authFullstackSrc, webAppDir, context);
2954
+ }
2899
2955
  }
2900
2956
  } else if (hasNuxtWeb) {
2901
2957
  const authWebNuxtSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/nuxt`);
@@ -2926,9 +2982,11 @@ async function setupPaymentsTemplate(projectDir, context) {
2926
2982
  const webAppDir = path.join(projectDir, "apps/web");
2927
2983
  const serverAppDirExists = await fs.pathExists(serverAppDir);
2928
2984
  const webAppDirExists = await fs.pathExists(webAppDir);
2929
- if (serverAppDirExists && context.backend !== "convex") {
2985
+ if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex") {
2986
+ const authPackageDir = path.join(projectDir, "packages/auth");
2987
+ await fs.ensureDir(authPackageDir);
2930
2988
  const paymentsServerSrc = path.join(PKG_ROOT, `templates/payments/${context.payments}/server/base`);
2931
- if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc, serverAppDir, context);
2989
+ if (await fs.pathExists(paymentsServerSrc)) await processAndCopyFiles("**/*", paymentsServerSrc, authPackageDir, context);
2932
2990
  }
2933
2991
  const hasReactWeb = context.frontend.some((f) => [
2934
2992
  "tanstack-router",
@@ -3004,17 +3062,19 @@ async function setupExamplesTemplate(projectDir, context) {
3004
3062
  for (const example of context.examples) {
3005
3063
  if (example === "none") continue;
3006
3064
  const exampleBaseDir = path.join(PKG_ROOT, `templates/examples/${example}`);
3007
- if (serverAppDirExists && context.backend !== "convex" && context.backend !== "none") {
3065
+ if ((serverAppDirExists || context.backend === "self") && context.backend !== "convex" && context.backend !== "none") {
3008
3066
  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);
3067
+ if (context.api !== "none") {
3068
+ const apiPackageDir = path.join(projectDir, "packages/api");
3069
+ await fs.ensureDir(apiPackageDir);
3070
+ const exampleOrmBaseSrc = path.join(exampleServerSrc, context.orm, "base");
3071
+ if (await fs.pathExists(exampleOrmBaseSrc)) await processAndCopyFiles("**/*", exampleOrmBaseSrc, apiPackageDir, context, false);
3012
3072
  }
3013
3073
  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);
3074
+ const dbPackageDir = path.join(projectDir, "packages/db");
3075
+ await fs.ensureDir(dbPackageDir);
3016
3076
  const exampleDbSchemaSrc = path.join(exampleServerSrc, context.orm, context.database);
3017
- if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc, serverAppDir, context, false);
3077
+ if (await fs.pathExists(exampleDbSchemaSrc)) await processAndCopyFiles("**/*", exampleDbSchemaSrc, dbPackageDir, context, false);
3018
3078
  }
3019
3079
  }
3020
3080
  if (webAppDirExists) {
@@ -3034,6 +3094,10 @@ async function setupExamplesTemplate(projectDir, context) {
3034
3094
  if (reactFramework) {
3035
3095
  const exampleWebFrameworkSrc = path.join(exampleWebSrc, reactFramework);
3036
3096
  if (await fs.pathExists(exampleWebFrameworkSrc)) await processAndCopyFiles("**/*", exampleWebFrameworkSrc, webAppDir, context, false);
3097
+ if (context.backend === "self" && reactFramework === "next") {
3098
+ const exampleFullstackSrc = path.join(exampleBaseDir, "fullstack/next");
3099
+ if (await fs.pathExists(exampleFullstackSrc)) await processAndCopyFiles("**/*", exampleFullstackSrc, webAppDir, context, false);
3100
+ }
3037
3101
  }
3038
3102
  }
3039
3103
  } else if (hasNuxtWeb) {
@@ -3081,9 +3145,9 @@ async function handleExtras(projectDir, context) {
3081
3145
  }
3082
3146
  async function setupDockerComposeTemplates(projectDir, context) {
3083
3147
  if (context.dbSetup !== "docker" || context.database === "none") return;
3084
- const serverAppDir = path.join(projectDir, "apps/server");
3148
+ const dbPackageDir = path.join(projectDir, "packages/db");
3085
3149
  const dockerSrcDir = path.join(PKG_ROOT, `templates/db-setup/docker-compose/${context.database}`);
3086
- if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, serverAppDir, context);
3150
+ if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, dbPackageDir, context);
3087
3151
  }
3088
3152
  async function setupDeploymentTemplates(projectDir, context) {
3089
3153
  if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") if (context.webDeploy === "alchemy" && context.serverDeploy === "alchemy") {
@@ -3504,8 +3568,8 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3504
3568
  await addPackageDependency({
3505
3569
  devDependencies: [
3506
3570
  "alchemy",
3507
- "nitropack",
3508
- "dotenv"
3571
+ "dotenv",
3572
+ "@cloudflare/vite-plugin"
3509
3573
  ],
3510
3574
  projectDir: webAppDir
3511
3575
  });
@@ -3533,17 +3597,6 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3533
3597
  defaultImport: "alchemy"
3534
3598
  });
3535
3599
  else alchemyImport.setModuleSpecifier("alchemy/cloudflare/tanstack-start");
3536
- const reactImport = sourceFile.getImportDeclaration("@vitejs/plugin-react");
3537
- let reactPluginIdentifier = "viteReact";
3538
- if (!reactImport) sourceFile.addImportDeclaration({
3539
- moduleSpecifier: "@vitejs/plugin-react",
3540
- defaultImport: "viteReact"
3541
- });
3542
- else {
3543
- const defaultImport = reactImport.getDefaultImport();
3544
- if (defaultImport) reactPluginIdentifier = defaultImport.getText();
3545
- else reactImport.setDefaultImport("viteReact");
3546
- }
3547
3600
  const exportAssignment = sourceFile.getExportAssignment((d) => !d.isExportEquals());
3548
3601
  if (!exportAssignment) return;
3549
3602
  const defineConfigCall = exportAssignment.getExpression();
@@ -3551,47 +3604,11 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3551
3604
  let configObject = defineConfigCall.getArguments()[0];
3552
3605
  if (!configObject) configObject = defineConfigCall.addArgument("{}");
3553
3606
  if (Node.isObjectLiteralExpression(configObject)) {
3554
- if (!configObject.getProperty("build")) configObject.addPropertyAssignment({
3555
- name: "build",
3556
- initializer: `{
3557
- target: "esnext",
3558
- rollupOptions: {
3559
- external: ["node:async_hooks", "cloudflare:workers"],
3560
- },
3561
- }`
3562
- });
3563
3607
  const pluginsProperty = configObject.getProperty("plugins");
3564
3608
  if (pluginsProperty && Node.isPropertyAssignment(pluginsProperty)) {
3565
3609
  const initializer = pluginsProperty.getInitializer();
3566
3610
  if (Node.isArrayLiteralExpression(initializer)) {
3567
- if (!initializer.getElements().some((el) => el.getText().includes("alchemy"))) initializer.addElement("alchemy()");
3568
- const tanstackElements = initializer.getElements().filter((el) => el.getText().includes("tanstackStart"));
3569
- let needsReactPlugin = false;
3570
- tanstackElements.forEach((element) => {
3571
- if (Node.isCallExpression(element)) {
3572
- const args = element.getArguments();
3573
- if (args.length === 0) {
3574
- element.addArgument(`{
3575
- target: "cloudflare-module",
3576
- customViteReactPlugin: true,
3577
- }`);
3578
- needsReactPlugin = true;
3579
- } else if (args.length === 1 && Node.isObjectLiteralExpression(args[0])) {
3580
- const configObj = args[0];
3581
- if (!configObj.getProperty("target")) configObj.addPropertyAssignment({
3582
- name: "target",
3583
- initializer: "\"cloudflare-module\""
3584
- });
3585
- if (!!!configObj.getProperty("customViteReactPlugin")) configObj.addPropertyAssignment({
3586
- name: "customViteReactPlugin",
3587
- initializer: "true"
3588
- });
3589
- needsReactPlugin = true;
3590
- }
3591
- }
3592
- });
3593
- const hasReactPlugin = initializer.getElements().some((el) => Node.isCallExpression(el) && el.getExpression().getText() === reactPluginIdentifier);
3594
- if (needsReactPlugin && !hasReactPlugin) initializer.addElement(`${reactPluginIdentifier}()`);
3611
+ if (!initializer.getElements().some((el) => el.getText().includes("alchemy("))) initializer.addElement("alchemy()");
3595
3612
  }
3596
3613
  } else configObject.addPropertyAssignment({
3597
3614
  name: "plugins",
@@ -3602,16 +3619,6 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3602
3619
  } catch (error) {
3603
3620
  console.warn("Failed to update vite.config.ts:", error);
3604
3621
  }
3605
- const nitroConfigPath = path.join(webAppDir, "nitro.config.ts");
3606
- await fs.writeFile(nitroConfigPath, `import { defineNitroConfig } from "nitropack/config";
3607
-
3608
- export default defineNitroConfig({
3609
- preset: "cloudflare-module",
3610
- cloudflare: {
3611
- nodeCompat: true,
3612
- },
3613
- });
3614
- `, "utf-8");
3615
3622
  }
3616
3623
 
3617
3624
  //#endregion
@@ -3778,7 +3785,7 @@ async function setupTanstackStartWorkersDeploy(projectDir, packageManager) {
3778
3785
  const webAppDir = path.join(projectDir, "apps/web");
3779
3786
  if (!await fs.pathExists(webAppDir)) return;
3780
3787
  await addPackageDependency({
3781
- devDependencies: ["wrangler"],
3788
+ devDependencies: ["wrangler", "@cloudflare/vite-plugin"],
3782
3789
  projectDir: webAppDir
3783
3790
  });
3784
3791
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3795,6 +3802,12 @@ async function setupTanstackStartWorkersDeploy(projectDir, packageManager) {
3795
3802
  if (!await fs.pathExists(viteConfigPath)) return;
3796
3803
  const sourceFile = tsProject.addSourceFileAtPathIfExists(viteConfigPath);
3797
3804
  if (!sourceFile) return;
3805
+ const cfImport = sourceFile.getImportDeclaration("@cloudflare/vite-plugin");
3806
+ if (!cfImport) sourceFile.addImportDeclaration({
3807
+ moduleSpecifier: "@cloudflare/vite-plugin",
3808
+ namedImports: [{ name: "cloudflare" }]
3809
+ });
3810
+ else if (!cfImport.getNamedImports().some((ni) => ni.getName() === "cloudflare")) cfImport.addNamedImport({ name: "cloudflare" });
3798
3811
  const reactImport = sourceFile.getImportDeclaration("@vitejs/plugin-react");
3799
3812
  let reactPluginIdentifier = "viteReact";
3800
3813
  if (!reactImport) sourceFile.addImportDeclaration({
@@ -3814,10 +3827,7 @@ async function setupTanstackStartWorkersDeploy(projectDir, packageManager) {
3814
3827
  const configObj = defineCall.getArguments()[0];
3815
3828
  if (!configObj) return;
3816
3829
  const pluginsArray = ensureArrayProperty(configObj, "plugins");
3817
- const tanstackPluginIndex = pluginsArray.getElements().findIndex((el) => el.getText().includes("tanstackStart("));
3818
- const tanstackPluginText = "tanstackStart({ target: \"cloudflare-module\", customViteReactPlugin: true })";
3819
- if (tanstackPluginIndex === -1) pluginsArray.addElement(tanstackPluginText);
3820
- else pluginsArray.getElements()[tanstackPluginIndex].replaceWithText(tanstackPluginText);
3830
+ if (!pluginsArray.getElements().some((el) => el.getText().includes("cloudflare("))) pluginsArray.insertElement(0, "cloudflare({ viteEnvironment: { name: 'ssr' } })");
3821
3831
  if (!pluginsArray.getElements().some((el) => Node.isCallExpression(el) && el.getExpression().getText() === reactPluginIdentifier)) {
3822
3832
  const nextIndex = pluginsArray.getElements().findIndex((el) => el.getText().includes("tanstackStart(")) + 1;
3823
3833
  if (nextIndex > 0) pluginsArray.insertElement(nextIndex, `${reactPluginIdentifier}()`);
@@ -3955,18 +3965,137 @@ async function addDeploymentToProject(input) {
3955
3965
  }
3956
3966
  }
3957
3967
 
3968
+ //#endregion
3969
+ //#region src/utils/setup-catalogs.ts
3970
+ async function setupCatalogs(projectDir, options) {
3971
+ if (options.packageManager === "npm") return;
3972
+ const packagePaths = [
3973
+ "apps/server",
3974
+ "apps/web",
3975
+ "packages/api",
3976
+ "packages/db",
3977
+ "packages/auth",
3978
+ "packages/backend"
3979
+ ];
3980
+ const packagesInfo = [];
3981
+ for (const pkgPath of packagePaths) {
3982
+ const fullPath = path.join(projectDir, pkgPath);
3983
+ const pkgJsonPath = path.join(fullPath, "package.json");
3984
+ if (await fs.pathExists(pkgJsonPath)) {
3985
+ const pkgJson = await fs.readJson(pkgJsonPath);
3986
+ packagesInfo.push({
3987
+ path: fullPath,
3988
+ dependencies: pkgJson.dependencies || {},
3989
+ devDependencies: pkgJson.devDependencies || {}
3990
+ });
3991
+ }
3992
+ }
3993
+ const catalog = findDuplicateDependencies(packagesInfo, options.projectName);
3994
+ if (Object.keys(catalog).length === 0) return;
3995
+ if (options.packageManager === "bun") await setupBunCatalogs(projectDir, catalog);
3996
+ else if (options.packageManager === "pnpm") await setupPnpmCatalogs(projectDir, catalog);
3997
+ await updatePackageJsonsWithCatalogs(packagesInfo, catalog);
3998
+ }
3999
+ function findDuplicateDependencies(packagesInfo, projectName) {
4000
+ const depCount = /* @__PURE__ */ new Map();
4001
+ const projectScope = `@${projectName}/`;
4002
+ for (const pkg of packagesInfo) {
4003
+ const allDeps = {
4004
+ ...pkg.dependencies,
4005
+ ...pkg.devDependencies
4006
+ };
4007
+ for (const [depName, version] of Object.entries(allDeps)) {
4008
+ if (depName.startsWith(projectScope)) continue;
4009
+ if (version.startsWith("workspace:")) continue;
4010
+ const existing = depCount.get(depName);
4011
+ if (existing) existing.packages.push(pkg.path);
4012
+ else depCount.set(depName, {
4013
+ version,
4014
+ packages: [pkg.path]
4015
+ });
4016
+ }
4017
+ }
4018
+ const catalog = {};
4019
+ for (const [depName, info] of depCount.entries()) if (info.packages.length > 1) catalog[depName] = info.version;
4020
+ return catalog;
4021
+ }
4022
+ async function setupBunCatalogs(projectDir, catalog) {
4023
+ const rootPkgJsonPath = path.join(projectDir, "package.json");
4024
+ const rootPkgJson = await fs.readJson(rootPkgJsonPath);
4025
+ if (!rootPkgJson.workspaces) rootPkgJson.workspaces = {};
4026
+ if (Array.isArray(rootPkgJson.workspaces)) rootPkgJson.workspaces = {
4027
+ packages: rootPkgJson.workspaces,
4028
+ catalog
4029
+ };
4030
+ else if (typeof rootPkgJson.workspaces === "object") {
4031
+ if (!rootPkgJson.workspaces.catalog) rootPkgJson.workspaces.catalog = {};
4032
+ rootPkgJson.workspaces.catalog = {
4033
+ ...rootPkgJson.workspaces.catalog,
4034
+ ...catalog
4035
+ };
4036
+ }
4037
+ await fs.writeJson(rootPkgJsonPath, rootPkgJson, { spaces: 2 });
4038
+ }
4039
+ async function setupPnpmCatalogs(projectDir, catalog) {
4040
+ const workspaceYamlPath = path.join(projectDir, "pnpm-workspace.yaml");
4041
+ if (!await fs.pathExists(workspaceYamlPath)) return;
4042
+ const workspaceContent = await fs.readFile(workspaceYamlPath, "utf-8");
4043
+ const workspaceYaml = yaml.parse(workspaceContent);
4044
+ if (!workspaceYaml.catalog) workspaceYaml.catalog = {};
4045
+ workspaceYaml.catalog = {
4046
+ ...workspaceYaml.catalog,
4047
+ ...catalog
4048
+ };
4049
+ await fs.writeFile(workspaceYamlPath, yaml.stringify(workspaceYaml));
4050
+ }
4051
+ async function updatePackageJsonsWithCatalogs(packagesInfo, catalog) {
4052
+ for (const pkg of packagesInfo) {
4053
+ const pkgJsonPath = path.join(pkg.path, "package.json");
4054
+ const pkgJson = await fs.readJson(pkgJsonPath);
4055
+ let updated = false;
4056
+ if (pkgJson.dependencies) {
4057
+ for (const depName of Object.keys(pkgJson.dependencies)) if (catalog[depName]) {
4058
+ pkgJson.dependencies[depName] = "catalog:";
4059
+ updated = true;
4060
+ }
4061
+ }
4062
+ if (pkgJson.devDependencies) {
4063
+ for (const depName of Object.keys(pkgJson.devDependencies)) if (catalog[depName]) {
4064
+ pkgJson.devDependencies[depName] = "catalog:";
4065
+ updated = true;
4066
+ }
4067
+ }
4068
+ if (updated) await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
4069
+ }
4070
+ }
4071
+
3958
4072
  //#endregion
3959
4073
  //#region src/helpers/addons/examples-setup.ts
3960
4074
  async function setupExamples(config) {
3961
- const { examples, frontend, backend, projectDir } = config;
4075
+ const { examples, frontend, backend, projectDir, orm } = config;
3962
4076
  if (backend === "convex" || !examples || examples.length === 0 || examples[0] === "none") return;
4077
+ const apiDir = path.join(projectDir, "packages/api");
4078
+ if (await fs.pathExists(apiDir) && backend !== "none") {
4079
+ if (orm === "drizzle") await addPackageDependency({
4080
+ dependencies: ["drizzle-orm"],
4081
+ projectDir: apiDir
4082
+ });
4083
+ else if (orm === "prisma") await addPackageDependency({
4084
+ dependencies: ["@prisma/client"],
4085
+ projectDir: apiDir
4086
+ });
4087
+ else if (orm === "mongoose") await addPackageDependency({
4088
+ dependencies: ["mongoose"],
4089
+ projectDir: apiDir
4090
+ });
4091
+ }
3963
4092
  if (examples.includes("ai")) {
3964
4093
  const webClientDir = path.join(projectDir, "apps/web");
3965
4094
  const nativeClientDir = path.join(projectDir, "apps/native");
3966
- const serverDir = path.join(projectDir, "apps/server");
4095
+ const apiDir$1 = path.join(projectDir, "packages/api");
3967
4096
  const webClientDirExists = await fs.pathExists(webClientDir);
3968
4097
  const nativeClientDirExists = await fs.pathExists(nativeClientDir);
3969
- const serverDirExists = await fs.pathExists(serverDir);
4098
+ const apiDirExists = await fs.pathExists(apiDir$1);
3970
4099
  const hasNuxt = frontend.includes("nuxt");
3971
4100
  const hasSvelte = frontend.includes("svelte");
3972
4101
  const hasReactWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next") || frontend.includes("tanstack-start");
@@ -3987,9 +4116,13 @@ async function setupExamples(config) {
3987
4116
  dependencies: ["ai", "@ai-sdk/react"],
3988
4117
  projectDir: nativeClientDir
3989
4118
  });
3990
- if (serverDirExists && backend !== "none") await addPackageDependency({
4119
+ if (apiDirExists && backend !== "none") await addPackageDependency({
3991
4120
  dependencies: ["ai", "@ai-sdk/google"],
3992
- projectDir: serverDir
4121
+ projectDir: apiDir$1
4122
+ });
4123
+ if (backend === "self" && webClientDirExists) await addPackageDependency({
4124
+ dependencies: ["ai", "@ai-sdk/google"],
4125
+ projectDir: webClientDir
3993
4126
  });
3994
4127
  }
3995
4128
  }
@@ -4105,32 +4238,61 @@ function getConvexDependencies(frontend) {
4105
4238
  return deps;
4106
4239
  }
4107
4240
  async function setupApi(config) {
4108
- const { api, projectName, frontend, backend, packageManager, projectDir } = config;
4241
+ const { api, projectName, frontend, backend, packageManager, projectDir, auth } = config;
4109
4242
  const isConvex = backend === "convex";
4110
4243
  const webDir = path.join(projectDir, "apps/web");
4111
4244
  const nativeDir = path.join(projectDir, "apps/native");
4112
4245
  const serverDir = path.join(projectDir, "apps/server");
4113
4246
  const webDirExists = await fs.pathExists(webDir);
4114
4247
  const nativeDirExists = await fs.pathExists(nativeDir);
4115
- const serverDirExists = await fs.pathExists(serverDir);
4248
+ await fs.pathExists(serverDir);
4116
4249
  const frontendType = getFrontendType(frontend);
4117
4250
  if (!isConvex && api !== "none") {
4118
4251
  const apiDeps = getApiDependencies(api, frontendType);
4119
- if (serverDirExists && apiDeps.server) {
4252
+ const apiPackageDir = path.join(projectDir, "packages/api");
4253
+ if (apiDeps.server) {
4120
4254
  await addPackageDependency({
4121
4255
  dependencies: apiDeps.server.dependencies,
4122
- projectDir: serverDir
4256
+ projectDir: apiPackageDir
4257
+ });
4258
+ if (backend === "self" && webDirExists) await addPackageDependency({
4259
+ dependencies: apiDeps.server.dependencies,
4260
+ projectDir: webDir
4261
+ });
4262
+ const frameworkDeps = [];
4263
+ if (backend === "hono") frameworkDeps.push("hono");
4264
+ else if (backend === "elysia") frameworkDeps.push("elysia");
4265
+ else if (backend === "express") frameworkDeps.push("express", "@types/express");
4266
+ else if (backend === "fastify") frameworkDeps.push("fastify");
4267
+ else if (backend === "self") {
4268
+ if (frontend.includes("next")) frameworkDeps.push("next");
4269
+ }
4270
+ if (frameworkDeps.length > 0) await addPackageDependency({
4271
+ dependencies: frameworkDeps,
4272
+ projectDir: apiPackageDir
4123
4273
  });
4124
4274
  if (api === "trpc") {
4125
4275
  if (backend === "hono") await addPackageDependency({
4126
4276
  dependencies: ["@hono/trpc-server"],
4127
- projectDir: serverDir
4277
+ projectDir: apiPackageDir
4128
4278
  });
4129
4279
  else if (backend === "elysia") await addPackageDependency({
4130
4280
  dependencies: ["@elysiajs/trpc"],
4131
- projectDir: serverDir
4281
+ projectDir: apiPackageDir
4282
+ });
4283
+ else if (backend === "express") await addPackageDependency({
4284
+ dependencies: ["@trpc/server"],
4285
+ projectDir: apiPackageDir
4286
+ });
4287
+ else if (backend === "fastify") await addPackageDependency({
4288
+ dependencies: ["@trpc/server"],
4289
+ projectDir: apiPackageDir
4132
4290
  });
4133
4291
  }
4292
+ if (auth === "better-auth") await addPackageDependency({
4293
+ dependencies: ["better-auth"],
4294
+ projectDir: apiPackageDir
4295
+ });
4134
4296
  }
4135
4297
  if (webDirExists && apiDeps.web) await addPackageDependency({
4136
4298
  dependencies: apiDeps.web.dependencies,
@@ -4174,7 +4336,7 @@ async function setupApi(config) {
4174
4336
  //#endregion
4175
4337
  //#region src/helpers/core/backend-setup.ts
4176
4338
  async function setupBackendDependencies(config) {
4177
- const { backend, runtime, api, projectDir } = config;
4339
+ const { backend, runtime, api, auth, examples, projectDir } = config;
4178
4340
  if (backend === "convex") return;
4179
4341
  const framework = backend;
4180
4342
  const serverDir = path.join(projectDir, "apps/server");
@@ -4202,6 +4364,13 @@ async function setupBackendDependencies(config) {
4202
4364
  dependencies.push("fastify", "@fastify/cors");
4203
4365
  if (runtime === "node") devDependencies.push("tsx", "@types/node");
4204
4366
  }
4367
+ if (api === "trpc") {
4368
+ if (framework === "express") dependencies.push("@trpc/server");
4369
+ else if (framework === "fastify") dependencies.push("@trpc/server");
4370
+ else if (runtime === "workers") dependencies.push("@trpc/server");
4371
+ } else if (api === "orpc") dependencies.push("@orpc/server", "@orpc/openapi", "@orpc/zod");
4372
+ if (auth === "better-auth") dependencies.push("better-auth");
4373
+ if (examples.includes("ai")) dependencies.push("ai", "@ai-sdk/google");
4205
4374
  if (runtime === "bun") devDependencies.push("@types/bun");
4206
4375
  if (dependencies.length > 0 || devDependencies.length > 0) await addPackageDependency({
4207
4376
  dependencies,
@@ -4220,7 +4389,7 @@ async function setupAuth(config) {
4220
4389
  const nativeDir = path.join(projectDir, "apps/native");
4221
4390
  const clientDirExists = await fs.pathExists(clientDir);
4222
4391
  const nativeDirExists = await fs.pathExists(nativeDir);
4223
- const serverDirExists = await fs.pathExists(serverDir);
4392
+ await fs.pathExists(serverDir);
4224
4393
  try {
4225
4394
  if (backend === "convex") {
4226
4395
  if (auth === "clerk" && clientDirExists) {
@@ -4276,9 +4445,11 @@ async function setupAuth(config) {
4276
4445
  });
4277
4446
  return;
4278
4447
  }
4279
- if (serverDirExists && auth === "better-auth") await addPackageDependency({
4448
+ const authPackageDir = path.join(projectDir, "packages/auth");
4449
+ const authPackageDirExists = await fs.pathExists(authPackageDir);
4450
+ if (authPackageDirExists && auth === "better-auth") await addPackageDependency({
4280
4451
  dependencies: ["better-auth"],
4281
- projectDir: serverDir
4452
+ projectDir: authPackageDir
4282
4453
  });
4283
4454
  if (frontend.some((f) => [
4284
4455
  "react-router",
@@ -4300,9 +4471,9 @@ async function setupAuth(config) {
4300
4471
  dependencies: ["better-auth", "@better-auth/expo"],
4301
4472
  projectDir: nativeDir
4302
4473
  });
4303
- if (serverDirExists) await addPackageDependency({
4474
+ if (authPackageDirExists) await addPackageDependency({
4304
4475
  dependencies: ["@better-auth/expo"],
4305
- projectDir: serverDir
4476
+ projectDir: authPackageDir
4306
4477
  });
4307
4478
  }
4308
4479
  }
@@ -4321,6 +4492,34 @@ function generateAuthSecret(length = 32) {
4321
4492
 
4322
4493
  //#endregion
4323
4494
  //#region src/helpers/core/env-setup.ts
4495
+ function getClientServerVar(frontend, backend) {
4496
+ const hasNextJs = frontend.includes("next");
4497
+ const hasNuxt = frontend.includes("nuxt");
4498
+ const hasSvelte = frontend.includes("svelte");
4499
+ if (backend === "self") return {
4500
+ key: "",
4501
+ value: "",
4502
+ write: false
4503
+ };
4504
+ let key = "VITE_SERVER_URL";
4505
+ if (hasNextJs) key = "NEXT_PUBLIC_SERVER_URL";
4506
+ else if (hasNuxt) key = "NUXT_PUBLIC_SERVER_URL";
4507
+ else if (hasSvelte) key = "PUBLIC_SERVER_URL";
4508
+ return {
4509
+ key,
4510
+ value: "http://localhost:3000",
4511
+ write: true
4512
+ };
4513
+ }
4514
+ function getConvexVar(frontend) {
4515
+ const hasNextJs = frontend.includes("next");
4516
+ const hasNuxt = frontend.includes("nuxt");
4517
+ const hasSvelte = frontend.includes("svelte");
4518
+ if (hasNextJs) return "NEXT_PUBLIC_CONVEX_URL";
4519
+ if (hasNuxt) return "NUXT_PUBLIC_CONVEX_URL";
4520
+ if (hasSvelte) return "PUBLIC_CONVEX_URL";
4521
+ return "VITE_CONVEX_URL";
4522
+ }
4324
4523
  async function addEnvVariablesToFile(filePath, variables) {
4325
4524
  await fs.ensureDir(path.dirname(filePath));
4326
4525
  let envContent = "";
@@ -4379,22 +4578,13 @@ async function setupEnvironmentVariables(config) {
4379
4578
  if (hasReactRouter || hasTanStackRouter || hasTanStackStart || hasNextJs || hasNuxt || hasSolid || hasSvelte) {
4380
4579
  const clientDir = path.join(projectDir, "apps/web");
4381
4580
  if (await fs.pathExists(clientDir)) {
4382
- let envVarName = "VITE_SERVER_URL";
4383
- let serverUrl = "http://localhost:3000";
4384
- if (hasNextJs) envVarName = "NEXT_PUBLIC_SERVER_URL";
4385
- else if (hasNuxt) envVarName = "NUXT_PUBLIC_SERVER_URL";
4386
- else if (hasSvelte) envVarName = "PUBLIC_SERVER_URL";
4387
- if (backend === "convex") {
4388
- if (hasNextJs) envVarName = "NEXT_PUBLIC_CONVEX_URL";
4389
- else if (hasNuxt) envVarName = "NUXT_PUBLIC_CONVEX_URL";
4390
- else if (hasSvelte) envVarName = "PUBLIC_CONVEX_URL";
4391
- else envVarName = "VITE_CONVEX_URL";
4392
- serverUrl = "https://<YOUR_CONVEX_URL>";
4393
- }
4581
+ const baseVar = getClientServerVar(frontend, backend);
4582
+ const envVarName = backend === "convex" ? getConvexVar(frontend) : baseVar.key;
4583
+ const serverUrl = backend === "convex" ? "https://<YOUR_CONVEX_URL>" : baseVar.value;
4394
4584
  const clientVars = [{
4395
4585
  key: envVarName,
4396
4586
  value: serverUrl,
4397
- condition: true
4587
+ condition: backend === "convex" ? true : baseVar.write
4398
4588
  }];
4399
4589
  if (backend === "convex" && auth === "clerk") {
4400
4590
  if (hasNextJs) clientVars.push({
@@ -4485,10 +4675,9 @@ async function setupEnvironmentVariables(config) {
4485
4675
  return;
4486
4676
  }
4487
4677
  const serverDir = path.join(projectDir, "apps/server");
4488
- if (!await fs.pathExists(serverDir)) return;
4489
- const envPath = path.join(serverDir, ".env");
4490
4678
  let corsOrigin = "http://localhost:3001";
4491
- if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
4679
+ if (backend === "self") corsOrigin = "http://localhost:3001";
4680
+ else if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
4492
4681
  let databaseUrl = null;
4493
4682
  if (database !== "none" && dbSetup === "none") switch (database) {
4494
4683
  case "postgres":
@@ -4502,15 +4691,13 @@ async function setupEnvironmentVariables(config) {
4502
4691
  break;
4503
4692
  case "sqlite":
4504
4693
  if (config.runtime === "workers") databaseUrl = "http://127.0.0.1:8080";
4505
- else databaseUrl = "file:./local.db";
4694
+ else {
4695
+ const dbAppDir = backend === "self" ? "apps/web" : "apps/server";
4696
+ databaseUrl = `file:${path.join(config.projectDir, dbAppDir, "local.db")}`;
4697
+ }
4506
4698
  break;
4507
4699
  }
4508
4700
  const serverVars = [
4509
- {
4510
- key: "CORS_ORIGIN",
4511
- value: corsOrigin,
4512
- condition: true
4513
- },
4514
4701
  {
4515
4702
  key: "BETTER_AUTH_SECRET",
4516
4703
  value: generateAuthSecret(),
@@ -4518,19 +4705,9 @@ async function setupEnvironmentVariables(config) {
4518
4705
  },
4519
4706
  {
4520
4707
  key: "BETTER_AUTH_URL",
4521
- value: "http://localhost:3000",
4708
+ value: backend === "self" ? "http://localhost:3001" : "http://localhost:3000",
4522
4709
  condition: !!auth
4523
4710
  },
4524
- {
4525
- key: "DATABASE_URL",
4526
- value: databaseUrl,
4527
- condition: database !== "none" && dbSetup === "none"
4528
- },
4529
- {
4530
- key: "GOOGLE_GENERATIVE_AI_API_KEY",
4531
- value: "",
4532
- condition: examples?.includes("ai") || false
4533
- },
4534
4711
  {
4535
4712
  key: "POLAR_ACCESS_TOKEN",
4536
4713
  value: "",
@@ -4540,9 +4717,27 @@ async function setupEnvironmentVariables(config) {
4540
4717
  key: "POLAR_SUCCESS_URL",
4541
4718
  value: `${corsOrigin}/success?checkout_id={CHECKOUT_ID}`,
4542
4719
  condition: config.payments === "polar"
4720
+ },
4721
+ {
4722
+ key: "CORS_ORIGIN",
4723
+ value: corsOrigin,
4724
+ condition: true
4725
+ },
4726
+ {
4727
+ key: "GOOGLE_GENERATIVE_AI_API_KEY",
4728
+ value: "",
4729
+ condition: examples?.includes("ai") || false
4730
+ },
4731
+ {
4732
+ key: "DATABASE_URL",
4733
+ value: databaseUrl,
4734
+ condition: database !== "none" && dbSetup === "none"
4543
4735
  }
4544
4736
  ];
4545
- await addEnvVariablesToFile(envPath, serverVars);
4737
+ if (backend === "self") {
4738
+ const webDir = path.join(projectDir, "apps/web");
4739
+ if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverVars);
4740
+ } else if (await fs.pathExists(serverDir)) await addEnvVariablesToFile(path.join(serverDir, ".env"), serverVars);
4546
4741
  const isUnifiedAlchemy = webDeploy === "alchemy" && serverDeploy === "alchemy";
4547
4742
  const isIndividualAlchemy = webDeploy === "alchemy" || serverDeploy === "alchemy";
4548
4743
  if (isUnifiedAlchemy) {
@@ -4562,12 +4757,15 @@ async function setupEnvironmentVariables(config) {
4562
4757
  }]);
4563
4758
  }
4564
4759
  if (serverDeploy === "alchemy") {
4565
- const serverDir$1 = path.join(projectDir, "apps/server");
4566
- if (await fs.pathExists(serverDir$1)) await addEnvVariablesToFile(path.join(serverDir$1, ".env"), [{
4760
+ const serverAlchemyVars = [{
4567
4761
  key: "ALCHEMY_PASSWORD",
4568
4762
  value: "please-change-this",
4569
4763
  condition: true
4570
- }]);
4764
+ }];
4765
+ if (backend === "self") {
4766
+ const webDir = path.join(projectDir, "apps/web");
4767
+ if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverAlchemyVars);
4768
+ } else await addEnvVariablesToFile(path.join(serverDir, ".env"), serverAlchemyVars);
4571
4769
  }
4572
4770
  }
4573
4771
  }
@@ -4575,9 +4773,10 @@ async function setupEnvironmentVariables(config) {
4575
4773
  //#endregion
4576
4774
  //#region src/helpers/database-providers/d1-setup.ts
4577
4775
  async function setupCloudflareD1(config) {
4578
- const { projectDir, serverDeploy, orm } = config;
4776
+ const { projectDir, serverDeploy, orm, backend } = config;
4579
4777
  if (serverDeploy === "wrangler") {
4580
- const envPath = path.join(projectDir, "apps/server", ".env");
4778
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
4779
+ const envPath = path.join(projectDir, targetApp, ".env");
4581
4780
  const variables = [
4582
4781
  {
4583
4782
  key: "CLOUDFLARE_ACCOUNT_ID",
@@ -4600,16 +4799,17 @@ async function setupCloudflareD1(config) {
4600
4799
  } catch (_err) {}
4601
4800
  }
4602
4801
  if ((serverDeploy === "wrangler" || serverDeploy === "alchemy") && orm === "prisma") {
4603
- const envPath = path.join(projectDir, "apps/server", ".env");
4802
+ const targetApp2 = backend === "self" ? "apps/web" : "apps/server";
4803
+ const envPath = path.join(projectDir, targetApp2, ".env");
4604
4804
  const variables = [{
4605
4805
  key: "DATABASE_URL",
4606
- value: "file:./local.db",
4806
+ value: `file:${path.join(projectDir, "apps/server", "local.db")}`,
4607
4807
  condition: true
4608
4808
  }];
4609
4809
  try {
4610
4810
  await addEnvVariablesToFile(envPath, variables);
4611
4811
  } catch (_err) {}
4612
- const serverDir = path.join(projectDir, "apps/server");
4812
+ const serverDir = path.join(projectDir, backend === "self" ? "apps/web" : "apps/server");
4613
4813
  await addPackageDependency({
4614
4814
  dependencies: ["@prisma/adapter-d1"],
4615
4815
  projectDir: serverDir
@@ -4702,9 +4902,10 @@ async function initMongoDBAtlas(serverDir) {
4702
4902
  return null;
4703
4903
  }
4704
4904
  }
4705
- async function writeEnvFile$3(projectDir, config) {
4905
+ async function writeEnvFile$3(projectDir, backend, config) {
4706
4906
  try {
4707
- const envPath = path.join(projectDir, "apps/server", ".env");
4907
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
4908
+ const envPath = path.join(projectDir, targetApp, ".env");
4708
4909
  const variables = [{
4709
4910
  key: "DATABASE_URL",
4710
4911
  value: config?.connectionString ?? "mongodb://localhost:27017/mydb",
@@ -4733,16 +4934,16 @@ ${pc.green("MongoDB Atlas Manual Setup Instructions:")}
4733
4934
  `);
4734
4935
  }
4735
4936
  async function setupMongoDBAtlas(config, cliInput) {
4736
- const { projectDir } = config;
4937
+ const { projectDir, backend } = config;
4737
4938
  const manualDb = cliInput?.manualDb ?? false;
4738
4939
  const mainSpinner = spinner();
4739
4940
  mainSpinner.start("Setting up MongoDB Atlas...");
4740
- const serverDir = path.join(projectDir, "apps/server");
4941
+ const serverDir = path.join(projectDir, "packages/db");
4741
4942
  try {
4742
4943
  await fs.ensureDir(serverDir);
4743
4944
  if (manualDb) {
4744
4945
  mainSpinner.stop("MongoDB Atlas manual setup selected");
4745
- await writeEnvFile$3(projectDir);
4946
+ await writeEnvFile$3(projectDir, backend);
4746
4947
  displayManualSetupInstructions$3();
4747
4948
  return;
4748
4949
  }
@@ -4762,25 +4963,25 @@ async function setupMongoDBAtlas(config, cliInput) {
4762
4963
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
4763
4964
  if (mode === "manual") {
4764
4965
  mainSpinner.stop("MongoDB Atlas manual setup selected");
4765
- await writeEnvFile$3(projectDir);
4966
+ await writeEnvFile$3(projectDir, backend);
4766
4967
  displayManualSetupInstructions$3();
4767
4968
  return;
4768
4969
  }
4769
4970
  mainSpinner.stop("MongoDB Atlas setup ready");
4770
4971
  const config$1 = await initMongoDBAtlas(serverDir);
4771
4972
  if (config$1) {
4772
- await writeEnvFile$3(projectDir, config$1);
4973
+ await writeEnvFile$3(projectDir, backend, config$1);
4773
4974
  log.success(pc.green("MongoDB Atlas setup complete! Connection saved to .env file."));
4774
4975
  } else {
4775
4976
  log.warn(pc.yellow("Falling back to local MongoDB configuration"));
4776
- await writeEnvFile$3(projectDir);
4977
+ await writeEnvFile$3(projectDir, backend);
4777
4978
  displayManualSetupInstructions$3();
4778
4979
  }
4779
4980
  } catch (error) {
4780
4981
  mainSpinner.stop(pc.red("MongoDB Atlas setup failed"));
4781
4982
  consola.error(pc.red(`Error during MongoDB Atlas setup: ${error instanceof Error ? error.message : String(error)}`));
4782
4983
  try {
4783
- await writeEnvFile$3(projectDir);
4984
+ await writeEnvFile$3(projectDir, backend);
4784
4985
  displayManualSetupInstructions$3();
4785
4986
  } catch {}
4786
4987
  }
@@ -4837,7 +5038,7 @@ async function executeNeonCommand(packageManager, commandArgsString, spinnerText
4837
5038
  }
4838
5039
  async function createNeonProject(projectName, regionId, packageManager) {
4839
5040
  try {
4840
- const commandArgsString = `neonctl projects create --name ${projectName} --region-id ${regionId} --output json`;
5041
+ const commandArgsString = `neonctl@latest projects create --name ${projectName} --region-id ${regionId} --output json`;
4841
5042
  const { stdout } = await executeNeonCommand(packageManager, commandArgsString, `Creating Neon project "${projectName}"...`);
4842
5043
  const response = JSON.parse(stdout);
4843
5044
  if (response.project && response.connection_uris && response.connection_uris.length > 0) {
@@ -4857,8 +5058,9 @@ async function createNeonProject(projectName, regionId, packageManager) {
4857
5058
  consola$1.error(pc.red("Failed to create Neon project"));
4858
5059
  }
4859
5060
  }
4860
- async function writeEnvFile$2(projectDir, config) {
4861
- const envPath = path.join(projectDir, "apps/server", ".env");
5061
+ async function writeEnvFile$2(projectDir, backend, config) {
5062
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5063
+ const envPath = path.join(projectDir, targetApp, ".env");
4862
5064
  const variables = [{
4863
5065
  key: "DATABASE_URL",
4864
5066
  value: config?.connectionString ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
@@ -4867,16 +5069,17 @@ async function writeEnvFile$2(projectDir, config) {
4867
5069
  await addEnvVariablesToFile(envPath, variables);
4868
5070
  return true;
4869
5071
  }
4870
- async function setupWithNeonDb(projectDir, packageManager) {
5072
+ async function setupWithNeonDb(projectDir, packageManager, backend) {
4871
5073
  try {
4872
5074
  const s = spinner();
4873
5075
  s.start("Creating Neon database using neondb...");
4874
- const serverDir = path.join(projectDir, "apps/server");
4875
- await fs.ensureDir(serverDir);
4876
- const packageCmd = getPackageExecutionCommand(packageManager, "neondb --yes");
5076
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5077
+ const targetDir = path.join(projectDir, targetApp);
5078
+ await fs.ensureDir(targetDir);
5079
+ const packageCmd = getPackageExecutionCommand(packageManager, "neondb@latest --yes");
4877
5080
  await execa(packageCmd, {
4878
5081
  shell: true,
4879
- cwd: serverDir
5082
+ cwd: targetDir
4880
5083
  });
4881
5084
  s.stop(pc.green("Neon database created successfully!"));
4882
5085
  return true;
@@ -4885,23 +5088,23 @@ async function setupWithNeonDb(projectDir, packageManager) {
4885
5088
  throw error;
4886
5089
  }
4887
5090
  }
4888
- function displayManualSetupInstructions$2() {
5091
+ function displayManualSetupInstructions$2(target) {
4889
5092
  log.info(`Manual Neon PostgreSQL Setup Instructions:
4890
5093
 
4891
5094
  1. Visit https://neon.tech and create an account
4892
5095
  2. Create a new project from the dashboard
4893
5096
  3. Get your connection string
4894
- 4. Add the database URL to the .env file in apps/server/.env
5097
+ 4. Add the database URL to the .env file in ${target}/.env
4895
5098
 
4896
5099
  DATABASE_URL="your_connection_string"`);
4897
5100
  }
4898
5101
  async function setupNeonPostgres(config, cliInput) {
4899
- const { packageManager, projectDir } = config;
5102
+ const { packageManager, projectDir, backend } = config;
4900
5103
  const manualDb = cliInput?.manualDb ?? false;
4901
5104
  try {
4902
5105
  if (manualDb) {
4903
- await writeEnvFile$2(projectDir);
4904
- displayManualSetupInstructions$2();
5106
+ await writeEnvFile$2(projectDir, backend);
5107
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
4905
5108
  return;
4906
5109
  }
4907
5110
  const mode = await select({
@@ -4919,8 +5122,8 @@ async function setupNeonPostgres(config, cliInput) {
4919
5122
  });
4920
5123
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
4921
5124
  if (mode === "manual") {
4922
- await writeEnvFile$2(projectDir);
4923
- displayManualSetupInstructions$2();
5125
+ await writeEnvFile$2(projectDir, backend);
5126
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
4924
5127
  return;
4925
5128
  }
4926
5129
  const setupMethod = await select({
@@ -4937,7 +5140,7 @@ async function setupNeonPostgres(config, cliInput) {
4937
5140
  initialValue: "neondb"
4938
5141
  });
4939
5142
  if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
4940
- if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager);
5143
+ if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager, backend);
4941
5144
  else {
4942
5145
  const suggestedProjectName = path.basename(projectDir);
4943
5146
  const projectName = await text({
@@ -4955,22 +5158,22 @@ async function setupNeonPostgres(config, cliInput) {
4955
5158
  if (!neonConfig) throw new Error("Failed to create project - couldn't get connection information");
4956
5159
  const finalSpinner = spinner();
4957
5160
  finalSpinner.start("Configuring database connection");
4958
- await fs.ensureDir(path.join(projectDir, "apps/server"));
4959
- await writeEnvFile$2(projectDir, neonConfig);
5161
+ await writeEnvFile$2(projectDir, backend, neonConfig);
4960
5162
  finalSpinner.stop("Neon database configured!");
4961
5163
  }
4962
5164
  } catch (error) {
4963
5165
  if (error instanceof Error) consola$1.error(pc.red(error.message));
4964
- await writeEnvFile$2(projectDir);
4965
- displayManualSetupInstructions$2();
5166
+ await writeEnvFile$2(projectDir, backend);
5167
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
4966
5168
  }
4967
5169
  }
4968
5170
 
4969
5171
  //#endregion
4970
5172
  //#region src/helpers/database-providers/planetscale-setup.ts
4971
5173
  async function setupPlanetScale(config) {
4972
- const { projectDir, database, orm } = config;
4973
- const envPath = path.join(projectDir, "apps/server", ".env");
5174
+ const { projectDir, database, orm, backend } = config;
5175
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5176
+ const envPath = path.join(projectDir, targetApp, ".env");
4974
5177
  if (database === "mysql" && orm === "drizzle") {
4975
5178
  const variables = [
4976
5179
  {
@@ -4994,7 +5197,7 @@ async function setupPlanetScale(config) {
4994
5197
  condition: true
4995
5198
  }
4996
5199
  ];
4997
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5200
+ await fs.ensureDir(path.join(projectDir, targetApp));
4998
5201
  await addEnvVariablesToFile(envPath, variables);
4999
5202
  }
5000
5203
  if (database === "postgres" && orm === "prisma") {
@@ -5003,7 +5206,7 @@ async function setupPlanetScale(config) {
5003
5206
  value: "postgresql://username:password@host/database?sslaccept=strict",
5004
5207
  condition: true
5005
5208
  }];
5006
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5209
+ await fs.ensureDir(path.join(projectDir, targetApp));
5007
5210
  await addEnvVariablesToFile(envPath, variables);
5008
5211
  }
5009
5212
  if (database === "postgres" && orm === "drizzle") {
@@ -5012,7 +5215,7 @@ async function setupPlanetScale(config) {
5012
5215
  value: "postgresql://username:password@host/database?sslmode=verify-full",
5013
5216
  condition: true
5014
5217
  }];
5015
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5218
+ await fs.ensureDir(path.join(projectDir, targetApp));
5016
5219
  await addEnvVariablesToFile(envPath, variables);
5017
5220
  }
5018
5221
  if (database === "mysql" && orm === "prisma") {
@@ -5021,7 +5224,7 @@ async function setupPlanetScale(config) {
5021
5224
  value: "mysql://username:password@host/database?sslaccept=strict",
5022
5225
  condition: true
5023
5226
  }];
5024
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5227
+ await fs.ensureDir(path.join(projectDir, targetApp));
5025
5228
  await addEnvVariablesToFile(envPath, variables);
5026
5229
  }
5027
5230
  }
@@ -5091,7 +5294,7 @@ async function initPrismaDatabase(serverDir, packageManager) {
5091
5294
  try {
5092
5295
  const prismaDir = path.join(serverDir, "prisma");
5093
5296
  await fs.ensureDir(prismaDir);
5094
- log.info("Starting Prisma PostgreSQL setup. Please follow the instructions below:");
5297
+ log.info("Starting Prisma PostgreSQL setup.");
5095
5298
  const prismaInitCommand = getPackageExecutionCommand(packageManager, "prisma init --db");
5096
5299
  await execa(prismaInitCommand, {
5097
5300
  cwd: serverDir,
@@ -5113,9 +5316,10 @@ async function initPrismaDatabase(serverDir, packageManager) {
5113
5316
  return null;
5114
5317
  }
5115
5318
  }
5116
- async function writeEnvFile$1(projectDir, config) {
5319
+ async function writeEnvFile$1(projectDir, backend, config) {
5117
5320
  try {
5118
- const envPath = path.join(projectDir, "apps/server", ".env");
5321
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5322
+ const envPath = path.join(projectDir, targetApp, ".env");
5119
5323
  const variables = [{
5120
5324
  key: "DATABASE_URL",
5121
5325
  value: config?.databaseUrl ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
@@ -5131,31 +5335,32 @@ async function writeEnvFile$1(projectDir, config) {
5131
5335
  consola$1.error("Failed to update environment configuration");
5132
5336
  }
5133
5337
  }
5134
- async function addDotenvImportToPrismaConfig(projectDir) {
5338
+ async function addDotenvImportToPrismaConfig(projectDir, backend) {
5135
5339
  try {
5136
- const prismaConfigPath = path.join(projectDir, "apps/server/prisma.config.ts");
5340
+ const prismaConfigPath = path.join(projectDir, "packages/db/prisma.config.ts");
5137
5341
  let content = await fs.readFile(prismaConfigPath, "utf8");
5138
- content = `import "dotenv/config";\n${content}`;
5342
+ content = `import dotenv from "dotenv";\ndotenv.config({ path: "${backend === "self" ? "../../apps/web/.env" : "../../apps/server/.env"}" });\n${content}`;
5139
5343
  await fs.writeFile(prismaConfigPath, content);
5140
5344
  } catch (_error) {
5141
5345
  consola$1.error("Failed to update prisma.config.ts");
5142
5346
  }
5143
5347
  }
5144
- function displayManualSetupInstructions$1() {
5348
+ function displayManualSetupInstructions$1(target) {
5145
5349
  log.info(`Manual Prisma PostgreSQL Setup Instructions:
5146
5350
 
5147
5351
  1. Visit https://console.prisma.io and create an account
5148
5352
  2. Create a new PostgreSQL database from the dashboard
5149
5353
  3. Get your database URL
5150
- 4. Add the database URL to the .env file in apps/server/.env
5354
+ 4. Add the database URL to the .env file in ${target}/.env
5151
5355
 
5152
5356
  DATABASE_URL="your_database_url"`);
5153
5357
  }
5154
- async function addPrismaAccelerateExtension(serverDir) {
5358
+ async function addPrismaAccelerateExtension(projectDir) {
5155
5359
  try {
5360
+ const dbPackageDir = path.join(projectDir, "packages/db");
5156
5361
  await addPackageDependency({
5157
5362
  dependencies: ["@prisma/extension-accelerate"],
5158
- projectDir: serverDir
5363
+ projectDir: dbPackageDir
5159
5364
  });
5160
5365
  return true;
5161
5366
  } catch (_error) {
@@ -5164,14 +5369,14 @@ async function addPrismaAccelerateExtension(serverDir) {
5164
5369
  }
5165
5370
  }
5166
5371
  async function setupPrismaPostgres(config, cliInput) {
5167
- const { packageManager, projectDir, orm } = config;
5372
+ const { packageManager, projectDir, orm, backend } = config;
5168
5373
  const manualDb = cliInput?.manualDb ?? false;
5169
- const serverDir = path.join(projectDir, "apps/server");
5374
+ const dbDir = path.join(projectDir, "packages/db");
5170
5375
  try {
5171
- await fs.ensureDir(serverDir);
5376
+ await fs.ensureDir(dbDir);
5172
5377
  if (manualDb) {
5173
- await writeEnvFile$1(projectDir);
5174
- displayManualSetupInstructions$1();
5378
+ await writeEnvFile$1(projectDir, backend);
5379
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5175
5380
  return;
5176
5381
  }
5177
5382
  const mode = await select({
@@ -5189,8 +5394,8 @@ async function setupPrismaPostgres(config, cliInput) {
5189
5394
  });
5190
5395
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5191
5396
  if (mode === "manual") {
5192
- await writeEnvFile$1(projectDir);
5193
- displayManualSetupInstructions$1();
5397
+ await writeEnvFile$1(projectDir, backend);
5398
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5194
5399
  return;
5195
5400
  }
5196
5401
  const setupOptions = [{
@@ -5210,26 +5415,26 @@ async function setupPrismaPostgres(config, cliInput) {
5210
5415
  });
5211
5416
  if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
5212
5417
  let prismaConfig = null;
5213
- if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(serverDir, packageManager, orm);
5214
- else prismaConfig = await initPrismaDatabase(serverDir, packageManager);
5418
+ if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(dbDir, packageManager, orm);
5419
+ else prismaConfig = await initPrismaDatabase(dbDir, packageManager);
5215
5420
  if (prismaConfig) {
5216
- await writeEnvFile$1(projectDir, prismaConfig);
5421
+ await writeEnvFile$1(projectDir, backend, prismaConfig);
5217
5422
  if (orm === "prisma") {
5218
- await addDotenvImportToPrismaConfig(projectDir);
5219
- await addPrismaAccelerateExtension(serverDir);
5423
+ await addDotenvImportToPrismaConfig(projectDir, backend);
5424
+ await addPrismaAccelerateExtension(projectDir);
5220
5425
  }
5221
5426
  const connectionType = orm === "drizzle" ? "direct connection" : "Prisma Accelerate";
5222
5427
  log.success(pc.green(`Prisma Postgres database configured successfully with ${connectionType}!`));
5223
5428
  if (prismaConfig.claimUrl) log.info(pc.blue(`Claim URL saved to .env: ${prismaConfig.claimUrl}`));
5224
5429
  } else {
5225
- await writeEnvFile$1(projectDir);
5226
- displayManualSetupInstructions$1();
5430
+ await writeEnvFile$1(projectDir, backend);
5431
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5227
5432
  }
5228
5433
  } catch (error) {
5229
5434
  consola$1.error(pc.red(`Error during Prisma Postgres setup: ${error instanceof Error ? error.message : String(error)}`));
5230
5435
  try {
5231
- await writeEnvFile$1(projectDir);
5232
- displayManualSetupInstructions$1();
5436
+ await writeEnvFile$1(projectDir, backend);
5437
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5233
5438
  } catch {}
5234
5439
  log.info("Setup completed with manual configuration required.");
5235
5440
  }
@@ -5237,9 +5442,10 @@ async function setupPrismaPostgres(config, cliInput) {
5237
5442
 
5238
5443
  //#endregion
5239
5444
  //#region src/helpers/database-providers/supabase-setup.ts
5240
- async function writeSupabaseEnvFile(projectDir, databaseUrl) {
5445
+ async function writeSupabaseEnvFile(projectDir, backend, databaseUrl) {
5241
5446
  try {
5242
- const envPath = path.join(projectDir, "apps/server", ".env");
5447
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5448
+ const envPath = path.join(projectDir, targetApp, ".env");
5243
5449
  const dbUrlToUse = databaseUrl || "postgresql://postgres:postgres@127.0.0.1:54322/postgres";
5244
5450
  await addEnvVariablesToFile(envPath, [{
5245
5451
  key: "DATABASE_URL",
@@ -5316,23 +5522,23 @@ function displayManualSupabaseInstructions(output) {
5316
5522
  log.info(`"Manual Supabase Setup Instructions:"
5317
5523
  1. Ensure Docker is installed and running.
5318
5524
  2. Install the Supabase CLI (e.g., \`npm install -g supabase\`).
5319
- 3. Run \`supabase init\` in your project's \`apps/server\` directory.
5320
- 4. Run \`supabase start\` in your project's \`apps/server\` directory.
5525
+ 3. Run \`supabase init\` in your project's \`packages/db\` directory.
5526
+ 4. Run \`supabase start\` in your project's \`packages/db\` directory.
5321
5527
  5. Copy the 'DB URL' from the output.${output ? `
5322
5528
  ${pc.bold("Relevant output from `supabase start`:")}
5323
5529
  ${pc.dim(output)}` : ""}
5324
- 6. Add the DB URL to the .env file in \`apps/server/.env\` as \`DATABASE_URL\`:
5530
+ 6. Add the DB URL to the .env file in \`packages/db/.env\` as \`DATABASE_URL\`:
5325
5531
  ${pc.gray("DATABASE_URL=\"your_supabase_db_url\"")}`);
5326
5532
  }
5327
5533
  async function setupSupabase(config, cliInput) {
5328
- const { projectDir, packageManager } = config;
5534
+ const { projectDir, packageManager, backend } = config;
5329
5535
  const manualDb = cliInput?.manualDb ?? false;
5330
- const serverDir = path.join(projectDir, "apps", "server");
5536
+ const serverDir = path.join(projectDir, "packages", "db");
5331
5537
  try {
5332
5538
  await fs.ensureDir(serverDir);
5333
5539
  if (manualDb) {
5334
5540
  displayManualSupabaseInstructions();
5335
- await writeSupabaseEnvFile(projectDir, "");
5541
+ await writeSupabaseEnvFile(projectDir, backend, "");
5336
5542
  return;
5337
5543
  }
5338
5544
  const mode = await select({
@@ -5351,7 +5557,7 @@ async function setupSupabase(config, cliInput) {
5351
5557
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5352
5558
  if (mode === "manual") {
5353
5559
  displayManualSupabaseInstructions();
5354
- await writeSupabaseEnvFile(projectDir, "");
5560
+ await writeSupabaseEnvFile(projectDir, backend, "");
5355
5561
  return;
5356
5562
  }
5357
5563
  if (!await initializeSupabase(serverDir, packageManager)) {
@@ -5364,7 +5570,7 @@ async function setupSupabase(config, cliInput) {
5364
5570
  return;
5365
5571
  }
5366
5572
  const dbUrl = extractDbUrl(supabaseOutput);
5367
- if (dbUrl) if (await writeSupabaseEnvFile(projectDir, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
5573
+ if (dbUrl) if (await writeSupabaseEnvFile(projectDir, backend, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
5368
5574
  else {
5369
5575
  log.error(pc.red("Supabase setup completed, but failed to update .env automatically."));
5370
5576
  displayManualSupabaseInstructions(supabaseOutput);
@@ -5492,8 +5698,9 @@ async function createTursoDatabase(dbName, groupName) {
5492
5698
  s.stop(pc.red("Failed to retrieve database connection details"));
5493
5699
  }
5494
5700
  }
5495
- async function writeEnvFile(projectDir, config) {
5496
- const envPath = path.join(projectDir, "apps/server", ".env");
5701
+ async function writeEnvFile(projectDir, backend, config) {
5702
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5703
+ const envPath = path.join(projectDir, targetApp, ".env");
5497
5704
  const variables = [{
5498
5705
  key: "DATABASE_URL",
5499
5706
  value: config?.dbUrl ?? "",
@@ -5517,12 +5724,12 @@ DATABASE_URL=your_database_url
5517
5724
  DATABASE_AUTH_TOKEN=your_auth_token`);
5518
5725
  }
5519
5726
  async function setupTurso(config, cliInput) {
5520
- const { orm, projectDir } = config;
5727
+ const { orm, projectDir, backend } = config;
5521
5728
  const manualDb = cliInput?.manualDb ?? false;
5522
5729
  const setupSpinner = spinner();
5523
5730
  try {
5524
5731
  if (manualDb) {
5525
- await writeEnvFile(projectDir);
5732
+ await writeEnvFile(projectDir, backend);
5526
5733
  displayManualSetupInstructions();
5527
5734
  return;
5528
5735
  }
@@ -5541,7 +5748,7 @@ async function setupTurso(config, cliInput) {
5541
5748
  });
5542
5749
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5543
5750
  if (mode === "manual") {
5544
- await writeEnvFile(projectDir);
5751
+ await writeEnvFile(projectDir, backend);
5545
5752
  displayManualSetupInstructions();
5546
5753
  return;
5547
5754
  }
@@ -5551,7 +5758,7 @@ async function setupTurso(config, cliInput) {
5551
5758
  if (platform === "win32") {
5552
5759
  if (setupSpinner) setupSpinner.stop(pc.yellow("Turso setup not supported on Windows"));
5553
5760
  log.warn(pc.yellow("Automatic Turso setup is not supported on Windows."));
5554
- await writeEnvFile(projectDir);
5761
+ await writeEnvFile(projectDir, backend);
5555
5762
  displayManualSetupInstructions();
5556
5763
  return;
5557
5764
  }
@@ -5563,7 +5770,7 @@ async function setupTurso(config, cliInput) {
5563
5770
  });
5564
5771
  if (isCancel(shouldInstall)) return exitCancelled("Operation cancelled");
5565
5772
  if (!shouldInstall) {
5566
- await writeEnvFile(projectDir);
5773
+ await writeEnvFile(projectDir, backend);
5567
5774
  displayManualSetupInstructions();
5568
5775
  return;
5569
5776
  }
@@ -5585,7 +5792,7 @@ async function setupTurso(config, cliInput) {
5585
5792
  dbName = dbNameResponse;
5586
5793
  try {
5587
5794
  const config$1 = await createTursoDatabase(dbName, selectedGroup);
5588
- await writeEnvFile(projectDir, config$1);
5795
+ await writeEnvFile(projectDir, backend, config$1);
5589
5796
  success = true;
5590
5797
  } catch (error) {
5591
5798
  if (error instanceof Error && error.message === "DATABASE_EXISTS") {
@@ -5598,7 +5805,7 @@ async function setupTurso(config, cliInput) {
5598
5805
  } catch (error) {
5599
5806
  if (setupSpinner) setupSpinner.stop(pc.red("Turso CLI availability check failed"));
5600
5807
  consola.error(pc.red(`Error during Turso setup: ${error instanceof Error ? error.message : String(error)}`));
5601
- await writeEnvFile(projectDir);
5808
+ await writeEnvFile(projectDir, backend);
5602
5809
  displayManualSetupInstructions();
5603
5810
  log.success("Setup completed with manual configuration required.");
5604
5811
  }
@@ -5610,40 +5817,52 @@ async function setupDatabase(config, cliInput) {
5610
5817
  const { database, orm, dbSetup, backend, projectDir } = config;
5611
5818
  if (backend === "convex" || database === "none") {
5612
5819
  if (backend !== "convex") {
5613
- const serverDir$1 = path.join(projectDir, "apps/server");
5614
- const serverDbDir = path.join(serverDir$1, "src/db");
5820
+ const serverDir = path.join(projectDir, "apps/server");
5821
+ const serverDbDir = path.join(serverDir, "src/db");
5615
5822
  if (await fs.pathExists(serverDbDir)) await fs.remove(serverDbDir);
5616
5823
  }
5617
5824
  return;
5618
5825
  }
5619
5826
  const s = spinner();
5620
- const serverDir = path.join(projectDir, "apps/server");
5621
- if (!await fs.pathExists(serverDir)) return;
5827
+ const dbPackageDir = path.join(projectDir, "packages/db");
5828
+ if (!await fs.pathExists(dbPackageDir)) return;
5622
5829
  try {
5623
- if (orm === "prisma") if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
5624
- dependencies: [
5625
- "@prisma/client",
5626
- "@prisma/adapter-planetscale",
5627
- "@planetscale/database"
5628
- ],
5629
- devDependencies: ["prisma"],
5630
- projectDir: serverDir
5631
- });
5632
- else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
5633
- dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
5634
- devDependencies: ["prisma"],
5635
- projectDir: serverDir
5636
- });
5637
- else await addPackageDependency({
5638
- dependencies: ["@prisma/client"],
5639
- devDependencies: ["prisma"],
5640
- projectDir: serverDir
5641
- });
5642
- else if (orm === "drizzle") {
5830
+ if (orm === "prisma") {
5831
+ if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
5832
+ dependencies: [
5833
+ "@prisma/client",
5834
+ "@prisma/adapter-planetscale",
5835
+ "@planetscale/database"
5836
+ ],
5837
+ devDependencies: ["prisma"],
5838
+ projectDir: dbPackageDir
5839
+ });
5840
+ else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
5841
+ dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
5842
+ devDependencies: ["prisma"],
5843
+ projectDir: dbPackageDir
5844
+ });
5845
+ else await addPackageDependency({
5846
+ dependencies: ["@prisma/client"],
5847
+ devDependencies: ["prisma"],
5848
+ projectDir: dbPackageDir
5849
+ });
5850
+ if (backend === "self") {
5851
+ const webDir = path.join(projectDir, "apps/web");
5852
+ if (await fs.pathExists(webDir)) await addPackageDependency({
5853
+ dependencies: ["@prisma/client"],
5854
+ projectDir: webDir
5855
+ });
5856
+ }
5857
+ } else if (orm === "drizzle") {
5643
5858
  if (database === "sqlite") await addPackageDependency({
5644
- dependencies: ["drizzle-orm", "@libsql/client"],
5859
+ dependencies: [
5860
+ "drizzle-orm",
5861
+ "@libsql/client",
5862
+ "libsql"
5863
+ ],
5645
5864
  devDependencies: ["drizzle-kit"],
5646
- projectDir: serverDir
5865
+ projectDir: dbPackageDir
5647
5866
  });
5648
5867
  else if (database === "postgres") if (dbSetup === "neon") await addPackageDependency({
5649
5868
  dependencies: [
@@ -5652,32 +5871,32 @@ async function setupDatabase(config, cliInput) {
5652
5871
  "ws"
5653
5872
  ],
5654
5873
  devDependencies: ["drizzle-kit", "@types/ws"],
5655
- projectDir: serverDir
5874
+ projectDir: dbPackageDir
5656
5875
  });
5657
5876
  else if (dbSetup === "planetscale") await addPackageDependency({
5658
5877
  dependencies: ["drizzle-orm", "pg"],
5659
5878
  devDependencies: ["drizzle-kit", "@types/pg"],
5660
- projectDir: serverDir
5879
+ projectDir: dbPackageDir
5661
5880
  });
5662
5881
  else await addPackageDependency({
5663
5882
  dependencies: ["drizzle-orm", "pg"],
5664
5883
  devDependencies: ["drizzle-kit", "@types/pg"],
5665
- projectDir: serverDir
5884
+ projectDir: dbPackageDir
5666
5885
  });
5667
5886
  else if (database === "mysql") if (dbSetup === "planetscale") await addPackageDependency({
5668
5887
  dependencies: ["drizzle-orm", "@planetscale/database"],
5669
5888
  devDependencies: ["drizzle-kit"],
5670
- projectDir: serverDir
5889
+ projectDir: dbPackageDir
5671
5890
  });
5672
5891
  else await addPackageDependency({
5673
5892
  dependencies: ["drizzle-orm", "mysql2"],
5674
5893
  devDependencies: ["drizzle-kit"],
5675
- projectDir: serverDir
5894
+ projectDir: dbPackageDir
5676
5895
  });
5677
5896
  } else if (orm === "mongoose") await addPackageDependency({
5678
5897
  dependencies: ["mongoose"],
5679
5898
  devDependencies: [],
5680
- projectDir: serverDir
5899
+ projectDir: dbPackageDir
5681
5900
  });
5682
5901
  if (dbSetup === "docker") await setupDockerCompose(config);
5683
5902
  else if (database === "sqlite" && dbSetup === "turso") await setupTurso(config, cliInput);
@@ -5700,7 +5919,7 @@ async function setupDatabase(config, cliInput) {
5700
5919
  //#region src/helpers/core/runtime-setup.ts
5701
5920
  async function setupRuntime(config) {
5702
5921
  const { runtime, backend, projectDir } = config;
5703
- if (backend === "convex" || backend === "next" || runtime === "none") return;
5922
+ if (backend === "convex" || backend === "self" || runtime === "none") return;
5704
5923
  const serverDir = path.join(projectDir, "apps/server");
5705
5924
  if (!await fs.pathExists(serverDir)) return;
5706
5925
  if (runtime === "bun") await setupBunRuntime(serverDir, backend);
@@ -6089,15 +6308,14 @@ async function initializeGit(projectDir, useGit) {
6089
6308
  async function setupPayments(config) {
6090
6309
  const { payments, projectDir, frontend } = config;
6091
6310
  if (!payments || payments === "none") return;
6092
- const serverDir = path.join(projectDir, "apps/server");
6093
6311
  const clientDir = path.join(projectDir, "apps/web");
6094
- const serverDirExists = await fs.pathExists(serverDir);
6312
+ const authDir = path.join(projectDir, "packages/auth");
6095
6313
  const clientDirExists = await fs.pathExists(clientDir);
6096
- if (!serverDirExists) return;
6314
+ const authDirExists = await fs.pathExists(authDir);
6097
6315
  if (payments === "polar") {
6098
- await addPackageDependency({
6316
+ if (authDirExists) await addPackageDependency({
6099
6317
  dependencies: ["@polar-sh/better-auth", "@polar-sh/sdk"],
6100
- projectDir: serverDir
6318
+ projectDir: authDir
6101
6319
  });
6102
6320
  if (clientDirExists) {
6103
6321
  if (frontend.some((f) => [
@@ -6220,7 +6438,7 @@ async function displayPostInstallInstructions(config) {
6220
6438
  else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
6221
6439
  if (!isConvex) {
6222
6440
  output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
6223
- if (api === "orpc") if (backend === "next") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/rpc/api\n`;
6441
+ if (api === "orpc") if (backend === "self") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/rpc/api\n`;
6224
6442
  else output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
6225
6443
  }
6226
6444
  if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
@@ -6334,12 +6552,79 @@ function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
6334
6552
  return instructions.length ? `\n${instructions.join("\n")}` : "";
6335
6553
  }
6336
6554
 
6555
+ //#endregion
6556
+ //#region src/helpers/core/workspace-setup.ts
6557
+ async function setupWorkspaceDependencies(projectDir, options) {
6558
+ const projectName = options.projectName;
6559
+ const workspaceVersion = options.packageManager === "npm" ? "*" : "workspace:*";
6560
+ const commonDeps = ["dotenv", "zod"];
6561
+ const commonDevDeps = ["tsdown"];
6562
+ const dbPackageDir = path.join(projectDir, "packages/db");
6563
+ if (await fs.pathExists(dbPackageDir)) await addPackageDependency({
6564
+ dependencies: commonDeps,
6565
+ devDependencies: commonDevDeps,
6566
+ projectDir: dbPackageDir
6567
+ });
6568
+ const authPackageDir = path.join(projectDir, "packages/auth");
6569
+ if (await fs.pathExists(authPackageDir)) await addPackageDependency({
6570
+ dependencies: commonDeps,
6571
+ devDependencies: commonDevDeps,
6572
+ customDependencies: { [`@${projectName}/db`]: workspaceVersion },
6573
+ projectDir: authPackageDir
6574
+ });
6575
+ const apiPackageDir = path.join(projectDir, "packages/api");
6576
+ if (await fs.pathExists(apiPackageDir)) await addPackageDependency({
6577
+ dependencies: commonDeps,
6578
+ devDependencies: commonDevDeps,
6579
+ customDependencies: {
6580
+ [`@${projectName}/auth`]: workspaceVersion,
6581
+ [`@${projectName}/db`]: workspaceVersion
6582
+ },
6583
+ projectDir: apiPackageDir
6584
+ });
6585
+ const serverPackageDir = path.join(projectDir, "apps/server");
6586
+ if (await fs.pathExists(serverPackageDir)) await addPackageDependency({
6587
+ dependencies: commonDeps,
6588
+ devDependencies: commonDevDeps,
6589
+ customDependencies: {
6590
+ [`@${projectName}/api`]: workspaceVersion,
6591
+ [`@${projectName}/auth`]: workspaceVersion,
6592
+ [`@${projectName}/db`]: workspaceVersion
6593
+ },
6594
+ projectDir: serverPackageDir
6595
+ });
6596
+ const needsApiDependency = options.api && options.api !== "none";
6597
+ const webPackageDir = path.join(projectDir, "apps/web");
6598
+ if (await fs.pathExists(webPackageDir)) {
6599
+ const webDeps = {};
6600
+ if (options.backend === "self") {
6601
+ webDeps[`@${projectName}/api`] = workspaceVersion;
6602
+ webDeps[`@${projectName}/auth`] = workspaceVersion;
6603
+ webDeps[`@${projectName}/db`] = workspaceVersion;
6604
+ } else if (needsApiDependency) webDeps[`@${projectName}/api`] = workspaceVersion;
6605
+ if (Object.keys(webDeps).length > 0) await addPackageDependency({
6606
+ customDependencies: webDeps,
6607
+ projectDir: webPackageDir
6608
+ });
6609
+ }
6610
+ }
6611
+
6337
6612
  //#endregion
6338
6613
  //#region src/helpers/core/project-config.ts
6339
6614
  async function updatePackageConfigurations(projectDir, options) {
6340
6615
  await updateRootPackageJson(projectDir, options);
6341
- if (options.backend !== "convex") await updateServerPackageJson(projectDir, options);
6342
- else await updateConvexPackageJson(projectDir, options);
6616
+ if (options.backend === "convex") await updateConvexPackageJson(projectDir, options);
6617
+ else if (options.backend === "self") {
6618
+ await updateDbPackageJson(projectDir, options);
6619
+ await updateAuthPackageJson(projectDir, options);
6620
+ await updateApiPackageJson(projectDir, options);
6621
+ await setupWorkspaceDependencies(projectDir, options);
6622
+ } else if (options.backend !== "none") {
6623
+ await updateServerPackageJson(projectDir, options);
6624
+ await updateAuthPackageJson(projectDir, options);
6625
+ await updateApiPackageJson(projectDir, options);
6626
+ await setupWorkspaceDependencies(projectDir, options);
6627
+ }
6343
6628
  }
6344
6629
  async function updateRootPackageJson(projectDir, options) {
6345
6630
  const rootPackageJsonPath = path.join(projectDir, "package.json");
@@ -6349,6 +6634,7 @@ async function updateRootPackageJson(projectDir, options) {
6349
6634
  if (!packageJson.scripts) packageJson.scripts = {};
6350
6635
  const scripts = packageJson.scripts;
6351
6636
  const backendPackageName = options.backend === "convex" ? `@${options.projectName}/backend` : "server";
6637
+ const dbPackageName = `@${options.projectName}/db`;
6352
6638
  let serverDevScript = "";
6353
6639
  if (options.addons.includes("turborepo")) serverDevScript = `turbo -F ${backendPackageName} dev`;
6354
6640
  else if (options.packageManager === "bun") serverDevScript = `bun run --filter ${backendPackageName} dev`;
@@ -6365,17 +6651,17 @@ async function updateRootPackageJson(projectDir, options) {
6365
6651
  scripts["check-types"] = "turbo check-types";
6366
6652
  scripts["dev:native"] = "turbo -F native dev";
6367
6653
  scripts["dev:web"] = "turbo -F web dev";
6368
- scripts["dev:server"] = serverDevScript;
6654
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6369
6655
  if (options.backend === "convex") scripts["dev:setup"] = `turbo -F ${backendPackageName} dev:setup`;
6370
6656
  if (needsDbScripts) {
6371
- scripts["db:push"] = `turbo -F ${backendPackageName} db:push`;
6372
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${backendPackageName} db:studio`;
6657
+ scripts["db:push"] = `turbo -F ${dbPackageName} db:push`;
6658
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${dbPackageName} db:studio`;
6373
6659
  if (options.orm === "prisma") {
6374
- scripts["db:generate"] = `turbo -F ${backendPackageName} db:generate`;
6375
- scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
6660
+ scripts["db:generate"] = `turbo -F ${dbPackageName} db:generate`;
6661
+ scripts["db:migrate"] = `turbo -F ${dbPackageName} db:migrate`;
6376
6662
  } else if (options.orm === "drizzle") {
6377
- scripts["db:generate"] = `turbo -F ${backendPackageName} db:generate`;
6378
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
6663
+ scripts["db:generate"] = `turbo -F ${dbPackageName} db:generate`;
6664
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${dbPackageName} db:migrate`;
6379
6665
  }
6380
6666
  }
6381
6667
  if (options.dbSetup === "docker") {
@@ -6390,17 +6676,17 @@ async function updateRootPackageJson(projectDir, options) {
6390
6676
  scripts["check-types"] = "pnpm -r check-types";
6391
6677
  scripts["dev:native"] = "pnpm --filter native dev";
6392
6678
  scripts["dev:web"] = "pnpm --filter web dev";
6393
- scripts["dev:server"] = serverDevScript;
6679
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6394
6680
  if (options.backend === "convex") scripts["dev:setup"] = `pnpm --filter ${backendPackageName} dev:setup`;
6395
6681
  if (needsDbScripts) {
6396
- scripts["db:push"] = `pnpm --filter ${backendPackageName} db:push`;
6397
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${backendPackageName} db:studio`;
6682
+ scripts["db:push"] = `pnpm --filter ${dbPackageName} db:push`;
6683
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${dbPackageName} db:studio`;
6398
6684
  if (options.orm === "prisma") {
6399
- scripts["db:generate"] = `pnpm --filter ${backendPackageName} db:generate`;
6400
- scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
6685
+ scripts["db:generate"] = `pnpm --filter ${dbPackageName} db:generate`;
6686
+ scripts["db:migrate"] = `pnpm --filter ${dbPackageName} db:migrate`;
6401
6687
  } else if (options.orm === "drizzle") {
6402
- scripts["db:generate"] = `pnpm --filter ${backendPackageName} db:generate`;
6403
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
6688
+ scripts["db:generate"] = `pnpm --filter ${dbPackageName} db:generate`;
6689
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${dbPackageName} db:migrate`;
6404
6690
  }
6405
6691
  }
6406
6692
  if (options.dbSetup === "docker") {
@@ -6415,17 +6701,17 @@ async function updateRootPackageJson(projectDir, options) {
6415
6701
  scripts["check-types"] = "npm run check-types --workspaces";
6416
6702
  scripts["dev:native"] = "npm run dev --workspace native";
6417
6703
  scripts["dev:web"] = "npm run dev --workspace web";
6418
- scripts["dev:server"] = serverDevScript;
6704
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6419
6705
  if (options.backend === "convex") scripts["dev:setup"] = `npm run dev:setup --workspace ${backendPackageName}`;
6420
6706
  if (needsDbScripts) {
6421
- scripts["db:push"] = `npm run db:push --workspace ${backendPackageName}`;
6422
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${backendPackageName}`;
6707
+ scripts["db:push"] = `npm run db:push --workspace ${dbPackageName}`;
6708
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${dbPackageName}`;
6423
6709
  if (options.orm === "prisma") {
6424
- scripts["db:generate"] = `npm run db:generate --workspace ${backendPackageName}`;
6425
- scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
6710
+ scripts["db:generate"] = `npm run db:generate --workspace ${dbPackageName}`;
6711
+ scripts["db:migrate"] = `npm run db:migrate --workspace ${dbPackageName}`;
6426
6712
  } else if (options.orm === "drizzle") {
6427
- scripts["db:generate"] = `npm run db:generate --workspace ${backendPackageName}`;
6428
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
6713
+ scripts["db:generate"] = `npm run db:generate --workspace ${dbPackageName}`;
6714
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${dbPackageName}`;
6429
6715
  }
6430
6716
  }
6431
6717
  if (options.dbSetup === "docker") {
@@ -6440,17 +6726,17 @@ async function updateRootPackageJson(projectDir, options) {
6440
6726
  scripts["check-types"] = "bun run --filter '*' check-types";
6441
6727
  scripts["dev:native"] = "bun run --filter native dev";
6442
6728
  scripts["dev:web"] = "bun run --filter web dev";
6443
- scripts["dev:server"] = serverDevScript;
6729
+ if (options.backend !== "self" && options.backend !== "none") scripts["dev:server"] = serverDevScript;
6444
6730
  if (options.backend === "convex") scripts["dev:setup"] = `bun run --filter ${backendPackageName} dev:setup`;
6445
6731
  if (needsDbScripts) {
6446
- scripts["db:push"] = `bun run --filter ${backendPackageName} db:push`;
6447
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${backendPackageName} db:studio`;
6732
+ scripts["db:push"] = `bun run --filter ${dbPackageName} db:push`;
6733
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${dbPackageName} db:studio`;
6448
6734
  if (options.orm === "prisma") {
6449
- scripts["db:generate"] = `bun run --filter ${backendPackageName} db:generate`;
6450
- scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
6735
+ scripts["db:generate"] = `bun run --filter ${dbPackageName} db:generate`;
6736
+ scripts["db:migrate"] = `bun run --filter ${dbPackageName} db:migrate`;
6451
6737
  } else if (options.orm === "drizzle") {
6452
- scripts["db:generate"] = `bun run --filter ${backendPackageName} db:generate`;
6453
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
6738
+ scripts["db:generate"] = `bun run --filter ${dbPackageName} db:generate`;
6739
+ if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${dbPackageName} db:migrate`;
6454
6740
  }
6455
6741
  }
6456
6742
  if (options.dbSetup === "docker") {
@@ -6483,6 +6769,22 @@ async function updateServerPackageJson(projectDir, options) {
6483
6769
  const serverPackageJson = await fs.readJson(serverPackageJsonPath);
6484
6770
  if (!serverPackageJson.scripts) serverPackageJson.scripts = {};
6485
6771
  const scripts = serverPackageJson.scripts;
6772
+ if (options.dbSetup === "docker") {
6773
+ scripts["db:start"] = "docker compose up -d";
6774
+ scripts["db:watch"] = "docker compose up";
6775
+ scripts["db:stop"] = "docker compose stop";
6776
+ scripts["db:down"] = "docker compose down";
6777
+ }
6778
+ await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
6779
+ await updateDbPackageJson(projectDir, options);
6780
+ }
6781
+ async function updateDbPackageJson(projectDir, options) {
6782
+ const dbPackageJsonPath = path.join(projectDir, "packages/db/package.json");
6783
+ if (!await fs.pathExists(dbPackageJsonPath)) return;
6784
+ const dbPackageJson = await fs.readJson(dbPackageJsonPath);
6785
+ dbPackageJson.name = `@${options.projectName}/db`;
6786
+ if (!dbPackageJson.scripts) dbPackageJson.scripts = {};
6787
+ const scripts = dbPackageJson.scripts;
6486
6788
  if (options.database !== "none") {
6487
6789
  if (options.database === "sqlite" && options.orm === "drizzle" && options.dbSetup !== "d1") scripts["db:local"] = "turso dev --db-file local.db";
6488
6790
  if (options.orm === "prisma") {
@@ -6497,13 +6799,21 @@ async function updateServerPackageJson(projectDir, options) {
6497
6799
  if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = "drizzle-kit migrate";
6498
6800
  }
6499
6801
  }
6500
- if (options.dbSetup === "docker") {
6501
- scripts["db:start"] = "docker compose up -d";
6502
- scripts["db:watch"] = "docker compose up";
6503
- scripts["db:stop"] = "docker compose stop";
6504
- scripts["db:down"] = "docker compose down";
6505
- }
6506
- await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
6802
+ await fs.writeJson(dbPackageJsonPath, dbPackageJson, { spaces: 2 });
6803
+ }
6804
+ async function updateAuthPackageJson(projectDir, options) {
6805
+ const authPackageJsonPath = path.join(projectDir, "packages/auth/package.json");
6806
+ if (!await fs.pathExists(authPackageJsonPath)) return;
6807
+ const authPackageJson = await fs.readJson(authPackageJsonPath);
6808
+ authPackageJson.name = `@${options.projectName}/auth`;
6809
+ await fs.writeJson(authPackageJsonPath, authPackageJson, { spaces: 2 });
6810
+ }
6811
+ async function updateApiPackageJson(projectDir, options) {
6812
+ const apiPackageJsonPath = path.join(projectDir, "packages/api/package.json");
6813
+ if (!await fs.pathExists(apiPackageJsonPath)) return;
6814
+ const apiPackageJson = await fs.readJson(apiPackageJsonPath);
6815
+ apiPackageJson.name = `@${options.projectName}/api`;
6816
+ await fs.writeJson(apiPackageJsonPath, apiPackageJson, { spaces: 2 });
6507
6817
  }
6508
6818
  async function updateConvexPackageJson(projectDir, options) {
6509
6819
  const convexPackageJsonPath = path.join(projectDir, "packages/backend/package.json");
@@ -6519,15 +6829,14 @@ async function updateConvexPackageJson(projectDir, options) {
6519
6829
  async function createProject(options, cliInput) {
6520
6830
  const projectDir = options.projectDir;
6521
6831
  const isConvex = options.backend === "convex";
6832
+ const isSelfBackend = options.backend === "self";
6833
+ const needsServerSetup = !isConvex && !isSelfBackend;
6522
6834
  try {
6523
6835
  await fs.ensureDir(projectDir);
6524
6836
  await copyBaseTemplate(projectDir, options);
6525
6837
  await setupFrontendTemplates(projectDir, options);
6526
6838
  await setupBackendFramework(projectDir, options);
6527
- if (!isConvex) {
6528
- await setupDbOrmTemplates(projectDir, options);
6529
- await setupDockerComposeTemplates(projectDir, options);
6530
- }
6839
+ if (needsServerSetup) await setupDockerComposeTemplates(projectDir, options);
6531
6840
  await setupAuthTemplate(projectDir, options);
6532
6841
  if (options.payments && options.payments !== "none") await setupPaymentsTemplate(projectDir, options);
6533
6842
  if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamplesTemplate(projectDir, options);
@@ -6535,9 +6844,11 @@ async function createProject(options, cliInput) {
6535
6844
  await setupDeploymentTemplates(projectDir, options);
6536
6845
  await setupApi(options);
6537
6846
  if (!isConvex) {
6538
- await setupBackendDependencies(options);
6847
+ if (needsServerSetup) {
6848
+ await setupBackendDependencies(options);
6849
+ await setupRuntime(options);
6850
+ }
6539
6851
  await setupDatabase(options, cliInput);
6540
- await setupRuntime(options);
6541
6852
  if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamples(options);
6542
6853
  }
6543
6854
  if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
@@ -6546,6 +6857,7 @@ async function createProject(options, cliInput) {
6546
6857
  await handleExtras(projectDir, options);
6547
6858
  await setupEnvironmentVariables(options);
6548
6859
  await updatePackageConfigurations(projectDir, options);
6860
+ await setupCatalogs(projectDir, options);
6549
6861
  await setupWebDeploy(options);
6550
6862
  await setupServerDeploy(options);
6551
6863
  await createReadme(projectDir, options);