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.
- 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-uKym6_H1.mjs} +1082 -86
- package/package.json +4 -4
|
@@ -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
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 }) =>
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
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:
|
|
2013
|
-
stateManagement:
|
|
2014
|
-
validation:
|
|
2015
|
-
forms:
|
|
2016
|
-
testing:
|
|
2017
|
-
realtime:
|
|
2018
|
-
jobQueue:
|
|
2019
|
-
animation:
|
|
2020
|
-
fileUpload:
|
|
2021
|
-
logging:
|
|
2022
|
-
observability:
|
|
2023
|
-
cms:
|
|
2024
|
-
caching:
|
|
2025
|
-
ecosystem:
|
|
2026
|
-
rustWebFramework:
|
|
2027
|
-
rustFrontend:
|
|
2028
|
-
rustOrm:
|
|
2029
|
-
rustApi:
|
|
2030
|
-
rustCli:
|
|
2031
|
-
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
|
|
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"
|
|
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(
|
|
2322
|
-
else console.log(gradient(Object.values(
|
|
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(),
|