create-better-t-stack 3.26.0 → 3.27.0

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/README.md CHANGED
@@ -217,6 +217,12 @@ Create a Cloudflare Workers project:
217
217
  npx create-better-t-stack --backend hono --runtime workers --database sqlite --orm drizzle --db-setup d1
218
218
  ```
219
219
 
220
+ Create a self-hosted fullstack project on Cloudflare with D1:
221
+
222
+ ```bash
223
+ npx create-better-t-stack --backend self --frontend next --api trpc --database sqlite --orm drizzle --db-setup d1 --web-deploy cloudflare
224
+ ```
225
+
220
226
  Create a minimal API-only project:
221
227
 
222
228
  ```bash
@@ -232,7 +238,8 @@ npx create-better-t-stack --frontend none --backend hono --api trpc --database n
232
238
  - **Database 'none'**: Disables database setup and requires ORM to be `none`.
233
239
  - **ORM 'none'**: Can be used when you want to handle database operations manually or use a different ORM.
234
240
  - **Runtime 'none'**: Only available with Convex backend, backend `none`, or backend `self`.
235
- - **Cloudflare Workers runtime**: Only compatible with Hono backend, Drizzle ORM (or no ORM), and SQLite database (with D1 setup). Not compatible with MongoDB.
241
+ - **Cloudflare Workers runtime**: Only compatible with Hono backend. If a database is used, MongoDB is not supported.
242
+ - **Cloudflare D1 setup**: Requires `sqlite` and either `--runtime workers --server-deploy cloudflare` or `--backend self --web-deploy cloudflare`. For `backend self`, D1 is supported on `next`, `tanstack-start`, `nuxt`, and `astro`.
236
243
  - **Addons 'none'**: Skips all addons.
237
244
  - **Examples 'none'**: Skips all example implementations (todo, AI chat).
238
245
  - **Nuxt, Svelte, SolidJS, and Astro** frontends are only compatible with oRPC API layer
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { _ as types_exports, i as SchemaNameSchema, l as create, m as getSchemaResult, s as add, u as createBtsCli, v as getLatestCLIVersion } from "./src-BVuxTUEs.mjs";
2
+ import { _ as types_exports, i as SchemaNameSchema, l as create, m as getSchemaResult, s as add, u as createBtsCli, v as getLatestCLIVersion } from "./src-CiY2JajO.mjs";
3
3
  import z from "zod";
4
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { C as ProjectCreationError, S as DirectoryConflictError, T as ValidationError, a as TEMPLATE_COUNT, b as CompatibilityError, c as builder, d as createVirtual, f as docs, g as sponsors, h as router, i as SchemaNameSchema, l as create, m as getSchemaResult, n as GeneratorError, o as VirtualFileSystem, p as generate, r as Result, s as add, t as EMBEDDED_TEMPLATES, u as createBtsCli, w as UserCancelledError, x as DatabaseSetupError, y as CLIError } from "./src-BVuxTUEs.mjs";
2
+ import { C as ProjectCreationError, S as DirectoryConflictError, T as ValidationError, a as TEMPLATE_COUNT, b as CompatibilityError, c as builder, d as createVirtual, f as docs, g as sponsors, h as router, i as SchemaNameSchema, l as create, m as getSchemaResult, n as GeneratorError, o as VirtualFileSystem, p as generate, r as Result, s as add, t as EMBEDDED_TEMPLATES, u as createBtsCli, w as UserCancelledError, x as DatabaseSetupError, y as CLIError } from "./src-CiY2JajO.mjs";
3
3
  export { CLIError, CompatibilityError, DatabaseSetupError, DirectoryConflictError, EMBEDDED_TEMPLATES, GeneratorError, ProjectCreationError, Result, SchemaNameSchema, TEMPLATE_COUNT, UserCancelledError, ValidationError, VirtualFileSystem, add, builder, create, createBtsCli, createVirtual, docs, generate, getSchemaResult, router, sponsors };
@@ -12,7 +12,7 @@ import envPaths from "env-paths";
12
12
  import fs from "fs-extra";
13
13
  import { fileURLToPath } from "node:url";
14
14
  import { desktopWebFrontends as desktopWebFrontends$3 } from "@better-t-stack/types";
15
- import { EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TEMPLATES$1, GeneratorError as GeneratorError$1, TEMPLATE_COUNT, VirtualFileSystem, VirtualFileSystem as VirtualFileSystem$1, dependencyVersionMap, generate, generate as generate$1, generateReproducibleCommand, processAddonTemplates, processAddonsDeps } from "@better-t-stack/template-generator";
15
+ import { EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TEMPLATES$1, GeneratorError, GeneratorError as GeneratorError$1, TEMPLATE_COUNT, VirtualFileSystem, VirtualFileSystem as VirtualFileSystem$1, dependencyVersionMap, generate, generate as generate$1, generateReproducibleCommand, processAddonTemplates, processAddonsDeps } from "@better-t-stack/template-generator";
16
16
  import { consola, createConsola } from "consola";
17
17
  import { AsyncLocalStorage } from "node:async_hooks";
18
18
  import gradient from "gradient-string";
@@ -728,6 +728,21 @@ const WEB_FRAMEWORKS = [
728
728
  ];
729
729
  //#endregion
730
730
  //#region src/utils/compatibility-rules.ts
731
+ const CONVEX_BETTER_AUTH_INCOMPATIBLE_FRONTENDS = [
732
+ "nuxt",
733
+ "svelte",
734
+ "solid",
735
+ "astro"
736
+ ];
737
+ const CONVEX_BETTER_AUTH_SUPPORTED_FRONTENDS = [
738
+ "tanstack-router",
739
+ "react-router",
740
+ "tanstack-start",
741
+ "next",
742
+ "native-bare",
743
+ "native-uniwind",
744
+ "native-unistyles"
745
+ ];
731
746
  function validationErr$1(message) {
732
747
  return Result.err(new ValidationError({ message }));
733
748
  }
@@ -781,7 +796,10 @@ function validateApiFrontendCompatibility(api, frontends = []) {
781
796
  return Result.ok(void 0);
782
797
  }
783
798
  function isFrontendAllowedWithBackend(frontend, backend, auth) {
784
- if (backend === "convex" && (frontend === "solid" || frontend === "astro")) return false;
799
+ if (backend === "convex") {
800
+ if (auth === "better-auth" && CONVEX_BETTER_AUTH_INCOMPATIBLE_FRONTENDS.includes(frontend)) return false;
801
+ if (frontend === "solid" || frontend === "astro") return false;
802
+ }
785
803
  if (auth === "clerk") {
786
804
  if ([
787
805
  "nuxt",
@@ -792,6 +810,9 @@ function isFrontendAllowedWithBackend(frontend, backend, auth) {
792
810
  }
793
811
  return true;
794
812
  }
813
+ function supportsConvexBetterAuth(frontends = []) {
814
+ return frontends.some((frontend) => CONVEX_BETTER_AUTH_SUPPORTED_FRONTENDS.includes(frontend));
815
+ }
795
816
  function allowedApisForFrontends(frontends = []) {
796
817
  const includesNuxt = frontends.includes("nuxt");
797
818
  const includesSvelte = frontends.includes("svelte");
@@ -3215,21 +3236,9 @@ async function getApiChoice(Api, frontend, backend) {
3215
3236
  }
3216
3237
  //#endregion
3217
3238
  //#region src/prompts/auth.ts
3218
- async function getAuthChoice(auth, backend, frontend) {
3219
- if (auth !== void 0) return auth;
3220
- if (backend === "none") return "none";
3221
- const supportedBetterAuthFrontends = frontend?.some((f) => [
3222
- "tanstack-router",
3223
- "tanstack-start",
3224
- "next",
3225
- "nuxt",
3226
- "svelte",
3227
- "solid",
3228
- "native-bare",
3229
- "native-uniwind",
3230
- "native-unistyles"
3231
- ].includes(f));
3232
- const hasClerkCompatibleFrontends = frontend?.some((f) => [
3239
+ function getAvailableAuthProviders(backend, frontend = []) {
3240
+ if (backend === "none") return ["none"];
3241
+ const hasClerkCompatibleFrontends = frontend.some((f) => [
3233
3242
  "react-router",
3234
3243
  "tanstack-router",
3235
3244
  "tanstack-start",
@@ -3240,26 +3249,34 @@ async function getAuthChoice(auth, backend, frontend) {
3240
3249
  ].includes(f));
3241
3250
  const options = [];
3242
3251
  if (backend === "convex") {
3243
- if (supportedBetterAuthFrontends) options.push({
3244
- value: "better-auth",
3245
- label: "Better-Auth",
3246
- hint: "comprehensive auth framework for TypeScript"
3247
- });
3248
- } else options.push({
3249
- value: "better-auth",
3250
- label: "Better-Auth",
3251
- hint: "comprehensive auth framework for TypeScript"
3252
- });
3253
- if (hasClerkCompatibleFrontends) options.push({
3254
- value: "clerk",
3255
- label: "Clerk",
3256
- hint: "More than auth, Complete User Management"
3257
- });
3258
- if (options.length === 0) return "none";
3259
- options.push({
3260
- value: "none",
3261
- label: "None",
3262
- hint: "No auth"
3252
+ if (supportsConvexBetterAuth(frontend)) options.push("better-auth");
3253
+ } else options.push("better-auth");
3254
+ if (hasClerkCompatibleFrontends) options.push("clerk");
3255
+ if (options.length === 0) return ["none"];
3256
+ return [...options, "none"];
3257
+ }
3258
+ async function getAuthChoice(auth, backend, frontend = []) {
3259
+ if (auth !== void 0) return auth;
3260
+ const availableProviders = getAvailableAuthProviders(backend, frontend);
3261
+ if (availableProviders.length === 1 && availableProviders[0] === "none") return "none";
3262
+ const options = availableProviders.map((provider) => {
3263
+ switch (provider) {
3264
+ case "better-auth": return {
3265
+ value: "better-auth",
3266
+ label: "Better-Auth",
3267
+ hint: "comprehensive auth framework for TypeScript"
3268
+ };
3269
+ case "clerk": return {
3270
+ value: "clerk",
3271
+ label: "Clerk",
3272
+ hint: "More than auth, Complete User Management"
3273
+ };
3274
+ default: return {
3275
+ value: "none",
3276
+ label: "None",
3277
+ hint: "No auth"
3278
+ };
3279
+ }
3263
3280
  });
3264
3281
  const response = await navigableSelect({
3265
3282
  message: "Select authentication provider",
@@ -3375,7 +3392,7 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
3375
3392
  label: "Turso",
3376
3393
  hint: "SQLite for Production. Powered by libSQL"
3377
3394
  },
3378
- ...runtime === "workers" ? [{
3395
+ ...runtime === "workers" || backend === "self" ? [{
3379
3396
  value: "d1",
3380
3397
  label: "Cloudflare D1",
3381
3398
  hint: "Cloudflare's managed, serverless database with SQLite's SQL semantics"
@@ -3815,9 +3832,10 @@ function getDeploymentDisplay(deployment) {
3815
3832
  hint: `Add ${deployment} deployment`
3816
3833
  };
3817
3834
  }
3818
- async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []) {
3835
+ async function getDeploymentChoice(deployment, _runtime, backend, frontend = [], dbSetup) {
3819
3836
  if (deployment !== void 0) return deployment;
3820
3837
  if (!hasWebFrontend(frontend)) return "none";
3838
+ if (backend === "self" && dbSetup === "d1") return "cloudflare";
3821
3839
  const response = await navigableSelect({
3822
3840
  message: "Select web deployment",
3823
3841
  options: ["cloudflare", "none"].map((deploy) => {
@@ -3871,7 +3889,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
3871
3889
  addons: ({ results }) => getAddonsChoice(flags.addons, results.frontend, results.auth),
3872
3890
  examples: ({ results }) => getExamplesChoice(flags.examples, results.database, results.frontend, results.backend, results.api),
3873
3891
  dbSetup: ({ results }) => getDBSetupChoice(results.database ?? "none", flags.dbSetup, results.orm, results.backend, results.runtime),
3874
- webDeploy: ({ results }) => getDeploymentChoice(flags.webDeploy, results.runtime, results.backend, results.frontend),
3892
+ webDeploy: ({ results }) => getDeploymentChoice(flags.webDeploy, results.runtime, results.backend, results.frontend, results.dbSetup),
3875
3893
  serverDeploy: ({ results }) => getServerDeploymentChoice(flags.serverDeploy, results.runtime, results.backend, results.webDeploy),
3876
3894
  git: () => getGitChoice(flags.git),
3877
3895
  packageManager: () => getPackageManagerChoice(flags.packageManager),
@@ -4288,6 +4306,18 @@ function validateArrayOptions(options) {
4288
4306
  function validationErr(message) {
4289
4307
  return Result.err(new ValidationError({ message }));
4290
4308
  }
4309
+ function hasResolvedWorkersD1Target(config) {
4310
+ return config.backend === "hono" && config.runtime === "workers" && config.serverDeploy === "cloudflare";
4311
+ }
4312
+ function hasResolvedSelfCloudflareD1Target(config) {
4313
+ return config.backend === "self" && config.runtime === "none" && config.webDeploy === "cloudflare";
4314
+ }
4315
+ function canResolveWorkersD1Target(config) {
4316
+ return (config.backend === void 0 || config.backend === "hono") && (config.runtime === void 0 || config.runtime === "workers") && (config.serverDeploy === void 0 || config.serverDeploy === "cloudflare");
4317
+ }
4318
+ function canResolveSelfCloudflareD1Target(config) {
4319
+ return (config.backend === void 0 || config.backend === "self") && (config.runtime === void 0 || config.runtime === "none") && (config.webDeploy === void 0 || config.webDeploy === "cloudflare");
4320
+ }
4291
4321
  function validateDatabaseOrmAuth(cfg, flags) {
4292
4322
  const db = cfg.database;
4293
4323
  const orm = cfg.orm;
@@ -4326,8 +4356,7 @@ function validateDatabaseSetup(config, providedFlags) {
4326
4356
  },
4327
4357
  d1: {
4328
4358
  database: "sqlite",
4329
- runtime: "workers",
4330
- errorMessage: "Cloudflare D1 setup requires SQLite database and Cloudflare Workers runtime."
4359
+ errorMessage: "Cloudflare D1 setup requires SQLite database."
4331
4360
  },
4332
4361
  docker: { errorMessage: "Docker setup is not compatible with SQLite database or Cloudflare Workers runtime." },
4333
4362
  none: { errorMessage: "" }
@@ -4338,6 +4367,13 @@ function validateDatabaseSetup(config, providedFlags) {
4338
4367
  if (database !== "postgres" && database !== "mysql") return validationErr(validation.errorMessage);
4339
4368
  } else if (validation.database && database !== validation.database) return validationErr(validation.errorMessage);
4340
4369
  if (validation.runtime && runtime !== validation.runtime) return validationErr(validation.errorMessage);
4370
+ if (dbSetup === "d1") {
4371
+ const isWorkersTarget = hasResolvedWorkersD1Target(config);
4372
+ const isSelfCloudflareTarget = hasResolvedSelfCloudflareD1Target(config);
4373
+ const canResolveWorkersTarget = canResolveWorkersD1Target(config);
4374
+ const canResolveSelfCloudflareTarget = canResolveSelfCloudflareD1Target(config);
4375
+ if (!isWorkersTarget && !isSelfCloudflareTarget && !canResolveWorkersTarget && !canResolveSelfCloudflareTarget) return validationErr("Cloudflare D1 setup requires SQLite database and either Cloudflare Workers runtime with server deployment or backend 'self' with Cloudflare web deployment.");
4376
+ }
4341
4377
  if (dbSetup === "docker") {
4342
4378
  if (database === "sqlite") return validationErr("Docker setup is not compatible with SQLite database. SQLite is file-based and doesn't require Docker. Please use '--database postgres', '--database mysql', '--database mongodb', or choose a different setup.");
4343
4379
  if (runtime === "workers") return validationErr("Docker setup is not compatible with Cloudflare Workers runtime. Workers runtime uses serverless databases (D1) and doesn't support local Docker containers. Please use '--db-setup d1' for SQLite or choose a different runtime.");
@@ -4356,15 +4392,10 @@ function validateConvexConstraints(config, providedFlags) {
4356
4392
  if (has("dbSetup") && config.dbSetup !== "none") return validationErr("Convex backend requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.");
4357
4393
  if (has("serverDeploy") && config.serverDeploy !== "none") return validationErr("Convex backend requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.");
4358
4394
  if (has("auth") && config.auth === "better-auth") {
4359
- const supportedFrontends = [
4360
- "tanstack-router",
4361
- "tanstack-start",
4362
- "next",
4363
- "native-bare",
4364
- "native-uniwind",
4365
- "native-unistyles"
4366
- ];
4367
- if (!config.frontend?.some((f) => supportedFrontends.includes(f))) return validationErr("Better-Auth with Convex backend requires a supported frontend (TanStack Router, TanStack Start, Next.js, or Native).");
4395
+ const incompatibleFrontends = config.frontend?.filter((f) => CONVEX_BETTER_AUTH_INCOMPATIBLE_FRONTENDS.includes(f)) ?? [];
4396
+ const hasSupportedFrontend = supportsConvexBetterAuth(config.frontend);
4397
+ if (incompatibleFrontends.length > 0) return validationErr(`Better Auth with '--backend convex' is not compatible with the following frontends: ${incompatibleFrontends.join(", ")}. Please use a React-based web frontend (next, tanstack-start, tanstack-router, react-router), a supported native frontend, or choose a different auth provider.`);
4398
+ if (!hasSupportedFrontend) return validationErr(`Better Auth with '--backend convex' requires a supported frontend (${CONVEX_BETTER_AUTH_SUPPORTED_FRONTENDS.join(", ")}).`);
4368
4399
  }
4369
4400
  return Result.ok(void 0);
4370
4401
  }
@@ -4600,8 +4631,8 @@ async function addEnvVariablesToFile(envPath, variables) {
4600
4631
  //#endregion
4601
4632
  //#region src/helpers/database-providers/d1-setup.ts
4602
4633
  async function setupCloudflareD1(config) {
4603
- const { projectDir, serverDeploy, orm, backend } = config;
4604
- if (!(serverDeploy === "cloudflare" && orm === "prisma")) return Result.ok(void 0);
4634
+ const { projectDir, serverDeploy, webDeploy, orm, backend } = config;
4635
+ if (!(orm === "prisma" && (serverDeploy === "cloudflare" || backend === "self" && webDeploy === "cloudflare"))) return Result.ok(void 0);
4605
4636
  return Result.tryPromise({
4606
4637
  try: async () => {
4607
4638
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
@@ -5983,7 +6014,7 @@ async function displayPostInstallInstructions(config) {
5983
6014
  const hasHusky = addons?.includes("husky");
5984
6015
  const hasLefthook = addons?.includes("lefthook");
5985
6016
  const hasGitHooksOrLinting = addons?.includes("husky") || addons?.includes("biome") || addons?.includes("lefthook") || addons?.includes("oxlint");
5986
- const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
6017
+ const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, webDeploy, serverDeploy, backend) : "";
5987
6018
  const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd, frontend) : "";
5988
6019
  const electrobunInstructions = addons?.includes("electrobun") ? getElectrobunInstructions(runCmd, frontend) : "";
5989
6020
  const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
@@ -5998,9 +6029,10 @@ async function displayPostInstallInstructions(config) {
5998
6029
  const hasWeb = frontend?.some((f) => types_exports.desktopWebFrontends.includes(f));
5999
6030
  const hasNative = frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles");
6000
6031
  const hasReactRouter = frontend?.includes("react-router");
6032
+ const hasTanStackRouter = frontend?.includes("tanstack-router");
6001
6033
  const hasSvelte = frontend?.includes("svelte");
6002
6034
  const hasAstro = frontend?.includes("astro");
6003
- const webPort = hasReactRouter || hasSvelte ? "5173" : hasAstro ? "4321" : "3001";
6035
+ const webPort = hasReactRouter || hasTanStackRouter || hasSvelte ? "5173" : hasAstro ? "4321" : "3001";
6004
6036
  const betterAuthConvexInstructions = isConvex && config.auth === "better-auth" ? getBetterAuthConvexInstructions(hasWeb ?? false, webPort, packageManager) : "";
6005
6037
  const bunWebNativeWarning = packageManager === "bun" && hasNative && hasWeb ? getBunWebNativeWarning() : "";
6006
6038
  const noOrmWarning = !isConvex && database !== "none" && orm === "none" ? getNoOrmWarning() : "";
@@ -6075,8 +6107,9 @@ function getLefthookInstructions(packageManager) {
6075
6107
  const cmd = packageManager === "npm" ? "npx" : packageManager;
6076
6108
  return `${pc.bold("Git hooks with Lefthook:")}\n${pc.cyan("•")} Install hooks: ${cmd} lefthook install\n`;
6077
6109
  }
6078
- async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup, serverDeploy, _backend) {
6110
+ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup, webDeploy, serverDeploy, backend) {
6079
6111
  const instructions = [];
6112
+ const isD1Alchemy = dbSetup === "d1" && (serverDeploy === "cloudflare" || backend === "self" && webDeploy === "cloudflare");
6080
6113
  if (dbSetup === "docker") {
6081
6114
  const dockerStatus = await getDockerStatus(database);
6082
6115
  if (dockerStatus.message) {
@@ -6084,7 +6117,7 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
6084
6117
  instructions.push("");
6085
6118
  }
6086
6119
  }
6087
- if (dbSetup === "d1" && serverDeploy === "cloudflare") {
6120
+ if (isD1Alchemy) {
6088
6121
  if (orm === "drizzle") instructions.push(`${pc.cyan("•")} Generate migrations: ${`${runCmd} db:generate`}`);
6089
6122
  else if (orm === "prisma") {
6090
6123
  instructions.push(`${pc.cyan("•")} Generate Prisma client: ${`${runCmd} db:generate`}`);
@@ -6099,15 +6132,15 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
6099
6132
  if (orm === "prisma") {
6100
6133
  if (database === "mongodb" && dbSetup === "docker") instructions.push(`${pc.yellow("WARNING:")} Prisma + MongoDB + Docker combination\n may not work.`);
6101
6134
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6102
- if (!(dbSetup === "d1" && serverDeploy === "cloudflare")) {
6135
+ if (!isD1Alchemy) {
6103
6136
  instructions.push(`${pc.cyan("•")} Generate Prisma Client: ${`${runCmd} db:generate`}`);
6104
6137
  instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
6105
6138
  }
6106
- if (!(dbSetup === "d1" && serverDeploy === "cloudflare")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
6139
+ if (!isD1Alchemy) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
6107
6140
  } else if (orm === "drizzle") {
6108
6141
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6109
- if (dbSetup !== "d1") instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
6110
- if (!(dbSetup === "d1" && serverDeploy === "cloudflare")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
6142
+ if (!isD1Alchemy) instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
6143
+ if (!isD1Alchemy) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
6111
6144
  } else if (orm === "mongoose") {
6112
6145
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6113
6146
  } else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
@@ -6819,30 +6852,51 @@ async function builder() {
6819
6852
  * ```
6820
6853
  */
6821
6854
  async function createVirtual(options) {
6855
+ const config = {
6856
+ projectName: options.projectName || "my-project",
6857
+ projectDir: "/virtual",
6858
+ relativePath: "./virtual",
6859
+ addonOptions: options.addonOptions,
6860
+ dbSetupOptions: options.dbSetupOptions,
6861
+ database: options.database || "none",
6862
+ orm: options.orm || "none",
6863
+ backend: options.backend || "hono",
6864
+ runtime: options.runtime || "bun",
6865
+ frontend: options.frontend || ["tanstack-router"],
6866
+ addons: options.addons || [],
6867
+ examples: options.examples || [],
6868
+ auth: options.auth || "none",
6869
+ payments: options.payments || "none",
6870
+ git: options.git ?? false,
6871
+ packageManager: options.packageManager || "bun",
6872
+ install: false,
6873
+ dbSetup: options.dbSetup || "none",
6874
+ api: options.api || "trpc",
6875
+ webDeploy: options.webDeploy || "none",
6876
+ serverDeploy: options.serverDeploy || "none"
6877
+ };
6878
+ const validationResult = validateConfigCompatibility(config, new Set([
6879
+ "database",
6880
+ "orm",
6881
+ "backend",
6882
+ "runtime",
6883
+ "frontend",
6884
+ "addons",
6885
+ "examples",
6886
+ "auth",
6887
+ "dbSetup",
6888
+ "payments",
6889
+ "api",
6890
+ "webDeploy",
6891
+ "serverDeploy"
6892
+ ]), config);
6893
+ if (validationResult.isErr()) return Result.err(new GeneratorError({
6894
+ message: validationResult.error.message,
6895
+ phase: "validation",
6896
+ cause: validationResult.error
6897
+ }));
6822
6898
  return generate({
6823
- config: {
6824
- projectName: options.projectName || "my-project",
6825
- projectDir: "/virtual",
6826
- relativePath: "./virtual",
6827
- addonOptions: options.addonOptions,
6828
- dbSetupOptions: options.dbSetupOptions,
6829
- database: options.database || "none",
6830
- orm: options.orm || "none",
6831
- backend: options.backend || "hono",
6832
- runtime: options.runtime || "bun",
6833
- frontend: options.frontend || ["tanstack-router"],
6834
- addons: options.addons || [],
6835
- examples: options.examples || [],
6836
- auth: options.auth || "none",
6837
- payments: options.payments || "none",
6838
- git: options.git ?? false,
6839
- packageManager: options.packageManager || "bun",
6840
- install: false,
6841
- dbSetup: options.dbSetup || "none",
6842
- api: options.api || "trpc",
6843
- webDeploy: options.webDeploy || "none",
6844
- serverDeploy: options.serverDeploy || "none"
6845
- },
6899
+ config,
6846
6900
  templates: EMBEDDED_TEMPLATES
6847
6901
  });
6848
6902
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "3.26.0",
3
+ "version": "3.27.0",
4
4
  "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
5
5
  "keywords": [
6
6
  "better-auth",
@@ -70,12 +70,12 @@
70
70
  "prepublishOnly": "npm run build"
71
71
  },
72
72
  "dependencies": {
73
- "@better-t-stack/template-generator": "^3.26.0",
74
- "@better-t-stack/types": "^3.26.0",
73
+ "@better-t-stack/template-generator": "^3.27.0",
74
+ "@better-t-stack/types": "^3.27.0",
75
75
  "@clack/core": "^1.1.0",
76
76
  "@clack/prompts": "^1.1.0",
77
77
  "@modelcontextprotocol/sdk": "1.27.1",
78
- "@trpc/server": "^11.4.3",
78
+ "@trpc/server": "^11.13.4",
79
79
  "better-result": "^2.7.0",
80
80
  "consola": "^3.4.2",
81
81
  "env-paths": "^4.0.0",