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 CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { o as createBtsCli } from "./src-B1JV3DqF.mjs";
2
+ import { o as createBtsCli } from "./src-BkBCdTQg.mjs";
3
3
 
4
4
  //#region src/cli.ts
5
5
  createBtsCli().run();
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-B1JV3DqF.mjs";
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: flags.ai ?? "none",
2013
- stateManagement: flags.stateManagement ?? "none",
2014
- validation: flags.validation ?? "zod",
2015
- forms: flags.forms ?? "react-hook-form",
2016
- testing: flags.testing ?? "vitest",
2017
- realtime: flags.realtime ?? "none",
2018
- jobQueue: flags.jobQueue ?? "none",
2019
- animation: flags.animation ?? "none",
2020
- fileUpload: flags.fileUpload ?? "none",
2021
- logging: flags.logging ?? "none",
2022
- observability: flags.observability ?? "none",
2023
- cms: flags.cms ?? "none",
2024
- caching: flags.caching ?? "none",
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 catppuccinTheme = {
2306
- pink: "#F5C2E7",
2307
- mauve: "#CBA6F7",
2308
- red: "#F38BA8",
2309
- maroon: "#E78284",
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(catppuccinTheme)).multiline(`Better Fullstack`));
2322
- else console.log(gradient(Object.values(catppuccinTheme)).multiline(TITLE_TEXT));
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.5",
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.5",
76
- "@better-fullstack/types": "^1.0.5",
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",