create-better-t-stack 2.50.1-canary.7ebd503f → 2.50.1-canary.9643d88e

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.
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { createBtsCli } from "./src-DG05Vcnx.js";
2
+ import { createBtsCli } from "./src-B0pvu6Z-.js";
3
3
 
4
4
  //#region src/cli.ts
5
5
  createBtsCli().run();
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { builder, createBtsCli, docs, init, router, sponsors } from "./src-DG05Vcnx.js";
2
+ import { builder, createBtsCli, docs, init, router, sponsors } from "./src-B0pvu6Z-.js";
3
3
 
4
4
  export { builder, createBtsCli, docs, init, router, sponsors };
@@ -347,21 +347,17 @@ function ensureSingleWebAndNative(frontends) {
347
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");
348
348
  if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles");
349
349
  }
350
- const FULLSTACK_FRONTENDS$1 = [
351
- "next",
352
- "nuxt",
353
- "svelte",
354
- "tanstack-start"
355
- ];
350
+ const FULLSTACK_FRONTENDS$1 = ["next"];
356
351
  function validateSelfBackendCompatibility(providedFlags, options, config) {
357
352
  const backend = config.backend || options.backend;
358
353
  const frontends = config.frontend || options.frontend || [];
359
354
  if (backend === "self") {
360
- if (!frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f))) exitWithError("Backend 'self' (fullstack) requires a fullstack-capable frontend. Please use --frontend with one of: next, nuxt, svelte, tanstack-start");
361
- if (frontends.length > 1) exitWithError("Backend 'self' (fullstack) can only be used with a single frontend framework.");
355
+ const { web, native } = splitFrontends(frontends);
356
+ if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) exitWithError("Backend 'self' (fullstack) currently only supports Next.js frontend. Please use --frontend next. Support for Nuxt, SvelteKit, and TanStack Start will be added in a future update.");
357
+ if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles");
362
358
  }
363
359
  const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
364
- if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) is only compatible with fullstack-capable frontends: next, nuxt, svelte, tanstack-start. Please choose a different backend or use a fullstack frontend.");
360
+ if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) currently only supports Next.js frontend. Please use --frontend next or choose a different backend. Support for Nuxt, SvelteKit, and TanStack Start will be added in a future update.");
365
361
  }
366
362
  function validateWorkersCompatibility(providedFlags, options, config) {
367
363
  if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && config.backend !== "hono") exitWithError(`Cloudflare Workers runtime (--runtime workers) is only supported with Hono backend (--backend hono). Current backend: ${config.backend}. Please use '--backend hono' or choose a different runtime.`);
@@ -684,12 +680,7 @@ async function getAuthChoice(auth, hasDatabase, backend, frontend) {
684
680
 
685
681
  //#endregion
686
682
  //#region src/prompts/backend.ts
687
- const FULLSTACK_FRONTENDS = [
688
- "next",
689
- "nuxt",
690
- "svelte",
691
- "tanstack-start"
692
- ];
683
+ const FULLSTACK_FRONTENDS = ["next"];
693
684
  async function getBackendFrameworkChoice(backendFramework, frontends) {
694
685
  if (backendFramework !== void 0) return backendFramework;
695
686
  const hasIncompatibleFrontend = frontends?.some((f) => f === "solid");
@@ -1776,6 +1767,12 @@ function validateBackendNoneConstraints(config, providedFlags) {
1776
1767
  if (has("dbSetup") && config.dbSetup !== "none") exitWithError("Backend 'none' requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.");
1777
1768
  if (has("serverDeploy") && config.serverDeploy !== "none") exitWithError("Backend 'none' requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.");
1778
1769
  }
1770
+ function validateSelfBackendConstraints(config, providedFlags) {
1771
+ const { backend } = config;
1772
+ if (backend !== "self") return;
1773
+ const has = (k) => providedFlags.has(k);
1774
+ if (has("runtime") && config.runtime !== "none") exitWithError("Backend 'self' (fullstack) requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.");
1775
+ }
1779
1776
  function validateBackendConstraints(config, providedFlags, options) {
1780
1777
  const { backend } = config;
1781
1778
  if (config.auth === "clerk" && backend !== "convex") exitWithError("Clerk authentication is only supported with the Convex backend. Please use '--backend convex' or choose a different auth provider.");
@@ -1814,6 +1811,7 @@ function validateFullConfig(config, providedFlags, options) {
1814
1811
  validateDatabaseSetup(config, providedFlags);
1815
1812
  validateConvexConstraints(config, providedFlags);
1816
1813
  validateBackendNoneConstraints(config, providedFlags);
1814
+ validateSelfBackendConstraints(config, providedFlags);
1817
1815
  validateBackendConstraints(config, providedFlags, options);
1818
1816
  validateFrontendConstraints(config, providedFlags);
1819
1817
  validateApiConstraints(config, options);
@@ -3155,25 +3153,29 @@ async function setupDockerComposeTemplates(projectDir, context) {
3155
3153
  if (await fs.pathExists(dockerSrcDir)) await processAndCopyFiles("**/*", dockerSrcDir, dbPackageDir, context);
3156
3154
  }
3157
3155
  async function setupDeploymentTemplates(projectDir, context) {
3158
- if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") if (context.webDeploy === "alchemy" && context.serverDeploy === "alchemy") {
3156
+ const isBackendSelf = context.backend === "self";
3157
+ if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") {
3159
3158
  const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
3160
- if (await fs.pathExists(alchemyTemplateSrc)) {
3161
- await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, projectDir, context);
3162
- const serverAppDir = path.join(projectDir, "apps/server");
3163
- if (await fs.pathExists(serverAppDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3164
- }
3165
- } else {
3166
- if (context.webDeploy === "alchemy") {
3167
- const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
3168
- const webAppDir = path.join(projectDir, "apps/web");
3169
- if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(webAppDir)) await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, webAppDir, context);
3170
- }
3171
- if (context.serverDeploy === "alchemy") {
3172
- const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
3173
- const serverAppDir = path.join(projectDir, "apps/server");
3174
- if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(serverAppDir)) {
3175
- await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3176
- await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3159
+ if (context.webDeploy === "alchemy" && (context.serverDeploy === "alchemy" || isBackendSelf)) {
3160
+ if (await fs.pathExists(alchemyTemplateSrc)) {
3161
+ await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, projectDir, context);
3162
+ await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3163
+ }
3164
+ } else {
3165
+ if (context.webDeploy === "alchemy") {
3166
+ const webAppDir = path.join(projectDir, "apps/web");
3167
+ if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(webAppDir)) {
3168
+ await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, webAppDir, context);
3169
+ await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3170
+ }
3171
+ }
3172
+ if (context.serverDeploy === "alchemy" && !isBackendSelf) {
3173
+ const serverAppDir = path.join(projectDir, "apps/server");
3174
+ if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(serverAppDir)) {
3175
+ await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3176
+ await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3177
+ await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3178
+ }
3177
3179
  }
3178
3180
  }
3179
3181
  }
@@ -3196,7 +3198,7 @@ async function setupDeploymentTemplates(projectDir, context) {
3196
3198
  }
3197
3199
  }
3198
3200
  }
3199
- if (context.serverDeploy !== "none" && context.serverDeploy !== "alchemy") {
3201
+ if (context.serverDeploy !== "none" && context.serverDeploy !== "alchemy" && !isBackendSelf) {
3200
3202
  const serverAppDir = path.join(projectDir, "apps/server");
3201
3203
  if (await fs.pathExists(serverAppDir)) {
3202
3204
  const deployTemplateSrc = path.join(PKG_ROOT, `templates/deploy/${context.serverDeploy}/server`);
@@ -3204,6 +3206,18 @@ async function setupDeploymentTemplates(projectDir, context) {
3204
3206
  }
3205
3207
  }
3206
3208
  }
3209
+ async function addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc) {
3210
+ for (const packageName of [
3211
+ "packages/api",
3212
+ "packages/auth",
3213
+ "packages/db"
3214
+ ]) {
3215
+ const packageDir = path.join(projectDir, packageName);
3216
+ if (await fs.pathExists(packageDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, packageDir, context);
3217
+ }
3218
+ const serverAppDir = path.join(projectDir, "apps/server");
3219
+ if (await fs.pathExists(serverAppDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3220
+ }
3207
3221
 
3208
3222
  //#endregion
3209
3223
  //#region src/helpers/core/add-addons.ts
@@ -3269,7 +3283,7 @@ async function setupServerDeploy(config) {
3269
3283
  serverDir,
3270
3284
  packageManager
3271
3285
  });
3272
- } else if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, packageManager);
3286
+ } else if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, packageManager, projectDir);
3273
3287
  }
3274
3288
  async function setupWorkersServerDeploy(serverDir, _packageManager) {
3275
3289
  const packageJsonPath = path.join(serverDir, "package.json");
@@ -3306,7 +3320,7 @@ async function generateCloudflareWorkerTypes({ serverDir, packageManager }) {
3306
3320
  log.warn(`Note: You can manually run 'cd apps/server && ${managerCmd} cf-typegen' in the project directory later`);
3307
3321
  }
3308
3322
  }
3309
- async function setupAlchemyServerDeploy(serverDir, _packageManager) {
3323
+ async function setupAlchemyServerDeploy(serverDir, _packageManager, projectDir) {
3310
3324
  if (!await fs.pathExists(serverDir)) return;
3311
3325
  await addPackageDependency({
3312
3326
  devDependencies: [
@@ -3317,6 +3331,7 @@ async function setupAlchemyServerDeploy(serverDir, _packageManager) {
3317
3331
  ],
3318
3332
  projectDir: serverDir
3319
3333
  });
3334
+ if (projectDir) await addAlchemyPackagesDependencies$1(projectDir);
3320
3335
  const packageJsonPath = path.join(serverDir, "package.json");
3321
3336
  if (await fs.pathExists(packageJsonPath)) {
3322
3337
  const packageJson = await fs.readJson(packageJsonPath);
@@ -3329,6 +3344,19 @@ async function setupAlchemyServerDeploy(serverDir, _packageManager) {
3329
3344
  await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
3330
3345
  }
3331
3346
  }
3347
+ async function addAlchemyPackagesDependencies$1(projectDir) {
3348
+ for (const packageName of [
3349
+ "packages/api",
3350
+ "packages/auth",
3351
+ "packages/db"
3352
+ ]) {
3353
+ const packageDir = path.join(projectDir, packageName);
3354
+ if (await fs.pathExists(packageDir)) await addPackageDependency({
3355
+ devDependencies: ["@cloudflare/workers-types"],
3356
+ projectDir: packageDir
3357
+ });
3358
+ }
3359
+ }
3332
3360
 
3333
3361
  //#endregion
3334
3362
  //#region src/helpers/deployment/alchemy/alchemy-next-setup.ts
@@ -3634,7 +3662,7 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
3634
3662
  await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
3635
3663
  }
3636
3664
  const serverDir = path.join(projectDir, "apps/server");
3637
- if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, packageManager);
3665
+ if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, packageManager, projectDir);
3638
3666
  const frontend = config.frontend;
3639
3667
  const isNext = frontend.includes("next");
3640
3668
  const isNuxt = frontend.includes("nuxt");
@@ -3867,6 +3895,7 @@ async function setupWebDeploy(config) {
3867
3895
  if (webDeploy !== "wrangler" && webDeploy !== "alchemy") return;
3868
3896
  if (webDeploy === "alchemy" && serverDeploy === "alchemy") {
3869
3897
  await setupCombinedAlchemyDeploy(projectDir, packageManager, config);
3898
+ await addAlchemyPackagesDependencies(projectDir);
3870
3899
  return;
3871
3900
  }
3872
3901
  const isNext = frontend.includes("next");
@@ -3890,6 +3919,7 @@ async function setupWebDeploy(config) {
3890
3919
  else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager);
3891
3920
  else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager);
3892
3921
  else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager);
3922
+ await addAlchemyPackagesDependencies(projectDir);
3893
3923
  }
3894
3924
  }
3895
3925
  async function setupWorkersWebDeploy(projectDir, pkgManager) {
@@ -3907,6 +3937,19 @@ async function setupWorkersWebDeploy(projectDir, pkgManager) {
3907
3937
  }
3908
3938
  await setupWorkersVitePlugin(projectDir);
3909
3939
  }
3940
+ async function addAlchemyPackagesDependencies(projectDir) {
3941
+ for (const packageName of [
3942
+ "packages/api",
3943
+ "packages/auth",
3944
+ "packages/db"
3945
+ ]) {
3946
+ const packageDir = path.join(projectDir, packageName);
3947
+ if (await fs.pathExists(packageDir)) await addPackageDependency({
3948
+ devDependencies: ["@cloudflare/workers-types"],
3949
+ projectDir: packageDir
3950
+ });
3951
+ }
3952
+ }
3910
3953
 
3911
3954
  //#endregion
3912
3955
  //#region src/helpers/core/add-deployment.ts
@@ -3966,6 +4009,7 @@ async function setupCatalogs(projectDir, options) {
3966
4009
  const packagePaths = [
3967
4010
  "apps/server",
3968
4011
  "apps/web",
4012
+ "apps/native",
3969
4013
  "packages/api",
3970
4014
  "packages/db",
3971
4015
  "packages/auth",
@@ -4232,7 +4276,7 @@ function getConvexDependencies(frontend) {
4232
4276
  return deps;
4233
4277
  }
4234
4278
  async function setupApi(config) {
4235
- const { api, projectName, frontend, backend, packageManager, projectDir, auth } = config;
4279
+ const { api, projectName, frontend, backend, packageManager, projectDir } = config;
4236
4280
  const isConvex = backend === "convex";
4237
4281
  const webDir = path.join(projectDir, "apps/web");
4238
4282
  const nativeDir = path.join(projectDir, "apps/native");
@@ -4253,40 +4297,14 @@ async function setupApi(config) {
4253
4297
  dependencies: apiDeps.server.dependencies,
4254
4298
  projectDir: webDir
4255
4299
  });
4256
- const frameworkDeps = [];
4257
- if (backend === "hono") frameworkDeps.push("hono");
4258
- else if (backend === "elysia") frameworkDeps.push("elysia");
4259
- else if (backend === "express") frameworkDeps.push("express", "@types/express");
4260
- else if (backend === "fastify") frameworkDeps.push("fastify");
4261
- else if (backend === "self") {
4300
+ if (backend === "self") {
4301
+ const frameworkDeps = [];
4262
4302
  if (frontend.includes("next")) frameworkDeps.push("next");
4263
- }
4264
- if (frameworkDeps.length > 0) await addPackageDependency({
4265
- dependencies: frameworkDeps,
4266
- projectDir: apiPackageDir
4267
- });
4268
- if (api === "trpc") {
4269
- if (backend === "hono") await addPackageDependency({
4270
- dependencies: ["@hono/trpc-server"],
4271
- projectDir: apiPackageDir
4272
- });
4273
- else if (backend === "elysia") await addPackageDependency({
4274
- dependencies: ["@elysiajs/trpc"],
4275
- projectDir: apiPackageDir
4276
- });
4277
- else if (backend === "express") await addPackageDependency({
4278
- dependencies: ["@trpc/server"],
4279
- projectDir: apiPackageDir
4280
- });
4281
- else if (backend === "fastify") await addPackageDependency({
4282
- dependencies: ["@trpc/server"],
4303
+ if (frameworkDeps.length > 0) await addPackageDependency({
4304
+ dependencies: frameworkDeps,
4283
4305
  projectDir: apiPackageDir
4284
4306
  });
4285
4307
  }
4286
- if (auth === "better-auth") await addPackageDependency({
4287
- dependencies: ["better-auth"],
4288
- projectDir: apiPackageDir
4289
- });
4290
4308
  }
4291
4309
  if (webDirExists && apiDeps.web) await addPackageDependency({
4292
4310
  dependencies: apiDeps.web.dependencies,
@@ -4338,34 +4356,23 @@ async function setupBackendDependencies(config) {
4338
4356
  const devDependencies = [];
4339
4357
  if (framework === "hono") {
4340
4358
  dependencies.push("hono");
4341
- if (api === "trpc") dependencies.push("@hono/trpc-server");
4342
- if (runtime === "node") {
4343
- dependencies.push("@hono/node-server");
4344
- devDependencies.push("tsx", "@types/node");
4345
- }
4359
+ if (runtime === "node") dependencies.push("@hono/node-server");
4346
4360
  } else if (framework === "elysia") {
4347
4361
  dependencies.push("elysia", "@elysiajs/cors");
4348
- if (api === "trpc") dependencies.push("@elysiajs/trpc");
4349
- if (runtime === "node") {
4350
- dependencies.push("@elysiajs/node");
4351
- devDependencies.push("tsx", "@types/node");
4352
- }
4362
+ if (runtime === "node") dependencies.push("@elysiajs/node");
4353
4363
  } else if (framework === "express") {
4354
4364
  dependencies.push("express", "cors");
4355
4365
  devDependencies.push("@types/express", "@types/cors");
4356
- if (runtime === "node") devDependencies.push("tsx", "@types/node");
4357
- } else if (framework === "fastify") {
4358
- dependencies.push("fastify", "@fastify/cors");
4359
- if (runtime === "node") devDependencies.push("tsx", "@types/node");
4360
- }
4366
+ } else if (framework === "fastify") dependencies.push("fastify", "@fastify/cors");
4361
4367
  if (api === "trpc") {
4362
- if (framework === "express") dependencies.push("@trpc/server");
4363
- else if (framework === "fastify") dependencies.push("@trpc/server");
4364
- else if (runtime === "workers") dependencies.push("@trpc/server");
4368
+ dependencies.push("@trpc/server");
4369
+ if (framework === "hono") dependencies.push("@hono/trpc-server");
4370
+ else if (framework === "elysia") dependencies.push("@elysiajs/trpc");
4365
4371
  } else if (api === "orpc") dependencies.push("@orpc/server", "@orpc/openapi", "@orpc/zod");
4366
4372
  if (auth === "better-auth") dependencies.push("better-auth");
4367
4373
  if (examples.includes("ai")) dependencies.push("ai", "@ai-sdk/google");
4368
- if (runtime === "bun") devDependencies.push("@types/bun");
4374
+ if (runtime === "node") devDependencies.push("tsx", "@types/node");
4375
+ else if (runtime === "bun") devDependencies.push("@types/bun");
4369
4376
  if (dependencies.length > 0 || devDependencies.length > 0) await addPackageDependency({
4370
4377
  dependencies,
4371
4378
  devDependencies,
@@ -4626,7 +4633,7 @@ async function setupEnvironmentVariables(config) {
4626
4633
  const nativeDir = path.join(projectDir, "apps/native");
4627
4634
  if (await fs.pathExists(nativeDir)) {
4628
4635
  let envVarName = "EXPO_PUBLIC_SERVER_URL";
4629
- let serverUrl = "http://localhost:3000";
4636
+ let serverUrl = backend === "self" ? "http://localhost:3001" : "http://localhost:3000";
4630
4637
  if (backend === "convex") {
4631
4638
  envVarName = "EXPO_PUBLIC_CONVEX_URL";
4632
4639
  serverUrl = "https://<YOUR_CONVEX_URL>";
@@ -5995,7 +6002,7 @@ This project uses Convex as a backend. You'll need to set up Convex before runni
5995
6002
  ${packageManagerRunCmd} dev:setup
5996
6003
  \`\`\`
5997
6004
 
5998
- 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)}
6005
+ 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)}
5999
6006
 
6000
6007
  Then, run the development server:
6001
6008
 
@@ -6048,6 +6055,7 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
6048
6055
  const instructions = [];
6049
6056
  const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
6050
6057
  const isBackendNone = backend === "none";
6058
+ const isBackendSelf = backend === "self";
6051
6059
  if (!hasFrontendNone) {
6052
6060
  const hasTanstackRouter = frontend.includes("tanstack-router");
6053
6061
  const hasReactRouter = frontend.includes("react-router");
@@ -6056,17 +6064,19 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
6056
6064
  const hasSvelte = frontend.includes("svelte");
6057
6065
  const hasNuxt = frontend.includes("nuxt");
6058
6066
  const hasSolid = frontend.includes("solid");
6059
- 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.`);
6067
+ 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.`);
6068
+ else instructions.push(`Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see the web application.`);
6060
6069
  }
6061
6070
  if (hasNative) instructions.push("Use the Expo Go app to run the mobile application.");
6062
6071
  if (isConvex) instructions.push("Your app will connect to the Convex cloud backend automatically.");
6063
- else if (!isBackendNone) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
6072
+ else if (!isBackendNone && !isBackendSelf) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
6064
6073
  return instructions.join("\n");
6065
6074
  }
6066
6075
  function generateProjectStructure(projectName, frontend, backend, addons, isConvex, api, auth) {
6067
6076
  const structure = [`${projectName}/`, "├── apps/"];
6068
6077
  const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
6069
6078
  const isBackendNone = backend === "none";
6079
+ const isBackendSelf = backend === "self";
6070
6080
  if (!hasFrontendNone) {
6071
6081
  const hasTanstackRouter = frontend.includes("tanstack-router");
6072
6082
  const hasReactRouter = frontend.includes("react-router");
@@ -6084,21 +6094,32 @@ function generateProjectStructure(projectName, frontend, backend, addons, isConv
6084
6094
  else if (hasSvelte) frontendType = "SvelteKit";
6085
6095
  else if (hasNuxt) frontendType = "Nuxt";
6086
6096
  else if (hasSolid) frontendType = "SolidJS";
6087
- structure.push(`│ ├── web/ # Frontend application (${frontendType})`);
6097
+ if (isBackendSelf) structure.push(`│ └── web/ # Fullstack application (${frontendType})`);
6098
+ else structure.push(`│ ├── web/ # Frontend application (${frontendType})`);
6088
6099
  }
6089
6100
  }
6090
- if (frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
6091
- if (addons.includes("starlight")) structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
6092
- if (isConvex) {
6093
- structure.push("├── packages/");
6094
- structure.push("│ └── backend/ # Convex backend functions and schema");
6095
- if (auth === "clerk") structure.push("│ ├── convex/ # Convex functions and schema", "│ └── .env.local # Convex environment variables");
6096
- } else if (!isBackendNone) {
6101
+ if (frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) if (isBackendSelf) structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
6102
+ else structure.push("│ ├── native/ # Mobile application (React Native, Expo)");
6103
+ if (addons.includes("starlight")) if (isBackendSelf) structure.push("│ ├── docs/ # Documentation site (Astro Starlight)");
6104
+ else structure.push("├── docs/ # Documentation site (Astro Starlight)");
6105
+ if (!isBackendSelf && !isBackendNone && !isConvex) {
6097
6106
  const backendName = backend[0].toUpperCase() + backend.slice(1);
6098
6107
  const apiName = api !== "none" ? api.toUpperCase() : "";
6099
6108
  const backendDesc = apiName ? `${backendName}, ${apiName}` : backendName;
6100
6109
  structure.push(`│ └── server/ # Backend API (${backendDesc})`);
6101
6110
  }
6111
+ if (isConvex || !isBackendNone) {
6112
+ structure.push("├── packages/");
6113
+ if (isConvex) {
6114
+ structure.push("│ ├── backend/ # Convex backend functions and schema");
6115
+ if (auth === "clerk") structure.push("│ │ ├── convex/ # Convex functions and schema", "│ │ └── .env.local # Convex environment variables");
6116
+ }
6117
+ if (!isConvex) {
6118
+ structure.push("│ ├── api/ # API layer / business logic");
6119
+ if (auth !== "none") structure.push("│ ├── auth/ # Authentication configuration & logic");
6120
+ if (api !== "none" || auth !== "none") structure.push("│ └── db/ # Database schema & queries");
6121
+ }
6122
+ }
6102
6123
  return structure.join("\n");
6103
6124
  }
6104
6125
  function generateFeaturesList(database, auth, addons, orm, runtime, frontend, backend, api) {
@@ -6134,7 +6155,6 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
6134
6155
  else if (backend === "express") addonsList.push("- **Express** - Fast, unopinionated web framework");
6135
6156
  else if (backend === "fastify") addonsList.push("- **Fastify** - Fast, low-overhead web framework");
6136
6157
  else if (backend === "elysia") addonsList.push("- **Elysia** - Type-safe, high-performance framework");
6137
- else if (backend === "next") addonsList.push("- **Next.js** - Full-stack React framework");
6138
6158
  if (api === "trpc") addonsList.push("- **tRPC** - End-to-end type-safe APIs");
6139
6159
  else if (api === "orpc") addonsList.push("- **oRPC** - End-to-end type-safe APIs with OpenAPI integration");
6140
6160
  if (runtime !== "none") addonsList.push(`- **${runtime === "bun" ? "Bun" : runtime === "node" ? "Node.js" : runtime}** - Runtime environment`);
@@ -6156,33 +6176,36 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
6156
6176
  else if (addon === "turborepo") addonsList.push("- **Turborepo** - Optimized monorepo build system");
6157
6177
  return addonsList.join("\n");
6158
6178
  }
6159
- function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, serverDeploy) {
6179
+ function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, serverDeploy, backend) {
6160
6180
  if (database === "none") return "";
6181
+ const isBackendSelf = backend === "self";
6182
+ const envPath = isBackendSelf ? "apps/web/.env" : "apps/server/.env";
6183
+ const dbLocalPath = isBackendSelf ? "apps/web" : "apps/server";
6161
6184
  let setup = "## Database Setup\n\n";
6162
6185
  if (database === "sqlite") setup += `This project uses SQLite${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
6163
6186
 
6164
6187
  1. Start the local SQLite database:
6165
6188
  ${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
6166
- cd apps/server && ${packageManagerRunCmd} db:local
6189
+ cd ${dbLocalPath} && ${packageManagerRunCmd} db:local
6167
6190
  \`\`\`
6168
6191
  `}
6169
6192
 
6170
- 2. Update your \`.env\` file in the \`apps/server\` directory with the appropriate connection details if needed.
6193
+ 2. Update your \`.env\` file in the \`${isBackendSelf ? "apps/web" : "apps/server"}\` directory with the appropriate connection details if needed.
6171
6194
  `;
6172
6195
  else if (database === "postgres") setup += `This project uses PostgreSQL${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
6173
6196
 
6174
6197
  1. Make sure you have a PostgreSQL database set up.
6175
- 2. Update your \`apps/server/.env\` file with your PostgreSQL connection details.
6198
+ 2. Update your \`${envPath}\` file with your PostgreSQL connection details.
6176
6199
  `;
6177
6200
  else if (database === "mysql") setup += `This project uses MySQL${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
6178
6201
 
6179
6202
  1. Make sure you have a MySQL database set up.
6180
- 2. Update your \`apps/server/.env\` file with your MySQL connection details.
6203
+ 2. Update your \`${envPath}\` file with your MySQL connection details.
6181
6204
  `;
6182
6205
  else if (database === "mongodb") setup += `This project uses MongoDB ${orm === "mongoose" ? "with Mongoose" : orm === "prisma" ? "with Prisma ORM" : `with ${orm}`}.
6183
6206
 
6184
6207
  1. Make sure you have MongoDB set up.
6185
- 2. Update your \`apps/server/.env\` file with your MongoDB connection URI.
6208
+ 2. Update your \`${envPath}\` file with your MongoDB connection URI.
6186
6209
  `;
6187
6210
  setup += `
6188
6211
  3. ${orm === "prisma" ? `Generate the Prisma client and push the schema:
@@ -6201,13 +6224,14 @@ ${packageManagerRunCmd} db:push
6201
6224
  function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNative, addons, backend) {
6202
6225
  const isConvex = backend === "convex";
6203
6226
  const isBackendNone = backend === "none";
6227
+ const isBackendSelf = backend === "self";
6204
6228
  let scripts = `- \`${packageManagerRunCmd} dev\`: Start all applications in development mode
6205
6229
  - \`${packageManagerRunCmd} build\`: Build all applications`;
6206
- scripts += `
6230
+ if (!isBackendSelf) scripts += `
6207
6231
  - \`${packageManagerRunCmd} dev:web\`: Start only the web application`;
6208
6232
  if (isConvex) scripts += `
6209
6233
  - \`${packageManagerRunCmd} dev:setup\`: Setup and configure your Convex project`;
6210
- else if (!isBackendNone) scripts += `
6234
+ else if (!isBackendNone && !isBackendSelf) scripts += `
6211
6235
  - \`${packageManagerRunCmd} dev:server\`: Start only the server`;
6212
6236
  scripts += `
6213
6237
  - \`${packageManagerRunCmd} check-types\`: Check TypeScript types across all apps`;
@@ -6218,7 +6242,7 @@ function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNati
6218
6242
  - \`${packageManagerRunCmd} db:push\`: Push schema changes to database
6219
6243
  - \`${packageManagerRunCmd} db:studio\`: Open database studio UI`;
6220
6244
  if (database === "sqlite" && orm === "drizzle") scripts += `
6221
- - \`cd apps/server && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
6245
+ - \`cd ${isBackendSelf ? "apps/web" : "apps/server"} && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
6222
6246
  }
6223
6247
  if (addons.includes("biome")) scripts += `
6224
6248
  - \`${packageManagerRunCmd} check\`: Run Biome formatting and linting`;
@@ -6375,19 +6399,20 @@ async function getDockerStatus(database) {
6375
6399
  async function displayPostInstallInstructions(config) {
6376
6400
  const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
6377
6401
  const isConvex = backend === "convex";
6402
+ const isBackendSelf = backend === "self";
6378
6403
  const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : "bun run";
6379
6404
  const cdCmd = `cd ${relativePath}`;
6380
6405
  const hasHuskyOrBiome = addons?.includes("husky") || addons?.includes("biome");
6381
- const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy) : "";
6406
+ const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
6382
6407
  const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
6383
6408
  const lintingInstructions = hasHuskyOrBiome ? getLintingInstructions(runCmd) : "";
6384
- const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex) : "";
6409
+ const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex, isBackendSelf) : "";
6385
6410
  const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
6386
6411
  const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
6387
6412
  const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
6388
- const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions() : "";
6389
- const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy);
6390
- const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy);
6413
+ const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions(backend) : "";
6414
+ const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
6415
+ const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
6391
6416
  const hasWeb = frontend?.some((f) => [
6392
6417
  "tanstack-router",
6393
6418
  "react-router",
@@ -6411,7 +6436,8 @@ async function displayPostInstallInstructions(config) {
6411
6436
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup\n${pc.dim(" (this will guide you through Convex project setup)")}\n`;
6412
6437
  output += `${pc.cyan(`${stepCounter++}.`)} Copy environment variables from\n${pc.white(" packages/backend/.env.local")} to ${pc.white("apps/*/.env")}\n`;
6413
6438
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
6414
- } else {
6439
+ } else if (isBackendSelf) output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
6440
+ else {
6415
6441
  if (runtime !== "workers") output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
6416
6442
  if (runtime === "workers") {
6417
6443
  if (dbSetup === "d1") output += `${pc.yellow("IMPORTANT:")} Complete D1 database setup first\n (see Database commands below)\n`;
@@ -6422,11 +6448,11 @@ async function displayPostInstallInstructions(config) {
6422
6448
  output += `${pc.bold("Your project will be available at:")}\n`;
6423
6449
  if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
6424
6450
  else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
6425
- if (!isConvex) {
6451
+ if (!isConvex && !isBackendSelf) {
6426
6452
  output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
6427
- if (api === "orpc") if (backend === "self") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/rpc/api\n`;
6428
- else output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
6453
+ if (api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
6429
6454
  }
6455
+ if (isBackendSelf && api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:${webPort}/rpc/api\n`;
6430
6456
  if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
6431
6457
  if (addons?.includes("fumadocs")) output += `${pc.cyan("•")} Fumadocs: http://localhost:4000\n`;
6432
6458
  if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
@@ -6446,9 +6472,9 @@ async function displayPostInstallInstructions(config) {
6446
6472
  output += pc.cyan("https://github.com/AmanVarshney01/create-better-t-stack");
6447
6473
  consola$1.box(output);
6448
6474
  }
6449
- function getNativeInstructions(isConvex) {
6475
+ function getNativeInstructions(isConvex, isBackendSelf) {
6450
6476
  const envVar = isConvex ? "EXPO_PUBLIC_CONVEX_URL" : "EXPO_PUBLIC_SERVER_URL";
6451
- const exampleUrl = isConvex ? "https://<YOUR_CONVEX_URL>" : "http://<YOUR_LOCAL_IP>:3000";
6477
+ const exampleUrl = isConvex ? "https://<YOUR_CONVEX_URL>" : isBackendSelf ? "http://<YOUR_LOCAL_IP>:3001" : "http://<YOUR_LOCAL_IP>:3000";
6452
6478
  const envFileName = ".env";
6453
6479
  const ipNote = isConvex ? "your Convex deployment URL (find after running 'dev:setup')" : "your local IP address";
6454
6480
  let instructions = `${pc.yellow("NOTE:")} For Expo connectivity issues, update\n apps/native/${envFileName} with ${ipNote}:\n ${`${envVar}=${exampleUrl}`}\n`;
@@ -6458,7 +6484,7 @@ function getNativeInstructions(isConvex) {
6458
6484
  function getLintingInstructions(runCmd) {
6459
6485
  return `${pc.bold("Linting and formatting:")}\n${pc.cyan("•")} Format and lint fix: ${`${runCmd} check`}\n`;
6460
6486
  }
6461
- async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy) {
6487
+ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) {
6462
6488
  const instructions = [];
6463
6489
  if (dbSetup === "docker") {
6464
6490
  const dockerStatus = await getDockerStatus(database);
@@ -6472,8 +6498,9 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup,
6472
6498
  const packageManager = runCmd === "npm run" ? "npm" : runCmd || "npm";
6473
6499
  instructions.push(`${pc.cyan("1.")} Login to Cloudflare: ${pc.white(`${packageManager} wrangler login`)}`);
6474
6500
  instructions.push(`${pc.cyan("2.")} Create D1 database: ${pc.white(`${packageManager} wrangler d1 create your-database-name`)}`);
6475
- instructions.push(`${pc.cyan("3.")} Update apps/server/wrangler.jsonc with database_id and database_name`);
6476
- instructions.push(`${pc.cyan("4.")} Generate migrations: ${pc.white(`cd apps/server && ${runCmd} db:generate`)}`);
6501
+ const wranglerPath = backend === "self" ? "apps/web" : "apps/server";
6502
+ instructions.push(`${pc.cyan("3.")} Update ${wranglerPath}/wrangler.jsonc with database_id and database_name`);
6503
+ instructions.push(`${pc.cyan("4.")} Generate migrations: ${pc.white(`cd ${wranglerPath} && ${runCmd} db:generate`)}`);
6477
6504
  instructions.push(`${pc.cyan("5.")} Apply migrations locally: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME --local`)}`);
6478
6505
  instructions.push(`${pc.cyan("6.")} Apply migrations to production: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME`)}`);
6479
6506
  }
@@ -6497,7 +6524,10 @@ async function getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup,
6497
6524
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6498
6525
  if (dbSetup !== "d1") instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
6499
6526
  if (!(dbSetup === "d1" && serverDeploy === "alchemy")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
6500
- if (database === "sqlite" && dbSetup !== "d1") instructions.push(`${pc.cyan("•")} Start local DB (if needed): ${`cd apps/server && ${runCmd} db:local`}`);
6527
+ if (database === "sqlite" && dbSetup !== "d1") {
6528
+ const dbLocalPath = backend === "self" ? "apps/web" : "apps/server";
6529
+ instructions.push(`${pc.cyan("•")} Start local DB (if needed): ${`cd ${dbLocalPath} && ${runCmd} db:local`}`);
6530
+ }
6501
6531
  } else if (orm === "mongoose") {
6502
6532
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6503
6533
  } else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
@@ -6518,23 +6548,28 @@ function getNoOrmWarning() {
6518
6548
  function getBunWebNativeWarning() {
6519
6549
  return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
6520
6550
  }
6521
- function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy) {
6551
+ function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
6522
6552
  const instructions = [];
6523
- if (webDeploy === "wrangler") instructions.push(`${pc.bold("Deploy web to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} run deploy`}`);
6524
- if (serverDeploy === "wrangler") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} run deploy`}`);
6553
+ if (webDeploy === "wrangler") {
6554
+ const deployPath = backend === "self" ? "apps/web" : "apps/web";
6555
+ instructions.push(`${pc.bold("Deploy web to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd ${deployPath} && ${runCmd} run deploy`}`);
6556
+ }
6557
+ if (serverDeploy === "wrangler" && backend !== "self") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} run deploy`}`);
6525
6558
  return instructions.length ? `\n${instructions.join("\n")}` : "";
6526
6559
  }
6527
6560
  function getClerkInstructions() {
6528
6561
  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`;
6529
6562
  }
6530
- function getPolarInstructions() {
6531
- 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`;
6563
+ function getPolarInstructions(backend) {
6564
+ const envPath = backend === "self" ? "apps/web/.env" : "apps/server/.env";
6565
+ 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}`;
6532
6566
  }
6533
- function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
6567
+ function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
6534
6568
  const instructions = [];
6569
+ const isBackendSelf = backend === "self";
6535
6570
  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`}`);
6536
- 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`}`);
6537
- 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`}`);
6571
+ 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`}`);
6572
+ 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`}`);
6538
6573
  return instructions.length ? `\n${instructions.join("\n")}` : "";
6539
6574
  }
6540
6575
 
@@ -6590,12 +6625,30 @@ async function setupWorkspaceDependencies(projectDir, options) {
6590
6625
  projectDir: webPackageDir
6591
6626
  });
6592
6627
  }
6628
+ const nativePackageDir = path.join(projectDir, "apps/native");
6629
+ if (await fs.pathExists(nativePackageDir)) {
6630
+ const nativeDeps = {};
6631
+ nativeDeps[`@${projectName}/api`] = workspaceVersion;
6632
+ if (Object.keys(nativeDeps).length > 0) await addPackageDependency({
6633
+ customDependencies: nativeDeps,
6634
+ projectDir: nativePackageDir
6635
+ });
6636
+ }
6637
+ const runtimeDevDeps = getRuntimeDevDeps(options);
6593
6638
  await addPackageDependency({
6594
6639
  dependencies: commonDeps,
6595
- devDependencies: commonDevDeps,
6640
+ devDependencies: [...commonDevDeps, ...runtimeDevDeps],
6596
6641
  projectDir
6597
6642
  });
6598
6643
  }
6644
+ function getRuntimeDevDeps(options) {
6645
+ const { runtime, backend } = options;
6646
+ if (runtime === "none" && backend === "self") return ["@types/node"];
6647
+ if (runtime === "node") return ["@types/node"];
6648
+ if (runtime === "bun") return ["@types/bun"];
6649
+ if (runtime === "workers") return ["@types/node"];
6650
+ return [];
6651
+ }
6599
6652
 
6600
6653
  //#endregion
6601
6654
  //#region src/helpers/core/project-config.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "2.50.1-canary.7ebd503f",
3
+ "version": "2.50.1-canary.9643d88e",
4
4
  "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -8,14 +8,14 @@ This is a monorepo with the following structure:
8
8
 
9
9
  {{#if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "tanstack-start")
10
10
  (includes frontend "next") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid"))}}
11
- - **`apps/web/`** - Frontend application{{#if (includes frontend "tanstack-router")}} (React with TanStack Router){{else
11
+ - **`apps/web/`** - {{#if (eq backend "self")}}Fullstack application{{else}}Frontend application{{/if}}{{#if (includes frontend "tanstack-router")}} (React with TanStack Router){{else
12
12
  if (includes frontend "react-router")}} (React with React Router){{else if (includes frontend "next")}} (Next.js){{else
13
13
  if (includes frontend "nuxt")}} (Nuxt.js){{else if (includes frontend "svelte")}} (SvelteKit){{else if (includes
14
14
  frontend "solid")}} (SolidStart){{/if}}
15
15
  {{/if}}
16
16
 
17
17
  {{#if (ne backend "convex")}}
18
- {{#if (ne backend "none")}}
18
+ {{#if (and (ne backend "none") (ne backend "self"))}}
19
19
  - **`apps/server/`** - Backend server{{#if (eq backend "hono")}} (Hono){{else if (eq backend "express")}}
20
20
  (Express){{else if (eq backend "fastify")}} (Fastify){{else if (eq backend "elysia")}} (Elysia){{else if (eq backend
21
21
  "next")}} (Next.js API){{/if}}
@@ -24,6 +24,18 @@ frontend "solid")}} (SolidStart){{/if}}
24
24
  - **`packages/backend/`** - Convex backend functions
25
25
  {{/if}}
26
26
 
27
+ {{#if (or (ne backend "none") (ne backend "convex"))}}
28
+ {{#if (ne api "none")}}
29
+ - **`packages/api/`** - Shared API logic and types
30
+ {{/if}}
31
+ {{#if (and (ne auth "none") (ne backend "convex"))}}
32
+ - **`packages/auth/`** - Authentication logic and utilities
33
+ {{/if}}
34
+ {{#if (and (ne database "none") (ne orm "none") (ne backend "convex"))}}
35
+ - **`packages/db/`** - Database schema and utilities
36
+ {{/if}}
37
+ {{/if}}
38
+
27
39
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
28
40
  - **`apps/native/`** - React Native mobile app{{#if (includes frontend "native-nativewind")}} (with NativeWind){{else if
29
41
  (includes frontend "native-unistyles")}} (with Unistyles){{/if}}
@@ -32,15 +44,13 @@ frontend "solid")}} (SolidStart){{/if}}
32
44
  ## Available Scripts
33
45
 
34
46
  - `{{packageManager}} run dev` - Start all apps in development mode
35
- {{#if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "tanstack-start")
36
- (includes frontend "next") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid"))}}
47
+ {{#if (and (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "tanstack-start")
48
+ (includes frontend "next") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid")) (ne backend "self"))}}
37
49
  - `{{packageManager}} run dev:web` - Start only the web app
38
50
  {{/if}}
39
- {{#if (ne backend "none")}}
40
- {{#if (ne backend "convex")}}
51
+ {{#if (and (ne backend "none") (ne backend "convex") (ne backend "self"))}}
41
52
  - `{{packageManager}} run dev:server` - Start only the server
42
53
  {{/if}}
43
- {{/if}}
44
54
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
45
55
  - `{{packageManager}} run dev:native` - Start only the native app
46
56
  {{/if}}
@@ -48,7 +58,7 @@ frontend "solid")}} (SolidStart){{/if}}
48
58
  {{#if (and (ne database "none") (ne orm "none") (ne backend "convex"))}}
49
59
  ## Database Commands
50
60
 
51
- All database operations should be run from the server workspace:
61
+ All database operations should be run from the {{#if (eq backend "self")}}web{{else}}server{{/if}} workspace:
52
62
 
53
63
  - `{{packageManager}} run db:push` - Push schema changes to database
54
64
  - `{{packageManager}} run db:studio` - Open database studio
@@ -57,11 +67,11 @@ All database operations should be run from the server workspace:
57
67
  - `{{packageManager}} run db:migrate` - Run database migrations
58
68
 
59
69
  {{#if (eq orm "drizzle")}}
60
- Database schema files are located in `apps/server/src/db/schema/`
70
+ Database schema files are located in {{#if (eq backend "self")}}`apps/web/src/db/schema/`{{else}}`apps/server/src/db/schema/`{{/if}}
61
71
  {{else if (eq orm "prisma")}}
62
- Database schema is located in `apps/server/prisma/schema.prisma`
72
+ Database schema is located in {{#if (eq backend "self")}}`apps/web/prisma/schema.prisma`{{else}}`apps/server/prisma/schema.prisma`{{/if}}
63
73
  {{else if (eq orm "mongoose")}}
64
- Database models are located in `apps/server/src/db/models/`
74
+ Database models are located in {{#if (eq backend "self")}}`apps/web/src/db/models/`{{else}}`apps/server/src/db/models/`{{/if}}
65
75
  {{/if}}
66
76
  {{/if}}
67
77
 
@@ -69,10 +79,10 @@ Database models are located in `apps/server/src/db/models/`
69
79
  ## API Structure
70
80
 
71
81
  {{#if (eq api "trpc")}}
72
- - tRPC routers are in `apps/server/src/routers/`
82
+ - tRPC routers are in {{#if (eq backend "self")}}`packages/api/src/routers/`{{else}}`apps/server/src/routers/`{{/if}}
73
83
  - Client-side tRPC utils are in `apps/web/src/utils/trpc.ts`
74
84
  {{else if (eq api "orpc")}}
75
- - oRPC endpoints are in `apps/server/src/api/`
85
+ - oRPC endpoints are in {{#if (eq backend "self")}}`packages/api/src/api/`{{else}}`apps/server/src/api/`{{/if}}
76
86
  - Client-side API utils are in `apps/web/src/utils/api.ts`
77
87
  {{/if}}
78
88
  {{/if}}
@@ -82,7 +92,7 @@ Database models are located in `apps/server/src/db/models/`
82
92
 
83
93
  Authentication is enabled in this project:
84
94
  {{#if (ne backend "convex")}}
85
- - Server auth logic is in `apps/server/src/lib/auth.ts`
95
+ - Server auth logic is in {{#if (eq backend "self")}}`packages/auth/src/lib/auth.ts`{{else}}`apps/server/src/lib/auth.ts`{{/if}}
86
96
  {{#if (or (includes frontend "tanstack-router") (includes frontend "react-router") (includes frontend "tanstack-start")
87
97
  (includes frontend "next") (includes frontend "nuxt") (includes frontend "svelte") (includes frontend "solid"))}}
88
98
  - Web app auth client is in `apps/web/src/lib/auth-client.ts`
@@ -2,7 +2,7 @@ import { createORPCClient } from "@orpc/client";
2
2
  import { RPCLink } from "@orpc/client/fetch";
3
3
  import { createTanstackQueryUtils } from "@orpc/tanstack-query";
4
4
  import { QueryCache, QueryClient } from "@tanstack/react-query";
5
- import type { AppRouterClient } from "../../server/src/routers";
5
+ import type { AppRouterClient } from "@{{projectName}}/api/routers/index";
6
6
  {{#if (eq auth "better-auth")}}
7
7
  import { authClient } from "@/lib/auth-client";
8
8
  {{/if}}
@@ -16,7 +16,11 @@ export const queryClient = new QueryClient({
16
16
  });
17
17
 
18
18
  export const link = new RPCLink({
19
+ {{#if (eq backend "self")}}
20
+ url: `${process.env.EXPO_PUBLIC_SERVER_URL}/api/rpc`,
21
+ {{else}}
19
22
  url: `${process.env.EXPO_PUBLIC_SERVER_URL}/rpc`,
23
+ {{/if}}
20
24
  {{#if (eq auth "better-auth")}}
21
25
  headers() {
22
26
  const headers = new Map<string, string>();
@@ -14,9 +14,7 @@
14
14
  "scripts": {
15
15
  "build": "tsdown"
16
16
  },
17
- "devDependencies": {
18
- "@types/bun": "latest"
19
- },
17
+ "devDependencies": {},
20
18
  "peerDependencies": {
21
19
  "typescript": "^5"
22
20
  },
@@ -1,10 +1,10 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "types": [
7
- "bun"
8
- ],
9
- }
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "declaration": true,
5
+ "declarationMap": true,
6
+ "sourceMap": true,
7
+ "outDir": "dist",
8
+ "composite": true
9
+ }
10
10
  }
@@ -4,14 +4,18 @@ import { authClient } from "@/lib/auth-client";
4
4
  import { QueryClient } from "@tanstack/react-query";
5
5
  import { createTRPCClient, httpBatchLink } from "@trpc/client";
6
6
  import { createTRPCOptionsProxy } from "@trpc/tanstack-react-query";
7
- import type { AppRouter } from "../../server/src/routers";
7
+ import type { AppRouter } from "@{{projectName}}/api/routers/index";
8
8
 
9
9
  export const queryClient = new QueryClient();
10
10
 
11
11
  const trpcClient = createTRPCClient<AppRouter>({
12
12
  links: [
13
13
  httpBatchLink({
14
+ {{#if (eq backend "self")}}
15
+ url: `${process.env.EXPO_PUBLIC_SERVER_URL}/api/trpc`,
16
+ {{else}}
14
17
  url: `${process.env.EXPO_PUBLIC_SERVER_URL}/trpc`,
18
+ {{/if}}
15
19
  {{#if (eq auth "better-auth")}}
16
20
  headers() {
17
21
  const headers = new Map<string, string>();
@@ -14,9 +14,7 @@
14
14
  "scripts": {
15
15
  "build": "tsdown"
16
16
  },
17
- "devDependencies": {
18
- "@types/bun": "latest"
19
- },
17
+ "devDependencies": {},
20
18
  "peerDependencies": {
21
19
  "typescript": "^5"
22
20
  }
@@ -1,13 +1,10 @@
1
1
  {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "declaration": true,
5
- "declarationMap": true,
6
- "sourceMap": true,
7
- "outDir": "dist",
8
- "composite": true,
9
- "types": [
10
- "bun"
11
- ]
12
- }
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "declaration": true,
5
+ "declarationMap": true,
6
+ "sourceMap": true,
7
+ "outDir": "dist",
8
+ "composite": true
9
+ }
13
10
  }
@@ -14,10 +14,7 @@
14
14
  "scripts": {
15
15
  "build": "tsdown"
16
16
  },
17
- "devDependencies": {
18
- "@types/bun": "latest",
19
- "@types/node": "^24.5.2"
20
- },
17
+ "devDependencies": {},
21
18
  "peerDependencies": {
22
19
  "typescript": "^5"
23
20
  }
@@ -1,13 +1,10 @@
1
1
  {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "declaration": true,
5
- "declarationMap": true,
6
- "sourceMap": true,
7
- "outDir": "dist",
8
- "composite": true,
9
- "types": [
10
- "bun"
11
- ]
12
- }
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "declaration": true,
5
+ "declarationMap": true,
6
+ "sourceMap": true,
7
+ "outDir": "dist",
8
+ "composite": true
9
+ }
13
10
  }
@@ -5,22 +5,8 @@
5
5
  "outDir": "dist",
6
6
  "baseUrl": ".",
7
7
  "paths": {
8
- "@/*": ["./src/*"]{{#if (eq orm "prisma")}},
9
- "prisma": ["node_modules/prisma"]{{/if}}
8
+ "@/*": ["./src/*"]
10
9
  },
11
- "types": [
12
- {{#if (eq runtime "node")}}
13
- "node"
14
- {{else if (eq runtime "bun")}}
15
- "bun"
16
- {{else if (eq runtime "workers")}}
17
- "node"
18
- {{else}}
19
- "node",
20
- "bun"
21
- {{/if}}{{#if (eq serverDeploy "alchemy")}},
22
- "@cloudflare/workers-types"{{/if}}
23
- ],
24
10
  "jsx": "react-jsx"{{#if (eq backend "hono")}},
25
11
  "jsxImportSource": "hono/jsx"{{/if}}
26
12
  }
@@ -1,23 +1,33 @@
1
1
  {
2
- "$schema": "https://json.schemastore.org/tsconfig",
3
- "compilerOptions": {
4
- "target": "ESNext",
5
- "module": "ESNext",
6
- "moduleResolution": "bundler",
7
- "lib": [
8
- "ESNext"
9
- ],
10
- "verbatimModuleSyntax": true,
11
- "strict": true,
12
- "skipLibCheck": true,
13
- "resolveJsonModule": true,
14
- "allowSyntheticDefaultImports": true,
15
- "esModuleInterop": true,
16
- "forceConsistentCasingInFileNames": true,
17
- "isolatedModules": true,
18
- "noUncheckedIndexedAccess": true,
19
- "noUnusedLocals": true,
20
- "noUnusedParameters": true,
21
- "noFallthroughCasesInSwitch": true
22
- }
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "compilerOptions": {
4
+ "target": "ESNext",
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "lib": ["ESNext"],
8
+ "verbatimModuleSyntax": true,
9
+ "strict": true,
10
+ "skipLibCheck": true,
11
+ "resolveJsonModule": true,
12
+ "allowSyntheticDefaultImports": true,
13
+ "esModuleInterop": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "isolatedModules": true,
16
+ "noUncheckedIndexedAccess": true,
17
+ "noUnusedLocals": true,
18
+ "noUnusedParameters": true,
19
+ "noFallthroughCasesInSwitch": true,
20
+ "types": [
21
+ {{#if (eq runtime "node")}}
22
+ "node"
23
+ {{else if (eq runtime "bun")}}
24
+ "bun"
25
+ {{else if (eq runtime "workers")}}
26
+ "node"
27
+ {{else}}
28
+ "node"
29
+ {{/if}}{{#if (eq serverDeploy "alchemy")}},
30
+ "@cloudflare/workers-types"{{/if}}
31
+ ]
32
+ }
23
33
  }
@@ -14,9 +14,7 @@
14
14
  "scripts": {
15
15
  "build": "tsdown"
16
16
  },
17
- "devDependencies": {
18
- "@types/node": "^24.5.2"
19
- },
17
+ "devDependencies": {},
20
18
  "peerDependencies": {
21
19
  "typescript": "^5"
22
20
  }
@@ -1,13 +1,10 @@
1
1
  {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "declaration": true,
5
- "declarationMap": true,
6
- "sourceMap": true,
7
- "outDir": "dist",
8
- "composite": true,
9
- "types": [
10
- "bun"
11
- ]
12
- }
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "declaration": true,
5
+ "declarationMap": true,
6
+ "sourceMap": true,
7
+ "outDir": "dist",
8
+ "composite": true
9
+ }
13
10
  }
@@ -1,5 +1,5 @@
1
1
  [install]
2
- {{#if (includes frontend "nuxt")}}
2
+ {{#if (or (includes frontend "nuxt") (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
3
3
  # linker = "isolated" # Commented out for Nuxt compatibility
4
4
  {{else}}
5
5
  linker = "isolated"