create-better-fullstack 1.1.12 → 1.1.15
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 +141 -17
- package/dist/index.mjs +1 -1
- package/dist/{src-BVho5D9n.mjs → src-DDAojCKV.mjs} +913 -144
- package/package.json +3 -3
|
@@ -211,8 +211,8 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
211
211
|
const distPath = path$1.dirname(__filename);
|
|
212
212
|
const PKG_ROOT = path$1.join(distPath, "../");
|
|
213
213
|
const DEFAULT_CONFIG_BASE = {
|
|
214
|
-
projectName: "my-
|
|
215
|
-
relativePath: "my-
|
|
214
|
+
projectName: "my-app",
|
|
215
|
+
relativePath: "my-app",
|
|
216
216
|
ecosystem: "typescript",
|
|
217
217
|
frontend: ["tanstack-router"],
|
|
218
218
|
database: "sqlite",
|
|
@@ -230,9 +230,13 @@ const DEFAULT_CONFIG_BASE = {
|
|
|
230
230
|
realtime: "none",
|
|
231
231
|
jobQueue: "none",
|
|
232
232
|
caching: "none",
|
|
233
|
+
search: "none",
|
|
234
|
+
fileStorage: "none",
|
|
233
235
|
animation: "none",
|
|
234
236
|
logging: "none",
|
|
235
237
|
observability: "none",
|
|
238
|
+
featureFlags: "none",
|
|
239
|
+
analytics: "none",
|
|
236
240
|
cms: "none",
|
|
237
241
|
addons: ["turborepo"],
|
|
238
242
|
examples: [],
|
|
@@ -251,7 +255,18 @@ const DEFAULT_CONFIG_BASE = {
|
|
|
251
255
|
rustOrm: "none",
|
|
252
256
|
rustApi: "none",
|
|
253
257
|
rustCli: "none",
|
|
254
|
-
rustLibraries: []
|
|
258
|
+
rustLibraries: [],
|
|
259
|
+
pythonWebFramework: "fastapi",
|
|
260
|
+
pythonOrm: "sqlalchemy",
|
|
261
|
+
pythonValidation: "pydantic",
|
|
262
|
+
pythonAi: [],
|
|
263
|
+
pythonTaskQueue: "none",
|
|
264
|
+
pythonQuality: "ruff",
|
|
265
|
+
goWebFramework: "gin",
|
|
266
|
+
goOrm: "gorm",
|
|
267
|
+
goApi: "none",
|
|
268
|
+
goCli: "none",
|
|
269
|
+
goLogging: "zap"
|
|
255
270
|
};
|
|
256
271
|
function getDefaultConfig() {
|
|
257
272
|
return {
|
|
@@ -261,7 +276,8 @@ function getDefaultConfig() {
|
|
|
261
276
|
frontend: [...DEFAULT_CONFIG_BASE.frontend],
|
|
262
277
|
addons: [...DEFAULT_CONFIG_BASE.addons],
|
|
263
278
|
examples: [...DEFAULT_CONFIG_BASE.examples],
|
|
264
|
-
rustLibraries: [...DEFAULT_CONFIG_BASE.rustLibraries]
|
|
279
|
+
rustLibraries: [...DEFAULT_CONFIG_BASE.rustLibraries],
|
|
280
|
+
pythonAi: [...DEFAULT_CONFIG_BASE.pythonAi]
|
|
265
281
|
};
|
|
266
282
|
}
|
|
267
283
|
const DEFAULT_CONFIG = getDefaultConfig();
|
|
@@ -322,7 +338,8 @@ const UI_LIBRARY_COMPATIBILITY = {
|
|
|
322
338
|
"tanstack-router",
|
|
323
339
|
"react-router",
|
|
324
340
|
"tanstack-start",
|
|
325
|
-
"next"
|
|
341
|
+
"next",
|
|
342
|
+
"astro"
|
|
326
343
|
],
|
|
327
344
|
cssFrameworks: ["tailwind"]
|
|
328
345
|
},
|
|
@@ -348,7 +365,8 @@ const UI_LIBRARY_COMPATIBILITY = {
|
|
|
348
365
|
"tanstack-router",
|
|
349
366
|
"react-router",
|
|
350
367
|
"tanstack-start",
|
|
351
|
-
"next"
|
|
368
|
+
"next",
|
|
369
|
+
"astro"
|
|
352
370
|
],
|
|
353
371
|
cssFrameworks: [
|
|
354
372
|
"tailwind",
|
|
@@ -364,7 +382,8 @@ const UI_LIBRARY_COMPATIBILITY = {
|
|
|
364
382
|
"react-router",
|
|
365
383
|
"tanstack-start",
|
|
366
384
|
"next",
|
|
367
|
-
"nuxt"
|
|
385
|
+
"nuxt",
|
|
386
|
+
"astro"
|
|
368
387
|
],
|
|
369
388
|
cssFrameworks: [
|
|
370
389
|
"tailwind",
|
|
@@ -381,7 +400,8 @@ const UI_LIBRARY_COMPATIBILITY = {
|
|
|
381
400
|
"tanstack-start",
|
|
382
401
|
"next",
|
|
383
402
|
"nuxt",
|
|
384
|
-
"solid"
|
|
403
|
+
"solid",
|
|
404
|
+
"astro"
|
|
385
405
|
],
|
|
386
406
|
cssFrameworks: [
|
|
387
407
|
"tailwind",
|
|
@@ -395,7 +415,8 @@ const UI_LIBRARY_COMPATIBILITY = {
|
|
|
395
415
|
"tanstack-router",
|
|
396
416
|
"react-router",
|
|
397
417
|
"tanstack-start",
|
|
398
|
-
"next"
|
|
418
|
+
"next",
|
|
419
|
+
"astro"
|
|
399
420
|
],
|
|
400
421
|
cssFrameworks: [
|
|
401
422
|
"tailwind",
|
|
@@ -410,7 +431,8 @@ const UI_LIBRARY_COMPATIBILITY = {
|
|
|
410
431
|
"tanstack-router",
|
|
411
432
|
"react-router",
|
|
412
433
|
"tanstack-start",
|
|
413
|
-
"next"
|
|
434
|
+
"next",
|
|
435
|
+
"astro"
|
|
414
436
|
],
|
|
415
437
|
cssFrameworks: ["tailwind"]
|
|
416
438
|
},
|
|
@@ -419,7 +441,8 @@ const UI_LIBRARY_COMPATIBILITY = {
|
|
|
419
441
|
"tanstack-router",
|
|
420
442
|
"react-router",
|
|
421
443
|
"tanstack-start",
|
|
422
|
-
"next"
|
|
444
|
+
"next",
|
|
445
|
+
"astro"
|
|
423
446
|
],
|
|
424
447
|
cssFrameworks: [
|
|
425
448
|
"tailwind",
|
|
@@ -434,7 +457,8 @@ const UI_LIBRARY_COMPATIBILITY = {
|
|
|
434
457
|
"tanstack-router",
|
|
435
458
|
"react-router",
|
|
436
459
|
"tanstack-start",
|
|
437
|
-
"next"
|
|
460
|
+
"next",
|
|
461
|
+
"astro"
|
|
438
462
|
],
|
|
439
463
|
cssFrameworks: [
|
|
440
464
|
"tailwind",
|
|
@@ -452,7 +476,8 @@ const UI_LIBRARY_COMPATIBILITY = {
|
|
|
452
476
|
"next",
|
|
453
477
|
"nuxt",
|
|
454
478
|
"svelte",
|
|
455
|
-
"solid"
|
|
479
|
+
"solid",
|
|
480
|
+
"astro"
|
|
456
481
|
],
|
|
457
482
|
cssFrameworks: [
|
|
458
483
|
"tailwind",
|
|
@@ -467,7 +492,8 @@ const UI_LIBRARY_COMPATIBILITY = {
|
|
|
467
492
|
"tanstack-router",
|
|
468
493
|
"react-router",
|
|
469
494
|
"tanstack-start",
|
|
470
|
-
"next"
|
|
495
|
+
"next",
|
|
496
|
+
"astro"
|
|
471
497
|
],
|
|
472
498
|
cssFrameworks: [
|
|
473
499
|
"tailwind",
|
|
@@ -870,6 +896,10 @@ function isFrontendAllowedWithBackend(frontend, backend, auth) {
|
|
|
870
896
|
if (frontend !== "next") return false;
|
|
871
897
|
if (backend !== "self") return false;
|
|
872
898
|
}
|
|
899
|
+
if (auth === "supabase-auth") {
|
|
900
|
+
if (frontend !== "next") return false;
|
|
901
|
+
if (backend !== "self") return false;
|
|
902
|
+
}
|
|
873
903
|
return true;
|
|
874
904
|
}
|
|
875
905
|
function validateNextAuthCompatibility(auth, backend, frontends = []) {
|
|
@@ -878,6 +908,24 @@ function validateNextAuthCompatibility(auth, backend, frontends = []) {
|
|
|
878
908
|
if (backend !== "self") exitWithError("Auth.js (NextAuth) is only supported with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
|
|
879
909
|
if (!hasNextJs) exitWithError("Auth.js (NextAuth) requires Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
|
|
880
910
|
}
|
|
911
|
+
function validateStackAuthCompatibility(auth, backend, frontends = []) {
|
|
912
|
+
if (auth !== "stack-auth") return;
|
|
913
|
+
const hasNextJs = frontends.includes("next");
|
|
914
|
+
if (backend !== "self") exitWithError("Stack Auth is only supported with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
|
|
915
|
+
if (!hasNextJs) exitWithError("Stack Auth requires Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
|
|
916
|
+
}
|
|
917
|
+
function validateSupabaseAuthCompatibility(auth, backend, frontends = []) {
|
|
918
|
+
if (auth !== "supabase-auth") return;
|
|
919
|
+
const hasNextJs = frontends.includes("next");
|
|
920
|
+
if (backend !== "self") exitWithError("Supabase Auth is only supported with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
|
|
921
|
+
if (!hasNextJs) exitWithError("Supabase Auth requires Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
|
|
922
|
+
}
|
|
923
|
+
function validateAuth0Compatibility(auth, backend, frontends = []) {
|
|
924
|
+
if (auth !== "auth0") return;
|
|
925
|
+
const hasNextJs = frontends.includes("next");
|
|
926
|
+
if (backend !== "self") exitWithError("Auth0 is only supported with the 'self' backend (fullstack Next.js). Please use '--backend self' or choose a different auth provider.");
|
|
927
|
+
if (!hasNextJs) exitWithError("Auth0 requires Next.js frontend. Please use '--frontend next' or choose a different auth provider.");
|
|
928
|
+
}
|
|
881
929
|
function allowedApisForFrontends(frontends = [], astroIntegration) {
|
|
882
930
|
const includesNuxt = frontends.includes("nuxt");
|
|
883
931
|
const includesSvelte = frontends.includes("svelte");
|
|
@@ -972,15 +1020,23 @@ function validateUILibraryFrontendCompatibility(uiLibrary, frontends = [], astro
|
|
|
972
1020
|
const compatibility = UI_LIBRARY_COMPATIBILITY[uiLibrary];
|
|
973
1021
|
const webFrontend = web[0];
|
|
974
1022
|
if (webFrontend === "astro") {
|
|
1023
|
+
const supportsReact = compatibility.frontends.some((f) => [
|
|
1024
|
+
"tanstack-router",
|
|
1025
|
+
"react-router",
|
|
1026
|
+
"tanstack-start",
|
|
1027
|
+
"next"
|
|
1028
|
+
].includes(f));
|
|
1029
|
+
const supportsNonReact = compatibility.frontends.some((f) => [
|
|
1030
|
+
"nuxt",
|
|
1031
|
+
"svelte",
|
|
1032
|
+
"solid",
|
|
1033
|
+
"qwik",
|
|
1034
|
+
"angular"
|
|
1035
|
+
].includes(f));
|
|
975
1036
|
if (astroIntegration === "react") {
|
|
976
|
-
if (
|
|
977
|
-
"tanstack-router",
|
|
978
|
-
"react-router",
|
|
979
|
-
"tanstack-start",
|
|
980
|
-
"next"
|
|
981
|
-
].includes(f))) return;
|
|
1037
|
+
if (supportsReact) return;
|
|
982
1038
|
}
|
|
983
|
-
if (!
|
|
1039
|
+
if (!supportsNonReact) {
|
|
984
1040
|
const integrationName = astroIntegration || "none";
|
|
985
1041
|
incompatibilityError({
|
|
986
1042
|
message: `UI library '${uiLibrary}' requires React.`,
|
|
@@ -1029,7 +1085,13 @@ function getCompatibleUILibraries(frontends = [], astroIntegration) {
|
|
|
1029
1085
|
"next",
|
|
1030
1086
|
"astro"
|
|
1031
1087
|
].includes(f));
|
|
1032
|
-
return compatibility.frontends.
|
|
1088
|
+
return compatibility.frontends.some((f) => [
|
|
1089
|
+
"nuxt",
|
|
1090
|
+
"svelte",
|
|
1091
|
+
"solid",
|
|
1092
|
+
"qwik",
|
|
1093
|
+
"angular"
|
|
1094
|
+
].includes(f));
|
|
1033
1095
|
}
|
|
1034
1096
|
return compatibility.frontends.includes(webFrontend);
|
|
1035
1097
|
});
|
|
@@ -1663,7 +1725,7 @@ async function getAuthChoice(auth, backend, frontend) {
|
|
|
1663
1725
|
if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
|
|
1664
1726
|
return response$1;
|
|
1665
1727
|
}
|
|
1666
|
-
const
|
|
1728
|
+
const supportsNextJsAuth = frontend?.includes("next") && backend === "self";
|
|
1667
1729
|
const options = [{
|
|
1668
1730
|
value: "better-auth",
|
|
1669
1731
|
label: "Better-Auth",
|
|
@@ -1673,11 +1735,28 @@ async function getAuthChoice(auth, backend, frontend) {
|
|
|
1673
1735
|
label: "Clerk",
|
|
1674
1736
|
hint: "More than auth, Complete User Management"
|
|
1675
1737
|
}];
|
|
1676
|
-
if (
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1738
|
+
if (supportsNextJsAuth) {
|
|
1739
|
+
options.push({
|
|
1740
|
+
value: "nextauth",
|
|
1741
|
+
label: "Auth.js (NextAuth)",
|
|
1742
|
+
hint: "Authentication for Next.js (formerly NextAuth.js)"
|
|
1743
|
+
});
|
|
1744
|
+
options.push({
|
|
1745
|
+
value: "stack-auth",
|
|
1746
|
+
label: "Stack Auth",
|
|
1747
|
+
hint: "Open-source Auth0/Clerk alternative"
|
|
1748
|
+
});
|
|
1749
|
+
options.push({
|
|
1750
|
+
value: "supabase-auth",
|
|
1751
|
+
label: "Supabase Auth",
|
|
1752
|
+
hint: "Auth with Supabase platform integration"
|
|
1753
|
+
});
|
|
1754
|
+
options.push({
|
|
1755
|
+
value: "auth0",
|
|
1756
|
+
label: "Auth0",
|
|
1757
|
+
hint: "Flexible identity platform for authentication"
|
|
1758
|
+
});
|
|
1759
|
+
}
|
|
1681
1760
|
options.push({
|
|
1682
1761
|
value: "none",
|
|
1683
1762
|
label: "None",
|
|
@@ -1889,11 +1968,23 @@ async function getDatabaseChoice(database, backend, runtime) {
|
|
|
1889
1968
|
hint: "popular open-source relational database system"
|
|
1890
1969
|
}
|
|
1891
1970
|
];
|
|
1892
|
-
if (runtime !== "workers")
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1971
|
+
if (runtime !== "workers") {
|
|
1972
|
+
databaseOptions.push({
|
|
1973
|
+
value: "mongodb",
|
|
1974
|
+
label: "MongoDB",
|
|
1975
|
+
hint: "open-source NoSQL database that stores data in JSON-like documents called BSON"
|
|
1976
|
+
});
|
|
1977
|
+
databaseOptions.push({
|
|
1978
|
+
value: "edgedb",
|
|
1979
|
+
label: "EdgeDB",
|
|
1980
|
+
hint: "graph-relational database with built-in query builder (no ORM needed)"
|
|
1981
|
+
});
|
|
1982
|
+
databaseOptions.push({
|
|
1983
|
+
value: "redis",
|
|
1984
|
+
label: "Redis",
|
|
1985
|
+
hint: "in-memory data store for caching, sessions, and real-time features"
|
|
1986
|
+
});
|
|
1987
|
+
}
|
|
1897
1988
|
const response = await navigableSelect({
|
|
1898
1989
|
message: "Select database",
|
|
1899
1990
|
options: databaseOptions,
|
|
@@ -1993,6 +2084,23 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
|
|
|
1993
2084
|
hint: "Manual setup"
|
|
1994
2085
|
}
|
|
1995
2086
|
];
|
|
2087
|
+
else if (databaseType === "redis") options = [
|
|
2088
|
+
{
|
|
2089
|
+
value: "upstash",
|
|
2090
|
+
label: "Upstash",
|
|
2091
|
+
hint: "Serverless Redis with REST API"
|
|
2092
|
+
},
|
|
2093
|
+
{
|
|
2094
|
+
value: "docker",
|
|
2095
|
+
label: "Docker",
|
|
2096
|
+
hint: "Run locally with docker compose"
|
|
2097
|
+
},
|
|
2098
|
+
{
|
|
2099
|
+
value: "none",
|
|
2100
|
+
label: "None",
|
|
2101
|
+
hint: "Manual setup"
|
|
2102
|
+
}
|
|
2103
|
+
];
|
|
1996
2104
|
else return "none";
|
|
1997
2105
|
const response = await navigableSelect({
|
|
1998
2106
|
message: `Select ${databaseType} setup option`,
|
|
@@ -2009,15 +2117,28 @@ async function getEcosystemChoice(ecosystem) {
|
|
|
2009
2117
|
if (ecosystem !== void 0) return ecosystem;
|
|
2010
2118
|
const response = await navigableSelect({
|
|
2011
2119
|
message: "Select ecosystem",
|
|
2012
|
-
options: [
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2120
|
+
options: [
|
|
2121
|
+
{
|
|
2122
|
+
value: "typescript",
|
|
2123
|
+
label: "TypeScript",
|
|
2124
|
+
hint: "Full-stack TypeScript with React, Vue, Svelte, and more"
|
|
2125
|
+
},
|
|
2126
|
+
{
|
|
2127
|
+
value: "rust",
|
|
2128
|
+
label: "Rust",
|
|
2129
|
+
hint: "Rust ecosystem with Axum, Leptos, and more"
|
|
2130
|
+
},
|
|
2131
|
+
{
|
|
2132
|
+
value: "python",
|
|
2133
|
+
label: "Python",
|
|
2134
|
+
hint: "Python ecosystem with FastAPI, Django, and AI/ML tools"
|
|
2135
|
+
},
|
|
2136
|
+
{
|
|
2137
|
+
value: "go",
|
|
2138
|
+
label: "Go",
|
|
2139
|
+
hint: "Go ecosystem with Gin, Echo, GORM, and more"
|
|
2140
|
+
}
|
|
2141
|
+
],
|
|
2021
2142
|
initialValue: "typescript"
|
|
2022
2143
|
});
|
|
2023
2144
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
@@ -2142,6 +2263,36 @@ async function getExamplesChoice(examples, database, frontends, backend, api) {
|
|
|
2142
2263
|
return response;
|
|
2143
2264
|
}
|
|
2144
2265
|
|
|
2266
|
+
//#endregion
|
|
2267
|
+
//#region src/prompts/file-storage.ts
|
|
2268
|
+
async function getFileStorageChoice(fileStorage, backend) {
|
|
2269
|
+
if (fileStorage !== void 0) return fileStorage;
|
|
2270
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
2271
|
+
const response = await navigableSelect({
|
|
2272
|
+
message: "Select file storage",
|
|
2273
|
+
options: [
|
|
2274
|
+
{
|
|
2275
|
+
value: "s3",
|
|
2276
|
+
label: "AWS S3",
|
|
2277
|
+
hint: "Amazon S3 object storage - scalable and reliable"
|
|
2278
|
+
},
|
|
2279
|
+
{
|
|
2280
|
+
value: "r2",
|
|
2281
|
+
label: "Cloudflare R2",
|
|
2282
|
+
hint: "S3-compatible storage with zero egress fees"
|
|
2283
|
+
},
|
|
2284
|
+
{
|
|
2285
|
+
value: "none",
|
|
2286
|
+
label: "None",
|
|
2287
|
+
hint: "Skip file storage setup"
|
|
2288
|
+
}
|
|
2289
|
+
],
|
|
2290
|
+
initialValue: "none"
|
|
2291
|
+
});
|
|
2292
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2293
|
+
return response;
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2145
2296
|
//#endregion
|
|
2146
2297
|
//#region src/prompts/file-upload.ts
|
|
2147
2298
|
async function getFileUploadChoice(fileUpload, backend) {
|
|
@@ -2384,6 +2535,123 @@ async function getGitChoice(git) {
|
|
|
2384
2535
|
return response;
|
|
2385
2536
|
}
|
|
2386
2537
|
|
|
2538
|
+
//#endregion
|
|
2539
|
+
//#region src/prompts/go-ecosystem.ts
|
|
2540
|
+
async function getGoWebFrameworkChoice(goWebFramework) {
|
|
2541
|
+
if (goWebFramework !== void 0) return goWebFramework;
|
|
2542
|
+
const response = await navigableSelect({
|
|
2543
|
+
message: "Select Go web framework",
|
|
2544
|
+
options: [
|
|
2545
|
+
{
|
|
2546
|
+
value: "gin",
|
|
2547
|
+
label: "Gin",
|
|
2548
|
+
hint: "High-performance HTTP web framework with martini-like API"
|
|
2549
|
+
},
|
|
2550
|
+
{
|
|
2551
|
+
value: "echo",
|
|
2552
|
+
label: "Echo",
|
|
2553
|
+
hint: "High performance, minimalist Go web framework"
|
|
2554
|
+
},
|
|
2555
|
+
{
|
|
2556
|
+
value: "none",
|
|
2557
|
+
label: "None",
|
|
2558
|
+
hint: "No web framework"
|
|
2559
|
+
}
|
|
2560
|
+
],
|
|
2561
|
+
initialValue: "gin"
|
|
2562
|
+
});
|
|
2563
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2564
|
+
return response;
|
|
2565
|
+
}
|
|
2566
|
+
async function getGoOrmChoice(goOrm) {
|
|
2567
|
+
if (goOrm !== void 0) return goOrm;
|
|
2568
|
+
const response = await navigableSelect({
|
|
2569
|
+
message: "Select Go ORM/database layer",
|
|
2570
|
+
options: [
|
|
2571
|
+
{
|
|
2572
|
+
value: "gorm",
|
|
2573
|
+
label: "GORM",
|
|
2574
|
+
hint: "The fantastic ORM library for Golang"
|
|
2575
|
+
},
|
|
2576
|
+
{
|
|
2577
|
+
value: "sqlc",
|
|
2578
|
+
label: "sqlc",
|
|
2579
|
+
hint: "Generate type-safe Go code from SQL"
|
|
2580
|
+
},
|
|
2581
|
+
{
|
|
2582
|
+
value: "none",
|
|
2583
|
+
label: "None",
|
|
2584
|
+
hint: "No ORM/database layer"
|
|
2585
|
+
}
|
|
2586
|
+
],
|
|
2587
|
+
initialValue: "gorm"
|
|
2588
|
+
});
|
|
2589
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2590
|
+
return response;
|
|
2591
|
+
}
|
|
2592
|
+
async function getGoApiChoice(goApi) {
|
|
2593
|
+
if (goApi !== void 0) return goApi;
|
|
2594
|
+
const response = await navigableSelect({
|
|
2595
|
+
message: "Select Go API layer",
|
|
2596
|
+
options: [{
|
|
2597
|
+
value: "grpc-go",
|
|
2598
|
+
label: "gRPC-Go",
|
|
2599
|
+
hint: "The Go implementation of gRPC"
|
|
2600
|
+
}, {
|
|
2601
|
+
value: "none",
|
|
2602
|
+
label: "None",
|
|
2603
|
+
hint: "No API layer"
|
|
2604
|
+
}],
|
|
2605
|
+
initialValue: "none"
|
|
2606
|
+
});
|
|
2607
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2608
|
+
return response;
|
|
2609
|
+
}
|
|
2610
|
+
async function getGoCliChoice(goCli) {
|
|
2611
|
+
if (goCli !== void 0) return goCli;
|
|
2612
|
+
const response = await navigableSelect({
|
|
2613
|
+
message: "Select Go CLI tools",
|
|
2614
|
+
options: [
|
|
2615
|
+
{
|
|
2616
|
+
value: "cobra",
|
|
2617
|
+
label: "Cobra",
|
|
2618
|
+
hint: "Library for creating powerful modern CLI applications"
|
|
2619
|
+
},
|
|
2620
|
+
{
|
|
2621
|
+
value: "bubbletea",
|
|
2622
|
+
label: "Bubble Tea",
|
|
2623
|
+
hint: "Powerful TUI framework based on The Elm Architecture"
|
|
2624
|
+
},
|
|
2625
|
+
{
|
|
2626
|
+
value: "none",
|
|
2627
|
+
label: "None",
|
|
2628
|
+
hint: "No CLI tools"
|
|
2629
|
+
}
|
|
2630
|
+
],
|
|
2631
|
+
initialValue: "none"
|
|
2632
|
+
});
|
|
2633
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2634
|
+
return response;
|
|
2635
|
+
}
|
|
2636
|
+
async function getGoLoggingChoice(goLogging) {
|
|
2637
|
+
if (goLogging !== void 0) return goLogging;
|
|
2638
|
+
const response = await navigableSelect({
|
|
2639
|
+
message: "Select Go logging library",
|
|
2640
|
+
options: [{
|
|
2641
|
+
value: "zap",
|
|
2642
|
+
label: "Zap",
|
|
2643
|
+
hint: "Blazing fast, structured, leveled logging in Go"
|
|
2644
|
+
}, {
|
|
2645
|
+
value: "none",
|
|
2646
|
+
label: "None",
|
|
2647
|
+
hint: "No logging library"
|
|
2648
|
+
}],
|
|
2649
|
+
initialValue: "zap"
|
|
2650
|
+
});
|
|
2651
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
2652
|
+
return response;
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2387
2655
|
//#endregion
|
|
2388
2656
|
//#region src/utils/command-exists.ts
|
|
2389
2657
|
async function commandExists(command) {
|
|
@@ -2411,6 +2679,18 @@ async function getinstallChoice(install, ecosystem) {
|
|
|
2411
2679
|
if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
|
|
2412
2680
|
return response$1;
|
|
2413
2681
|
}
|
|
2682
|
+
if (ecosystem === "python") {
|
|
2683
|
+
if (!await commandExists("uv")) {
|
|
2684
|
+
log.warn("uv is not installed. Please install uv from https://docs.astral.sh/uv/");
|
|
2685
|
+
return false;
|
|
2686
|
+
}
|
|
2687
|
+
const response$1 = await navigableConfirm({
|
|
2688
|
+
message: "Run uv sync?",
|
|
2689
|
+
initialValue: DEFAULT_CONFIG.install
|
|
2690
|
+
});
|
|
2691
|
+
if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
|
|
2692
|
+
return response$1;
|
|
2693
|
+
}
|
|
2414
2694
|
const response = await navigableConfirm({
|
|
2415
2695
|
message: "Install dependencies?",
|
|
2416
2696
|
initialValue: DEFAULT_CONFIG.install
|
|
@@ -2553,15 +2833,28 @@ async function getObservabilityChoice(observability, backend) {
|
|
|
2553
2833
|
if (backend === "none" || backend === "convex") return "none";
|
|
2554
2834
|
const response = await navigableSelect({
|
|
2555
2835
|
message: "Select observability solution",
|
|
2556
|
-
options: [
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2836
|
+
options: [
|
|
2837
|
+
{
|
|
2838
|
+
value: "opentelemetry",
|
|
2839
|
+
label: "OpenTelemetry",
|
|
2840
|
+
hint: "Observability framework for traces, metrics, and logs"
|
|
2841
|
+
},
|
|
2842
|
+
{
|
|
2843
|
+
value: "sentry",
|
|
2844
|
+
label: "Sentry",
|
|
2845
|
+
hint: "Error tracking and performance monitoring"
|
|
2846
|
+
},
|
|
2847
|
+
{
|
|
2848
|
+
value: "grafana",
|
|
2849
|
+
label: "Grafana",
|
|
2850
|
+
hint: "Prometheus metrics for Grafana dashboards and alerting"
|
|
2851
|
+
},
|
|
2852
|
+
{
|
|
2853
|
+
value: "none",
|
|
2854
|
+
label: "None",
|
|
2855
|
+
hint: "Skip observability/tracing setup"
|
|
2856
|
+
}
|
|
2857
|
+
],
|
|
2565
2858
|
initialValue: "none"
|
|
2566
2859
|
});
|
|
2567
2860
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
@@ -2610,6 +2903,8 @@ const ormOptions = {
|
|
|
2610
2903
|
async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
|
|
2611
2904
|
if (backend === "convex") return "none";
|
|
2612
2905
|
if (!hasDatabase) return "none";
|
|
2906
|
+
if (database === "edgedb") return "none";
|
|
2907
|
+
if (database === "redis") return "none";
|
|
2613
2908
|
if (orm !== void 0) return orm;
|
|
2614
2909
|
const response = await navigableSelect({
|
|
2615
2910
|
message: "Select ORM",
|
|
@@ -2698,6 +2993,157 @@ async function getPaymentsChoice(payments, auth, backend, frontends) {
|
|
|
2698
2993
|
return response;
|
|
2699
2994
|
}
|
|
2700
2995
|
|
|
2996
|
+
//#endregion
|
|
2997
|
+
//#region src/prompts/python-ecosystem.ts
|
|
2998
|
+
async function getPythonWebFrameworkChoice(pythonWebFramework) {
|
|
2999
|
+
if (pythonWebFramework !== void 0) return pythonWebFramework;
|
|
3000
|
+
const response = await navigableSelect({
|
|
3001
|
+
message: "Select Python web framework",
|
|
3002
|
+
options: [
|
|
3003
|
+
{
|
|
3004
|
+
value: "fastapi",
|
|
3005
|
+
label: "FastAPI",
|
|
3006
|
+
hint: "Modern, fast (high-performance) web framework for building APIs"
|
|
3007
|
+
},
|
|
3008
|
+
{
|
|
3009
|
+
value: "django",
|
|
3010
|
+
label: "Django",
|
|
3011
|
+
hint: "High-level Python web framework with batteries included"
|
|
3012
|
+
},
|
|
3013
|
+
{
|
|
3014
|
+
value: "none",
|
|
3015
|
+
label: "None",
|
|
3016
|
+
hint: "No web framework"
|
|
3017
|
+
}
|
|
3018
|
+
],
|
|
3019
|
+
initialValue: "fastapi"
|
|
3020
|
+
});
|
|
3021
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
3022
|
+
return response;
|
|
3023
|
+
}
|
|
3024
|
+
async function getPythonOrmChoice(pythonOrm) {
|
|
3025
|
+
if (pythonOrm !== void 0) return pythonOrm;
|
|
3026
|
+
const response = await navigableSelect({
|
|
3027
|
+
message: "Select Python ORM/database layer",
|
|
3028
|
+
options: [
|
|
3029
|
+
{
|
|
3030
|
+
value: "sqlalchemy",
|
|
3031
|
+
label: "SQLAlchemy",
|
|
3032
|
+
hint: "The SQL toolkit and ORM for Python"
|
|
3033
|
+
},
|
|
3034
|
+
{
|
|
3035
|
+
value: "sqlmodel",
|
|
3036
|
+
label: "SQLModel",
|
|
3037
|
+
hint: "SQL databases in Python with Pydantic and SQLAlchemy"
|
|
3038
|
+
},
|
|
3039
|
+
{
|
|
3040
|
+
value: "none",
|
|
3041
|
+
label: "None",
|
|
3042
|
+
hint: "No ORM/database layer"
|
|
3043
|
+
}
|
|
3044
|
+
],
|
|
3045
|
+
initialValue: "sqlalchemy"
|
|
3046
|
+
});
|
|
3047
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
3048
|
+
return response;
|
|
3049
|
+
}
|
|
3050
|
+
async function getPythonValidationChoice(pythonValidation) {
|
|
3051
|
+
if (pythonValidation !== void 0) return pythonValidation;
|
|
3052
|
+
const response = await navigableSelect({
|
|
3053
|
+
message: "Select Python validation library",
|
|
3054
|
+
options: [{
|
|
3055
|
+
value: "pydantic",
|
|
3056
|
+
label: "Pydantic",
|
|
3057
|
+
hint: "Data validation using Python type hints"
|
|
3058
|
+
}, {
|
|
3059
|
+
value: "none",
|
|
3060
|
+
label: "None",
|
|
3061
|
+
hint: "No validation library"
|
|
3062
|
+
}],
|
|
3063
|
+
initialValue: "pydantic"
|
|
3064
|
+
});
|
|
3065
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
3066
|
+
return response;
|
|
3067
|
+
}
|
|
3068
|
+
async function getPythonAiChoice(pythonAi) {
|
|
3069
|
+
if (pythonAi !== void 0) return pythonAi;
|
|
3070
|
+
const response = await navigableMultiselect({
|
|
3071
|
+
message: "Select Python AI/ML frameworks",
|
|
3072
|
+
options: [
|
|
3073
|
+
{
|
|
3074
|
+
value: "langchain",
|
|
3075
|
+
label: "LangChain",
|
|
3076
|
+
hint: "Building applications with LLMs through composability"
|
|
3077
|
+
},
|
|
3078
|
+
{
|
|
3079
|
+
value: "llamaindex",
|
|
3080
|
+
label: "LlamaIndex",
|
|
3081
|
+
hint: "Data framework for LLM applications"
|
|
3082
|
+
},
|
|
3083
|
+
{
|
|
3084
|
+
value: "openai-sdk",
|
|
3085
|
+
label: "OpenAI SDK",
|
|
3086
|
+
hint: "Official OpenAI Python client"
|
|
3087
|
+
},
|
|
3088
|
+
{
|
|
3089
|
+
value: "anthropic-sdk",
|
|
3090
|
+
label: "Anthropic SDK",
|
|
3091
|
+
hint: "Official Anthropic Claude API client"
|
|
3092
|
+
},
|
|
3093
|
+
{
|
|
3094
|
+
value: "langgraph",
|
|
3095
|
+
label: "LangGraph",
|
|
3096
|
+
hint: "Graph-based agent orchestration"
|
|
3097
|
+
},
|
|
3098
|
+
{
|
|
3099
|
+
value: "crewai",
|
|
3100
|
+
label: "CrewAI",
|
|
3101
|
+
hint: "Multi-agent orchestration framework"
|
|
3102
|
+
}
|
|
3103
|
+
],
|
|
3104
|
+
required: false,
|
|
3105
|
+
initialValues: []
|
|
3106
|
+
});
|
|
3107
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
3108
|
+
return response;
|
|
3109
|
+
}
|
|
3110
|
+
async function getPythonTaskQueueChoice(pythonTaskQueue) {
|
|
3111
|
+
if (pythonTaskQueue !== void 0) return pythonTaskQueue;
|
|
3112
|
+
const response = await navigableSelect({
|
|
3113
|
+
message: "Select Python task queue",
|
|
3114
|
+
options: [{
|
|
3115
|
+
value: "celery",
|
|
3116
|
+
label: "Celery",
|
|
3117
|
+
hint: "Distributed task queue for Python"
|
|
3118
|
+
}, {
|
|
3119
|
+
value: "none",
|
|
3120
|
+
label: "None",
|
|
3121
|
+
hint: "No task queue"
|
|
3122
|
+
}],
|
|
3123
|
+
initialValue: "none"
|
|
3124
|
+
});
|
|
3125
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
3126
|
+
return response;
|
|
3127
|
+
}
|
|
3128
|
+
async function getPythonQualityChoice(pythonQuality) {
|
|
3129
|
+
if (pythonQuality !== void 0) return pythonQuality;
|
|
3130
|
+
const response = await navigableSelect({
|
|
3131
|
+
message: "Select Python code quality tool",
|
|
3132
|
+
options: [{
|
|
3133
|
+
value: "ruff",
|
|
3134
|
+
label: "Ruff",
|
|
3135
|
+
hint: "An extremely fast Python linter and formatter"
|
|
3136
|
+
}, {
|
|
3137
|
+
value: "none",
|
|
3138
|
+
label: "None",
|
|
3139
|
+
hint: "No code quality tools"
|
|
3140
|
+
}],
|
|
3141
|
+
initialValue: "ruff"
|
|
3142
|
+
});
|
|
3143
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
3144
|
+
return response;
|
|
3145
|
+
}
|
|
3146
|
+
|
|
2701
3147
|
//#endregion
|
|
2702
3148
|
//#region src/prompts/realtime.ts
|
|
2703
3149
|
async function getRealtimeChoice(realtime, backend) {
|
|
@@ -2951,6 +3397,36 @@ async function getRustLibrariesChoice(rustLibraries) {
|
|
|
2951
3397
|
return response;
|
|
2952
3398
|
}
|
|
2953
3399
|
|
|
3400
|
+
//#endregion
|
|
3401
|
+
//#region src/prompts/search.ts
|
|
3402
|
+
async function getSearchChoice(search, backend) {
|
|
3403
|
+
if (search !== void 0) return search;
|
|
3404
|
+
if (backend === "none" || backend === "convex") return "none";
|
|
3405
|
+
const response = await navigableSelect({
|
|
3406
|
+
message: "Select search engine",
|
|
3407
|
+
options: [
|
|
3408
|
+
{
|
|
3409
|
+
value: "meilisearch",
|
|
3410
|
+
label: "Meilisearch",
|
|
3411
|
+
hint: "Lightning-fast search engine with typo tolerance"
|
|
3412
|
+
},
|
|
3413
|
+
{
|
|
3414
|
+
value: "typesense",
|
|
3415
|
+
label: "Typesense",
|
|
3416
|
+
hint: "Fast, typo-tolerant search with built-in vector search"
|
|
3417
|
+
},
|
|
3418
|
+
{
|
|
3419
|
+
value: "none",
|
|
3420
|
+
label: "None",
|
|
3421
|
+
hint: "Skip search engine setup"
|
|
3422
|
+
}
|
|
3423
|
+
],
|
|
3424
|
+
initialValue: "none"
|
|
3425
|
+
});
|
|
3426
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
3427
|
+
return response;
|
|
3428
|
+
}
|
|
3429
|
+
|
|
2954
3430
|
//#endregion
|
|
2955
3431
|
//#region src/prompts/server-deploy.ts
|
|
2956
3432
|
async function getServerDeploymentChoice(deployment, runtime, backend, _webDeploy) {
|
|
@@ -3236,159 +3712,219 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
3236
3712
|
const result = await navigableGroup({
|
|
3237
3713
|
ecosystem: () => getEcosystemChoice(flags.ecosystem),
|
|
3238
3714
|
frontend: ({ results }) => {
|
|
3239
|
-
if (results.ecosystem
|
|
3715
|
+
if (results.ecosystem !== "typescript") return Promise.resolve([]);
|
|
3240
3716
|
return getFrontendChoice(flags.frontend, flags.backend, flags.auth);
|
|
3241
3717
|
},
|
|
3242
3718
|
astroIntegration: ({ results }) => {
|
|
3243
|
-
if (results.ecosystem
|
|
3719
|
+
if (results.ecosystem !== "typescript") return Promise.resolve(void 0);
|
|
3244
3720
|
if (results.frontend?.includes("astro")) return getAstroIntegrationChoice(flags.astroIntegration);
|
|
3245
3721
|
return Promise.resolve(void 0);
|
|
3246
3722
|
},
|
|
3247
3723
|
uiLibrary: ({ results }) => {
|
|
3248
|
-
if (results.ecosystem
|
|
3724
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3249
3725
|
if (hasWebStyling(results.frontend)) return getUILibraryChoice(flags.uiLibrary, results.frontend, results.astroIntegration);
|
|
3250
3726
|
return Promise.resolve("none");
|
|
3251
3727
|
},
|
|
3252
3728
|
cssFramework: ({ results }) => {
|
|
3253
|
-
if (results.ecosystem
|
|
3729
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3254
3730
|
if (hasWebStyling(results.frontend)) return getCSSFrameworkChoice(flags.cssFramework, results.uiLibrary);
|
|
3255
3731
|
return Promise.resolve("none");
|
|
3256
3732
|
},
|
|
3257
3733
|
backend: ({ results }) => {
|
|
3258
|
-
if (results.ecosystem
|
|
3734
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3259
3735
|
return getBackendFrameworkChoice(flags.backend, results.frontend);
|
|
3260
3736
|
},
|
|
3261
3737
|
runtime: ({ results }) => {
|
|
3262
|
-
if (results.ecosystem
|
|
3738
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3263
3739
|
return getRuntimeChoice(flags.runtime, results.backend);
|
|
3264
3740
|
},
|
|
3265
3741
|
database: ({ results }) => {
|
|
3266
|
-
if (results.ecosystem
|
|
3742
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3267
3743
|
return getDatabaseChoice(flags.database, results.backend, results.runtime);
|
|
3268
3744
|
},
|
|
3269
3745
|
orm: ({ results }) => {
|
|
3270
|
-
if (results.ecosystem
|
|
3746
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3271
3747
|
return getORMChoice(flags.orm, results.database !== "none", results.database, results.backend, results.runtime);
|
|
3272
3748
|
},
|
|
3273
3749
|
api: ({ results }) => {
|
|
3274
|
-
if (results.ecosystem
|
|
3750
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3275
3751
|
return getApiChoice(flags.api, results.frontend, results.backend, results.astroIntegration);
|
|
3276
3752
|
},
|
|
3277
3753
|
auth: ({ results }) => {
|
|
3278
|
-
if (results.ecosystem
|
|
3754
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3279
3755
|
return getAuthChoice(flags.auth, results.backend, results.frontend);
|
|
3280
3756
|
},
|
|
3281
3757
|
payments: ({ results }) => {
|
|
3282
|
-
if (results.ecosystem
|
|
3758
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3283
3759
|
return getPaymentsChoice(flags.payments, results.auth, results.backend, results.frontend);
|
|
3284
3760
|
},
|
|
3285
3761
|
email: ({ results }) => {
|
|
3286
|
-
if (results.ecosystem
|
|
3762
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3287
3763
|
return getEmailChoice(flags.email, results.backend);
|
|
3288
3764
|
},
|
|
3289
3765
|
effect: ({ results }) => {
|
|
3290
|
-
if (results.ecosystem
|
|
3766
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3291
3767
|
return getEffectChoice(flags.effect);
|
|
3292
3768
|
},
|
|
3293
3769
|
addons: ({ results }) => {
|
|
3294
|
-
if (results.ecosystem
|
|
3770
|
+
if (results.ecosystem !== "typescript") return Promise.resolve([]);
|
|
3295
3771
|
return getAddonsChoice(flags.addons, results.frontend, results.auth);
|
|
3296
3772
|
},
|
|
3297
3773
|
examples: ({ results }) => {
|
|
3298
|
-
if (results.ecosystem
|
|
3774
|
+
if (results.ecosystem !== "typescript") return Promise.resolve([]);
|
|
3299
3775
|
return getExamplesChoice(flags.examples, results.database, results.frontend, results.backend, results.api);
|
|
3300
3776
|
},
|
|
3301
3777
|
dbSetup: ({ results }) => {
|
|
3302
|
-
if (results.ecosystem
|
|
3778
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3303
3779
|
return getDBSetupChoice(results.database ?? "none", flags.dbSetup, results.orm, results.backend, results.runtime);
|
|
3304
3780
|
},
|
|
3305
3781
|
webDeploy: ({ results }) => {
|
|
3306
|
-
if (results.ecosystem
|
|
3782
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3307
3783
|
return getDeploymentChoice(flags.webDeploy, results.runtime, results.backend, results.frontend);
|
|
3308
3784
|
},
|
|
3309
3785
|
serverDeploy: ({ results }) => {
|
|
3310
|
-
if (results.ecosystem
|
|
3786
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3311
3787
|
return getServerDeploymentChoice(flags.serverDeploy, results.runtime, results.backend, results.webDeploy);
|
|
3312
3788
|
},
|
|
3313
3789
|
ai: ({ results }) => {
|
|
3314
|
-
if (results.ecosystem
|
|
3790
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3315
3791
|
return getAIChoice(flags.ai);
|
|
3316
3792
|
},
|
|
3317
3793
|
validation: ({ results }) => {
|
|
3318
|
-
if (results.ecosystem
|
|
3794
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3319
3795
|
return getValidationChoice(flags.validation);
|
|
3320
3796
|
},
|
|
3321
3797
|
forms: ({ results }) => {
|
|
3322
|
-
if (results.ecosystem
|
|
3798
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3323
3799
|
return getFormsChoice(flags.forms, results.frontend);
|
|
3324
3800
|
},
|
|
3325
3801
|
stateManagement: ({ results }) => {
|
|
3326
|
-
if (results.ecosystem
|
|
3802
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3327
3803
|
return getStateManagementChoice(flags.stateManagement, results.frontend);
|
|
3328
3804
|
},
|
|
3329
3805
|
animation: ({ results }) => {
|
|
3330
|
-
if (results.ecosystem
|
|
3806
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3331
3807
|
return getAnimationChoice(flags.animation, results.frontend);
|
|
3332
3808
|
},
|
|
3333
3809
|
testing: ({ results }) => {
|
|
3334
|
-
if (results.ecosystem
|
|
3810
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3335
3811
|
return getTestingChoice(flags.testing);
|
|
3336
3812
|
},
|
|
3337
3813
|
realtime: ({ results }) => {
|
|
3338
|
-
if (results.ecosystem
|
|
3814
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3339
3815
|
return getRealtimeChoice(flags.realtime, results.backend);
|
|
3340
3816
|
},
|
|
3341
3817
|
jobQueue: ({ results }) => {
|
|
3342
|
-
if (results.ecosystem
|
|
3818
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3343
3819
|
return getJobQueueChoice(flags.jobQueue, results.backend);
|
|
3344
3820
|
},
|
|
3345
3821
|
fileUpload: ({ results }) => {
|
|
3346
|
-
if (results.ecosystem
|
|
3822
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3347
3823
|
return getFileUploadChoice(flags.fileUpload, results.backend);
|
|
3348
3824
|
},
|
|
3349
3825
|
logging: ({ results }) => {
|
|
3350
|
-
if (results.ecosystem
|
|
3826
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3351
3827
|
return getLoggingChoice(flags.logging, results.backend);
|
|
3352
3828
|
},
|
|
3353
3829
|
observability: ({ results }) => {
|
|
3354
|
-
if (results.ecosystem
|
|
3830
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3355
3831
|
return getObservabilityChoice(flags.observability, results.backend);
|
|
3356
3832
|
},
|
|
3833
|
+
featureFlags: ({ results }) => {
|
|
3834
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3835
|
+
return Promise.resolve(flags.featureFlags || "none");
|
|
3836
|
+
},
|
|
3837
|
+
analytics: ({ results }) => {
|
|
3838
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3839
|
+
return Promise.resolve(flags.analytics || "none");
|
|
3840
|
+
},
|
|
3357
3841
|
cms: ({ results }) => {
|
|
3358
|
-
if (results.ecosystem
|
|
3842
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3359
3843
|
return getCMSChoice(flags.cms, results.backend);
|
|
3360
3844
|
},
|
|
3361
3845
|
caching: ({ results }) => {
|
|
3362
|
-
if (results.ecosystem
|
|
3846
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3363
3847
|
return getCachingChoice(flags.caching, results.backend);
|
|
3364
3848
|
},
|
|
3849
|
+
search: ({ results }) => {
|
|
3850
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3851
|
+
return getSearchChoice(flags.search, results.backend);
|
|
3852
|
+
},
|
|
3853
|
+
fileStorage: ({ results }) => {
|
|
3854
|
+
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
3855
|
+
return getFileStorageChoice(flags.fileStorage, results.backend);
|
|
3856
|
+
},
|
|
3365
3857
|
rustWebFramework: ({ results }) => {
|
|
3366
|
-
if (results.ecosystem
|
|
3858
|
+
if (results.ecosystem !== "rust") return Promise.resolve("none");
|
|
3367
3859
|
return getRustWebFrameworkChoice(flags.rustWebFramework);
|
|
3368
3860
|
},
|
|
3369
3861
|
rustFrontend: ({ results }) => {
|
|
3370
|
-
if (results.ecosystem
|
|
3862
|
+
if (results.ecosystem !== "rust") return Promise.resolve("none");
|
|
3371
3863
|
return getRustFrontendChoice(flags.rustFrontend);
|
|
3372
3864
|
},
|
|
3373
3865
|
rustOrm: ({ results }) => {
|
|
3374
|
-
if (results.ecosystem
|
|
3866
|
+
if (results.ecosystem !== "rust") return Promise.resolve("none");
|
|
3375
3867
|
return getRustOrmChoice(flags.rustOrm);
|
|
3376
3868
|
},
|
|
3377
3869
|
rustApi: ({ results }) => {
|
|
3378
|
-
if (results.ecosystem
|
|
3870
|
+
if (results.ecosystem !== "rust") return Promise.resolve("none");
|
|
3379
3871
|
return getRustApiChoice(flags.rustApi);
|
|
3380
3872
|
},
|
|
3381
3873
|
rustCli: ({ results }) => {
|
|
3382
|
-
if (results.ecosystem
|
|
3874
|
+
if (results.ecosystem !== "rust") return Promise.resolve("none");
|
|
3383
3875
|
return getRustCliChoice(flags.rustCli);
|
|
3384
3876
|
},
|
|
3385
3877
|
rustLibraries: ({ results }) => {
|
|
3386
|
-
if (results.ecosystem
|
|
3878
|
+
if (results.ecosystem !== "rust") return Promise.resolve([]);
|
|
3387
3879
|
return getRustLibrariesChoice(flags.rustLibraries);
|
|
3388
3880
|
},
|
|
3881
|
+
pythonWebFramework: ({ results }) => {
|
|
3882
|
+
if (results.ecosystem !== "python") return Promise.resolve("none");
|
|
3883
|
+
return getPythonWebFrameworkChoice(flags.pythonWebFramework);
|
|
3884
|
+
},
|
|
3885
|
+
pythonOrm: ({ results }) => {
|
|
3886
|
+
if (results.ecosystem !== "python") return Promise.resolve("none");
|
|
3887
|
+
return getPythonOrmChoice(flags.pythonOrm);
|
|
3888
|
+
},
|
|
3889
|
+
pythonValidation: ({ results }) => {
|
|
3890
|
+
if (results.ecosystem !== "python") return Promise.resolve("none");
|
|
3891
|
+
return getPythonValidationChoice(flags.pythonValidation);
|
|
3892
|
+
},
|
|
3893
|
+
pythonAi: ({ results }) => {
|
|
3894
|
+
if (results.ecosystem !== "python") return Promise.resolve([]);
|
|
3895
|
+
return getPythonAiChoice(flags.pythonAi);
|
|
3896
|
+
},
|
|
3897
|
+
pythonTaskQueue: ({ results }) => {
|
|
3898
|
+
if (results.ecosystem !== "python") return Promise.resolve("none");
|
|
3899
|
+
return getPythonTaskQueueChoice(flags.pythonTaskQueue);
|
|
3900
|
+
},
|
|
3901
|
+
pythonQuality: ({ results }) => {
|
|
3902
|
+
if (results.ecosystem !== "python") return Promise.resolve("none");
|
|
3903
|
+
return getPythonQualityChoice(flags.pythonQuality);
|
|
3904
|
+
},
|
|
3905
|
+
goWebFramework: ({ results }) => {
|
|
3906
|
+
if (results.ecosystem !== "go") return Promise.resolve("none");
|
|
3907
|
+
return getGoWebFrameworkChoice(flags.goWebFramework);
|
|
3908
|
+
},
|
|
3909
|
+
goOrm: ({ results }) => {
|
|
3910
|
+
if (results.ecosystem !== "go") return Promise.resolve("none");
|
|
3911
|
+
return getGoOrmChoice(flags.goOrm);
|
|
3912
|
+
},
|
|
3913
|
+
goApi: ({ results }) => {
|
|
3914
|
+
if (results.ecosystem !== "go") return Promise.resolve("none");
|
|
3915
|
+
return getGoApiChoice(flags.goApi);
|
|
3916
|
+
},
|
|
3917
|
+
goCli: ({ results }) => {
|
|
3918
|
+
if (results.ecosystem !== "go") return Promise.resolve("none");
|
|
3919
|
+
return getGoCliChoice(flags.goCli);
|
|
3920
|
+
},
|
|
3921
|
+
goLogging: ({ results }) => {
|
|
3922
|
+
if (results.ecosystem !== "go") return Promise.resolve("none");
|
|
3923
|
+
return getGoLoggingChoice(flags.goLogging);
|
|
3924
|
+
},
|
|
3389
3925
|
git: () => getGitChoice(flags.git),
|
|
3390
3926
|
packageManager: ({ results }) => {
|
|
3391
|
-
if (results.ecosystem === "rust") return Promise.resolve("npm");
|
|
3927
|
+
if (results.ecosystem === "rust" || results.ecosystem === "python" || results.ecosystem === "go") return Promise.resolve("npm");
|
|
3392
3928
|
return getPackageManagerChoice(flags.packageManager);
|
|
3393
3929
|
},
|
|
3394
3930
|
install: ({ results }) => getinstallChoice(flags.install, results.ecosystem)
|
|
@@ -3429,15 +3965,30 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
3429
3965
|
fileUpload: result.fileUpload,
|
|
3430
3966
|
logging: result.logging,
|
|
3431
3967
|
observability: result.observability,
|
|
3968
|
+
featureFlags: result.featureFlags,
|
|
3969
|
+
analytics: result.analytics,
|
|
3432
3970
|
cms: result.cms,
|
|
3433
3971
|
caching: result.caching,
|
|
3972
|
+
search: result.search,
|
|
3973
|
+
fileStorage: result.fileStorage,
|
|
3434
3974
|
ecosystem: result.ecosystem,
|
|
3435
3975
|
rustWebFramework: result.rustWebFramework,
|
|
3436
3976
|
rustFrontend: result.rustFrontend,
|
|
3437
3977
|
rustOrm: result.rustOrm,
|
|
3438
3978
|
rustApi: result.rustApi,
|
|
3439
3979
|
rustCli: result.rustCli,
|
|
3440
|
-
rustLibraries: result.rustLibraries
|
|
3980
|
+
rustLibraries: result.rustLibraries,
|
|
3981
|
+
pythonWebFramework: result.pythonWebFramework,
|
|
3982
|
+
pythonOrm: result.pythonOrm,
|
|
3983
|
+
pythonValidation: result.pythonValidation,
|
|
3984
|
+
pythonAi: result.pythonAi,
|
|
3985
|
+
pythonTaskQueue: result.pythonTaskQueue,
|
|
3986
|
+
pythonQuality: result.pythonQuality,
|
|
3987
|
+
goWebFramework: result.goWebFramework,
|
|
3988
|
+
goOrm: result.goOrm,
|
|
3989
|
+
goApi: result.goApi,
|
|
3990
|
+
goCli: result.goCli,
|
|
3991
|
+
goLogging: result.goLogging
|
|
3441
3992
|
};
|
|
3442
3993
|
}
|
|
3443
3994
|
|
|
@@ -3849,6 +4400,10 @@ function processFlags(options, projectName) {
|
|
|
3849
4400
|
if (options.observability !== void 0) config.observability = options.observability;
|
|
3850
4401
|
if (options.cms !== void 0) config.cms = options.cms;
|
|
3851
4402
|
if (options.caching !== void 0) config.caching = options.caching;
|
|
4403
|
+
if (options.search !== void 0) config.search = options.search;
|
|
4404
|
+
if (options.fileStorage !== void 0) config.fileStorage = options.fileStorage;
|
|
4405
|
+
if (options.analytics !== void 0) config.analytics = options.analytics;
|
|
4406
|
+
if (options.featureFlags !== void 0) config.featureFlags = options.featureFlags;
|
|
3852
4407
|
if (options.fileUpload !== void 0) config.fileUpload = options.fileUpload;
|
|
3853
4408
|
if (options.git !== void 0) config.git = options.git;
|
|
3854
4409
|
if (options.install !== void 0) config.install = options.install;
|
|
@@ -4093,7 +4648,7 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
4093
4648
|
},
|
|
4094
4649
|
suggestions: ["Use --orm mongoose", "Use --orm prisma"]
|
|
4095
4650
|
});
|
|
4096
|
-
if (has("database") && has("orm") && db && db !== "none" && orm === "none") missingRequirementError({
|
|
4651
|
+
if (has("database") && has("orm") && db && db !== "none" && db !== "edgedb" && db !== "redis" && orm === "none") missingRequirementError({
|
|
4097
4652
|
message: "Database selection requires an ORM.",
|
|
4098
4653
|
provided: {
|
|
4099
4654
|
database: db,
|
|
@@ -4105,6 +4660,22 @@ function validateDatabaseOrmAuth(cfg, flags) {
|
|
|
4105
4660
|
"Use --orm mongoose (MongoDB only)"
|
|
4106
4661
|
]
|
|
4107
4662
|
});
|
|
4663
|
+
if (has("database") && has("orm") && db === "edgedb" && orm && orm !== "none") incompatibilityError({
|
|
4664
|
+
message: "EdgeDB has its own built-in query builder and does not require an ORM.",
|
|
4665
|
+
provided: {
|
|
4666
|
+
database: "edgedb",
|
|
4667
|
+
orm
|
|
4668
|
+
},
|
|
4669
|
+
suggestions: ["Use --orm none with EdgeDB", "Choose a different database if you want to use an ORM"]
|
|
4670
|
+
});
|
|
4671
|
+
if (has("database") && has("orm") && db === "redis" && orm && orm !== "none") incompatibilityError({
|
|
4672
|
+
message: "Redis is a key-value store and does not require an ORM.",
|
|
4673
|
+
provided: {
|
|
4674
|
+
database: "redis",
|
|
4675
|
+
orm
|
|
4676
|
+
},
|
|
4677
|
+
suggestions: ["Use --orm none with Redis", "Choose a different database if you want to use an ORM"]
|
|
4678
|
+
});
|
|
4108
4679
|
if (has("orm") && has("database") && orm && orm !== "none" && db === "none") missingRequirementError({
|
|
4109
4680
|
message: "ORM selection requires a database.",
|
|
4110
4681
|
provided: {
|
|
@@ -4140,6 +4711,10 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
4140
4711
|
database: "mongodb",
|
|
4141
4712
|
errorMessage: "MongoDB Atlas setup requires MongoDB database. Please use '--database mongodb' or choose a different setup."
|
|
4142
4713
|
},
|
|
4714
|
+
upstash: {
|
|
4715
|
+
database: "redis",
|
|
4716
|
+
errorMessage: "Upstash setup requires Redis database. Please use '--database redis' or choose a different setup."
|
|
4717
|
+
},
|
|
4143
4718
|
supabase: {
|
|
4144
4719
|
database: "postgres",
|
|
4145
4720
|
errorMessage: "Supabase setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup."
|
|
@@ -4333,6 +4908,9 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
4333
4908
|
validateExamplesCompatibility(config.examples ?? [], config.backend, config.database, config.frontend ?? [], config.api);
|
|
4334
4909
|
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend ?? []);
|
|
4335
4910
|
validateNextAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
4911
|
+
validateStackAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
4912
|
+
validateSupabaseAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
4913
|
+
validateAuth0Compatibility(config.auth, config.backend, config.frontend ?? []);
|
|
4336
4914
|
validateUILibraryFrontendCompatibility(config.uiLibrary, config.frontend ?? [], config.astroIntegration);
|
|
4337
4915
|
validateUILibraryCSSFrameworkCompatibility(config.uiLibrary, config.cssFramework);
|
|
4338
4916
|
validatePeerDependencies(config);
|
|
@@ -4344,6 +4922,9 @@ function validateConfigForProgrammaticUse(config) {
|
|
|
4344
4922
|
validateApiFrontendCompatibility(config.api, config.frontend, config.astroIntegration);
|
|
4345
4923
|
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
|
|
4346
4924
|
validateNextAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
4925
|
+
validateStackAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
4926
|
+
validateSupabaseAuthCompatibility(config.auth, config.backend, config.frontend ?? []);
|
|
4927
|
+
validateAuth0Compatibility(config.auth, config.backend, config.frontend ?? []);
|
|
4347
4928
|
if (config.addons && config.addons.length > 0) validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
|
|
4348
4929
|
validateExamplesCompatibility(config.examples ?? [], config.backend, config.database, config.frontend ?? [], config.api);
|
|
4349
4930
|
validateUILibraryFrontendCompatibility(config.uiLibrary, config.frontend ?? [], config.astroIntegration);
|
|
@@ -4471,14 +5052,29 @@ async function writeBtsConfig(projectConfig) {
|
|
|
4471
5052
|
animation: projectConfig.animation,
|
|
4472
5053
|
logging: projectConfig.logging,
|
|
4473
5054
|
observability: projectConfig.observability,
|
|
5055
|
+
featureFlags: projectConfig.featureFlags,
|
|
5056
|
+
analytics: projectConfig.analytics,
|
|
4474
5057
|
cms: projectConfig.cms,
|
|
4475
5058
|
caching: projectConfig.caching,
|
|
5059
|
+
search: projectConfig.search,
|
|
5060
|
+
fileStorage: projectConfig.fileStorage,
|
|
4476
5061
|
rustWebFramework: projectConfig.rustWebFramework,
|
|
4477
5062
|
rustFrontend: projectConfig.rustFrontend,
|
|
4478
5063
|
rustOrm: projectConfig.rustOrm,
|
|
4479
5064
|
rustApi: projectConfig.rustApi,
|
|
4480
5065
|
rustCli: projectConfig.rustCli,
|
|
4481
|
-
rustLibraries: projectConfig.rustLibraries
|
|
5066
|
+
rustLibraries: projectConfig.rustLibraries,
|
|
5067
|
+
pythonWebFramework: projectConfig.pythonWebFramework,
|
|
5068
|
+
pythonOrm: projectConfig.pythonOrm,
|
|
5069
|
+
pythonValidation: projectConfig.pythonValidation,
|
|
5070
|
+
pythonAi: projectConfig.pythonAi,
|
|
5071
|
+
pythonTaskQueue: projectConfig.pythonTaskQueue,
|
|
5072
|
+
pythonQuality: projectConfig.pythonQuality,
|
|
5073
|
+
goWebFramework: projectConfig.goWebFramework,
|
|
5074
|
+
goOrm: projectConfig.goOrm,
|
|
5075
|
+
goApi: projectConfig.goApi,
|
|
5076
|
+
goCli: projectConfig.goCli,
|
|
5077
|
+
goLogging: projectConfig.goLogging
|
|
4482
5078
|
};
|
|
4483
5079
|
const baseContent = {
|
|
4484
5080
|
$schema: "https://better-fullstack-web.vercel.app/schema.json",
|
|
@@ -4514,12 +5110,29 @@ async function writeBtsConfig(projectConfig) {
|
|
|
4514
5110
|
animation: btsConfig.animation,
|
|
4515
5111
|
logging: btsConfig.logging,
|
|
4516
5112
|
observability: btsConfig.observability,
|
|
5113
|
+
featureFlags: btsConfig.featureFlags,
|
|
5114
|
+
analytics: btsConfig.analytics,
|
|
5115
|
+
cms: btsConfig.cms,
|
|
5116
|
+
caching: btsConfig.caching,
|
|
5117
|
+
search: btsConfig.search,
|
|
5118
|
+
fileStorage: btsConfig.fileStorage,
|
|
4517
5119
|
rustWebFramework: btsConfig.rustWebFramework,
|
|
4518
5120
|
rustFrontend: btsConfig.rustFrontend,
|
|
4519
5121
|
rustOrm: btsConfig.rustOrm,
|
|
4520
5122
|
rustApi: btsConfig.rustApi,
|
|
4521
5123
|
rustCli: btsConfig.rustCli,
|
|
4522
|
-
rustLibraries: btsConfig.rustLibraries
|
|
5124
|
+
rustLibraries: btsConfig.rustLibraries,
|
|
5125
|
+
pythonWebFramework: btsConfig.pythonWebFramework,
|
|
5126
|
+
pythonOrm: btsConfig.pythonOrm,
|
|
5127
|
+
pythonValidation: btsConfig.pythonValidation,
|
|
5128
|
+
pythonAi: btsConfig.pythonAi,
|
|
5129
|
+
pythonTaskQueue: btsConfig.pythonTaskQueue,
|
|
5130
|
+
pythonQuality: btsConfig.pythonQuality,
|
|
5131
|
+
goWebFramework: btsConfig.goWebFramework,
|
|
5132
|
+
goOrm: btsConfig.goOrm,
|
|
5133
|
+
goApi: btsConfig.goApi,
|
|
5134
|
+
goCli: btsConfig.goCli,
|
|
5135
|
+
goLogging: btsConfig.goLogging
|
|
4523
5136
|
};
|
|
4524
5137
|
let configContent = JSON.stringify(baseContent);
|
|
4525
5138
|
const formatResult = JSONC.format(configContent, void 0, {
|
|
@@ -5292,12 +5905,12 @@ async function setupDockerCompose(config) {
|
|
|
5292
5905
|
const { database, projectDir, projectName, backend } = config;
|
|
5293
5906
|
if (database === "none" || database === "sqlite") return;
|
|
5294
5907
|
try {
|
|
5295
|
-
await writeEnvFile$
|
|
5908
|
+
await writeEnvFile$5(projectDir, database, projectName, backend);
|
|
5296
5909
|
} catch (error) {
|
|
5297
5910
|
if (error instanceof Error) console.error(`Error: ${error.message}`);
|
|
5298
5911
|
}
|
|
5299
5912
|
}
|
|
5300
|
-
async function writeEnvFile$
|
|
5913
|
+
async function writeEnvFile$5(projectDir, database, projectName, backend) {
|
|
5301
5914
|
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5302
5915
|
await addEnvVariablesToFile(path$1.join(projectDir, targetApp, ".env"), [{
|
|
5303
5916
|
key: "DATABASE_URL",
|
|
@@ -5358,7 +5971,7 @@ async function initMongoDBAtlas(serverDir) {
|
|
|
5358
5971
|
return null;
|
|
5359
5972
|
}
|
|
5360
5973
|
}
|
|
5361
|
-
async function writeEnvFile$
|
|
5974
|
+
async function writeEnvFile$4(projectDir, backend, config) {
|
|
5362
5975
|
try {
|
|
5363
5976
|
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5364
5977
|
await addEnvVariablesToFile(path$1.join(projectDir, targetApp, ".env"), [{
|
|
@@ -5370,7 +5983,7 @@ async function writeEnvFile$3(projectDir, backend, config) {
|
|
|
5370
5983
|
consola.error("Failed to update environment configuration");
|
|
5371
5984
|
}
|
|
5372
5985
|
}
|
|
5373
|
-
function displayManualSetupInstructions$
|
|
5986
|
+
function displayManualSetupInstructions$4() {
|
|
5374
5987
|
log.info(`
|
|
5375
5988
|
${pc.green("MongoDB Atlas Manual Setup Instructions:")}
|
|
5376
5989
|
|
|
@@ -5395,8 +6008,8 @@ async function setupMongoDBAtlas(config, cliInput) {
|
|
|
5395
6008
|
await fs.ensureDir(serverDir);
|
|
5396
6009
|
if (manualDb) {
|
|
5397
6010
|
log.info("MongoDB Atlas manual setup selected");
|
|
5398
|
-
await writeEnvFile$
|
|
5399
|
-
displayManualSetupInstructions$
|
|
6011
|
+
await writeEnvFile$4(projectDir, backend);
|
|
6012
|
+
displayManualSetupInstructions$4();
|
|
5400
6013
|
return;
|
|
5401
6014
|
}
|
|
5402
6015
|
const mode = await select({
|
|
@@ -5415,24 +6028,24 @@ async function setupMongoDBAtlas(config, cliInput) {
|
|
|
5415
6028
|
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
5416
6029
|
if (mode === "manual") {
|
|
5417
6030
|
log.info("MongoDB Atlas manual setup selected");
|
|
5418
|
-
await writeEnvFile$
|
|
5419
|
-
displayManualSetupInstructions$
|
|
6031
|
+
await writeEnvFile$4(projectDir, backend);
|
|
6032
|
+
displayManualSetupInstructions$4();
|
|
5420
6033
|
return;
|
|
5421
6034
|
}
|
|
5422
6035
|
const config$1 = await initMongoDBAtlas(serverDir);
|
|
5423
6036
|
if (config$1) {
|
|
5424
|
-
await writeEnvFile$
|
|
6037
|
+
await writeEnvFile$4(projectDir, backend, config$1);
|
|
5425
6038
|
log.success(pc.green("MongoDB Atlas setup complete! Connection saved to .env file."));
|
|
5426
6039
|
} else {
|
|
5427
6040
|
log.warn(pc.yellow("Falling back to local MongoDB configuration"));
|
|
5428
|
-
await writeEnvFile$
|
|
5429
|
-
displayManualSetupInstructions$
|
|
6041
|
+
await writeEnvFile$4(projectDir, backend);
|
|
6042
|
+
displayManualSetupInstructions$4();
|
|
5430
6043
|
}
|
|
5431
6044
|
} catch (error) {
|
|
5432
6045
|
consola.error(pc.red(`Error during MongoDB Atlas setup: ${error instanceof Error ? error.message : String(error)}`));
|
|
5433
6046
|
try {
|
|
5434
|
-
await writeEnvFile$
|
|
5435
|
-
displayManualSetupInstructions$
|
|
6047
|
+
await writeEnvFile$4(projectDir, backend);
|
|
6048
|
+
displayManualSetupInstructions$4();
|
|
5436
6049
|
} catch {}
|
|
5437
6050
|
}
|
|
5438
6051
|
}
|
|
@@ -5507,7 +6120,7 @@ async function createNeonProject(projectName, regionId, packageManager) {
|
|
|
5507
6120
|
consola$1.error(pc.red("Failed to create Neon project"));
|
|
5508
6121
|
}
|
|
5509
6122
|
}
|
|
5510
|
-
async function writeEnvFile$
|
|
6123
|
+
async function writeEnvFile$3(projectDir, backend, config) {
|
|
5511
6124
|
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5512
6125
|
await addEnvVariablesToFile(path$1.join(projectDir, targetApp, ".env"), [{
|
|
5513
6126
|
key: "DATABASE_URL",
|
|
@@ -5532,7 +6145,7 @@ async function setupWithNeonDb(projectDir, packageManager, backend) {
|
|
|
5532
6145
|
throw error;
|
|
5533
6146
|
}
|
|
5534
6147
|
}
|
|
5535
|
-
function displayManualSetupInstructions$
|
|
6148
|
+
function displayManualSetupInstructions$3(target) {
|
|
5536
6149
|
log.info(`Manual Neon PostgreSQL Setup Instructions:
|
|
5537
6150
|
|
|
5538
6151
|
1. Visit https://neon.tech and create an account
|
|
@@ -5547,8 +6160,8 @@ async function setupNeonPostgres(config, cliInput) {
|
|
|
5547
6160
|
const manualDb = cliInput?.manualDb ?? false;
|
|
5548
6161
|
try {
|
|
5549
6162
|
if (manualDb) {
|
|
5550
|
-
await writeEnvFile$
|
|
5551
|
-
displayManualSetupInstructions$
|
|
6163
|
+
await writeEnvFile$3(projectDir, backend);
|
|
6164
|
+
displayManualSetupInstructions$3(backend === "self" ? "apps/web" : "apps/server");
|
|
5552
6165
|
return;
|
|
5553
6166
|
}
|
|
5554
6167
|
const mode = await select({
|
|
@@ -5566,8 +6179,8 @@ async function setupNeonPostgres(config, cliInput) {
|
|
|
5566
6179
|
});
|
|
5567
6180
|
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
5568
6181
|
if (mode === "manual") {
|
|
5569
|
-
await writeEnvFile$
|
|
5570
|
-
displayManualSetupInstructions$
|
|
6182
|
+
await writeEnvFile$3(projectDir, backend);
|
|
6183
|
+
displayManualSetupInstructions$3(backend === "self" ? "apps/web" : "apps/server");
|
|
5571
6184
|
return;
|
|
5572
6185
|
}
|
|
5573
6186
|
const setupMethod = await select({
|
|
@@ -5602,13 +6215,13 @@ async function setupNeonPostgres(config, cliInput) {
|
|
|
5602
6215
|
if (!neonConfig) throw new Error("Failed to create project - couldn't get connection information");
|
|
5603
6216
|
const finalSpinner = spinner();
|
|
5604
6217
|
finalSpinner.start("Configuring database connection");
|
|
5605
|
-
await writeEnvFile$
|
|
6218
|
+
await writeEnvFile$3(projectDir, backend, neonConfig);
|
|
5606
6219
|
finalSpinner.stop("Neon database configured!");
|
|
5607
6220
|
}
|
|
5608
6221
|
} catch (error) {
|
|
5609
6222
|
if (error instanceof Error) consola$1.error(pc.red(error.message));
|
|
5610
|
-
await writeEnvFile$
|
|
5611
|
-
displayManualSetupInstructions$
|
|
6223
|
+
await writeEnvFile$3(projectDir, backend);
|
|
6224
|
+
displayManualSetupInstructions$3(backend === "self" ? "apps/web" : "apps/server");
|
|
5612
6225
|
}
|
|
5613
6226
|
}
|
|
5614
6227
|
|
|
@@ -5731,7 +6344,7 @@ async function setupWithCreateDb(serverDir, packageManager) {
|
|
|
5731
6344
|
return null;
|
|
5732
6345
|
}
|
|
5733
6346
|
}
|
|
5734
|
-
async function writeEnvFile$
|
|
6347
|
+
async function writeEnvFile$2(projectDir, backend, config) {
|
|
5735
6348
|
try {
|
|
5736
6349
|
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
5737
6350
|
const envPath = path$1.join(projectDir, targetApp, ".env");
|
|
@@ -5750,7 +6363,7 @@ async function writeEnvFile$1(projectDir, backend, config) {
|
|
|
5750
6363
|
consola$1.error("Failed to update environment configuration");
|
|
5751
6364
|
}
|
|
5752
6365
|
}
|
|
5753
|
-
function displayManualSetupInstructions$
|
|
6366
|
+
function displayManualSetupInstructions$2(target) {
|
|
5754
6367
|
log.info(`Manual Prisma PostgreSQL Setup Instructions:
|
|
5755
6368
|
|
|
5756
6369
|
1. Visit https://console.prisma.io and create an account
|
|
@@ -5767,8 +6380,8 @@ async function setupPrismaPostgres(config, cliInput) {
|
|
|
5767
6380
|
try {
|
|
5768
6381
|
await fs.ensureDir(dbDir);
|
|
5769
6382
|
if (manualDb) {
|
|
5770
|
-
await writeEnvFile$
|
|
5771
|
-
displayManualSetupInstructions$
|
|
6383
|
+
await writeEnvFile$2(projectDir, backend);
|
|
6384
|
+
displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
|
|
5772
6385
|
return;
|
|
5773
6386
|
}
|
|
5774
6387
|
const setupMode = await select({
|
|
@@ -5786,24 +6399,24 @@ async function setupPrismaPostgres(config, cliInput) {
|
|
|
5786
6399
|
});
|
|
5787
6400
|
if (isCancel(setupMode)) return;
|
|
5788
6401
|
if (setupMode === "manual") {
|
|
5789
|
-
await writeEnvFile$
|
|
5790
|
-
displayManualSetupInstructions$
|
|
6402
|
+
await writeEnvFile$2(projectDir, backend);
|
|
6403
|
+
displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
|
|
5791
6404
|
return;
|
|
5792
6405
|
}
|
|
5793
6406
|
const prismaConfig = await setupWithCreateDb(dbDir, packageManager);
|
|
5794
6407
|
if (prismaConfig) {
|
|
5795
|
-
await writeEnvFile$
|
|
6408
|
+
await writeEnvFile$2(projectDir, backend, prismaConfig);
|
|
5796
6409
|
log.success(pc.green("Prisma Postgres database configured successfully!"));
|
|
5797
6410
|
if (prismaConfig.claimUrl) log.info(pc.blue(`Claim URL saved to .env: ${prismaConfig.claimUrl}`));
|
|
5798
6411
|
} else {
|
|
5799
|
-
await writeEnvFile$
|
|
5800
|
-
displayManualSetupInstructions$
|
|
6412
|
+
await writeEnvFile$2(projectDir, backend);
|
|
6413
|
+
displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
|
|
5801
6414
|
}
|
|
5802
6415
|
} catch (error) {
|
|
5803
6416
|
consola$1.error(pc.red(`Error during Prisma Postgres setup: ${error instanceof Error ? error.message : String(error)}`));
|
|
5804
6417
|
try {
|
|
5805
|
-
await writeEnvFile$
|
|
5806
|
-
displayManualSetupInstructions$
|
|
6418
|
+
await writeEnvFile$2(projectDir, backend);
|
|
6419
|
+
displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
|
|
5807
6420
|
} catch {}
|
|
5808
6421
|
log.info("Setup completed with manual configuration required.");
|
|
5809
6422
|
}
|
|
@@ -6062,7 +6675,7 @@ async function createTursoDatabase(dbName, groupName) {
|
|
|
6062
6675
|
s.stop(pc.red("Failed to retrieve database connection details"));
|
|
6063
6676
|
}
|
|
6064
6677
|
}
|
|
6065
|
-
async function writeEnvFile(projectDir, backend, config) {
|
|
6678
|
+
async function writeEnvFile$1(projectDir, backend, config) {
|
|
6066
6679
|
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
6067
6680
|
await addEnvVariablesToFile(path$1.join(projectDir, targetApp, ".env"), [{
|
|
6068
6681
|
key: "DATABASE_URL",
|
|
@@ -6074,7 +6687,7 @@ async function writeEnvFile(projectDir, backend, config) {
|
|
|
6074
6687
|
condition: true
|
|
6075
6688
|
}]);
|
|
6076
6689
|
}
|
|
6077
|
-
function displayManualSetupInstructions() {
|
|
6690
|
+
function displayManualSetupInstructions$1() {
|
|
6078
6691
|
log.info(`Manual Turso Setup Instructions:
|
|
6079
6692
|
|
|
6080
6693
|
1. Visit https://turso.tech and create an account
|
|
@@ -6091,8 +6704,8 @@ async function setupTurso(config, cliInput) {
|
|
|
6091
6704
|
const setupSpinner = spinner();
|
|
6092
6705
|
try {
|
|
6093
6706
|
if (manualDb) {
|
|
6094
|
-
await writeEnvFile(projectDir, backend);
|
|
6095
|
-
displayManualSetupInstructions();
|
|
6707
|
+
await writeEnvFile$1(projectDir, backend);
|
|
6708
|
+
displayManualSetupInstructions$1();
|
|
6096
6709
|
return;
|
|
6097
6710
|
}
|
|
6098
6711
|
const mode = await select({
|
|
@@ -6110,8 +6723,8 @@ async function setupTurso(config, cliInput) {
|
|
|
6110
6723
|
});
|
|
6111
6724
|
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
6112
6725
|
if (mode === "manual") {
|
|
6113
|
-
await writeEnvFile(projectDir, backend);
|
|
6114
|
-
displayManualSetupInstructions();
|
|
6726
|
+
await writeEnvFile$1(projectDir, backend);
|
|
6727
|
+
displayManualSetupInstructions$1();
|
|
6115
6728
|
return;
|
|
6116
6729
|
}
|
|
6117
6730
|
setupSpinner.start("Checking Turso CLI availability...");
|
|
@@ -6120,8 +6733,8 @@ async function setupTurso(config, cliInput) {
|
|
|
6120
6733
|
if (platform === "win32") {
|
|
6121
6734
|
if (setupSpinner) setupSpinner.stop(pc.yellow("Turso setup not supported on Windows"));
|
|
6122
6735
|
log.warn(pc.yellow("Automatic Turso setup is not supported on Windows."));
|
|
6123
|
-
await writeEnvFile(projectDir, backend);
|
|
6124
|
-
displayManualSetupInstructions();
|
|
6736
|
+
await writeEnvFile$1(projectDir, backend);
|
|
6737
|
+
displayManualSetupInstructions$1();
|
|
6125
6738
|
return;
|
|
6126
6739
|
}
|
|
6127
6740
|
if (setupSpinner) setupSpinner.stop("Turso CLI availability checked");
|
|
@@ -6132,8 +6745,8 @@ async function setupTurso(config, cliInput) {
|
|
|
6132
6745
|
});
|
|
6133
6746
|
if (isCancel(shouldInstall)) return exitCancelled("Operation cancelled");
|
|
6134
6747
|
if (!shouldInstall) {
|
|
6135
|
-
await writeEnvFile(projectDir, backend);
|
|
6136
|
-
displayManualSetupInstructions();
|
|
6748
|
+
await writeEnvFile$1(projectDir, backend);
|
|
6749
|
+
displayManualSetupInstructions$1();
|
|
6137
6750
|
return;
|
|
6138
6751
|
}
|
|
6139
6752
|
await installTursoCLI(isMac);
|
|
@@ -6153,7 +6766,7 @@ async function setupTurso(config, cliInput) {
|
|
|
6153
6766
|
if (isCancel(dbNameResponse)) return exitCancelled("Operation cancelled");
|
|
6154
6767
|
dbName = dbNameResponse;
|
|
6155
6768
|
try {
|
|
6156
|
-
await writeEnvFile(projectDir, backend, await createTursoDatabase(dbName, selectedGroup));
|
|
6769
|
+
await writeEnvFile$1(projectDir, backend, await createTursoDatabase(dbName, selectedGroup));
|
|
6157
6770
|
success = true;
|
|
6158
6771
|
} catch (error) {
|
|
6159
6772
|
if (error instanceof Error && error.message === "DATABASE_EXISTS") {
|
|
@@ -6166,12 +6779,109 @@ async function setupTurso(config, cliInput) {
|
|
|
6166
6779
|
} catch (error) {
|
|
6167
6780
|
if (setupSpinner) setupSpinner.stop(pc.red("Turso CLI availability check failed"));
|
|
6168
6781
|
consola.error(pc.red(`Error during Turso setup: ${error instanceof Error ? error.message : String(error)}`));
|
|
6169
|
-
await writeEnvFile(projectDir, backend);
|
|
6170
|
-
displayManualSetupInstructions();
|
|
6782
|
+
await writeEnvFile$1(projectDir, backend);
|
|
6783
|
+
displayManualSetupInstructions$1();
|
|
6171
6784
|
log.success("Setup completed with manual configuration required.");
|
|
6172
6785
|
}
|
|
6173
6786
|
}
|
|
6174
6787
|
|
|
6788
|
+
//#endregion
|
|
6789
|
+
//#region src/helpers/database-providers/upstash-setup.ts
|
|
6790
|
+
async function writeEnvFile(projectDir, backend, config) {
|
|
6791
|
+
try {
|
|
6792
|
+
const targetApp = backend === "self" ? "apps/web" : "apps/server";
|
|
6793
|
+
await addEnvVariablesToFile(path$1.join(projectDir, targetApp, ".env"), [{
|
|
6794
|
+
key: "UPSTASH_REDIS_REST_URL",
|
|
6795
|
+
value: config?.redisUrl ?? "",
|
|
6796
|
+
condition: true
|
|
6797
|
+
}, {
|
|
6798
|
+
key: "UPSTASH_REDIS_REST_TOKEN",
|
|
6799
|
+
value: config?.redisToken ?? "",
|
|
6800
|
+
condition: true
|
|
6801
|
+
}]);
|
|
6802
|
+
} catch {
|
|
6803
|
+
consola.error("Failed to update environment configuration");
|
|
6804
|
+
}
|
|
6805
|
+
}
|
|
6806
|
+
function displayManualSetupInstructions() {
|
|
6807
|
+
log.info(`
|
|
6808
|
+
${pc.green("Upstash Redis Manual Setup Instructions:")}
|
|
6809
|
+
|
|
6810
|
+
1. Visit ${pc.blue("https://console.upstash.com")} and create an account
|
|
6811
|
+
|
|
6812
|
+
2. Create a new Redis database from the dashboard
|
|
6813
|
+
|
|
6814
|
+
3. Copy your REST URL and REST Token from the database details page
|
|
6815
|
+
|
|
6816
|
+
4. Add them to the .env file in apps/server/.env:
|
|
6817
|
+
${pc.dim("UPSTASH_REDIS_REST_URL=\"your_rest_url\"")}
|
|
6818
|
+
${pc.dim("UPSTASH_REDIS_REST_TOKEN=\"your_rest_token\"")}
|
|
6819
|
+
`);
|
|
6820
|
+
}
|
|
6821
|
+
async function setupUpstash(config, cliInput) {
|
|
6822
|
+
const { projectDir, backend } = config;
|
|
6823
|
+
const manualDb = cliInput?.manualDb ?? false;
|
|
6824
|
+
const serverDir = path$1.join(projectDir, "packages/db");
|
|
6825
|
+
try {
|
|
6826
|
+
await fs.ensureDir(serverDir);
|
|
6827
|
+
if (manualDb) {
|
|
6828
|
+
log.info("Upstash Redis manual setup selected");
|
|
6829
|
+
await writeEnvFile(projectDir, backend);
|
|
6830
|
+
displayManualSetupInstructions();
|
|
6831
|
+
return;
|
|
6832
|
+
}
|
|
6833
|
+
const mode = await select({
|
|
6834
|
+
message: "Upstash Redis setup: choose mode",
|
|
6835
|
+
options: [{
|
|
6836
|
+
label: "Enter credentials",
|
|
6837
|
+
value: "credentials",
|
|
6838
|
+
hint: "Enter your Upstash REST URL and token"
|
|
6839
|
+
}, {
|
|
6840
|
+
label: "Manual",
|
|
6841
|
+
value: "manual",
|
|
6842
|
+
hint: "Manual setup, add env vars yourself"
|
|
6843
|
+
}],
|
|
6844
|
+
initialValue: "credentials"
|
|
6845
|
+
});
|
|
6846
|
+
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
6847
|
+
if (mode === "manual") {
|
|
6848
|
+
log.info("Upstash Redis manual setup selected");
|
|
6849
|
+
await writeEnvFile(projectDir, backend);
|
|
6850
|
+
displayManualSetupInstructions();
|
|
6851
|
+
return;
|
|
6852
|
+
}
|
|
6853
|
+
const redisUrl = await text({
|
|
6854
|
+
message: "Enter your Upstash Redis REST URL:",
|
|
6855
|
+
placeholder: "https://xxx.upstash.io",
|
|
6856
|
+
validate(value) {
|
|
6857
|
+
if (!value) return "Please enter a REST URL";
|
|
6858
|
+
if (!value.startsWith("https://") || !value.includes("upstash.io")) return "URL should be a valid Upstash REST URL (https://xxx.upstash.io)";
|
|
6859
|
+
}
|
|
6860
|
+
});
|
|
6861
|
+
if (isCancel(redisUrl)) return exitCancelled("Operation cancelled");
|
|
6862
|
+
const redisToken = await text({
|
|
6863
|
+
message: "Enter your Upstash Redis REST Token:",
|
|
6864
|
+
placeholder: "AXxxxxxxxxxxxxxxxxxxxx",
|
|
6865
|
+
validate(value) {
|
|
6866
|
+
if (!value) return "Please enter a REST token";
|
|
6867
|
+
if (value.length < 20) return "Token appears to be too short";
|
|
6868
|
+
}
|
|
6869
|
+
});
|
|
6870
|
+
if (isCancel(redisToken)) return exitCancelled("Operation cancelled");
|
|
6871
|
+
await writeEnvFile(projectDir, backend, {
|
|
6872
|
+
redisUrl,
|
|
6873
|
+
redisToken
|
|
6874
|
+
});
|
|
6875
|
+
log.success(pc.green("Upstash Redis setup complete! Credentials saved to .env file."));
|
|
6876
|
+
} catch (error) {
|
|
6877
|
+
consola.error(pc.red(`Error during Upstash setup: ${error instanceof Error ? error.message : String(error)}`));
|
|
6878
|
+
try {
|
|
6879
|
+
await writeEnvFile(projectDir, backend);
|
|
6880
|
+
displayManualSetupInstructions();
|
|
6881
|
+
} catch {}
|
|
6882
|
+
}
|
|
6883
|
+
}
|
|
6884
|
+
|
|
6175
6885
|
//#endregion
|
|
6176
6886
|
//#region src/helpers/core/db-setup.ts
|
|
6177
6887
|
/**
|
|
@@ -6201,6 +6911,7 @@ async function setupDatabase(config, cliInput) {
|
|
|
6201
6911
|
else if (dbSetup === "supabase") await setupSupabase(config, cliInput);
|
|
6202
6912
|
} else if (database === "mysql" && dbSetup === "planetscale") await setupPlanetScale(config);
|
|
6203
6913
|
else if (database === "mongodb" && dbSetup === "mongodb-atlas") await setupMongoDBAtlas(config, cliInput);
|
|
6914
|
+
else if (database === "redis" && dbSetup === "upstash") await setupUpstash(config, cliInput);
|
|
6204
6915
|
} catch (error) {
|
|
6205
6916
|
if (error instanceof Error) consola.error(pc.red(error.message));
|
|
6206
6917
|
}
|
|
@@ -6258,6 +6969,20 @@ async function runCargoBuild({ projectDir }) {
|
|
|
6258
6969
|
if (error instanceof Error) consola.error(pc.red(`Cargo build error: ${error.message}`));
|
|
6259
6970
|
}
|
|
6260
6971
|
}
|
|
6972
|
+
async function runUvSync({ projectDir }) {
|
|
6973
|
+
const s = spinner();
|
|
6974
|
+
try {
|
|
6975
|
+
s.start("Running uv sync...");
|
|
6976
|
+
await $({
|
|
6977
|
+
cwd: projectDir,
|
|
6978
|
+
stderr: "inherit"
|
|
6979
|
+
})`uv sync`;
|
|
6980
|
+
s.stop("Python dependencies installed successfully");
|
|
6981
|
+
} catch (error) {
|
|
6982
|
+
s.stop(pc.red("uv sync failed"));
|
|
6983
|
+
if (error instanceof Error) consola.error(pc.red(`uv sync error: ${error.message}`));
|
|
6984
|
+
}
|
|
6985
|
+
}
|
|
6261
6986
|
|
|
6262
6987
|
//#endregion
|
|
6263
6988
|
//#region src/utils/docker-utils.ts
|
|
@@ -6546,11 +7271,12 @@ async function createProject(options, cliInput = {}) {
|
|
|
6546
7271
|
await writeBtsConfig(options);
|
|
6547
7272
|
await formatProject(projectDir);
|
|
6548
7273
|
if (!isSilent()) log.success("Project template successfully scaffolded!");
|
|
6549
|
-
if (options.install && options.ecosystem
|
|
7274
|
+
if (options.install && options.ecosystem === "typescript") await installDependencies({
|
|
6550
7275
|
projectDir,
|
|
6551
7276
|
packageManager: options.packageManager
|
|
6552
7277
|
});
|
|
6553
7278
|
if (options.install && options.ecosystem === "rust") await runCargoBuild({ projectDir });
|
|
7279
|
+
if (options.install && options.ecosystem === "python") await runUvSync({ projectDir });
|
|
6554
7280
|
await initializeGit(projectDir, options.git);
|
|
6555
7281
|
if (!isSilent()) await displayPostInstallInstructions({
|
|
6556
7282
|
...options,
|
|
@@ -6665,7 +7391,22 @@ async function createProjectHandler(input, options = {}) {
|
|
|
6665
7391
|
rustCli: "none",
|
|
6666
7392
|
rustLibraries: [],
|
|
6667
7393
|
cms: "none",
|
|
6668
|
-
caching: "none"
|
|
7394
|
+
caching: "none",
|
|
7395
|
+
search: "none",
|
|
7396
|
+
featureFlags: "none",
|
|
7397
|
+
analytics: "none",
|
|
7398
|
+
fileStorage: "none",
|
|
7399
|
+
pythonWebFramework: "none",
|
|
7400
|
+
pythonOrm: "none",
|
|
7401
|
+
pythonValidation: "none",
|
|
7402
|
+
pythonAi: [],
|
|
7403
|
+
pythonTaskQueue: "none",
|
|
7404
|
+
pythonQuality: "none",
|
|
7405
|
+
goWebFramework: "none",
|
|
7406
|
+
goOrm: "none",
|
|
7407
|
+
goApi: "none",
|
|
7408
|
+
goCli: "none",
|
|
7409
|
+
goLogging: "none"
|
|
6669
7410
|
},
|
|
6670
7411
|
reproducibleCommand: "",
|
|
6671
7412
|
timeScaffolded,
|
|
@@ -6880,7 +7621,7 @@ const router = os.router({
|
|
|
6880
7621
|
yes: z.boolean().optional().default(false).describe("Use default configuration"),
|
|
6881
7622
|
yolo: z.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
|
|
6882
7623
|
verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
|
|
6883
|
-
ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript or
|
|
7624
|
+
ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript, rust, or python)"),
|
|
6884
7625
|
database: types_exports.DatabaseSchema.optional(),
|
|
6885
7626
|
orm: types_exports.ORMSchema.optional(),
|
|
6886
7627
|
auth: types_exports.AuthSchema.optional(),
|
|
@@ -6898,8 +7639,10 @@ const router = os.router({
|
|
|
6898
7639
|
animation: types_exports.AnimationSchema.optional(),
|
|
6899
7640
|
logging: types_exports.LoggingSchema.optional(),
|
|
6900
7641
|
observability: types_exports.ObservabilitySchema.optional(),
|
|
7642
|
+
analytics: types_exports.AnalyticsSchema.optional().describe("Privacy-focused analytics"),
|
|
6901
7643
|
cms: types_exports.CMSSchema.optional().describe("Headless CMS solution"),
|
|
6902
7644
|
caching: types_exports.CachingSchema.optional().describe("Caching solution"),
|
|
7645
|
+
fileStorage: types_exports.FileStorageSchema.optional().describe("File storage solution (S3, R2)"),
|
|
6903
7646
|
frontend: z.array(types_exports.FrontendSchema).optional(),
|
|
6904
7647
|
astroIntegration: types_exports.AstroIntegrationSchema.optional().describe("Astro UI framework integration (react, vue, svelte, solid)"),
|
|
6905
7648
|
addons: z.array(types_exports.AddonsSchema).optional(),
|
|
@@ -6924,7 +7667,18 @@ const router = os.router({
|
|
|
6924
7667
|
rustOrm: types_exports.RustOrmSchema.optional().describe("Rust ORM/database (sea-orm, sqlx)"),
|
|
6925
7668
|
rustApi: types_exports.RustApiSchema.optional().describe("Rust API layer (tonic, async-graphql)"),
|
|
6926
7669
|
rustCli: types_exports.RustCliSchema.optional().describe("Rust CLI tools (clap, ratatui)"),
|
|
6927
|
-
rustLibraries: z.array(types_exports.RustLibrariesSchema).optional().describe("Rust core libraries")
|
|
7670
|
+
rustLibraries: z.array(types_exports.RustLibrariesSchema).optional().describe("Rust core libraries"),
|
|
7671
|
+
pythonWebFramework: types_exports.PythonWebFrameworkSchema.optional().describe("Python web framework (fastapi, django)"),
|
|
7672
|
+
pythonOrm: types_exports.PythonOrmSchema.optional().describe("Python ORM/database (sqlalchemy, sqlmodel)"),
|
|
7673
|
+
pythonValidation: types_exports.PythonValidationSchema.optional().describe("Python validation (pydantic)"),
|
|
7674
|
+
pythonAi: z.array(types_exports.PythonAiSchema).optional().describe("Python AI/ML frameworks"),
|
|
7675
|
+
pythonTaskQueue: types_exports.PythonTaskQueueSchema.optional().describe("Python task queue (celery)"),
|
|
7676
|
+
pythonQuality: types_exports.PythonQualitySchema.optional().describe("Python code quality (ruff)"),
|
|
7677
|
+
goWebFramework: types_exports.GoWebFrameworkSchema.optional().describe("Go web framework (gin, echo)"),
|
|
7678
|
+
goOrm: types_exports.GoOrmSchema.optional().describe("Go ORM/database (gorm, sqlc)"),
|
|
7679
|
+
goApi: types_exports.GoApiSchema.optional().describe("Go API layer (grpc-go)"),
|
|
7680
|
+
goCli: types_exports.GoCliSchema.optional().describe("Go CLI tools (cobra, bubbletea)"),
|
|
7681
|
+
goLogging: types_exports.GoLoggingSchema.optional().describe("Go logging (zap)")
|
|
6928
7682
|
})])).handler(async ({ input }) => {
|
|
6929
7683
|
const [projectName, options] = input;
|
|
6930
7684
|
const result = await createProjectHandler({
|
|
@@ -7102,14 +7856,29 @@ async function createVirtual(options) {
|
|
|
7102
7856
|
animation: options.animation || "none",
|
|
7103
7857
|
logging: options.logging || "none",
|
|
7104
7858
|
observability: options.observability || "none",
|
|
7859
|
+
featureFlags: options.featureFlags || "none",
|
|
7860
|
+
analytics: options.analytics || "none",
|
|
7105
7861
|
cms: options.cms || "none",
|
|
7106
7862
|
caching: options.caching || "none",
|
|
7863
|
+
search: options.search || "none",
|
|
7864
|
+
fileStorage: options.fileStorage || "none",
|
|
7107
7865
|
rustWebFramework: options.rustWebFramework || "none",
|
|
7108
7866
|
rustFrontend: options.rustFrontend || "none",
|
|
7109
7867
|
rustOrm: options.rustOrm || "none",
|
|
7110
7868
|
rustApi: options.rustApi || "none",
|
|
7111
7869
|
rustCli: options.rustCli || "none",
|
|
7112
|
-
rustLibraries: options.rustLibraries || []
|
|
7870
|
+
rustLibraries: options.rustLibraries || [],
|
|
7871
|
+
pythonWebFramework: options.pythonWebFramework || "none",
|
|
7872
|
+
pythonOrm: options.pythonOrm || "none",
|
|
7873
|
+
pythonValidation: options.pythonValidation || "none",
|
|
7874
|
+
pythonAi: options.pythonAi || [],
|
|
7875
|
+
pythonTaskQueue: options.pythonTaskQueue || "none",
|
|
7876
|
+
pythonQuality: options.pythonQuality || "none",
|
|
7877
|
+
goWebFramework: options.goWebFramework || "none",
|
|
7878
|
+
goOrm: options.goOrm || "none",
|
|
7879
|
+
goApi: options.goApi || "none",
|
|
7880
|
+
goCli: options.goCli || "none",
|
|
7881
|
+
goLogging: options.goLogging || "none"
|
|
7113
7882
|
},
|
|
7114
7883
|
templates: EMBEDDED_TEMPLATES
|
|
7115
7884
|
});
|