create-better-t-stack 2.36.3 → 2.37.0-canary.6846bd90

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 (112) 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-CN1zi2A-.js → src-ByxWjVSi.js} +331 -241
  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 +1 -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 +3 -18
  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/deploy/alchemy/wrangler.jsonc.hbs +0 -11
  55. /package/templates/auth/{native → better-auth/native}/native-base/lib/auth-client.ts.hbs +0 -0
  56. /package/templates/auth/{native → better-auth/native}/nativewind/app/(drawer)/index.tsx.hbs +0 -0
  57. /package/templates/auth/{native → better-auth/native}/nativewind/components/sign-in.tsx.hbs +0 -0
  58. /package/templates/auth/{native → better-auth/native}/nativewind/components/sign-up.tsx.hbs +0 -0
  59. /package/templates/auth/{native → better-auth/native}/unistyles/app/(drawer)/index.tsx.hbs +0 -0
  60. /package/templates/auth/{native → better-auth/native}/unistyles/components/sign-in.tsx.hbs +0 -0
  61. /package/templates/auth/{native → better-auth/native}/unistyles/components/sign-up.tsx.hbs +0 -0
  62. /package/templates/auth/{server → better-auth/server}/base/src/lib/auth.ts.hbs +0 -0
  63. /package/templates/auth/{server → better-auth/server}/db/drizzle/mysql/src/db/schema/auth.ts +0 -0
  64. /package/templates/auth/{server → better-auth/server}/db/drizzle/postgres/src/db/schema/auth.ts +0 -0
  65. /package/templates/auth/{server → better-auth/server}/db/drizzle/sqlite/src/db/schema/auth.ts +0 -0
  66. /package/templates/auth/{server → better-auth/server}/db/mongoose/mongodb/src/db/models/auth.model.ts +0 -0
  67. /package/templates/auth/{server → better-auth/server}/db/prisma/mongodb/prisma/schema/auth.prisma +0 -0
  68. /package/templates/auth/{server → better-auth/server}/db/prisma/mysql/prisma/schema/auth.prisma +0 -0
  69. /package/templates/auth/{server → better-auth/server}/db/prisma/postgres/prisma/schema/auth.prisma +0 -0
  70. /package/templates/auth/{server → better-auth/server}/db/prisma/sqlite/prisma/schema/auth.prisma +0 -0
  71. /package/templates/auth/{server → better-auth/server}/next/src/app/api/auth/[...all]/route.ts +0 -0
  72. /package/templates/auth/{web → better-auth/web}/nuxt/app/components/SignInForm.vue +0 -0
  73. /package/templates/auth/{web → better-auth/web}/nuxt/app/components/SignUpForm.vue +0 -0
  74. /package/templates/auth/{web → better-auth/web}/nuxt/app/components/UserMenu.vue +0 -0
  75. /package/templates/auth/{web → better-auth/web}/nuxt/app/middleware/auth.ts +0 -0
  76. /package/templates/auth/{web → better-auth/web}/nuxt/app/pages/dashboard.vue.hbs +0 -0
  77. /package/templates/auth/{web → better-auth/web}/nuxt/app/pages/login.vue +0 -0
  78. /package/templates/auth/{web → better-auth/web}/nuxt/app/plugins/auth-client.ts +0 -0
  79. /package/templates/auth/{web → better-auth/web}/react/base/src/lib/auth-client.ts.hbs +0 -0
  80. /package/templates/auth/{web → better-auth/web}/react/next/src/app/dashboard/page.tsx.hbs +0 -0
  81. /package/templates/auth/{web → better-auth/web}/react/next/src/app/login/page.tsx +0 -0
  82. /package/templates/auth/{web → better-auth/web}/react/next/src/components/sign-in-form.tsx +0 -0
  83. /package/templates/auth/{web → better-auth/web}/react/next/src/components/sign-up-form.tsx +0 -0
  84. /package/templates/auth/{web → better-auth/web}/react/next/src/components/theme-provider.tsx +0 -0
  85. /package/templates/auth/{web → better-auth/web}/react/next/src/components/user-menu.tsx +0 -0
  86. /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/sign-in-form.tsx +0 -0
  87. /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/sign-up-form.tsx +0 -0
  88. /package/templates/auth/{web → better-auth/web}/react/react-router/src/components/user-menu.tsx +0 -0
  89. /package/templates/auth/{web → better-auth/web}/react/react-router/src/routes/dashboard.tsx.hbs +0 -0
  90. /package/templates/auth/{web → better-auth/web}/react/react-router/src/routes/login.tsx +0 -0
  91. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/sign-in-form.tsx +0 -0
  92. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/sign-up-form.tsx +0 -0
  93. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/components/user-menu.tsx +0 -0
  94. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/routes/dashboard.tsx.hbs +0 -0
  95. /package/templates/auth/{web → better-auth/web}/react/tanstack-router/src/routes/login.tsx +0 -0
  96. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/sign-in-form.tsx +0 -0
  97. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/sign-up-form.tsx +0 -0
  98. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/components/user-menu.tsx +0 -0
  99. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/routes/dashboard.tsx.hbs +0 -0
  100. /package/templates/auth/{web → better-auth/web}/react/tanstack-start/src/routes/login.tsx +0 -0
  101. /package/templates/auth/{web → better-auth/web}/solid/src/components/sign-in-form.tsx +0 -0
  102. /package/templates/auth/{web → better-auth/web}/solid/src/components/sign-up-form.tsx +0 -0
  103. /package/templates/auth/{web → better-auth/web}/solid/src/components/user-menu.tsx.hbs +0 -0
  104. /package/templates/auth/{web → better-auth/web}/solid/src/lib/auth-client.ts +0 -0
  105. /package/templates/auth/{web → better-auth/web}/solid/src/routes/dashboard.tsx.hbs +0 -0
  106. /package/templates/auth/{web → better-auth/web}/solid/src/routes/login.tsx +0 -0
  107. /package/templates/auth/{web → better-auth/web}/svelte/src/components/SignInForm.svelte +0 -0
  108. /package/templates/auth/{web → better-auth/web}/svelte/src/components/SignUpForm.svelte +0 -0
  109. /package/templates/auth/{web → better-auth/web}/svelte/src/components/UserMenu.svelte +0 -0
  110. /package/templates/auth/{web → better-auth/web}/svelte/src/lib/auth-client.ts +0 -0
  111. /package/templates/auth/{web → better-auth/web}/svelte/src/routes/dashboard/+page.svelte.hbs +0 -0
  112. /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",
@@ -129,7 +133,7 @@ const dependencyVersionMap = {
129
133
  "nitro-cloudflare-dev": "^0.2.2",
130
134
  "@sveltejs/adapter-cloudflare": "^7.2.1",
131
135
  "@cloudflare/workers-types": "^4.20250822.0",
132
- alchemy: "^0.62.1",
136
+ alchemy: "^0.63.0",
133
137
  nitropack: "^2.12.4",
134
138
  dotenv: "^17.2.1"
135
139
  };
@@ -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 = []) {
@@ -592,7 +608,7 @@ function validateAlchemyCompatibility(webDeploy, serverDeploy, frontends = []) {
592
608
  const isAlchemyWebDeploy = webDeploy === "alchemy";
593
609
  const isAlchemyServerDeploy = serverDeploy === "alchemy";
594
610
  if (isAlchemyWebDeploy || isAlchemyServerDeploy) {
595
- const incompatibleFrontends = frontends.filter((f) => f === "next" || f === "react-router");
611
+ const incompatibleFrontends = frontends.filter((f) => f === "next");
596
612
  if (incompatibleFrontends.length > 0) {
597
613
  const deployType = isAlchemyWebDeploy && isAlchemyServerDeploy ? "web and server deployment" : isAlchemyWebDeploy ? "web deployment" : "server deployment";
598
614
  exitWithError(`Alchemy ${deployType} is temporarily not compatible with ${incompatibleFrontends.join(" and ")} frontend(s). Please choose a different frontend or deployment option.`);
@@ -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,
@@ -1152,7 +1197,7 @@ function getDeploymentDisplay(deployment) {
1152
1197
  async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []) {
1153
1198
  if (deployment !== void 0) return deployment;
1154
1199
  if (!hasWebFrontend(frontend)) return "none";
1155
- const hasIncompatibleFrontend = frontend.some((f) => f === "next" || f === "react-router");
1200
+ const hasIncompatibleFrontend = frontend.some((f) => f === "next");
1156
1201
  const availableDeployments = hasIncompatibleFrontend ? ["wrangler", "none"] : [
1157
1202
  "wrangler",
1158
1203
  "alchemy",
@@ -1176,7 +1221,7 @@ async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []
1176
1221
  }
1177
1222
  async function getDeploymentToAdd(frontend, existingDeployment) {
1178
1223
  if (!hasWebFrontend(frontend)) return "none";
1179
- const hasIncompatibleFrontend = frontend.some((f) => f === "next" || f === "react-router");
1224
+ const hasIncompatibleFrontend = frontend.some((f) => f === "next");
1180
1225
  const options = [];
1181
1226
  if (existingDeployment !== "wrangler") {
1182
1227
  const { label, hint } = getDeploymentDisplay("wrangler");
@@ -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
  }
@@ -1345,7 +1389,7 @@ const getLatestCLIVersion = () => {
1345
1389
  */
1346
1390
  function isTelemetryEnabled() {
1347
1391
  const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
1348
- const BTS_TELEMETRY = "1";
1392
+ const BTS_TELEMETRY = "0";
1349
1393
  if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
1350
1394
  if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
1351
1395
  return true;
@@ -1353,8 +1397,8 @@ function isTelemetryEnabled() {
1353
1397
 
1354
1398
  //#endregion
1355
1399
  //#region src/utils/analytics.ts
1356
- const POSTHOG_API_KEY = "phc_8ZUxEwwfKMajJLvxz1daGd931dYbQrwKNficBmsdIrs";
1357
- const POSTHOG_HOST = "https://us.i.posthog.com";
1400
+ const POSTHOG_API_KEY = "random";
1401
+ const POSTHOG_HOST = "random";
1358
1402
  function generateSessionId() {
1359
1403
  const rand = Math.random().toString(36).slice(2);
1360
1404
  const now = Date.now().toString(36);
@@ -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
  }
@@ -2350,10 +2435,7 @@ async function setupDeploymentTemplates(projectDir, context) {
2350
2435
  if (await fs.pathExists(alchemyTemplateSrc)) {
2351
2436
  await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, projectDir, context);
2352
2437
  const serverAppDir = path.join(projectDir, "apps/server");
2353
- if (await fs.pathExists(serverAppDir)) {
2354
- await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
2355
- await processAndCopyFiles("wrangler.jsonc.hbs", alchemyTemplateSrc, serverAppDir, context);
2356
- }
2438
+ if (await fs.pathExists(serverAppDir)) await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
2357
2439
  }
2358
2440
  } else {
2359
2441
  if (context.webDeploy === "alchemy") {
@@ -2367,7 +2449,6 @@ async function setupDeploymentTemplates(projectDir, context) {
2367
2449
  if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(serverAppDir)) {
2368
2450
  await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
2369
2451
  await processAndCopyFiles("env.d.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
2370
- await processAndCopyFiles("wrangler.jsonc.hbs", alchemyTemplateSrc, serverAppDir, context);
2371
2452
  }
2372
2453
  }
2373
2454
  }
@@ -2905,7 +2986,7 @@ async function addAddonsToProject(input) {
2905
2986
  frontend: detectedConfig.frontend || [],
2906
2987
  addons: input.addons,
2907
2988
  examples: detectedConfig.examples || [],
2908
- auth: detectedConfig.auth || false,
2989
+ auth: detectedConfig.auth || "none",
2909
2990
  git: false,
2910
2991
  packageManager: input.packageManager || detectedConfig.packageManager || "npm",
2911
2992
  install: input.install || false,
@@ -3000,11 +3081,9 @@ async function setupAlchemyServerDeploy(serverDir, _packageManager) {
3000
3081
  const packageJson = await fs.readJson(packageJsonPath);
3001
3082
  packageJson.scripts = {
3002
3083
  ...packageJson.scripts,
3003
- dev: "wrangler dev --port=3000",
3004
- build: "wrangler deploy --dry-run",
3084
+ dev: "alchemy dev",
3005
3085
  deploy: "alchemy deploy",
3006
- destroy: "alchemy destroy",
3007
- "alchemy:dev": "alchemy dev"
3086
+ destroy: "alchemy destroy"
3008
3087
  };
3009
3088
  await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
3010
3089
  }
@@ -3012,7 +3091,7 @@ async function setupAlchemyServerDeploy(serverDir, _packageManager) {
3012
3091
 
3013
3092
  //#endregion
3014
3093
  //#region src/helpers/deployment/alchemy/alchemy-next-setup.ts
3015
- async function setupNextAlchemyDeploy(projectDir, _packageManager) {
3094
+ async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
3016
3095
  const webAppDir = path.join(projectDir, "apps/web");
3017
3096
  if (!await fs.pathExists(webAppDir)) return;
3018
3097
  await addPackageDependency({
@@ -3022,11 +3101,11 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager) {
3022
3101
  const pkgPath = path.join(webAppDir, "package.json");
3023
3102
  if (await fs.pathExists(pkgPath)) {
3024
3103
  const pkg = await fs.readJson(pkgPath);
3025
- pkg.scripts = {
3104
+ if (!options?.skipAppScripts) pkg.scripts = {
3026
3105
  ...pkg.scripts,
3027
3106
  deploy: "alchemy deploy",
3028
3107
  destroy: "alchemy destroy",
3029
- "alchemy:dev": "alchemy dev"
3108
+ dev: "alchemy dev"
3030
3109
  };
3031
3110
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3032
3111
  }
@@ -3034,7 +3113,7 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager) {
3034
3113
 
3035
3114
  //#endregion
3036
3115
  //#region src/helpers/deployment/alchemy/alchemy-nuxt-setup.ts
3037
- async function setupNuxtAlchemyDeploy(projectDir, _packageManager) {
3116
+ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
3038
3117
  const webAppDir = path.join(projectDir, "apps/web");
3039
3118
  if (!await fs.pathExists(webAppDir)) return;
3040
3119
  await addPackageDependency({
@@ -3048,11 +3127,11 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager) {
3048
3127
  const pkgPath = path.join(webAppDir, "package.json");
3049
3128
  if (await fs.pathExists(pkgPath)) {
3050
3129
  const pkg = await fs.readJson(pkgPath);
3051
- pkg.scripts = {
3130
+ if (!options?.skipAppScripts) pkg.scripts = {
3052
3131
  ...pkg.scripts,
3053
3132
  deploy: "alchemy deploy",
3054
3133
  destroy: "alchemy destroy",
3055
- "alchemy:dev": "alchemy dev"
3134
+ dev: "alchemy dev"
3056
3135
  };
3057
3136
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3058
3137
  }
@@ -3102,112 +3181,29 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager) {
3102
3181
 
3103
3182
  //#endregion
3104
3183
  //#region src/helpers/deployment/alchemy/alchemy-react-router-setup.ts
3105
- async function setupReactRouterAlchemyDeploy(projectDir, _packageManager) {
3184
+ async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, options) {
3106
3185
  const webAppDir = path.join(projectDir, "apps/web");
3107
3186
  if (!await fs.pathExists(webAppDir)) return;
3108
3187
  await addPackageDependency({
3109
- devDependencies: [
3110
- "alchemy",
3111
- "@cloudflare/vite-plugin",
3112
- "dotenv"
3113
- ],
3188
+ devDependencies: ["alchemy", "dotenv"],
3114
3189
  projectDir: webAppDir
3115
3190
  });
3116
3191
  const pkgPath = path.join(webAppDir, "package.json");
3117
3192
  if (await fs.pathExists(pkgPath)) {
3118
3193
  const pkg = await fs.readJson(pkgPath);
3119
- pkg.scripts = {
3194
+ if (!options?.skipAppScripts) pkg.scripts = {
3120
3195
  ...pkg.scripts,
3121
3196
  deploy: "alchemy deploy",
3122
3197
  destroy: "alchemy destroy",
3123
- "alchemy:dev": "alchemy dev"
3198
+ dev: "alchemy dev"
3124
3199
  };
3125
3200
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3126
3201
  }
3127
- const viteConfigPath = path.join(webAppDir, "vite.config.ts");
3128
- if (await fs.pathExists(viteConfigPath)) try {
3129
- const project = new Project({ manipulationSettings: {
3130
- indentationText: IndentationText.TwoSpaces,
3131
- quoteKind: QuoteKind.Double
3132
- } });
3133
- project.addSourceFileAtPath(viteConfigPath);
3134
- const sourceFile = project.getSourceFileOrThrow(viteConfigPath);
3135
- const alchemyImport = sourceFile.getImportDeclaration("alchemy/cloudflare/react-router");
3136
- if (!alchemyImport) sourceFile.addImportDeclaration({
3137
- moduleSpecifier: "alchemy/cloudflare/react-router",
3138
- defaultImport: "alchemy"
3139
- });
3140
- const exportAssignment = sourceFile.getExportAssignment((d) => !d.isExportEquals());
3141
- if (!exportAssignment) return;
3142
- const defineConfigCall = exportAssignment.getExpression();
3143
- if (!Node.isCallExpression(defineConfigCall) || defineConfigCall.getExpression().getText() !== "defineConfig") return;
3144
- let configObject = defineConfigCall.getArguments()[0];
3145
- if (!configObject) configObject = defineConfigCall.addArgument("{}");
3146
- if (Node.isObjectLiteralExpression(configObject)) {
3147
- const pluginsProperty = configObject.getProperty("plugins");
3148
- if (pluginsProperty && Node.isPropertyAssignment(pluginsProperty)) {
3149
- const initializer = pluginsProperty.getInitializer();
3150
- if (Node.isArrayLiteralExpression(initializer)) {
3151
- const hasCloudflarePlugin = initializer.getElements().some((el) => el.getText().includes("cloudflare("));
3152
- if (!hasCloudflarePlugin) initializer.addElement("alchemy()");
3153
- }
3154
- } else if (!pluginsProperty) configObject.addPropertyAssignment({
3155
- name: "plugins",
3156
- initializer: "[alchemy()]"
3157
- });
3158
- }
3159
- await project.save();
3160
- } catch (error) {
3161
- console.warn("Failed to update vite.config.ts:", error);
3162
- }
3163
- const reactRouterConfigPath = path.join(webAppDir, "react-router.config.ts");
3164
- if (await fs.pathExists(reactRouterConfigPath)) try {
3165
- const project = new Project({ manipulationSettings: {
3166
- indentationText: IndentationText.TwoSpaces,
3167
- quoteKind: QuoteKind.Double
3168
- } });
3169
- project.addSourceFileAtPath(reactRouterConfigPath);
3170
- const sourceFile = project.getSourceFileOrThrow(reactRouterConfigPath);
3171
- const exportAssignment = sourceFile.getExportAssignment((d) => !d.isExportEquals());
3172
- if (!exportAssignment) return;
3173
- const configExpression = exportAssignment.getExpression();
3174
- let configObject;
3175
- if (Node.isObjectLiteralExpression(configExpression)) configObject = configExpression;
3176
- else if (Node.isSatisfiesExpression(configExpression)) {
3177
- const expression = configExpression.getExpression();
3178
- if (Node.isObjectLiteralExpression(expression)) configObject = expression;
3179
- }
3180
- if (!configObject || !Node.isObjectLiteralExpression(configObject)) return;
3181
- const futureProperty = configObject.getProperty("future");
3182
- if (!futureProperty) configObject.addPropertyAssignment({
3183
- name: "future",
3184
- initializer: `{
3185
- unstable_viteEnvironmentApi: true,
3186
- }`
3187
- });
3188
- else if (Node.isPropertyAssignment(futureProperty)) {
3189
- const futureInitializer = futureProperty.getInitializer();
3190
- if (Node.isObjectLiteralExpression(futureInitializer)) {
3191
- const viteEnvApiProp = futureInitializer.getProperty("unstable_viteEnvironmentApi");
3192
- if (!viteEnvApiProp) futureInitializer.addPropertyAssignment({
3193
- name: "unstable_viteEnvironmentApi",
3194
- initializer: "true"
3195
- });
3196
- else if (Node.isPropertyAssignment(viteEnvApiProp)) {
3197
- const value = viteEnvApiProp.getInitializer()?.getText();
3198
- if (value === "false") viteEnvApiProp.setInitializer("true");
3199
- }
3200
- }
3201
- }
3202
- await project.save();
3203
- } catch (error) {
3204
- console.warn("Failed to update react-router.config.ts:", error);
3205
- }
3206
3202
  }
3207
3203
 
3208
3204
  //#endregion
3209
3205
  //#region src/helpers/deployment/alchemy/alchemy-solid-setup.ts
3210
- async function setupSolidAlchemyDeploy(projectDir, _packageManager) {
3206
+ async function setupSolidAlchemyDeploy(projectDir, _packageManager, options) {
3211
3207
  const webAppDir = path.join(projectDir, "apps/web");
3212
3208
  if (!await fs.pathExists(webAppDir)) return;
3213
3209
  await addPackageDependency({
@@ -3217,11 +3213,11 @@ async function setupSolidAlchemyDeploy(projectDir, _packageManager) {
3217
3213
  const pkgPath = path.join(webAppDir, "package.json");
3218
3214
  if (await fs.pathExists(pkgPath)) {
3219
3215
  const pkg = await fs.readJson(pkgPath);
3220
- pkg.scripts = {
3216
+ if (!options?.skipAppScripts) pkg.scripts = {
3221
3217
  ...pkg.scripts,
3222
3218
  deploy: "alchemy deploy",
3223
3219
  destroy: "alchemy destroy",
3224
- "alchemy:dev": "alchemy dev"
3220
+ dev: "alchemy dev"
3225
3221
  };
3226
3222
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3227
3223
  }
@@ -3229,7 +3225,7 @@ async function setupSolidAlchemyDeploy(projectDir, _packageManager) {
3229
3225
 
3230
3226
  //#endregion
3231
3227
  //#region src/helpers/deployment/alchemy/alchemy-svelte-setup.ts
3232
- async function setupSvelteAlchemyDeploy(projectDir, _packageManager) {
3228
+ async function setupSvelteAlchemyDeploy(projectDir, _packageManager, options) {
3233
3229
  const webAppDir = path.join(projectDir, "apps/web");
3234
3230
  if (!await fs.pathExists(webAppDir)) return;
3235
3231
  await addPackageDependency({
@@ -3243,11 +3239,11 @@ async function setupSvelteAlchemyDeploy(projectDir, _packageManager) {
3243
3239
  const pkgPath = path.join(webAppDir, "package.json");
3244
3240
  if (await fs.pathExists(pkgPath)) {
3245
3241
  const pkg = await fs.readJson(pkgPath);
3246
- pkg.scripts = {
3242
+ if (!options?.skipAppScripts) pkg.scripts = {
3247
3243
  ...pkg.scripts,
3248
3244
  deploy: "alchemy deploy",
3249
3245
  destroy: "alchemy destroy",
3250
- "alchemy:dev": "alchemy dev"
3246
+ dev: "alchemy dev"
3251
3247
  };
3252
3248
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3253
3249
  }
@@ -3300,7 +3296,7 @@ function updateAdapterInConfig(configObject) {
3300
3296
 
3301
3297
  //#endregion
3302
3298
  //#region src/helpers/deployment/alchemy/alchemy-tanstack-router-setup.ts
3303
- async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager) {
3299
+ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, options) {
3304
3300
  const webAppDir = path.join(projectDir, "apps/web");
3305
3301
  if (!await fs.pathExists(webAppDir)) return;
3306
3302
  await addPackageDependency({
@@ -3310,11 +3306,11 @@ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager) {
3310
3306
  const pkgPath = path.join(webAppDir, "package.json");
3311
3307
  if (await fs.pathExists(pkgPath)) {
3312
3308
  const pkg = await fs.readJson(pkgPath);
3313
- pkg.scripts = {
3309
+ if (!options?.skipAppScripts) pkg.scripts = {
3314
3310
  ...pkg.scripts,
3315
3311
  deploy: "alchemy deploy",
3316
3312
  destroy: "alchemy destroy",
3317
- "alchemy:dev": "alchemy dev"
3313
+ dev: "alchemy dev"
3318
3314
  };
3319
3315
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3320
3316
  }
@@ -3322,7 +3318,7 @@ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager) {
3322
3318
 
3323
3319
  //#endregion
3324
3320
  //#region src/helpers/deployment/alchemy/alchemy-tanstack-start-setup.ts
3325
- async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager) {
3321
+ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, options) {
3326
3322
  const webAppDir = path.join(projectDir, "apps/web");
3327
3323
  if (!await fs.pathExists(webAppDir)) return;
3328
3324
  await addPackageDependency({
@@ -3336,11 +3332,11 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager) {
3336
3332
  const pkgPath = path.join(webAppDir, "package.json");
3337
3333
  if (await fs.pathExists(pkgPath)) {
3338
3334
  const pkg = await fs.readJson(pkgPath);
3339
- pkg.scripts = {
3335
+ if (!options?.skipAppScripts) pkg.scripts = {
3340
3336
  ...pkg.scripts,
3341
3337
  deploy: "alchemy deploy",
3342
3338
  destroy: "alchemy destroy",
3343
- "alchemy:dev": "alchemy dev"
3339
+ dev: "alchemy dev"
3344
3340
  };
3345
3341
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3346
3342
  }
@@ -3456,7 +3452,7 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
3456
3452
  ...pkg.scripts,
3457
3453
  deploy: "alchemy deploy",
3458
3454
  destroy: "alchemy destroy",
3459
- "alchemy:dev": "alchemy dev"
3455
+ dev: "alchemy dev"
3460
3456
  };
3461
3457
  await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
3462
3458
  }
@@ -3470,13 +3466,13 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
3470
3466
  const isTanstackStart = frontend.includes("tanstack-start");
3471
3467
  const isReactRouter = frontend.includes("react-router");
3472
3468
  const isSolid = frontend.includes("solid");
3473
- if (isNext) await setupNextAlchemyDeploy(projectDir, packageManager);
3474
- else if (isNuxt) await setupNuxtAlchemyDeploy(projectDir, packageManager);
3475
- else if (isSvelte) await setupSvelteAlchemyDeploy(projectDir, packageManager);
3476
- else if (isTanstackStart) await setupTanStackStartAlchemyDeploy(projectDir, packageManager);
3477
- else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager);
3478
- else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager);
3479
- else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager);
3469
+ if (isNext) await setupNextAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
3470
+ else if (isNuxt) await setupNuxtAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
3471
+ else if (isSvelte) await setupSvelteAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
3472
+ else if (isTanstackStart) await setupTanStackStartAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
3473
+ else if (isTanstackRouter) await setupTanStackRouterAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
3474
+ else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
3475
+ else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
3480
3476
  }
3481
3477
 
3482
3478
  //#endregion
@@ -3762,7 +3758,7 @@ async function addDeploymentToProject(input) {
3762
3758
  frontend: detectedConfig.frontend || [],
3763
3759
  addons: detectedConfig.addons || [],
3764
3760
  examples: detectedConfig.examples || [],
3765
- auth: detectedConfig.auth || false,
3761
+ auth: detectedConfig.auth || "none",
3766
3762
  git: false,
3767
3763
  packageManager: input.packageManager || detectedConfig.packageManager || "npm",
3768
3764
  install: input.install || false,
@@ -3838,58 +3834,6 @@ async function formatProjectWithBiome(projectDir) {
3838
3834
  } catch {}
3839
3835
  }
3840
3836
 
3841
- //#endregion
3842
- //#region src/helpers/addons/auth-setup.ts
3843
- async function setupAuth(config) {
3844
- const { auth, frontend, backend, projectDir } = config;
3845
- if (backend === "convex" || !auth) return;
3846
- const serverDir = path.join(projectDir, "apps/server");
3847
- const clientDir = path.join(projectDir, "apps/web");
3848
- const nativeDir = path.join(projectDir, "apps/native");
3849
- const clientDirExists = await fs.pathExists(clientDir);
3850
- const nativeDirExists = await fs.pathExists(nativeDir);
3851
- const serverDirExists = await fs.pathExists(serverDir);
3852
- try {
3853
- if (serverDirExists) await addPackageDependency({
3854
- dependencies: ["better-auth"],
3855
- projectDir: serverDir
3856
- });
3857
- const hasWebFrontend$1 = frontend.some((f) => [
3858
- "react-router",
3859
- "tanstack-router",
3860
- "tanstack-start",
3861
- "next",
3862
- "nuxt",
3863
- "svelte",
3864
- "solid"
3865
- ].includes(f));
3866
- if (hasWebFrontend$1 && clientDirExists) await addPackageDependency({
3867
- dependencies: ["better-auth"],
3868
- projectDir: clientDir
3869
- });
3870
- if ((frontend.includes("native-nativewind") || frontend.includes("native-unistyles")) && nativeDirExists) {
3871
- await addPackageDependency({
3872
- dependencies: ["better-auth", "@better-auth/expo"],
3873
- projectDir: nativeDir
3874
- });
3875
- if (serverDirExists) await addPackageDependency({
3876
- dependencies: ["@better-auth/expo"],
3877
- projectDir: serverDir
3878
- });
3879
- }
3880
- } catch (error) {
3881
- consola.error(pc.red("Failed to configure authentication dependencies"));
3882
- if (error instanceof Error) consola.error(pc.red(error.message));
3883
- }
3884
- }
3885
- function generateAuthSecret(length = 32) {
3886
- const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
3887
- let result = "";
3888
- const charactersLength = 62;
3889
- for (let i = 0; i < length; i++) result += characters.charAt(Math.floor(Math.random() * charactersLength));
3890
- return result;
3891
- }
3892
-
3893
3837
  //#endregion
3894
3838
  //#region src/helpers/addons/examples-setup.ts
3895
3839
  async function setupExamples(config) {
@@ -4139,6 +4083,88 @@ async function setupBackendDependencies(config) {
4139
4083
  });
4140
4084
  }
4141
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
+
4142
4168
  //#endregion
4143
4169
  //#region src/helpers/core/env-setup.ts
4144
4170
  async function addEnvVariablesToFile(filePath, variables) {
@@ -4217,6 +4243,33 @@ async function setupEnvironmentVariables(config) {
4217
4243
  value: serverUrl,
4218
4244
  condition: true
4219
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
+ }
4220
4273
  await addEnvVariablesToFile(path.join(clientDir, ".env"), clientVars);
4221
4274
  }
4222
4275
  }
@@ -4234,6 +4287,11 @@ async function setupEnvironmentVariables(config) {
4234
4287
  value: serverUrl,
4235
4288
  condition: true
4236
4289
  }];
4290
+ if (backend === "convex" && auth === "clerk") nativeVars.push({
4291
+ key: "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY",
4292
+ value: "",
4293
+ condition: true
4294
+ });
4237
4295
  await addEnvVariablesToFile(path.join(nativeDir, ".env"), nativeVars);
4238
4296
  }
4239
4297
  }
@@ -5282,7 +5340,7 @@ async function createReadme(projectDir, options) {
5282
5340
  }
5283
5341
  }
5284
5342
  function generateReadmeContent(options) {
5285
- const { projectName, packageManager, database, auth, addons = [], orm = "drizzle", runtime = "bun", frontend = ["tanstack-router"], backend = "hono", api = "trpc" } = options;
5343
+ const { projectName, packageManager, database, auth, addons = [], orm = "drizzle", runtime = "bun", frontend = ["tanstack-router"], backend = "hono", api = "trpc", webDeploy, serverDeploy } = options;
5286
5344
  const isConvex = backend === "convex";
5287
5345
  const hasReactRouter = frontend.includes("react-router");
5288
5346
  const hasNative = frontend.includes("native-nativewind") || frontend.includes("native-unistyles");
@@ -5315,7 +5373,7 @@ This project uses Convex as a backend. You'll need to set up Convex before runni
5315
5373
  ${packageManagerRunCmd} dev:setup
5316
5374
  \`\`\`
5317
5375
 
5318
- Follow the prompts to create a new Convex project and connect it to your application.` : generateDatabaseSetup(database, auth, packageManagerRunCmd, orm, options.dbSetup)}
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)}
5319
5377
 
5320
5378
  Then, run the development server:
5321
5379
 
@@ -5327,10 +5385,12 @@ ${generateRunningInstructions(frontend, backend, webPort, hasNative, isConvex)}
5327
5385
 
5328
5386
  ${addons.includes("pwa") && hasReactRouter ? "\n## PWA Support with React Router v7\n\nThere is a known compatibility issue between VitePWA and React Router v7.\nSee: https://github.com/vite-pwa/vite-plugin-pwa/issues/809\n" : ""}
5329
5387
 
5388
+ ${generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeploy)}
5389
+
5330
5390
  ## Project Structure
5331
5391
 
5332
5392
  \`\`\`
5333
- ${generateProjectStructure(projectName, frontend, backend, addons, isConvex, api)}
5393
+ ${generateProjectStructure(projectName, frontend, backend, addons, isConvex, api, auth)}
5334
5394
  \`\`\`
5335
5395
 
5336
5396
  ## Available Scripts
@@ -5380,7 +5440,7 @@ function generateRunningInstructions(frontend, backend, webPort, hasNative, isCo
5380
5440
  else if (!isBackendNone) instructions.push("The API is running at [http://localhost:3000](http://localhost:3000).");
5381
5441
  return instructions.join("\n");
5382
5442
  }
5383
- function generateProjectStructure(projectName, frontend, backend, addons, isConvex, api) {
5443
+ function generateProjectStructure(projectName, frontend, backend, addons, isConvex, api, auth) {
5384
5444
  const structure = [`${projectName}/`, "├── apps/"];
5385
5445
  const hasFrontendNone = frontend.length === 0 || frontend.includes("none");
5386
5446
  const isBackendNone = backend === "none";
@@ -5410,6 +5470,7 @@ function generateProjectStructure(projectName, frontend, backend, addons, isConv
5410
5470
  if (isConvex) {
5411
5471
  structure.push("├── packages/");
5412
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");
5413
5474
  } else if (!isBackendNone) {
5414
5475
  const backendName = backend[0].toUpperCase() + backend.slice(1);
5415
5476
  const apiName = api !== "none" ? api.toUpperCase() : "";
@@ -5461,7 +5522,10 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
5461
5522
  const dbName = database === "sqlite" ? "SQLite/Turso" : database === "postgres" ? "PostgreSQL" : database === "mysql" ? "MySQL" : database === "mongodb" ? "MongoDB" : "Database";
5462
5523
  addonsList.push(`- **${ormName}** - TypeScript-first ORM`, `- **${dbName}** - Database engine`);
5463
5524
  }
5464
- 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
+ }
5465
5529
  for (const addon of addons) if (addon === "pwa") addonsList.push("- **PWA** - Progressive Web App support");
5466
5530
  else if (addon === "tauri") addonsList.push("- **Tauri** - Build native desktop applications");
5467
5531
  else if (addon === "biome") addonsList.push("- **Biome** - Linting and formatting");
@@ -5470,13 +5534,13 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
5470
5534
  else if (addon === "turborepo") addonsList.push("- **Turborepo** - Optimized monorepo build system");
5471
5535
  return addonsList.join("\n");
5472
5536
  }
5473
- function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup) {
5537
+ function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSetup, serverDeploy) {
5474
5538
  if (database === "none") return "";
5475
5539
  let setup = "## Database Setup\n\n";
5476
5540
  if (database === "sqlite") setup += `This project uses SQLite${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
5477
5541
 
5478
5542
  1. Start the local SQLite database:
5479
- ${dbSetup === "d1" ? "Local development for a Cloudflare D1 database will already be running as part of the `wrangler dev` command." : `\`\`\`bash
5543
+ ${dbSetup === "d1" ? serverDeploy === "alchemy" ? "D1 local development and migrations are handled automatically by Alchemy during dev and deploy." : "Local development for a Cloudflare D1 database will already be running as part of the `wrangler dev` command." : `\`\`\`bash
5480
5544
  cd apps/server && ${packageManagerRunCmd} db:local
5481
5545
  \`\`\`
5482
5546
  `}
@@ -5546,6 +5610,21 @@ function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNati
5546
5610
  - \`cd apps/docs && ${packageManagerRunCmd} build\`: Build documentation site`;
5547
5611
  return scripts;
5548
5612
  }
5613
+ function generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeploy) {
5614
+ const lines = [];
5615
+ if (webDeploy === "alchemy" || serverDeploy === "alchemy") {
5616
+ lines.push("## Deployment (Alchemy)");
5617
+ if (webDeploy === "alchemy" && serverDeploy !== "alchemy") lines.push(`- Web dev: cd apps/web && ${packageManagerRunCmd} dev`, `- Web deploy: cd apps/web && ${packageManagerRunCmd} deploy`, `- Web destroy: cd apps/web && ${packageManagerRunCmd} destroy`);
5618
+ if (serverDeploy === "alchemy" && webDeploy !== "alchemy") lines.push(`- Server dev: cd apps/server && ${packageManagerRunCmd} dev`, `- Server deploy: cd apps/server && ${packageManagerRunCmd} deploy`, `- Server destroy: cd apps/server && ${packageManagerRunCmd} destroy`);
5619
+ if (webDeploy === "alchemy" && serverDeploy === "alchemy") lines.push(`- Dev: ${packageManagerRunCmd} dev`, `- Deploy: ${packageManagerRunCmd} deploy`, `- Destroy: ${packageManagerRunCmd} destroy`);
5620
+ }
5621
+ if (webDeploy === "wrangler" || serverDeploy === "wrangler") {
5622
+ lines.push("\n## Deployment (Cloudflare Wrangler)");
5623
+ if (webDeploy === "wrangler") lines.push(`- Web deploy: cd apps/web && ${packageManagerRunCmd} deploy`);
5624
+ if (serverDeploy === "wrangler") lines.push(`- Server dev: cd apps/server && ${packageManagerRunCmd} dev`, `- Server deploy: cd apps/server && ${packageManagerRunCmd} deploy`);
5625
+ }
5626
+ return lines.length ? `\n${lines.join("\n")}\n` : "";
5627
+ }
5549
5628
 
5550
5629
  //#endregion
5551
5630
  //#region src/helpers/core/git.ts
@@ -5637,6 +5716,7 @@ async function displayPostInstallInstructions(config) {
5637
5716
  const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex) : "";
5638
5717
  const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
5639
5718
  const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
5719
+ const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
5640
5720
  const wranglerDeployInstructions = getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy);
5641
5721
  const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy);
5642
5722
  const hasWeb = frontend?.some((f) => [
@@ -5660,6 +5740,12 @@ async function displayPostInstallInstructions(config) {
5660
5740
  if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} ${packageManager} install\n`;
5661
5741
  if (isConvex) {
5662
5742
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup\n${pc.dim(" (this will guide you through Convex project setup)")}\n`;
5743
+ if (config.auth === "clerk") {
5744
+ output += `${pc.cyan(`${stepCounter++}.`)} ${pc.bold("Clerk Setup:")}\n${pc.dim(" Follow the Convex + Clerk guide to configure authentication:")}\n${pc.cyan(" ")}${pc.underline("https://docs.convex.dev/auth/clerk")}\n\n`;
5745
+ output += `${pc.cyan(`${stepCounter++}.`)} ${pc.bold("Required Environment Variables:")}\n`;
5746
+ output += `${pc.dim(" •")} Set CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard\n`;
5747
+ output += `${pc.dim(" •")} Set CLERK_PUBLISHABLE_KEY in apps/*/.env\n`;
5748
+ }
5663
5749
  output += `${pc.cyan(`${stepCounter++}.`)} Copy environment variables from\n${pc.white(" packages/backend/.env.local")} to ${pc.white("apps/*/.env")}\n`;
5664
5750
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
5665
5751
  } else {
@@ -5684,6 +5770,7 @@ async function displayPostInstallInstructions(config) {
5684
5770
  if (wranglerDeployInstructions) output += `\n${wranglerDeployInstructions.trim()}\n`;
5685
5771
  if (alchemyDeployInstructions) output += `\n${alchemyDeployInstructions.trim()}\n`;
5686
5772
  if (starlightInstructions) output += `\n${starlightInstructions.trim()}\n`;
5773
+ if (clerkInstructions) output += `\n${clerkInstructions.trim()}\n`;
5687
5774
  if (noOrmWarning) output += `\n${noOrmWarning.trim()}\n`;
5688
5775
  if (bunWebNativeWarning) output += `\n${bunWebNativeWarning.trim()}\n`;
5689
5776
  output += `\n${pc.bold("Update all dependencies:\n")}${pc.cyan(tazeCommand)}\n\n`;
@@ -5759,11 +5846,14 @@ function getWranglerDeployInstructions(runCmd, webDeploy, serverDeploy) {
5759
5846
  if (serverDeploy === "wrangler") instructions.push(`${pc.bold("Deploy server to Cloudflare Workers:")}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} run deploy`}`);
5760
5847
  return instructions.length ? `\n${instructions.join("\n")}` : "";
5761
5848
  }
5849
+ function getClerkInstructions() {
5850
+ return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("1.")} Sign up for Clerk at ${pc.underline("https://clerk.com/sign-up")}\n${pc.cyan("2.")} Create a new application in Clerk Dashboard\n${pc.cyan("3.")} Create a JWT template named ${pc.bold("'convex'")} (exact name required)\n${pc.cyan("4.")} Copy your Clerk Frontend API URL (Issuer URL)\n${pc.cyan("5.")} Set environment variables:\n${pc.dim(" •")} CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard\n${pc.dim(" •")} CLERK_PUBLISHABLE_KEY in apps/*/.env\n${pc.cyan("6.")} Follow the complete guide: ${pc.underline("https://docs.convex.dev/auth/clerk")}\n${pc.yellow("NOTE:")} Use Convex's <Authenticated> components instead of Clerk's <SignedIn>`;
5851
+ }
5762
5852
  function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy) {
5763
5853
  const instructions = [];
5764
- if (webDeploy === "alchemy" && serverDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy web with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/web && ${runCmd} alchemy:dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/web && ${runCmd} destroy`}`);
5765
- else if (serverDeploy === "alchemy" && webDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd} alchemy:dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/server && ${runCmd} destroy`}`);
5766
- else if (webDeploy === "alchemy" && serverDeploy === "alchemy") instructions.push(`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd} alchemy:dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`);
5854
+ 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`}`);
5855
+ else if (serverDeploy === "alchemy" && webDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/server && ${runCmd} destroy`}`);
5856
+ else if (webDeploy === "alchemy" && serverDeploy === "alchemy") instructions.push(`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`);
5767
5857
  return instructions.length ? `\n${instructions.join("\n")}` : "";
5768
5858
  }
5769
5859
 
@@ -5961,8 +6051,8 @@ async function createProject(options) {
5961
6051
  if (!isConvex) {
5962
6052
  await setupDbOrmTemplates(projectDir, options);
5963
6053
  await setupDockerComposeTemplates(projectDir, options);
5964
- await setupAuthTemplate(projectDir, options);
5965
6054
  }
6055
+ await setupAuthTemplate(projectDir, options);
5966
6056
  if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamplesTemplate(projectDir, options);
5967
6057
  await setupAddonsTemplate(projectDir, options);
5968
6058
  await setupDeploymentTemplates(projectDir, options);
@@ -5974,12 +6064,12 @@ async function createProject(options) {
5974
6064
  if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamples(options);
5975
6065
  }
5976
6066
  if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
5977
- if (!isConvex && options.auth) await setupAuth(options);
6067
+ if (options.auth && options.auth !== "none") await setupAuth(options);
5978
6068
  await handleExtras(projectDir, options);
5979
- await setupWebDeploy(options);
5980
- await setupServerDeploy(options);
5981
6069
  await setupEnvironmentVariables(options);
5982
6070
  await updatePackageConfigurations(projectDir, options);
6071
+ await setupWebDeploy(options);
6072
+ await setupServerDeploy(options);
5983
6073
  await createReadme(projectDir, options);
5984
6074
  await writeBtsConfig(options);
5985
6075
  await formatProjectWithBiome(projectDir);
@@ -6053,7 +6143,7 @@ async function createProjectHandler(input) {
6053
6143
  frontend: [],
6054
6144
  addons: [],
6055
6145
  examples: [],
6056
- auth: false,
6146
+ auth: "none",
6057
6147
  git: false,
6058
6148
  packageManager: "npm",
6059
6149
  install: false,
@@ -6088,8 +6178,8 @@ async function createProjectHandler(input) {
6088
6178
  };
6089
6179
  coerceBackendPresets(config);
6090
6180
  validateConfigCompatibility(config, providedFlags, cliInput);
6091
- 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");
6092
- 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");
6181
+ 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`);
6182
+ 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");
6093
6183
  log.info(pc.yellow("Using default/flag options (config prompts skipped):"));
6094
6184
  log.message(displayConfig(config));
6095
6185
  log.message("");
@@ -6295,7 +6385,7 @@ const router = t.router({
6295
6385
  verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
6296
6386
  database: DatabaseSchema.optional(),
6297
6387
  orm: ORMSchema.optional(),
6298
- auth: z.boolean().optional(),
6388
+ auth: AuthSchema.optional(),
6299
6389
  frontend: z.array(FrontendSchema).optional(),
6300
6390
  addons: z.array(AddonsSchema).optional(),
6301
6391
  examples: z.array(ExamplesSchema).optional(),
@@ -6386,7 +6476,7 @@ function createBtsCli() {
6386
6476
  * backend: "hono",
6387
6477
  * database: "sqlite",
6388
6478
  * orm: "drizzle",
6389
- * auth: true,
6479
+ * auth: "better-auth",
6390
6480
  * addons: ["biome", "turborepo"],
6391
6481
  * packageManager: "bun",
6392
6482
  * install: false,