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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.d.ts +6 -7
  3. package/dist/index.js +1 -1
  4. package/dist/{src-dv6H37db.js → src-CulfT5QB.js} +461 -358
  5. package/package.json +1 -1
  6. package/templates/addons/ruler/.ruler/bts.md.hbs +24 -14
  7. package/templates/api/orpc/fullstack/next/src/app/api/rpc/[[...rest]]/route.ts.hbs +50 -0
  8. package/templates/api/orpc/server/package.json.hbs +1 -3
  9. package/templates/api/orpc/server/tsconfig.json.hbs +8 -8
  10. package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +1 -1
  11. package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +4 -2
  12. package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +1 -1
  13. package/templates/api/orpc/web/svelte/src/lib/orpc.ts.hbs +1 -1
  14. package/templates/api/trpc/fullstack/next/src/app/api/trpc/[trpc]/route.ts.hbs +14 -0
  15. package/templates/api/trpc/server/package.json.hbs +1 -3
  16. package/templates/api/trpc/server/tsconfig.json.hbs +8 -11
  17. package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +6 -4
  18. package/templates/auth/better-auth/fullstack/next/src/app/api/auth/[...all]/route.ts.hbs +4 -0
  19. package/templates/auth/better-auth/server/base/package.json.hbs +1 -4
  20. package/templates/auth/better-auth/server/base/src/index.ts.hbs +5 -5
  21. package/templates/auth/better-auth/server/base/tsconfig.json.hbs +8 -11
  22. package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +2 -0
  23. package/templates/auth/better-auth/web/react/next/src/app/dashboard/page.tsx.hbs +31 -0
  24. package/templates/backend/server/base/package.json.hbs +1 -4
  25. package/templates/backend/server/base/tsconfig.json.hbs +1 -15
  26. package/templates/backend/server/base/tsdown.config.ts.hbs +1 -6
  27. package/templates/base/tsconfig.base.json.hbs +34 -0
  28. package/templates/base/tsconfig.json.hbs +3 -0
  29. package/templates/db/base/package.json.hbs +1 -3
  30. package/templates/db/base/tsconfig.json.hbs +8 -11
  31. package/templates/db/drizzle/mysql/drizzle.config.ts.hbs +5 -1
  32. package/templates/db/drizzle/postgres/drizzle.config.ts.hbs +5 -1
  33. package/templates/db/drizzle/sqlite/drizzle.config.ts.hbs +5 -1
  34. package/templates/db/prisma/mongodb/prisma.config.ts.hbs +5 -1
  35. package/templates/db/prisma/mysql/prisma.config.ts.hbs +5 -1
  36. package/templates/db/prisma/postgres/prisma.config.ts.hbs +5 -1
  37. package/templates/db/prisma/sqlite/prisma.config.ts.hbs +5 -1
  38. package/templates/deploy/alchemy/alchemy.run.ts.hbs +3 -3
  39. package/templates/examples/ai/fullstack/next/src/app/api/ai/route.ts.hbs +15 -0
  40. package/templates/examples/todo/server/drizzle/base/src/routers/todo.ts.hbs +2 -2
  41. package/templates/frontend/native/nativewind/tsconfig.json.hbs +1 -6
  42. package/templates/frontend/native/unistyles/tsconfig.json.hbs +1 -6
  43. package/templates/frontend/nuxt/tsconfig.json.hbs +0 -4
  44. package/templates/frontend/react/next/package.json.hbs +1 -1
  45. package/templates/frontend/react/next/tsconfig.json.hbs +0 -7
  46. package/templates/frontend/react/react-router/tsconfig.json.hbs +1 -6
  47. package/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs +1 -1
  48. package/templates/frontend/react/tanstack-router/tsconfig.json.hbs +1 -6
  49. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +1 -1
  50. package/templates/frontend/react/tanstack-start/tsconfig.json.hbs +1 -6
  51. package/templates/frontend/solid/tsconfig.json.hbs +1 -6
  52. package/templates/frontend/svelte/tsconfig.json.hbs +1 -6
  53. package/templates/base/tsconfig.base.json +0 -23
@@ -71,8 +71,7 @@ const dependencyVersionMap = {
71
71
  "drizzle-orm": "^0.44.2",
72
72
  "drizzle-kit": "^0.31.2",
73
73
  "@planetscale/database": "^1.19.0",
74
- "@libsql/client": "^0.15.9",
75
- libsql: "^0.5.22",
74
+ "@libsql/client": "^0.14.0",
76
75
  "@neondatabase/serverless": "^1.0.1",
77
76
  pg: "^8.14.1",
78
77
  "@types/pg": "^8.11.11",
@@ -125,7 +124,7 @@ const dependencyVersionMap = {
125
124
  "@trpc/tanstack-react-query": "^11.5.0",
126
125
  "@trpc/server": "^11.5.0",
127
126
  "@trpc/client": "^11.5.0",
128
- next: "^15.1.0",
127
+ next: "15.5.4",
129
128
  convex: "^1.27.0",
130
129
  "@convex-dev/react-query": "^0.0.0-alpha.8",
131
130
  "convex-svelte": "^0.0.11",
@@ -348,21 +347,16 @@ function ensureSingleWebAndNative(frontends) {
348
347
  if (web.length > 1) exitWithError("Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next, nuxt, svelte, solid");
349
348
  if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles");
350
349
  }
351
- const FULLSTACK_FRONTENDS$1 = [
352
- "next",
353
- "nuxt",
354
- "svelte",
355
- "tanstack-start"
356
- ];
350
+ const FULLSTACK_FRONTENDS$1 = ["next"];
357
351
  function validateSelfBackendCompatibility(providedFlags, options, config) {
358
352
  const backend = config.backend || options.backend;
359
353
  const frontends = config.frontend || options.frontend || [];
360
354
  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");
355
+ if (!frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f))) exitWithError("Backend 'self' (fullstack) currently only supports Next.js frontend. Please use --frontend next. Support for Nuxt, SvelteKit, and TanStack Start will be added in a future update.");
362
356
  if (frontends.length > 1) exitWithError("Backend 'self' (fullstack) can only be used with a single frontend framework.");
363
357
  }
364
358
  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.");
359
+ if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) currently only supports Next.js frontend. Please use --frontend next or choose a different backend. Support for Nuxt, SvelteKit, and TanStack Start will be added in a future update.");
366
360
  }
367
361
  function validateWorkersCompatibility(providedFlags, options, config) {
368
362
  if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && config.backend !== "hono") exitWithError(`Cloudflare Workers runtime (--runtime workers) is only supported with Hono backend (--backend hono). Current backend: ${config.backend}. Please use '--backend hono' or choose a different runtime.`);
@@ -685,12 +679,7 @@ async function getAuthChoice(auth, hasDatabase, backend, frontend) {
685
679
 
686
680
  //#endregion
687
681
  //#region src/prompts/backend.ts
688
- const FULLSTACK_FRONTENDS = [
689
- "next",
690
- "nuxt",
691
- "svelte",
692
- "tanstack-start"
693
- ];
682
+ const FULLSTACK_FRONTENDS = ["next"];
694
683
  async function getBackendFrameworkChoice(backendFramework, frontends) {
695
684
  if (backendFramework !== void 0) return backendFramework;
696
685
  const hasIncompatibleFrontend = frontends?.some((f) => f === "solid");
@@ -699,7 +688,7 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
699
688
  if (hasFullstackFrontend) backendOptions.push({
700
689
  value: "self",
701
690
  label: "Self (Fullstack)",
702
- hint: "Use frontend's built-in backend capabilities"
691
+ hint: "Use frontend's built-in api routes"
703
692
  });
704
693
  backendOptions.push({
705
694
  value: "hono",
@@ -2089,11 +2078,14 @@ async function setupFumadocs(config) {
2089
2078
  if (isCancel(template)) return exitCancelled("Operation cancelled");
2090
2079
  const commandWithArgs = `create-fumadocs-app@latest fumadocs --template ${TEMPLATES[template].value} --src --no-install --pm ${packageManager} --no-eslint --no-git`;
2091
2080
  const fumadocsInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2081
+ const s = spinner();
2082
+ s.start("Setting up Fumadocs...");
2092
2083
  await execa(fumadocsInitCommand, {
2093
2084
  cwd: path.join(projectDir, "apps"),
2094
2085
  env: { CI: "true" },
2095
2086
  shell: true
2096
2087
  });
2088
+ s.stop("Fumadocs setup complete!");
2097
2089
  const fumadocsDir = path.join(projectDir, "apps", "fumadocs");
2098
2090
  const packageJsonPath = path.join(fumadocsDir, "package.json");
2099
2091
  if (await fs.pathExists(packageJsonPath)) {
@@ -2334,11 +2326,14 @@ async function setupUltracite(config, hasHusky) {
2334
2326
  if (hasHusky) ultraciteArgs.push("--integrations", "husky", "lint-staged");
2335
2327
  const commandWithArgs = `ultracite@latest ${ultraciteArgs.join(" ")} --skip-install`;
2336
2328
  const ultraciteInitCommand = getPackageExecutionCommand(packageManager, commandWithArgs);
2329
+ const s = spinner();
2330
+ s.start("Setting up Ultracite...");
2337
2331
  await execa(ultraciteInitCommand, {
2338
2332
  cwd: projectDir,
2339
2333
  env: { CI: "true" },
2340
2334
  shell: true
2341
2335
  });
2336
+ s.stop("Ultracite setup complete!");
2342
2337
  if (hasHusky) await addPackageDependency({
2343
2338
  devDependencies: ["husky", "lint-staged"],
2344
2339
  projectDir
@@ -2749,6 +2744,10 @@ async function setupFrontendTemplates(projectDir, context) {
2749
2744
  const apiWebBaseDir = path.join(PKG_ROOT, `templates/api/${context.api}/web/react/base`);
2750
2745
  if (await fs.pathExists(apiWebBaseDir)) await processAndCopyFiles("**/*", apiWebBaseDir, webAppDir, context);
2751
2746
  }
2747
+ if (context.backend === "self" && reactFramework === "next" && context.api !== "none") {
2748
+ const apiFullstackDir = path.join(PKG_ROOT, `templates/api/${context.api}/fullstack/next`);
2749
+ if (await fs.pathExists(apiFullstackDir)) await processAndCopyFiles("**/*", apiFullstackDir, webAppDir, context);
2750
+ }
2752
2751
  }
2753
2752
  } else if (hasNuxtWeb) {
2754
2753
  const nuxtBaseDir = path.join(PKG_ROOT, "templates/frontend/nuxt");
@@ -2944,6 +2943,10 @@ async function setupAuthTemplate(projectDir, context) {
2944
2943
  if (reactFramework) {
2945
2944
  const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/${reactFramework}`);
2946
2945
  if (await fs.pathExists(authWebFrameworkSrc)) await processAndCopyFiles("**/*", authWebFrameworkSrc, webAppDir, context);
2946
+ if (context.backend === "self" && reactFramework === "next") {
2947
+ const authFullstackSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/fullstack/next`);
2948
+ if (await fs.pathExists(authFullstackSrc)) await processAndCopyFiles("**/*", authFullstackSrc, webAppDir, context);
2949
+ }
2947
2950
  }
2948
2951
  } else if (hasNuxtWeb) {
2949
2952
  const authWebNuxtSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/nuxt`);
@@ -3086,6 +3089,10 @@ async function setupExamplesTemplate(projectDir, context) {
3086
3089
  if (reactFramework) {
3087
3090
  const exampleWebFrameworkSrc = path.join(exampleWebSrc, reactFramework);
3088
3091
  if (await fs.pathExists(exampleWebFrameworkSrc)) await processAndCopyFiles("**/*", exampleWebFrameworkSrc, webAppDir, context, false);
3092
+ if (context.backend === "self" && reactFramework === "next") {
3093
+ const exampleFullstackSrc = path.join(exampleBaseDir, "fullstack/next");
3094
+ if (await fs.pathExists(exampleFullstackSrc)) await processAndCopyFiles("**/*", exampleFullstackSrc, webAppDir, context, false);
3095
+ }
3089
3096
  }
3090
3097
  }
3091
3098
  } else if (hasNuxtWeb) {
@@ -3138,25 +3145,29 @@ async function setupDockerComposeTemplates(projectDir, context) {
3138
3145
  if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, dbPackageDir, context);
3139
3146
  }
3140
3147
  async function setupDeploymentTemplates(projectDir, context) {
3141
- if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") if (context.webDeploy === "alchemy" && context.serverDeploy === "alchemy") {
3148
+ const isBackendSelf = context.backend === "self";
3149
+ if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") {
3142
3150
  const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
3143
- if (await fs.pathExists(alchemyTemplateSrc)) {
3144
- await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, projectDir, context);
3145
- const serverAppDir = path.join(projectDir, "apps/server");
3146
- if (await fs.pathExists(serverAppDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3147
- }
3148
- } else {
3149
- if (context.webDeploy === "alchemy") {
3150
- const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
3151
- const webAppDir = path.join(projectDir, "apps/web");
3152
- if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(webAppDir)) await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, webAppDir, context);
3153
- }
3154
- if (context.serverDeploy === "alchemy") {
3155
- const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
3156
- const serverAppDir = path.join(projectDir, "apps/server");
3157
- if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(serverAppDir)) {
3158
- await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3159
- await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3151
+ if (context.webDeploy === "alchemy" && (context.serverDeploy === "alchemy" || isBackendSelf)) {
3152
+ if (await fs.pathExists(alchemyTemplateSrc)) {
3153
+ await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, projectDir, context);
3154
+ await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3155
+ }
3156
+ } else {
3157
+ if (context.webDeploy === "alchemy") {
3158
+ const webAppDir = path.join(projectDir, "apps/web");
3159
+ if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(webAppDir)) {
3160
+ await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, webAppDir, context);
3161
+ await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3162
+ }
3163
+ }
3164
+ if (context.serverDeploy === "alchemy" && !isBackendSelf) {
3165
+ const serverAppDir = path.join(projectDir, "apps/server");
3166
+ if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(serverAppDir)) {
3167
+ await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3168
+ await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3169
+ await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3170
+ }
3160
3171
  }
3161
3172
  }
3162
3173
  }
@@ -3179,7 +3190,7 @@ async function setupDeploymentTemplates(projectDir, context) {
3179
3190
  }
3180
3191
  }
3181
3192
  }
3182
- if (context.serverDeploy !== "none" && context.serverDeploy !== "alchemy") {
3193
+ if (context.serverDeploy !== "none" && context.serverDeploy !== "alchemy" && !isBackendSelf) {
3183
3194
  const serverAppDir = path.join(projectDir, "apps/server");
3184
3195
  if (await fs.pathExists(serverAppDir)) {
3185
3196
  const deployTemplateSrc = path.join(PKG_ROOT, `templates/deploy/${context.serverDeploy}/server`);
@@ -3187,6 +3198,18 @@ async function setupDeploymentTemplates(projectDir, context) {
3187
3198
  }
3188
3199
  }
3189
3200
  }
3201
+ async function addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc) {
3202
+ for (const packageName of [
3203
+ "packages/api",
3204
+ "packages/auth",
3205
+ "packages/db"
3206
+ ]) {
3207
+ const packageDir = path.join(projectDir, packageName);
3208
+ if (await fs.pathExists(packageDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, packageDir, context);
3209
+ }
3210
+ const serverAppDir = path.join(projectDir, "apps/server");
3211
+ if (await fs.pathExists(serverAppDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3212
+ }
3190
3213
 
3191
3214
  //#endregion
3192
3215
  //#region src/helpers/core/add-addons.ts
@@ -3252,7 +3275,7 @@ async function setupServerDeploy(config) {
3252
3275
  serverDir,
3253
3276
  packageManager
3254
3277
  });
3255
- } else if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, packageManager);
3278
+ } else if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, packageManager, projectDir);
3256
3279
  }
3257
3280
  async function setupWorkersServerDeploy(serverDir, _packageManager) {
3258
3281
  const packageJsonPath = path.join(serverDir, "package.json");
@@ -3289,18 +3312,18 @@ async function generateCloudflareWorkerTypes({ serverDir, packageManager }) {
3289
3312
  log.warn(`Note: You can manually run 'cd apps/server && ${managerCmd} cf-typegen' in the project directory later`);
3290
3313
  }
3291
3314
  }
3292
- async function setupAlchemyServerDeploy(serverDir, _packageManager) {
3315
+ async function setupAlchemyServerDeploy(serverDir, _packageManager, projectDir) {
3293
3316
  if (!await fs.pathExists(serverDir)) return;
3294
3317
  await addPackageDependency({
3295
3318
  devDependencies: [
3296
3319
  "alchemy",
3297
3320
  "wrangler",
3298
3321
  "@types/node",
3299
- "dotenv",
3300
3322
  "@cloudflare/workers-types"
3301
3323
  ],
3302
3324
  projectDir: serverDir
3303
3325
  });
3326
+ if (projectDir) await addAlchemyPackagesDependencies$1(projectDir);
3304
3327
  const packageJsonPath = path.join(serverDir, "package.json");
3305
3328
  if (await fs.pathExists(packageJsonPath)) {
3306
3329
  const packageJson = await fs.readJson(packageJsonPath);
@@ -3313,6 +3336,19 @@ async function setupAlchemyServerDeploy(serverDir, _packageManager) {
3313
3336
  await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
3314
3337
  }
3315
3338
  }
3339
+ async function addAlchemyPackagesDependencies$1(projectDir) {
3340
+ for (const packageName of [
3341
+ "packages/api",
3342
+ "packages/auth",
3343
+ "packages/db"
3344
+ ]) {
3345
+ const packageDir = path.join(projectDir, packageName);
3346
+ if (await fs.pathExists(packageDir)) await addPackageDependency({
3347
+ devDependencies: ["@cloudflare/workers-types"],
3348
+ projectDir: packageDir
3349
+ });
3350
+ }
3351
+ }
3316
3352
 
3317
3353
  //#endregion
3318
3354
  //#region src/helpers/deployment/alchemy/alchemy-next-setup.ts
@@ -3323,7 +3359,6 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
3323
3359
  dependencies: ["@opennextjs/cloudflare"],
3324
3360
  devDependencies: [
3325
3361
  "alchemy",
3326
- "dotenv",
3327
3362
  "wrangler",
3328
3363
  "@cloudflare/workers-types"
3329
3364
  ],
@@ -3359,7 +3394,6 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
3359
3394
  devDependencies: [
3360
3395
  "alchemy",
3361
3396
  "nitro-cloudflare-dev",
3362
- "dotenv",
3363
3397
  "wrangler"
3364
3398
  ],
3365
3399
  projectDir: webAppDir
@@ -3422,7 +3456,7 @@ async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, option
3422
3456
  const webAppDir = path.join(projectDir, "apps/web");
3423
3457
  if (!await fs.pathExists(webAppDir)) return;
3424
3458
  await addPackageDependency({
3425
- devDependencies: ["alchemy", "dotenv"],
3459
+ devDependencies: ["alchemy"],
3426
3460
  projectDir: webAppDir
3427
3461
  });
3428
3462
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3443,7 +3477,7 @@ async function setupSolidAlchemyDeploy(projectDir, _packageManager, options) {
3443
3477
  const webAppDir = path.join(projectDir, "apps/web");
3444
3478
  if (!await fs.pathExists(webAppDir)) return;
3445
3479
  await addPackageDependency({
3446
- devDependencies: ["alchemy", "dotenv"],
3480
+ devDependencies: ["alchemy"],
3447
3481
  projectDir: webAppDir
3448
3482
  });
3449
3483
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3464,11 +3498,7 @@ async function setupSvelteAlchemyDeploy(projectDir, _packageManager, options) {
3464
3498
  const webAppDir = path.join(projectDir, "apps/web");
3465
3499
  if (!await fs.pathExists(webAppDir)) return;
3466
3500
  await addPackageDependency({
3467
- devDependencies: [
3468
- "alchemy",
3469
- "@sveltejs/adapter-cloudflare",
3470
- "dotenv"
3471
- ],
3501
+ devDependencies: ["alchemy", "@sveltejs/adapter-cloudflare"],
3472
3502
  projectDir: webAppDir
3473
3503
  });
3474
3504
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3533,7 +3563,7 @@ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, opt
3533
3563
  const webAppDir = path.join(projectDir, "apps/web");
3534
3564
  if (!await fs.pathExists(webAppDir)) return;
3535
3565
  await addPackageDependency({
3536
- devDependencies: ["alchemy", "dotenv"],
3566
+ devDependencies: ["alchemy"],
3537
3567
  projectDir: webAppDir
3538
3568
  });
3539
3569
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3554,11 +3584,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3554
3584
  const webAppDir = path.join(projectDir, "apps/web");
3555
3585
  if (!await fs.pathExists(webAppDir)) return;
3556
3586
  await addPackageDependency({
3557
- devDependencies: [
3558
- "alchemy",
3559
- "dotenv",
3560
- "@cloudflare/vite-plugin"
3561
- ],
3587
+ devDependencies: ["alchemy", "@cloudflare/vite-plugin"],
3562
3588
  projectDir: webAppDir
3563
3589
  });
3564
3590
  const pkgPath = path.join(webAppDir, "package.json");
@@ -3613,7 +3639,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3613
3639
  //#region src/helpers/deployment/alchemy/alchemy-combined-setup.ts
3614
3640
  async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
3615
3641
  await addPackageDependency({
3616
- devDependencies: ["alchemy", "dotenv"],
3642
+ devDependencies: ["alchemy"],
3617
3643
  projectDir
3618
3644
  });
3619
3645
  const rootPkgPath = path.join(projectDir, "package.json");
@@ -3628,7 +3654,7 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
3628
3654
  await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
3629
3655
  }
3630
3656
  const serverDir = path.join(projectDir, "apps/server");
3631
- if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, packageManager);
3657
+ if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, packageManager, projectDir);
3632
3658
  const frontend = config.frontend;
3633
3659
  const isNext = frontend.includes("next");
3634
3660
  const isNuxt = frontend.includes("nuxt");
@@ -3861,6 +3887,7 @@ async function setupWebDeploy(config) {
3861
3887
  if (webDeploy !== "wrangler" && webDeploy !== "alchemy") return;
3862
3888
  if (webDeploy === "alchemy" && serverDeploy === "alchemy") {
3863
3889
  await setupCombinedAlchemyDeploy(projectDir, packageManager, config);
3890
+ await addAlchemyPackagesDependencies(projectDir);
3864
3891
  return;
3865
3892
  }
3866
3893
  const isNext = frontend.includes("next");
@@ -3884,6 +3911,7 @@ async function setupWebDeploy(config) {
3884
3911
  else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager);
3885
3912
  else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager);
3886
3913
  else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager);
3914
+ await addAlchemyPackagesDependencies(projectDir);
3887
3915
  }
3888
3916
  }
3889
3917
  async function setupWorkersWebDeploy(projectDir, pkgManager) {
@@ -3901,6 +3929,19 @@ async function setupWorkersWebDeploy(projectDir, pkgManager) {
3901
3929
  }
3902
3930
  await setupWorkersVitePlugin(projectDir);
3903
3931
  }
3932
+ async function addAlchemyPackagesDependencies(projectDir) {
3933
+ for (const packageName of [
3934
+ "packages/api",
3935
+ "packages/auth",
3936
+ "packages/db"
3937
+ ]) {
3938
+ const packageDir = path.join(projectDir, packageName);
3939
+ if (await fs.pathExists(packageDir)) await addPackageDependency({
3940
+ devDependencies: ["@cloudflare/workers-types"],
3941
+ projectDir: packageDir
3942
+ });
3943
+ }
3944
+ }
3904
3945
 
3905
3946
  //#endregion
3906
3947
  //#region src/helpers/core/add-deployment.ts
@@ -4060,15 +4101,30 @@ async function updatePackageJsonsWithCatalogs(packagesInfo, catalog) {
4060
4101
  //#endregion
4061
4102
  //#region src/helpers/addons/examples-setup.ts
4062
4103
  async function setupExamples(config) {
4063
- const { examples, frontend, backend, projectDir } = config;
4104
+ const { examples, frontend, backend, projectDir, orm } = config;
4064
4105
  if (backend === "convex" || !examples || examples.length === 0 || examples[0] === "none") return;
4106
+ const apiDir = path.join(projectDir, "packages/api");
4107
+ if (await fs.pathExists(apiDir) && backend !== "none") {
4108
+ if (orm === "drizzle") await addPackageDependency({
4109
+ dependencies: ["drizzle-orm"],
4110
+ projectDir: apiDir
4111
+ });
4112
+ else if (orm === "prisma") await addPackageDependency({
4113
+ dependencies: ["@prisma/client"],
4114
+ projectDir: apiDir
4115
+ });
4116
+ else if (orm === "mongoose") await addPackageDependency({
4117
+ dependencies: ["mongoose"],
4118
+ projectDir: apiDir
4119
+ });
4120
+ }
4065
4121
  if (examples.includes("ai")) {
4066
4122
  const webClientDir = path.join(projectDir, "apps/web");
4067
4123
  const nativeClientDir = path.join(projectDir, "apps/native");
4068
- const apiDir = path.join(projectDir, "packages/api");
4124
+ const apiDir$1 = path.join(projectDir, "packages/api");
4069
4125
  const webClientDirExists = await fs.pathExists(webClientDir);
4070
4126
  const nativeClientDirExists = await fs.pathExists(nativeClientDir);
4071
- const apiDirExists = await fs.pathExists(apiDir);
4127
+ const apiDirExists = await fs.pathExists(apiDir$1);
4072
4128
  const hasNuxt = frontend.includes("nuxt");
4073
4129
  const hasSvelte = frontend.includes("svelte");
4074
4130
  const hasReactWeb = frontend.includes("react-router") || frontend.includes("tanstack-router") || frontend.includes("next") || frontend.includes("tanstack-start");
@@ -4091,7 +4147,11 @@ async function setupExamples(config) {
4091
4147
  });
4092
4148
  if (apiDirExists && backend !== "none") await addPackageDependency({
4093
4149
  dependencies: ["ai", "@ai-sdk/google"],
4094
- projectDir: apiDir
4150
+ projectDir: apiDir$1
4151
+ });
4152
+ if (backend === "self" && webClientDirExists) await addPackageDependency({
4153
+ dependencies: ["ai", "@ai-sdk/google"],
4154
+ projectDir: webClientDir
4095
4155
  });
4096
4156
  }
4097
4157
  }
@@ -4207,7 +4267,7 @@ function getConvexDependencies(frontend) {
4207
4267
  return deps;
4208
4268
  }
4209
4269
  async function setupApi(config) {
4210
- const { api, projectName, frontend, backend, packageManager, projectDir, auth } = config;
4270
+ const { api, projectName, frontend, backend, packageManager, projectDir } = config;
4211
4271
  const isConvex = backend === "convex";
4212
4272
  const webDir = path.join(projectDir, "apps/web");
4213
4273
  const nativeDir = path.join(projectDir, "apps/native");
@@ -4224,40 +4284,18 @@ async function setupApi(config) {
4224
4284
  dependencies: apiDeps.server.dependencies,
4225
4285
  projectDir: apiPackageDir
4226
4286
  });
4227
- const frameworkDeps = [];
4228
- if (backend === "hono") frameworkDeps.push("hono");
4229
- else if (backend === "elysia") frameworkDeps.push("elysia");
4230
- else if (backend === "express") frameworkDeps.push("express", "@types/express");
4231
- else if (backend === "fastify") frameworkDeps.push("fastify");
4232
- else if (backend === "self") {
4233
- if (frontend.includes("next")) frameworkDeps.push("next");
4234
- }
4235
- if (frameworkDeps.length > 0) await addPackageDependency({
4236
- dependencies: frameworkDeps,
4237
- projectDir: apiPackageDir
4287
+ if (backend === "self" && webDirExists) await addPackageDependency({
4288
+ dependencies: apiDeps.server.dependencies,
4289
+ projectDir: webDir
4238
4290
  });
4239
- if (api === "trpc") {
4240
- if (backend === "hono") await addPackageDependency({
4241
- dependencies: ["@hono/trpc-server"],
4242
- projectDir: apiPackageDir
4243
- });
4244
- else if (backend === "elysia") await addPackageDependency({
4245
- dependencies: ["@elysiajs/trpc"],
4246
- projectDir: apiPackageDir
4247
- });
4248
- else if (backend === "express") await addPackageDependency({
4249
- dependencies: ["@trpc/server"],
4250
- projectDir: apiPackageDir
4251
- });
4252
- else if (backend === "fastify") await addPackageDependency({
4253
- dependencies: ["@trpc/server"],
4291
+ if (backend === "self") {
4292
+ const frameworkDeps = [];
4293
+ if (frontend.includes("next")) frameworkDeps.push("next");
4294
+ if (frameworkDeps.length > 0) await addPackageDependency({
4295
+ dependencies: frameworkDeps,
4254
4296
  projectDir: apiPackageDir
4255
4297
  });
4256
4298
  }
4257
- if (auth === "better-auth") await addPackageDependency({
4258
- dependencies: ["better-auth"],
4259
- projectDir: apiPackageDir
4260
- });
4261
4299
  }
4262
4300
  if (webDirExists && apiDeps.web) await addPackageDependency({
4263
4301
  dependencies: apiDeps.web.dependencies,
@@ -4309,34 +4347,23 @@ async function setupBackendDependencies(config) {
4309
4347
  const devDependencies = [];
4310
4348
  if (framework === "hono") {
4311
4349
  dependencies.push("hono");
4312
- if (api === "trpc") dependencies.push("@hono/trpc-server");
4313
- if (runtime === "node") {
4314
- dependencies.push("@hono/node-server");
4315
- devDependencies.push("tsx", "@types/node");
4316
- }
4350
+ if (runtime === "node") dependencies.push("@hono/node-server");
4317
4351
  } else if (framework === "elysia") {
4318
4352
  dependencies.push("elysia", "@elysiajs/cors");
4319
- if (api === "trpc") dependencies.push("@elysiajs/trpc");
4320
- if (runtime === "node") {
4321
- dependencies.push("@elysiajs/node");
4322
- devDependencies.push("tsx", "@types/node");
4323
- }
4353
+ if (runtime === "node") dependencies.push("@elysiajs/node");
4324
4354
  } else if (framework === "express") {
4325
4355
  dependencies.push("express", "cors");
4326
4356
  devDependencies.push("@types/express", "@types/cors");
4327
- if (runtime === "node") devDependencies.push("tsx", "@types/node");
4328
- } else if (framework === "fastify") {
4329
- dependencies.push("fastify", "@fastify/cors");
4330
- if (runtime === "node") devDependencies.push("tsx", "@types/node");
4331
- }
4357
+ } else if (framework === "fastify") dependencies.push("fastify", "@fastify/cors");
4332
4358
  if (api === "trpc") {
4333
- if (framework === "express") dependencies.push("@trpc/server");
4334
- else if (framework === "fastify") dependencies.push("@trpc/server");
4335
- else if (runtime === "workers") dependencies.push("@trpc/server");
4359
+ dependencies.push("@trpc/server");
4360
+ if (framework === "hono") dependencies.push("@hono/trpc-server");
4361
+ else if (framework === "elysia") dependencies.push("@elysiajs/trpc");
4336
4362
  } else if (api === "orpc") dependencies.push("@orpc/server", "@orpc/openapi", "@orpc/zod");
4337
4363
  if (auth === "better-auth") dependencies.push("better-auth");
4338
4364
  if (examples.includes("ai")) dependencies.push("ai", "@ai-sdk/google");
4339
- if (runtime === "bun") devDependencies.push("@types/bun");
4365
+ if (runtime === "node") devDependencies.push("tsx", "@types/node");
4366
+ else if (runtime === "bun") devDependencies.push("@types/bun");
4340
4367
  if (dependencies.length > 0 || devDependencies.length > 0) await addPackageDependency({
4341
4368
  dependencies,
4342
4369
  devDependencies,
@@ -4457,6 +4484,34 @@ function generateAuthSecret(length = 32) {
4457
4484
 
4458
4485
  //#endregion
4459
4486
  //#region src/helpers/core/env-setup.ts
4487
+ function getClientServerVar(frontend, backend) {
4488
+ const hasNextJs = frontend.includes("next");
4489
+ const hasNuxt = frontend.includes("nuxt");
4490
+ const hasSvelte = frontend.includes("svelte");
4491
+ if (backend === "self") return {
4492
+ key: "",
4493
+ value: "",
4494
+ write: false
4495
+ };
4496
+ let key = "VITE_SERVER_URL";
4497
+ if (hasNextJs) key = "NEXT_PUBLIC_SERVER_URL";
4498
+ else if (hasNuxt) key = "NUXT_PUBLIC_SERVER_URL";
4499
+ else if (hasSvelte) key = "PUBLIC_SERVER_URL";
4500
+ return {
4501
+ key,
4502
+ value: "http://localhost:3000",
4503
+ write: true
4504
+ };
4505
+ }
4506
+ function getConvexVar(frontend) {
4507
+ const hasNextJs = frontend.includes("next");
4508
+ const hasNuxt = frontend.includes("nuxt");
4509
+ const hasSvelte = frontend.includes("svelte");
4510
+ if (hasNextJs) return "NEXT_PUBLIC_CONVEX_URL";
4511
+ if (hasNuxt) return "NUXT_PUBLIC_CONVEX_URL";
4512
+ if (hasSvelte) return "PUBLIC_CONVEX_URL";
4513
+ return "VITE_CONVEX_URL";
4514
+ }
4460
4515
  async function addEnvVariablesToFile(filePath, variables) {
4461
4516
  await fs.ensureDir(path.dirname(filePath));
4462
4517
  let envContent = "";
@@ -4515,22 +4570,13 @@ async function setupEnvironmentVariables(config) {
4515
4570
  if (hasReactRouter || hasTanStackRouter || hasTanStackStart || hasNextJs || hasNuxt || hasSolid || hasSvelte) {
4516
4571
  const clientDir = path.join(projectDir, "apps/web");
4517
4572
  if (await fs.pathExists(clientDir)) {
4518
- let envVarName = "VITE_SERVER_URL";
4519
- let serverUrl = "http://localhost:3000";
4520
- if (hasNextJs) envVarName = "NEXT_PUBLIC_SERVER_URL";
4521
- else if (hasNuxt) envVarName = "NUXT_PUBLIC_SERVER_URL";
4522
- else if (hasSvelte) envVarName = "PUBLIC_SERVER_URL";
4523
- if (backend === "convex") {
4524
- if (hasNextJs) envVarName = "NEXT_PUBLIC_CONVEX_URL";
4525
- else if (hasNuxt) envVarName = "NUXT_PUBLIC_CONVEX_URL";
4526
- else if (hasSvelte) envVarName = "PUBLIC_CONVEX_URL";
4527
- else envVarName = "VITE_CONVEX_URL";
4528
- serverUrl = "https://<YOUR_CONVEX_URL>";
4529
- }
4573
+ const baseVar = getClientServerVar(frontend, backend);
4574
+ const envVarName = backend === "convex" ? getConvexVar(frontend) : baseVar.key;
4575
+ const serverUrl = backend === "convex" ? "https://<YOUR_CONVEX_URL>" : baseVar.value;
4530
4576
  const clientVars = [{
4531
4577
  key: envVarName,
4532
4578
  value: serverUrl,
4533
- condition: true
4579
+ condition: backend === "convex" ? true : baseVar.write
4534
4580
  }];
4535
4581
  if (backend === "convex" && auth === "clerk") {
4536
4582
  if (hasNextJs) clientVars.push({
@@ -4622,7 +4668,8 @@ async function setupEnvironmentVariables(config) {
4622
4668
  }
4623
4669
  const serverDir = path.join(projectDir, "apps/server");
4624
4670
  let corsOrigin = "http://localhost:3001";
4625
- if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
4671
+ if (backend === "self") corsOrigin = "http://localhost:3001";
4672
+ else if (hasReactRouter || hasSvelte) corsOrigin = "http://localhost:5173";
4626
4673
  let databaseUrl = null;
4627
4674
  if (database !== "none" && dbSetup === "none") switch (database) {
4628
4675
  case "postgres":
@@ -4636,50 +4683,53 @@ async function setupEnvironmentVariables(config) {
4636
4683
  break;
4637
4684
  case "sqlite":
4638
4685
  if (config.runtime === "workers") databaseUrl = "http://127.0.0.1:8080";
4639
- else databaseUrl = `file:${path.join(config.projectDir, "apps/server", "local.db")}`;
4640
- break;
4641
- }
4642
- if (await fs.pathExists(serverDir)) {
4643
- const serverEnvPath = path.join(serverDir, ".env");
4644
- const serverVars = [
4645
- {
4646
- key: "BETTER_AUTH_SECRET",
4647
- value: generateAuthSecret(),
4648
- condition: !!auth
4649
- },
4650
- {
4651
- key: "BETTER_AUTH_URL",
4652
- value: "http://localhost:3000",
4653
- condition: !!auth
4654
- },
4655
- {
4656
- key: "POLAR_ACCESS_TOKEN",
4657
- value: "",
4658
- condition: config.payments === "polar"
4659
- },
4660
- {
4661
- key: "POLAR_SUCCESS_URL",
4662
- value: `${corsOrigin}/success?checkout_id={CHECKOUT_ID}`,
4663
- condition: config.payments === "polar"
4664
- },
4665
- {
4666
- key: "CORS_ORIGIN",
4667
- value: corsOrigin,
4668
- condition: true
4669
- },
4670
- {
4671
- key: "GOOGLE_GENERATIVE_AI_API_KEY",
4672
- value: "",
4673
- condition: examples?.includes("ai") || false
4674
- },
4675
- {
4676
- key: "DATABASE_URL",
4677
- value: databaseUrl,
4678
- condition: database !== "none" && dbSetup === "none"
4686
+ else {
4687
+ const dbAppDir = backend === "self" ? "apps/web" : "apps/server";
4688
+ databaseUrl = `file:${path.join(config.projectDir, dbAppDir, "local.db")}`;
4679
4689
  }
4680
- ];
4681
- await addEnvVariablesToFile(serverEnvPath, serverVars);
4690
+ break;
4682
4691
  }
4692
+ const serverVars = [
4693
+ {
4694
+ key: "BETTER_AUTH_SECRET",
4695
+ value: generateAuthSecret(),
4696
+ condition: !!auth
4697
+ },
4698
+ {
4699
+ key: "BETTER_AUTH_URL",
4700
+ value: backend === "self" ? "http://localhost:3001" : "http://localhost:3000",
4701
+ condition: !!auth
4702
+ },
4703
+ {
4704
+ key: "POLAR_ACCESS_TOKEN",
4705
+ value: "",
4706
+ condition: config.payments === "polar"
4707
+ },
4708
+ {
4709
+ key: "POLAR_SUCCESS_URL",
4710
+ value: `${corsOrigin}/success?checkout_id={CHECKOUT_ID}`,
4711
+ condition: config.payments === "polar"
4712
+ },
4713
+ {
4714
+ key: "CORS_ORIGIN",
4715
+ value: corsOrigin,
4716
+ condition: true
4717
+ },
4718
+ {
4719
+ key: "GOOGLE_GENERATIVE_AI_API_KEY",
4720
+ value: "",
4721
+ condition: examples?.includes("ai") || false
4722
+ },
4723
+ {
4724
+ key: "DATABASE_URL",
4725
+ value: databaseUrl,
4726
+ condition: database !== "none" && dbSetup === "none"
4727
+ }
4728
+ ];
4729
+ if (backend === "self") {
4730
+ const webDir = path.join(projectDir, "apps/web");
4731
+ if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverVars);
4732
+ } else if (await fs.pathExists(serverDir)) await addEnvVariablesToFile(path.join(serverDir, ".env"), serverVars);
4683
4733
  const isUnifiedAlchemy = webDeploy === "alchemy" && serverDeploy === "alchemy";
4684
4734
  const isIndividualAlchemy = webDeploy === "alchemy" || serverDeploy === "alchemy";
4685
4735
  if (isUnifiedAlchemy) {
@@ -4698,20 +4748,27 @@ async function setupEnvironmentVariables(config) {
4698
4748
  condition: true
4699
4749
  }]);
4700
4750
  }
4701
- if (serverDeploy === "alchemy") await addEnvVariablesToFile(path.join(serverDir, ".env"), [{
4702
- key: "ALCHEMY_PASSWORD",
4703
- value: "please-change-this",
4704
- condition: true
4705
- }]);
4751
+ if (serverDeploy === "alchemy") {
4752
+ const serverAlchemyVars = [{
4753
+ key: "ALCHEMY_PASSWORD",
4754
+ value: "please-change-this",
4755
+ condition: true
4756
+ }];
4757
+ if (backend === "self") {
4758
+ const webDir = path.join(projectDir, "apps/web");
4759
+ if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverAlchemyVars);
4760
+ } else await addEnvVariablesToFile(path.join(serverDir, ".env"), serverAlchemyVars);
4761
+ }
4706
4762
  }
4707
4763
  }
4708
4764
 
4709
4765
  //#endregion
4710
4766
  //#region src/helpers/database-providers/d1-setup.ts
4711
4767
  async function setupCloudflareD1(config) {
4712
- const { projectDir, serverDeploy, orm } = config;
4768
+ const { projectDir, serverDeploy, orm, backend } = config;
4713
4769
  if (serverDeploy === "wrangler") {
4714
- const envPath = path.join(projectDir, "apps/server", ".env");
4770
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
4771
+ const envPath = path.join(projectDir, targetApp, ".env");
4715
4772
  const variables = [
4716
4773
  {
4717
4774
  key: "CLOUDFLARE_ACCOUNT_ID",
@@ -4734,7 +4791,8 @@ async function setupCloudflareD1(config) {
4734
4791
  } catch (_err) {}
4735
4792
  }
4736
4793
  if ((serverDeploy === "wrangler" || serverDeploy === "alchemy") && orm === "prisma") {
4737
- const envPath = path.join(projectDir, "apps/server", ".env");
4794
+ const targetApp2 = backend === "self" ? "apps/web" : "apps/server";
4795
+ const envPath = path.join(projectDir, targetApp2, ".env");
4738
4796
  const variables = [{
4739
4797
  key: "DATABASE_URL",
4740
4798
  value: `file:${path.join(projectDir, "apps/server", "local.db")}`,
@@ -4743,7 +4801,7 @@ async function setupCloudflareD1(config) {
4743
4801
  try {
4744
4802
  await addEnvVariablesToFile(envPath, variables);
4745
4803
  } catch (_err) {}
4746
- const serverDir = path.join(projectDir, "apps/server");
4804
+ const serverDir = path.join(projectDir, backend === "self" ? "apps/web" : "apps/server");
4747
4805
  await addPackageDependency({
4748
4806
  dependencies: ["@prisma/adapter-d1"],
4749
4807
  projectDir: serverDir
@@ -4794,14 +4852,13 @@ async function commandExists(command) {
4794
4852
  //#endregion
4795
4853
  //#region src/helpers/database-providers/mongodb-atlas-setup.ts
4796
4854
  async function checkAtlasCLI() {
4797
- const s = spinner();
4798
- s.start("Checking for MongoDB Atlas CLI...");
4799
4855
  try {
4800
4856
  const exists = await commandExists("atlas");
4801
- s.stop(exists ? "MongoDB Atlas CLI found" : pc.yellow("MongoDB Atlas CLI not found"));
4857
+ if (exists) log.info("MongoDB Atlas CLI found");
4858
+ else log.warn(pc.yellow("MongoDB Atlas CLI not found"));
4802
4859
  return exists;
4803
4860
  } catch (_error) {
4804
- s.stop(pc.red("Error checking MongoDB Atlas CLI"));
4861
+ log.error(pc.red("Error checking MongoDB Atlas CLI"));
4805
4862
  return false;
4806
4863
  }
4807
4864
  }
@@ -4812,12 +4869,13 @@ async function initMongoDBAtlas(serverDir) {
4812
4869
  log.info(pc.yellow("Please install it from: https://www.mongodb.com/docs/atlas/cli/current/install-atlas-cli/"));
4813
4870
  return null;
4814
4871
  }
4815
- log.info(pc.blue("Running MongoDB Atlas setup..."));
4872
+ log.info("Running MongoDB Atlas setup...");
4816
4873
  await execa("atlas", ["deployments", "setup"], {
4817
4874
  cwd: serverDir,
4875
+ shell: true,
4818
4876
  stdio: "inherit"
4819
4877
  });
4820
- log.info(pc.green("MongoDB Atlas deployment ready"));
4878
+ log.success("MongoDB Atlas deployment ready");
4821
4879
  const connectionString = await text({
4822
4880
  message: "Enter your MongoDB connection string:",
4823
4881
  placeholder: "mongodb+srv://username:password@cluster.mongodb.net/database",
@@ -4836,9 +4894,10 @@ async function initMongoDBAtlas(serverDir) {
4836
4894
  return null;
4837
4895
  }
4838
4896
  }
4839
- async function writeEnvFile$3(projectDir, config) {
4897
+ async function writeEnvFile$3(projectDir, backend, config) {
4840
4898
  try {
4841
- const envPath = path.join(projectDir, "apps/server", ".env");
4899
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
4900
+ const envPath = path.join(projectDir, targetApp, ".env");
4842
4901
  const variables = [{
4843
4902
  key: "DATABASE_URL",
4844
4903
  value: config?.connectionString ?? "mongodb://localhost:27017/mydb",
@@ -4867,16 +4926,14 @@ ${pc.green("MongoDB Atlas Manual Setup Instructions:")}
4867
4926
  `);
4868
4927
  }
4869
4928
  async function setupMongoDBAtlas(config, cliInput) {
4870
- const { projectDir } = config;
4929
+ const { projectDir, backend } = config;
4871
4930
  const manualDb = cliInput?.manualDb ?? false;
4872
- const mainSpinner = spinner();
4873
- mainSpinner.start("Setting up MongoDB Atlas...");
4874
- const serverDir = path.join(projectDir, "apps/server");
4931
+ const serverDir = path.join(projectDir, "packages/db");
4875
4932
  try {
4876
4933
  await fs.ensureDir(serverDir);
4877
4934
  if (manualDb) {
4878
- mainSpinner.stop("MongoDB Atlas manual setup selected");
4879
- await writeEnvFile$3(projectDir);
4935
+ log.info("MongoDB Atlas manual setup selected");
4936
+ await writeEnvFile$3(projectDir, backend);
4880
4937
  displayManualSetupInstructions$3();
4881
4938
  return;
4882
4939
  }
@@ -4895,26 +4952,24 @@ async function setupMongoDBAtlas(config, cliInput) {
4895
4952
  });
4896
4953
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
4897
4954
  if (mode === "manual") {
4898
- mainSpinner.stop("MongoDB Atlas manual setup selected");
4899
- await writeEnvFile$3(projectDir);
4955
+ log.info("MongoDB Atlas manual setup selected");
4956
+ await writeEnvFile$3(projectDir, backend);
4900
4957
  displayManualSetupInstructions$3();
4901
4958
  return;
4902
4959
  }
4903
- mainSpinner.stop("MongoDB Atlas setup ready");
4904
4960
  const config$1 = await initMongoDBAtlas(serverDir);
4905
4961
  if (config$1) {
4906
- await writeEnvFile$3(projectDir, config$1);
4962
+ await writeEnvFile$3(projectDir, backend, config$1);
4907
4963
  log.success(pc.green("MongoDB Atlas setup complete! Connection saved to .env file."));
4908
4964
  } else {
4909
4965
  log.warn(pc.yellow("Falling back to local MongoDB configuration"));
4910
- await writeEnvFile$3(projectDir);
4966
+ await writeEnvFile$3(projectDir, backend);
4911
4967
  displayManualSetupInstructions$3();
4912
4968
  }
4913
4969
  } catch (error) {
4914
- mainSpinner.stop(pc.red("MongoDB Atlas setup failed"));
4915
4970
  consola.error(pc.red(`Error during MongoDB Atlas setup: ${error instanceof Error ? error.message : String(error)}`));
4916
4971
  try {
4917
- await writeEnvFile$3(projectDir);
4972
+ await writeEnvFile$3(projectDir, backend);
4918
4973
  displayManualSetupInstructions$3();
4919
4974
  } catch {}
4920
4975
  }
@@ -4971,7 +5026,7 @@ async function executeNeonCommand(packageManager, commandArgsString, spinnerText
4971
5026
  }
4972
5027
  async function createNeonProject(projectName, regionId, packageManager) {
4973
5028
  try {
4974
- const commandArgsString = `neonctl projects create --name ${projectName} --region-id ${regionId} --output json`;
5029
+ const commandArgsString = `neonctl@latest projects create --name ${projectName} --region-id ${regionId} --output json`;
4975
5030
  const { stdout } = await executeNeonCommand(packageManager, commandArgsString, `Creating Neon project "${projectName}"...`);
4976
5031
  const response = JSON.parse(stdout);
4977
5032
  if (response.project && response.connection_uris && response.connection_uris.length > 0) {
@@ -4991,8 +5046,9 @@ async function createNeonProject(projectName, regionId, packageManager) {
4991
5046
  consola$1.error(pc.red("Failed to create Neon project"));
4992
5047
  }
4993
5048
  }
4994
- async function writeEnvFile$2(projectDir, config) {
4995
- const envPath = path.join(projectDir, "apps/server", ".env");
5049
+ async function writeEnvFile$2(projectDir, backend, config) {
5050
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5051
+ const envPath = path.join(projectDir, targetApp, ".env");
4996
5052
  const variables = [{
4997
5053
  key: "DATABASE_URL",
4998
5054
  value: config?.connectionString ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
@@ -5001,16 +5057,17 @@ async function writeEnvFile$2(projectDir, config) {
5001
5057
  await addEnvVariablesToFile(envPath, variables);
5002
5058
  return true;
5003
5059
  }
5004
- async function setupWithNeonDb(projectDir, packageManager) {
5060
+ async function setupWithNeonDb(projectDir, packageManager, backend) {
5005
5061
  try {
5006
5062
  const s = spinner();
5007
5063
  s.start("Creating Neon database using neondb...");
5008
- const serverDir = path.join(projectDir, "apps/server");
5009
- await fs.ensureDir(serverDir);
5010
- const packageCmd = getPackageExecutionCommand(packageManager, "neondb --yes");
5064
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5065
+ const targetDir = path.join(projectDir, targetApp);
5066
+ await fs.ensureDir(targetDir);
5067
+ const packageCmd = getPackageExecutionCommand(packageManager, "neondb@latest --yes");
5011
5068
  await execa(packageCmd, {
5012
5069
  shell: true,
5013
- cwd: serverDir
5070
+ cwd: targetDir
5014
5071
  });
5015
5072
  s.stop(pc.green("Neon database created successfully!"));
5016
5073
  return true;
@@ -5019,23 +5076,23 @@ async function setupWithNeonDb(projectDir, packageManager) {
5019
5076
  throw error;
5020
5077
  }
5021
5078
  }
5022
- function displayManualSetupInstructions$2() {
5079
+ function displayManualSetupInstructions$2(target) {
5023
5080
  log.info(`Manual Neon PostgreSQL Setup Instructions:
5024
5081
 
5025
5082
  1. Visit https://neon.tech and create an account
5026
5083
  2. Create a new project from the dashboard
5027
5084
  3. Get your connection string
5028
- 4. Add the database URL to the .env file in apps/server/.env
5085
+ 4. Add the database URL to the .env file in ${target}/.env
5029
5086
 
5030
5087
  DATABASE_URL="your_connection_string"`);
5031
5088
  }
5032
5089
  async function setupNeonPostgres(config, cliInput) {
5033
- const { packageManager, projectDir } = config;
5090
+ const { packageManager, projectDir, backend } = config;
5034
5091
  const manualDb = cliInput?.manualDb ?? false;
5035
5092
  try {
5036
5093
  if (manualDb) {
5037
- await writeEnvFile$2(projectDir);
5038
- displayManualSetupInstructions$2();
5094
+ await writeEnvFile$2(projectDir, backend);
5095
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
5039
5096
  return;
5040
5097
  }
5041
5098
  const mode = await select({
@@ -5053,8 +5110,8 @@ async function setupNeonPostgres(config, cliInput) {
5053
5110
  });
5054
5111
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5055
5112
  if (mode === "manual") {
5056
- await writeEnvFile$2(projectDir);
5057
- displayManualSetupInstructions$2();
5113
+ await writeEnvFile$2(projectDir, backend);
5114
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
5058
5115
  return;
5059
5116
  }
5060
5117
  const setupMethod = await select({
@@ -5071,7 +5128,7 @@ async function setupNeonPostgres(config, cliInput) {
5071
5128
  initialValue: "neondb"
5072
5129
  });
5073
5130
  if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
5074
- if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager);
5131
+ if (setupMethod === "neondb") await setupWithNeonDb(projectDir, packageManager, backend);
5075
5132
  else {
5076
5133
  const suggestedProjectName = path.basename(projectDir);
5077
5134
  const projectName = await text({
@@ -5089,22 +5146,22 @@ async function setupNeonPostgres(config, cliInput) {
5089
5146
  if (!neonConfig) throw new Error("Failed to create project - couldn't get connection information");
5090
5147
  const finalSpinner = spinner();
5091
5148
  finalSpinner.start("Configuring database connection");
5092
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5093
- await writeEnvFile$2(projectDir, neonConfig);
5149
+ await writeEnvFile$2(projectDir, backend, neonConfig);
5094
5150
  finalSpinner.stop("Neon database configured!");
5095
5151
  }
5096
5152
  } catch (error) {
5097
5153
  if (error instanceof Error) consola$1.error(pc.red(error.message));
5098
- await writeEnvFile$2(projectDir);
5099
- displayManualSetupInstructions$2();
5154
+ await writeEnvFile$2(projectDir, backend);
5155
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
5100
5156
  }
5101
5157
  }
5102
5158
 
5103
5159
  //#endregion
5104
5160
  //#region src/helpers/database-providers/planetscale-setup.ts
5105
5161
  async function setupPlanetScale(config) {
5106
- const { projectDir, database, orm } = config;
5107
- const envPath = path.join(projectDir, "apps/server", ".env");
5162
+ const { projectDir, database, orm, backend } = config;
5163
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5164
+ const envPath = path.join(projectDir, targetApp, ".env");
5108
5165
  if (database === "mysql" && orm === "drizzle") {
5109
5166
  const variables = [
5110
5167
  {
@@ -5128,7 +5185,7 @@ async function setupPlanetScale(config) {
5128
5185
  condition: true
5129
5186
  }
5130
5187
  ];
5131
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5188
+ await fs.ensureDir(path.join(projectDir, targetApp));
5132
5189
  await addEnvVariablesToFile(envPath, variables);
5133
5190
  }
5134
5191
  if (database === "postgres" && orm === "prisma") {
@@ -5137,7 +5194,7 @@ async function setupPlanetScale(config) {
5137
5194
  value: "postgresql://username:password@host/database?sslaccept=strict",
5138
5195
  condition: true
5139
5196
  }];
5140
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5197
+ await fs.ensureDir(path.join(projectDir, targetApp));
5141
5198
  await addEnvVariablesToFile(envPath, variables);
5142
5199
  }
5143
5200
  if (database === "postgres" && orm === "drizzle") {
@@ -5146,7 +5203,7 @@ async function setupPlanetScale(config) {
5146
5203
  value: "postgresql://username:password@host/database?sslmode=verify-full",
5147
5204
  condition: true
5148
5205
  }];
5149
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5206
+ await fs.ensureDir(path.join(projectDir, targetApp));
5150
5207
  await addEnvVariablesToFile(envPath, variables);
5151
5208
  }
5152
5209
  if (database === "mysql" && orm === "prisma") {
@@ -5155,7 +5212,7 @@ async function setupPlanetScale(config) {
5155
5212
  value: "mysql://username:password@host/database?sslaccept=strict",
5156
5213
  condition: true
5157
5214
  }];
5158
- await fs.ensureDir(path.join(projectDir, "apps/server"));
5215
+ await fs.ensureDir(path.join(projectDir, targetApp));
5159
5216
  await addEnvVariablesToFile(envPath, variables);
5160
5217
  }
5161
5218
  }
@@ -5225,7 +5282,7 @@ async function initPrismaDatabase(serverDir, packageManager) {
5225
5282
  try {
5226
5283
  const prismaDir = path.join(serverDir, "prisma");
5227
5284
  await fs.ensureDir(prismaDir);
5228
- log.info("Starting Prisma PostgreSQL setup. Please follow the instructions below:");
5285
+ log.info("Starting Prisma PostgreSQL setup.");
5229
5286
  const prismaInitCommand = getPackageExecutionCommand(packageManager, "prisma init --db");
5230
5287
  await execa(prismaInitCommand, {
5231
5288
  cwd: serverDir,
@@ -5247,9 +5304,10 @@ async function initPrismaDatabase(serverDir, packageManager) {
5247
5304
  return null;
5248
5305
  }
5249
5306
  }
5250
- async function writeEnvFile$1(projectDir, config) {
5307
+ async function writeEnvFile$1(projectDir, backend, config) {
5251
5308
  try {
5252
- const envPath = path.join(projectDir, "apps/server", ".env");
5309
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5310
+ const envPath = path.join(projectDir, targetApp, ".env");
5253
5311
  const variables = [{
5254
5312
  key: "DATABASE_URL",
5255
5313
  value: config?.databaseUrl ?? "postgresql://postgres:postgres@localhost:5432/mydb?schema=public",
@@ -5265,23 +5323,23 @@ async function writeEnvFile$1(projectDir, config) {
5265
5323
  consola$1.error("Failed to update environment configuration");
5266
5324
  }
5267
5325
  }
5268
- async function addDotenvImportToPrismaConfig(projectDir) {
5326
+ async function addDotenvImportToPrismaConfig(projectDir, backend) {
5269
5327
  try {
5270
5328
  const prismaConfigPath = path.join(projectDir, "packages/db/prisma.config.ts");
5271
5329
  let content = await fs.readFile(prismaConfigPath, "utf8");
5272
- content = `import dotenv from "dotenv";\ndotenv.config({ path: "../../apps/server/.env" });\n${content}`;
5330
+ content = `import dotenv from "dotenv";\ndotenv.config({ path: "${backend === "self" ? "../../apps/web/.env" : "../../apps/server/.env"}" });\n${content}`;
5273
5331
  await fs.writeFile(prismaConfigPath, content);
5274
5332
  } catch (_error) {
5275
5333
  consola$1.error("Failed to update prisma.config.ts");
5276
5334
  }
5277
5335
  }
5278
- function displayManualSetupInstructions$1() {
5336
+ function displayManualSetupInstructions$1(target) {
5279
5337
  log.info(`Manual Prisma PostgreSQL Setup Instructions:
5280
5338
 
5281
5339
  1. Visit https://console.prisma.io and create an account
5282
5340
  2. Create a new PostgreSQL database from the dashboard
5283
5341
  3. Get your database URL
5284
- 4. Add the database URL to the .env file in apps/server/.env
5342
+ 4. Add the database URL to the .env file in ${target}/.env
5285
5343
 
5286
5344
  DATABASE_URL="your_database_url"`);
5287
5345
  }
@@ -5299,14 +5357,14 @@ async function addPrismaAccelerateExtension(projectDir) {
5299
5357
  }
5300
5358
  }
5301
5359
  async function setupPrismaPostgres(config, cliInput) {
5302
- const { packageManager, projectDir, orm } = config;
5360
+ const { packageManager, projectDir, orm, backend } = config;
5303
5361
  const manualDb = cliInput?.manualDb ?? false;
5304
- const serverDir = path.join(projectDir, "apps/server");
5362
+ const dbDir = path.join(projectDir, "packages/db");
5305
5363
  try {
5306
- await fs.ensureDir(serverDir);
5364
+ await fs.ensureDir(dbDir);
5307
5365
  if (manualDb) {
5308
- await writeEnvFile$1(projectDir);
5309
- displayManualSetupInstructions$1();
5366
+ await writeEnvFile$1(projectDir, backend);
5367
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5310
5368
  return;
5311
5369
  }
5312
5370
  const mode = await select({
@@ -5324,8 +5382,8 @@ async function setupPrismaPostgres(config, cliInput) {
5324
5382
  });
5325
5383
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5326
5384
  if (mode === "manual") {
5327
- await writeEnvFile$1(projectDir);
5328
- displayManualSetupInstructions$1();
5385
+ await writeEnvFile$1(projectDir, backend);
5386
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5329
5387
  return;
5330
5388
  }
5331
5389
  const setupOptions = [{
@@ -5345,26 +5403,26 @@ async function setupPrismaPostgres(config, cliInput) {
5345
5403
  });
5346
5404
  if (isCancel(setupMethod)) return exitCancelled("Operation cancelled");
5347
5405
  let prismaConfig = null;
5348
- if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(serverDir, packageManager, orm);
5349
- else prismaConfig = await initPrismaDatabase(serverDir, packageManager);
5406
+ if (setupMethod === "create-db") prismaConfig = await setupWithCreateDb(dbDir, packageManager, orm);
5407
+ else prismaConfig = await initPrismaDatabase(dbDir, packageManager);
5350
5408
  if (prismaConfig) {
5351
- await writeEnvFile$1(projectDir, prismaConfig);
5409
+ await writeEnvFile$1(projectDir, backend, prismaConfig);
5352
5410
  if (orm === "prisma") {
5353
- await addDotenvImportToPrismaConfig(projectDir);
5411
+ await addDotenvImportToPrismaConfig(projectDir, backend);
5354
5412
  await addPrismaAccelerateExtension(projectDir);
5355
5413
  }
5356
5414
  const connectionType = orm === "drizzle" ? "direct connection" : "Prisma Accelerate";
5357
5415
  log.success(pc.green(`Prisma Postgres database configured successfully with ${connectionType}!`));
5358
5416
  if (prismaConfig.claimUrl) log.info(pc.blue(`Claim URL saved to .env: ${prismaConfig.claimUrl}`));
5359
5417
  } else {
5360
- await writeEnvFile$1(projectDir);
5361
- displayManualSetupInstructions$1();
5418
+ await writeEnvFile$1(projectDir, backend);
5419
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5362
5420
  }
5363
5421
  } catch (error) {
5364
5422
  consola$1.error(pc.red(`Error during Prisma Postgres setup: ${error instanceof Error ? error.message : String(error)}`));
5365
5423
  try {
5366
- await writeEnvFile$1(projectDir);
5367
- displayManualSetupInstructions$1();
5424
+ await writeEnvFile$1(projectDir, backend);
5425
+ displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
5368
5426
  } catch {}
5369
5427
  log.info("Setup completed with manual configuration required.");
5370
5428
  }
@@ -5372,9 +5430,10 @@ async function setupPrismaPostgres(config, cliInput) {
5372
5430
 
5373
5431
  //#endregion
5374
5432
  //#region src/helpers/database-providers/supabase-setup.ts
5375
- async function writeSupabaseEnvFile(projectDir, databaseUrl) {
5433
+ async function writeSupabaseEnvFile(projectDir, backend, databaseUrl) {
5376
5434
  try {
5377
- const envPath = path.join(projectDir, "apps/server", ".env");
5435
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5436
+ const envPath = path.join(projectDir, targetApp, ".env");
5378
5437
  const dbUrlToUse = databaseUrl || "postgresql://postgres:postgres@127.0.0.1:54322/postgres";
5379
5438
  await addEnvVariablesToFile(envPath, [{
5380
5439
  key: "DATABASE_URL",
@@ -5460,14 +5519,14 @@ ${pc.dim(output)}` : ""}
5460
5519
  ${pc.gray("DATABASE_URL=\"your_supabase_db_url\"")}`);
5461
5520
  }
5462
5521
  async function setupSupabase(config, cliInput) {
5463
- const { projectDir, packageManager } = config;
5522
+ const { projectDir, packageManager, backend } = config;
5464
5523
  const manualDb = cliInput?.manualDb ?? false;
5465
5524
  const serverDir = path.join(projectDir, "packages", "db");
5466
5525
  try {
5467
5526
  await fs.ensureDir(serverDir);
5468
5527
  if (manualDb) {
5469
5528
  displayManualSupabaseInstructions();
5470
- await writeSupabaseEnvFile(projectDir, "");
5529
+ await writeSupabaseEnvFile(projectDir, backend, "");
5471
5530
  return;
5472
5531
  }
5473
5532
  const mode = await select({
@@ -5486,7 +5545,7 @@ async function setupSupabase(config, cliInput) {
5486
5545
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5487
5546
  if (mode === "manual") {
5488
5547
  displayManualSupabaseInstructions();
5489
- await writeSupabaseEnvFile(projectDir, "");
5548
+ await writeSupabaseEnvFile(projectDir, backend, "");
5490
5549
  return;
5491
5550
  }
5492
5551
  if (!await initializeSupabase(serverDir, packageManager)) {
@@ -5499,7 +5558,7 @@ async function setupSupabase(config, cliInput) {
5499
5558
  return;
5500
5559
  }
5501
5560
  const dbUrl = extractDbUrl(supabaseOutput);
5502
- if (dbUrl) if (await writeSupabaseEnvFile(projectDir, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
5561
+ if (dbUrl) if (await writeSupabaseEnvFile(projectDir, backend, dbUrl)) log.success(pc.green("Supabase local development setup ready!"));
5503
5562
  else {
5504
5563
  log.error(pc.red("Supabase setup completed, but failed to update .env automatically."));
5505
5564
  displayManualSupabaseInstructions(supabaseOutput);
@@ -5627,8 +5686,9 @@ async function createTursoDatabase(dbName, groupName) {
5627
5686
  s.stop(pc.red("Failed to retrieve database connection details"));
5628
5687
  }
5629
5688
  }
5630
- async function writeEnvFile(projectDir, config) {
5631
- const envPath = path.join(projectDir, "apps/server", ".env");
5689
+ async function writeEnvFile(projectDir, backend, config) {
5690
+ const targetApp = backend === "self" ? "apps/web" : "apps/server";
5691
+ const envPath = path.join(projectDir, targetApp, ".env");
5632
5692
  const variables = [{
5633
5693
  key: "DATABASE_URL",
5634
5694
  value: config?.dbUrl ?? "",
@@ -5652,12 +5712,12 @@ DATABASE_URL=your_database_url
5652
5712
  DATABASE_AUTH_TOKEN=your_auth_token`);
5653
5713
  }
5654
5714
  async function setupTurso(config, cliInput) {
5655
- const { orm, projectDir } = config;
5715
+ const { orm, projectDir, backend } = config;
5656
5716
  const manualDb = cliInput?.manualDb ?? false;
5657
5717
  const setupSpinner = spinner();
5658
5718
  try {
5659
5719
  if (manualDb) {
5660
- await writeEnvFile(projectDir);
5720
+ await writeEnvFile(projectDir, backend);
5661
5721
  displayManualSetupInstructions();
5662
5722
  return;
5663
5723
  }
@@ -5676,7 +5736,7 @@ async function setupTurso(config, cliInput) {
5676
5736
  });
5677
5737
  if (isCancel(mode)) return exitCancelled("Operation cancelled");
5678
5738
  if (mode === "manual") {
5679
- await writeEnvFile(projectDir);
5739
+ await writeEnvFile(projectDir, backend);
5680
5740
  displayManualSetupInstructions();
5681
5741
  return;
5682
5742
  }
@@ -5686,7 +5746,7 @@ async function setupTurso(config, cliInput) {
5686
5746
  if (platform === "win32") {
5687
5747
  if (setupSpinner) setupSpinner.stop(pc.yellow("Turso setup not supported on Windows"));
5688
5748
  log.warn(pc.yellow("Automatic Turso setup is not supported on Windows."));
5689
- await writeEnvFile(projectDir);
5749
+ await writeEnvFile(projectDir, backend);
5690
5750
  displayManualSetupInstructions();
5691
5751
  return;
5692
5752
  }
@@ -5698,7 +5758,7 @@ async function setupTurso(config, cliInput) {
5698
5758
  });
5699
5759
  if (isCancel(shouldInstall)) return exitCancelled("Operation cancelled");
5700
5760
  if (!shouldInstall) {
5701
- await writeEnvFile(projectDir);
5761
+ await writeEnvFile(projectDir, backend);
5702
5762
  displayManualSetupInstructions();
5703
5763
  return;
5704
5764
  }
@@ -5720,7 +5780,7 @@ async function setupTurso(config, cliInput) {
5720
5780
  dbName = dbNameResponse;
5721
5781
  try {
5722
5782
  const config$1 = await createTursoDatabase(dbName, selectedGroup);
5723
- await writeEnvFile(projectDir, config$1);
5783
+ await writeEnvFile(projectDir, backend, config$1);
5724
5784
  success = true;
5725
5785
  } catch (error) {
5726
5786
  if (error instanceof Error && error.message === "DATABASE_EXISTS") {
@@ -5733,7 +5793,7 @@ async function setupTurso(config, cliInput) {
5733
5793
  } catch (error) {
5734
5794
  if (setupSpinner) setupSpinner.stop(pc.red("Turso CLI availability check failed"));
5735
5795
  consola.error(pc.red(`Error during Turso setup: ${error instanceof Error ? error.message : String(error)}`));
5736
- await writeEnvFile(projectDir);
5796
+ await writeEnvFile(projectDir, backend);
5737
5797
  displayManualSetupInstructions();
5738
5798
  log.success("Setup completed with manual configuration required.");
5739
5799
  }
@@ -5755,32 +5815,36 @@ async function setupDatabase(config, cliInput) {
5755
5815
  const dbPackageDir = path.join(projectDir, "packages/db");
5756
5816
  if (!await fs.pathExists(dbPackageDir)) return;
5757
5817
  try {
5758
- if (orm === "prisma") if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
5759
- dependencies: [
5760
- "@prisma/client",
5761
- "@prisma/adapter-planetscale",
5762
- "@planetscale/database"
5763
- ],
5764
- devDependencies: ["prisma"],
5765
- projectDir: dbPackageDir
5766
- });
5767
- else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
5768
- dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
5769
- devDependencies: ["prisma"],
5770
- projectDir: dbPackageDir
5771
- });
5772
- else await addPackageDependency({
5773
- dependencies: ["@prisma/client"],
5774
- devDependencies: ["prisma"],
5775
- projectDir: dbPackageDir
5776
- });
5777
- else if (orm === "drizzle") {
5778
- if (database === "sqlite") await addPackageDependency({
5818
+ if (orm === "prisma") {
5819
+ if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
5779
5820
  dependencies: [
5780
- "drizzle-orm",
5781
- "@libsql/client",
5782
- "libsql"
5821
+ "@prisma/client",
5822
+ "@prisma/adapter-planetscale",
5823
+ "@planetscale/database"
5783
5824
  ],
5825
+ devDependencies: ["prisma"],
5826
+ projectDir: dbPackageDir
5827
+ });
5828
+ else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
5829
+ dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
5830
+ devDependencies: ["prisma"],
5831
+ projectDir: dbPackageDir
5832
+ });
5833
+ else await addPackageDependency({
5834
+ dependencies: ["@prisma/client"],
5835
+ devDependencies: ["prisma"],
5836
+ projectDir: dbPackageDir
5837
+ });
5838
+ if (backend === "self") {
5839
+ const webDir = path.join(projectDir, "apps/web");
5840
+ if (await fs.pathExists(webDir)) await addPackageDependency({
5841
+ dependencies: ["@prisma/client"],
5842
+ projectDir: webDir
5843
+ });
5844
+ }
5845
+ } else if (orm === "drizzle") {
5846
+ if (database === "sqlite") await addPackageDependency({
5847
+ dependencies: ["drizzle-orm", "@libsql/client"],
5784
5848
  devDependencies: ["drizzle-kit"],
5785
5849
  projectDir: dbPackageDir
5786
5850
  });
@@ -5929,7 +5993,7 @@ This project uses Convex as a backend. You'll need to set up Convex before runni
5929
5993
  ${packageManagerRunCmd} dev:setup
5930
5994
  \`\`\`
5931
5995
 
5932
- Follow the prompts to create a new Convex project and connect it to your application.${auth === "clerk" ? " See [Convex + Clerk guide](https://docs.convex.dev/auth/clerk) for auth setup." : ""}` : generateDatabaseSetup(database, auth, packageManagerRunCmd, orm, options.dbSetup, options.serverDeploy)}
5996
+ Follow the prompts to create a new Convex project and connect it to your application.${auth === "clerk" ? " See [Convex + Clerk guide](https://docs.convex.dev/auth/clerk) for auth setup." : ""}` : generateDatabaseSetup(database, auth, packageManagerRunCmd, orm, options.dbSetup, options.serverDeploy, options.backend)}
5933
5997
 
5934
5998
  Then, run the development server:
5935
5999
 
@@ -5982,6 +6046,7 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
5982
6046
  const instructions = [];
5983
6047
  const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
5984
6048
  const isBackendNone = backend === "none";
6049
+ const isBackendSelf = backend === "self";
5985
6050
  if (!hasFrontendNone) {
5986
6051
  const hasTanstackRouter = frontend.includes("tanstack-router");
5987
6052
  const hasReactRouter = frontend.includes("react-router");
@@ -5990,17 +6055,19 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
5990
6055
  const hasSvelte = frontend.includes("svelte");
5991
6056
  const hasNuxt = frontend.includes("nuxt");
5992
6057
  const hasSolid = frontend.includes("solid");
5993
- if (hasTanstackRouter || hasReactRouter || hasNext || hasTanstackStart || hasSvelte || hasNuxt || hasSolid) instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see the web application.`);
6058
+ if (hasTanstackRouter || hasReactRouter || hasNext || hasTanstackStart || hasSvelte || hasNuxt || hasSolid) if (isBackendSelf) instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see your fullstack application.`);
6059
+ else instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see the web application.`);
5994
6060
  }
5995
6061
  if (hasNative) instructions.push("Use the Expo Go app to run the mobile application.");
5996
6062
  if (isConvex) instructions.push("Your app will connect to the Convex cloud backend automatically.");
5997
- else if (!isBackendNone) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
6063
+ else if (!isBackendNone && !isBackendSelf) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
5998
6064
  return instructions.join("\n");
5999
6065
  }
6000
6066
  function generateProjectStructure(projectName, frontend, backend, addons, isConvex, api, auth) {
6001
6067
  const structure = [`${projectName}/`, "├── apps/"];
6002
6068
  const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
6003
6069
  const isBackendNone = backend === "none";
6070
+ const isBackendSelf = backend === "self";
6004
6071
  if (!hasFrontendNone) {
6005
6072
  const hasTanstackRouter = frontend.includes("tanstack-router");
6006
6073
  const hasReactRouter = frontend.includes("react-router");
@@ -6018,21 +6085,32 @@ function generateProjectStructure(projectName, frontend, backend, addons, isConv
6018
6085
  else if (hasSvelte) frontendType = "SvelteKit";
6019
6086
  else if (hasNuxt) frontendType = "Nuxt";
6020
6087
  else if (hasSolid) frontendType = "SolidJS";
6021
- structure.push(`│ ├── web/ # Frontend application (${frontendType})`);
6088
+ if (isBackendSelf) structure.push(`│ └── web/ # Fullstack application (${frontendType})`);
6089
+ else structure.push(`│ ├── web/ # Frontend application (${frontendType})`);
6022
6090
  }
6023
6091
  }
6024
- if (frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
6025
- if (addons.includes("starlight")) structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
6026
- if (isConvex) {
6027
- structure.push("├── packages/");
6028
- structure.push("│ └── backend/ # Convex backend functions and schema");
6029
- if (auth === "clerk") structure.push("│ ├── convex/ # Convex functions and schema", "│ └── .env.local # Convex environment variables");
6030
- } else if (!isBackendNone) {
6092
+ if (frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) if (isBackendSelf) structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
6093
+ else structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
6094
+ if (addons.includes("starlight")) if (isBackendSelf) structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
6095
+ else structure.push("├── docs/ # Documentation site (Astro Starlight)");
6096
+ if (!isBackendSelf && !isBackendNone && !isConvex) {
6031
6097
  const backendName = backend[0].toUpperCase() + backend.slice(1);
6032
6098
  const apiName = api !== "none" ? api.toUpperCase() : "";
6033
6099
  const backendDesc = apiName ? `${backendName}, ${apiName}` : backendName;
6034
6100
  structure.push(`│ └── server/ # Backend API (${backendDesc})`);
6035
6101
  }
6102
+ if (isConvex || !isBackendNone) {
6103
+ structure.push("├── packages/");
6104
+ if (isConvex) {
6105
+ structure.push("│ ├── backend/ # Convex backend functions and schema");
6106
+ if (auth === "clerk") structure.push("│ │ ├── convex/ # Convex functions and schema", "│ │ └── .env.local # Convex environment variables");
6107
+ }
6108
+ if (!isConvex) {
6109
+ structure.push("│ ├── api/ # API layer / business logic");
6110
+ if (auth !== "none") structure.push("│ ├── auth/ # Authentication configuration & logic");
6111
+ if (api !== "none" || auth !== "none") structure.push("│ └── db/ # Database schema & queries");
6112
+ }
6113
+ }
6036
6114
  return structure.join("\n");
6037
6115
  }
6038
6116
  function generateFeaturesList(database, auth, addons, orm, runtime, frontend, backend, api) {
@@ -6090,33 +6168,36 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
6090
6168
  else if (addon === "turborepo") addonsList.push("- **Turborepo** - Optimized monorepo build system");
6091
6169
  return addonsList.join("\n");
6092
6170
  }
6093
- function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, serverDeploy) {
6171
+ function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, serverDeploy, backend) {
6094
6172
  if (database === "none") return "";
6173
+ const isBackendSelf = backend === "self";
6174
+ const envPath = isBackendSelf ? "apps/web/.env" : "apps/server/.env";
6175
+ const dbLocalPath = isBackendSelf ? "apps/web" : "apps/server";
6095
6176
  let setup = "## Database Setup\n\n";
6096
6177
  if (database === "sqlite") setup += `This project uses SQLite${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
6097
6178
 
6098
6179
  1. Start the local SQLite database:
6099
6180
  ${dbSetup === "d1" ? serverDeploy === "alchemy" ? "D1 local development and migrations are handled automatically by Alchemy during dev and deploy." : "Local development for a Cloudflare D1 database will already be running as part of the `wrangler dev` command." : `\`\`\`bash
6100
- cd apps/server && ${packageManagerRunCmd} db:local
6181
+ cd ${dbLocalPath} && ${packageManagerRunCmd} db:local
6101
6182
  \`\`\`
6102
6183
  `}
6103
6184
 
6104
- 2. Update your \`.env\` file in the \`apps/server\` directory with the appropriate connection details if needed.
6185
+ 2. Update your \`.env\` file in the \`${isBackendSelf ? "apps/web" : "apps/server"}\` directory with the appropriate connection details if needed.
6105
6186
  `;
6106
6187
  else if (database === "postgres") setup += `This project uses PostgreSQL${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
6107
6188
 
6108
6189
  1. Make sure you have a PostgreSQL database set up.
6109
- 2. Update your \`apps/server/.env\` file with your PostgreSQL connection details.
6190
+ 2. Update your \`${envPath}\` file with your PostgreSQL connection details.
6110
6191
  `;
6111
6192
  else if (database === "mysql") setup += `This project uses MySQL${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
6112
6193
 
6113
6194
  1. Make sure you have a MySQL database set up.
6114
- 2. Update your \`apps/server/.env\` file with your MySQL connection details.
6195
+ 2. Update your \`${envPath}\` file with your MySQL connection details.
6115
6196
  `;
6116
6197
  else if (database === "mongodb") setup += `This project uses MongoDB ${orm === "mongoose" ? "with Mongoose" : orm === "prisma" ? "with Prisma ORM" : `with ${orm}`}.
6117
6198
 
6118
6199
  1. Make sure you have MongoDB set up.
6119
- 2. Update your \`apps/server/.env\` file with your MongoDB connection URI.
6200
+ 2. Update your \`${envPath}\` file with your MongoDB connection URI.
6120
6201
  `;
6121
6202
  setup += `
6122
6203
  3. ${orm === "prisma" ? `Generate the Prisma client and push the schema:
@@ -6135,13 +6216,14 @@ ${packageManagerRunCmd} db:push
6135
6216
  function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNative, addons, backend) {
6136
6217
  const isConvex = backend === "convex";
6137
6218
  const isBackendNone = backend === "none";
6219
+ const isBackendSelf = backend === "self";
6138
6220
  let scripts = `- \`${packageManagerRunCmd} dev\`: Start all applications in development mode
6139
6221
  - \`${packageManagerRunCmd} build\`: Build all applications`;
6140
- scripts += `
6222
+ if (!isBackendSelf) scripts += `
6141
6223
  - \`${packageManagerRunCmd} dev:web\`: Start only the web application`;
6142
6224
  if (isConvex) scripts += `
6143
6225
  - \`${packageManagerRunCmd} dev:setup\`: Setup and configure your Convex project`;
6144
- else if (!isBackendNone) scripts += `
6226
+ else if (!isBackendNone && !isBackendSelf) scripts += `
6145
6227
  - \`${packageManagerRunCmd} dev:server\`: Start only the server`;
6146
6228
  scripts += `
6147
6229
  - \`${packageManagerRunCmd} check-types\`: Check TypeScript types across all apps`;
@@ -6152,7 +6234,7 @@ function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNati
6152
6234
  - \`${packageManagerRunCmd} db:push\`: Push schema changes to database
6153
6235
  - \`${packageManagerRunCmd} db:studio\`: Open database studio UI`;
6154
6236
  if (database === "sqlite" && orm === "drizzle") scripts += `
6155
- - \`cd apps/server && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
6237
+ - \`cd ${isBackendSelf ? "apps/web" : "apps/server"} && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
6156
6238
  }
6157
6239
  if (addons.includes("biome")) scripts += `
6158
6240
  - \`${packageManagerRunCmd} check\`: Run Biome formatting and linting`;
@@ -6228,15 +6310,14 @@ async function initializeGit(projectDir, useGit) {
6228
6310
  async function setupPayments(config) {
6229
6311
  const { payments, projectDir, frontend } = config;
6230
6312
  if (!payments || payments === "none") return;
6231
- const serverDir = path.join(projectDir, "apps/server");
6232
6313
  const clientDir = path.join(projectDir, "apps/web");
6233
- const serverDirExists = await fs.pathExists(serverDir);
6314
+ const authDir = path.join(projectDir, "packages/auth");
6234
6315
  const clientDirExists = await fs.pathExists(clientDir);
6235
- if (!serverDirExists) return;
6316
+ const authDirExists = await fs.pathExists(authDir);
6236
6317
  if (payments === "polar") {
6237
- await addPackageDependency({
6318
+ if (authDirExists) await addPackageDependency({
6238
6319
  dependencies: ["@polar-sh/better-auth", "@polar-sh/sdk"],
6239
- projectDir: serverDir
6320
+ projectDir: authDir
6240
6321
  });
6241
6322
  if (clientDirExists) {
6242
6323
  if (frontend.some((f) => [
@@ -6310,19 +6391,20 @@ async function getDockerStatus(database) {
6310
6391
  async function displayPostInstallInstructions(config) {
6311
6392
  const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
6312
6393
  const isConvex = backend === "convex";
6394
+ const isBackendSelf = backend === "self";
6313
6395
  const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : "bun run";
6314
6396
  const cdCmd = `cd ${relativePath}`;
6315
6397
  const hasHuskyOrBiome = addons?.includes("husky") || addons?.includes("biome");
6316
- const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy) : "";
6398
+ const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
6317
6399
  const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
6318
6400
  const lintingInstructions = hasHuskyOrBiome ? getLintingInstructions(runCmd) : "";
6319
6401
  const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex) : "";
6320
6402
  const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
6321
6403
  const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
6322
6404
  const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
6323
- const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions() : "";
6324
- const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy);
6325
- const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy);
6405
+ const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions(backend) : "";
6406
+ const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
6407
+ const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
6326
6408
  const hasWeb = frontend?.some((f) => [
6327
6409
  "tanstack-router",
6328
6410
  "react-router",
@@ -6346,7 +6428,8 @@ async function displayPostInstallInstructions(config) {
6346
6428
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup\n${pc.dim(" (this will guide you through Convex project setup)")}\n`;
6347
6429
  output += `${pc.cyan(`${stepCounter++}.`)} Copy environment variables from\n${pc.white(" packages/backend/.env.local")} to ${pc.white("apps/*/.env")}\n`;
6348
6430
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
6349
- } else {
6431
+ } else if (isBackendSelf) output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
6432
+ else {
6350
6433
  if (runtime !== "workers") output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
6351
6434
  if (runtime === "workers") {
6352
6435
  if (dbSetup === "d1") output += `${pc.yellow("IMPORTANT:")} Complete D1 database setup first\n (see Database commands below)\n`;
@@ -6357,11 +6440,11 @@ async function displayPostInstallInstructions(config) {
6357
6440
  output += `${pc.bold("Your project will be available at:")}\n`;
6358
6441
  if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
6359
6442
  else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
6360
- if (!isConvex) {
6443
+ if (!isConvex && !isBackendSelf) {
6361
6444
  output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
6362
- if (api === "orpc") if (backend === "self") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/rpc/api\n`;
6363
- else output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
6445
+ if (api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
6364
6446
  }
6447
+ if (isBackendSelf && api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:${webPort}/rpc/api\n`;
6365
6448
  if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
6366
6449
  if (addons?.includes("fumadocs")) output += `${pc.cyan("•")} Fumadocs: http://localhost:4000\n`;
6367
6450
  if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
@@ -6393,7 +6476,7 @@ function getNativeInstructions(isConvex) {
6393
6476
  function getLintingInstructions(runCmd) {
6394
6477
  return `${pc.bold("Linting and formatting:")}\n${pc.cyan("•")} Format and lint fix: ${`${runCmd} check`}\n`;
6395
6478
  }
6396
- async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy) {
6479
+ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) {
6397
6480
  const instructions = [];
6398
6481
  if (dbSetup === "docker") {
6399
6482
  const dockerStatus = await getDockerStatus(database);
@@ -6407,8 +6490,9 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup,
6407
6490
  const packageManager = runCmd === "npm run" ? "npm" : runCmd || "npm";
6408
6491
  instructions.push(`${pc.cyan("1.")} Login to Cloudflare: ${pc.white(`${packageManager} wrangler login`)}`);
6409
6492
  instructions.push(`${pc.cyan("2.")} Create D1 database: ${pc.white(`${packageManager} wrangler d1 create your-database-name`)}`);
6410
- instructions.push(`${pc.cyan("3.")} Update apps/server/wrangler.jsonc with database_id and database_name`);
6411
- instructions.push(`${pc.cyan("4.")} Generate migrations: ${pc.white(`cd apps/server && ${runCmd} db:generate`)}`);
6493
+ const wranglerPath = backend === "self" ? "apps/web" : "apps/server";
6494
+ instructions.push(`${pc.cyan("3.")} Update ${wranglerPath}/wrangler.jsonc with database_id and database_name`);
6495
+ instructions.push(`${pc.cyan("4.")} Generate migrations: ${pc.white(`cd ${wranglerPath} && ${runCmd} db:generate`)}`);
6412
6496
  instructions.push(`${pc.cyan("5.")} Apply migrations locally: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME --local`)}`);
6413
6497
  instructions.push(`${pc.cyan("6.")} Apply migrations to production: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME`)}`);
6414
6498
  }
@@ -6432,7 +6516,10 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup,
6432
6516
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6433
6517
  if (dbSetup !== "d1") instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
6434
6518
  if (!(dbSetup === "d1" && serverDeploy === "alchemy")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
6435
- if (database === "sqlite" && dbSetup !== "d1") instructions.push(`${pc.cyan("•")} Start local DB (if needed): ${`cd apps/server && ${runCmd} db:local`}`);
6519
+ if (database === "sqlite" && dbSetup !== "d1") {
6520
+ const dbLocalPath = backend === "self" ? "apps/web" : "apps/server";
6521
+ instructions.push(`${pc.cyan("•")} Start local DB (if needed): ${`cd ${dbLocalPath} && ${runCmd} db:local`}`);
6522
+ }
6436
6523
  } else if (orm === "mongoose") {
6437
6524
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6438
6525
  } else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
@@ -6453,23 +6540,28 @@ function getNoOrmWarning() {
6453
6540
  function getBunWebNativeWarning() {
6454
6541
  return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
6455
6542
  }
6456
- function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy) {
6543
+ function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
6457
6544
  const instructions = [];
6458
- if (webDeploy === "wrangler") instructions.push(`${pc.bold("Deploy web to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} run deploy`}`);
6459
- if (serverDeploy === "wrangler") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} run deploy`}`);
6545
+ if (webDeploy === "wrangler") {
6546
+ const deployPath = backend === "self" ? "apps/web" : "apps/web";
6547
+ instructions.push(`${pc.bold("Deploy web to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd ${deployPath} && ${runCmd} run deploy`}`);
6548
+ }
6549
+ if (serverDeploy === "wrangler" && backend !== "self") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} run deploy`}`);
6460
6550
  return instructions.length ? `\n${instructions.join("\n")}` : "";
6461
6551
  }
6462
6552
  function getClerkInstructions() {
6463
6553
  return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Follow the guide: ${pc.underline("https://docs.convex.dev/auth/clerk")}\n${pc.cyan("•")} Set CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard\n${pc.cyan("•")} Set CLERK_PUBLISHABLE_KEY in apps/*/.env`;
6464
6554
  }
6465
- function getPolarInstructions() {
6466
- return `${pc.bold("Polar Payments Setup:")}\n${pc.cyan("•")} Get access token & product ID from ${pc.underline("https://sandbox.polar.sh/")}\n${pc.cyan("•")} Set POLAR_ACCESS_TOKEN in apps/server/.env`;
6555
+ function getPolarInstructions(backend) {
6556
+ const envPath = backend === "self" ? "apps/web/.env" : "apps/server/.env";
6557
+ return `${pc.bold("Polar Payments Setup:")}\n${pc.cyan("•")} Get access token & product ID from ${pc.underline("https://sandbox.polar.sh/")}\n${pc.cyan("•")} Set POLAR_ACCESS_TOKEN in ${envPath}`;
6467
6558
  }
6468
- function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
6559
+ function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
6469
6560
  const instructions = [];
6561
+ const isBackendSelf = backend === "self";
6470
6562
  if (webDeploy === "alchemy" && serverDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy web with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/web && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/web && ${runCmd} destroy`}`);
6471
- else if (serverDeploy === "alchemy" && webDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/server && ${runCmd} destroy`}`);
6472
- else if (webDeploy === "alchemy" && serverDeploy === "alchemy") instructions.push(`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`);
6563
+ else if (serverDeploy === "alchemy" && webDeploy !== "alchemy" && !isBackendSelf) instructions.push(`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/server && ${runCmd} destroy`}`);
6564
+ else if (webDeploy === "alchemy" && (serverDeploy === "alchemy" || isBackendSelf)) instructions.push(`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`);
6473
6565
  return instructions.length ? `\n${instructions.join("\n")}` : "";
6474
6566
  }
6475
6567
 
@@ -6514,20 +6606,31 @@ async function setupWorkspaceDependencies(projectDir, options) {
6514
6606
  },
6515
6607
  projectDir: serverPackageDir
6516
6608
  });
6517
- const needsApiDependency = options.api && options.api !== "none";
6518
6609
  const webPackageDir = path.join(projectDir, "apps/web");
6519
6610
  if (await fs.pathExists(webPackageDir)) {
6520
6611
  const webDeps = {};
6521
- if (options.backend === "self") {
6522
- webDeps[`@${projectName}/api`] = workspaceVersion;
6523
- webDeps[`@${projectName}/auth`] = workspaceVersion;
6524
- webDeps[`@${projectName}/db`] = workspaceVersion;
6525
- } else if (needsApiDependency) webDeps[`@${projectName}/api`] = workspaceVersion;
6612
+ webDeps[`@${projectName}/api`] = workspaceVersion;
6613
+ webDeps[`@${projectName}/auth`] = workspaceVersion;
6614
+ webDeps[`@${projectName}/db`] = workspaceVersion;
6526
6615
  if (Object.keys(webDeps).length > 0) await addPackageDependency({
6527
6616
  customDependencies: webDeps,
6528
6617
  projectDir: webPackageDir
6529
6618
  });
6530
6619
  }
6620
+ const runtimeDevDeps = getRuntimeDevDeps(options);
6621
+ await addPackageDependency({
6622
+ dependencies: commonDeps,
6623
+ devDependencies: [...commonDevDeps, ...runtimeDevDeps],
6624
+ projectDir
6625
+ });
6626
+ }
6627
+ function getRuntimeDevDeps(options) {
6628
+ const { runtime, backend } = options;
6629
+ if (runtime === "none" && backend === "self") return ["@types/node"];
6630
+ if (runtime === "node") return ["@types/node"];
6631
+ if (runtime === "bun") return ["@types/bun"];
6632
+ if (runtime === "workers") return ["@types/node"];
6633
+ return [];
6531
6634
  }
6532
6635
 
6533
6636
  //#endregion
@@ -6536,6 +6639,7 @@ async function updatePackageConfigurations(projectDir, options) {
6536
6639
  await updateRootPackageJson(projectDir, options);
6537
6640
  if (options.backend === "convex") await updateConvexPackageJson(projectDir, options);
6538
6641
  else if (options.backend === "self") {
6642
+ await updateDbPackageJson(projectDir, options);
6539
6643
  await updateAuthPackageJson(projectDir, options);
6540
6644
  await updateApiPackageJson(projectDir, options);
6541
6645
  await setupWorkspaceDependencies(projectDir, options);
@@ -6585,10 +6689,10 @@ async function updateRootPackageJson(projectDir, options) {
6585
6689
  }
6586
6690
  }
6587
6691
  if (options.dbSetup === "docker") {
6588
- scripts["db:start"] = `turbo -F ${backendPackageName} db:start`;
6589
- scripts["db:watch"] = `turbo -F ${backendPackageName} db:watch`;
6590
- scripts["db:stop"] = `turbo -F ${backendPackageName} db:stop`;
6591
- scripts["db:down"] = `turbo -F ${backendPackageName} db:down`;
6692
+ scripts["db:start"] = `turbo -F ${dbPackageName} db:start`;
6693
+ scripts["db:watch"] = `turbo -F ${dbPackageName} db:watch`;
6694
+ scripts["db:stop"] = `turbo -F ${dbPackageName} db:stop`;
6695
+ scripts["db:down"] = `turbo -F ${dbPackageName} db:down`;
6592
6696
  }
6593
6697
  } else if (options.packageManager === "pnpm") {
6594
6698
  scripts.dev = devScript;
@@ -6610,10 +6714,10 @@ async function updateRootPackageJson(projectDir, options) {
6610
6714
  }
6611
6715
  }
6612
6716
  if (options.dbSetup === "docker") {
6613
- scripts["db:start"] = `pnpm --filter ${backendPackageName} db:start`;
6614
- scripts["db:watch"] = `pnpm --filter ${backendPackageName} db:watch`;
6615
- scripts["db:stop"] = `pnpm --filter ${backendPackageName} db:stop`;
6616
- scripts["db:down"] = `pnpm --filter ${backendPackageName} db:down`;
6717
+ scripts["db:start"] = `pnpm --filter ${dbPackageName} db:start`;
6718
+ scripts["db:watch"] = `pnpm --filter ${dbPackageName} db:watch`;
6719
+ scripts["db:stop"] = `pnpm --filter ${dbPackageName} db:stop`;
6720
+ scripts["db:down"] = `pnpm --filter ${dbPackageName} db:down`;
6617
6721
  }
6618
6722
  } else if (options.packageManager === "npm") {
6619
6723
  scripts.dev = devScript;
@@ -6635,10 +6739,10 @@ async function updateRootPackageJson(projectDir, options) {
6635
6739
  }
6636
6740
  }
6637
6741
  if (options.dbSetup === "docker") {
6638
- scripts["db:start"] = `npm run db:start --workspace ${backendPackageName}`;
6639
- scripts["db:watch"] = `npm run db:watch --workspace ${backendPackageName}`;
6640
- scripts["db:stop"] = `npm run db:stop --workspace ${backendPackageName}`;
6641
- scripts["db:down"] = `npm run db:down --workspace ${backendPackageName}`;
6742
+ scripts["db:start"] = `npm run db:start --workspace ${dbPackageName}`;
6743
+ scripts["db:watch"] = `npm run db:watch --workspace ${dbPackageName}`;
6744
+ scripts["db:stop"] = `npm run db:stop --workspace ${dbPackageName}`;
6745
+ scripts["db:down"] = `npm run db:down --workspace ${dbPackageName}`;
6642
6746
  }
6643
6747
  } else if (options.packageManager === "bun") {
6644
6748
  scripts.dev = devScript;
@@ -6660,10 +6764,10 @@ async function updateRootPackageJson(projectDir, options) {
6660
6764
  }
6661
6765
  }
6662
6766
  if (options.dbSetup === "docker") {
6663
- scripts["db:start"] = `bun run --filter ${backendPackageName} db:start`;
6664
- scripts["db:watch"] = `bun run --filter ${backendPackageName} db:watch`;
6665
- scripts["db:stop"] = `bun run --filter ${backendPackageName} db:stop`;
6666
- scripts["db:down"] = `bun run --filter ${backendPackageName} db:down`;
6767
+ scripts["db:start"] = `bun run --filter ${dbPackageName} db:start`;
6768
+ scripts["db:watch"] = `bun run --filter ${dbPackageName} db:watch`;
6769
+ scripts["db:stop"] = `bun run --filter ${dbPackageName} db:stop`;
6770
+ scripts["db:down"] = `bun run --filter ${dbPackageName} db:down`;
6667
6771
  }
6668
6772
  }
6669
6773
  try {
@@ -6688,13 +6792,6 @@ async function updateServerPackageJson(projectDir, options) {
6688
6792
  if (!await fs.pathExists(serverPackageJsonPath)) return;
6689
6793
  const serverPackageJson = await fs.readJson(serverPackageJsonPath);
6690
6794
  if (!serverPackageJson.scripts) serverPackageJson.scripts = {};
6691
- const scripts = serverPackageJson.scripts;
6692
- if (options.dbSetup === "docker") {
6693
- scripts["db:start"] = "docker compose up -d";
6694
- scripts["db:watch"] = "docker compose up";
6695
- scripts["db:stop"] = "docker compose stop";
6696
- scripts["db:down"] = "docker compose down";
6697
- }
6698
6795
  await fs.writeJson(serverPackageJsonPath, serverPackageJson, { spaces: 2 });
6699
6796
  await updateDbPackageJson(projectDir, options);
6700
6797
  }
@@ -6719,6 +6816,12 @@ async function updateDbPackageJson(projectDir, options) {
6719
6816
  if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = "drizzle-kit migrate";
6720
6817
  }
6721
6818
  }
6819
+ if (options.dbSetup === "docker") {
6820
+ scripts["db:start"] = "docker compose up -d";
6821
+ scripts["db:watch"] = "docker compose up";
6822
+ scripts["db:stop"] = "docker compose stop";
6823
+ scripts["db:down"] = "docker compose down";
6824
+ }
6722
6825
  await fs.writeJson(dbPackageJsonPath, dbPackageJson, { spaces: 2 });
6723
6826
  }
6724
6827
  async function updateAuthPackageJson(projectDir, options) {