create-better-t-stack 3.27.0 → 3.27.1

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.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-CiY2JajO.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-nmBsmday.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-CiY2JajO.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-nmBsmday.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 };
@@ -783,7 +783,6 @@ function validateWorkersCompatibility(providedFlags, options, config) {
783
783
  if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && config.backend !== "hono") return validationErr$1(`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.`);
784
784
  if (providedFlags.has("backend") && config.backend && config.backend !== "hono" && config.runtime === "workers") return validationErr$1(`Backend '${config.backend}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Hono backend. Please use '--backend hono' or choose a different runtime.`);
785
785
  if (providedFlags.has("runtime") && options.runtime === "workers" && config.database === "mongodb") return validationErr$1("Cloudflare Workers runtime (--runtime workers) is not compatible with MongoDB database. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle or Prisma ORM. Please use a different database or runtime.");
786
- if (providedFlags.has("runtime") && options.runtime === "workers" && config.dbSetup === "docker") return validationErr$1("Cloudflare Workers runtime (--runtime workers) is not compatible with Docker setup. 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.");
787
786
  if (providedFlags.has("database") && config.database === "mongodb" && config.runtime === "workers") return validationErr$1("MongoDB database is not compatible with Cloudflare Workers runtime. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle or Prisma ORM. Please use a different database or runtime.");
788
787
  return Result.ok(void 0);
789
788
  }
@@ -3212,7 +3211,11 @@ async function addHandlerInternal(input) {
3212
3211
  async function getApiChoice(Api, frontend, backend) {
3213
3212
  if (backend === "convex" || backend === "none") return "none";
3214
3213
  const allowed = allowedApisForFrontends(frontend ?? []);
3215
- if (Api) return allowed.includes(Api) ? Api : allowed[0];
3214
+ if (Api) {
3215
+ const compat = validateApiFrontendCompatibility(Api, frontend ?? []);
3216
+ if (compat.isErr()) throw compat.error;
3217
+ return Api;
3218
+ }
3216
3219
  const apiOptions = allowed.map((a) => a === "trpc" ? {
3217
3220
  value: "trpc",
3218
3221
  label: "tRPC",
@@ -3701,6 +3704,209 @@ async function navigableGroup(prompts, opts) {
3701
3704
  return results;
3702
3705
  }
3703
3706
  //#endregion
3707
+ //#region src/utils/config-validation.ts
3708
+ function validationErr(message) {
3709
+ return Result.err(new ValidationError({ message }));
3710
+ }
3711
+ function hasResolvedWorkersD1Target(config) {
3712
+ return config.backend === "hono" && config.runtime === "workers" && config.serverDeploy === "cloudflare";
3713
+ }
3714
+ function hasResolvedSelfCloudflareD1Target(config) {
3715
+ return config.backend === "self" && config.runtime === "none" && config.webDeploy === "cloudflare";
3716
+ }
3717
+ function canResolveWorkersD1Target(config) {
3718
+ return (config.backend === void 0 || config.backend === "hono") && (config.runtime === void 0 || config.runtime === "workers") && (config.serverDeploy === void 0 || config.serverDeploy === "cloudflare");
3719
+ }
3720
+ function canResolveSelfCloudflareD1Target(config) {
3721
+ return (config.backend === void 0 || config.backend === "self") && (config.runtime === void 0 || config.runtime === "none") && (config.webDeploy === void 0 || config.webDeploy === "cloudflare");
3722
+ }
3723
+ /**
3724
+ * Pure ORM + database compatibility check. Used by the flag-path
3725
+ * validator below and by the orm prompt directly (for flag+prompt
3726
+ * combos where one value came from a flag and the other from a
3727
+ * prompt).
3728
+ */
3729
+ function validateOrmDatabaseCompat(orm, database) {
3730
+ if (orm === "mongoose" && database && database !== "mongodb") return validationErr("Mongoose ORM requires MongoDB database. Please use '--database mongodb' or choose a different ORM.");
3731
+ if (orm === "drizzle" && database === "mongodb") return validationErr("Drizzle ORM does not support MongoDB. Please use '--orm mongoose' or '--orm prisma' or choose a different database.");
3732
+ if (database === "mongodb" && orm && orm !== "mongoose" && orm !== "prisma" && orm !== "none") return validationErr("MongoDB database requires Mongoose or Prisma ORM. Please use '--orm mongoose' or '--orm prisma' or choose a different database.");
3733
+ if (database && database !== "none" && orm === "none") return validationErr("Database selection requires an ORM. Please choose '--orm drizzle', '--orm prisma', or '--orm mongoose'.");
3734
+ if (orm && orm !== "none" && database === "none") return validationErr("ORM selection requires a database. Please choose a database or set '--orm none'.");
3735
+ return Result.ok(void 0);
3736
+ }
3737
+ function validateDatabaseOrmAuth(cfg, flags) {
3738
+ const has = (k) => flags ? flags.has(k) : true;
3739
+ if (!has("orm") || !has("database")) return Result.ok(void 0);
3740
+ return validateOrmDatabaseCompat(cfg.orm, cfg.database);
3741
+ }
3742
+ function validateDatabaseSetup(config, providedFlags) {
3743
+ const { dbSetup, database, runtime } = config;
3744
+ if (providedFlags.has("dbSetup") && providedFlags.has("database") && dbSetup && dbSetup !== "none" && database === "none") return validationErr("Database setup requires a database. Please choose a database or set '--db-setup none'.");
3745
+ const setupValidations = {
3746
+ turso: {
3747
+ database: "sqlite",
3748
+ errorMessage: "Turso setup requires SQLite database. Please use '--database sqlite' or choose a different setup."
3749
+ },
3750
+ neon: {
3751
+ database: "postgres",
3752
+ errorMessage: "Neon setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup."
3753
+ },
3754
+ "prisma-postgres": {
3755
+ database: "postgres",
3756
+ errorMessage: "Prisma PostgreSQL setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup."
3757
+ },
3758
+ planetscale: { errorMessage: "PlanetScale setup requires PostgreSQL or MySQL database. Please use '--database postgres' or '--database mysql' or choose a different setup." },
3759
+ "mongodb-atlas": {
3760
+ database: "mongodb",
3761
+ errorMessage: "MongoDB Atlas setup requires MongoDB database. Please use '--database mongodb' or choose a different setup."
3762
+ },
3763
+ supabase: {
3764
+ database: "postgres",
3765
+ errorMessage: "Supabase setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup."
3766
+ },
3767
+ d1: {
3768
+ database: "sqlite",
3769
+ errorMessage: "Cloudflare D1 setup requires SQLite database."
3770
+ },
3771
+ docker: { errorMessage: "Docker setup is not compatible with SQLite database or Cloudflare Workers runtime." },
3772
+ none: { errorMessage: "" }
3773
+ };
3774
+ if (dbSetup && dbSetup !== "none") {
3775
+ const validation = setupValidations[dbSetup];
3776
+ if (dbSetup === "planetscale") {
3777
+ if (database !== "postgres" && database !== "mysql") return validationErr(validation.errorMessage);
3778
+ } else if (validation.database && database !== validation.database) return validationErr(validation.errorMessage);
3779
+ if (validation.runtime && runtime !== validation.runtime) return validationErr(validation.errorMessage);
3780
+ if (dbSetup === "d1") {
3781
+ const isWorkersTarget = hasResolvedWorkersD1Target(config);
3782
+ const isSelfCloudflareTarget = hasResolvedSelfCloudflareD1Target(config);
3783
+ const canResolveWorkersTarget = canResolveWorkersD1Target(config);
3784
+ const canResolveSelfCloudflareTarget = canResolveSelfCloudflareD1Target(config);
3785
+ 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.");
3786
+ }
3787
+ if (dbSetup === "docker") {
3788
+ 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.");
3789
+ 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.");
3790
+ }
3791
+ }
3792
+ return Result.ok(void 0);
3793
+ }
3794
+ function validateConvexConstraints(config, providedFlags) {
3795
+ const { backend } = config;
3796
+ if (backend !== "convex") return Result.ok(void 0);
3797
+ const has = (k) => providedFlags.has(k);
3798
+ if (has("runtime") && config.runtime !== "none") return validationErr("Convex backend requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.");
3799
+ if (has("database") && config.database !== "none") return validationErr("Convex backend requires '--database none'. Please remove the --database flag or set it to 'none'.");
3800
+ if (has("orm") && config.orm !== "none") return validationErr("Convex backend requires '--orm none'. Please remove the --orm flag or set it to 'none'.");
3801
+ if (has("api") && config.api !== "none") return validationErr("Convex backend requires '--api none'. Please remove the --api flag or set it to 'none'.");
3802
+ 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'.");
3803
+ 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'.");
3804
+ if (has("auth") && config.auth === "better-auth") {
3805
+ const incompatibleFrontends = config.frontend?.filter((f) => CONVEX_BETTER_AUTH_INCOMPATIBLE_FRONTENDS.includes(f)) ?? [];
3806
+ const hasSupportedFrontend = supportsConvexBetterAuth(config.frontend);
3807
+ 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.`);
3808
+ if (!hasSupportedFrontend) return validationErr(`Better Auth with '--backend convex' requires a supported frontend (${CONVEX_BETTER_AUTH_SUPPORTED_FRONTENDS.join(", ")}).`);
3809
+ }
3810
+ return Result.ok(void 0);
3811
+ }
3812
+ function validateBackendNoneConstraints(config, providedFlags) {
3813
+ const { backend } = config;
3814
+ if (backend !== "none") return Result.ok(void 0);
3815
+ const has = (k) => providedFlags.has(k);
3816
+ if (has("runtime") && config.runtime !== "none") return validationErr("Backend 'none' requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.");
3817
+ if (has("database") && config.database !== "none") return validationErr("Backend 'none' requires '--database none'. Please remove the --database flag or set it to 'none'.");
3818
+ if (has("orm") && config.orm !== "none") return validationErr("Backend 'none' requires '--orm none'. Please remove the --orm flag or set it to 'none'.");
3819
+ if (has("api") && config.api !== "none") return validationErr("Backend 'none' requires '--api none'. Please remove the --api flag or set it to 'none'.");
3820
+ if (has("auth") && config.auth !== "none") return validationErr("Backend 'none' requires '--auth none'. Please remove the --auth flag or set it to 'none'.");
3821
+ if (has("payments") && config.payments !== "none") return validationErr("Backend 'none' requires '--payments none'. Please remove the --payments flag or set it to 'none'.");
3822
+ if (has("dbSetup") && config.dbSetup !== "none") return validationErr("Backend 'none' requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.");
3823
+ if (has("serverDeploy") && config.serverDeploy !== "none") return validationErr("Backend 'none' requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.");
3824
+ return Result.ok(void 0);
3825
+ }
3826
+ function validateSelfBackendConstraints(config, providedFlags) {
3827
+ const { backend } = config;
3828
+ if (backend !== "self") return Result.ok(void 0);
3829
+ const has = (k) => providedFlags.has(k);
3830
+ if (has("runtime") && config.runtime !== "none") return validationErr("Backend 'self' (fullstack) requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.");
3831
+ return Result.ok(void 0);
3832
+ }
3833
+ function validateBackendConstraints(config, providedFlags, options) {
3834
+ const { backend } = config;
3835
+ if (config.auth === "clerk" && config.frontend) {
3836
+ const incompatibleFrontends = config.frontend.filter((f) => [
3837
+ "nuxt",
3838
+ "svelte",
3839
+ "solid",
3840
+ "astro"
3841
+ ].includes(f));
3842
+ if (incompatibleFrontends.length > 0) return validationErr(`Clerk authentication is not compatible with the following frontends: ${incompatibleFrontends.join(", ")}. Please choose a different frontend or auth provider.`);
3843
+ }
3844
+ if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none" && backend !== "self") {
3845
+ if (providedFlags.has("runtime") && options.runtime === "none") return validationErr("'--runtime none' is only supported with '--backend convex', '--backend none', or '--backend self'. Please choose 'bun', 'node', or remove the --runtime flag.");
3846
+ }
3847
+ if (backend === "convex" && providedFlags.has("frontend") && options.frontend) {
3848
+ const incompatibleFrontends = options.frontend.filter((f) => f === "solid" || f === "astro");
3849
+ if (incompatibleFrontends.length > 0) return validationErr(`The following frontends are not compatible with '--backend convex': ${incompatibleFrontends.join(", ")}. Please choose a different frontend or backend.`);
3850
+ }
3851
+ return Result.ok(void 0);
3852
+ }
3853
+ function validateFrontendConstraints(config, providedFlags) {
3854
+ const { frontend } = config;
3855
+ if (frontend && frontend.length > 0) {
3856
+ const singleWebNativeResult = ensureSingleWebAndNative(frontend);
3857
+ if (singleWebNativeResult.isErr()) return singleWebNativeResult;
3858
+ if (providedFlags.has("api") && providedFlags.has("frontend") && config.api) {
3859
+ const apiResult = validateApiFrontendCompatibility(config.api, frontend);
3860
+ if (apiResult.isErr()) return apiResult;
3861
+ }
3862
+ }
3863
+ const hasWebFrontendFlag = (frontend ?? []).some((f) => isWebFrontend(f));
3864
+ const webDeployResult = validateWebDeployRequiresWebFrontend(config.webDeploy, hasWebFrontendFlag);
3865
+ if (webDeployResult.isErr()) return webDeployResult;
3866
+ return Result.ok(void 0);
3867
+ }
3868
+ function validateApiConstraints(config, options) {
3869
+ if (config.api === "none") {
3870
+ if (options.examples?.includes("todo") && options.backend !== "convex" && options.backend !== "none") return validationErr("Cannot use '--examples todo' when '--api' is set to 'none'. The todo example requires an API layer. Please remove 'todo' from --examples or choose an API type.");
3871
+ }
3872
+ return Result.ok(void 0);
3873
+ }
3874
+ function validateFullConfig(config, providedFlags, options) {
3875
+ return Result.gen(function* () {
3876
+ yield* validateDatabaseOrmAuth(config, providedFlags);
3877
+ yield* validateDatabaseSetup(config, providedFlags);
3878
+ yield* validateConvexConstraints(config, providedFlags);
3879
+ yield* validateBackendNoneConstraints(config, providedFlags);
3880
+ yield* validateSelfBackendConstraints(config, providedFlags);
3881
+ yield* validateBackendConstraints(config, providedFlags, options);
3882
+ yield* validateFrontendConstraints(config, providedFlags);
3883
+ yield* validateApiConstraints(config, options);
3884
+ yield* validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
3885
+ yield* validateSelfBackendCompatibility(providedFlags, options, config);
3886
+ yield* validateWorkersCompatibility(providedFlags, options, config);
3887
+ if (config.runtime === "workers" && config.serverDeploy === "none") yield* validationErr("Cloudflare Workers runtime requires a server deployment. Please choose 'cloudflare' for --server-deploy.");
3888
+ if (providedFlags.has("serverDeploy") && config.serverDeploy === "cloudflare" && config.runtime !== "workers") yield* validationErr(`Server deployment '${config.serverDeploy}' requires '--runtime workers'. Please use '--runtime workers' or choose a different server deployment.`);
3889
+ if (config.addons && config.addons.length > 0) {
3890
+ yield* validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
3891
+ config.addons = [...new Set(config.addons)];
3892
+ }
3893
+ yield* validateExamplesCompatibility(config.examples ?? [], config.backend, config.database, config.frontend ?? [], config.api);
3894
+ yield* validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend ?? []);
3895
+ return Result.ok(void 0);
3896
+ });
3897
+ }
3898
+ function validateConfigForProgrammaticUse(config) {
3899
+ return Result.gen(function* () {
3900
+ yield* validateDatabaseOrmAuth(config);
3901
+ if (config.frontend && config.frontend.length > 0) yield* ensureSingleWebAndNative(config.frontend);
3902
+ yield* validateApiFrontendCompatibility(config.api, config.frontend);
3903
+ yield* validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
3904
+ if (config.addons && config.addons.length > 0) yield* validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
3905
+ yield* validateExamplesCompatibility(config.examples ?? [], config.backend, config.database, config.frontend ?? [], config.api);
3906
+ return Result.ok(void 0);
3907
+ });
3908
+ }
3909
+ //#endregion
3704
3910
  //#region src/prompts/orm.ts
3705
3911
  const ormOptions = {
3706
3912
  prisma: {
@@ -3722,7 +3928,11 @@ const ormOptions = {
3722
3928
  async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
3723
3929
  if (backend === "convex") return "none";
3724
3930
  if (!hasDatabase) return "none";
3725
- if (orm !== void 0) return orm;
3931
+ if (orm !== void 0) {
3932
+ const compat = validateOrmDatabaseCompat(orm, database);
3933
+ if (compat.isErr()) throw compat.error;
3934
+ return orm;
3935
+ }
3726
3936
  const response = await navigableSelect({
3727
3937
  message: "Select ORM",
3728
3938
  options: database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [ormOptions.drizzle, ormOptions.prisma],
@@ -4302,201 +4512,6 @@ function validateArrayOptions(options) {
4302
4512
  return Result.ok(void 0);
4303
4513
  }
4304
4514
  //#endregion
4305
- //#region src/utils/config-validation.ts
4306
- function validationErr(message) {
4307
- return Result.err(new ValidationError({ message }));
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
- }
4321
- function validateDatabaseOrmAuth(cfg, flags) {
4322
- const db = cfg.database;
4323
- const orm = cfg.orm;
4324
- const has = (k) => flags ? flags.has(k) : true;
4325
- if (has("orm") && has("database") && orm === "mongoose" && db !== "mongodb") return validationErr("Mongoose ORM requires MongoDB database. Please use '--database mongodb' or choose a different ORM.");
4326
- if (has("orm") && has("database") && orm === "drizzle" && db === "mongodb") return validationErr("Drizzle ORM does not support MongoDB. Please use '--orm mongoose' or '--orm prisma' or choose a different database.");
4327
- if (has("database") && has("orm") && db === "mongodb" && orm && orm !== "mongoose" && orm !== "prisma" && orm !== "none") return validationErr("MongoDB database requires Mongoose or Prisma ORM. Please use '--orm mongoose' or '--orm prisma' or choose a different database.");
4328
- if (has("database") && has("orm") && db && db !== "none" && orm === "none") return validationErr("Database selection requires an ORM. Please choose '--orm drizzle', '--orm prisma', or '--orm mongoose'.");
4329
- if (has("orm") && has("database") && orm && orm !== "none" && db === "none") return validationErr("ORM selection requires a database. Please choose a database or set '--orm none'.");
4330
- return Result.ok(void 0);
4331
- }
4332
- function validateDatabaseSetup(config, providedFlags) {
4333
- const { dbSetup, database, runtime } = config;
4334
- if (providedFlags.has("dbSetup") && providedFlags.has("database") && dbSetup && dbSetup !== "none" && database === "none") return validationErr("Database setup requires a database. Please choose a database or set '--db-setup none'.");
4335
- const setupValidations = {
4336
- turso: {
4337
- database: "sqlite",
4338
- errorMessage: "Turso setup requires SQLite database. Please use '--database sqlite' or choose a different setup."
4339
- },
4340
- neon: {
4341
- database: "postgres",
4342
- errorMessage: "Neon setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup."
4343
- },
4344
- "prisma-postgres": {
4345
- database: "postgres",
4346
- errorMessage: "Prisma PostgreSQL setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup."
4347
- },
4348
- planetscale: { errorMessage: "PlanetScale setup requires PostgreSQL or MySQL database. Please use '--database postgres' or '--database mysql' or choose a different setup." },
4349
- "mongodb-atlas": {
4350
- database: "mongodb",
4351
- errorMessage: "MongoDB Atlas setup requires MongoDB database. Please use '--database mongodb' or choose a different setup."
4352
- },
4353
- supabase: {
4354
- database: "postgres",
4355
- errorMessage: "Supabase setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup."
4356
- },
4357
- d1: {
4358
- database: "sqlite",
4359
- errorMessage: "Cloudflare D1 setup requires SQLite database."
4360
- },
4361
- docker: { errorMessage: "Docker setup is not compatible with SQLite database or Cloudflare Workers runtime." },
4362
- none: { errorMessage: "" }
4363
- };
4364
- if (dbSetup && dbSetup !== "none") {
4365
- const validation = setupValidations[dbSetup];
4366
- if (dbSetup === "planetscale") {
4367
- if (database !== "postgres" && database !== "mysql") return validationErr(validation.errorMessage);
4368
- } else if (validation.database && database !== validation.database) return validationErr(validation.errorMessage);
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
- }
4377
- if (dbSetup === "docker") {
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.");
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.");
4380
- }
4381
- }
4382
- return Result.ok(void 0);
4383
- }
4384
- function validateConvexConstraints(config, providedFlags) {
4385
- const { backend } = config;
4386
- if (backend !== "convex") return Result.ok(void 0);
4387
- const has = (k) => providedFlags.has(k);
4388
- if (has("runtime") && config.runtime !== "none") return validationErr("Convex backend requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.");
4389
- if (has("database") && config.database !== "none") return validationErr("Convex backend requires '--database none'. Please remove the --database flag or set it to 'none'.");
4390
- if (has("orm") && config.orm !== "none") return validationErr("Convex backend requires '--orm none'. Please remove the --orm flag or set it to 'none'.");
4391
- if (has("api") && config.api !== "none") return validationErr("Convex backend requires '--api none'. Please remove the --api flag or set it to 'none'.");
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'.");
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'.");
4394
- if (has("auth") && config.auth === "better-auth") {
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(", ")}).`);
4399
- }
4400
- return Result.ok(void 0);
4401
- }
4402
- function validateBackendNoneConstraints(config, providedFlags) {
4403
- const { backend } = config;
4404
- if (backend !== "none") return Result.ok(void 0);
4405
- const has = (k) => providedFlags.has(k);
4406
- if (has("runtime") && config.runtime !== "none") return validationErr("Backend 'none' requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.");
4407
- if (has("database") && config.database !== "none") return validationErr("Backend 'none' requires '--database none'. Please remove the --database flag or set it to 'none'.");
4408
- if (has("orm") && config.orm !== "none") return validationErr("Backend 'none' requires '--orm none'. Please remove the --orm flag or set it to 'none'.");
4409
- if (has("api") && config.api !== "none") return validationErr("Backend 'none' requires '--api none'. Please remove the --api flag or set it to 'none'.");
4410
- if (has("auth") && config.auth !== "none") return validationErr("Backend 'none' requires '--auth none'. Please remove the --auth flag or set it to 'none'.");
4411
- if (has("payments") && config.payments !== "none") return validationErr("Backend 'none' requires '--payments none'. Please remove the --payments flag or set it to 'none'.");
4412
- if (has("dbSetup") && config.dbSetup !== "none") return validationErr("Backend 'none' requires '--db-setup none'. Please remove the --db-setup flag or set it to 'none'.");
4413
- if (has("serverDeploy") && config.serverDeploy !== "none") return validationErr("Backend 'none' requires '--server-deploy none'. Please remove the --server-deploy flag or set it to 'none'.");
4414
- return Result.ok(void 0);
4415
- }
4416
- function validateSelfBackendConstraints(config, providedFlags) {
4417
- const { backend } = config;
4418
- if (backend !== "self") return Result.ok(void 0);
4419
- const has = (k) => providedFlags.has(k);
4420
- if (has("runtime") && config.runtime !== "none") return validationErr("Backend 'self' (fullstack) requires '--runtime none'. Please remove the --runtime flag or set it to 'none'.");
4421
- return Result.ok(void 0);
4422
- }
4423
- function validateBackendConstraints(config, providedFlags, options) {
4424
- const { backend } = config;
4425
- if (config.auth === "clerk" && config.frontend) {
4426
- const incompatibleFrontends = config.frontend.filter((f) => [
4427
- "nuxt",
4428
- "svelte",
4429
- "solid",
4430
- "astro"
4431
- ].includes(f));
4432
- if (incompatibleFrontends.length > 0) return validationErr(`Clerk authentication is not compatible with the following frontends: ${incompatibleFrontends.join(", ")}. Please choose a different frontend or auth provider.`);
4433
- }
4434
- if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none" && backend !== "self") {
4435
- if (providedFlags.has("runtime") && options.runtime === "none") return validationErr("'--runtime none' is only supported with '--backend convex', '--backend none', or '--backend self'. Please choose 'bun', 'node', or remove the --runtime flag.");
4436
- }
4437
- if (backend === "convex" && providedFlags.has("frontend") && options.frontend) {
4438
- const incompatibleFrontends = options.frontend.filter((f) => f === "solid" || f === "astro");
4439
- if (incompatibleFrontends.length > 0) return validationErr(`The following frontends are not compatible with '--backend convex': ${incompatibleFrontends.join(", ")}. Please choose a different frontend or backend.`);
4440
- }
4441
- return Result.ok(void 0);
4442
- }
4443
- function validateFrontendConstraints(config, providedFlags) {
4444
- const { frontend } = config;
4445
- if (frontend && frontend.length > 0) {
4446
- const singleWebNativeResult = ensureSingleWebAndNative(frontend);
4447
- if (singleWebNativeResult.isErr()) return singleWebNativeResult;
4448
- if (providedFlags.has("api") && providedFlags.has("frontend") && config.api) {
4449
- const apiResult = validateApiFrontendCompatibility(config.api, frontend);
4450
- if (apiResult.isErr()) return apiResult;
4451
- }
4452
- }
4453
- const hasWebFrontendFlag = (frontend ?? []).some((f) => isWebFrontend(f));
4454
- const webDeployResult = validateWebDeployRequiresWebFrontend(config.webDeploy, hasWebFrontendFlag);
4455
- if (webDeployResult.isErr()) return webDeployResult;
4456
- return Result.ok(void 0);
4457
- }
4458
- function validateApiConstraints(config, options) {
4459
- if (config.api === "none") {
4460
- if (options.examples?.includes("todo") && options.backend !== "convex" && options.backend !== "none") return validationErr("Cannot use '--examples todo' when '--api' is set to 'none'. The todo example requires an API layer. Please remove 'todo' from --examples or choose an API type.");
4461
- }
4462
- return Result.ok(void 0);
4463
- }
4464
- function validateFullConfig(config, providedFlags, options) {
4465
- return Result.gen(function* () {
4466
- yield* validateDatabaseOrmAuth(config, providedFlags);
4467
- yield* validateDatabaseSetup(config, providedFlags);
4468
- yield* validateConvexConstraints(config, providedFlags);
4469
- yield* validateBackendNoneConstraints(config, providedFlags);
4470
- yield* validateSelfBackendConstraints(config, providedFlags);
4471
- yield* validateBackendConstraints(config, providedFlags, options);
4472
- yield* validateFrontendConstraints(config, providedFlags);
4473
- yield* validateApiConstraints(config, options);
4474
- yield* validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
4475
- yield* validateSelfBackendCompatibility(providedFlags, options, config);
4476
- yield* validateWorkersCompatibility(providedFlags, options, config);
4477
- if (config.runtime === "workers" && config.serverDeploy === "none") yield* validationErr("Cloudflare Workers runtime requires a server deployment. Please choose 'cloudflare' for --server-deploy.");
4478
- if (providedFlags.has("serverDeploy") && config.serverDeploy === "cloudflare" && config.runtime !== "workers") yield* validationErr(`Server deployment '${config.serverDeploy}' requires '--runtime workers'. Please use '--runtime workers' or choose a different server deployment.`);
4479
- if (config.addons && config.addons.length > 0) {
4480
- yield* validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
4481
- config.addons = [...new Set(config.addons)];
4482
- }
4483
- yield* validateExamplesCompatibility(config.examples ?? [], config.backend, config.database, config.frontend ?? [], config.api);
4484
- yield* validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend ?? []);
4485
- return Result.ok(void 0);
4486
- });
4487
- }
4488
- function validateConfigForProgrammaticUse(config) {
4489
- return Result.gen(function* () {
4490
- yield* validateDatabaseOrmAuth(config);
4491
- if (config.frontend && config.frontend.length > 0) yield* ensureSingleWebAndNative(config.frontend);
4492
- yield* validateApiFrontendCompatibility(config.api, config.frontend);
4493
- yield* validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
4494
- if (config.addons && config.addons.length > 0) yield* validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
4495
- yield* validateExamplesCompatibility(config.examples ?? [], config.backend, config.database, config.frontend ?? [], config.api);
4496
- return Result.ok(void 0);
4497
- });
4498
- }
4499
- //#endregion
4500
4515
  //#region src/validation.ts
4501
4516
  const CORE_STACK_FLAGS = new Set([
4502
4517
  "database",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "3.27.0",
3
+ "version": "3.27.1",
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.27.0",
74
- "@better-t-stack/types": "^3.27.0",
73
+ "@better-t-stack/template-generator": "^3.27.1",
74
+ "@better-t-stack/types": "^3.27.1",
75
75
  "@clack/core": "^1.1.0",
76
76
  "@clack/prompts": "^1.1.0",
77
77
  "@modelcontextprotocol/sdk": "1.27.1",