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.
@@ -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-better-t-app",
215
- relativePath: "my-better-t-app",
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 (compatibility.frontends.some((f) => [
977
- "tanstack-router",
978
- "react-router",
979
- "tanstack-start",
980
- "next"
981
- ].includes(f))) return;
1037
+ if (supportsReact) return;
982
1038
  }
983
- if (!compatibility.frontends.includes("astro")) {
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.includes("astro");
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 supportsNextAuth = frontend?.includes("next") && backend === "self";
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 (supportsNextAuth) options.push({
1677
- value: "nextauth",
1678
- label: "Auth.js (NextAuth)",
1679
- hint: "Authentication for Next.js (formerly NextAuth.js)"
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") databaseOptions.push({
1893
- value: "mongodb",
1894
- label: "MongoDB",
1895
- hint: "open-source NoSQL database that stores data in JSON-like documents called BSON"
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
- value: "typescript",
2014
- label: "TypeScript",
2015
- hint: "Full-stack TypeScript with React, Vue, Svelte, and more"
2016
- }, {
2017
- value: "rust",
2018
- label: "Rust",
2019
- hint: "Rust ecosystem with Axum, Leptos, and more"
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
- value: "opentelemetry",
2558
- label: "OpenTelemetry",
2559
- hint: "Observability framework for traces, metrics, and logs"
2560
- }, {
2561
- value: "none",
2562
- label: "None",
2563
- hint: "Skip observability/tracing setup"
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 === "rust") return Promise.resolve([]);
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 === "rust") return Promise.resolve(void 0);
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve([]);
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 === "rust") return Promise.resolve([]);
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "rust") return Promise.resolve("none");
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 === "typescript") return Promise.resolve("none");
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 === "typescript") return Promise.resolve("none");
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 === "typescript") return Promise.resolve("none");
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 === "typescript") return Promise.resolve("none");
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 === "typescript") return Promise.resolve("none");
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 === "typescript") return Promise.resolve([]);
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$4(projectDir, database, projectName, backend);
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$4(projectDir, database, projectName, backend) {
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$3(projectDir, backend, config) {
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$3() {
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$3(projectDir, backend);
5399
- displayManualSetupInstructions$3();
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$3(projectDir, backend);
5419
- displayManualSetupInstructions$3();
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$3(projectDir, backend, config$1);
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$3(projectDir, backend);
5429
- displayManualSetupInstructions$3();
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$3(projectDir, backend);
5435
- displayManualSetupInstructions$3();
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$2(projectDir, backend, config) {
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$2(target) {
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$2(projectDir, backend);
5551
- displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
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$2(projectDir, backend);
5570
- displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
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$2(projectDir, backend, neonConfig);
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$2(projectDir, backend);
5611
- displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
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$1(projectDir, backend, config) {
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$1(target) {
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$1(projectDir, backend);
5771
- displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
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$1(projectDir, backend);
5790
- displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
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$1(projectDir, backend, prismaConfig);
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$1(projectDir, backend);
5800
- displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
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$1(projectDir, backend);
5806
- displayManualSetupInstructions$1(backend === "self" ? "apps/web" : "apps/server");
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 !== "rust") await installDependencies({
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 rust)"),
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
  });