create-better-fullstack 1.0.5 → 1.1.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 +13 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-B1JV3DqF.mjs → src-BkBCdTQg.mjs} +602 -27
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -162,6 +162,16 @@ declare const router: {
|
|
|
162
162
|
none: "none";
|
|
163
163
|
opentelemetry: "opentelemetry";
|
|
164
164
|
}>>;
|
|
165
|
+
cms: z.ZodOptional<z.ZodEnum<{
|
|
166
|
+
none: "none";
|
|
167
|
+
payload: "payload";
|
|
168
|
+
sanity: "sanity";
|
|
169
|
+
strapi: "strapi";
|
|
170
|
+
}>>;
|
|
171
|
+
caching: z.ZodOptional<z.ZodEnum<{
|
|
172
|
+
none: "none";
|
|
173
|
+
"upstash-redis": "upstash-redis";
|
|
174
|
+
}>>;
|
|
165
175
|
frontend: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
166
176
|
none: "none";
|
|
167
177
|
"tanstack-router": "tanstack-router";
|
|
@@ -510,7 +520,9 @@ type Animation = import__better_fullstack_types.Animation;
|
|
|
510
520
|
type Auth = import__better_fullstack_types.Auth;
|
|
511
521
|
type Backend = import__better_fullstack_types.Backend;
|
|
512
522
|
type BetterTStackConfig = import__better_fullstack_types.BetterTStackConfig;
|
|
523
|
+
type CMS = import__better_fullstack_types.CMS;
|
|
513
524
|
type CSSFramework = import__better_fullstack_types.CSSFramework;
|
|
525
|
+
type Caching = import__better_fullstack_types.Caching;
|
|
514
526
|
type CreateInput = import__better_fullstack_types.CreateInput;
|
|
515
527
|
type Database = import__better_fullstack_types.Database;
|
|
516
528
|
type DatabaseSetup = import__better_fullstack_types.DatabaseSetup;
|
|
@@ -536,4 +548,4 @@ type ServerDeploy = import__better_fullstack_types.ServerDeploy;
|
|
|
536
548
|
type Template = import__better_fullstack_types.Template;
|
|
537
549
|
type UILibrary = import__better_fullstack_types.UILibrary;
|
|
538
550
|
type WebDeploy = import__better_fullstack_types.WebDeploy;
|
|
539
|
-
export { type API, type Addons, type Animation, type Auth, type Backend, type BetterTStackConfig, type CSSFramework, type CreateInput, type Database, type DatabaseSetup, type DirectoryConflict, EMBEDDED_TEMPLATES, type Ecosystem, type Effect, type Examples, type Frontend, type GeneratorOptions, type GeneratorResult, type InitResult, type Logging, type ORM, type PackageManager, type Payments, type Realtime, type Runtime, type RustApi, type RustCli, type RustFrontend, type RustLibraries, type RustOrm, type RustWebFramework, type ServerDeploy, TEMPLATE_COUNT, type Template, type UILibrary, type VirtualDirectory, type VirtualFile, VirtualFileSystem, type VirtualFileTree, type VirtualNode, type WebDeploy, builder, create, createBtsCli, createVirtual, docs, generateVirtualProject, router, sponsors };
|
|
551
|
+
export { type API, type Addons, type Animation, type Auth, type Backend, type BetterTStackConfig, type CMS, type CSSFramework, type Caching, type CreateInput, type Database, type DatabaseSetup, type DirectoryConflict, EMBEDDED_TEMPLATES, type Ecosystem, type Effect, type Examples, type Frontend, type GeneratorOptions, type GeneratorResult, type InitResult, type Logging, type ORM, type PackageManager, type Payments, type Realtime, type Runtime, type RustApi, type RustCli, type RustFrontend, type RustLibraries, type RustOrm, type RustWebFramework, type ServerDeploy, TEMPLATE_COUNT, type Template, type UILibrary, type VirtualDirectory, type VirtualFile, VirtualFileSystem, type VirtualFileTree, type VirtualNode, type WebDeploy, builder, create, createBtsCli, createVirtual, docs, generateVirtualProject, router, sponsors };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as create, c as docs, d as sponsors, i as builder, l as generateVirtualProject, n as TEMPLATE_COUNT, o as createBtsCli, r as VirtualFileSystem, s as createVirtual, t as EMBEDDED_TEMPLATES, u as router } from "./src-
|
|
2
|
+
import { a as create, c as docs, d as sponsors, i as builder, l as generateVirtualProject, n as TEMPLATE_COUNT, o as createBtsCli, r as VirtualFileSystem, s as createVirtual, t as EMBEDDED_TEMPLATES, u as router } from "./src-BkBCdTQg.mjs";
|
|
3
3
|
|
|
4
4
|
export { EMBEDDED_TEMPLATES, TEMPLATE_COUNT, VirtualFileSystem, builder, create, createBtsCli, createVirtual, docs, generateVirtualProject, router, sponsors };
|
|
@@ -1038,6 +1038,119 @@ async function getAddonsChoice(addons, frontends, auth) {
|
|
|
1038
1038
|
return response;
|
|
1039
1039
|
}
|
|
1040
1040
|
|
|
1041
|
+
//#endregion
|
|
1042
|
+
//#region src/prompts/ai.ts
|
|
1043
|
+
async function getAIChoice(ai) {
|
|
1044
|
+
if (ai !== void 0) return ai;
|
|
1045
|
+
const response = await navigableSelect({
|
|
1046
|
+
message: "Select AI SDK",
|
|
1047
|
+
options: [
|
|
1048
|
+
{
|
|
1049
|
+
value: "vercel-ai",
|
|
1050
|
+
label: "Vercel AI SDK",
|
|
1051
|
+
hint: "The AI Toolkit for TypeScript - supports OpenAI, Anthropic, Google, etc."
|
|
1052
|
+
},
|
|
1053
|
+
{
|
|
1054
|
+
value: "mastra",
|
|
1055
|
+
label: "Mastra",
|
|
1056
|
+
hint: "TypeScript-native AI agent framework with workflows"
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
value: "voltagent",
|
|
1060
|
+
label: "VoltAgent",
|
|
1061
|
+
hint: "AI Agent framework with memory, workflows, and observability"
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
value: "langgraph",
|
|
1065
|
+
label: "LangGraph.js",
|
|
1066
|
+
hint: "Graph-based agent orchestration with stateful workflows"
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
value: "openai-agents",
|
|
1070
|
+
label: "OpenAI Agents SDK",
|
|
1071
|
+
hint: "Official multi-agent framework with handoffs and guardrails"
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
value: "google-adk",
|
|
1075
|
+
label: "Google ADK",
|
|
1076
|
+
hint: "Code-first agent development kit for building AI agents"
|
|
1077
|
+
},
|
|
1078
|
+
{
|
|
1079
|
+
value: "modelfusion",
|
|
1080
|
+
label: "ModelFusion",
|
|
1081
|
+
hint: "Type-safe AI library for multi-provider text generation"
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
value: "langchain",
|
|
1085
|
+
label: "LangChain",
|
|
1086
|
+
hint: "Build context-aware reasoning applications"
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
value: "llamaindex",
|
|
1090
|
+
label: "LlamaIndex",
|
|
1091
|
+
hint: "Data framework for LLM applications"
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
value: "none",
|
|
1095
|
+
label: "None",
|
|
1096
|
+
hint: "No AI SDK"
|
|
1097
|
+
}
|
|
1098
|
+
],
|
|
1099
|
+
initialValue: "none"
|
|
1100
|
+
});
|
|
1101
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1102
|
+
return response;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
//#endregion
|
|
1106
|
+
//#region src/prompts/animation.ts
|
|
1107
|
+
async function getAnimationChoice(animation, frontends) {
|
|
1108
|
+
if (animation !== void 0) return animation;
|
|
1109
|
+
const { web } = splitFrontends(frontends);
|
|
1110
|
+
if (web.length === 0) return "none";
|
|
1111
|
+
const isReact = web.some((f) => [
|
|
1112
|
+
"tanstack-router",
|
|
1113
|
+
"react-router",
|
|
1114
|
+
"tanstack-start",
|
|
1115
|
+
"next",
|
|
1116
|
+
"redwood"
|
|
1117
|
+
].includes(f));
|
|
1118
|
+
const options = [];
|
|
1119
|
+
if (isReact) options.push({
|
|
1120
|
+
value: "framer-motion",
|
|
1121
|
+
label: "Framer Motion",
|
|
1122
|
+
hint: "Production-ready declarative animations for React"
|
|
1123
|
+
}, {
|
|
1124
|
+
value: "react-spring",
|
|
1125
|
+
label: "React Spring",
|
|
1126
|
+
hint: "Physics-based animations for fluid interactions"
|
|
1127
|
+
});
|
|
1128
|
+
options.push({
|
|
1129
|
+
value: "gsap",
|
|
1130
|
+
label: "GSAP",
|
|
1131
|
+
hint: "Professional-grade animation engine for the web"
|
|
1132
|
+
}, {
|
|
1133
|
+
value: "auto-animate",
|
|
1134
|
+
label: "Auto Animate",
|
|
1135
|
+
hint: "Zero-config, drop-in animation utility"
|
|
1136
|
+
}, {
|
|
1137
|
+
value: "lottie",
|
|
1138
|
+
label: "Lottie",
|
|
1139
|
+
hint: "Render After Effects animations natively"
|
|
1140
|
+
}, {
|
|
1141
|
+
value: "none",
|
|
1142
|
+
label: "None",
|
|
1143
|
+
hint: "Skip animation library setup"
|
|
1144
|
+
});
|
|
1145
|
+
const response = await navigableSelect({
|
|
1146
|
+
message: "Select animation library",
|
|
1147
|
+
options,
|
|
1148
|
+
initialValue: "none"
|
|
1149
|
+
});
|
|
1150
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1151
|
+
return response;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1041
1154
|
//#endregion
|
|
1042
1155
|
//#region src/prompts/api.ts
|
|
1043
1156
|
async function getApiChoice(Api, frontend, backend, astroIntegration) {
|
|
@@ -1226,6 +1339,63 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
|
|
|
1226
1339
|
return response;
|
|
1227
1340
|
}
|
|
1228
1341
|
|
|
1342
|
+
//#endregion
|
|
1343
|
+
//#region src/prompts/caching.ts
|
|
1344
|
+
async function getCachingChoice(caching, backend) {
|
|
1345
|
+
if (caching !== void 0) return caching;
|
|
1346
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
1347
|
+
const response = await navigableSelect({
|
|
1348
|
+
message: "Select caching solution",
|
|
1349
|
+
options: [{
|
|
1350
|
+
value: "upstash-redis",
|
|
1351
|
+
label: "Upstash Redis",
|
|
1352
|
+
hint: "Serverless Redis with REST API for edge and serverless"
|
|
1353
|
+
}, {
|
|
1354
|
+
value: "none",
|
|
1355
|
+
label: "None",
|
|
1356
|
+
hint: "Skip caching layer setup"
|
|
1357
|
+
}],
|
|
1358
|
+
initialValue: "none"
|
|
1359
|
+
});
|
|
1360
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1361
|
+
return response;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
//#endregion
|
|
1365
|
+
//#region src/prompts/cms.ts
|
|
1366
|
+
async function getCMSChoice(cms, backend) {
|
|
1367
|
+
if (cms !== void 0) return cms;
|
|
1368
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
1369
|
+
const response = await navigableSelect({
|
|
1370
|
+
message: "Select headless CMS",
|
|
1371
|
+
options: [
|
|
1372
|
+
{
|
|
1373
|
+
value: "payload",
|
|
1374
|
+
label: "Payload",
|
|
1375
|
+
hint: "TypeScript-first headless CMS with Next.js integration"
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
value: "sanity",
|
|
1379
|
+
label: "Sanity",
|
|
1380
|
+
hint: "Real-time collaborative CMS with schema-as-code"
|
|
1381
|
+
},
|
|
1382
|
+
{
|
|
1383
|
+
value: "strapi",
|
|
1384
|
+
label: "Strapi",
|
|
1385
|
+
hint: "Open-source headless CMS with admin panel"
|
|
1386
|
+
},
|
|
1387
|
+
{
|
|
1388
|
+
value: "none",
|
|
1389
|
+
label: "None",
|
|
1390
|
+
hint: "Skip headless CMS setup"
|
|
1391
|
+
}
|
|
1392
|
+
],
|
|
1393
|
+
initialValue: "none"
|
|
1394
|
+
});
|
|
1395
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1396
|
+
return response;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1229
1399
|
//#endregion
|
|
1230
1400
|
//#region src/prompts/css-framework.ts
|
|
1231
1401
|
const CSS_FRAMEWORK_OPTIONS = {
|
|
@@ -1495,6 +1665,98 @@ async function getExamplesChoice(examples, database, frontends, backend, api) {
|
|
|
1495
1665
|
return response;
|
|
1496
1666
|
}
|
|
1497
1667
|
|
|
1668
|
+
//#endregion
|
|
1669
|
+
//#region src/prompts/file-upload.ts
|
|
1670
|
+
async function getFileUploadChoice(fileUpload, backend) {
|
|
1671
|
+
if (fileUpload !== void 0) return fileUpload;
|
|
1672
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
1673
|
+
const response = await navigableSelect({
|
|
1674
|
+
message: "Select file upload solution",
|
|
1675
|
+
options: [
|
|
1676
|
+
{
|
|
1677
|
+
value: "uploadthing",
|
|
1678
|
+
label: "UploadThing",
|
|
1679
|
+
hint: "TypeScript-first file uploads with built-in validation"
|
|
1680
|
+
},
|
|
1681
|
+
{
|
|
1682
|
+
value: "filepond",
|
|
1683
|
+
label: "FilePond",
|
|
1684
|
+
hint: "Flexible file upload with image preview and drag & drop"
|
|
1685
|
+
},
|
|
1686
|
+
{
|
|
1687
|
+
value: "uppy",
|
|
1688
|
+
label: "Uppy",
|
|
1689
|
+
hint: "Modular file uploader with resumable uploads and plugins"
|
|
1690
|
+
},
|
|
1691
|
+
{
|
|
1692
|
+
value: "none",
|
|
1693
|
+
label: "None",
|
|
1694
|
+
hint: "Skip file upload integration"
|
|
1695
|
+
}
|
|
1696
|
+
],
|
|
1697
|
+
initialValue: "none"
|
|
1698
|
+
});
|
|
1699
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1700
|
+
return response;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
//#endregion
|
|
1704
|
+
//#region src/prompts/forms.ts
|
|
1705
|
+
async function getFormsChoice(forms, frontends) {
|
|
1706
|
+
if (forms !== void 0) return forms;
|
|
1707
|
+
const { web } = splitFrontends(frontends);
|
|
1708
|
+
if (web.length === 0) return "none";
|
|
1709
|
+
const isReact = web.some((f) => [
|
|
1710
|
+
"tanstack-router",
|
|
1711
|
+
"react-router",
|
|
1712
|
+
"tanstack-start",
|
|
1713
|
+
"next",
|
|
1714
|
+
"redwood"
|
|
1715
|
+
].includes(f));
|
|
1716
|
+
const isSolid = web.includes("solid");
|
|
1717
|
+
const isQwik = web.includes("qwik");
|
|
1718
|
+
const options = [];
|
|
1719
|
+
if (isReact) options.push({
|
|
1720
|
+
value: "react-hook-form",
|
|
1721
|
+
label: "React Hook Form",
|
|
1722
|
+
hint: "Performant, flexible form validation library"
|
|
1723
|
+
}, {
|
|
1724
|
+
value: "formik",
|
|
1725
|
+
label: "Formik",
|
|
1726
|
+
hint: "Popular form state management with Yup validation"
|
|
1727
|
+
}, {
|
|
1728
|
+
value: "final-form",
|
|
1729
|
+
label: "Final Form",
|
|
1730
|
+
hint: "Framework-agnostic form state management"
|
|
1731
|
+
}, {
|
|
1732
|
+
value: "conform",
|
|
1733
|
+
label: "Conform",
|
|
1734
|
+
hint: "Progressive enhancement forms with Zod validation"
|
|
1735
|
+
});
|
|
1736
|
+
options.push({
|
|
1737
|
+
value: "tanstack-form",
|
|
1738
|
+
label: "TanStack Form",
|
|
1739
|
+
hint: "Fully-typed, framework-agnostic form library"
|
|
1740
|
+
});
|
|
1741
|
+
if (isSolid || isQwik) options.push({
|
|
1742
|
+
value: "modular-forms",
|
|
1743
|
+
label: "Modular Forms",
|
|
1744
|
+
hint: "Type-safe forms for Solid and Qwik (3KB bundle)"
|
|
1745
|
+
});
|
|
1746
|
+
options.push({
|
|
1747
|
+
value: "none",
|
|
1748
|
+
label: "None",
|
|
1749
|
+
hint: "Build custom form handling"
|
|
1750
|
+
});
|
|
1751
|
+
const response = await navigableSelect({
|
|
1752
|
+
message: "Select form library",
|
|
1753
|
+
options,
|
|
1754
|
+
initialValue: isReact ? "react-hook-form" : "tanstack-form"
|
|
1755
|
+
});
|
|
1756
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1757
|
+
return response;
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1498
1760
|
//#endregion
|
|
1499
1761
|
//#region src/prompts/frontend.ts
|
|
1500
1762
|
async function getFrontendChoice(frontendOptions, backend, auth) {
|
|
@@ -1637,6 +1899,76 @@ async function getinstallChoice(install) {
|
|
|
1637
1899
|
return response;
|
|
1638
1900
|
}
|
|
1639
1901
|
|
|
1902
|
+
//#endregion
|
|
1903
|
+
//#region src/prompts/job-queue.ts
|
|
1904
|
+
async function getJobQueueChoice(jobQueue, backend) {
|
|
1905
|
+
if (jobQueue !== void 0) return jobQueue;
|
|
1906
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
1907
|
+
const response = await navigableSelect({
|
|
1908
|
+
message: "Select job queue solution",
|
|
1909
|
+
options: [
|
|
1910
|
+
{
|
|
1911
|
+
value: "bullmq",
|
|
1912
|
+
label: "BullMQ",
|
|
1913
|
+
hint: "Redis-backed job queue for background tasks and scheduling"
|
|
1914
|
+
},
|
|
1915
|
+
{
|
|
1916
|
+
value: "trigger-dev",
|
|
1917
|
+
label: "Trigger.dev",
|
|
1918
|
+
hint: "Background jobs as code with serverless execution"
|
|
1919
|
+
},
|
|
1920
|
+
{
|
|
1921
|
+
value: "inngest",
|
|
1922
|
+
label: "Inngest",
|
|
1923
|
+
hint: "Event-driven functions with built-in queuing and scheduling"
|
|
1924
|
+
},
|
|
1925
|
+
{
|
|
1926
|
+
value: "temporal",
|
|
1927
|
+
label: "Temporal",
|
|
1928
|
+
hint: "Durable workflow orchestration for reliable distributed systems"
|
|
1929
|
+
},
|
|
1930
|
+
{
|
|
1931
|
+
value: "none",
|
|
1932
|
+
label: "None",
|
|
1933
|
+
hint: "Skip job queue/background worker setup"
|
|
1934
|
+
}
|
|
1935
|
+
],
|
|
1936
|
+
initialValue: "none"
|
|
1937
|
+
});
|
|
1938
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1939
|
+
return response;
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
//#endregion
|
|
1943
|
+
//#region src/prompts/logging.ts
|
|
1944
|
+
async function getLoggingChoice(logging, backend) {
|
|
1945
|
+
if (logging !== void 0) return logging;
|
|
1946
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
1947
|
+
const response = await navigableSelect({
|
|
1948
|
+
message: "Select logging framework",
|
|
1949
|
+
options: [
|
|
1950
|
+
{
|
|
1951
|
+
value: "pino",
|
|
1952
|
+
label: "Pino",
|
|
1953
|
+
hint: "Fast JSON logger with minimal overhead"
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
value: "winston",
|
|
1957
|
+
label: "Winston",
|
|
1958
|
+
hint: "Flexible logging library with multiple transports"
|
|
1959
|
+
},
|
|
1960
|
+
{
|
|
1961
|
+
value: "none",
|
|
1962
|
+
label: "None",
|
|
1963
|
+
hint: "Skip logging framework setup"
|
|
1964
|
+
}
|
|
1965
|
+
],
|
|
1966
|
+
initialValue: "none"
|
|
1967
|
+
});
|
|
1968
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
1969
|
+
return response;
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1640
1972
|
//#endregion
|
|
1641
1973
|
//#region src/prompts/navigable-group.ts
|
|
1642
1974
|
/**
|
|
@@ -1694,6 +2026,28 @@ async function navigableGroup(prompts, opts) {
|
|
|
1694
2026
|
return results;
|
|
1695
2027
|
}
|
|
1696
2028
|
|
|
2029
|
+
//#endregion
|
|
2030
|
+
//#region src/prompts/observability.ts
|
|
2031
|
+
async function getObservabilityChoice(observability, backend) {
|
|
2032
|
+
if (observability !== void 0) return observability;
|
|
2033
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
2034
|
+
const response = await navigableSelect({
|
|
2035
|
+
message: "Select observability solution",
|
|
2036
|
+
options: [{
|
|
2037
|
+
value: "opentelemetry",
|
|
2038
|
+
label: "OpenTelemetry",
|
|
2039
|
+
hint: "Observability framework for traces, metrics, and logs"
|
|
2040
|
+
}, {
|
|
2041
|
+
value: "none",
|
|
2042
|
+
label: "None",
|
|
2043
|
+
hint: "Skip observability/tracing setup"
|
|
2044
|
+
}],
|
|
2045
|
+
initialValue: "none"
|
|
2046
|
+
});
|
|
2047
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2048
|
+
return response;
|
|
2049
|
+
}
|
|
2050
|
+
|
|
1697
2051
|
//#endregion
|
|
1698
2052
|
//#region src/prompts/orm.ts
|
|
1699
2053
|
const ormOptions = {
|
|
@@ -1805,6 +2159,56 @@ async function getPaymentsChoice(payments, auth, backend, frontends) {
|
|
|
1805
2159
|
return response;
|
|
1806
2160
|
}
|
|
1807
2161
|
|
|
2162
|
+
//#endregion
|
|
2163
|
+
//#region src/prompts/realtime.ts
|
|
2164
|
+
async function getRealtimeChoice(realtime, backend) {
|
|
2165
|
+
if (realtime !== void 0) return realtime;
|
|
2166
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
2167
|
+
const response = await navigableSelect({
|
|
2168
|
+
message: "Select real-time solution",
|
|
2169
|
+
options: [
|
|
2170
|
+
{
|
|
2171
|
+
value: "socket-io",
|
|
2172
|
+
label: "Socket.IO",
|
|
2173
|
+
hint: "Real-time bidirectional communication with fallbacks"
|
|
2174
|
+
},
|
|
2175
|
+
{
|
|
2176
|
+
value: "partykit",
|
|
2177
|
+
label: "PartyKit",
|
|
2178
|
+
hint: "Edge-native multiplayer infrastructure on Cloudflare"
|
|
2179
|
+
},
|
|
2180
|
+
{
|
|
2181
|
+
value: "ably",
|
|
2182
|
+
label: "Ably",
|
|
2183
|
+
hint: "Real-time messaging platform with pub/sub and presence"
|
|
2184
|
+
},
|
|
2185
|
+
{
|
|
2186
|
+
value: "pusher",
|
|
2187
|
+
label: "Pusher",
|
|
2188
|
+
hint: "Real-time communication APIs with channels and events"
|
|
2189
|
+
},
|
|
2190
|
+
{
|
|
2191
|
+
value: "liveblocks",
|
|
2192
|
+
label: "Liveblocks",
|
|
2193
|
+
hint: "Collaboration infrastructure for multiplayer experiences"
|
|
2194
|
+
},
|
|
2195
|
+
{
|
|
2196
|
+
value: "yjs",
|
|
2197
|
+
label: "Y.js",
|
|
2198
|
+
hint: "CRDT library for real-time collaboration with conflict-free sync"
|
|
2199
|
+
},
|
|
2200
|
+
{
|
|
2201
|
+
value: "none",
|
|
2202
|
+
label: "None",
|
|
2203
|
+
hint: "Skip real-time/WebSocket integration"
|
|
2204
|
+
}
|
|
2205
|
+
],
|
|
2206
|
+
initialValue: "none"
|
|
2207
|
+
});
|
|
2208
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2209
|
+
return response;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
1808
2212
|
//#endregion
|
|
1809
2213
|
//#region src/prompts/runtime.ts
|
|
1810
2214
|
async function getRuntimeChoice(runtime, backend) {
|
|
@@ -1843,6 +2247,115 @@ async function getServerDeploymentChoice(deployment, runtime, backend, _webDeplo
|
|
|
1843
2247
|
return "none";
|
|
1844
2248
|
}
|
|
1845
2249
|
|
|
2250
|
+
//#endregion
|
|
2251
|
+
//#region src/prompts/state-management.ts
|
|
2252
|
+
async function getStateManagementChoice(stateManagement, frontends) {
|
|
2253
|
+
if (stateManagement !== void 0) return stateManagement;
|
|
2254
|
+
const { web } = splitFrontends(frontends);
|
|
2255
|
+
if (web.length === 0) return "none";
|
|
2256
|
+
const isReact = web.some((f) => [
|
|
2257
|
+
"tanstack-router",
|
|
2258
|
+
"react-router",
|
|
2259
|
+
"tanstack-start",
|
|
2260
|
+
"next",
|
|
2261
|
+
"redwood"
|
|
2262
|
+
].includes(f));
|
|
2263
|
+
const options = [];
|
|
2264
|
+
if (isReact) options.push({
|
|
2265
|
+
value: "zustand",
|
|
2266
|
+
label: "Zustand",
|
|
2267
|
+
hint: "Lightweight state management with simple API"
|
|
2268
|
+
}, {
|
|
2269
|
+
value: "jotai",
|
|
2270
|
+
label: "Jotai",
|
|
2271
|
+
hint: "Primitive and flexible atomic state"
|
|
2272
|
+
}, {
|
|
2273
|
+
value: "redux-toolkit",
|
|
2274
|
+
label: "Redux Toolkit",
|
|
2275
|
+
hint: "Enterprise-standard state with excellent TS support"
|
|
2276
|
+
}, {
|
|
2277
|
+
value: "valtio",
|
|
2278
|
+
label: "Valtio",
|
|
2279
|
+
hint: "Proxy-based state management"
|
|
2280
|
+
}, {
|
|
2281
|
+
value: "legend-state",
|
|
2282
|
+
label: "Legend State",
|
|
2283
|
+
hint: "High-performance observable state for React"
|
|
2284
|
+
}, {
|
|
2285
|
+
value: "mobx",
|
|
2286
|
+
label: "MobX",
|
|
2287
|
+
hint: "Observable-based reactive state management"
|
|
2288
|
+
});
|
|
2289
|
+
options.push({
|
|
2290
|
+
value: "nanostores",
|
|
2291
|
+
label: "Nanostores",
|
|
2292
|
+
hint: "Tiny state manager (1KB) for all frameworks"
|
|
2293
|
+
}, {
|
|
2294
|
+
value: "xstate",
|
|
2295
|
+
label: "XState",
|
|
2296
|
+
hint: "State machines and statecharts for complex logic"
|
|
2297
|
+
}, {
|
|
2298
|
+
value: "tanstack-store",
|
|
2299
|
+
label: "TanStack Store",
|
|
2300
|
+
hint: "Framework-agnostic store powering TanStack ecosystem"
|
|
2301
|
+
}, {
|
|
2302
|
+
value: "none",
|
|
2303
|
+
label: "None",
|
|
2304
|
+
hint: "Skip state management setup"
|
|
2305
|
+
});
|
|
2306
|
+
const response = await navigableSelect({
|
|
2307
|
+
message: "Select state management",
|
|
2308
|
+
options,
|
|
2309
|
+
initialValue: "none"
|
|
2310
|
+
});
|
|
2311
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2312
|
+
return response;
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
//#endregion
|
|
2316
|
+
//#region src/prompts/testing.ts
|
|
2317
|
+
async function getTestingChoice(testing) {
|
|
2318
|
+
if (testing !== void 0) return testing;
|
|
2319
|
+
const response = await navigableSelect({
|
|
2320
|
+
message: "Select testing framework",
|
|
2321
|
+
options: [
|
|
2322
|
+
{
|
|
2323
|
+
value: "vitest",
|
|
2324
|
+
label: "Vitest",
|
|
2325
|
+
hint: "Blazing fast Vite-native unit test framework"
|
|
2326
|
+
},
|
|
2327
|
+
{
|
|
2328
|
+
value: "vitest-playwright",
|
|
2329
|
+
label: "Vitest + Playwright",
|
|
2330
|
+
hint: "Both unit and E2E testing for complete coverage"
|
|
2331
|
+
},
|
|
2332
|
+
{
|
|
2333
|
+
value: "playwright",
|
|
2334
|
+
label: "Playwright",
|
|
2335
|
+
hint: "End-to-end testing framework by Microsoft"
|
|
2336
|
+
},
|
|
2337
|
+
{
|
|
2338
|
+
value: "jest",
|
|
2339
|
+
label: "Jest",
|
|
2340
|
+
hint: "Classic testing framework with wide ecosystem"
|
|
2341
|
+
},
|
|
2342
|
+
{
|
|
2343
|
+
value: "cypress",
|
|
2344
|
+
label: "Cypress",
|
|
2345
|
+
hint: "E2E testing with time travel debugging"
|
|
2346
|
+
},
|
|
2347
|
+
{
|
|
2348
|
+
value: "none",
|
|
2349
|
+
label: "None",
|
|
2350
|
+
hint: "Skip testing framework setup"
|
|
2351
|
+
}
|
|
2352
|
+
],
|
|
2353
|
+
initialValue: "vitest"
|
|
2354
|
+
});
|
|
2355
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2356
|
+
return response;
|
|
2357
|
+
}
|
|
2358
|
+
|
|
1846
2359
|
//#endregion
|
|
1847
2360
|
//#region src/prompts/ui-library.ts
|
|
1848
2361
|
const UI_LIBRARY_OPTIONS = {
|
|
@@ -1915,6 +2428,60 @@ async function getUILibraryChoice(uiLibrary, frontends) {
|
|
|
1915
2428
|
return selected;
|
|
1916
2429
|
}
|
|
1917
2430
|
|
|
2431
|
+
//#endregion
|
|
2432
|
+
//#region src/prompts/validation.ts
|
|
2433
|
+
async function getValidationChoice(validation) {
|
|
2434
|
+
if (validation !== void 0) return validation;
|
|
2435
|
+
const response = await navigableSelect({
|
|
2436
|
+
message: "Select validation library",
|
|
2437
|
+
options: [
|
|
2438
|
+
{
|
|
2439
|
+
value: "zod",
|
|
2440
|
+
label: "Zod",
|
|
2441
|
+
hint: "TypeScript-first schema validation (recommended)"
|
|
2442
|
+
},
|
|
2443
|
+
{
|
|
2444
|
+
value: "valibot",
|
|
2445
|
+
label: "Valibot",
|
|
2446
|
+
hint: "Smaller bundle alternative to Zod (~1KB)"
|
|
2447
|
+
},
|
|
2448
|
+
{
|
|
2449
|
+
value: "arktype",
|
|
2450
|
+
label: "ArkType",
|
|
2451
|
+
hint: "TypeScript-first validation, 2-4x faster than Zod"
|
|
2452
|
+
},
|
|
2453
|
+
{
|
|
2454
|
+
value: "typebox",
|
|
2455
|
+
label: "TypeBox",
|
|
2456
|
+
hint: "JSON Schema type builder for TypeScript"
|
|
2457
|
+
},
|
|
2458
|
+
{
|
|
2459
|
+
value: "typia",
|
|
2460
|
+
label: "Typia",
|
|
2461
|
+
hint: "Super-fast validation via compile-time transform"
|
|
2462
|
+
},
|
|
2463
|
+
{
|
|
2464
|
+
value: "runtypes",
|
|
2465
|
+
label: "Runtypes",
|
|
2466
|
+
hint: "Runtime type validation with composable validators"
|
|
2467
|
+
},
|
|
2468
|
+
{
|
|
2469
|
+
value: "effect-schema",
|
|
2470
|
+
label: "@effect/schema",
|
|
2471
|
+
hint: "Effect ecosystem schema validation with powerful transformations"
|
|
2472
|
+
},
|
|
2473
|
+
{
|
|
2474
|
+
value: "none",
|
|
2475
|
+
label: "None",
|
|
2476
|
+
hint: "Use Zod internally only (no additional library)"
|
|
2477
|
+
}
|
|
2478
|
+
],
|
|
2479
|
+
initialValue: "zod"
|
|
2480
|
+
});
|
|
2481
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2482
|
+
return response;
|
|
2483
|
+
}
|
|
2484
|
+
|
|
1918
2485
|
//#endregion
|
|
1919
2486
|
//#region src/prompts/web-deploy.ts
|
|
1920
2487
|
function hasWebFrontend(frontends) {
|
|
@@ -1980,6 +2547,19 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
1980
2547
|
dbSetup: ({ results }) => getDBSetupChoice(results.database ?? "none", flags.dbSetup, results.orm, results.backend, results.runtime),
|
|
1981
2548
|
webDeploy: ({ results }) => getDeploymentChoice(flags.webDeploy, results.runtime, results.backend, results.frontend),
|
|
1982
2549
|
serverDeploy: ({ results }) => getServerDeploymentChoice(flags.serverDeploy, results.runtime, results.backend, results.webDeploy),
|
|
2550
|
+
ai: () => getAIChoice(flags.ai),
|
|
2551
|
+
validation: () => getValidationChoice(flags.validation),
|
|
2552
|
+
forms: ({ results }) => getFormsChoice(flags.forms, results.frontend),
|
|
2553
|
+
stateManagement: ({ results }) => getStateManagementChoice(flags.stateManagement, results.frontend),
|
|
2554
|
+
animation: ({ results }) => getAnimationChoice(flags.animation, results.frontend),
|
|
2555
|
+
testing: () => getTestingChoice(flags.testing),
|
|
2556
|
+
realtime: ({ results }) => getRealtimeChoice(flags.realtime, results.backend),
|
|
2557
|
+
jobQueue: ({ results }) => getJobQueueChoice(flags.jobQueue, results.backend),
|
|
2558
|
+
fileUpload: ({ results }) => getFileUploadChoice(flags.fileUpload, results.backend),
|
|
2559
|
+
logging: ({ results }) => getLoggingChoice(flags.logging, results.backend),
|
|
2560
|
+
observability: ({ results }) => getObservabilityChoice(flags.observability, results.backend),
|
|
2561
|
+
cms: ({ results }) => getCMSChoice(flags.cms, results.backend),
|
|
2562
|
+
caching: ({ results }) => getCachingChoice(flags.caching, results.backend),
|
|
1983
2563
|
git: () => getGitChoice(flags.git),
|
|
1984
2564
|
packageManager: () => getPackageManagerChoice(flags.packageManager),
|
|
1985
2565
|
install: () => getinstallChoice(flags.install)
|
|
@@ -2009,19 +2589,19 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
2009
2589
|
api: result.api,
|
|
2010
2590
|
webDeploy: result.webDeploy,
|
|
2011
2591
|
serverDeploy: result.serverDeploy,
|
|
2012
|
-
ai:
|
|
2013
|
-
stateManagement:
|
|
2014
|
-
validation:
|
|
2015
|
-
forms:
|
|
2016
|
-
testing:
|
|
2017
|
-
realtime:
|
|
2018
|
-
jobQueue:
|
|
2019
|
-
animation:
|
|
2020
|
-
fileUpload:
|
|
2021
|
-
logging:
|
|
2022
|
-
observability:
|
|
2023
|
-
cms:
|
|
2024
|
-
caching:
|
|
2592
|
+
ai: result.ai,
|
|
2593
|
+
stateManagement: result.stateManagement,
|
|
2594
|
+
validation: result.validation,
|
|
2595
|
+
forms: result.forms,
|
|
2596
|
+
testing: result.testing,
|
|
2597
|
+
realtime: result.realtime,
|
|
2598
|
+
jobQueue: result.jobQueue,
|
|
2599
|
+
animation: result.animation,
|
|
2600
|
+
fileUpload: result.fileUpload,
|
|
2601
|
+
logging: result.logging,
|
|
2602
|
+
observability: result.observability,
|
|
2603
|
+
cms: result.cms,
|
|
2604
|
+
caching: result.caching,
|
|
2025
2605
|
ecosystem: flags.ecosystem ?? "typescript",
|
|
2026
2606
|
rustWebFramework: flags.rustWebFramework ?? "none",
|
|
2027
2607
|
rustFrontend: flags.rustFrontend ?? "none",
|
|
@@ -2302,24 +2882,17 @@ const TITLE_TEXT = `
|
|
|
2302
2882
|
██║ ╚██████╔╝███████╗███████╗███████║ ██║ ██║ ██║╚██████╗██║ ██╗
|
|
2303
2883
|
╚═╝ ╚═════╝ ╚══════╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
|
|
2304
2884
|
`;
|
|
2305
|
-
const
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
peach: "#FAB387",
|
|
2311
|
-
yellow: "#F9E2AF",
|
|
2312
|
-
green: "#A6E3A1",
|
|
2313
|
-
teal: "#94E2D5",
|
|
2314
|
-
sky: "#89DCEB",
|
|
2315
|
-
sapphire: "#74C7EC",
|
|
2316
|
-
lavender: "#B4BEFE"
|
|
2885
|
+
const monochromeTheme = {
|
|
2886
|
+
white: "#FFFFFF",
|
|
2887
|
+
lightGray: "#E5E5E5",
|
|
2888
|
+
gray: "#A3A3A3",
|
|
2889
|
+
darkGray: "#737373"
|
|
2317
2890
|
};
|
|
2318
2891
|
const renderTitle = () => {
|
|
2319
2892
|
const terminalWidth = process.stdout.columns || 80;
|
|
2320
2893
|
const titleLines = TITLE_TEXT.split("\n");
|
|
2321
|
-
if (terminalWidth < Math.max(...titleLines.map((line) => line.length))) console.log(gradient(Object.values(
|
|
2322
|
-
else console.log(gradient(Object.values(
|
|
2894
|
+
if (terminalWidth < Math.max(...titleLines.map((line) => line.length))) console.log(gradient(Object.values(monochromeTheme)).multiline(`Better Fullstack`));
|
|
2895
|
+
else console.log(gradient(Object.values(monochromeTheme)).multiline(TITLE_TEXT));
|
|
2323
2896
|
};
|
|
2324
2897
|
|
|
2325
2898
|
//#endregion
|
|
@@ -5148,6 +5721,8 @@ const router = os.router({
|
|
|
5148
5721
|
animation: types_exports.AnimationSchema.optional(),
|
|
5149
5722
|
logging: types_exports.LoggingSchema.optional(),
|
|
5150
5723
|
observability: types_exports.ObservabilitySchema.optional(),
|
|
5724
|
+
cms: types_exports.CMSSchema.optional().describe("Headless CMS solution"),
|
|
5725
|
+
caching: types_exports.CachingSchema.optional().describe("Caching solution"),
|
|
5151
5726
|
frontend: z.array(types_exports.FrontendSchema).optional(),
|
|
5152
5727
|
addons: z.array(types_exports.AddonsSchema).optional(),
|
|
5153
5728
|
examples: z.array(types_exports.ExamplesSchema).optional(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-fullstack",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.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",
|
|
@@ -72,8 +72,8 @@
|
|
|
72
72
|
"prepublishOnly": "npm run build"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@better-fullstack/template-generator": "^1.0
|
|
76
|
-
"@better-fullstack/types": "^1.0
|
|
75
|
+
"@better-fullstack/template-generator": "^1.1.0",
|
|
76
|
+
"@better-fullstack/types": "^1.1.0",
|
|
77
77
|
"@clack/core": "^0.5.0",
|
|
78
78
|
"@clack/prompts": "^1.0.0-alpha.8",
|
|
79
79
|
"@orpc/server": "^1.13.0",
|