create-better-t-stack 3.26.1 → 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-Kus6HC8q.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-Kus6HC8q.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 };
@@ -3392,7 +3392,7 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
3392
3392
  label: "Turso",
3393
3393
  hint: "SQLite for Production. Powered by libSQL"
3394
3394
  },
3395
- ...runtime === "workers" ? [{
3395
+ ...runtime === "workers" || backend === "self" ? [{
3396
3396
  value: "d1",
3397
3397
  label: "Cloudflare D1",
3398
3398
  hint: "Cloudflare's managed, serverless database with SQLite's SQL semantics"
@@ -3832,9 +3832,10 @@ function getDeploymentDisplay(deployment) {
3832
3832
  hint: `Add ${deployment} deployment`
3833
3833
  };
3834
3834
  }
3835
- async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []) {
3835
+ async function getDeploymentChoice(deployment, _runtime, backend, frontend = [], dbSetup) {
3836
3836
  if (deployment !== void 0) return deployment;
3837
3837
  if (!hasWebFrontend(frontend)) return "none";
3838
+ if (backend === "self" && dbSetup === "d1") return "cloudflare";
3838
3839
  const response = await navigableSelect({
3839
3840
  message: "Select web deployment",
3840
3841
  options: ["cloudflare", "none"].map((deploy) => {
@@ -3888,7 +3889,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
3888
3889
  addons: ({ results }) => getAddonsChoice(flags.addons, results.frontend, results.auth),
3889
3890
  examples: ({ results }) => getExamplesChoice(flags.examples, results.database, results.frontend, results.backend, results.api),
3890
3891
  dbSetup: ({ results }) => getDBSetupChoice(results.database ?? "none", flags.dbSetup, results.orm, results.backend, results.runtime),
3891
- 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),
3892
3893
  serverDeploy: ({ results }) => getServerDeploymentChoice(flags.serverDeploy, results.runtime, results.backend, results.webDeploy),
3893
3894
  git: () => getGitChoice(flags.git),
3894
3895
  packageManager: () => getPackageManagerChoice(flags.packageManager),
@@ -4305,6 +4306,18 @@ function validateArrayOptions(options) {
4305
4306
  function validationErr(message) {
4306
4307
  return Result.err(new ValidationError({ message }));
4307
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
+ }
4308
4321
  function validateDatabaseOrmAuth(cfg, flags) {
4309
4322
  const db = cfg.database;
4310
4323
  const orm = cfg.orm;
@@ -4343,8 +4356,7 @@ function validateDatabaseSetup(config, providedFlags) {
4343
4356
  },
4344
4357
  d1: {
4345
4358
  database: "sqlite",
4346
- runtime: "workers",
4347
- errorMessage: "Cloudflare D1 setup requires SQLite database and Cloudflare Workers runtime."
4359
+ errorMessage: "Cloudflare D1 setup requires SQLite database."
4348
4360
  },
4349
4361
  docker: { errorMessage: "Docker setup is not compatible with SQLite database or Cloudflare Workers runtime." },
4350
4362
  none: { errorMessage: "" }
@@ -4355,6 +4367,13 @@ function validateDatabaseSetup(config, providedFlags) {
4355
4367
  if (database !== "postgres" && database !== "mysql") return validationErr(validation.errorMessage);
4356
4368
  } else if (validation.database && database !== validation.database) return validationErr(validation.errorMessage);
4357
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
+ }
4358
4377
  if (dbSetup === "docker") {
4359
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.");
4360
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.");
@@ -4612,8 +4631,8 @@ async function addEnvVariablesToFile(envPath, variables) {
4612
4631
  //#endregion
4613
4632
  //#region src/helpers/database-providers/d1-setup.ts
4614
4633
  async function setupCloudflareD1(config) {
4615
- const { projectDir, serverDeploy, orm, backend } = config;
4616
- 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);
4617
4636
  return Result.tryPromise({
4618
4637
  try: async () => {
4619
4638
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
@@ -5995,7 +6014,7 @@ async function displayPostInstallInstructions(config) {
5995
6014
  const hasHusky = addons?.includes("husky");
5996
6015
  const hasLefthook = addons?.includes("lefthook");
5997
6016
  const hasGitHooksOrLinting = addons?.includes("husky") || addons?.includes("biome") || addons?.includes("lefthook") || addons?.includes("oxlint");
5998
- 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) : "";
5999
6018
  const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd, frontend) : "";
6000
6019
  const electrobunInstructions = addons?.includes("electrobun") ? getElectrobunInstructions(runCmd, frontend) : "";
6001
6020
  const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
@@ -6088,8 +6107,9 @@ function getLefthookInstructions(packageManager) {
6088
6107
  const cmd = packageManager === "npm" ? "npx" : packageManager;
6089
6108
  return `${pc.bold("Git hooks with Lefthook:")}\n${pc.cyan("•")} Install hooks: ${cmd} lefthook install\n`;
6090
6109
  }
6091
- async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup, serverDeploy, _backend) {
6110
+ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup, webDeploy, serverDeploy, backend) {
6092
6111
  const instructions = [];
6112
+ const isD1Alchemy = dbSetup === "d1" && (serverDeploy === "cloudflare" || backend === "self" && webDeploy === "cloudflare");
6093
6113
  if (dbSetup === "docker") {
6094
6114
  const dockerStatus = await getDockerStatus(database);
6095
6115
  if (dockerStatus.message) {
@@ -6097,7 +6117,7 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
6097
6117
  instructions.push("");
6098
6118
  }
6099
6119
  }
6100
- if (dbSetup === "d1" && serverDeploy === "cloudflare") {
6120
+ if (isD1Alchemy) {
6101
6121
  if (orm === "drizzle") instructions.push(`${pc.cyan("•")} Generate migrations: ${`${runCmd} db:generate`}`);
6102
6122
  else if (orm === "prisma") {
6103
6123
  instructions.push(`${pc.cyan("•")} Generate Prisma client: ${`${runCmd} db:generate`}`);
@@ -6112,15 +6132,15 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
6112
6132
  if (orm === "prisma") {
6113
6133
  if (database === "mongodb" && dbSetup === "docker") instructions.push(`${pc.yellow("WARNING:")} Prisma + MongoDB + Docker combination\n may not work.`);
6114
6134
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6115
- if (!(dbSetup === "d1" && serverDeploy === "cloudflare")) {
6135
+ if (!isD1Alchemy) {
6116
6136
  instructions.push(`${pc.cyan("•")} Generate Prisma Client: ${`${runCmd} db:generate`}`);
6117
6137
  instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
6118
6138
  }
6119
- 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`}`);
6120
6140
  } else if (orm === "drizzle") {
6121
6141
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6122
- if (dbSetup !== "d1") instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
6123
- 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`}`);
6124
6144
  } else if (orm === "mongoose") {
6125
6145
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6126
6146
  } else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "3.26.1",
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,8 +70,8 @@
70
70
  "prepublishOnly": "npm run build"
71
71
  },
72
72
  "dependencies": {
73
- "@better-t-stack/template-generator": "^3.26.1",
74
- "@better-t-stack/types": "^3.26.1",
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",