create-better-t-stack 2.37.0 → 2.38.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.
Files changed (111) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.d.ts +11 -5
  3. package/dist/index.js +1 -1
  4. package/dist/{src-CGVrE3la.js → src-DlPots7G.js} +260 -104
  5. package/package.json +1 -1
  6. package/templates/addons/ruler/.ruler/bts.md.hbs +2 -2
  7. package/templates/api/orpc/native/utils/orpc.ts.hbs +2 -2
  8. package/templates/api/orpc/server/base/src/lib/context.ts.hbs +10 -10
  9. package/templates/api/orpc/server/base/src/lib/orpc.ts.hbs +1 -1
  10. package/templates/api/orpc/server/next/src/app/rpc/[...all]/route.ts.hbs +2 -2
  11. package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +1 -1
  12. package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +11 -1
  13. package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +1 -1
  14. package/templates/api/orpc/web/svelte/src/lib/orpc.ts.hbs +1 -1
  15. package/templates/api/trpc/native/utils/trpc.ts.hbs +2 -2
  16. package/templates/api/trpc/server/base/src/lib/context.ts.hbs +10 -10
  17. package/templates/api/trpc/server/base/src/lib/trpc.ts.hbs +1 -1
  18. package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +2 -2
  19. package/templates/auth/clerk/convex/backend/convex/auth.config.ts.hbs +12 -0
  20. package/templates/auth/clerk/convex/backend/convex/privateData.ts.hbs +16 -0
  21. package/templates/auth/clerk/convex/native/base/app/(auth)/_layout.tsx.hbs +12 -0
  22. package/templates/auth/clerk/convex/native/base/app/(auth)/sign-in.tsx.hbs +67 -0
  23. package/templates/auth/clerk/convex/native/base/app/(auth)/sign-out.tsx.hbs +110 -0
  24. package/templates/auth/clerk/convex/native/base/components/sign-out-button.tsx.hbs +27 -0
  25. package/templates/auth/clerk/convex/web/react/next/src/app/dashboard/page.tsx.hbs +29 -0
  26. package/templates/auth/clerk/convex/web/react/next/src/middleware.ts.hbs +12 -0
  27. package/templates/auth/clerk/convex/web/react/react-router/src/routes/dashboard.tsx.hbs +32 -0
  28. package/templates/auth/clerk/convex/web/react/tanstack-router/src/routes/dashboard.tsx.hbs +37 -0
  29. package/templates/auth/clerk/convex/web/react/tanstack-start/src/routes/dashboard.tsx.hbs +37 -0
  30. package/templates/auth/clerk/convex/web/react/tanstack-start/src/server.ts.hbs +18 -0
  31. package/templates/backend/convex/packages/backend/package.json.hbs +1 -0
  32. package/templates/backend/server/elysia/src/index.ts.hbs +3 -3
  33. package/templates/backend/server/express/src/index.ts.hbs +6 -6
  34. package/templates/backend/server/fastify/src/index.ts.hbs +4 -4
  35. package/templates/backend/server/hono/src/index.ts.hbs +4 -4
  36. package/templates/backend/server/server-base/src/routers/index.ts.hbs +4 -4
  37. package/templates/deploy/alchemy/alchemy.run.ts.hbs +2 -2
  38. package/templates/frontend/native/nativewind/app/(drawer)/index.tsx.hbs +35 -3
  39. package/templates/frontend/native/nativewind/app/_layout.tsx.hbs +28 -0
  40. package/templates/frontend/native/nativewind/package.json.hbs +1 -0
  41. package/templates/frontend/native/unistyles/app/(drawer)/index.tsx.hbs +32 -0
  42. package/templates/frontend/native/unistyles/app/_layout.tsx.hbs +35 -0
  43. package/templates/frontend/native/unistyles/package.json.hbs +1 -0
  44. package/templates/frontend/nuxt/app/components/Header.vue.hbs +3 -3
  45. package/templates/frontend/react/next/src/app/layout.tsx.hbs +23 -15
  46. package/templates/frontend/react/next/src/components/providers.tsx.hbs +12 -0
  47. package/templates/frontend/react/react-router/src/root.tsx.hbs +28 -1
  48. package/templates/frontend/react/tanstack-router/src/main.tsx.hbs +19 -1
  49. package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +8 -4
  50. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +52 -5
  51. package/templates/frontend/react/web-base/src/components/header.tsx.hbs +3 -3
  52. package/templates/frontend/solid/src/components/header.tsx.hbs +3 -3
  53. package/templates/frontend/svelte/src/components/Header.svelte.hbs +3 -3
  54. /package/templates/auth/{native → better-auth/native}/native-base/lib/auth-client.ts.hbs +0 -0
  55. /package/templates/auth/{native → better-auth/native}/nativewind/app/(drawer)/index.tsx.hbs +0 -0
  56. /package/templates/auth/{native → better-auth/native}/nativewind/components/sign-in.tsx.hbs +0 -0
  57. /package/templates/auth/{native → better-auth/native}/nativewind/components/sign-up.tsx.hbs +0 -0
  58. /package/templates/auth/{native → better-auth/native}/unistyles/app/(drawer)/index.tsx.hbs +0 -0
  59. /package/templates/auth/{native → better-auth/native}/unistyles/components/sign-in.tsx.hbs +0 -0
  60. /package/templates/auth/{native → better-auth/native}/unistyles/components/sign-up.tsx.hbs +0 -0
  61. /package/templates/auth/{server → better-auth/server}/base/src/lib/auth.ts.hbs +0 -0
  62. /package/templates/auth/{server → better-auth/server}/db/drizzle/mysql/src/db/schema/auth.ts +0 -0
  63. /package/templates/auth/{server → better-auth/server}/db/drizzle/postgres/src/db/schema/auth.ts +0 -0
  64. /package/templates/auth/{server → better-auth/server}/db/drizzle/sqlite/src/db/schema/auth.ts +0 -0
  65. /package/templates/auth/{server → better-auth/server}/db/mongoose/mongodb/src/db/models/auth.model.ts +0 -0
  66. /package/templates/auth/{server → better-auth/server}/db/prisma/mongodb/prisma/schema/auth.prisma +0 -0
  67. /package/templates/auth/{server → better-auth/server}/db/prisma/mysql/prisma/schema/auth.prisma +0 -0
  68. /package/templates/auth/{server → better-auth/server}/db/prisma/postgres/prisma/schema/auth.prisma +0 -0
  69. /package/templates/auth/{server → better-auth/server}/db/prisma/sqlite/prisma/schema/auth.prisma +0 -0
  70. /package/templates/auth/{server → better-auth/server}/next/src/app/api/auth/[...all]/route.ts +0 -0
  71. /package/templates/auth/{web → better-auth/web}/nuxt/app/components/SignInForm.vue +0 -0
  72. /package/templates/auth/{web → better-auth/web}/nuxt/app/components/SignUpForm.vue +0 -0
  73. /package/templates/auth/{web → better-auth/web}/nuxt/app/components/UserMenu.vue +0 -0
  74. /package/templates/auth/{web → better-auth/web}/nuxt/app/middleware/auth.ts +0 -0
  75. /package/templates/auth/{web → better-auth/web}/nuxt/app/pages/dashboard.vue.hbs +0 -0
  76. /package/templates/auth/{web → better-auth/web}/nuxt/app/pages/login.vue +0 -0
  77. /package/templates/auth/{web → better-auth/web}/nuxt/app/plugins/auth-client.ts +0 -0
  78. /package/templates/auth/{web → better-auth/web}/react/base/src/lib/auth-client.ts.hbs +0 -0
  79. /package/templates/auth/{web → better-auth/web}/react/next/src/app/dashboard/page.tsx.hbs +0 -0
  80. /package/templates/auth/{web → better-auth/web}/react/next/src/app/login/page.tsx +0 -0
  81. /package/templates/auth/{web → better-auth/web}/react/next/src/components/sign-in-form.tsx +0 -0
  82. /package/templates/auth/{web → better-auth/web}/react/next/src/components/sign-up-form.tsx +0 -0
  83. /package/templates/auth/{web → better-auth/web}/react/next/src/components/theme-provider.tsx +0 -0
  84. /package/templates/auth/{web → better-auth/web}/react/next/src/components/user-menu.tsx +0 -0
  85. /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/sign-in-form.tsx +0 -0
  86. /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/sign-up-form.tsx +0 -0
  87. /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/user-menu.tsx +0 -0
  88. /package/templates/auth/{web → better-auth/web}/react/react-router/src/routes/dashboard.tsx.hbs +0 -0
  89. /package/templates/auth/{web → better-auth/web}/react/react-router/src/routes/login.tsx +0 -0
  90. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/sign-in-form.tsx +0 -0
  91. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/sign-up-form.tsx +0 -0
  92. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/user-menu.tsx +0 -0
  93. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/routes/dashboard.tsx.hbs +0 -0
  94. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/routes/login.tsx +0 -0
  95. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/sign-in-form.tsx +0 -0
  96. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/sign-up-form.tsx +0 -0
  97. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/user-menu.tsx +0 -0
  98. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/routes/dashboard.tsx.hbs +0 -0
  99. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/routes/login.tsx +0 -0
  100. /package/templates/auth/{web → better-auth/web}/solid/src/components/sign-in-form.tsx +0 -0
  101. /package/templates/auth/{web → better-auth/web}/solid/src/components/sign-up-form.tsx +0 -0
  102. /package/templates/auth/{web → better-auth/web}/solid/src/components/user-menu.tsx.hbs +0 -0
  103. /package/templates/auth/{web → better-auth/web}/solid/src/lib/auth-client.ts +0 -0
  104. /package/templates/auth/{web → better-auth/web}/solid/src/routes/dashboard.tsx.hbs +0 -0
  105. /package/templates/auth/{web → better-auth/web}/solid/src/routes/login.tsx +0 -0
  106. /package/templates/auth/{web → better-auth/web}/svelte/src/components/SignInForm.svelte +0 -0
  107. /package/templates/auth/{web → better-auth/web}/svelte/src/components/SignUpForm.svelte +0 -0
  108. /package/templates/auth/{web → better-auth/web}/svelte/src/components/UserMenu.svelte +0 -0
  109. /package/templates/auth/{web → better-auth/web}/svelte/src/lib/auth-client.ts +0 -0
  110. /package/templates/auth/{web → better-auth/web}/svelte/src/routes/dashboard/+page.svelte.hbs +0 -0
  111. /package/templates/auth/{web → better-auth/web}/svelte/src/routes/login/+page.svelte +0 -0
@@ -35,7 +35,7 @@ const DEFAULT_CONFIG_BASE = {
35
35
  frontend: ["tanstack-router"],
36
36
  database: "sqlite",
37
37
  orm: "drizzle",
38
- auth: true,
38
+ auth: "better-auth",
39
39
  addons: ["turborepo"],
40
40
  examples: [],
41
41
  git: true,
@@ -61,6 +61,10 @@ const DEFAULT_CONFIG = getDefaultConfig();
61
61
  const dependencyVersionMap = {
62
62
  "better-auth": "^1.3.7",
63
63
  "@better-auth/expo": "^1.3.7",
64
+ "@clerk/nextjs": "^6.31.5",
65
+ "@clerk/clerk-react": "^5.45.0",
66
+ "@clerk/tanstack-react-start": "^0.23.1",
67
+ "@clerk/clerk-expo": "^2.14.25",
64
68
  "drizzle-orm": "^0.44.2",
65
69
  "drizzle-kit": "^0.31.2",
66
70
  "@libsql/client": "^0.15.9",
@@ -239,6 +243,11 @@ const APISchema = z.enum([
239
243
  "orpc",
240
244
  "none"
241
245
  ]).describe("API type");
246
+ const AuthSchema = z.enum([
247
+ "better-auth",
248
+ "clerk",
249
+ "none"
250
+ ]).describe("Authentication provider");
242
251
  const ProjectNameSchema = z.string().min(1, "Project name cannot be empty").max(255, "Project name must be less than 255 characters").refine((name) => name === "." || !name.startsWith("."), "Project name cannot start with a dot (except for '.')").refine((name) => name === "." || !name.startsWith("-"), "Project name cannot start with a dash").refine((name) => {
243
252
  const invalidChars = [
244
253
  "<",
@@ -495,7 +504,6 @@ function validateWorkersCompatibility(providedFlags, options, config) {
495
504
  }
496
505
  function coerceBackendPresets(config) {
497
506
  if (config.backend === "convex") {
498
- config.auth = false;
499
507
  config.database = "none";
500
508
  config.orm = "none";
501
509
  config.api = "none";
@@ -504,7 +512,7 @@ function coerceBackendPresets(config) {
504
512
  config.examples = ["todo"];
505
513
  }
506
514
  if (config.backend === "none") {
507
- config.auth = false;
515
+ config.auth = "none";
508
516
  config.database = "none";
509
517
  config.orm = "none";
510
518
  config.api = "none";
@@ -516,7 +524,7 @@ function coerceBackendPresets(config) {
516
524
  function incompatibleFlagsForBackend(backend, providedFlags, options) {
517
525
  const list = [];
518
526
  if (backend === "convex") {
519
- if (providedFlags.has("auth") && options.auth === true) list.push("--auth");
527
+ if (providedFlags.has("auth") && options.auth && options.auth !== "none" && options.auth !== "clerk") list.push(`--auth ${options.auth}`);
520
528
  if (providedFlags.has("database") && options.database !== "none") list.push(`--database ${options.database}`);
521
529
  if (providedFlags.has("orm") && options.orm !== "none") list.push(`--orm ${options.orm}`);
522
530
  if (providedFlags.has("api") && options.api !== "none") list.push(`--api ${options.api}`);
@@ -524,7 +532,7 @@ function incompatibleFlagsForBackend(backend, providedFlags, options) {
524
532
  if (providedFlags.has("dbSetup") && options.dbSetup !== "none") list.push(`--db-setup ${options.dbSetup}`);
525
533
  }
526
534
  if (backend === "none") {
527
- if (providedFlags.has("auth") && options.auth === true) list.push("--auth");
535
+ if (providedFlags.has("auth") && options.auth && options.auth !== "none") list.push(`--auth ${options.auth}`);
528
536
  if (providedFlags.has("database") && options.database !== "none") list.push(`--database ${options.database}`);
529
537
  if (providedFlags.has("orm") && options.orm !== "none") list.push(`--orm ${options.orm}`);
530
538
  if (providedFlags.has("api") && options.api !== "none") list.push(`--api ${options.api}`);
@@ -543,8 +551,16 @@ function validateApiFrontendCompatibility(api, frontends = []) {
543
551
  const includesSolid = frontends.includes("solid");
544
552
  if ((includesNuxt || includesSvelte || includesSolid) && api === "trpc") exitWithError(`tRPC API is not supported with '${includesNuxt ? "nuxt" : includesSvelte ? "svelte" : "solid"}' frontend. Please use --api orpc or --api none or remove '${includesNuxt ? "nuxt" : includesSvelte ? "svelte" : "solid"}' from --frontend.`);
545
553
  }
546
- function isFrontendAllowedWithBackend(frontend, backend) {
554
+ function isFrontendAllowedWithBackend(frontend, backend, auth) {
547
555
  if (backend === "convex" && frontend === "solid") return false;
556
+ if (auth === "clerk" && backend === "convex") {
557
+ const incompatibleFrontends = [
558
+ "nuxt",
559
+ "svelte",
560
+ "solid"
561
+ ];
562
+ if (incompatibleFrontends.includes(frontend)) return false;
563
+ }
548
564
  return true;
549
565
  }
550
566
  function allowedApisForFrontends(frontends = []) {
@@ -630,12 +646,41 @@ async function getApiChoice(Api, frontend, backend) {
630
646
 
631
647
  //#endregion
632
648
  //#region src/prompts/auth.ts
633
- async function getAuthChoice(auth, hasDatabase, backend) {
634
- if (backend === "convex") return false;
635
- if (!hasDatabase) return false;
649
+ async function getAuthChoice(auth, hasDatabase, backend, frontend) {
636
650
  if (auth !== void 0) return auth;
637
- const response = await confirm({
638
- message: "Add authentication with Better-Auth?",
651
+ if (backend === "convex") {
652
+ const unsupportedFrontends = frontend?.filter((f) => [
653
+ "nuxt",
654
+ "svelte",
655
+ "solid"
656
+ ].includes(f));
657
+ if (unsupportedFrontends && unsupportedFrontends.length > 0) return "none";
658
+ const response$1 = await select({
659
+ message: "Select authentication provider",
660
+ options: [{
661
+ value: "clerk",
662
+ label: "Clerk",
663
+ hint: "More than auth, Complete User Management"
664
+ }, {
665
+ value: "none",
666
+ label: "None"
667
+ }],
668
+ initialValue: "clerk"
669
+ });
670
+ if (isCancel(response$1)) return exitCancelled("Operation cancelled");
671
+ return response$1;
672
+ }
673
+ if (!hasDatabase) return "none";
674
+ const response = await select({
675
+ message: "Select authentication provider",
676
+ options: [{
677
+ value: "better-auth",
678
+ label: "Better-Auth",
679
+ hint: "comprehensive auth framework for TypeScript"
680
+ }, {
681
+ value: "none",
682
+ label: "None"
683
+ }],
639
684
  initialValue: DEFAULT_CONFIG.auth
640
685
  });
641
686
  if (isCancel(response)) return exitCancelled("Operation cancelled");
@@ -857,7 +902,7 @@ async function getExamplesChoice(examples, database, frontends, backend, api) {
857
902
 
858
903
  //#endregion
859
904
  //#region src/prompts/frontend.ts
860
- async function getFrontendChoice(frontendOptions, backend) {
905
+ async function getFrontendChoice(frontendOptions, backend, auth) {
861
906
  if (frontendOptions !== void 0) return frontendOptions;
862
907
  const frontendTypes = await multiselect({
863
908
  message: "Select project type",
@@ -913,7 +958,7 @@ async function getFrontendChoice(frontendOptions, backend) {
913
958
  hint: "SSR, Server Functions, API Routes and more with TanStack Router"
914
959
  }
915
960
  ];
916
- const webOptions = allWebOptions.filter((option) => isFrontendAllowedWithBackend(option.value, backend));
961
+ const webOptions = allWebOptions.filter((option) => isFrontendAllowedWithBackend(option.value, backend, auth));
917
962
  const webFramework = await select({
918
963
  message: "Choose web",
919
964
  options: webOptions,
@@ -1214,13 +1259,13 @@ async function getDeploymentToAdd(frontend, existingDeployment) {
1214
1259
  //#region src/prompts/config-prompts.ts
1215
1260
  async function gatherConfig(flags, projectName, projectDir, relativePath) {
1216
1261
  const result = await group({
1217
- frontend: () => getFrontendChoice(flags.frontend, flags.backend),
1262
+ frontend: () => getFrontendChoice(flags.frontend, flags.backend, flags.auth),
1218
1263
  backend: ({ results }) => getBackendFrameworkChoice(flags.backend, results.frontend),
1219
1264
  runtime: ({ results }) => getRuntimeChoice(flags.runtime, results.backend),
1220
1265
  database: ({ results }) => getDatabaseChoice(flags.database, results.backend, results.runtime),
1221
1266
  orm: ({ results }) => getORMChoice(flags.orm, results.database !== "none", results.database, results.backend, results.runtime),
1222
1267
  api: ({ results }) => getApiChoice(flags.api, results.frontend, results.backend),
1223
- auth: ({ results }) => getAuthChoice(flags.auth, results.database !== "none", results.backend),
1268
+ auth: ({ results }) => getAuthChoice(flags.auth, results.database !== "none", results.backend, results.frontend),
1224
1269
  addons: ({ results }) => getAddonsChoice(flags.addons, results.frontend),
1225
1270
  examples: ({ results }) => getExamplesChoice(flags.examples, results.database, results.frontend, results.backend, results.api),
1226
1271
  dbSetup: ({ results }) => getDBSetupChoice(results.database ?? "none", flags.dbSetup, results.orm, results.backend, results.runtime),
@@ -1235,7 +1280,6 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
1235
1280
  result.database = "none";
1236
1281
  result.orm = "none";
1237
1282
  result.api = "none";
1238
- result.auth = false;
1239
1283
  result.dbSetup = "none";
1240
1284
  result.examples = ["todo"];
1241
1285
  }
@@ -1244,7 +1288,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
1244
1288
  result.database = "none";
1245
1289
  result.orm = "none";
1246
1290
  result.api = "none";
1247
- result.auth = false;
1291
+ result.auth = "none";
1248
1292
  result.dbSetup = "none";
1249
1293
  result.examples = [];
1250
1294
  }
@@ -1400,10 +1444,7 @@ function displayConfig(config) {
1400
1444
  if (config.api !== void 0) configDisplay.push(`${pc.blue("API:")} ${String(config.api)}`);
1401
1445
  if (config.database !== void 0) configDisplay.push(`${pc.blue("Database:")} ${String(config.database)}`);
1402
1446
  if (config.orm !== void 0) configDisplay.push(`${pc.blue("ORM:")} ${String(config.orm)}`);
1403
- if (config.auth !== void 0) {
1404
- const authText = typeof config.auth === "boolean" ? config.auth ? "Yes" : "No" : String(config.auth);
1405
- configDisplay.push(`${pc.blue("Authentication:")} ${authText}`);
1406
- }
1447
+ if (config.auth !== void 0) configDisplay.push(`${pc.blue("Auth:")} ${String(config.auth)}`);
1407
1448
  if (config.addons !== void 0) {
1408
1449
  const addons = Array.isArray(config.addons) ? config.addons : [config.addons];
1409
1450
  const addonsText = addons.length > 0 && addons[0] !== void 0 ? addons.join(", ") : "none";
@@ -1441,7 +1482,7 @@ function generateReproducibleCommand(config) {
1441
1482
  flags.push(`--database ${config.database}`);
1442
1483
  flags.push(`--orm ${config.orm}`);
1443
1484
  flags.push(`--api ${config.api}`);
1444
- flags.push(config.auth ? "--auth" : "--no-auth");
1485
+ flags.push(`--auth ${config.auth}`);
1445
1486
  if (config.addons && config.addons.length > 0) flags.push(`--addons ${config.addons.join(" ")}`);
1446
1487
  else flags.push("--addons none");
1447
1488
  if (config.examples && config.examples.length > 0) flags.push(`--examples ${config.examples.join(" ")}`);
@@ -1649,8 +1690,8 @@ function validateDatabaseOrmAuth(cfg, flags) {
1649
1690
  if (has("database") && has("orm") && db === "mongodb" && orm && orm !== "mongoose" && orm !== "prisma" && orm !== "none") exitWithError("MongoDB database requires Mongoose or Prisma ORM. Please use '--orm mongoose' or '--orm prisma' or choose a different database.");
1650
1691
  if (has("database") && has("orm") && db && db !== "none" && orm === "none") exitWithError("Database selection requires an ORM. Please choose '--orm drizzle', '--orm prisma', or '--orm mongoose'.");
1651
1692
  if (has("orm") && has("database") && orm && orm !== "none" && db === "none") exitWithError("ORM selection requires a database. Please choose a database or set '--orm none'.");
1652
- if (has("auth") && has("database") && cfg.auth && db === "none") exitWithError("Authentication requires a database. Please choose a database or set '--no-auth'.");
1653
- if (cfg.auth && db === "none") exitWithError("Authentication requires a database. Please choose a database or set '--no-auth'.");
1693
+ if (has("auth") && has("database") && cfg.auth !== "none" && db === "none" && cfg.backend !== "convex") exitWithError("Authentication requires a database. Please choose a database or set '--auth none'.");
1694
+ if (cfg.auth !== "none" && db === "none" && cfg.backend !== "convex") exitWithError("Authentication requires a database. Please choose a database or set '--auth none'.");
1654
1695
  if (orm && orm !== "none" && db === "none") exitWithError("ORM selection requires a database. Please choose a database or set '--orm none'.");
1655
1696
  }
1656
1697
  function validateDatabaseSetup(config, providedFlags) {
@@ -1697,6 +1738,16 @@ function validateDatabaseSetup(config, providedFlags) {
1697
1738
  }
1698
1739
  function validateBackendConstraints(config, providedFlags, options) {
1699
1740
  const { backend } = config;
1741
+ 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.");
1742
+ if (backend === "convex" && config.auth === "clerk" && config.frontend) {
1743
+ const incompatibleFrontends = config.frontend.filter((f) => [
1744
+ "nuxt",
1745
+ "svelte",
1746
+ "solid"
1747
+ ].includes(f));
1748
+ if (incompatibleFrontends.length > 0) exitWithError(`Clerk authentication is not compatible with the following frontends: ${incompatibleFrontends.join(", ")}. Please choose a different frontend or auth provider.`);
1749
+ }
1750
+ if (backend === "convex" && config.auth === "better-auth" && providedFlags.has("auth")) exitWithError("Better-Auth is not compatible with the Convex backend. Please use '--auth clerk' or '--auth none'.");
1700
1751
  if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none") {
1701
1752
  if (providedFlags.has("runtime") && options.runtime === "none") exitWithError("'--runtime none' is only supported with '--backend convex' or '--backend none'. Please choose 'bun', 'node', or remove the --runtime flag.");
1702
1753
  }
@@ -2151,7 +2202,7 @@ async function setupDbOrmTemplates(projectDir, context) {
2151
2202
  if (await fs.pathExists(dbOrmSrcDir)) await processAndCopyFiles("**/*", dbOrmSrcDir, serverAppDir, context);
2152
2203
  }
2153
2204
  async function setupAuthTemplate(projectDir, context) {
2154
- if (context.backend === "convex" || !context.auth) return;
2205
+ if (!context.auth || context.auth === "none") return;
2155
2206
  const serverAppDir = path.join(projectDir, "apps/server");
2156
2207
  const webAppDir = path.join(projectDir, "apps/web");
2157
2208
  const nativeAppDir = path.join(projectDir, "apps/native");
@@ -2170,27 +2221,61 @@ async function setupAuthTemplate(projectDir, context) {
2170
2221
  const hasNativeWind = context.frontend.includes("native-nativewind");
2171
2222
  const hasUnistyles = context.frontend.includes("native-unistyles");
2172
2223
  const hasNative = hasNativeWind || hasUnistyles;
2173
- if (serverAppDirExists) {
2174
- const authServerBaseSrc = path.join(PKG_ROOT, "templates/auth/server/base");
2224
+ const authProvider = context.auth;
2225
+ if (context.backend === "convex" && authProvider === "clerk") {
2226
+ const convexBackendDestDir = path.join(projectDir, "packages/backend");
2227
+ const convexClerkBackendSrc = path.join(PKG_ROOT, "templates/auth/clerk/convex/backend");
2228
+ if (await fs.pathExists(convexClerkBackendSrc)) {
2229
+ await fs.ensureDir(convexBackendDestDir);
2230
+ await processAndCopyFiles("**/*", convexClerkBackendSrc, convexBackendDestDir, context);
2231
+ }
2232
+ if (webAppDirExists) {
2233
+ const reactFramework = context.frontend.find((f) => [
2234
+ "tanstack-router",
2235
+ "react-router",
2236
+ "tanstack-start",
2237
+ "next"
2238
+ ].includes(f));
2239
+ if (reactFramework) {
2240
+ const convexClerkWebSrc = path.join(PKG_ROOT, `templates/auth/clerk/convex/web/react/${reactFramework}`);
2241
+ if (await fs.pathExists(convexClerkWebSrc)) await processAndCopyFiles("**/*", convexClerkWebSrc, webAppDir, context);
2242
+ }
2243
+ }
2244
+ if (nativeAppDirExists) {
2245
+ const convexClerkNativeBaseSrc = path.join(PKG_ROOT, "templates/auth/clerk/convex/native/base");
2246
+ if (await fs.pathExists(convexClerkNativeBaseSrc)) await processAndCopyFiles("**/*", convexClerkNativeBaseSrc, nativeAppDir, context);
2247
+ const hasNativeWind$1 = context.frontend.includes("native-nativewind");
2248
+ const hasUnistyles$1 = context.frontend.includes("native-unistyles");
2249
+ let nativeFrameworkPath = "";
2250
+ if (hasNativeWind$1) nativeFrameworkPath = "nativewind";
2251
+ else if (hasUnistyles$1) nativeFrameworkPath = "unistyles";
2252
+ if (nativeFrameworkPath) {
2253
+ const convexClerkNativeFrameworkSrc = path.join(PKG_ROOT, `templates/auth/clerk/convex/native/${nativeFrameworkPath}`);
2254
+ if (await fs.pathExists(convexClerkNativeFrameworkSrc)) await processAndCopyFiles("**/*", convexClerkNativeFrameworkSrc, nativeAppDir, context);
2255
+ }
2256
+ }
2257
+ return;
2258
+ }
2259
+ if (serverAppDirExists && context.backend !== "convex") {
2260
+ const authServerBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/base`);
2175
2261
  if (await fs.pathExists(authServerBaseSrc)) await processAndCopyFiles("**/*", authServerBaseSrc, serverAppDir, context);
2176
2262
  if (context.backend === "next") {
2177
- const authServerNextSrc = path.join(PKG_ROOT, "templates/auth/server/next");
2263
+ const authServerNextSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/next`);
2178
2264
  if (await fs.pathExists(authServerNextSrc)) await processAndCopyFiles("**/*", authServerNextSrc, serverAppDir, context);
2179
2265
  }
2180
2266
  if (context.orm !== "none" && context.database !== "none") {
2181
2267
  const orm = context.orm;
2182
2268
  const db = context.database;
2183
2269
  let authDbSrc = "";
2184
- if (orm === "drizzle") authDbSrc = path.join(PKG_ROOT, `templates/auth/server/db/drizzle/${db}`);
2185
- else if (orm === "prisma") authDbSrc = path.join(PKG_ROOT, `templates/auth/server/db/prisma/${db}`);
2186
- else if (orm === "mongoose") authDbSrc = path.join(PKG_ROOT, `templates/auth/server/db/mongoose/${db}`);
2270
+ if (orm === "drizzle") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/drizzle/${db}`);
2271
+ else if (orm === "prisma") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/prisma/${db}`);
2272
+ else if (orm === "mongoose") authDbSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/server/db/mongoose/${db}`);
2187
2273
  if (authDbSrc && await fs.pathExists(authDbSrc)) await processAndCopyFiles("**/*", authDbSrc, serverAppDir, context);
2188
- else if (authDbSrc) {}
2189
2274
  }
2190
2275
  }
2191
2276
  if ((hasReactWeb || hasNuxtWeb || hasSvelteWeb || hasSolidWeb) && webAppDirExists) {
2192
2277
  if (hasReactWeb) {
2193
- const authWebBaseSrc = path.join(PKG_ROOT, "templates/auth/web/react/base");
2278
+ const authWebBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/base`);
2194
2279
  if (await fs.pathExists(authWebBaseSrc)) await processAndCopyFiles("**/*", authWebBaseSrc, webAppDir, context);
2195
2280
  const reactFramework = context.frontend.find((f) => [
2196
2281
  "tanstack-router",
@@ -2199,28 +2284,28 @@ async function setupAuthTemplate(projectDir, context) {
2199
2284
  "next"
2200
2285
  ].includes(f));
2201
2286
  if (reactFramework) {
2202
- const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/web/react/${reactFramework}`);
2287
+ const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/${reactFramework}`);
2203
2288
  if (await fs.pathExists(authWebFrameworkSrc)) await processAndCopyFiles("**/*", authWebFrameworkSrc, webAppDir, context);
2204
2289
  }
2205
2290
  } else if (hasNuxtWeb) {
2206
- const authWebNuxtSrc = path.join(PKG_ROOT, "templates/auth/web/nuxt");
2291
+ const authWebNuxtSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/nuxt`);
2207
2292
  if (await fs.pathExists(authWebNuxtSrc)) await processAndCopyFiles("**/*", authWebNuxtSrc, webAppDir, context);
2208
2293
  } else if (hasSvelteWeb) {
2209
- const authWebSvelteSrc = path.join(PKG_ROOT, "templates/auth/web/svelte");
2294
+ const authWebSvelteSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/svelte`);
2210
2295
  if (await fs.pathExists(authWebSvelteSrc)) await processAndCopyFiles("**/*", authWebSvelteSrc, webAppDir, context);
2211
2296
  } else if (hasSolidWeb) {
2212
- const authWebSolidSrc = path.join(PKG_ROOT, "templates/auth/web/solid");
2297
+ const authWebSolidSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/solid`);
2213
2298
  if (await fs.pathExists(authWebSolidSrc)) await processAndCopyFiles("**/*", authWebSolidSrc, webAppDir, context);
2214
2299
  }
2215
2300
  }
2216
2301
  if (hasNative && nativeAppDirExists) {
2217
- const authNativeBaseSrc = path.join(PKG_ROOT, "templates/auth/native/native-base");
2302
+ const authNativeBaseSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/native/native-base`);
2218
2303
  if (await fs.pathExists(authNativeBaseSrc)) await processAndCopyFiles("**/*", authNativeBaseSrc, nativeAppDir, context);
2219
2304
  let nativeFrameworkAuthPath = "";
2220
2305
  if (hasNativeWind) nativeFrameworkAuthPath = "nativewind";
2221
2306
  else if (hasUnistyles) nativeFrameworkAuthPath = "unistyles";
2222
2307
  if (nativeFrameworkAuthPath) {
2223
- const authNativeFrameworkSrc = path.join(PKG_ROOT, `templates/auth/native/${nativeFrameworkAuthPath}`);
2308
+ const authNativeFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/native/${nativeFrameworkAuthPath}`);
2224
2309
  if (await fs.pathExists(authNativeFrameworkSrc)) await processAndCopyFiles("**/*", authNativeFrameworkSrc, nativeAppDir, context);
2225
2310
  }
2226
2311
  }
@@ -2901,7 +2986,7 @@ async function addAddonsToProject(input) {
2901
2986
  frontend: detectedConfig.frontend || [],
2902
2987
  addons: input.addons,
2903
2988
  examples: detectedConfig.examples || [],
2904
- auth: detectedConfig.auth || false,
2989
+ auth: detectedConfig.auth || "none",
2905
2990
  git: false,
2906
2991
  packageManager: input.packageManager || detectedConfig.packageManager || "npm",
2907
2992
  install: input.install || false,
@@ -3673,7 +3758,7 @@ async function addDeploymentToProject(input) {
3673
3758
  frontend: detectedConfig.frontend || [],
3674
3759
  addons: detectedConfig.addons || [],
3675
3760
  examples: detectedConfig.examples || [],
3676
- auth: detectedConfig.auth || false,
3761
+ auth: detectedConfig.auth || "none",
3677
3762
  git: false,
3678
3763
  packageManager: input.packageManager || detectedConfig.packageManager || "npm",
3679
3764
  install: input.install || false,
@@ -3749,58 +3834,6 @@ async function formatProjectWithBiome(projectDir) {
3749
3834
  } catch {}
3750
3835
  }
3751
3836
 
3752
- //#endregion
3753
- //#region src/helpers/addons/auth-setup.ts
3754
- async function setupAuth(config) {
3755
- const { auth, frontend, backend, projectDir } = config;
3756
- if (backend === "convex" || !auth) return;
3757
- const serverDir = path.join(projectDir, "apps/server");
3758
- const clientDir = path.join(projectDir, "apps/web");
3759
- const nativeDir = path.join(projectDir, "apps/native");
3760
- const clientDirExists = await fs.pathExists(clientDir);
3761
- const nativeDirExists = await fs.pathExists(nativeDir);
3762
- const serverDirExists = await fs.pathExists(serverDir);
3763
- try {
3764
- if (serverDirExists) await addPackageDependency({
3765
- dependencies: ["better-auth"],
3766
- projectDir: serverDir
3767
- });
3768
- const hasWebFrontend$1 = frontend.some((f) => [
3769
- "react-router",
3770
- "tanstack-router",
3771
- "tanstack-start",
3772
- "next",
3773
- "nuxt",
3774
- "svelte",
3775
- "solid"
3776
- ].includes(f));
3777
- if (hasWebFrontend$1 && clientDirExists) await addPackageDependency({
3778
- dependencies: ["better-auth"],
3779
- projectDir: clientDir
3780
- });
3781
- if ((frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) && nativeDirExists) {
3782
- await addPackageDependency({
3783
- dependencies: ["better-auth", "@better-auth/expo"],
3784
- projectDir: nativeDir
3785
- });
3786
- if (serverDirExists) await addPackageDependency({
3787
- dependencies: ["@better-auth/expo"],
3788
- projectDir: serverDir
3789
- });
3790
- }
3791
- } catch (error) {
3792
- consola.error(pc.red("Failed to configure authentication dependencies"));
3793
- if (error instanceof Error) consola.error(pc.red(error.message));
3794
- }
3795
- }
3796
- function generateAuthSecret(length = 32) {
3797
- const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
3798
- let result = "";
3799
- const charactersLength = 62;
3800
- for (let i = 0; i < length; i++) result += characters.charAt(Math.floor(Math.random() * charactersLength));
3801
- return result;
3802
- }
3803
-
3804
3837
  //#endregion
3805
3838
  //#region src/helpers/addons/examples-setup.ts
3806
3839
  async function setupExamples(config) {
@@ -4050,6 +4083,88 @@ async function setupBackendDependencies(config) {
4050
4083
  });
4051
4084
  }
4052
4085
 
4086
+ //#endregion
4087
+ //#region src/helpers/core/auth-setup.ts
4088
+ async function setupAuth(config) {
4089
+ const { auth, frontend, backend, projectDir } = config;
4090
+ if (!auth || auth === "none") return;
4091
+ const serverDir = path.join(projectDir, "apps/server");
4092
+ const clientDir = path.join(projectDir, "apps/web");
4093
+ const nativeDir = path.join(projectDir, "apps/native");
4094
+ const clientDirExists = await fs.pathExists(clientDir);
4095
+ const nativeDirExists = await fs.pathExists(nativeDir);
4096
+ const serverDirExists = await fs.pathExists(serverDir);
4097
+ try {
4098
+ if (backend === "convex") {
4099
+ if (auth === "clerk" && clientDirExists) {
4100
+ const hasNextJs = frontend.includes("next");
4101
+ const hasTanStackStart = frontend.includes("tanstack-start");
4102
+ const hasViteReactOther = frontend.some((f) => ["tanstack-router", "react-router"].includes(f));
4103
+ if (hasNextJs) await addPackageDependency({
4104
+ dependencies: ["@clerk/nextjs"],
4105
+ projectDir: clientDir
4106
+ });
4107
+ else if (hasTanStackStart) await addPackageDependency({
4108
+ dependencies: ["@clerk/tanstack-react-start"],
4109
+ projectDir: clientDir
4110
+ });
4111
+ else if (hasViteReactOther) await addPackageDependency({
4112
+ dependencies: ["@clerk/clerk-react"],
4113
+ projectDir: clientDir
4114
+ });
4115
+ }
4116
+ const hasNativeWind = frontend.includes("native-nativewind");
4117
+ const hasUnistyles = frontend.includes("native-unistyles");
4118
+ if (auth === "clerk" && nativeDirExists && (hasNativeWind || hasUnistyles)) await addPackageDependency({
4119
+ dependencies: ["@clerk/clerk-expo"],
4120
+ projectDir: nativeDir
4121
+ });
4122
+ return;
4123
+ }
4124
+ if (serverDirExists && auth === "better-auth") await addPackageDependency({
4125
+ dependencies: ["better-auth"],
4126
+ projectDir: serverDir
4127
+ });
4128
+ const hasWebFrontend$1 = frontend.some((f) => [
4129
+ "react-router",
4130
+ "tanstack-router",
4131
+ "tanstack-start",
4132
+ "next",
4133
+ "nuxt",
4134
+ "svelte",
4135
+ "solid"
4136
+ ].includes(f));
4137
+ if (hasWebFrontend$1 && clientDirExists) {
4138
+ if (auth === "better-auth") await addPackageDependency({
4139
+ dependencies: ["better-auth"],
4140
+ projectDir: clientDir
4141
+ });
4142
+ }
4143
+ if ((frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) && nativeDirExists) {
4144
+ if (auth === "better-auth") {
4145
+ await addPackageDependency({
4146
+ dependencies: ["better-auth", "@better-auth/expo"],
4147
+ projectDir: nativeDir
4148
+ });
4149
+ if (serverDirExists) await addPackageDependency({
4150
+ dependencies: ["@better-auth/expo"],
4151
+ projectDir: serverDir
4152
+ });
4153
+ }
4154
+ }
4155
+ } catch (error) {
4156
+ consola.error(pc.red("Failed to configure authentication dependencies"));
4157
+ if (error instanceof Error) consola.error(pc.red(error.message));
4158
+ }
4159
+ }
4160
+ function generateAuthSecret(length = 32) {
4161
+ const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
4162
+ let result = "";
4163
+ const charactersLength = 62;
4164
+ for (let i = 0; i < length; i++) result += characters.charAt(Math.floor(Math.random() * charactersLength));
4165
+ return result;
4166
+ }
4167
+
4053
4168
  //#endregion
4054
4169
  //#region src/helpers/core/env-setup.ts
4055
4170
  async function addEnvVariablesToFile(filePath, variables) {
@@ -4128,6 +4243,33 @@ async function setupEnvironmentVariables(config) {
4128
4243
  value: serverUrl,
4129
4244
  condition: true
4130
4245
  }];
4246
+ if (backend === "convex" && auth === "clerk") {
4247
+ if (hasNextJs) clientVars.push({
4248
+ key: "NEXT_PUBLIC_CLERK_FRONTEND_API_URL",
4249
+ value: "",
4250
+ condition: true
4251
+ }, {
4252
+ key: "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY",
4253
+ value: "",
4254
+ condition: true
4255
+ }, {
4256
+ key: "CLERK_SECRET_KEY",
4257
+ value: "",
4258
+ condition: true
4259
+ });
4260
+ else if (hasReactRouter || hasTanStackRouter || hasTanStackStart) {
4261
+ clientVars.push({
4262
+ key: "VITE_CLERK_PUBLISHABLE_KEY",
4263
+ value: "",
4264
+ condition: true
4265
+ });
4266
+ if (hasTanStackStart) clientVars.push({
4267
+ key: "CLERK_SECRET_KEY",
4268
+ value: "",
4269
+ condition: true
4270
+ });
4271
+ }
4272
+ }
4131
4273
  await addEnvVariablesToFile(path.join(clientDir, ".env"), clientVars);
4132
4274
  }
4133
4275
  }
@@ -4145,6 +4287,11 @@ async function setupEnvironmentVariables(config) {
4145
4287
  value: serverUrl,
4146
4288
  condition: true
4147
4289
  }];
4290
+ if (backend === "convex" && auth === "clerk") nativeVars.push({
4291
+ key: "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY",
4292
+ value: "",
4293
+ condition: true
4294
+ });
4148
4295
  await addEnvVariablesToFile(path.join(nativeDir, ".env"), nativeVars);
4149
4296
  }
4150
4297
  }
@@ -5226,7 +5373,7 @@ This project uses Convex as a backend. You'll need to set up Convex before runni
5226
5373
  ${packageManagerRunCmd} dev:setup
5227
5374
  \`\`\`
5228
5375
 
5229
- Follow the prompts to create a new Convex project and connect it to your application.` : generateDatabaseSetup(database, auth, packageManagerRunCmd, orm, options.dbSetup, options.serverDeploy)}
5376
+ 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)}
5230
5377
 
5231
5378
  Then, run the development server:
5232
5379
 
@@ -5243,7 +5390,7 @@ ${generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeploy)}
5243
5390
  ## Project Structure
5244
5391
 
5245
5392
  \`\`\`
5246
- ${generateProjectStructure(projectName, frontend, backend, addons, isConvex, api)}
5393
+ ${generateProjectStructure(projectName, frontend, backend, addons, isConvex, api, auth)}
5247
5394
  \`\`\`
5248
5395
 
5249
5396
  ## Available Scripts
@@ -5293,7 +5440,7 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
5293
5440
  else if (!isBackendNone) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
5294
5441
  return instructions.join("\n");
5295
5442
  }
5296
- function generateProjectStructure(projectName, frontend, backend, addons, isConvex, api) {
5443
+ function generateProjectStructure(projectName, frontend, backend, addons, isConvex, api, auth) {
5297
5444
  const structure = [`${projectName}/`, "├── apps/"];
5298
5445
  const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
5299
5446
  const isBackendNone = backend === "none";
@@ -5323,6 +5470,7 @@ function generateProjectStructure(projectName, frontend, backend, addons, isConv
5323
5470
  if (isConvex) {
5324
5471
  structure.push("├── packages/");
5325
5472
  structure.push("│ └── backend/ # Convex backend functions and schema");
5473
+ if (auth === "clerk") structure.push("│ ├── convex/ # Convex functions and schema", "│ └── .env.local # Convex environment variables");
5326
5474
  } else if (!isBackendNone) {
5327
5475
  const backendName = backend[0].toUpperCase() + backend.slice(1);
5328
5476
  const apiName = api !== "none" ? api.toUpperCase() : "";
@@ -5374,7 +5522,10 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
5374
5522
  const dbName = database === "sqlite" ? "SQLite/Turso" : database === "postgres" ? "PostgreSQL" : database === "mysql" ? "MySQL" : database === "mongodb" ? "MongoDB" : "Database";
5375
5523
  addonsList.push(`- **${ormName}** - TypeScript-first ORM`, `- **${dbName}** - Database engine`);
5376
5524
  }
5377
- if (auth && !isConvex) addonsList.push("- **Authentication** - Email & password authentication with Better Auth");
5525
+ if (auth !== "none") {
5526
+ const authLabel = auth === "clerk" ? "Clerk" : "Better-Auth";
5527
+ addonsList.push(`- **Authentication** - ${authLabel}`);
5528
+ }
5378
5529
  for (const addon of addons) if (addon === "pwa") addonsList.push("- **PWA** - Progressive Web App support");
5379
5530
  else if (addon === "tauri") addonsList.push("- **Tauri** - Build native desktop applications");
5380
5531
  else if (addon === "biome") addonsList.push("- **Biome** - Linting and formatting");
@@ -5565,6 +5716,7 @@ async function displayPostInstallInstructions(config) {
5565
5716
  const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex) : "";
5566
5717
  const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
5567
5718
  const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
5719
+ const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
5568
5720
  const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy);
5569
5721
  const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy);
5570
5722
  const hasWeb = frontend?.some((f) => [
@@ -5612,6 +5764,7 @@ async function displayPostInstallInstructions(config) {
5612
5764
  if (wranglerDeployInstructions) output += `\n${wranglerDeployInstructions.trim()}\n`;
5613
5765
  if (alchemyDeployInstructions) output += `\n${alchemyDeployInstructions.trim()}\n`;
5614
5766
  if (starlightInstructions) output += `\n${starlightInstructions.trim()}\n`;
5767
+ if (clerkInstructions) output += `\n${clerkInstructions.trim()}\n`;
5615
5768
  if (noOrmWarning) output += `\n${noOrmWarning.trim()}\n`;
5616
5769
  if (bunWebNativeWarning) output += `\n${bunWebNativeWarning.trim()}\n`;
5617
5770
  output += `\n${pc.bold("Update all dependencies:\n")}${pc.cyan(tazeCommand)}\n\n`;
@@ -5687,6 +5840,9 @@ function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy) {
5687
5840
  if (serverDeploy === "wrangler") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} run deploy`}`);
5688
5841
  return instructions.length ? `\n${instructions.join("\n")}` : "";
5689
5842
  }
5843
+ function getClerkInstructions() {
5844
+ 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`;
5845
+ }
5690
5846
  function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
5691
5847
  const instructions = [];
5692
5848
  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`}`);
@@ -5889,8 +6045,8 @@ async function createProject(options) {
5889
6045
  if (!isConvex) {
5890
6046
  await setupDbOrmTemplates(projectDir, options);
5891
6047
  await setupDockerComposeTemplates(projectDir, options);
5892
- await setupAuthTemplate(projectDir, options);
5893
6048
  }
6049
+ await setupAuthTemplate(projectDir, options);
5894
6050
  if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamplesTemplate(projectDir, options);
5895
6051
  await setupAddonsTemplate(projectDir, options);
5896
6052
  await setupDeploymentTemplates(projectDir, options);
@@ -5902,7 +6058,7 @@ async function createProject(options) {
5902
6058
  if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamples(options);
5903
6059
  }
5904
6060
  if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
5905
- if (!isConvex && options.auth) await setupAuth(options);
6061
+ if (options.auth && options.auth !== "none") await setupAuth(options);
5906
6062
  await handleExtras(projectDir, options);
5907
6063
  await setupEnvironmentVariables(options);
5908
6064
  await updatePackageConfigurations(projectDir, options);
@@ -5981,7 +6137,7 @@ async function createProjectHandler(input) {
5981
6137
  frontend: [],
5982
6138
  addons: [],
5983
6139
  examples: [],
5984
- auth: false,
6140
+ auth: "none",
5985
6141
  git: false,
5986
6142
  packageManager: "npm",
5987
6143
  install: false,
@@ -6016,8 +6172,8 @@ async function createProjectHandler(input) {
6016
6172
  };
6017
6173
  coerceBackendPresets(config);
6018
6174
  validateConfigCompatibility(config, providedFlags, cliInput);
6019
- if (config.backend === "convex") log.info("Due to '--backend convex' flag, the following options have been automatically set: auth=false, database=none, orm=none, api=none, runtime=none, dbSetup=none, examples=todo");
6020
- else if (config.backend === "none") log.info("Due to '--backend none', the following options have been automatically set: --auth=false, --database=none, --orm=none, --api=none, --runtime=none, --db-setup=none, --examples=none");
6175
+ if (config.backend === "convex") log.info(`Due to '--backend convex' flag, the following options have been automatically set: database=none, orm=none, api=none, runtime=none, dbSetup=none, examples=todo`);
6176
+ else if (config.backend === "none") log.info("Due to '--backend none', the following options have been automatically set: --auth none, --database=none, --orm=none, --api=none, --runtime=none, --db-setup=none, --examples=none");
6021
6177
  log.info(pc.yellow("Using default/flag options (config prompts skipped):"));
6022
6178
  log.message(displayConfig(config));
6023
6179
  log.message("");
@@ -6223,7 +6379,7 @@ const router = t.router({
6223
6379
  verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
6224
6380
  database: DatabaseSchema.optional(),
6225
6381
  orm: ORMSchema.optional(),
6226
- auth: z.boolean().optional(),
6382
+ auth: AuthSchema.optional(),
6227
6383
  frontend: z.array(FrontendSchema).optional(),
6228
6384
  addons: z.array(AddonsSchema).optional(),
6229
6385
  examples: z.array(ExamplesSchema).optional(),
@@ -6314,7 +6470,7 @@ function createBtsCli() {
6314
6470
  * backend: "hono",
6315
6471
  * database: "sqlite",
6316
6472
  * orm: "drizzle",
6317
- * auth: true,
6473
+ * auth: "better-auth",
6318
6474
  * addons: ["biome", "turborepo"],
6319
6475
  * packageManager: "bun",
6320
6476
  * install: false,