create-better-fullstack 1.0.5 → 1.1.1

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.
@@ -1038,25 +1038,153 @@ 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) {
1044
1157
  if (backend === "convex" || backend === "none") return "none";
1045
1158
  const allowed = allowedApisForFrontends(frontend ?? [], astroIntegration);
1046
1159
  if (Api) return allowed.includes(Api) ? Api : allowed[0];
1047
- const apiOptions = allowed.map((a) => a === "trpc" ? {
1048
- value: "trpc",
1049
- label: "tRPC",
1050
- hint: "End-to-end typesafe APIs made easy"
1051
- } : a === "orpc" ? {
1052
- value: "orpc",
1053
- label: "oRPC",
1054
- hint: "End-to-end type-safe APIs that adhere to OpenAPI standards"
1055
- } : {
1056
- value: "none",
1057
- label: "None",
1058
- hint: "No API layer (e.g. for full-stack frameworks like Next.js with Route Handlers)"
1059
- });
1160
+ const apiOptionMap = {
1161
+ trpc: {
1162
+ value: "trpc",
1163
+ label: "tRPC",
1164
+ hint: "End-to-end typesafe APIs made easy"
1165
+ },
1166
+ orpc: {
1167
+ value: "orpc",
1168
+ label: "oRPC",
1169
+ hint: "End-to-end type-safe APIs that adhere to OpenAPI standards"
1170
+ },
1171
+ "ts-rest": {
1172
+ value: "ts-rest",
1173
+ label: "ts-rest",
1174
+ hint: "RPC-like client, contract, and server implementation for REST APIs"
1175
+ },
1176
+ garph: {
1177
+ value: "garph",
1178
+ label: "Garph",
1179
+ hint: "Fullstack GraphQL framework with end-to-end type safety"
1180
+ },
1181
+ none: {
1182
+ value: "none",
1183
+ label: "None",
1184
+ hint: "No API layer (e.g. for full-stack frameworks like Next.js with Route Handlers)"
1185
+ }
1186
+ };
1187
+ const apiOptions = allowed.map((a) => apiOptionMap[a]);
1060
1188
  const apiType = await navigableSelect({
1061
1189
  message: "Select API type",
1062
1190
  options: apiOptions,
@@ -1128,41 +1256,54 @@ async function getAuthChoice(auth, backend, frontend) {
1128
1256
  "native-uniwind",
1129
1257
  "native-unistyles"
1130
1258
  ].includes(f));
1131
- const options = [];
1132
- if (supportedBetterAuthFrontends) options.push({
1259
+ const options$1 = [];
1260
+ if (supportedBetterAuthFrontends) options$1.push({
1133
1261
  value: "better-auth",
1134
1262
  label: "Better-Auth",
1135
1263
  hint: "comprehensive auth framework for TypeScript"
1136
1264
  });
1137
- if (hasClerkCompatibleFrontends) options.push({
1265
+ if (hasClerkCompatibleFrontends) options$1.push({
1138
1266
  value: "clerk",
1139
1267
  label: "Clerk",
1140
1268
  hint: "More than auth, Complete User Management"
1141
1269
  });
1142
- if (options.length === 0) return "none";
1143
- options.push({
1270
+ if (options$1.length === 0) return "none";
1271
+ options$1.push({
1144
1272
  value: "none",
1145
1273
  label: "None",
1146
1274
  hint: "No auth"
1147
1275
  });
1148
1276
  const response$1 = await navigableSelect({
1149
1277
  message: "Select authentication provider",
1150
- options,
1278
+ options: options$1,
1151
1279
  initialValue: "none"
1152
1280
  });
1153
1281
  if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
1154
1282
  return response$1;
1155
1283
  }
1284
+ const supportsNextAuth = frontend?.includes("next") && backend === "self";
1285
+ const options = [{
1286
+ value: "better-auth",
1287
+ label: "Better-Auth",
1288
+ hint: "comprehensive auth framework for TypeScript"
1289
+ }, {
1290
+ value: "clerk",
1291
+ label: "Clerk",
1292
+ hint: "More than auth, Complete User Management"
1293
+ }];
1294
+ if (supportsNextAuth) options.push({
1295
+ value: "nextauth",
1296
+ label: "Auth.js (NextAuth)",
1297
+ hint: "Authentication for Next.js (formerly NextAuth.js)"
1298
+ });
1299
+ options.push({
1300
+ value: "none",
1301
+ label: "None",
1302
+ hint: "No authentication"
1303
+ });
1156
1304
  const response = await navigableSelect({
1157
1305
  message: "Select authentication provider",
1158
- options: [{
1159
- value: "better-auth",
1160
- label: "Better-Auth",
1161
- hint: "comprehensive auth framework for TypeScript"
1162
- }, {
1163
- value: "none",
1164
- label: "None"
1165
- }],
1306
+ options,
1166
1307
  initialValue: DEFAULT_CONFIG.auth
1167
1308
  });
1168
1309
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
@@ -1206,6 +1347,22 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
1206
1347
  value: "fets",
1207
1348
  label: "feTS",
1208
1349
  hint: "TypeScript HTTP Framework with e2e type-safety"
1350
+ }, {
1351
+ value: "nestjs",
1352
+ label: "NestJS",
1353
+ hint: "Progressive Node.js framework for scalable applications"
1354
+ }, {
1355
+ value: "adonisjs",
1356
+ label: "AdonisJS",
1357
+ hint: "Full-featured MVC framework for Node.js"
1358
+ }, {
1359
+ value: "nitro",
1360
+ label: "Nitro",
1361
+ hint: "Universal server framework from UnJS"
1362
+ }, {
1363
+ value: "encore",
1364
+ label: "Encore",
1365
+ hint: "Backend development platform with built-in infrastructure"
1209
1366
  });
1210
1367
  if (!hasIncompatibleFrontend) backendOptions.push({
1211
1368
  value: "convex",
@@ -1226,6 +1383,63 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
1226
1383
  return response;
1227
1384
  }
1228
1385
 
1386
+ //#endregion
1387
+ //#region src/prompts/caching.ts
1388
+ async function getCachingChoice(caching, backend) {
1389
+ if (caching !== void 0) return caching;
1390
+ if (backend === "none" || backend === "convex") return "none";
1391
+ const response = await navigableSelect({
1392
+ message: "Select caching solution",
1393
+ options: [{
1394
+ value: "upstash-redis",
1395
+ label: "Upstash Redis",
1396
+ hint: "Serverless Redis with REST API for edge and serverless"
1397
+ }, {
1398
+ value: "none",
1399
+ label: "None",
1400
+ hint: "Skip caching layer setup"
1401
+ }],
1402
+ initialValue: "none"
1403
+ });
1404
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1405
+ return response;
1406
+ }
1407
+
1408
+ //#endregion
1409
+ //#region src/prompts/cms.ts
1410
+ async function getCMSChoice(cms, backend) {
1411
+ if (cms !== void 0) return cms;
1412
+ if (backend === "none" || backend === "convex") return "none";
1413
+ const response = await navigableSelect({
1414
+ message: "Select headless CMS",
1415
+ options: [
1416
+ {
1417
+ value: "payload",
1418
+ label: "Payload",
1419
+ hint: "TypeScript-first headless CMS with Next.js integration"
1420
+ },
1421
+ {
1422
+ value: "sanity",
1423
+ label: "Sanity",
1424
+ hint: "Real-time collaborative CMS with schema-as-code"
1425
+ },
1426
+ {
1427
+ value: "strapi",
1428
+ label: "Strapi",
1429
+ hint: "Open-source headless CMS with admin panel"
1430
+ },
1431
+ {
1432
+ value: "none",
1433
+ label: "None",
1434
+ hint: "Skip headless CMS setup"
1435
+ }
1436
+ ],
1437
+ initialValue: "none"
1438
+ });
1439
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1440
+ return response;
1441
+ }
1442
+
1229
1443
  //#endregion
1230
1444
  //#region src/prompts/css-framework.ts
1231
1445
  const CSS_FRAMEWORK_OPTIONS = {
@@ -1407,6 +1621,27 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
1407
1621
  return response;
1408
1622
  }
1409
1623
 
1624
+ //#endregion
1625
+ //#region src/prompts/ecosystem.ts
1626
+ async function getEcosystemChoice(ecosystem) {
1627
+ if (ecosystem !== void 0) return ecosystem;
1628
+ const response = await navigableSelect({
1629
+ message: "Select ecosystem",
1630
+ options: [{
1631
+ value: "typescript",
1632
+ label: "TypeScript",
1633
+ hint: "Full-stack TypeScript with React, Vue, Svelte, and more"
1634
+ }, {
1635
+ value: "rust",
1636
+ label: "Rust",
1637
+ hint: "Rust ecosystem with Axum, Leptos, and more"
1638
+ }],
1639
+ initialValue: "typescript"
1640
+ });
1641
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1642
+ return response;
1643
+ }
1644
+
1410
1645
  //#endregion
1411
1646
  //#region src/prompts/effect.ts
1412
1647
  async function getEffectChoice(effect) {
@@ -1455,6 +1690,36 @@ async function getEmailChoice(email, backend) {
1455
1690
  label: "React Email",
1456
1691
  hint: "Build emails using React components (no sending service)."
1457
1692
  },
1693
+ {
1694
+ value: "nodemailer",
1695
+ label: "Nodemailer",
1696
+ hint: "Classic Node.js email sending library."
1697
+ },
1698
+ {
1699
+ value: "postmark",
1700
+ label: "Postmark",
1701
+ hint: "Transactional email service with high deliverability."
1702
+ },
1703
+ {
1704
+ value: "sendgrid",
1705
+ label: "SendGrid",
1706
+ hint: "Email delivery and marketing platform by Twilio."
1707
+ },
1708
+ {
1709
+ value: "aws-ses",
1710
+ label: "AWS SES",
1711
+ hint: "Amazon Simple Email Service for scalable email."
1712
+ },
1713
+ {
1714
+ value: "mailgun",
1715
+ label: "Mailgun",
1716
+ hint: "Email API service for sending and tracking emails."
1717
+ },
1718
+ {
1719
+ value: "plunk",
1720
+ label: "Plunk",
1721
+ hint: "Open-source email platform for developers."
1722
+ },
1458
1723
  {
1459
1724
  value: "none",
1460
1725
  label: "None",
@@ -1495,6 +1760,98 @@ async function getExamplesChoice(examples, database, frontends, backend, api) {
1495
1760
  return response;
1496
1761
  }
1497
1762
 
1763
+ //#endregion
1764
+ //#region src/prompts/file-upload.ts
1765
+ async function getFileUploadChoice(fileUpload, backend) {
1766
+ if (fileUpload !== void 0) return fileUpload;
1767
+ if (backend === "none" || backend === "convex") return "none";
1768
+ const response = await navigableSelect({
1769
+ message: "Select file upload solution",
1770
+ options: [
1771
+ {
1772
+ value: "uploadthing",
1773
+ label: "UploadThing",
1774
+ hint: "TypeScript-first file uploads with built-in validation"
1775
+ },
1776
+ {
1777
+ value: "filepond",
1778
+ label: "FilePond",
1779
+ hint: "Flexible file upload with image preview and drag & drop"
1780
+ },
1781
+ {
1782
+ value: "uppy",
1783
+ label: "Uppy",
1784
+ hint: "Modular file uploader with resumable uploads and plugins"
1785
+ },
1786
+ {
1787
+ value: "none",
1788
+ label: "None",
1789
+ hint: "Skip file upload integration"
1790
+ }
1791
+ ],
1792
+ initialValue: "none"
1793
+ });
1794
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1795
+ return response;
1796
+ }
1797
+
1798
+ //#endregion
1799
+ //#region src/prompts/forms.ts
1800
+ async function getFormsChoice(forms, frontends) {
1801
+ if (forms !== void 0) return forms;
1802
+ const { web } = splitFrontends(frontends);
1803
+ if (web.length === 0) return "none";
1804
+ const isReact = web.some((f) => [
1805
+ "tanstack-router",
1806
+ "react-router",
1807
+ "tanstack-start",
1808
+ "next",
1809
+ "redwood"
1810
+ ].includes(f));
1811
+ const isSolid = web.includes("solid");
1812
+ const isQwik = web.includes("qwik");
1813
+ const options = [];
1814
+ if (isReact) options.push({
1815
+ value: "react-hook-form",
1816
+ label: "React Hook Form",
1817
+ hint: "Performant, flexible form validation library"
1818
+ }, {
1819
+ value: "formik",
1820
+ label: "Formik",
1821
+ hint: "Popular form state management with Yup validation"
1822
+ }, {
1823
+ value: "final-form",
1824
+ label: "Final Form",
1825
+ hint: "Framework-agnostic form state management"
1826
+ }, {
1827
+ value: "conform",
1828
+ label: "Conform",
1829
+ hint: "Progressive enhancement forms with Zod validation"
1830
+ });
1831
+ options.push({
1832
+ value: "tanstack-form",
1833
+ label: "TanStack Form",
1834
+ hint: "Fully-typed, framework-agnostic form library"
1835
+ });
1836
+ if (isSolid || isQwik) options.push({
1837
+ value: "modular-forms",
1838
+ label: "Modular Forms",
1839
+ hint: "Type-safe forms for Solid and Qwik (3KB bundle)"
1840
+ });
1841
+ options.push({
1842
+ value: "none",
1843
+ label: "None",
1844
+ hint: "Build custom form handling"
1845
+ });
1846
+ const response = await navigableSelect({
1847
+ message: "Select form library",
1848
+ options,
1849
+ initialValue: isReact ? "react-hook-form" : "tanstack-form"
1850
+ });
1851
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1852
+ return response;
1853
+ }
1854
+
1498
1855
  //#endregion
1499
1856
  //#region src/prompts/frontend.ts
1500
1857
  async function getFrontendChoice(frontendOptions, backend, auth) {
@@ -1563,6 +1920,26 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
1563
1920
  value: "tanstack-start",
1564
1921
  label: "TanStack Start",
1565
1922
  hint: "SSR, Server Functions, API Routes and more with TanStack Router"
1923
+ },
1924
+ {
1925
+ value: "qwik",
1926
+ label: "Qwik",
1927
+ hint: "Resumable framework with instant load times"
1928
+ },
1929
+ {
1930
+ value: "angular",
1931
+ label: "Angular",
1932
+ hint: "Enterprise-grade TypeScript framework by Google"
1933
+ },
1934
+ {
1935
+ value: "redwood",
1936
+ label: "RedwoodJS",
1937
+ hint: "Opinionated fullstack (React + GraphQL + Prisma)"
1938
+ },
1939
+ {
1940
+ value: "fresh",
1941
+ label: "Fresh",
1942
+ hint: "Deno-native framework with islands architecture"
1566
1943
  }
1567
1944
  ].filter((option) => isFrontendAllowedWithBackend(option.value, backend, auth)),
1568
1945
  initialValue: DEFAULT_CONFIG.frontend[0]
@@ -1637,6 +2014,76 @@ async function getinstallChoice(install) {
1637
2014
  return response;
1638
2015
  }
1639
2016
 
2017
+ //#endregion
2018
+ //#region src/prompts/job-queue.ts
2019
+ async function getJobQueueChoice(jobQueue, backend) {
2020
+ if (jobQueue !== void 0) return jobQueue;
2021
+ if (backend === "none" || backend === "convex") return "none";
2022
+ const response = await navigableSelect({
2023
+ message: "Select job queue solution",
2024
+ options: [
2025
+ {
2026
+ value: "bullmq",
2027
+ label: "BullMQ",
2028
+ hint: "Redis-backed job queue for background tasks and scheduling"
2029
+ },
2030
+ {
2031
+ value: "trigger-dev",
2032
+ label: "Trigger.dev",
2033
+ hint: "Background jobs as code with serverless execution"
2034
+ },
2035
+ {
2036
+ value: "inngest",
2037
+ label: "Inngest",
2038
+ hint: "Event-driven functions with built-in queuing and scheduling"
2039
+ },
2040
+ {
2041
+ value: "temporal",
2042
+ label: "Temporal",
2043
+ hint: "Durable workflow orchestration for reliable distributed systems"
2044
+ },
2045
+ {
2046
+ value: "none",
2047
+ label: "None",
2048
+ hint: "Skip job queue/background worker setup"
2049
+ }
2050
+ ],
2051
+ initialValue: "none"
2052
+ });
2053
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2054
+ return response;
2055
+ }
2056
+
2057
+ //#endregion
2058
+ //#region src/prompts/logging.ts
2059
+ async function getLoggingChoice(logging, backend) {
2060
+ if (logging !== void 0) return logging;
2061
+ if (backend === "none" || backend === "convex") return "none";
2062
+ const response = await navigableSelect({
2063
+ message: "Select logging framework",
2064
+ options: [
2065
+ {
2066
+ value: "pino",
2067
+ label: "Pino",
2068
+ hint: "Fast JSON logger with minimal overhead"
2069
+ },
2070
+ {
2071
+ value: "winston",
2072
+ label: "Winston",
2073
+ hint: "Flexible logging library with multiple transports"
2074
+ },
2075
+ {
2076
+ value: "none",
2077
+ label: "None",
2078
+ hint: "Skip logging framework setup"
2079
+ }
2080
+ ],
2081
+ initialValue: "none"
2082
+ });
2083
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2084
+ return response;
2085
+ }
2086
+
1640
2087
  //#endregion
1641
2088
  //#region src/prompts/navigable-group.ts
1642
2089
  /**
@@ -1694,6 +2141,28 @@ async function navigableGroup(prompts, opts) {
1694
2141
  return results;
1695
2142
  }
1696
2143
 
2144
+ //#endregion
2145
+ //#region src/prompts/observability.ts
2146
+ async function getObservabilityChoice(observability, backend) {
2147
+ if (observability !== void 0) return observability;
2148
+ if (backend === "none" || backend === "convex") return "none";
2149
+ const response = await navigableSelect({
2150
+ message: "Select observability solution",
2151
+ options: [{
2152
+ value: "opentelemetry",
2153
+ label: "OpenTelemetry",
2154
+ hint: "Observability framework for traces, metrics, and logs"
2155
+ }, {
2156
+ value: "none",
2157
+ label: "None",
2158
+ hint: "Skip observability/tracing setup"
2159
+ }],
2160
+ initialValue: "none"
2161
+ });
2162
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2163
+ return response;
2164
+ }
2165
+
1697
2166
  //#endregion
1698
2167
  //#region src/prompts/orm.ts
1699
2168
  const ormOptions = {
@@ -1787,24 +2256,93 @@ async function getPackageManagerChoice(packageManager) {
1787
2256
  async function getPaymentsChoice(payments, auth, backend, frontends) {
1788
2257
  if (payments !== void 0) return payments;
1789
2258
  if (backend === "none") return "none";
1790
- if (!(auth === "better-auth" && backend !== "convex" && (frontends?.length === 0 || splitFrontends(frontends).web.length > 0))) return "none";
2259
+ const isPolarCompatible = auth === "better-auth" && backend !== "convex" && (frontends?.length === 0 || splitFrontends(frontends).web.length > 0);
2260
+ const options = [];
2261
+ if (isPolarCompatible) options.push({
2262
+ value: "polar",
2263
+ label: "Polar",
2264
+ hint: "Turn your software into a business. 6 lines of code."
2265
+ });
2266
+ options.push({
2267
+ value: "stripe",
2268
+ label: "Stripe",
2269
+ hint: "Payment processing platform for internet businesses."
2270
+ }, {
2271
+ value: "lemon-squeezy",
2272
+ label: "Lemon Squeezy",
2273
+ hint: "All-in-one platform for SaaS, digital products, and subscriptions."
2274
+ }, {
2275
+ value: "paddle",
2276
+ label: "Paddle",
2277
+ hint: "Complete payments infrastructure for SaaS."
2278
+ }, {
2279
+ value: "dodo",
2280
+ label: "Dodo Payments",
2281
+ hint: "Simple payment infrastructure for developers."
2282
+ }, {
2283
+ value: "none",
2284
+ label: "None",
2285
+ hint: "No payments integration"
2286
+ });
1791
2287
  const response = await navigableSelect({
1792
2288
  message: "Select payments provider",
1793
- options: [{
1794
- value: "polar",
1795
- label: "Polar",
1796
- hint: "Turn your software into a business. 6 lines of code."
1797
- }, {
1798
- value: "none",
1799
- label: "None",
1800
- hint: "No payments integration"
1801
- }],
2289
+ options,
1802
2290
  initialValue: DEFAULT_CONFIG.payments
1803
2291
  });
1804
2292
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1805
2293
  return response;
1806
2294
  }
1807
2295
 
2296
+ //#endregion
2297
+ //#region src/prompts/realtime.ts
2298
+ async function getRealtimeChoice(realtime, backend) {
2299
+ if (realtime !== void 0) return realtime;
2300
+ if (backend === "none" || backend === "convex") return "none";
2301
+ const response = await navigableSelect({
2302
+ message: "Select real-time solution",
2303
+ options: [
2304
+ {
2305
+ value: "socket-io",
2306
+ label: "Socket.IO",
2307
+ hint: "Real-time bidirectional communication with fallbacks"
2308
+ },
2309
+ {
2310
+ value: "partykit",
2311
+ label: "PartyKit",
2312
+ hint: "Edge-native multiplayer infrastructure on Cloudflare"
2313
+ },
2314
+ {
2315
+ value: "ably",
2316
+ label: "Ably",
2317
+ hint: "Real-time messaging platform with pub/sub and presence"
2318
+ },
2319
+ {
2320
+ value: "pusher",
2321
+ label: "Pusher",
2322
+ hint: "Real-time communication APIs with channels and events"
2323
+ },
2324
+ {
2325
+ value: "liveblocks",
2326
+ label: "Liveblocks",
2327
+ hint: "Collaboration infrastructure for multiplayer experiences"
2328
+ },
2329
+ {
2330
+ value: "yjs",
2331
+ label: "Y.js",
2332
+ hint: "CRDT library for real-time collaboration with conflict-free sync"
2333
+ },
2334
+ {
2335
+ value: "none",
2336
+ label: "None",
2337
+ hint: "Skip real-time/WebSocket integration"
2338
+ }
2339
+ ],
2340
+ initialValue: "none"
2341
+ });
2342
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2343
+ return response;
2344
+ }
2345
+
1808
2346
  //#endregion
1809
2347
  //#region src/prompts/runtime.ts
1810
2348
  async function getRuntimeChoice(runtime, backend) {
@@ -1833,6 +2371,181 @@ async function getRuntimeChoice(runtime, backend) {
1833
2371
  return response;
1834
2372
  }
1835
2373
 
2374
+ //#endregion
2375
+ //#region src/prompts/rust-ecosystem.ts
2376
+ async function getRustWebFrameworkChoice(rustWebFramework) {
2377
+ if (rustWebFramework !== void 0) return rustWebFramework;
2378
+ const response = await navigableSelect({
2379
+ message: "Select Rust web framework",
2380
+ options: [
2381
+ {
2382
+ value: "axum",
2383
+ label: "Axum",
2384
+ hint: "Ergonomic and modular web framework from Tokio"
2385
+ },
2386
+ {
2387
+ value: "actix-web",
2388
+ label: "Actix Web",
2389
+ hint: "Powerful, pragmatic, and extremely fast web framework"
2390
+ },
2391
+ {
2392
+ value: "none",
2393
+ label: "None",
2394
+ hint: "No web framework"
2395
+ }
2396
+ ],
2397
+ initialValue: "axum"
2398
+ });
2399
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2400
+ return response;
2401
+ }
2402
+ async function getRustFrontendChoice(rustFrontend) {
2403
+ if (rustFrontend !== void 0) return rustFrontend;
2404
+ const response = await navigableSelect({
2405
+ message: "Select Rust frontend framework",
2406
+ options: [
2407
+ {
2408
+ value: "leptos",
2409
+ label: "Leptos",
2410
+ hint: "Build fast web applications with Rust"
2411
+ },
2412
+ {
2413
+ value: "dioxus",
2414
+ label: "Dioxus",
2415
+ hint: "Fullstack, cross-platform UI library for Rust"
2416
+ },
2417
+ {
2418
+ value: "none",
2419
+ label: "None",
2420
+ hint: "No Rust frontend (API only)"
2421
+ }
2422
+ ],
2423
+ initialValue: "none"
2424
+ });
2425
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2426
+ return response;
2427
+ }
2428
+ async function getRustOrmChoice(rustOrm) {
2429
+ if (rustOrm !== void 0) return rustOrm;
2430
+ const response = await navigableSelect({
2431
+ message: "Select Rust ORM/database layer",
2432
+ options: [
2433
+ {
2434
+ value: "sea-orm",
2435
+ label: "SeaORM",
2436
+ hint: "Async & dynamic ORM for Rust"
2437
+ },
2438
+ {
2439
+ value: "sqlx",
2440
+ label: "SQLx",
2441
+ hint: "Async SQL toolkit with compile-time checked queries"
2442
+ },
2443
+ {
2444
+ value: "none",
2445
+ label: "None",
2446
+ hint: "No database layer"
2447
+ }
2448
+ ],
2449
+ initialValue: "none"
2450
+ });
2451
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2452
+ return response;
2453
+ }
2454
+ async function getRustApiChoice(rustApi) {
2455
+ if (rustApi !== void 0) return rustApi;
2456
+ const response = await navigableSelect({
2457
+ message: "Select Rust API layer",
2458
+ options: [
2459
+ {
2460
+ value: "tonic",
2461
+ label: "Tonic",
2462
+ hint: "gRPC implementation for Rust"
2463
+ },
2464
+ {
2465
+ value: "async-graphql",
2466
+ label: "async-graphql",
2467
+ hint: "High-performance GraphQL server library"
2468
+ },
2469
+ {
2470
+ value: "none",
2471
+ label: "None",
2472
+ hint: "REST API only"
2473
+ }
2474
+ ],
2475
+ initialValue: "none"
2476
+ });
2477
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2478
+ return response;
2479
+ }
2480
+ async function getRustCliChoice(rustCli) {
2481
+ if (rustCli !== void 0) return rustCli;
2482
+ const response = await navigableSelect({
2483
+ message: "Select Rust CLI tools",
2484
+ options: [
2485
+ {
2486
+ value: "clap",
2487
+ label: "Clap",
2488
+ hint: "Command Line Argument Parser for Rust"
2489
+ },
2490
+ {
2491
+ value: "ratatui",
2492
+ label: "Ratatui",
2493
+ hint: "Build rich terminal user interfaces"
2494
+ },
2495
+ {
2496
+ value: "none",
2497
+ label: "None",
2498
+ hint: "No CLI tools"
2499
+ }
2500
+ ],
2501
+ initialValue: "none"
2502
+ });
2503
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2504
+ return response;
2505
+ }
2506
+ async function getRustLibrariesChoice(rustLibraries) {
2507
+ if (rustLibraries !== void 0) return rustLibraries;
2508
+ const response = await navigableMultiselect({
2509
+ message: "Select Rust libraries",
2510
+ options: [
2511
+ {
2512
+ value: "serde",
2513
+ label: "Serde",
2514
+ hint: "Serialization framework for Rust"
2515
+ },
2516
+ {
2517
+ value: "validator",
2518
+ label: "Validator",
2519
+ hint: "Struct validation derive macros"
2520
+ },
2521
+ {
2522
+ value: "jsonwebtoken",
2523
+ label: "jsonwebtoken",
2524
+ hint: "JWT encoding/decoding library"
2525
+ },
2526
+ {
2527
+ value: "argon2",
2528
+ label: "Argon2",
2529
+ hint: "Password hashing library"
2530
+ },
2531
+ {
2532
+ value: "tokio-test",
2533
+ label: "Tokio Test",
2534
+ hint: "Testing utilities for Tokio"
2535
+ },
2536
+ {
2537
+ value: "mockall",
2538
+ label: "Mockall",
2539
+ hint: "Powerful mocking library for Rust"
2540
+ }
2541
+ ],
2542
+ required: false,
2543
+ initialValues: ["serde"]
2544
+ });
2545
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2546
+ return response;
2547
+ }
2548
+
1836
2549
  //#endregion
1837
2550
  //#region src/prompts/server-deploy.ts
1838
2551
  async function getServerDeploymentChoice(deployment, runtime, backend, _webDeploy) {
@@ -1843,6 +2556,115 @@ async function getServerDeploymentChoice(deployment, runtime, backend, _webDeplo
1843
2556
  return "none";
1844
2557
  }
1845
2558
 
2559
+ //#endregion
2560
+ //#region src/prompts/state-management.ts
2561
+ async function getStateManagementChoice(stateManagement, frontends) {
2562
+ if (stateManagement !== void 0) return stateManagement;
2563
+ const { web } = splitFrontends(frontends);
2564
+ if (web.length === 0) return "none";
2565
+ const isReact = web.some((f) => [
2566
+ "tanstack-router",
2567
+ "react-router",
2568
+ "tanstack-start",
2569
+ "next",
2570
+ "redwood"
2571
+ ].includes(f));
2572
+ const options = [];
2573
+ if (isReact) options.push({
2574
+ value: "zustand",
2575
+ label: "Zustand",
2576
+ hint: "Lightweight state management with simple API"
2577
+ }, {
2578
+ value: "jotai",
2579
+ label: "Jotai",
2580
+ hint: "Primitive and flexible atomic state"
2581
+ }, {
2582
+ value: "redux-toolkit",
2583
+ label: "Redux Toolkit",
2584
+ hint: "Enterprise-standard state with excellent TS support"
2585
+ }, {
2586
+ value: "valtio",
2587
+ label: "Valtio",
2588
+ hint: "Proxy-based state management"
2589
+ }, {
2590
+ value: "legend-state",
2591
+ label: "Legend State",
2592
+ hint: "High-performance observable state for React"
2593
+ }, {
2594
+ value: "mobx",
2595
+ label: "MobX",
2596
+ hint: "Observable-based reactive state management"
2597
+ });
2598
+ options.push({
2599
+ value: "nanostores",
2600
+ label: "Nanostores",
2601
+ hint: "Tiny state manager (1KB) for all frameworks"
2602
+ }, {
2603
+ value: "xstate",
2604
+ label: "XState",
2605
+ hint: "State machines and statecharts for complex logic"
2606
+ }, {
2607
+ value: "tanstack-store",
2608
+ label: "TanStack Store",
2609
+ hint: "Framework-agnostic store powering TanStack ecosystem"
2610
+ }, {
2611
+ value: "none",
2612
+ label: "None",
2613
+ hint: "Skip state management setup"
2614
+ });
2615
+ const response = await navigableSelect({
2616
+ message: "Select state management",
2617
+ options,
2618
+ initialValue: "none"
2619
+ });
2620
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2621
+ return response;
2622
+ }
2623
+
2624
+ //#endregion
2625
+ //#region src/prompts/testing.ts
2626
+ async function getTestingChoice(testing) {
2627
+ if (testing !== void 0) return testing;
2628
+ const response = await navigableSelect({
2629
+ message: "Select testing framework",
2630
+ options: [
2631
+ {
2632
+ value: "vitest",
2633
+ label: "Vitest",
2634
+ hint: "Blazing fast Vite-native unit test framework"
2635
+ },
2636
+ {
2637
+ value: "vitest-playwright",
2638
+ label: "Vitest + Playwright",
2639
+ hint: "Both unit and E2E testing for complete coverage"
2640
+ },
2641
+ {
2642
+ value: "playwright",
2643
+ label: "Playwright",
2644
+ hint: "End-to-end testing framework by Microsoft"
2645
+ },
2646
+ {
2647
+ value: "jest",
2648
+ label: "Jest",
2649
+ hint: "Classic testing framework with wide ecosystem"
2650
+ },
2651
+ {
2652
+ value: "cypress",
2653
+ label: "Cypress",
2654
+ hint: "E2E testing with time travel debugging"
2655
+ },
2656
+ {
2657
+ value: "none",
2658
+ label: "None",
2659
+ hint: "Skip testing framework setup"
2660
+ }
2661
+ ],
2662
+ initialValue: "vitest"
2663
+ });
2664
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2665
+ return response;
2666
+ }
2667
+
1846
2668
  //#endregion
1847
2669
  //#region src/prompts/ui-library.ts
1848
2670
  const UI_LIBRARY_OPTIONS = {
@@ -1915,6 +2737,60 @@ async function getUILibraryChoice(uiLibrary, frontends) {
1915
2737
  return selected;
1916
2738
  }
1917
2739
 
2740
+ //#endregion
2741
+ //#region src/prompts/validation.ts
2742
+ async function getValidationChoice(validation) {
2743
+ if (validation !== void 0) return validation;
2744
+ const response = await navigableSelect({
2745
+ message: "Select validation library",
2746
+ options: [
2747
+ {
2748
+ value: "zod",
2749
+ label: "Zod",
2750
+ hint: "TypeScript-first schema validation (recommended)"
2751
+ },
2752
+ {
2753
+ value: "valibot",
2754
+ label: "Valibot",
2755
+ hint: "Smaller bundle alternative to Zod (~1KB)"
2756
+ },
2757
+ {
2758
+ value: "arktype",
2759
+ label: "ArkType",
2760
+ hint: "TypeScript-first validation, 2-4x faster than Zod"
2761
+ },
2762
+ {
2763
+ value: "typebox",
2764
+ label: "TypeBox",
2765
+ hint: "JSON Schema type builder for TypeScript"
2766
+ },
2767
+ {
2768
+ value: "typia",
2769
+ label: "Typia",
2770
+ hint: "Super-fast validation via compile-time transform"
2771
+ },
2772
+ {
2773
+ value: "runtypes",
2774
+ label: "Runtypes",
2775
+ hint: "Runtime type validation with composable validators"
2776
+ },
2777
+ {
2778
+ value: "effect-schema",
2779
+ label: "@effect/schema",
2780
+ hint: "Effect ecosystem schema validation with powerful transformations"
2781
+ },
2782
+ {
2783
+ value: "none",
2784
+ label: "None",
2785
+ hint: "Use Zod internally only (no additional library)"
2786
+ }
2787
+ ],
2788
+ initialValue: "zod"
2789
+ });
2790
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2791
+ return response;
2792
+ }
2793
+
1918
2794
  //#endregion
1919
2795
  //#region src/prompts/web-deploy.ts
1920
2796
  function hasWebFrontend(frontends) {
@@ -1953,33 +2829,158 @@ async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []
1953
2829
  //#region src/prompts/config-prompts.ts
1954
2830
  async function gatherConfig(flags, projectName, projectDir, relativePath) {
1955
2831
  const result = await navigableGroup({
1956
- frontend: () => getFrontendChoice(flags.frontend, flags.backend, flags.auth),
2832
+ ecosystem: () => getEcosystemChoice(flags.ecosystem),
2833
+ frontend: ({ results }) => {
2834
+ if (results.ecosystem === "rust") return Promise.resolve([]);
2835
+ return getFrontendChoice(flags.frontend, flags.backend, flags.auth);
2836
+ },
1957
2837
  astroIntegration: ({ results }) => {
2838
+ if (results.ecosystem === "rust") return Promise.resolve(void 0);
1958
2839
  if (results.frontend?.includes("astro")) return getAstroIntegrationChoice(flags.astroIntegration);
1959
2840
  return Promise.resolve(void 0);
1960
2841
  },
1961
2842
  uiLibrary: ({ results }) => {
2843
+ if (results.ecosystem === "rust") return Promise.resolve("none");
1962
2844
  if (hasWebStyling(results.frontend)) return getUILibraryChoice(flags.uiLibrary, results.frontend);
1963
2845
  return Promise.resolve("none");
1964
2846
  },
1965
2847
  cssFramework: ({ results }) => {
2848
+ if (results.ecosystem === "rust") return Promise.resolve("none");
1966
2849
  if (hasWebStyling(results.frontend)) return getCSSFrameworkChoice(flags.cssFramework, results.uiLibrary);
1967
2850
  return Promise.resolve("none");
1968
2851
  },
1969
- backend: ({ results }) => getBackendFrameworkChoice(flags.backend, results.frontend),
1970
- runtime: ({ results }) => getRuntimeChoice(flags.runtime, results.backend),
1971
- database: ({ results }) => getDatabaseChoice(flags.database, results.backend, results.runtime),
1972
- orm: ({ results }) => getORMChoice(flags.orm, results.database !== "none", results.database, results.backend, results.runtime),
1973
- api: ({ results }) => getApiChoice(flags.api, results.frontend, results.backend, results.astroIntegration),
1974
- auth: ({ results }) => getAuthChoice(flags.auth, results.backend, results.frontend),
1975
- payments: ({ results }) => getPaymentsChoice(flags.payments, results.auth, results.backend, results.frontend),
1976
- email: ({ results }) => getEmailChoice(flags.email, results.backend),
1977
- effect: () => getEffectChoice(flags.effect),
1978
- addons: ({ results }) => getAddonsChoice(flags.addons, results.frontend, results.auth),
1979
- examples: ({ results }) => getExamplesChoice(flags.examples, results.database, results.frontend, results.backend, results.api),
1980
- dbSetup: ({ results }) => getDBSetupChoice(results.database ?? "none", flags.dbSetup, results.orm, results.backend, results.runtime),
1981
- webDeploy: ({ results }) => getDeploymentChoice(flags.webDeploy, results.runtime, results.backend, results.frontend),
1982
- serverDeploy: ({ results }) => getServerDeploymentChoice(flags.serverDeploy, results.runtime, results.backend, results.webDeploy),
2852
+ backend: ({ results }) => {
2853
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2854
+ return getBackendFrameworkChoice(flags.backend, results.frontend);
2855
+ },
2856
+ runtime: ({ results }) => {
2857
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2858
+ return getRuntimeChoice(flags.runtime, results.backend);
2859
+ },
2860
+ database: ({ results }) => {
2861
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2862
+ return getDatabaseChoice(flags.database, results.backend, results.runtime);
2863
+ },
2864
+ orm: ({ results }) => {
2865
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2866
+ return getORMChoice(flags.orm, results.database !== "none", results.database, results.backend, results.runtime);
2867
+ },
2868
+ api: ({ results }) => {
2869
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2870
+ return getApiChoice(flags.api, results.frontend, results.backend, results.astroIntegration);
2871
+ },
2872
+ auth: ({ results }) => {
2873
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2874
+ return getAuthChoice(flags.auth, results.backend, results.frontend);
2875
+ },
2876
+ payments: ({ results }) => {
2877
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2878
+ return getPaymentsChoice(flags.payments, results.auth, results.backend, results.frontend);
2879
+ },
2880
+ email: ({ results }) => {
2881
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2882
+ return getEmailChoice(flags.email, results.backend);
2883
+ },
2884
+ effect: ({ results }) => {
2885
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2886
+ return getEffectChoice(flags.effect);
2887
+ },
2888
+ addons: ({ results }) => {
2889
+ if (results.ecosystem === "rust") return Promise.resolve([]);
2890
+ return getAddonsChoice(flags.addons, results.frontend, results.auth);
2891
+ },
2892
+ examples: ({ results }) => {
2893
+ if (results.ecosystem === "rust") return Promise.resolve([]);
2894
+ return getExamplesChoice(flags.examples, results.database, results.frontend, results.backend, results.api);
2895
+ },
2896
+ dbSetup: ({ results }) => {
2897
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2898
+ return getDBSetupChoice(results.database ?? "none", flags.dbSetup, results.orm, results.backend, results.runtime);
2899
+ },
2900
+ webDeploy: ({ results }) => {
2901
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2902
+ return getDeploymentChoice(flags.webDeploy, results.runtime, results.backend, results.frontend);
2903
+ },
2904
+ serverDeploy: ({ results }) => {
2905
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2906
+ return getServerDeploymentChoice(flags.serverDeploy, results.runtime, results.backend, results.webDeploy);
2907
+ },
2908
+ ai: ({ results }) => {
2909
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2910
+ return getAIChoice(flags.ai);
2911
+ },
2912
+ validation: ({ results }) => {
2913
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2914
+ return getValidationChoice(flags.validation);
2915
+ },
2916
+ forms: ({ results }) => {
2917
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2918
+ return getFormsChoice(flags.forms, results.frontend);
2919
+ },
2920
+ stateManagement: ({ results }) => {
2921
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2922
+ return getStateManagementChoice(flags.stateManagement, results.frontend);
2923
+ },
2924
+ animation: ({ results }) => {
2925
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2926
+ return getAnimationChoice(flags.animation, results.frontend);
2927
+ },
2928
+ testing: ({ results }) => {
2929
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2930
+ return getTestingChoice(flags.testing);
2931
+ },
2932
+ realtime: ({ results }) => {
2933
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2934
+ return getRealtimeChoice(flags.realtime, results.backend);
2935
+ },
2936
+ jobQueue: ({ results }) => {
2937
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2938
+ return getJobQueueChoice(flags.jobQueue, results.backend);
2939
+ },
2940
+ fileUpload: ({ results }) => {
2941
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2942
+ return getFileUploadChoice(flags.fileUpload, results.backend);
2943
+ },
2944
+ logging: ({ results }) => {
2945
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2946
+ return getLoggingChoice(flags.logging, results.backend);
2947
+ },
2948
+ observability: ({ results }) => {
2949
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2950
+ return getObservabilityChoice(flags.observability, results.backend);
2951
+ },
2952
+ cms: ({ results }) => {
2953
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2954
+ return getCMSChoice(flags.cms, results.backend);
2955
+ },
2956
+ caching: ({ results }) => {
2957
+ if (results.ecosystem === "rust") return Promise.resolve("none");
2958
+ return getCachingChoice(flags.caching, results.backend);
2959
+ },
2960
+ rustWebFramework: ({ results }) => {
2961
+ if (results.ecosystem === "typescript") return Promise.resolve("none");
2962
+ return getRustWebFrameworkChoice(flags.rustWebFramework);
2963
+ },
2964
+ rustFrontend: ({ results }) => {
2965
+ if (results.ecosystem === "typescript") return Promise.resolve("none");
2966
+ return getRustFrontendChoice(flags.rustFrontend);
2967
+ },
2968
+ rustOrm: ({ results }) => {
2969
+ if (results.ecosystem === "typescript") return Promise.resolve("none");
2970
+ return getRustOrmChoice(flags.rustOrm);
2971
+ },
2972
+ rustApi: ({ results }) => {
2973
+ if (results.ecosystem === "typescript") return Promise.resolve("none");
2974
+ return getRustApiChoice(flags.rustApi);
2975
+ },
2976
+ rustCli: ({ results }) => {
2977
+ if (results.ecosystem === "typescript") return Promise.resolve("none");
2978
+ return getRustCliChoice(flags.rustCli);
2979
+ },
2980
+ rustLibraries: ({ results }) => {
2981
+ if (results.ecosystem === "typescript") return Promise.resolve([]);
2982
+ return getRustLibrariesChoice(flags.rustLibraries);
2983
+ },
1983
2984
  git: () => getGitChoice(flags.git),
1984
2985
  packageManager: () => getPackageManagerChoice(flags.packageManager),
1985
2986
  install: () => getinstallChoice(flags.install)
@@ -2009,26 +3010,26 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
2009
3010
  api: result.api,
2010
3011
  webDeploy: result.webDeploy,
2011
3012
  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",
2025
- ecosystem: flags.ecosystem ?? "typescript",
2026
- rustWebFramework: flags.rustWebFramework ?? "none",
2027
- rustFrontend: flags.rustFrontend ?? "none",
2028
- rustOrm: flags.rustOrm ?? "none",
2029
- rustApi: flags.rustApi ?? "none",
2030
- rustCli: flags.rustCli ?? "none",
2031
- rustLibraries: flags.rustLibraries ?? []
3013
+ ai: result.ai,
3014
+ stateManagement: result.stateManagement,
3015
+ validation: result.validation,
3016
+ forms: result.forms,
3017
+ testing: result.testing,
3018
+ realtime: result.realtime,
3019
+ jobQueue: result.jobQueue,
3020
+ animation: result.animation,
3021
+ fileUpload: result.fileUpload,
3022
+ logging: result.logging,
3023
+ observability: result.observability,
3024
+ cms: result.cms,
3025
+ caching: result.caching,
3026
+ ecosystem: result.ecosystem,
3027
+ rustWebFramework: result.rustWebFramework,
3028
+ rustFrontend: result.rustFrontend,
3029
+ rustOrm: result.rustOrm,
3030
+ rustApi: result.rustApi,
3031
+ rustCli: result.rustCli,
3032
+ rustLibraries: result.rustLibraries
2032
3033
  };
2033
3034
  }
2034
3035
 
@@ -2302,24 +3303,17 @@ const TITLE_TEXT = `
2302
3303
  ██║ ╚██████╔╝███████╗███████╗███████║ ██║ ██║ ██║╚██████╗██║ ██╗
2303
3304
  ╚═╝ ╚═════╝ ╚══════╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
2304
3305
  `;
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"
3306
+ const monochromeTheme = {
3307
+ white: "#FFFFFF",
3308
+ lightGray: "#E5E5E5",
3309
+ gray: "#A3A3A3",
3310
+ darkGray: "#737373"
2317
3311
  };
2318
3312
  const renderTitle = () => {
2319
3313
  const terminalWidth = process.stdout.columns || 80;
2320
3314
  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));
3315
+ if (terminalWidth < Math.max(...titleLines.map((line) => line.length))) console.log(gradient(Object.values(monochromeTheme)).multiline(`Better Fullstack`));
3316
+ else console.log(gradient(Object.values(monochromeTheme)).multiline(TITLE_TEXT));
2323
3317
  };
2324
3318
 
2325
3319
  //#endregion
@@ -5148,6 +6142,8 @@ const router = os.router({
5148
6142
  animation: types_exports.AnimationSchema.optional(),
5149
6143
  logging: types_exports.LoggingSchema.optional(),
5150
6144
  observability: types_exports.ObservabilitySchema.optional(),
6145
+ cms: types_exports.CMSSchema.optional().describe("Headless CMS solution"),
6146
+ caching: types_exports.CachingSchema.optional().describe("Caching solution"),
5151
6147
  frontend: z.array(types_exports.FrontendSchema).optional(),
5152
6148
  addons: z.array(types_exports.AddonsSchema).optional(),
5153
6149
  examples: z.array(types_exports.ExamplesSchema).optional(),