create-better-fullstack 1.3.16 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { s as createBtsCli } from "./src-zCIIp5Gn.mjs";
2
+ import { s as createBtsCli } from "./src-p54ND5wJ.mjs";
3
3
 
4
4
  //#region src/cli.ts
5
5
  createBtsCli().run();
package/dist/index.d.mts CHANGED
@@ -252,6 +252,7 @@ declare const router: {
252
252
  examples: z.ZodOptional<z.ZodArray<z.ZodEnum<{
253
253
  none: "none";
254
254
  ai: "ai";
255
+ "chat-sdk": "chat-sdk";
255
256
  }>>>;
256
257
  git: z.ZodOptional<z.ZodBoolean>;
257
258
  packageManager: z.ZodOptional<z.ZodEnum<{
@@ -452,7 +453,7 @@ declare const router: {
452
453
  runtime: "none" | "bun" | "node" | "workers";
453
454
  frontend: ("none" | "tanstack-router" | "react-router" | "tanstack-start" | "next" | "nuxt" | "native-bare" | "native-uniwind" | "native-unistyles" | "svelte" | "solid" | "astro" | "qwik" | "angular" | "redwood" | "fresh")[];
454
455
  addons: ("none" | "pwa" | "tauri" | "starlight" | "biome" | "lefthook" | "husky" | "ruler" | "mcp" | "skills" | "turborepo" | "fumadocs" | "ultracite" | "oxlint" | "opentui" | "wxt" | "msw" | "storybook")[];
455
- examples: ("ai" | "none")[];
456
+ examples: ("ai" | "none" | "chat-sdk")[];
456
457
  auth: "none" | "better-auth" | "clerk" | "nextauth" | "stack-auth" | "supabase-auth" | "auth0";
457
458
  payments: "none" | "polar" | "stripe" | "lemon-squeezy" | "paddle" | "dodo";
458
459
  git: boolean;
@@ -531,7 +532,7 @@ declare const router: {
531
532
  runtime: "none" | "bun" | "node" | "workers";
532
533
  frontend: ("none" | "tanstack-router" | "react-router" | "tanstack-start" | "next" | "nuxt" | "native-bare" | "native-uniwind" | "native-unistyles" | "svelte" | "solid" | "astro" | "qwik" | "angular" | "redwood" | "fresh")[];
533
534
  addons: ("none" | "pwa" | "tauri" | "starlight" | "biome" | "lefthook" | "husky" | "ruler" | "mcp" | "skills" | "turborepo" | "fumadocs" | "ultracite" | "oxlint" | "opentui" | "wxt" | "msw" | "storybook")[];
534
- examples: ("ai" | "none")[];
535
+ examples: ("ai" | "none" | "chat-sdk")[];
535
536
  auth: "none" | "better-auth" | "clerk" | "nextauth" | "stack-auth" | "supabase-auth" | "auth0";
536
537
  payments: "none" | "polar" | "stripe" | "lemon-squeezy" | "paddle" | "dodo";
537
538
  git: boolean;
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { a as builder, c as createVirtual, d as history, f as router, i as add, l as docs, n as TEMPLATE_COUNT, o as create, p as sponsors, r as VirtualFileSystem, s as createBtsCli, t as EMBEDDED_TEMPLATES, u as generateVirtualProject } from "./src-zCIIp5Gn.mjs";
2
+ import { a as builder, c as createVirtual, d as history, f as router, i as add, l as docs, n as TEMPLATE_COUNT, o as create, p as sponsors, r as VirtualFileSystem, s as createBtsCli, t as EMBEDDED_TEMPLATES, u as generateVirtualProject } from "./src-p54ND5wJ.mjs";
3
3
 
4
4
  export { EMBEDDED_TEMPLATES, TEMPLATE_COUNT, VirtualFileSystem, add, builder, create, createBtsCli, createVirtual, docs, generateVirtualProject, history, router, sponsors };
@@ -18,8 +18,8 @@ import { AsyncLocalStorage } from "node:async_hooks";
18
18
  import { ConfirmPrompt, GroupMultiSelectPrompt, MultiSelectPrompt, SelectPrompt, isCancel as isCancel$1 } from "@clack/core";
19
19
  import * as JSONC from "jsonc-parser";
20
20
  import { $, execa } from "execa";
21
- import { format } from "oxfmt";
22
21
  import os$1 from "node:os";
22
+ import { format } from "oxfmt";
23
23
 
24
24
  //#region src/utils/get-package-manager.ts
25
25
  const getUserPkgManager = () => {
@@ -542,6 +542,7 @@ async function historyHandler(input) {
542
542
  */
543
543
  function formatUpdate(info) {
544
544
  const colorFn = {
545
+ downgrade: pc.red,
545
546
  major: pc.red,
546
547
  minor: pc.yellow,
547
548
  patch: pc.green,
@@ -597,6 +598,7 @@ async function interactiveUpdate(updates) {
597
598
  console.log(formatUpdate(update));
598
599
  if (update.ecosystem) console.log(pc.dim(` Ecosystem: ${update.ecosystem}`));
599
600
  if (update.updateType === "major") console.log(pc.yellow(" Warning: Breaking changes possible. Check changelog."));
601
+ else if (update.updateType === "downgrade") console.log(pc.red(" Warning: npm latest is lower than current pinned version. Review carefully."));
600
602
  const action = await select({
601
603
  message: "What would you like to do?",
602
604
  options: [
@@ -652,6 +654,7 @@ async function updateDepsHandler(options) {
652
654
  console.log(generateCliReport(result));
653
655
  if (check || result.outdated.length === 0) return;
654
656
  let toApply = [];
657
+ const downgradeCount = result.outdated.filter((u) => u.updateType === "downgrade").length;
655
658
  if (patch) {
656
659
  toApply = result.outdated.filter((u) => u.updateType === "patch" || u.updateType === "minor");
657
660
  if (toApply.length === 0) {
@@ -659,6 +662,7 @@ async function updateDepsHandler(options) {
659
662
  return;
660
663
  }
661
664
  log.info(`Found ${toApply.length} patch/minor updates to apply automatically.`);
665
+ if (downgradeCount > 0) log.warn(`${downgradeCount} downgrade${downgradeCount === 1 ? "" : "s"} detected and excluded from --patch mode.`);
662
666
  const shouldProceed = await confirm({ message: `Apply ${toApply.length} safe updates?` });
663
667
  if (isCancel(shouldProceed) || !shouldProceed) {
664
668
  log.info("Cancelled.");
@@ -672,7 +676,7 @@ async function updateDepsHandler(options) {
672
676
  return;
673
677
  }
674
678
  } else {
675
- const shouldProceed = await confirm({ message: `Apply all ${result.outdated.length} updates?` });
679
+ const shouldProceed = await confirm({ message: downgradeCount > 0 ? `Apply all ${result.outdated.length} updates (including ${downgradeCount} downgrade${downgradeCount === 1 ? "" : "s"})?` : `Apply all ${result.outdated.length} updates?` });
676
680
  if (isCancel(shouldProceed) || !shouldProceed) {
677
681
  log.info("Cancelled. Use --check to only view updates without prompting.");
678
682
  return;
@@ -925,7 +929,7 @@ const WORKERS_COMPATIBLE_BACKENDS = [
925
929
  ];
926
930
  function validateWorkersCompatibility(providedFlags, options, config) {
927
931
  if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && !WORKERS_COMPATIBLE_BACKENDS.includes(config.backend)) incompatibilityError({
928
- message: "Cloudflare Workers runtime requires a compatible backend.",
932
+ message: "In Better-Fullstack, Cloudflare Workers runtime is currently supported only with compatible backends (Hono, Nitro, or Fets).",
929
933
  provided: {
930
934
  runtime: "workers",
931
935
  backend: config.backend
@@ -938,7 +942,7 @@ function validateWorkersCompatibility(providedFlags, options, config) {
938
942
  ]
939
943
  });
940
944
  if (providedFlags.has("backend") && config.backend && !WORKERS_COMPATIBLE_BACKENDS.includes(config.backend) && config.runtime === "workers") incompatibilityError({
941
- message: `Backend '${config.backend}' is not compatible with Workers runtime.`,
945
+ message: `In Better-Fullstack, backend '${config.backend}' is currently not available with Cloudflare Workers runtime.`,
942
946
  provided: {
943
947
  backend: config.backend,
944
948
  runtime: "workers"
@@ -946,7 +950,7 @@ function validateWorkersCompatibility(providedFlags, options, config) {
946
950
  suggestions: ["Use --backend hono, --backend nitro, or --backend fets", "Choose a different runtime (node, bun)"]
947
951
  });
948
952
  if (providedFlags.has("runtime") && options.runtime === "workers" && config.database === "mongodb") incompatibilityError({
949
- message: "Cloudflare Workers runtime is not compatible with MongoDB.",
953
+ message: "In Better-Fullstack, Cloudflare Workers runtime is currently not available with MongoDB.",
950
954
  provided: {
951
955
  runtime: "workers",
952
956
  database: "mongodb"
@@ -954,7 +958,7 @@ function validateWorkersCompatibility(providedFlags, options, config) {
954
958
  suggestions: ["Use a different database (postgres, sqlite, mysql)", "Choose a different runtime (node, bun)"]
955
959
  });
956
960
  if (providedFlags.has("runtime") && options.runtime === "workers" && config.dbSetup === "docker") incompatibilityError({
957
- message: "Cloudflare Workers runtime is not compatible with Docker setup.",
961
+ message: "In Better-Fullstack, Cloudflare Workers runtime is currently not available with Docker database setup.",
958
962
  provided: {
959
963
  runtime: "workers",
960
964
  "db-setup": "docker"
@@ -962,7 +966,7 @@ function validateWorkersCompatibility(providedFlags, options, config) {
962
966
  suggestions: ["Use --db-setup d1 for SQLite", "Choose a different runtime (node, bun)"]
963
967
  });
964
968
  if (providedFlags.has("database") && config.database === "mongodb" && config.runtime === "workers") incompatibilityError({
965
- message: "MongoDB is not compatible with Cloudflare Workers runtime.",
969
+ message: "In Better-Fullstack, MongoDB is currently not available with Cloudflare Workers runtime.",
966
970
  provided: {
967
971
  database: "mongodb",
968
972
  runtime: "workers"
@@ -1058,6 +1062,9 @@ function isFrontendAllowedWithBackend(frontend, backend, auth) {
1058
1062
  "solid"
1059
1063
  ].includes(frontend)) return false;
1060
1064
  }
1065
+ if (auth === "clerk" && backend === "self") {
1066
+ if (!["next", "tanstack-start"].includes(frontend)) return false;
1067
+ }
1061
1068
  if (auth === "nextauth") {
1062
1069
  if (frontend !== "next") return false;
1063
1070
  if (backend !== "self") return false;
@@ -1068,29 +1075,57 @@ function isFrontendAllowedWithBackend(frontend, backend, auth) {
1068
1075
  }
1069
1076
  return true;
1070
1077
  }
1078
+ function validateClerkCompatibility(auth, backend, frontends = []) {
1079
+ if (auth !== "clerk") return;
1080
+ if (backend === "convex") {
1081
+ const incompatibleFrontends = frontends.filter((f) => [
1082
+ "nuxt",
1083
+ "svelte",
1084
+ "solid"
1085
+ ].includes(f));
1086
+ if (incompatibleFrontends.length > 0) exitWithError(`In Better-Fullstack, Clerk + Convex is not compatible with the following frontends: ${incompatibleFrontends.join(", ")}. Please choose a different frontend or auth provider.`);
1087
+ return;
1088
+ }
1089
+ if (backend === "self") {
1090
+ if (frontends.some((f) => [
1091
+ "native-bare",
1092
+ "native-uniwind",
1093
+ "native-unistyles"
1094
+ ].includes(f))) exitWithError("In Better-Fullstack, Clerk with the 'self' backend is currently supported only for web-only Next.js or TanStack Start projects (no native companion app). Please remove the native frontend or choose a different auth provider.");
1095
+ const hasNextJs = frontends.includes("next");
1096
+ const hasTanStackStart = frontends.includes("tanstack-start");
1097
+ if (!hasNextJs && !hasTanStackStart) {
1098
+ if (frontends.includes("astro")) exitWithError("In Better-Fullstack, Clerk is not yet supported for Astro fullstack projects. Please use '--frontend next' or '--frontend tanstack-start' with '--backend self', or choose a different auth provider.");
1099
+ if (frontends.includes("nuxt")) exitWithError("In Better-Fullstack, Clerk is not yet supported for Nuxt fullstack projects. Please use '--frontend next' or '--frontend tanstack-start' with '--backend self', or choose a different auth provider.");
1100
+ exitWithError("In Better-Fullstack, Clerk with the 'self' backend currently requires the Next.js or TanStack Start frontend. Please use '--frontend next' or '--frontend tanstack-start', or choose a different auth provider.");
1101
+ }
1102
+ return;
1103
+ }
1104
+ exitWithError("In Better-Fullstack, Clerk authentication is currently supported with the Convex backend, or with the 'self' backend when using Next.js or TanStack Start. Please choose a supported backend/frontend combination or a different auth provider.");
1105
+ }
1071
1106
  function validateNextAuthCompatibility(auth, backend, frontends = []) {
1072
1107
  if (auth !== "nextauth") return;
1073
1108
  const hasNextJs = frontends.includes("next");
1074
- if (backend !== "self") exitWithError("Auth.js (NextAuth) is only supported with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
1075
- if (!hasNextJs) exitWithError("Auth.js (NextAuth) requires Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
1109
+ if (backend !== "self") exitWithError("In Better-Fullstack, Auth.js (NextAuth) is currently supported only with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
1110
+ if (!hasNextJs) exitWithError("In Better-Fullstack, Auth.js (NextAuth) currently requires the Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
1076
1111
  }
1077
1112
  function validateStackAuthCompatibility(auth, backend, frontends = []) {
1078
1113
  if (auth !== "stack-auth") return;
1079
1114
  const hasNextJs = frontends.includes("next");
1080
- if (backend !== "self") exitWithError("Stack Auth is only supported with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
1081
- if (!hasNextJs) exitWithError("Stack Auth requires Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
1115
+ if (backend !== "self") exitWithError("In Better-Fullstack, Stack Auth is currently supported only with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
1116
+ if (!hasNextJs) exitWithError("In Better-Fullstack, Stack Auth currently requires the Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
1082
1117
  }
1083
1118
  function validateSupabaseAuthCompatibility(auth, backend, frontends = []) {
1084
1119
  if (auth !== "supabase-auth") return;
1085
1120
  const hasNextJs = frontends.includes("next");
1086
- if (backend !== "self") exitWithError("Supabase Auth is only supported with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
1087
- if (!hasNextJs) exitWithError("Supabase Auth requires Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
1121
+ if (backend !== "self") exitWithError("In Better-Fullstack, Supabase Auth is currently supported only with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
1122
+ if (!hasNextJs) exitWithError("In Better-Fullstack, Supabase Auth currently requires the Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
1088
1123
  }
1089
1124
  function validateAuth0Compatibility(auth, backend, frontends = []) {
1090
1125
  if (auth !== "auth0") return;
1091
1126
  const hasNextJs = frontends.includes("next");
1092
- if (backend !== "self") exitWithError("Auth0 is only supported with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
1093
- if (!hasNextJs) exitWithError("Auth0 requires Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
1127
+ if (backend !== "self") exitWithError("In Better-Fullstack, Auth0 is currently supported only with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
1128
+ if (!hasNextJs) exitWithError("In Better-Fullstack, Auth0 currently requires the Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
1094
1129
  }
1095
1130
  function allowedApisForFrontends(frontends = [], astroIntegration) {
1096
1131
  const includesNuxt = frontends.includes("nuxt");
@@ -1125,6 +1160,24 @@ function isExampleAIAllowed(backend, frontends = []) {
1125
1160
  }
1126
1161
  return true;
1127
1162
  }
1163
+ function hasExampleChatSdkSelfFrontend(frontends = []) {
1164
+ return frontends.some((f) => [
1165
+ "next",
1166
+ "tanstack-start",
1167
+ "nuxt"
1168
+ ].includes(f));
1169
+ }
1170
+ function isExampleChatSdkAllowed(backend, frontends = [], runtime) {
1171
+ if (!backend || backend === "none" || backend === "convex") return false;
1172
+ if (backend === "self") return hasExampleChatSdkSelfFrontend(frontends);
1173
+ if (backend === "hono") return runtime === "node";
1174
+ return false;
1175
+ }
1176
+ function requiresChatSdkVercelAI(backend, frontends = [], runtime) {
1177
+ if (backend === "self" && frontends.includes("nuxt")) return true;
1178
+ if (backend === "hono" && runtime === "node") return true;
1179
+ return false;
1180
+ }
1128
1181
  function validateWebDeployRequiresWebFrontend(webDeploy, hasWebFrontendFlag) {
1129
1182
  if (webDeploy && webDeploy !== "none" && !hasWebFrontendFlag) exitWithError("'--web-deploy' requires a web frontend. Please select a web frontend or set '--web-deploy none'.");
1130
1183
  }
@@ -1164,7 +1217,7 @@ function validatePaymentsCompatibility(payments, auth, _backend, frontends = [])
1164
1217
  if (web.length === 0 && frontends.length > 0) exitWithError("Polar payments requires a web frontend or no frontend. Please select a web frontend or choose a different payments provider.");
1165
1218
  }
1166
1219
  }
1167
- function validateExamplesCompatibility(examples, backend, frontend) {
1220
+ function validateExamplesCompatibility(examples, backend, frontend, runtime, ai) {
1168
1221
  const examplesArr = examples ?? [];
1169
1222
  if (examplesArr.length === 0 || examplesArr.includes("none")) return;
1170
1223
  if (examplesArr.includes("ai") && (frontend ?? []).includes("solid")) exitWithError("The 'ai' example is not compatible with the Solid frontend.");
@@ -1174,6 +1227,17 @@ function validateExamplesCompatibility(examples, backend, frontend) {
1174
1227
  const includesSvelte = frontendArr.includes("svelte");
1175
1228
  if (includesNuxt || includesSvelte) exitWithError("The 'ai' example with Convex backend only supports React-based frontends (Next.js, TanStack Router, TanStack Start, React Router). Svelte and Nuxt are not supported with Convex AI.");
1176
1229
  }
1230
+ if (examplesArr.includes("chat-sdk")) {
1231
+ const frontendArr = frontend ?? [];
1232
+ if (!isExampleChatSdkAllowed(backend, frontendArr, runtime)) {
1233
+ if (backend === "none") exitWithError("The 'chat-sdk' example requires a backend.");
1234
+ if (backend === "convex") exitWithError("The 'chat-sdk' example is not supported with the Convex backend in v1. Use self backend (Next.js, TanStack Start, Nuxt) or Hono with Node runtime.");
1235
+ if (backend === "self") exitWithError("The 'chat-sdk' example with self backend only supports Next.js, TanStack Start, or Nuxt frontends in v1.");
1236
+ if (backend === "hono" && runtime !== "node") exitWithError("The 'chat-sdk' example with Hono requires '--runtime node' in v1 (Bun/Workers not supported yet).");
1237
+ exitWithError("The 'chat-sdk' example is only supported with self backend (Next.js, TanStack Start, Nuxt) or Hono with Node runtime in v1.");
1238
+ }
1239
+ if (requiresChatSdkVercelAI(backend, frontendArr, runtime) && ai && ai !== "vercel-ai") exitWithError("The 'chat-sdk' example requires '--ai vercel-ai' for the selected stack in v1 (Nuxt Discord and Hono GitHub profiles).");
1240
+ }
1177
1241
  }
1178
1242
  /**
1179
1243
  * Validates that a UI library is compatible with the selected frontend(s)
@@ -2404,7 +2468,8 @@ const SKILL_SOURCES = {
2404
2468
  "prisma/skills": { label: "Prisma" },
2405
2469
  "elysiajs/skills": { label: "ElysiaJS" },
2406
2470
  "waynesutton/convexskills": { label: "Convex" },
2407
- "msmps/opentui-skill": { label: "OpenTUI Platform" }
2471
+ "msmps/opentui-skill": { label: "OpenTUI Platform" },
2472
+ "haydenbleasel/ultracite": { label: "Ultracite" }
2408
2473
  };
2409
2474
  const AVAILABLE_AGENTS = [
2410
2475
  {
@@ -2536,6 +2601,7 @@ function getRecommendedSourceKeys(config) {
2536
2601
  if (backend === "elysia") sources.push("elysiajs/skills");
2537
2602
  if (backend === "convex") sources.push("waynesutton/convexskills");
2538
2603
  if (addons.includes("opentui")) sources.push("msmps/opentui-skill");
2604
+ if (addons.includes("ultracite")) sources.push("haydenbleasel/ultracite");
2539
2605
  return sources;
2540
2606
  }
2541
2607
  const CURATED_SKILLS_BY_SOURCE = {
@@ -2593,7 +2659,8 @@ const CURATED_SKILLS_BY_SOURCE = {
2593
2659
  "convex-migrations",
2594
2660
  "convex-security-check"
2595
2661
  ],
2596
- "msmps/opentui-skill": () => ["opentui"]
2662
+ "msmps/opentui-skill": () => ["opentui"],
2663
+ "haydenbleasel/ultracite": () => ["ultracite"]
2597
2664
  };
2598
2665
  function getCuratedSkillNamesForSourceKey(sourceKey, config) {
2599
2666
  return CURATED_SKILLS_BY_SOURCE[sourceKey](config);
@@ -3563,16 +3630,26 @@ async function getAuthChoice(auth, backend, frontend) {
3563
3630
  if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
3564
3631
  return response$1;
3565
3632
  }
3566
- const supportsNextJsAuth = frontend?.includes("next") && backend === "self";
3633
+ const hasNextJs = frontend?.includes("next");
3634
+ const hasTanStackStart = frontend?.includes("tanstack-start");
3635
+ const isSelfBackend = backend === "self";
3636
+ const supportsNextJsAuth = hasNextJs && isSelfBackend;
3637
+ const hasNativeFrontend$2 = frontend?.some((f) => [
3638
+ "native-bare",
3639
+ "native-uniwind",
3640
+ "native-unistyles"
3641
+ ].includes(f));
3642
+ const supportsClerkSelf = isSelfBackend && !hasNativeFrontend$2 && Boolean(hasNextJs || hasTanStackStart);
3567
3643
  const options = [{
3568
3644
  value: "better-auth",
3569
3645
  label: "Better-Auth",
3570
3646
  hint: "comprehensive auth framework for TypeScript"
3571
- }, {
3647
+ }];
3648
+ if (supportsClerkSelf) options.push({
3572
3649
  value: "clerk",
3573
3650
  label: "Clerk",
3574
3651
  hint: "More than auth, Complete User Management"
3575
- }];
3652
+ });
3576
3653
  if (supportsNextJsAuth) {
3577
3654
  options.push({
3578
3655
  value: "nextauth",
@@ -4076,7 +4153,7 @@ async function getEmailChoice(email, backend) {
4076
4153
 
4077
4154
  //#endregion
4078
4155
  //#region src/prompts/examples.ts
4079
- async function getExamplesChoice(examples, frontends, backend) {
4156
+ async function getExamplesChoice(examples, frontends, backend, runtime) {
4080
4157
  if (examples !== void 0) return examples;
4081
4158
  if (backend === "none") return [];
4082
4159
  let response = [];
@@ -4086,6 +4163,11 @@ async function getExamplesChoice(examples, frontends, backend) {
4086
4163
  label: "AI Chat",
4087
4164
  hint: "A simple AI chat interface using AI SDK"
4088
4165
  });
4166
+ if (isExampleChatSdkAllowed(backend, frontends ?? [], runtime)) options.push({
4167
+ value: "chat-sdk",
4168
+ label: "Chat SDK Bots",
4169
+ hint: "Framework-specific Chat SDK bot example (Slack/Discord/GitHub depending on stack)"
4170
+ });
4089
4171
  if (options.length === 0) return [];
4090
4172
  response = await navigableMultiselect({
4091
4173
  message: "Include examples",
@@ -5621,7 +5703,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5621
5703
  },
5622
5704
  examples: ({ results }) => {
5623
5705
  if (results.ecosystem !== "typescript") return Promise.resolve([]);
5624
- return getExamplesChoice(flags.examples, results.frontend, results.backend);
5706
+ return getExamplesChoice(flags.examples, results.frontend, results.backend, results.runtime);
5625
5707
  },
5626
5708
  dbSetup: ({ results }) => {
5627
5709
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
@@ -5637,6 +5719,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5637
5719
  },
5638
5720
  ai: ({ results }) => {
5639
5721
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
5722
+ if (flags.ai === void 0 && results.examples?.includes("chat-sdk") && requiresChatSdkVercelAI(results.backend, results.frontend, results.runtime)) return Promise.resolve("vercel-ai");
5640
5723
  return getAIChoice(flags.ai);
5641
5724
  },
5642
5725
  validation: ({ results }) => {
@@ -6510,7 +6593,7 @@ function validateDatabaseOrmAuth(cfg, flags) {
6510
6593
  suggestions: ["Use --orm mongoose or --orm prisma for MongoDB", "Choose a different database (postgres, sqlite, mysql)"]
6511
6594
  });
6512
6595
  if (has("database") && has("orm") && db === "mongodb" && orm && orm !== "mongoose" && orm !== "prisma" && orm !== "none") incompatibilityError({
6513
- message: "MongoDB only works with Mongoose or Prisma ORM.",
6596
+ message: "In Better-Fullstack, MongoDB is currently supported only with Mongoose or Prisma ORM.",
6514
6597
  provided: {
6515
6598
  database: "mongodb",
6516
6599
  orm
@@ -6593,7 +6676,7 @@ function validateDatabaseSetup(config, providedFlags) {
6593
6676
  runtime: "workers",
6594
6677
  errorMessage: "Cloudflare D1 setup requires SQLite database and Cloudflare Workers runtime."
6595
6678
  },
6596
- docker: { errorMessage: "Docker setup is not compatible with SQLite database or Cloudflare Workers runtime." },
6679
+ docker: { errorMessage: "In Better-Fullstack, Docker setup is currently not available with SQLite database or Cloudflare Workers runtime." },
6597
6680
  none: { errorMessage: "" }
6598
6681
  };
6599
6682
  if (dbSetup && dbSetup !== "none") {
@@ -6603,8 +6686,8 @@ function validateDatabaseSetup(config, providedFlags) {
6603
6686
  } else if (validation.database && database !== validation.database) exitWithError(validation.errorMessage);
6604
6687
  if (validation.runtime && runtime !== validation.runtime) exitWithError(validation.errorMessage);
6605
6688
  if (dbSetup === "docker") {
6606
- if (database === "sqlite") exitWithError("Docker setup is not compatible with SQLite database. SQLite is file-based and doesn't require Docker. Please use '--database postgres', '--database mysql', '--database mongodb', or choose a different setup.");
6607
- if (runtime === "workers") exitWithError("Docker setup is not compatible with Cloudflare Workers runtime. Workers runtime uses serverless databases (D1) and doesn't support local Docker containers. Please use '--db-setup d1' for SQLite or choose a different runtime.");
6689
+ if (database === "sqlite") exitWithError("In Better-Fullstack, Docker setup is currently not available with SQLite database. SQLite is file-based and doesn't require Docker. Please use '--database postgres', '--database mysql', '--database mongodb', or choose a different setup.");
6690
+ if (runtime === "workers") exitWithError("In Better-Fullstack, Docker setup is currently not available with Cloudflare Workers runtime. Workers runtime uses serverless databases (D1) and doesn't support local Docker containers. Please use '--db-setup d1' for SQLite or choose a different runtime.");
6608
6691
  }
6609
6692
  }
6610
6693
  }
@@ -6723,15 +6806,6 @@ function validateAdonisJSConstraints(config, providedFlags) {
6723
6806
  }
6724
6807
  function validateBackendConstraints(config, providedFlags, options) {
6725
6808
  const { backend } = config;
6726
- 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.");
6727
- if (backend === "convex" && config.auth === "clerk" && config.frontend) {
6728
- const incompatibleFrontends = config.frontend.filter((f) => [
6729
- "nuxt",
6730
- "svelte",
6731
- "solid"
6732
- ].includes(f));
6733
- if (incompatibleFrontends.length > 0) exitWithError(`Clerk authentication is not compatible with the following frontends: ${incompatibleFrontends.join(", ")}. Please choose a different frontend or auth provider.`);
6734
- }
6735
6809
  if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none" && backend !== "self" && backend !== "encore") {
6736
6810
  if (providedFlags.has("runtime") && options.runtime === "none") exitWithError("'--runtime none' is only supported with '--backend convex', '--backend none', '--backend self', or '--backend encore'. Please choose 'bun', 'node', or remove the --runtime flag.");
6737
6811
  }
@@ -6770,8 +6844,9 @@ function validateFullConfig(config, providedFlags, options) {
6770
6844
  validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
6771
6845
  config.addons = [...new Set(config.addons)];
6772
6846
  }
6773
- validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? []);
6847
+ validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
6774
6848
  validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend ?? []);
6849
+ validateClerkCompatibility(config.auth, config.backend, config.frontend ?? []);
6775
6850
  validateNextAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
6776
6851
  validateStackAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
6777
6852
  validateSupabaseAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
@@ -6786,12 +6861,13 @@ function validateConfigForProgrammaticUse(config) {
6786
6861
  if (config.frontend && config.frontend.length > 0) ensureSingleWebAndNative(config.frontend);
6787
6862
  validateApiFrontendCompatibility(config.api, config.frontend, config.astroIntegration);
6788
6863
  validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
6864
+ validateClerkCompatibility(config.auth, config.backend, config.frontend ?? []);
6789
6865
  validateNextAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
6790
6866
  validateStackAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
6791
6867
  validateSupabaseAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
6792
6868
  validateAuth0Compatibility(config.auth, config.backend, config.frontend ?? []);
6793
6869
  if (config.addons && config.addons.length > 0) validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
6794
- validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? []);
6870
+ validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
6795
6871
  validateUILibraryFrontendCompatibility(config.uiLibrary, config.frontend ?? [], config.astroIntegration);
6796
6872
  validateUILibraryCSSFrameworkCompatibility(config.uiLibrary, config.cssFramework);
6797
6873
  validatePeerDependencies(config);
@@ -8113,7 +8189,7 @@ async function displayPostInstallInstructions(config) {
8113
8189
  const nativeInstructions = (frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles")) && backend !== "none" ? getNativeInstructions(isConvex, isBackendSelf, frontend || [], runCmd) : "";
8114
8190
  const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
8115
8191
  const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
8116
- const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
8192
+ const clerkInstructions = config.auth === "clerk" ? getClerkInstructions(config.backend, config.frontend ?? []) : "";
8117
8193
  const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions(backend) : "";
8118
8194
  const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
8119
8195
  const hasWeb = frontend?.some((f) => WEB_FRAMEWORKS.includes(f));
@@ -8244,8 +8320,13 @@ function getNoOrmWarning() {
8244
8320
  function getBunWebNativeWarning() {
8245
8321
  return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
8246
8322
  }
8247
- function getClerkInstructions() {
8248
- 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`;
8323
+ function getClerkInstructions(backend, frontend) {
8324
+ if (backend === "convex") 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`;
8325
+ if (backend === "self" && (frontend.includes("next") || frontend.includes("tanstack-start"))) {
8326
+ const publishableKeyVar = frontend.includes("next") ? "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY" : "VITE_CLERK_PUBLISHABLE_KEY";
8327
+ return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Create an application in ${pc.underline("https://dashboard.clerk.com/")}\n${pc.cyan("•")} Set ${publishableKeyVar} in ${pc.white("apps/web/.env")}\n${pc.cyan("•")} Set CLERK_SECRET_KEY in ${pc.white("apps/web/.env")}\n${pc.cyan("•")} Clerk middleware and a protected dashboard route are already generated`;
8328
+ }
8329
+ return "";
8249
8330
  }
8250
8331
  function getBetterAuthConvexInstructions(hasWeb, webPort, packageManager) {
8251
8332
  const cmd = packageManager === "npm" ? "npx" : packageManager;
@@ -8433,7 +8514,7 @@ async function setPackageManagerVersion(projectDir, packageManager) {
8433
8514
  const pkgJsonPath = path.join(projectDir, "package.json");
8434
8515
  if (!await fs.pathExists(pkgJsonPath)) return;
8435
8516
  try {
8436
- const { stdout } = await $`${packageManager} -v`;
8517
+ const { stdout } = await $({ cwd: os$1.tmpdir() })`${packageManager} -v`;
8437
8518
  const version = stdout.trim();
8438
8519
  const pkgJson = await fs.readJson(pkgJsonPath);
8439
8520
  pkgJson.packageManager = `${packageManager}@${version}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-fullstack",
3
- "version": "1.3.16",
3
+ "version": "1.4.0",
4
4
  "description": "A CLI-first toolkit for building Full Stack applications. Skip the configuration. Ship the code.",
5
5
  "keywords": [
6
6
  "better-auth",
@@ -76,8 +76,8 @@
76
76
  "prepublishOnly": "npm run build"
77
77
  },
78
78
  "dependencies": {
79
- "@better-fullstack/template-generator": "^1.3.16",
80
- "@better-fullstack/types": "^1.3.16",
79
+ "@better-fullstack/template-generator": "^1.4.0",
80
+ "@better-fullstack/types": "^1.4.0",
81
81
  "@clack/core": "^0.5.0",
82
82
  "@clack/prompts": "^1.0.0-alpha.8",
83
83
  "@orpc/server": "^1.13.0",