create-better-fullstack 1.3.17 → 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 +1 -1
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +1 -1
- package/dist/{src-BYQLJiGD.mjs → src-p54ND5wJ.mjs} +118 -41
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
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-
|
|
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 = () => {
|
|
@@ -929,7 +929,7 @@ const WORKERS_COMPATIBLE_BACKENDS = [
|
|
|
929
929
|
];
|
|
930
930
|
function validateWorkersCompatibility(providedFlags, options, config) {
|
|
931
931
|
if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && !WORKERS_COMPATIBLE_BACKENDS.includes(config.backend)) incompatibilityError({
|
|
932
|
-
message: "Cloudflare Workers runtime
|
|
932
|
+
message: "In Better-Fullstack, Cloudflare Workers runtime is currently supported only with compatible backends (Hono, Nitro, or Fets).",
|
|
933
933
|
provided: {
|
|
934
934
|
runtime: "workers",
|
|
935
935
|
backend: config.backend
|
|
@@ -942,7 +942,7 @@ function validateWorkersCompatibility(providedFlags, options, config) {
|
|
|
942
942
|
]
|
|
943
943
|
});
|
|
944
944
|
if (providedFlags.has("backend") && config.backend && !WORKERS_COMPATIBLE_BACKENDS.includes(config.backend) && config.runtime === "workers") incompatibilityError({
|
|
945
|
-
message: `
|
|
945
|
+
message: `In Better-Fullstack, backend '${config.backend}' is currently not available with Cloudflare Workers runtime.`,
|
|
946
946
|
provided: {
|
|
947
947
|
backend: config.backend,
|
|
948
948
|
runtime: "workers"
|
|
@@ -950,7 +950,7 @@ function validateWorkersCompatibility(providedFlags, options, config) {
|
|
|
950
950
|
suggestions: ["Use --backend hono, --backend nitro, or --backend fets", "Choose a different runtime (node, bun)"]
|
|
951
951
|
});
|
|
952
952
|
if (providedFlags.has("runtime") && options.runtime === "workers" && config.database === "mongodb") incompatibilityError({
|
|
953
|
-
message: "Cloudflare Workers runtime is not
|
|
953
|
+
message: "In Better-Fullstack, Cloudflare Workers runtime is currently not available with MongoDB.",
|
|
954
954
|
provided: {
|
|
955
955
|
runtime: "workers",
|
|
956
956
|
database: "mongodb"
|
|
@@ -958,7 +958,7 @@ function validateWorkersCompatibility(providedFlags, options, config) {
|
|
|
958
958
|
suggestions: ["Use a different database (postgres, sqlite, mysql)", "Choose a different runtime (node, bun)"]
|
|
959
959
|
});
|
|
960
960
|
if (providedFlags.has("runtime") && options.runtime === "workers" && config.dbSetup === "docker") incompatibilityError({
|
|
961
|
-
message: "Cloudflare Workers runtime is not
|
|
961
|
+
message: "In Better-Fullstack, Cloudflare Workers runtime is currently not available with Docker database setup.",
|
|
962
962
|
provided: {
|
|
963
963
|
runtime: "workers",
|
|
964
964
|
"db-setup": "docker"
|
|
@@ -966,7 +966,7 @@ function validateWorkersCompatibility(providedFlags, options, config) {
|
|
|
966
966
|
suggestions: ["Use --db-setup d1 for SQLite", "Choose a different runtime (node, bun)"]
|
|
967
967
|
});
|
|
968
968
|
if (providedFlags.has("database") && config.database === "mongodb" && config.runtime === "workers") incompatibilityError({
|
|
969
|
-
message: "MongoDB is not
|
|
969
|
+
message: "In Better-Fullstack, MongoDB is currently not available with Cloudflare Workers runtime.",
|
|
970
970
|
provided: {
|
|
971
971
|
database: "mongodb",
|
|
972
972
|
runtime: "workers"
|
|
@@ -1062,6 +1062,9 @@ function isFrontendAllowedWithBackend(frontend, backend, auth) {
|
|
|
1062
1062
|
"solid"
|
|
1063
1063
|
].includes(frontend)) return false;
|
|
1064
1064
|
}
|
|
1065
|
+
if (auth === "clerk" && backend === "self") {
|
|
1066
|
+
if (!["next", "tanstack-start"].includes(frontend)) return false;
|
|
1067
|
+
}
|
|
1065
1068
|
if (auth === "nextauth") {
|
|
1066
1069
|
if (frontend !== "next") return false;
|
|
1067
1070
|
if (backend !== "self") return false;
|
|
@@ -1072,29 +1075,57 @@ function isFrontendAllowedWithBackend(frontend, backend, auth) {
|
|
|
1072
1075
|
}
|
|
1073
1076
|
return true;
|
|
1074
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
|
+
}
|
|
1075
1106
|
function validateNextAuthCompatibility(auth, backend, frontends = []) {
|
|
1076
1107
|
if (auth !== "nextauth") return;
|
|
1077
1108
|
const hasNextJs = frontends.includes("next");
|
|
1078
|
-
if (backend !== "self") exitWithError("Auth.js (NextAuth) is
|
|
1079
|
-
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.");
|
|
1080
1111
|
}
|
|
1081
1112
|
function validateStackAuthCompatibility(auth, backend, frontends = []) {
|
|
1082
1113
|
if (auth !== "stack-auth") return;
|
|
1083
1114
|
const hasNextJs = frontends.includes("next");
|
|
1084
|
-
if (backend !== "self") exitWithError("Stack Auth is
|
|
1085
|
-
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.");
|
|
1086
1117
|
}
|
|
1087
1118
|
function validateSupabaseAuthCompatibility(auth, backend, frontends = []) {
|
|
1088
1119
|
if (auth !== "supabase-auth") return;
|
|
1089
1120
|
const hasNextJs = frontends.includes("next");
|
|
1090
|
-
if (backend !== "self") exitWithError("Supabase Auth is
|
|
1091
|
-
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.");
|
|
1092
1123
|
}
|
|
1093
1124
|
function validateAuth0Compatibility(auth, backend, frontends = []) {
|
|
1094
1125
|
if (auth !== "auth0") return;
|
|
1095
1126
|
const hasNextJs = frontends.includes("next");
|
|
1096
|
-
if (backend !== "self") exitWithError("Auth0 is
|
|
1097
|
-
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.");
|
|
1098
1129
|
}
|
|
1099
1130
|
function allowedApisForFrontends(frontends = [], astroIntegration) {
|
|
1100
1131
|
const includesNuxt = frontends.includes("nuxt");
|
|
@@ -1129,6 +1160,24 @@ function isExampleAIAllowed(backend, frontends = []) {
|
|
|
1129
1160
|
}
|
|
1130
1161
|
return true;
|
|
1131
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
|
+
}
|
|
1132
1181
|
function validateWebDeployRequiresWebFrontend(webDeploy, hasWebFrontendFlag) {
|
|
1133
1182
|
if (webDeploy && webDeploy !== "none" && !hasWebFrontendFlag) exitWithError("'--web-deploy' requires a web frontend. Please select a web frontend or set '--web-deploy none'.");
|
|
1134
1183
|
}
|
|
@@ -1168,7 +1217,7 @@ function validatePaymentsCompatibility(payments, auth, _backend, frontends = [])
|
|
|
1168
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.");
|
|
1169
1218
|
}
|
|
1170
1219
|
}
|
|
1171
|
-
function validateExamplesCompatibility(examples, backend, frontend) {
|
|
1220
|
+
function validateExamplesCompatibility(examples, backend, frontend, runtime, ai) {
|
|
1172
1221
|
const examplesArr = examples ?? [];
|
|
1173
1222
|
if (examplesArr.length === 0 || examplesArr.includes("none")) return;
|
|
1174
1223
|
if (examplesArr.includes("ai") && (frontend ?? []).includes("solid")) exitWithError("The 'ai' example is not compatible with the Solid frontend.");
|
|
@@ -1178,6 +1227,17 @@ function validateExamplesCompatibility(examples, backend, frontend) {
|
|
|
1178
1227
|
const includesSvelte = frontendArr.includes("svelte");
|
|
1179
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.");
|
|
1180
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
|
+
}
|
|
1181
1241
|
}
|
|
1182
1242
|
/**
|
|
1183
1243
|
* Validates that a UI library is compatible with the selected frontend(s)
|
|
@@ -2408,7 +2468,8 @@ const SKILL_SOURCES = {
|
|
|
2408
2468
|
"prisma/skills": { label: "Prisma" },
|
|
2409
2469
|
"elysiajs/skills": { label: "ElysiaJS" },
|
|
2410
2470
|
"waynesutton/convexskills": { label: "Convex" },
|
|
2411
|
-
"msmps/opentui-skill": { label: "OpenTUI Platform" }
|
|
2471
|
+
"msmps/opentui-skill": { label: "OpenTUI Platform" },
|
|
2472
|
+
"haydenbleasel/ultracite": { label: "Ultracite" }
|
|
2412
2473
|
};
|
|
2413
2474
|
const AVAILABLE_AGENTS = [
|
|
2414
2475
|
{
|
|
@@ -2540,6 +2601,7 @@ function getRecommendedSourceKeys(config) {
|
|
|
2540
2601
|
if (backend === "elysia") sources.push("elysiajs/skills");
|
|
2541
2602
|
if (backend === "convex") sources.push("waynesutton/convexskills");
|
|
2542
2603
|
if (addons.includes("opentui")) sources.push("msmps/opentui-skill");
|
|
2604
|
+
if (addons.includes("ultracite")) sources.push("haydenbleasel/ultracite");
|
|
2543
2605
|
return sources;
|
|
2544
2606
|
}
|
|
2545
2607
|
const CURATED_SKILLS_BY_SOURCE = {
|
|
@@ -2597,7 +2659,8 @@ const CURATED_SKILLS_BY_SOURCE = {
|
|
|
2597
2659
|
"convex-migrations",
|
|
2598
2660
|
"convex-security-check"
|
|
2599
2661
|
],
|
|
2600
|
-
"msmps/opentui-skill": () => ["opentui"]
|
|
2662
|
+
"msmps/opentui-skill": () => ["opentui"],
|
|
2663
|
+
"haydenbleasel/ultracite": () => ["ultracite"]
|
|
2601
2664
|
};
|
|
2602
2665
|
function getCuratedSkillNamesForSourceKey(sourceKey, config) {
|
|
2603
2666
|
return CURATED_SKILLS_BY_SOURCE[sourceKey](config);
|
|
@@ -3567,16 +3630,26 @@ async function getAuthChoice(auth, backend, frontend) {
|
|
|
3567
3630
|
if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
|
|
3568
3631
|
return response$1;
|
|
3569
3632
|
}
|
|
3570
|
-
const
|
|
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);
|
|
3571
3643
|
const options = [{
|
|
3572
3644
|
value: "better-auth",
|
|
3573
3645
|
label: "Better-Auth",
|
|
3574
3646
|
hint: "comprehensive auth framework for TypeScript"
|
|
3575
|
-
}
|
|
3647
|
+
}];
|
|
3648
|
+
if (supportsClerkSelf) options.push({
|
|
3576
3649
|
value: "clerk",
|
|
3577
3650
|
label: "Clerk",
|
|
3578
3651
|
hint: "More than auth, Complete User Management"
|
|
3579
|
-
}
|
|
3652
|
+
});
|
|
3580
3653
|
if (supportsNextJsAuth) {
|
|
3581
3654
|
options.push({
|
|
3582
3655
|
value: "nextauth",
|
|
@@ -4080,7 +4153,7 @@ async function getEmailChoice(email, backend) {
|
|
|
4080
4153
|
|
|
4081
4154
|
//#endregion
|
|
4082
4155
|
//#region src/prompts/examples.ts
|
|
4083
|
-
async function getExamplesChoice(examples, frontends, backend) {
|
|
4156
|
+
async function getExamplesChoice(examples, frontends, backend, runtime) {
|
|
4084
4157
|
if (examples !== void 0) return examples;
|
|
4085
4158
|
if (backend === "none") return [];
|
|
4086
4159
|
let response = [];
|
|
@@ -4090,6 +4163,11 @@ async function getExamplesChoice(examples, frontends, backend) {
|
|
|
4090
4163
|
label: "AI Chat",
|
|
4091
4164
|
hint: "A simple AI chat interface using AI SDK"
|
|
4092
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
|
+
});
|
|
4093
4171
|
if (options.length === 0) return [];
|
|
4094
4172
|
response = await navigableMultiselect({
|
|
4095
4173
|
message: "Include examples",
|
|
@@ -5625,7 +5703,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5625
5703
|
},
|
|
5626
5704
|
examples: ({ results }) => {
|
|
5627
5705
|
if (results.ecosystem !== "typescript") return Promise.resolve([]);
|
|
5628
|
-
return getExamplesChoice(flags.examples, results.frontend, results.backend);
|
|
5706
|
+
return getExamplesChoice(flags.examples, results.frontend, results.backend, results.runtime);
|
|
5629
5707
|
},
|
|
5630
5708
|
dbSetup: ({ results }) => {
|
|
5631
5709
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
@@ -5641,6 +5719,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5641
5719
|
},
|
|
5642
5720
|
ai: ({ results }) => {
|
|
5643
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");
|
|
5644
5723
|
return getAIChoice(flags.ai);
|
|
5645
5724
|
},
|
|
5646
5725
|
validation: ({ results }) => {
|
|
@@ -6514,7 +6593,7 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
6514
6593
|
suggestions: ["Use --orm mongoose or --orm prisma for MongoDB", "Choose a different database (postgres, sqlite, mysql)"]
|
|
6515
6594
|
});
|
|
6516
6595
|
if (has("database") && has("orm") && db === "mongodb" && orm && orm !== "mongoose" && orm !== "prisma" && orm !== "none") incompatibilityError({
|
|
6517
|
-
message: "MongoDB only
|
|
6596
|
+
message: "In Better-Fullstack, MongoDB is currently supported only with Mongoose or Prisma ORM.",
|
|
6518
6597
|
provided: {
|
|
6519
6598
|
database: "mongodb",
|
|
6520
6599
|
orm
|
|
@@ -6597,7 +6676,7 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
6597
6676
|
runtime: "workers",
|
|
6598
6677
|
errorMessage: "Cloudflare D1 setup requires SQLite database and Cloudflare Workers runtime."
|
|
6599
6678
|
},
|
|
6600
|
-
docker: { errorMessage: "Docker setup is not
|
|
6679
|
+
docker: { errorMessage: "In Better-Fullstack, Docker setup is currently not available with SQLite database or Cloudflare Workers runtime." },
|
|
6601
6680
|
none: { errorMessage: "" }
|
|
6602
6681
|
};
|
|
6603
6682
|
if (dbSetup && dbSetup !== "none") {
|
|
@@ -6607,8 +6686,8 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
6607
6686
|
} else if (validation.database && database !== validation.database) exitWithError(validation.errorMessage);
|
|
6608
6687
|
if (validation.runtime && runtime !== validation.runtime) exitWithError(validation.errorMessage);
|
|
6609
6688
|
if (dbSetup === "docker") {
|
|
6610
|
-
if (database === "sqlite") exitWithError("Docker setup is not
|
|
6611
|
-
if (runtime === "workers") exitWithError("Docker setup is not
|
|
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.");
|
|
6612
6691
|
}
|
|
6613
6692
|
}
|
|
6614
6693
|
}
|
|
@@ -6727,15 +6806,6 @@ function validateAdonisJSConstraints(config, providedFlags) {
|
|
|
6727
6806
|
}
|
|
6728
6807
|
function validateBackendConstraints(config, providedFlags, options) {
|
|
6729
6808
|
const { backend } = config;
|
|
6730
|
-
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.");
|
|
6731
|
-
if (backend === "convex" && config.auth === "clerk" && config.frontend) {
|
|
6732
|
-
const incompatibleFrontends = config.frontend.filter((f) => [
|
|
6733
|
-
"nuxt",
|
|
6734
|
-
"svelte",
|
|
6735
|
-
"solid"
|
|
6736
|
-
].includes(f));
|
|
6737
|
-
if (incompatibleFrontends.length > 0) exitWithError(`Clerk authentication is not compatible with the following frontends: ${incompatibleFrontends.join(", ")}. Please choose a different frontend or auth provider.`);
|
|
6738
|
-
}
|
|
6739
6809
|
if (providedFlags.has("backend") && backend && backend !== "convex" && backend !== "none" && backend !== "self" && backend !== "encore") {
|
|
6740
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.");
|
|
6741
6811
|
}
|
|
@@ -6774,8 +6844,9 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
6774
6844
|
validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
|
|
6775
6845
|
config.addons = [...new Set(config.addons)];
|
|
6776
6846
|
}
|
|
6777
|
-
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? []);
|
|
6847
|
+
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
|
|
6778
6848
|
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend ?? []);
|
|
6849
|
+
validateClerkCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6779
6850
|
validateNextAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6780
6851
|
validateStackAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6781
6852
|
validateSupabaseAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
@@ -6790,12 +6861,13 @@ function validateConfigForProgrammaticUse(config) {
|
|
|
6790
6861
|
if (config.frontend && config.frontend.length > 0) ensureSingleWebAndNative(config.frontend);
|
|
6791
6862
|
validateApiFrontendCompatibility(config.api, config.frontend, config.astroIntegration);
|
|
6792
6863
|
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
|
|
6864
|
+
validateClerkCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6793
6865
|
validateNextAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6794
6866
|
validateStackAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6795
6867
|
validateSupabaseAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6796
6868
|
validateAuth0Compatibility(config.auth, config.backend, config.frontend ?? []);
|
|
6797
6869
|
if (config.addons && config.addons.length > 0) validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
|
|
6798
|
-
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? []);
|
|
6870
|
+
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
|
|
6799
6871
|
validateUILibraryFrontendCompatibility(config.uiLibrary, config.frontend ?? [], config.astroIntegration);
|
|
6800
6872
|
validateUILibraryCSSFrameworkCompatibility(config.uiLibrary, config.cssFramework);
|
|
6801
6873
|
validatePeerDependencies(config);
|
|
@@ -8117,7 +8189,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
8117
8189
|
const nativeInstructions = (frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles")) && backend !== "none" ? getNativeInstructions(isConvex, isBackendSelf, frontend || [], runCmd) : "";
|
|
8118
8190
|
const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
|
|
8119
8191
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
8120
|
-
const clerkInstructions =
|
|
8192
|
+
const clerkInstructions = config.auth === "clerk" ? getClerkInstructions(config.backend, config.frontend ?? []) : "";
|
|
8121
8193
|
const polarInstructions = config.payments === "polar" && config.auth === "better-auth" ? getPolarInstructions(backend) : "";
|
|
8122
8194
|
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
8123
8195
|
const hasWeb = frontend?.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
@@ -8248,8 +8320,13 @@ function getNoOrmWarning() {
|
|
|
8248
8320
|
function getBunWebNativeWarning() {
|
|
8249
8321
|
return `\n${pc.yellow("WARNING:")} 'bun' might cause issues with web + native apps in a monorepo.\n Use 'pnpm' if problems arise.`;
|
|
8250
8322
|
}
|
|
8251
|
-
function getClerkInstructions() {
|
|
8252
|
-
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 "";
|
|
8253
8330
|
}
|
|
8254
8331
|
function getBetterAuthConvexInstructions(hasWeb, webPort, packageManager) {
|
|
8255
8332
|
const cmd = packageManager === "npm" ? "npx" : packageManager;
|
|
@@ -8437,7 +8514,7 @@ async function setPackageManagerVersion(projectDir, packageManager) {
|
|
|
8437
8514
|
const pkgJsonPath = path.join(projectDir, "package.json");
|
|
8438
8515
|
if (!await fs.pathExists(pkgJsonPath)) return;
|
|
8439
8516
|
try {
|
|
8440
|
-
const { stdout } = await
|
|
8517
|
+
const { stdout } = await $({ cwd: os$1.tmpdir() })`${packageManager} -v`;
|
|
8441
8518
|
const version = stdout.trim();
|
|
8442
8519
|
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
8443
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
|
+
"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.
|
|
80
|
-
"@better-fullstack/types": "^1.
|
|
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",
|