create-better-fullstack 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as __reExport } from "./chunk-CCII7kTE.mjs";
3
- import { a as DEFAULT_CONFIG, c as getDefaultConfig, i as getLatestCLIVersion, l as getUserPkgManager, n as updateBtsConfig, o as DEFAULT_UI_LIBRARY_BY_FRONTEND, r as writeBtsConfig, t as readBtsConfig } from "./bts-config-B_rZ4_sj.mjs";
4
- import { _ as setIsFirstPrompt$1, a as canPromptInteractively, c as CLIError, d as exitWithError, f as handleError, g as runWithContextAsync, h as isSilent, l as UserCancelledError, m as isFirstPrompt, o as getPackageExecutionArgs, p as didLastPromptShowUI, s as addPackageDependency, t as setupAddons, u as exitCancelled, v as setLastPromptShownUI } from "./addons-setup-DQa6TRrx.mjs";
3
+ import { a as DEFAULT_CONFIG, c as getDefaultConfig, i as getLatestCLIVersion, l as getUserPkgManager, n as updateBtsConfig, o as DEFAULT_UI_LIBRARY_BY_FRONTEND, r as writeBtsConfig, t as readBtsConfig } from "./bts-config-YcroedMK.mjs";
4
+ import { _ as setIsFirstPrompt$1, a as canPromptInteractively, c as CLIError, d as exitWithError, f as handleError, g as runWithContextAsync, h as isSilent, l as UserCancelledError, m as isFirstPrompt, o as getPackageExecutionArgs, p as didLastPromptShowUI, s as addPackageDependency, t as setupAddons, u as exitCancelled, v as setLastPromptShownUI } from "./addons-setup-CUmA_nra.mjs";
5
5
  import { cancel, confirm, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
6
6
  import { createRouterClient, os } from "@orpc/server";
7
7
  import pc from "picocolors";
@@ -363,7 +363,7 @@ const CreateCommandOptionsSchema = z.object({
363
363
  yolo: z.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
364
364
  verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
365
365
  dryRun: z.boolean().optional().default(false).describe("Preview generated file tree without writing to disk"),
366
- ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript, rust, python, go, or java)"),
366
+ ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript, react-native, rust, python, go, java, or elixir)"),
367
367
  database: types_exports.DatabaseSchema.optional(),
368
368
  orm: types_exports.ORMSchema.optional(),
369
369
  auth: types_exports.AuthSchema.optional(),
@@ -388,6 +388,13 @@ const CreateCommandOptionsSchema = z.object({
388
388
  i18n: types_exports.I18nSchema.optional().describe("Internationalization (i18n) library"),
389
389
  search: types_exports.SearchSchema.optional().describe("Search engine solution"),
390
390
  fileStorage: types_exports.FileStorageSchema.optional().describe("File storage solution (S3, R2)"),
391
+ mobileNavigation: types_exports.MobileNavigationSchema.optional().describe("Mobile navigation (expo-router, react-navigation)"),
392
+ mobileUI: types_exports.MobileUISchema.optional().describe("Mobile UI (tamagui, gluestack-ui, uniwind, unistyles)"),
393
+ mobileStorage: types_exports.MobileStorageSchema.optional().describe("Mobile storage (mmkv)"),
394
+ mobileTesting: types_exports.MobileTestingSchema.optional().describe("Mobile testing (maestro, react-native-testing-library)"),
395
+ mobilePush: types_exports.MobilePushSchema.optional().describe("Mobile push notifications (expo-notifications)"),
396
+ mobileOTA: types_exports.MobileOTASchema.optional().describe("Mobile OTA updates (expo-updates)"),
397
+ mobileDeepLinking: types_exports.MobileDeepLinkingSchema.optional().describe("Mobile deep linking (expo-linking)"),
391
398
  frontend: z.array(types_exports.FrontendSchema).optional(),
392
399
  astroIntegration: types_exports.AstroIntegrationSchema.optional().describe("Astro UI framework integration (react, vue, svelte, solid)"),
393
400
  addons: z.array(types_exports.AddonsSchema).optional(),
@@ -430,21 +437,37 @@ const CreateCommandOptionsSchema = z.object({
430
437
  pythonValidation: types_exports.PythonValidationSchema.optional().describe("Python validation (pydantic)"),
431
438
  pythonAi: z.array(types_exports.PythonAiSchema).optional().describe("Python AI/ML frameworks"),
432
439
  pythonAuth: types_exports.PythonAuthSchema.optional().describe("Python auth library (authlib, jwt)"),
440
+ pythonApi: types_exports.PythonApiSchema.optional().describe("Python API framework (django-rest-framework, django-ninja)"),
433
441
  pythonTaskQueue: types_exports.PythonTaskQueueSchema.optional().describe("Python task queue (celery)"),
434
442
  pythonGraphql: types_exports.PythonGraphqlSchema.optional().describe("Python GraphQL framework (strawberry)"),
435
- pythonQuality: types_exports.PythonQualitySchema.optional().describe("Python code quality (ruff)"),
443
+ pythonQuality: types_exports.PythonQualitySchema.optional().describe("Python code quality (ruff, mypy, pyright)"),
436
444
  goWebFramework: types_exports.GoWebFrameworkSchema.optional().describe("Go web framework (gin, echo, fiber)"),
437
445
  goOrm: types_exports.GoOrmSchema.optional().describe("Go ORM/database (gorm, sqlc)"),
438
446
  goApi: types_exports.GoApiSchema.optional().describe("Go API layer (grpc-go)"),
439
- goCli: types_exports.GoCliSchema.optional().describe("Go CLI tools (cobra, bubbletea)"),
447
+ goCli: types_exports.GoCliSchema.optional().describe("Go CLI tools (cobra, bubbletea, urfave-cli)"),
440
448
  goLogging: types_exports.GoLoggingSchema.optional().describe("Go logging (zap, zerolog, slog)"),
441
449
  goAuth: types_exports.GoAuthSchema.optional().describe("Go auth (casbin, jwt)"),
442
- javaWebFramework: types_exports.JavaWebFrameworkSchema.optional().describe("Java web framework (spring-boot, none)"),
450
+ javaWebFramework: types_exports.JavaWebFrameworkSchema.optional().describe("Java web framework (spring-boot, quarkus, none)"),
443
451
  javaBuildTool: types_exports.JavaBuildToolSchema.optional().describe("Java build tool (maven, gradle, none)"),
444
452
  javaOrm: types_exports.JavaOrmSchema.optional().describe("Java ORM/database (spring-data-jpa)"),
445
453
  javaAuth: types_exports.JavaAuthSchema.optional().describe("Java auth (spring-security)"),
446
454
  javaLibraries: z.array(types_exports.JavaLibrariesSchema).optional().describe("Java application libraries"),
447
455
  javaTestingLibraries: z.array(types_exports.JavaTestingLibrariesSchema).optional().describe("Java testing libraries"),
456
+ elixirWebFramework: types_exports.ElixirWebFrameworkSchema.optional().describe("Elixir web framework (phoenix, phoenix-live-view, none)"),
457
+ elixirOrm: types_exports.ElixirOrmSchema.optional().describe("Elixir ORM/database (ecto, ecto-sql, none)"),
458
+ elixirAuth: types_exports.ElixirAuthSchema.optional().describe("Elixir auth (phx-gen-auth, ueberauth, guardian, none)"),
459
+ elixirApi: types_exports.ElixirApiSchema.optional().describe("Elixir API layer (rest, absinthe, none)"),
460
+ elixirRealtime: types_exports.ElixirRealtimeSchema.optional().describe("Elixir realtime (channels, presence, pubsub, live-view-streams, none)"),
461
+ elixirJobs: types_exports.ElixirJobsSchema.optional().describe("Elixir jobs (oban, quantum, none)"),
462
+ elixirValidation: types_exports.ElixirValidationSchema.optional().describe("Elixir validation (ecto-changesets, nimble-options, none)"),
463
+ elixirHttp: types_exports.ElixirHttpSchema.optional().describe("Elixir HTTP client (req, finch, none)"),
464
+ elixirJson: types_exports.ElixirJsonSchema.optional().describe("Elixir JSON library (jason, none)"),
465
+ elixirEmail: types_exports.ElixirEmailSchema.optional().describe("Elixir email library (swoosh, none)"),
466
+ elixirCaching: types_exports.ElixirCachingSchema.optional().describe("Elixir caching (cachex, nebulex, none)"),
467
+ elixirObservability: types_exports.ElixirObservabilitySchema.optional().describe("Elixir observability (telemetry, opentelemetry, prom_ex, none)"),
468
+ elixirTesting: types_exports.ElixirTestingSchema.optional().describe("Elixir testing (ex_unit, mox, bypass, wallaby, none)"),
469
+ elixirQuality: types_exports.ElixirQualitySchema.optional().describe("Elixir code quality (credo, dialyxir, sobelow, none)"),
470
+ elixirDeploy: types_exports.ElixirDeploySchema.optional().describe("Elixir deploy target (docker, fly, gigalixir, mix-release, none)"),
448
471
  aiDocs: z.array(types_exports.AiDocsSchema).optional().describe("AI documentation files (claude-md, agents-md, cursorrules)")
449
472
  });
450
473
  const CreateCommandInputSchema = z.tuple([types_exports.ProjectNameSchema.optional(), CreateCommandOptionsSchema]);
@@ -533,6 +556,7 @@ function ensureSingleWebAndNative(frontends) {
533
556
  }
534
557
  const FULLSTACK_FRONTENDS$1 = [
535
558
  "next",
559
+ "vinext",
536
560
  "tanstack-start",
537
561
  "astro",
538
562
  "nuxt",
@@ -544,11 +568,11 @@ function validateSelfBackendCompatibility(providedFlags, options, config) {
544
568
  const frontends = config.frontend || options.frontend || [];
545
569
  if (backend === "self") {
546
570
  const { web, native } = splitFrontends$1(frontends);
547
- if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) exitWithError("Backend 'self' (fullstack) only supports Next.js, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, or --frontend solid-start.");
571
+ if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) exitWithError("Backend 'self' (fullstack) only supports Next.js, Vinext, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend vinext, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, or --frontend solid-start.");
548
572
  if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-bare, native-uniwind, native-unistyles");
549
573
  }
550
574
  const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
551
- if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) only supports Next.js, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, --frontend solid-start, or choose a different backend.");
575
+ if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) only supports Next.js, Vinext, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend vinext, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, --frontend solid-start, or choose a different backend.");
552
576
  }
553
577
  const WORKERS_COMPATIBLE_BACKENDS = [
554
578
  "hono",
@@ -646,11 +670,11 @@ function validateAddonCompatibility$1(addon, frontend, _auth, backend, runtime,
646
670
  };
647
671
  if (ecosystem === "typescript" && !hasDockerComposeCompatibleFrontend(frontend)) return {
648
672
  isCompatible: false,
649
- reason: "Docker Compose currently supports Next.js, TanStack Router, React Router, React Vite, Solid, or Astro"
673
+ reason: "Docker Compose currently supports Next.js, Vinext, TanStack Router, React Router, React Vite, Solid, or Astro"
650
674
  };
651
- if (ecosystem === "typescript" && backend === "self" && !frontend.includes("next")) return {
675
+ if (ecosystem === "typescript" && backend === "self" && !frontend.includes("next") && !frontend.includes("vinext")) return {
652
676
  isCompatible: false,
653
- reason: "Docker Compose self-backend support currently requires Next.js"
677
+ reason: "Docker Compose self-backend support currently requires Next.js or Vinext"
654
678
  };
655
679
  if (ecosystem === "rust" && rustFrontend && rustFrontend !== "none") return {
656
680
  isCompatible: false,
@@ -1512,6 +1536,24 @@ async function runGradleTests({ projectDir }) {
1512
1536
  if (error instanceof Error) consola.error(pc.red(`Gradle test error: ${error.message}`));
1513
1537
  }
1514
1538
  }
1539
+ async function runMixCompile({ projectDir }) {
1540
+ const s = spinner();
1541
+ try {
1542
+ s.start("Running mix deps.get and mix compile...");
1543
+ await $({
1544
+ cwd: projectDir,
1545
+ stderr: "inherit"
1546
+ })`mix deps.get`;
1547
+ await $({
1548
+ cwd: projectDir,
1549
+ stderr: "inherit"
1550
+ })`mix compile`;
1551
+ s.stop("Elixir dependencies installed and project compiled");
1552
+ } catch (error) {
1553
+ s.stop(pc.red("mix compile failed"));
1554
+ if (error instanceof Error) consola.error(pc.red(`Mix error: ${error.message}`));
1555
+ }
1556
+ }
1515
1557
 
1516
1558
  //#endregion
1517
1559
  //#region src/helpers/core/add-handler.ts
@@ -1816,6 +1858,7 @@ function resolveAnimationPrompt(context = {}) {
1816
1858
  "react-vite",
1817
1859
  "tanstack-start",
1818
1860
  "next",
1861
+ "vinext",
1819
1862
  "redwood"
1820
1863
  ].includes(f));
1821
1864
  const isFresh = web.includes("fresh");
@@ -2053,6 +2096,7 @@ async function getAuthChoice(auth, backend, frontend, ecosystem = "typescript")
2053
2096
  //#region src/prompts/backend.ts
2054
2097
  const FULLSTACK_FRONTENDS = [
2055
2098
  "next",
2099
+ "vinext",
2056
2100
  "tanstack-start",
2057
2101
  "astro",
2058
2102
  "nuxt",
@@ -2178,6 +2222,23 @@ const CACHING_PROMPT_OPTIONS = [{
2178
2222
  hint: "Skip caching layer setup"
2179
2223
  }];
2180
2224
  function resolveCachingPrompt(context = {}) {
2225
+ if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
2226
+ shouldPrompt: false,
2227
+ mode: "single",
2228
+ options: [],
2229
+ autoValue: "none"
2230
+ };
2231
+ if (context.ecosystem && context.ecosystem !== "typescript") return context.caching !== void 0 ? {
2232
+ shouldPrompt: false,
2233
+ mode: "single",
2234
+ options: CACHING_PROMPT_OPTIONS,
2235
+ autoValue: context.caching
2236
+ } : {
2237
+ shouldPrompt: true,
2238
+ mode: "single",
2239
+ options: CACHING_PROMPT_OPTIONS,
2240
+ initialValue: "none"
2241
+ };
2181
2242
  if (context.backend === "none" || context.backend === "convex") return {
2182
2243
  shouldPrompt: false,
2183
2244
  mode: "single",
@@ -2196,10 +2257,11 @@ function resolveCachingPrompt(context = {}) {
2196
2257
  initialValue: "none"
2197
2258
  };
2198
2259
  }
2199
- async function getCachingChoice(caching, backend) {
2260
+ async function getCachingChoice(caching, backend, ecosystem) {
2200
2261
  const resolution = resolveCachingPrompt({
2201
2262
  caching,
2202
- backend
2263
+ backend,
2264
+ ecosystem
2203
2265
  });
2204
2266
  if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2205
2267
  const response = await navigableSelect({
@@ -2566,6 +2628,317 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
2566
2628
  return response;
2567
2629
  }
2568
2630
 
2631
+ //#endregion
2632
+ //#region src/prompts/elixir-ecosystem.ts
2633
+ function makeChoice(message, options, defaultValue, value) {
2634
+ const resolution = createStaticSinglePromptResolution(options, defaultValue, value);
2635
+ if (!resolution.shouldPrompt) return Promise.resolve(resolution.autoValue ?? defaultValue);
2636
+ return navigableSelect({
2637
+ message,
2638
+ options: resolution.options,
2639
+ initialValue: resolution.initialValue
2640
+ }).then((response) => isCancel$1(response) ? exitCancelled("Operation cancelled") : response);
2641
+ }
2642
+ const WEB_FRAMEWORK_OPTIONS = [
2643
+ {
2644
+ value: "phoenix",
2645
+ label: "Phoenix",
2646
+ hint: "Conventional Phoenix web application"
2647
+ },
2648
+ {
2649
+ value: "phoenix-live-view",
2650
+ label: "Phoenix LiveView",
2651
+ hint: "Server-rendered realtime UI"
2652
+ },
2653
+ {
2654
+ value: "none",
2655
+ label: "None",
2656
+ hint: "No Elixir web framework"
2657
+ }
2658
+ ];
2659
+ const ORM_OPTIONS = [
2660
+ {
2661
+ value: "ecto-sql",
2662
+ label: "Ecto SQL",
2663
+ hint: "Ecto plus SQL adapters and migrations"
2664
+ },
2665
+ {
2666
+ value: "ecto",
2667
+ label: "Ecto",
2668
+ hint: "Ecto schemas and changesets without SQL repo wiring"
2669
+ },
2670
+ {
2671
+ value: "none",
2672
+ label: "None",
2673
+ hint: "No database layer"
2674
+ }
2675
+ ];
2676
+ const AUTH_OPTIONS = [
2677
+ {
2678
+ value: "phx-gen-auth",
2679
+ label: "phx.gen.auth",
2680
+ hint: "Phoenix account/session scaffold"
2681
+ },
2682
+ {
2683
+ value: "ueberauth",
2684
+ label: "Ueberauth",
2685
+ hint: "OAuth strategy foundation"
2686
+ },
2687
+ {
2688
+ value: "guardian",
2689
+ label: "Guardian",
2690
+ hint: "JWT authentication foundation"
2691
+ },
2692
+ {
2693
+ value: "none",
2694
+ label: "None",
2695
+ hint: "No auth layer"
2696
+ }
2697
+ ];
2698
+ const API_OPTIONS = [
2699
+ {
2700
+ value: "rest",
2701
+ label: "Phoenix REST",
2702
+ hint: "Controllers and JSON endpoints"
2703
+ },
2704
+ {
2705
+ value: "absinthe",
2706
+ label: "Absinthe GraphQL",
2707
+ hint: "GraphQL schema and resolvers"
2708
+ },
2709
+ {
2710
+ value: "none",
2711
+ label: "None",
2712
+ hint: "No API layer"
2713
+ }
2714
+ ];
2715
+ const REALTIME_OPTIONS = [
2716
+ {
2717
+ value: "channels",
2718
+ label: "Phoenix Channels",
2719
+ hint: "WebSocket channel endpoint"
2720
+ },
2721
+ {
2722
+ value: "presence",
2723
+ label: "Phoenix Presence",
2724
+ hint: "Presence tracking over PubSub"
2725
+ },
2726
+ {
2727
+ value: "pubsub",
2728
+ label: "Phoenix PubSub",
2729
+ hint: "PubSub foundation only"
2730
+ },
2731
+ {
2732
+ value: "live-view-streams",
2733
+ label: "LiveView Streams",
2734
+ hint: "Realtime LiveView stream demo"
2735
+ },
2736
+ {
2737
+ value: "none",
2738
+ label: "None",
2739
+ hint: "No realtime feature"
2740
+ }
2741
+ ];
2742
+ const JOB_OPTIONS = [
2743
+ {
2744
+ value: "oban",
2745
+ label: "Oban",
2746
+ hint: "PostgreSQL-backed jobs and workers"
2747
+ },
2748
+ {
2749
+ value: "quantum",
2750
+ label: "Quantum",
2751
+ hint: "Cron-like scheduler"
2752
+ },
2753
+ {
2754
+ value: "none",
2755
+ label: "None",
2756
+ hint: "No jobs layer"
2757
+ }
2758
+ ];
2759
+ const VALIDATION_OPTIONS = [
2760
+ {
2761
+ value: "ecto-changesets",
2762
+ label: "Ecto Changesets",
2763
+ hint: "Data validation with Ecto"
2764
+ },
2765
+ {
2766
+ value: "nimble-options",
2767
+ label: "NimbleOptions",
2768
+ hint: "Declarative option validation"
2769
+ },
2770
+ {
2771
+ value: "none",
2772
+ label: "None",
2773
+ hint: "No extra validation helper"
2774
+ }
2775
+ ];
2776
+ const HTTP_OPTIONS = [
2777
+ {
2778
+ value: "req",
2779
+ label: "Req",
2780
+ hint: "High-level HTTP client"
2781
+ },
2782
+ {
2783
+ value: "finch",
2784
+ label: "Finch",
2785
+ hint: "Pooled HTTP client"
2786
+ },
2787
+ {
2788
+ value: "none",
2789
+ label: "None",
2790
+ hint: "No HTTP client"
2791
+ }
2792
+ ];
2793
+ const JSON_OPTIONS = [{
2794
+ value: "jason",
2795
+ label: "Jason",
2796
+ hint: "Phoenix default JSON library"
2797
+ }, {
2798
+ value: "none",
2799
+ label: "None",
2800
+ hint: "No JSON library"
2801
+ }];
2802
+ const EMAIL_OPTIONS = [{
2803
+ value: "swoosh",
2804
+ label: "Swoosh",
2805
+ hint: "Phoenix email library"
2806
+ }, {
2807
+ value: "none",
2808
+ label: "None",
2809
+ hint: "No email library"
2810
+ }];
2811
+ const CACHING_OPTIONS = [
2812
+ {
2813
+ value: "cachex",
2814
+ label: "Cachex",
2815
+ hint: "In-memory cache"
2816
+ },
2817
+ {
2818
+ value: "nebulex",
2819
+ label: "Nebulex",
2820
+ hint: "Cache abstraction"
2821
+ },
2822
+ {
2823
+ value: "none",
2824
+ label: "None",
2825
+ hint: "No cache layer"
2826
+ }
2827
+ ];
2828
+ const OBSERVABILITY_OPTIONS = [
2829
+ {
2830
+ value: "telemetry",
2831
+ label: "Telemetry",
2832
+ hint: "Phoenix telemetry metrics"
2833
+ },
2834
+ {
2835
+ value: "opentelemetry",
2836
+ label: "OpenTelemetry",
2837
+ hint: "Distributed tracing foundation"
2838
+ },
2839
+ {
2840
+ value: "prom_ex",
2841
+ label: "PromEx",
2842
+ hint: "Prometheus metrics for Phoenix"
2843
+ },
2844
+ {
2845
+ value: "none",
2846
+ label: "None",
2847
+ hint: "No observability add-on"
2848
+ }
2849
+ ];
2850
+ const TESTING_OPTIONS = [
2851
+ {
2852
+ value: "ex_unit",
2853
+ label: "ExUnit",
2854
+ hint: "Standard Elixir tests"
2855
+ },
2856
+ {
2857
+ value: "mox",
2858
+ label: "Mox",
2859
+ hint: "Concurrent-safe mocks"
2860
+ },
2861
+ {
2862
+ value: "bypass",
2863
+ label: "Bypass",
2864
+ hint: "External HTTP service fakes"
2865
+ },
2866
+ {
2867
+ value: "wallaby",
2868
+ label: "Wallaby",
2869
+ hint: "Browser acceptance testing"
2870
+ },
2871
+ {
2872
+ value: "none",
2873
+ label: "None",
2874
+ hint: "No extra test library"
2875
+ }
2876
+ ];
2877
+ const QUALITY_OPTIONS = [
2878
+ {
2879
+ value: "credo",
2880
+ label: "Credo",
2881
+ hint: "Static code analysis"
2882
+ },
2883
+ {
2884
+ value: "dialyxir",
2885
+ label: "Dialyxir",
2886
+ hint: "Dialyzer integration"
2887
+ },
2888
+ {
2889
+ value: "sobelow",
2890
+ label: "Sobelow",
2891
+ hint: "Phoenix security analysis"
2892
+ },
2893
+ {
2894
+ value: "none",
2895
+ label: "None",
2896
+ hint: "No code quality tool"
2897
+ }
2898
+ ];
2899
+ const DEPLOY_OPTIONS = [
2900
+ {
2901
+ value: "docker",
2902
+ label: "Docker",
2903
+ hint: "Dockerfile for Phoenix releases"
2904
+ },
2905
+ {
2906
+ value: "fly",
2907
+ label: "Fly.io",
2908
+ hint: "Fly.io release config"
2909
+ },
2910
+ {
2911
+ value: "gigalixir",
2912
+ label: "Gigalixir",
2913
+ hint: "Gigalixir Procfile and notes"
2914
+ },
2915
+ {
2916
+ value: "mix-release",
2917
+ label: "Mix Release",
2918
+ hint: "Release-ready runtime config"
2919
+ },
2920
+ {
2921
+ value: "none",
2922
+ label: "None",
2923
+ hint: "No deploy files"
2924
+ }
2925
+ ];
2926
+ const getElixirWebFrameworkChoice = (value) => makeChoice("Select Elixir web framework", WEB_FRAMEWORK_OPTIONS, "phoenix", value);
2927
+ const getElixirOrmChoice = (value) => makeChoice("Select Elixir database layer", ORM_OPTIONS, "ecto-sql", value);
2928
+ const getElixirAuthChoice = (value) => makeChoice("Select Elixir auth", AUTH_OPTIONS, "none", value);
2929
+ const getElixirApiChoice = (value) => makeChoice("Select Elixir API layer", API_OPTIONS, "rest", value);
2930
+ const getElixirRealtimeChoice = (value) => makeChoice("Select Elixir realtime feature", REALTIME_OPTIONS, "channels", value);
2931
+ const getElixirJobsChoice = (value) => makeChoice("Select Elixir jobs layer", JOB_OPTIONS, "none", value);
2932
+ const getElixirValidationChoice = (value) => makeChoice("Select Elixir validation", VALIDATION_OPTIONS, "ecto-changesets", value);
2933
+ const getElixirHttpChoice = (value) => makeChoice("Select Elixir HTTP client", HTTP_OPTIONS, "req", value);
2934
+ const getElixirJsonChoice = (value) => makeChoice("Select Elixir JSON library", JSON_OPTIONS, "jason", value);
2935
+ const getElixirEmailChoice = (value) => makeChoice("Select Elixir email library", EMAIL_OPTIONS, "none", value);
2936
+ const getElixirCachingChoice = (value) => makeChoice("Select Elixir caching", CACHING_OPTIONS, "none", value);
2937
+ const getElixirObservabilityChoice = (value) => makeChoice("Select Elixir observability", OBSERVABILITY_OPTIONS, "telemetry", value);
2938
+ const getElixirTestingChoice = (value) => makeChoice("Select Elixir testing", TESTING_OPTIONS, "ex_unit", value);
2939
+ const getElixirQualityChoice = (value) => makeChoice("Select Elixir code quality", QUALITY_OPTIONS, "credo", value);
2940
+ const getElixirDeployChoice = (value) => makeChoice("Select Elixir deploy target", DEPLOY_OPTIONS, "none", value);
2941
+
2569
2942
  //#endregion
2570
2943
  //#region src/prompts/ecosystem.ts
2571
2944
  async function getEcosystemChoice(ecosystem) {
@@ -2576,7 +2949,12 @@ async function getEcosystemChoice(ecosystem) {
2576
2949
  {
2577
2950
  value: "typescript",
2578
2951
  label: "TypeScript",
2579
- hint: "Full-stack TypeScript with React, Vue, Svelte, and more"
2952
+ hint: "Full-stack TypeScript web with React, Vue, Svelte, and more"
2953
+ },
2954
+ {
2955
+ value: "react-native",
2956
+ label: "React Native",
2957
+ hint: "Expo and React Native mobile apps with native integrations"
2580
2958
  },
2581
2959
  {
2582
2960
  value: "rust",
@@ -2592,6 +2970,11 @@ async function getEcosystemChoice(ecosystem) {
2592
2970
  value: "go",
2593
2971
  label: "Go",
2594
2972
  hint: "Go ecosystem with Gin, Echo, GORM, and more"
2973
+ },
2974
+ {
2975
+ value: "java",
2976
+ label: "Java",
2977
+ hint: "Java ecosystem with Spring Boot, Maven, Gradle, and more"
2595
2978
  }
2596
2979
  ],
2597
2980
  initialValue: "typescript"
@@ -2678,8 +3061,16 @@ const EMAIL_PROMPT_OPTIONS = [
2678
3061
  hint: "No email integration"
2679
3062
  }
2680
3063
  ];
3064
+ const NON_TYPESCRIPT_EMAIL_PROMPT_OPTIONS = EMAIL_PROMPT_OPTIONS.filter((option) => option.value === "resend" || option.value === "none");
2681
3065
  function resolveEmailPrompt(context = {}) {
2682
- if (context.backend === "none" || context.backend === "convex") return {
3066
+ if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
3067
+ shouldPrompt: false,
3068
+ mode: "single",
3069
+ options: [],
3070
+ autoValue: "none"
3071
+ };
3072
+ const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_EMAIL_PROMPT_OPTIONS : EMAIL_PROMPT_OPTIONS;
3073
+ if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
2683
3074
  shouldPrompt: false,
2684
3075
  mode: "single",
2685
3076
  options: [],
@@ -2688,19 +3079,20 @@ function resolveEmailPrompt(context = {}) {
2688
3079
  return context.email !== void 0 ? {
2689
3080
  shouldPrompt: false,
2690
3081
  mode: "single",
2691
- options: EMAIL_PROMPT_OPTIONS,
3082
+ options,
2692
3083
  autoValue: context.email
2693
3084
  } : {
2694
3085
  shouldPrompt: true,
2695
3086
  mode: "single",
2696
- options: EMAIL_PROMPT_OPTIONS,
3087
+ options,
2697
3088
  initialValue: DEFAULT_CONFIG.email ?? "none"
2698
3089
  };
2699
3090
  }
2700
- async function getEmailChoice(email, backend) {
3091
+ async function getEmailChoice(email, backend, ecosystem) {
2701
3092
  const resolution = resolveEmailPrompt({
2702
3093
  email,
2703
- backend
3094
+ backend,
3095
+ ecosystem
2704
3096
  });
2705
3097
  if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2706
3098
  const response = await navigableSelect({
@@ -2850,6 +3242,7 @@ function resolveFormsPrompt(context = {}) {
2850
3242
  "react-vite",
2851
3243
  "tanstack-start",
2852
3244
  "next",
3245
+ "vinext",
2853
3246
  "redwood"
2854
3247
  ].includes(f));
2855
3248
  const isSolid = web.includes("solid");
@@ -2933,6 +3326,11 @@ const WEB_FRONTEND_PROMPT_OPTIONS = [
2933
3326
  label: "Next.js",
2934
3327
  hint: "The React Framework for the Web"
2935
3328
  },
3329
+ {
3330
+ value: "vinext",
3331
+ label: "Vinext",
3332
+ hint: "The Vite Compiler for Next.js"
3333
+ },
2936
3334
  {
2937
3335
  value: "nuxt",
2938
3336
  label: "Nuxt",
@@ -3085,6 +3483,17 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
3085
3483
  return result;
3086
3484
  }
3087
3485
  }
3486
+ async function getNativeFrontendChoice(frontendOptions) {
3487
+ if (frontendOptions !== void 0) return frontendOptions.filter((frontend) => frontend.startsWith("native-"));
3488
+ const nativeFramework = await navigableSelect({
3489
+ message: "Choose React Native app type",
3490
+ options: NATIVE_FRONTEND_PROMPT_OPTIONS,
3491
+ initialValue: "native-bare"
3492
+ });
3493
+ if (isGoBack(nativeFramework)) return GO_BACK_SYMBOL;
3494
+ if (isCancel$1(nativeFramework)) return exitCancelled("Operation cancelled");
3495
+ return [nativeFramework];
3496
+ }
3088
3497
 
3089
3498
  //#endregion
3090
3499
  //#region src/prompts/git.ts
@@ -3169,6 +3578,11 @@ const GO_CLI_PROMPT_OPTIONS = [
3169
3578
  label: "Bubble Tea",
3170
3579
  hint: "Powerful TUI framework based on The Elm Architecture"
3171
3580
  },
3581
+ {
3582
+ value: "urfave-cli",
3583
+ label: "urfave/cli",
3584
+ hint: "Declarative CLI framework with commands, flags, and shell completion"
3585
+ },
3172
3586
  {
3173
3587
  value: "none",
3174
3588
  label: "None",
@@ -3191,6 +3605,11 @@ const GO_LOGGING_PROMPT_OPTIONS = [
3191
3605
  label: "slog",
3192
3606
  hint: "Go 1.21+ stdlib structured logging (no external dependency)"
3193
3607
  },
3608
+ {
3609
+ value: "logrus",
3610
+ label: "Logrus",
3611
+ hint: "Classic structured logger with hooks and formatter ecosystem"
3612
+ },
3194
3613
  {
3195
3614
  value: "none",
3196
3615
  label: "None",
@@ -3395,6 +3814,18 @@ async function getinstallChoice(install, ecosystem, javaBuildTool) {
3395
3814
  if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
3396
3815
  return response$1;
3397
3816
  }
3817
+ if (ecosystem === "elixir") {
3818
+ if (!await commandExists("mix")) {
3819
+ log.warn("Mix is not installed. Please install Elixir from https://elixir-lang.org/install.html");
3820
+ return false;
3821
+ }
3822
+ const response$1 = await navigableConfirm({
3823
+ message: "Run mix deps.get and mix compile?",
3824
+ initialValue: DEFAULT_CONFIG.install
3825
+ });
3826
+ if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
3827
+ return response$1;
3828
+ }
3398
3829
  const response = await navigableConfirm({
3399
3830
  message: "Install dependencies?",
3400
3831
  initialValue: DEFAULT_CONFIG.install
@@ -3405,15 +3836,23 @@ async function getinstallChoice(install, ecosystem, javaBuildTool) {
3405
3836
 
3406
3837
  //#endregion
3407
3838
  //#region src/prompts/java-ecosystem.ts
3408
- const JAVA_WEB_FRAMEWORK_PROMPT_OPTIONS = [{
3409
- value: "spring-boot",
3410
- label: "Spring Boot",
3411
- hint: "Production-grade Java framework with embedded server and auto-configuration"
3412
- }, {
3413
- value: "none",
3414
- label: "None",
3415
- hint: "No Java web framework"
3416
- }];
3839
+ const JAVA_WEB_FRAMEWORK_PROMPT_OPTIONS = [
3840
+ {
3841
+ value: "spring-boot",
3842
+ label: "Spring Boot",
3843
+ hint: "Production-grade Java framework with embedded server and auto-configuration"
3844
+ },
3845
+ {
3846
+ value: "quarkus",
3847
+ label: "Quarkus",
3848
+ hint: "Cloud-native Java framework optimized for fast startup and lower memory use"
3849
+ },
3850
+ {
3851
+ value: "none",
3852
+ label: "None",
3853
+ hint: "No Java web framework"
3854
+ }
3855
+ ];
3417
3856
  const JAVA_BUILD_TOOL_PROMPT_OPTIONS = [
3418
3857
  {
3419
3858
  value: "maven",
@@ -3542,6 +3981,46 @@ const JAVA_LIBRARY_PROMPT_OPTIONS = [
3542
3981
  label: "Caffeine",
3543
3982
  hint: "High-performance in-memory caching through Spring Cache"
3544
3983
  },
3984
+ {
3985
+ value: "resilience4j",
3986
+ label: "Resilience4j",
3987
+ hint: "Fault tolerance patterns for retries, circuit breakers, and rate limiting"
3988
+ },
3989
+ {
3990
+ value: "spring-webflux",
3991
+ label: "Spring WebFlux",
3992
+ hint: "Reactive HTTP stack for Spring applications"
3993
+ },
3994
+ {
3995
+ value: "spring-batch",
3996
+ label: "Spring Batch",
3997
+ hint: "Batch processing framework for ETL and scheduled jobs"
3998
+ },
3999
+ {
4000
+ value: "spring-kafka",
4001
+ label: "Spring for Apache Kafka",
4002
+ hint: "Kafka producer, consumer, and listener integration"
4003
+ },
4004
+ {
4005
+ value: "spring-mail",
4006
+ label: "Spring Mail",
4007
+ hint: "Email support through Jakarta Mail and Spring abstractions"
4008
+ },
4009
+ {
4010
+ value: "spring-devtools",
4011
+ label: "Spring Boot DevTools",
4012
+ hint: "Developer-time restart and local productivity support"
4013
+ },
4014
+ {
4015
+ value: "micrometer-prometheus",
4016
+ label: "Micrometer Prometheus",
4017
+ hint: "Prometheus metrics registry for Micrometer and Actuator"
4018
+ },
4019
+ {
4020
+ value: "thymeleaf",
4021
+ label: "Thymeleaf",
4022
+ hint: "Server-rendered HTML templates for Spring MVC apps"
4023
+ },
3545
4024
  {
3546
4025
  value: "none",
3547
4026
  label: "None",
@@ -3753,6 +4232,143 @@ async function getLoggingChoice(logging, backend) {
3753
4232
  return response;
3754
4233
  }
3755
4234
 
4235
+ //#endregion
4236
+ //#region src/prompts/mobile.ts
4237
+ const MOBILE_NAVIGATION_OPTIONS = [
4238
+ {
4239
+ value: "expo-router",
4240
+ label: "Expo Router",
4241
+ hint: "File-based routing for Expo apps"
4242
+ },
4243
+ {
4244
+ value: "react-navigation",
4245
+ label: "React Navigation",
4246
+ hint: "Code-defined native stacks and tabs"
4247
+ },
4248
+ {
4249
+ value: "none",
4250
+ label: "None",
4251
+ hint: "Skip navigation setup"
4252
+ }
4253
+ ];
4254
+ const MOBILE_UI_OPTIONS = [
4255
+ {
4256
+ value: "none",
4257
+ label: "None",
4258
+ hint: "Use React Native primitives"
4259
+ },
4260
+ {
4261
+ value: "tamagui",
4262
+ label: "Tamagui",
4263
+ hint: "Universal themed UI primitives"
4264
+ },
4265
+ {
4266
+ value: "gluestack-ui",
4267
+ label: "Gluestack UI",
4268
+ hint: "Accessible cross-platform components"
4269
+ },
4270
+ {
4271
+ value: "uniwind",
4272
+ label: "Uniwind",
4273
+ hint: "Tailwind-style React Native styling"
4274
+ },
4275
+ {
4276
+ value: "unistyles",
4277
+ label: "Unistyles",
4278
+ hint: "Type-safe React Native stylesheets"
4279
+ }
4280
+ ];
4281
+ const MOBILE_STORAGE_OPTIONS = [{
4282
+ value: "none",
4283
+ label: "None",
4284
+ hint: "Skip device storage helpers"
4285
+ }, {
4286
+ value: "mmkv",
4287
+ label: "MMKV",
4288
+ hint: "Fast encrypted key-value storage"
4289
+ }];
4290
+ const MOBILE_TESTING_OPTIONS = [
4291
+ {
4292
+ value: "none",
4293
+ label: "None",
4294
+ hint: "Skip mobile testing setup"
4295
+ },
4296
+ {
4297
+ value: "maestro",
4298
+ label: "Maestro",
4299
+ hint: "Mobile E2E flow files"
4300
+ },
4301
+ {
4302
+ value: "react-native-testing-library",
4303
+ label: "React Native Testing Library",
4304
+ hint: "Unit tests for native components"
4305
+ },
4306
+ {
4307
+ value: "maestro-react-native-testing-library",
4308
+ label: "Maestro + RN Testing Library",
4309
+ hint: "Mobile E2E flows and unit tests"
4310
+ }
4311
+ ];
4312
+ const MOBILE_PUSH_OPTIONS = [{
4313
+ value: "none",
4314
+ label: "None",
4315
+ hint: "Skip push notification setup"
4316
+ }, {
4317
+ value: "expo-notifications",
4318
+ label: "Expo Notifications",
4319
+ hint: "Expo push token helper"
4320
+ }];
4321
+ const MOBILE_OTA_OPTIONS = [{
4322
+ value: "none",
4323
+ label: "None",
4324
+ hint: "Skip OTA update setup"
4325
+ }, {
4326
+ value: "expo-updates",
4327
+ label: "Expo Updates",
4328
+ hint: "Runtime version and update helper"
4329
+ }];
4330
+ const MOBILE_DEEP_LINKING_OPTIONS = [{
4331
+ value: "expo-linking",
4332
+ label: "Expo Linking",
4333
+ hint: "Scheme config and redirect URI helpers"
4334
+ }, {
4335
+ value: "none",
4336
+ label: "None",
4337
+ hint: "Skip deep link helpers"
4338
+ }];
4339
+ async function promptMobileOption(options, defaultValue, selected, message) {
4340
+ const resolution = createStaticSinglePromptResolution(options, defaultValue, selected);
4341
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? defaultValue;
4342
+ const response = await navigableSelect({
4343
+ message,
4344
+ options: resolution.options,
4345
+ initialValue: resolution.initialValue
4346
+ });
4347
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
4348
+ return response;
4349
+ }
4350
+ function getMobileNavigationChoice(mobileNavigation) {
4351
+ return promptMobileOption(MOBILE_NAVIGATION_OPTIONS, "expo-router", mobileNavigation, "Select mobile navigation");
4352
+ }
4353
+ function getMobileUIChoice(mobileUI) {
4354
+ return promptMobileOption(MOBILE_UI_OPTIONS, "none", mobileUI, "Select mobile UI");
4355
+ }
4356
+ function getMobileStorageChoice(mobileStorage) {
4357
+ return promptMobileOption(MOBILE_STORAGE_OPTIONS, "none", mobileStorage, "Select mobile storage");
4358
+ }
4359
+ function getMobileTestingChoice(mobileTesting) {
4360
+ return promptMobileOption(MOBILE_TESTING_OPTIONS, "none", mobileTesting, "Select mobile testing");
4361
+ }
4362
+ function getMobilePushChoice(mobilePush) {
4363
+ return promptMobileOption(MOBILE_PUSH_OPTIONS, "none", mobilePush, "Select mobile push");
4364
+ }
4365
+ function getMobileOTAChoice(mobileOTA) {
4366
+ return promptMobileOption(MOBILE_OTA_OPTIONS, "none", mobileOTA, "Select mobile OTA updates");
4367
+ }
4368
+ function getMobileDeepLinkingChoice(mobileDeepLinking) {
4369
+ return promptMobileOption(MOBILE_DEEP_LINKING_OPTIONS, "expo-linking", mobileDeepLinking, "Select mobile deep linking");
4370
+ }
4371
+
3756
4372
  //#endregion
3757
4373
  //#region src/prompts/navigable-group.ts
3758
4374
  /**
@@ -3834,8 +4450,16 @@ const OBSERVABILITY_PROMPT_OPTIONS = [
3834
4450
  hint: "Skip observability/tracing setup"
3835
4451
  }
3836
4452
  ];
4453
+ const NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS = OBSERVABILITY_PROMPT_OPTIONS.filter((option) => option.value === "sentry" || option.value === "none");
3837
4454
  function resolveObservabilityPrompt(context = {}) {
3838
- if (context.backend === "none" || context.backend === "convex") return {
4455
+ if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
4456
+ shouldPrompt: false,
4457
+ mode: "single",
4458
+ options: [],
4459
+ autoValue: "none"
4460
+ };
4461
+ const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS : OBSERVABILITY_PROMPT_OPTIONS;
4462
+ if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
3839
4463
  shouldPrompt: false,
3840
4464
  mode: "single",
3841
4465
  options: [],
@@ -3844,19 +4468,20 @@ function resolveObservabilityPrompt(context = {}) {
3844
4468
  return context.observability !== void 0 ? {
3845
4469
  shouldPrompt: false,
3846
4470
  mode: "single",
3847
- options: OBSERVABILITY_PROMPT_OPTIONS,
4471
+ options,
3848
4472
  autoValue: context.observability
3849
4473
  } : {
3850
4474
  shouldPrompt: true,
3851
4475
  mode: "single",
3852
- options: OBSERVABILITY_PROMPT_OPTIONS,
4476
+ options,
3853
4477
  initialValue: "none"
3854
4478
  };
3855
4479
  }
3856
- async function getObservabilityChoice(observability, backend) {
4480
+ async function getObservabilityChoice(observability, backend, ecosystem) {
3857
4481
  const resolution = resolveObservabilityPrompt({
3858
4482
  observability,
3859
- backend
4483
+ backend,
4484
+ ecosystem
3860
4485
  });
3861
4486
  if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3862
4487
  const response = await navigableSelect({
@@ -4154,52 +4779,113 @@ const PYTHON_AI_PROMPT_OPTIONS = [
4154
4779
  value: "crewai",
4155
4780
  label: "CrewAI",
4156
4781
  hint: "Multi-agent orchestration framework"
4782
+ },
4783
+ {
4784
+ value: "haystack",
4785
+ label: "Haystack",
4786
+ hint: "Composable LLM pipelines, RAG, and search applications"
4157
4787
  }
4158
4788
  ];
4159
4789
  const PYTHON_AUTH_PROMPT_OPTIONS = [
4160
4790
  {
4161
- value: "authlib",
4162
- label: "Authlib",
4163
- hint: "Comprehensive auth library — OAuth1/2, OIDC, JWS, JWK, JWT"
4791
+ value: "authlib",
4792
+ label: "Authlib",
4793
+ hint: "Comprehensive auth library — OAuth1/2, OIDC, JWS, JWK, JWT"
4794
+ },
4795
+ {
4796
+ value: "jwt",
4797
+ label: "JWT (python-jose)",
4798
+ hint: "Simple JWT token creation and verification"
4799
+ },
4800
+ {
4801
+ value: "none",
4802
+ label: "None",
4803
+ hint: "No authentication library"
4804
+ }
4805
+ ];
4806
+ const PYTHON_API_PROMPT_OPTIONS = [
4807
+ {
4808
+ value: "django-rest-framework",
4809
+ label: "Django REST Framework",
4810
+ hint: "Mature, widely used toolkit for building Django REST APIs"
4811
+ },
4812
+ {
4813
+ value: "django-ninja",
4814
+ label: "Django Ninja",
4815
+ hint: "FastAPI-style Django APIs with type hints and automatic OpenAPI docs"
4816
+ },
4817
+ {
4818
+ value: "none",
4819
+ label: "None",
4820
+ hint: "No additional Python API framework"
4821
+ }
4822
+ ];
4823
+ const PYTHON_TASK_QUEUE_PROMPT_OPTIONS = [
4824
+ {
4825
+ value: "celery",
4826
+ label: "Celery",
4827
+ hint: "Distributed task queue for Python"
4828
+ },
4829
+ {
4830
+ value: "rq",
4831
+ label: "RQ",
4832
+ hint: "Simple Redis-backed job queue for Python"
4833
+ },
4834
+ {
4835
+ value: "dramatiq",
4836
+ label: "Dramatiq",
4837
+ hint: "Distributed task processing with Redis or RabbitMQ brokers"
4838
+ },
4839
+ {
4840
+ value: "huey",
4841
+ label: "Huey",
4842
+ hint: "Lightweight task queue with Redis-backed scheduling"
4843
+ },
4844
+ {
4845
+ value: "none",
4846
+ label: "None",
4847
+ hint: "No task queue"
4848
+ }
4849
+ ];
4850
+ const PYTHON_GRAPHQL_PROMPT_OPTIONS = [
4851
+ {
4852
+ value: "strawberry",
4853
+ label: "Strawberry",
4854
+ hint: "Python GraphQL library using dataclasses and type hints"
4855
+ },
4856
+ {
4857
+ value: "ariadne",
4858
+ label: "Ariadne",
4859
+ hint: "Schema-first GraphQL server library for Python"
4860
+ },
4861
+ {
4862
+ value: "none",
4863
+ label: "None",
4864
+ hint: "No GraphQL framework"
4865
+ }
4866
+ ];
4867
+ const PYTHON_QUALITY_PROMPT_OPTIONS = [
4868
+ {
4869
+ value: "ruff",
4870
+ label: "Ruff",
4871
+ hint: "An extremely fast Python linter and formatter"
4872
+ },
4873
+ {
4874
+ value: "mypy",
4875
+ label: "mypy",
4876
+ hint: "Static type checker for Python"
4164
4877
  },
4165
4878
  {
4166
- value: "jwt",
4167
- label: "JWT (python-jose)",
4168
- hint: "Simple JWT token creation and verification"
4879
+ value: "pyright",
4880
+ label: "Pyright",
4881
+ hint: "Fast Python type checker from Microsoft"
4169
4882
  },
4170
4883
  {
4171
4884
  value: "none",
4172
4885
  label: "None",
4173
- hint: "No authentication library"
4886
+ hint: "No code quality tools"
4174
4887
  }
4175
4888
  ];
4176
- const PYTHON_TASK_QUEUE_PROMPT_OPTIONS = [{
4177
- value: "celery",
4178
- label: "Celery",
4179
- hint: "Distributed task queue for Python"
4180
- }, {
4181
- value: "none",
4182
- label: "None",
4183
- hint: "No task queue"
4184
- }];
4185
- const PYTHON_GRAPHQL_PROMPT_OPTIONS = [{
4186
- value: "strawberry",
4187
- label: "Strawberry",
4188
- hint: "Python GraphQL library using dataclasses and type hints"
4189
- }, {
4190
- value: "none",
4191
- label: "None",
4192
- hint: "No GraphQL framework"
4193
- }];
4194
- const PYTHON_QUALITY_PROMPT_OPTIONS = [{
4195
- value: "ruff",
4196
- label: "Ruff",
4197
- hint: "An extremely fast Python linter and formatter"
4198
- }, {
4199
- value: "none",
4200
- label: "None",
4201
- hint: "No code quality tools"
4202
- }];
4203
4889
  function resolvePythonWebFrameworkPrompt(pythonWebFramework) {
4204
4890
  return createStaticSinglePromptResolution(PYTHON_WEB_FRAMEWORK_PROMPT_OPTIONS, "fastapi", pythonWebFramework);
4205
4891
  }
@@ -4272,6 +4958,20 @@ async function getPythonAuthChoice(pythonAuth) {
4272
4958
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
4273
4959
  return response;
4274
4960
  }
4961
+ function resolvePythonApiPrompt(pythonApi) {
4962
+ return createStaticSinglePromptResolution(PYTHON_API_PROMPT_OPTIONS, "none", pythonApi);
4963
+ }
4964
+ async function getPythonApiChoice(pythonApi) {
4965
+ const resolution = resolvePythonApiPrompt(pythonApi);
4966
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
4967
+ const response = await navigableSelect({
4968
+ message: "Select Python API framework",
4969
+ options: resolution.options,
4970
+ initialValue: resolution.initialValue
4971
+ });
4972
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
4973
+ return response;
4974
+ }
4275
4975
  function resolvePythonTaskQueuePrompt(pythonTaskQueue) {
4276
4976
  return createStaticSinglePromptResolution(PYTHON_TASK_QUEUE_PROMPT_OPTIONS, "none", pythonTaskQueue);
4277
4977
  }
@@ -4545,6 +5245,51 @@ const RUST_LIBRARIES_PROMPT_OPTIONS = [
4545
5245
  label: "Serde",
4546
5246
  hint: "Serialization framework for Rust"
4547
5247
  },
5248
+ {
5249
+ value: "uuid",
5250
+ label: "uuid",
5251
+ hint: "UUID generation and parsing with Serde support"
5252
+ },
5253
+ {
5254
+ value: "chrono",
5255
+ label: "Chrono",
5256
+ hint: "Date and time library with Serde support"
5257
+ },
5258
+ {
5259
+ value: "reqwest",
5260
+ label: "Reqwest",
5261
+ hint: "Ergonomic HTTP client with JSON and Rustls TLS support"
5262
+ },
5263
+ {
5264
+ value: "config",
5265
+ label: "config",
5266
+ hint: "Layered configuration from files, environment, and defaults"
5267
+ },
5268
+ {
5269
+ value: "dashmap",
5270
+ label: "DashMap",
5271
+ hint: "Concurrent hash map for shared mutable state"
5272
+ },
5273
+ {
5274
+ value: "parking-lot",
5275
+ label: "parking_lot",
5276
+ hint: "Compact, fast synchronization primitives"
5277
+ },
5278
+ {
5279
+ value: "secrecy",
5280
+ label: "Secrecy",
5281
+ hint: "Secret value wrapper that avoids accidental exposure"
5282
+ },
5283
+ {
5284
+ value: "tokio-util",
5285
+ label: "Tokio Util",
5286
+ hint: "Tokio utilities for codecs, cancellation tokens, and IO helpers"
5287
+ },
5288
+ {
5289
+ value: "utoipa",
5290
+ label: "utoipa",
5291
+ hint: "OpenAPI documentation generation from Rust types"
5292
+ },
4548
5293
  {
4549
5294
  value: "validator",
4550
5295
  label: "Validator",
@@ -4569,6 +5314,16 @@ const RUST_LIBRARIES_PROMPT_OPTIONS = [
4569
5314
  value: "mockall",
4570
5315
  label: "Mockall",
4571
5316
  hint: "Powerful mocking library for Rust"
5317
+ },
5318
+ {
5319
+ value: "proptest",
5320
+ label: "Proptest",
5321
+ hint: "Property-based testing for Rust"
5322
+ },
5323
+ {
5324
+ value: "insta",
5325
+ label: "Insta",
5326
+ hint: "Snapshot testing for Rust"
4572
5327
  }
4573
5328
  ];
4574
5329
  const RUST_LOGGING_PROMPT_OPTIONS = [
@@ -4770,39 +5525,71 @@ async function getRustAuthChoice(rustAuth) {
4770
5525
 
4771
5526
  //#endregion
4772
5527
  //#region src/prompts/search.ts
4773
- async function getSearchChoice(search, backend) {
4774
- if (search !== void 0) return search;
4775
- if (backend === "none" || backend === "convex") return "none";
5528
+ const SEARCH_PROMPT_OPTIONS = [
5529
+ {
5530
+ value: "meilisearch",
5531
+ label: "Meilisearch",
5532
+ hint: "Lightning-fast search engine with typo tolerance"
5533
+ },
5534
+ {
5535
+ value: "typesense",
5536
+ label: "Typesense",
5537
+ hint: "Fast, typo-tolerant search with built-in vector search"
5538
+ },
5539
+ {
5540
+ value: "elasticsearch",
5541
+ label: "Elasticsearch",
5542
+ hint: "Distributed search and analytics engine with local and cloud deployments"
5543
+ },
5544
+ {
5545
+ value: "algolia",
5546
+ label: "Algolia",
5547
+ hint: "Hosted search API with instant results, typo tolerance, and analytics"
5548
+ },
5549
+ {
5550
+ value: "none",
5551
+ label: "None",
5552
+ hint: "Skip search engine setup"
5553
+ }
5554
+ ];
5555
+ const NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS = SEARCH_PROMPT_OPTIONS.filter((option) => option.value === "meilisearch" || option.value === "none");
5556
+ function resolveSearchPrompt(context = {}) {
5557
+ if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
5558
+ shouldPrompt: false,
5559
+ mode: "single",
5560
+ options: [],
5561
+ autoValue: "none"
5562
+ };
5563
+ const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS : SEARCH_PROMPT_OPTIONS;
5564
+ if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
5565
+ shouldPrompt: false,
5566
+ mode: "single",
5567
+ options: [],
5568
+ autoValue: "none"
5569
+ };
5570
+ return context.search !== void 0 ? {
5571
+ shouldPrompt: false,
5572
+ mode: "single",
5573
+ options,
5574
+ autoValue: context.search
5575
+ } : {
5576
+ shouldPrompt: true,
5577
+ mode: "single",
5578
+ options,
5579
+ initialValue: "none"
5580
+ };
5581
+ }
5582
+ async function getSearchChoice(search, backend, ecosystem) {
5583
+ const resolution = resolveSearchPrompt({
5584
+ search,
5585
+ backend,
5586
+ ecosystem
5587
+ });
5588
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
4776
5589
  const response = await navigableSelect({
4777
5590
  message: "Select search engine",
4778
- options: [
4779
- {
4780
- value: "meilisearch",
4781
- label: "Meilisearch",
4782
- hint: "Lightning-fast search engine with typo tolerance"
4783
- },
4784
- {
4785
- value: "typesense",
4786
- label: "Typesense",
4787
- hint: "Fast, typo-tolerant search with built-in vector search"
4788
- },
4789
- {
4790
- value: "elasticsearch",
4791
- label: "Elasticsearch",
4792
- hint: "Distributed search and analytics engine with local and cloud deployments"
4793
- },
4794
- {
4795
- value: "algolia",
4796
- label: "Algolia",
4797
- hint: "Hosted search API with instant results, typo tolerance, and analytics"
4798
- },
4799
- {
4800
- value: "none",
4801
- label: "None",
4802
- hint: "Skip search engine setup"
4803
- }
4804
- ],
4805
- initialValue: "none"
5591
+ options: resolution.options,
5592
+ initialValue: resolution.initialValue
4806
5593
  });
4807
5594
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
4808
5595
  return response;
@@ -4854,6 +5641,16 @@ const STYLE_OPTIONS = [
4854
5641
  value: "mira",
4855
5642
  label: "Mira",
4856
5643
  hint: "Dense, made for data-heavy interfaces"
5644
+ },
5645
+ {
5646
+ value: "luma",
5647
+ label: "Luma",
5648
+ hint: "Modern shadcn/ui v4 preset"
5649
+ },
5650
+ {
5651
+ value: "sera",
5652
+ label: "Sera",
5653
+ hint: "Modern shadcn/ui v4 preset"
4857
5654
  }
4858
5655
  ];
4859
5656
  const ICON_LIBRARY_OPTIONS = [
@@ -4881,6 +5678,16 @@ const ICON_LIBRARY_OPTIONS = [
4881
5678
  value: "remixicon",
4882
5679
  label: "Remix Icon",
4883
5680
  hint: "Open-source neutral style icons"
5681
+ },
5682
+ {
5683
+ value: "heroicons",
5684
+ label: "Heroicons",
5685
+ hint: "Tailwind Labs SVG icon set"
5686
+ },
5687
+ {
5688
+ value: "react-icons",
5689
+ label: "React Icons",
5690
+ hint: "Popular wrapper for many icon packs"
4884
5691
  }
4885
5692
  ];
4886
5693
  const COLOR_THEME_OPTIONS = [
@@ -5226,6 +6033,7 @@ function resolveStateManagementPrompt(context = {}) {
5226
6033
  "react-vite",
5227
6034
  "tanstack-start",
5228
6035
  "next",
6036
+ "vinext",
5229
6037
  "redwood"
5230
6038
  ].includes(f));
5231
6039
  const isFresh = web.includes("fresh");
@@ -5379,6 +6187,14 @@ const UI_LIBRARY_OPTIONS = {
5379
6187
  label: "Mantine",
5380
6188
  hint: "Full-featured React component library with 120+ components"
5381
6189
  },
6190
+ mui: {
6191
+ label: "MUI",
6192
+ hint: "Popular React component library implementing Material Design"
6193
+ },
6194
+ antd: {
6195
+ label: "Ant Design",
6196
+ hint: "Enterprise-class React UI component library"
6197
+ },
5382
6198
  "base-ui": {
5383
6199
  label: "Base UI",
5384
6200
  hint: "Unstyled, accessible components from MUI team (Radix successor)"
@@ -5510,6 +6326,7 @@ const WEB_FRAMEWORKS = [
5510
6326
  "react-vite",
5511
6327
  "tanstack-start",
5512
6328
  "next",
6329
+ "vinext",
5513
6330
  "nuxt",
5514
6331
  "svelte",
5515
6332
  "solid",
@@ -5569,6 +6386,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5569
6386
  const result = await navigableGroup({
5570
6387
  ecosystem: () => getEcosystemChoice(flags.ecosystem),
5571
6388
  frontend: ({ results }) => {
6389
+ if (results.ecosystem === "react-native") return getNativeFrontendChoice(flags.frontend);
5572
6390
  if (results.ecosystem !== "typescript") return Promise.resolve([]);
5573
6391
  return getFrontendChoice(flags.frontend, flags.backend, flags.auth);
5574
6392
  },
@@ -5621,6 +6439,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5621
6439
  },
5622
6440
  auth: ({ results }) => {
5623
6441
  if (results.ecosystem === "typescript") return getAuthChoice(flags.auth, results.backend, results.frontend, "typescript");
6442
+ if (results.ecosystem === "react-native") return Promise.resolve(flags.auth ?? "none");
5624
6443
  if (results.ecosystem === "go") return getAuthChoice(flags.auth, void 0, void 0, "go");
5625
6444
  return Promise.resolve("none");
5626
6445
  },
@@ -5629,8 +6448,8 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5629
6448
  return getPaymentsChoice(flags.payments, results.auth, results.backend, results.frontend);
5630
6449
  },
5631
6450
  email: ({ results }) => {
5632
- if (results.ecosystem !== "typescript") return Promise.resolve("none");
5633
- return getEmailChoice(flags.email, results.backend);
6451
+ if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
6452
+ return getEmailChoice(flags.email, results.backend, results.ecosystem);
5634
6453
  },
5635
6454
  effect: ({ results }) => {
5636
6455
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
@@ -5701,8 +6520,8 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5701
6520
  return getLoggingChoice(flags.logging, results.backend);
5702
6521
  },
5703
6522
  observability: ({ results }) => {
5704
- if (results.ecosystem !== "typescript") return Promise.resolve("none");
5705
- return getObservabilityChoice(flags.observability, results.backend);
6523
+ if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
6524
+ return getObservabilityChoice(flags.observability, results.backend, results.ecosystem);
5706
6525
  },
5707
6526
  featureFlags: ({ results }) => {
5708
6527
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
@@ -5717,21 +6536,58 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5717
6536
  return getCMSChoice(flags.cms, results.backend);
5718
6537
  },
5719
6538
  caching: ({ results }) => {
5720
- if (results.ecosystem !== "typescript") return Promise.resolve("none");
5721
- return getCachingChoice(flags.caching, results.backend);
6539
+ if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
6540
+ return getCachingChoice(flags.caching, results.backend, results.ecosystem);
5722
6541
  },
5723
6542
  i18n: ({ results }) => {
5724
6543
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
5725
6544
  return getI18nChoice(flags.i18n, results.frontend);
5726
6545
  },
5727
6546
  search: ({ results }) => {
5728
- if (results.ecosystem !== "typescript") return Promise.resolve("none");
5729
- return getSearchChoice(flags.search, results.backend);
6547
+ if (results.ecosystem === "react-native" || results.ecosystem === "elixir") return Promise.resolve("none");
6548
+ return getSearchChoice(flags.search, results.backend, results.ecosystem);
5730
6549
  },
5731
6550
  fileStorage: ({ results }) => {
5732
6551
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
5733
6552
  return getFileStorageChoice(flags.fileStorage, results.backend);
5734
6553
  },
6554
+ mobileNavigation: ({ results }) => {
6555
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6556
+ if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
6557
+ return getMobileNavigationChoice(flags.mobileNavigation);
6558
+ },
6559
+ mobileUI: ({ results }) => {
6560
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6561
+ if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
6562
+ if (results.frontend.includes("native-uniwind")) return Promise.resolve("uniwind");
6563
+ if (results.frontend.includes("native-unistyles")) return Promise.resolve("unistyles");
6564
+ return getMobileUIChoice(flags.mobileUI);
6565
+ },
6566
+ mobileStorage: ({ results }) => {
6567
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6568
+ if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
6569
+ return getMobileStorageChoice(flags.mobileStorage);
6570
+ },
6571
+ mobileTesting: ({ results }) => {
6572
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6573
+ if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
6574
+ return getMobileTestingChoice(flags.mobileTesting);
6575
+ },
6576
+ mobilePush: ({ results }) => {
6577
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6578
+ if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
6579
+ return getMobilePushChoice(flags.mobilePush);
6580
+ },
6581
+ mobileOTA: ({ results }) => {
6582
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6583
+ if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
6584
+ return getMobileOTAChoice(flags.mobileOTA);
6585
+ },
6586
+ mobileDeepLinking: ({ results }) => {
6587
+ if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
6588
+ if (!results.frontend?.some((frontend) => frontend.startsWith("native-"))) return Promise.resolve("none");
6589
+ return getMobileDeepLinkingChoice(flags.mobileDeepLinking);
6590
+ },
5735
6591
  rustWebFramework: ({ results }) => {
5736
6592
  if (results.ecosystem !== "rust") return Promise.resolve("none");
5737
6593
  return getRustWebFrameworkChoice(flags.rustWebFramework);
@@ -5792,6 +6648,11 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5792
6648
  if (results.ecosystem !== "python") return Promise.resolve("none");
5793
6649
  return getPythonAuthChoice(flags.pythonAuth);
5794
6650
  },
6651
+ pythonApi: ({ results }) => {
6652
+ if (results.ecosystem !== "python") return Promise.resolve("none");
6653
+ if (results.pythonWebFramework !== "django") return Promise.resolve("none");
6654
+ return getPythonApiChoice(flags.pythonApi);
6655
+ },
5795
6656
  pythonTaskQueue: ({ results }) => {
5796
6657
  if (results.ecosystem !== "python") return Promise.resolve("none");
5797
6658
  return getPythonTaskQueueChoice(flags.pythonTaskQueue);
@@ -5856,10 +6717,70 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5856
6717
  if (results.javaBuildTool === "none") return Promise.resolve([]);
5857
6718
  return getJavaTestingLibrariesChoice(flags.javaTestingLibraries);
5858
6719
  },
6720
+ elixirWebFramework: ({ results }) => {
6721
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6722
+ return getElixirWebFrameworkChoice(flags.elixirWebFramework);
6723
+ },
6724
+ elixirOrm: ({ results }) => {
6725
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6726
+ return getElixirOrmChoice(flags.elixirOrm);
6727
+ },
6728
+ elixirAuth: ({ results }) => {
6729
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6730
+ return getElixirAuthChoice(flags.elixirAuth);
6731
+ },
6732
+ elixirApi: ({ results }) => {
6733
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6734
+ return getElixirApiChoice(flags.elixirApi);
6735
+ },
6736
+ elixirRealtime: ({ results }) => {
6737
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6738
+ return getElixirRealtimeChoice(flags.elixirRealtime);
6739
+ },
6740
+ elixirJobs: ({ results }) => {
6741
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6742
+ return getElixirJobsChoice(flags.elixirJobs);
6743
+ },
6744
+ elixirValidation: ({ results }) => {
6745
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6746
+ return getElixirValidationChoice(flags.elixirValidation);
6747
+ },
6748
+ elixirHttp: ({ results }) => {
6749
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6750
+ return getElixirHttpChoice(flags.elixirHttp);
6751
+ },
6752
+ elixirJson: ({ results }) => {
6753
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6754
+ return getElixirJsonChoice(flags.elixirJson);
6755
+ },
6756
+ elixirEmail: ({ results }) => {
6757
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6758
+ return getElixirEmailChoice(flags.elixirEmail);
6759
+ },
6760
+ elixirCaching: ({ results }) => {
6761
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6762
+ return getElixirCachingChoice(flags.elixirCaching);
6763
+ },
6764
+ elixirObservability: ({ results }) => {
6765
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6766
+ return getElixirObservabilityChoice(flags.elixirObservability);
6767
+ },
6768
+ elixirTesting: ({ results }) => {
6769
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6770
+ return getElixirTestingChoice(flags.elixirTesting);
6771
+ },
6772
+ elixirQuality: ({ results }) => {
6773
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6774
+ return getElixirQualityChoice(flags.elixirQuality);
6775
+ },
6776
+ elixirDeploy: ({ results }) => {
6777
+ if (results.ecosystem !== "elixir") return Promise.resolve("none");
6778
+ return getElixirDeployChoice(flags.elixirDeploy);
6779
+ },
5859
6780
  aiDocs: () => getAiDocsChoice(flags.aiDocs),
5860
6781
  git: () => getGitChoice(flags.git),
5861
6782
  packageManager: ({ results }) => {
5862
- if (results.ecosystem === "rust" || results.ecosystem === "python" || results.ecosystem === "go" || results.ecosystem === "java") return Promise.resolve(flags.packageManager ?? getUserPkgManager());
6783
+ if (results.ecosystem === "rust" || results.ecosystem === "python" || results.ecosystem === "go" || results.ecosystem === "java" || results.ecosystem === "elixir") return Promise.resolve(flags.packageManager ?? getUserPkgManager());
5863
6784
  return getPackageManagerChoice(flags.packageManager);
5864
6785
  },
5865
6786
  install: ({ results }) => getinstallChoice(flags.install, results.ecosystem, results.javaBuildTool)
@@ -5908,6 +6829,13 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5908
6829
  i18n: result.i18n,
5909
6830
  search: result.search,
5910
6831
  fileStorage: result.fileStorage,
6832
+ mobileNavigation: result.mobileNavigation,
6833
+ mobileUI: result.mobileUI,
6834
+ mobileStorage: result.mobileStorage,
6835
+ mobileTesting: result.mobileTesting,
6836
+ mobilePush: result.mobilePush,
6837
+ mobileOTA: result.mobileOTA,
6838
+ mobileDeepLinking: result.mobileDeepLinking,
5911
6839
  ecosystem: result.ecosystem,
5912
6840
  rustWebFramework: result.rustWebFramework,
5913
6841
  rustFrontend: result.rustFrontend,
@@ -5924,6 +6852,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5924
6852
  pythonValidation: result.pythonValidation,
5925
6853
  pythonAi: result.pythonAi,
5926
6854
  pythonAuth: result.pythonAuth,
6855
+ pythonApi: result.pythonApi,
5927
6856
  pythonTaskQueue: result.pythonTaskQueue,
5928
6857
  pythonGraphql: result.pythonGraphql,
5929
6858
  pythonQuality: result.pythonQuality,
@@ -5939,6 +6868,21 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5939
6868
  javaAuth: result.javaAuth,
5940
6869
  javaLibraries: result.javaLibraries,
5941
6870
  javaTestingLibraries: result.javaTestingLibraries,
6871
+ elixirWebFramework: result.elixirWebFramework,
6872
+ elixirOrm: result.elixirOrm,
6873
+ elixirAuth: result.elixirAuth,
6874
+ elixirApi: result.elixirApi,
6875
+ elixirRealtime: result.elixirRealtime,
6876
+ elixirJobs: result.elixirJobs,
6877
+ elixirValidation: result.elixirValidation,
6878
+ elixirHttp: result.elixirHttp,
6879
+ elixirJson: result.elixirJson,
6880
+ elixirEmail: result.elixirEmail,
6881
+ elixirCaching: result.elixirCaching,
6882
+ elixirObservability: result.elixirObservability,
6883
+ elixirTesting: result.elixirTesting,
6884
+ elixirQuality: result.elixirQuality,
6885
+ elixirDeploy: result.elixirDeploy,
5942
6886
  aiDocs: result.aiDocs
5943
6887
  };
5944
6888
  }
@@ -6095,7 +7039,17 @@ function displayConfig(config) {
6095
7039
  if (config.jobQueue !== void 0) configDisplay.push(`${pc.blue("Job Queue:")} ${String(config.jobQueue)}`);
6096
7040
  if (config.logging !== void 0) configDisplay.push(`${pc.blue("Logging:")} ${String(config.logging)}`);
6097
7041
  if (config.observability !== void 0) configDisplay.push(`${pc.blue("Observability:")} ${String(config.observability)}`);
7042
+ if (config.featureFlags !== void 0) configDisplay.push(`${pc.blue("Feature Flags:")} ${String(config.featureFlags)}`);
7043
+ if (config.analytics !== void 0) configDisplay.push(`${pc.blue("Analytics:")} ${String(config.analytics)}`);
7044
+ if (config.mobileNavigation !== void 0) configDisplay.push(`${pc.blue("Mobile Navigation:")} ${String(config.mobileNavigation)}`);
7045
+ if (config.mobileUI !== void 0) configDisplay.push(`${pc.blue("Mobile UI:")} ${String(config.mobileUI)}`);
7046
+ if (config.mobileStorage !== void 0) configDisplay.push(`${pc.blue("Mobile Storage:")} ${String(config.mobileStorage)}`);
7047
+ if (config.mobileTesting !== void 0) configDisplay.push(`${pc.blue("Mobile Testing:")} ${String(config.mobileTesting)}`);
7048
+ if (config.mobilePush !== void 0) configDisplay.push(`${pc.blue("Mobile Push:")} ${String(config.mobilePush)}`);
7049
+ if (config.mobileOTA !== void 0) configDisplay.push(`${pc.blue("Mobile OTA:")} ${String(config.mobileOTA)}`);
7050
+ if (config.mobileDeepLinking !== void 0) configDisplay.push(`${pc.blue("Mobile Deep Linking:")} ${String(config.mobileDeepLinking)}`);
6098
7051
  if (config.caching !== void 0) configDisplay.push(`${pc.blue("Caching:")} ${String(config.caching)}`);
7052
+ if (config.i18n !== void 0) configDisplay.push(`${pc.blue("i18n:")} ${String(config.i18n)}`);
6099
7053
  if (config.cms !== void 0) configDisplay.push(`${pc.blue("CMS:")} ${String(config.cms)}`);
6100
7054
  if (config.search !== void 0) configDisplay.push(`${pc.blue("Search:")} ${String(config.search)}`);
6101
7055
  if (config.fileStorage !== void 0) configDisplay.push(`${pc.blue("File Storage:")} ${String(config.fileStorage)}`);
@@ -6109,6 +7063,11 @@ function displayConfig(config) {
6109
7063
  const examplesText = examples.length > 0 && examples[0] !== void 0 ? examples.join(", ") : "none";
6110
7064
  configDisplay.push(`${pc.blue("Examples:")} ${examplesText}`);
6111
7065
  }
7066
+ if (config.aiDocs !== void 0) {
7067
+ const aiDocs = Array.isArray(config.aiDocs) ? config.aiDocs : [config.aiDocs];
7068
+ const aiDocsText = aiDocs.length > 0 && aiDocs[0] !== void 0 ? aiDocs.join(", ") : "none";
7069
+ configDisplay.push(`${pc.blue("AI Docs:")} ${aiDocsText}`);
7070
+ }
6112
7071
  if (config.git !== void 0) {
6113
7072
  const gitText = typeof config.git === "boolean" ? config.git ? "Yes" : "No" : String(config.git);
6114
7073
  configDisplay.push(`${pc.blue("Git Init:")} ${gitText}`);
@@ -6151,6 +7110,10 @@ function appendCommonFlags(flags, config) {
6151
7110
  flags.push(config.install ? "--install" : "--no-install");
6152
7111
  }
6153
7112
  function appendSharedNonTypeScriptFlags(flags, config) {
7113
+ flags.push(`--email ${config.email}`);
7114
+ flags.push(`--observability ${config.observability}`);
7115
+ flags.push(`--caching ${config.caching}`);
7116
+ flags.push(`--search ${config.search}`);
6154
7117
  flags.push(formatArrayFlag("addons", config.addons));
6155
7118
  flags.push(formatArrayFlag("examples", config.examples));
6156
7119
  flags.push(`--db-setup ${config.dbSetup}`);
@@ -6192,11 +7155,19 @@ function getTypeScriptFlags(config) {
6192
7155
  flags.push(`--job-queue ${config.jobQueue}`);
6193
7156
  flags.push(`--logging ${config.logging}`);
6194
7157
  flags.push(`--observability ${config.observability}`);
7158
+ flags.push(`--feature-flags ${config.featureFlags}`);
6195
7159
  flags.push(`--caching ${config.caching}`);
6196
7160
  flags.push(`--i18n ${config.i18n}`);
6197
7161
  flags.push(`--cms ${config.cms}`);
6198
7162
  flags.push(`--search ${config.search}`);
6199
7163
  flags.push(`--file-storage ${config.fileStorage}`);
7164
+ flags.push(`--mobile-navigation ${config.mobileNavigation}`);
7165
+ flags.push(`--mobile-ui ${config.mobileUI}`);
7166
+ flags.push(`--mobile-storage ${config.mobileStorage}`);
7167
+ flags.push(`--mobile-testing ${config.mobileTesting}`);
7168
+ flags.push(`--mobile-push ${config.mobilePush}`);
7169
+ flags.push(`--mobile-ota ${config.mobileOTA}`);
7170
+ flags.push(`--mobile-deep-linking ${config.mobileDeepLinking}`);
6200
7171
  if (config.addons && config.addons.length > 0) flags.push(`--addons ${config.addons.join(" ")}`);
6201
7172
  else flags.push("--addons none");
6202
7173
  if (config.examples && config.examples.length > 0) flags.push(`--examples ${config.examples.join(" ")}`);
@@ -6207,6 +7178,21 @@ function getTypeScriptFlags(config) {
6207
7178
  appendCommonFlags(flags, config);
6208
7179
  return flags;
6209
7180
  }
7181
+ function getReactNativeFlags(config) {
7182
+ const flags = ["--ecosystem react-native"];
7183
+ if (config.frontend && config.frontend.length > 0) flags.push(`--frontend ${config.frontend.join(" ")}`);
7184
+ else flags.push("--frontend native-bare");
7185
+ flags.push(`--auth ${config.auth}`);
7186
+ flags.push(`--mobile-navigation ${config.mobileNavigation}`);
7187
+ flags.push(`--mobile-ui ${config.mobileUI}`);
7188
+ flags.push(`--mobile-storage ${config.mobileStorage}`);
7189
+ flags.push(`--mobile-testing ${config.mobileTesting}`);
7190
+ flags.push(`--mobile-push ${config.mobilePush}`);
7191
+ flags.push(`--mobile-ota ${config.mobileOTA}`);
7192
+ flags.push(`--mobile-deep-linking ${config.mobileDeepLinking}`);
7193
+ appendCommonFlags(flags, config);
7194
+ return flags;
7195
+ }
6210
7196
  function getRustFlags(config) {
6211
7197
  const flags = ["--ecosystem rust"];
6212
7198
  flags.push(`--rust-web-framework ${config.rustWebFramework}`);
@@ -6230,6 +7216,7 @@ function getPythonFlags(config) {
6230
7216
  flags.push(`--python-validation ${config.pythonValidation}`);
6231
7217
  flags.push(formatArrayFlag("python-ai", config.pythonAi));
6232
7218
  flags.push(`--python-auth ${config.pythonAuth}`);
7219
+ flags.push(`--python-api ${config.pythonApi}`);
6233
7220
  flags.push(`--python-task-queue ${config.pythonTaskQueue}`);
6234
7221
  flags.push(`--python-graphql ${config.pythonGraphql}`);
6235
7222
  flags.push(`--python-quality ${config.pythonQuality}`);
@@ -6262,9 +7249,32 @@ function getJavaFlags(config) {
6262
7249
  appendCommonFlags(flags, config);
6263
7250
  return flags;
6264
7251
  }
7252
+ function getElixirFlags(config) {
7253
+ const flags = ["--ecosystem elixir"];
7254
+ flags.push(`--elixir-web-framework ${config.elixirWebFramework}`);
7255
+ flags.push(`--elixir-orm ${config.elixirOrm}`);
7256
+ flags.push(`--elixir-auth ${config.elixirAuth}`);
7257
+ flags.push(`--elixir-api ${config.elixirApi}`);
7258
+ flags.push(`--elixir-realtime ${config.elixirRealtime}`);
7259
+ flags.push(`--elixir-jobs ${config.elixirJobs}`);
7260
+ flags.push(`--elixir-validation ${config.elixirValidation}`);
7261
+ flags.push(`--elixir-http ${config.elixirHttp}`);
7262
+ flags.push(`--elixir-json ${config.elixirJson}`);
7263
+ flags.push(`--elixir-email ${config.elixirEmail}`);
7264
+ flags.push(`--elixir-caching ${config.elixirCaching}`);
7265
+ flags.push(`--elixir-observability ${config.elixirObservability}`);
7266
+ flags.push(`--elixir-testing ${config.elixirTesting}`);
7267
+ flags.push(`--elixir-quality ${config.elixirQuality}`);
7268
+ flags.push(`--elixir-deploy ${config.elixirDeploy}`);
7269
+ appendCommonFlags(flags, config);
7270
+ return flags;
7271
+ }
6265
7272
  function generateReproducibleCommand(config) {
6266
7273
  let flags;
6267
7274
  switch (config.ecosystem) {
7275
+ case "react-native":
7276
+ flags = getReactNativeFlags(config);
7277
+ break;
6268
7278
  case "rust":
6269
7279
  flags = getRustFlags(config);
6270
7280
  break;
@@ -6277,6 +7287,9 @@ function generateReproducibleCommand(config) {
6277
7287
  case "java":
6278
7288
  flags = getJavaFlags(config);
6279
7289
  break;
7290
+ case "elixir":
7291
+ flags = getElixirFlags(config);
7292
+ break;
6280
7293
  case "typescript":
6281
7294
  default:
6282
7295
  flags = getTypeScriptFlags(config);
@@ -6499,7 +7512,7 @@ const PEER_DEPENDENCY_CONFLICTS = [
6499
7512
  }],
6500
7513
  conflictsWithOptions: [{
6501
7514
  optionKey: "frontend",
6502
- values: ["next"]
7515
+ values: ["next", "vinext"]
6503
7516
  }]
6504
7517
  },
6505
7518
  {
@@ -6563,7 +7576,7 @@ const PEER_DEPENDENCY_CONFLICTS = [
6563
7576
  }],
6564
7577
  conflictsWithOptions: [{
6565
7578
  optionKey: "frontend",
6566
- values: ["next"]
7579
+ values: ["next", "vinext"]
6567
7580
  }]
6568
7581
  }
6569
7582
  ];
@@ -6904,6 +7917,18 @@ function validateBackendConstraints(config, providedFlags, options) {
6904
7917
  function validateFrontendConstraints(config, providedFlags) {
6905
7918
  const { frontend } = config;
6906
7919
  if (frontend && frontend.length > 0) {
7920
+ if (config.ecosystem === "react-native" && frontend.some((item) => !item.startsWith("native-") && item !== "none")) incompatibilityError({
7921
+ message: "React Native ecosystem only supports native Expo frontends.",
7922
+ provided: {
7923
+ ecosystem: "react-native",
7924
+ frontend: frontend.join(" ")
7925
+ },
7926
+ suggestions: [
7927
+ "Use --frontend native-bare",
7928
+ "Use --frontend native-uniwind",
7929
+ "Use --frontend native-unistyles"
7930
+ ]
7931
+ });
6907
7932
  ensureSingleWebAndNative(frontend);
6908
7933
  if (providedFlags.has("api") && providedFlags.has("frontend") && config.api) validateApiFrontendCompatibility(config.api, frontend, config.astroIntegration);
6909
7934
  }
@@ -6914,19 +7939,20 @@ function validateApiConstraints(_config, _options) {}
6914
7939
  function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set()) {
6915
7940
  if (config.ecosystem !== "java") return;
6916
7941
  const hasSpringBoot = config.javaWebFramework === "spring-boot";
7942
+ const hasJavaWebFramework = config.javaWebFramework !== "none";
6917
7943
  const hasNoBuildTool = config.javaBuildTool === "none";
6918
7944
  const hasJavaLibraries = (config.javaLibraries ?? []).some((library) => library !== "none");
6919
7945
  const hasJavaTestingLibraries = (config.javaTestingLibraries ?? []).some((library) => library !== "none");
6920
7946
  const hasSpringOnlyFeatures = config.javaOrm !== "none" || config.javaAuth !== "none" || hasJavaLibraries;
6921
- if (hasNoBuildTool && hasSpringBoot) incompatibilityError({
6922
- message: "Spring Boot requires Maven or Gradle in the Java scaffold.",
7947
+ if (hasNoBuildTool && hasJavaWebFramework) incompatibilityError({
7948
+ message: "Java web frameworks require Maven or Gradle in the Java scaffold.",
6923
7949
  provided: {
6924
7950
  "java-web-framework": config.javaWebFramework ?? "none",
6925
7951
  "java-build-tool": config.javaBuildTool ?? "none"
6926
7952
  },
6927
- suggestions: ["Use --java-build-tool maven or --java-build-tool gradle with Spring Boot", "Use --java-web-framework none for a plain Java source-only scaffold"]
7953
+ suggestions: ["Use --java-build-tool maven or --java-build-tool gradle with Java web frameworks", "Use --java-web-framework none for a plain Java source-only scaffold"]
6928
7954
  });
6929
- if ((config.javaWebFramework === "none" || hasNoBuildTool) && hasSpringOnlyFeatures) incompatibilityError({
7955
+ if ((!hasSpringBoot || hasNoBuildTool) && hasSpringOnlyFeatures) incompatibilityError({
6930
7956
  message: "Spring-only Java features require the Spring Boot scaffold with Maven or Gradle.",
6931
7957
  provided: {
6932
7958
  "java-web-framework": config.javaWebFramework ?? "none",
@@ -6935,7 +7961,7 @@ function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set
6935
7961
  "java-auth": config.javaAuth ?? "none",
6936
7962
  "java-libraries": (config.javaLibraries ?? []).join(" ") || "none"
6937
7963
  },
6938
- suggestions: ["Use --java-web-framework spring-boot and a real build tool for Spring features", "Clear --java-orm, --java-auth, and --java-libraries when using plain Java"]
7964
+ suggestions: ["Use --java-web-framework spring-boot and a real build tool for Spring features", "Clear --java-orm, --java-auth, and --java-libraries when using plain Java or Quarkus"]
6939
7965
  });
6940
7966
  if (hasNoBuildTool && hasJavaTestingLibraries) incompatibilityError({
6941
7967
  message: "Java testing libraries require Maven or Gradle to manage test dependencies.",
@@ -6946,6 +7972,218 @@ function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set
6946
7972
  suggestions: ["Use --java-build-tool maven or --java-build-tool gradle to enable JUnit/Mockito/Testcontainers", "Set --java-testing-libraries none for a source-only plain Java scaffold"]
6947
7973
  });
6948
7974
  }
7975
+ function validateElixirConstraints(config) {
7976
+ if (config.ecosystem !== "elixir") return;
7977
+ const hasPhoenix = config.elixirWebFramework !== "none";
7978
+ const hasEcto = config.elixirOrm !== "none";
7979
+ const unsupportedSelections = [
7980
+ {
7981
+ flag: "elixir-orm",
7982
+ value: config.elixirOrm,
7983
+ unsupported: ["ecto"],
7984
+ message: "Plain Ecto without SQL Repo wiring is not generated yet.",
7985
+ suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-orm none"]
7986
+ },
7987
+ {
7988
+ flag: "elixir-auth",
7989
+ value: config.elixirAuth,
7990
+ unsupported: ["ueberauth", "guardian"],
7991
+ message: "Only phx.gen.auth currently generates Phoenix auth files.",
7992
+ suggestions: ["Use --elixir-auth phx-gen-auth", "Use --elixir-auth none"]
7993
+ },
7994
+ {
7995
+ flag: "elixir-validation",
7996
+ value: config.elixirValidation,
7997
+ unsupported: ["nimble-options"],
7998
+ message: "NimbleOptions is not generated yet.",
7999
+ suggestions: ["Use --elixir-validation ecto-changesets", "Use --elixir-validation none"]
8000
+ },
8001
+ {
8002
+ flag: "elixir-caching",
8003
+ value: config.elixirCaching,
8004
+ unsupported: ["nebulex"],
8005
+ message: "Nebulex cache modules are not generated yet.",
8006
+ suggestions: ["Use --elixir-caching cachex", "Use --elixir-caching none"]
8007
+ },
8008
+ {
8009
+ flag: "elixir-observability",
8010
+ value: config.elixirObservability,
8011
+ unsupported: ["opentelemetry", "prom_ex"],
8012
+ message: "OpenTelemetry and PromEx setup are not generated yet.",
8013
+ suggestions: ["Use --elixir-observability telemetry", "Use --elixir-observability none"]
8014
+ },
8015
+ {
8016
+ flag: "elixir-testing",
8017
+ value: config.elixirTesting,
8018
+ unsupported: [
8019
+ "mox",
8020
+ "bypass",
8021
+ "wallaby"
8022
+ ],
8023
+ message: "Generated Phoenix projects currently include ExUnit tests only.",
8024
+ suggestions: ["Use --elixir-testing ex_unit"]
8025
+ },
8026
+ {
8027
+ flag: "elixir-deploy",
8028
+ value: config.elixirDeploy,
8029
+ unsupported: ["fly", "gigalixir"],
8030
+ message: "Fly.io and Gigalixir config files are not generated yet.",
8031
+ suggestions: ["Use --elixir-deploy docker", "Use --elixir-deploy mix-release"]
8032
+ }
8033
+ ];
8034
+ for (const selection of unsupportedSelections) if (selection.value && selection.unsupported.includes(selection.value)) incompatibilityError({
8035
+ message: selection.message,
8036
+ provided: { [selection.flag]: selection.value },
8037
+ suggestions: selection.suggestions
8038
+ });
8039
+ if (!hasPhoenix) {
8040
+ const phoenixOnlySelections = [
8041
+ {
8042
+ flag: "elixir-auth",
8043
+ value: config.elixirAuth,
8044
+ message: "Elixir auth scaffolds require Phoenix."
8045
+ },
8046
+ {
8047
+ flag: "elixir-api",
8048
+ value: config.elixirApi,
8049
+ message: "Elixir API scaffolds require Phoenix."
8050
+ },
8051
+ {
8052
+ flag: "elixir-realtime",
8053
+ value: config.elixirRealtime,
8054
+ message: "Elixir realtime scaffolds require Phoenix."
8055
+ }
8056
+ ];
8057
+ for (const selection of phoenixOnlySelections) {
8058
+ if (!selection.value || selection.value === "none") continue;
8059
+ incompatibilityError({
8060
+ message: selection.message,
8061
+ provided: {
8062
+ "elixir-web-framework": config.elixirWebFramework ?? "none",
8063
+ [selection.flag]: selection.value
8064
+ },
8065
+ suggestions: [
8066
+ "Use --elixir-web-framework phoenix",
8067
+ "Use --elixir-web-framework phoenix-live-view",
8068
+ `Use --${selection.flag} none`
8069
+ ]
8070
+ });
8071
+ }
8072
+ }
8073
+ if (hasPhoenix && config.elixirJson === "none") incompatibilityError({
8074
+ message: "Phoenix JSON scaffolds require Jason.",
8075
+ provided: { "elixir-json": "none" },
8076
+ suggestions: ["Use --elixir-json jason"]
8077
+ });
8078
+ if (config.elixirAuth === "phx-gen-auth" && !hasEcto) incompatibilityError({
8079
+ message: "phx.gen.auth requires Ecto in the generated Phoenix scaffold.",
8080
+ provided: {
8081
+ "elixir-auth": "phx-gen-auth",
8082
+ "elixir-orm": config.elixirOrm ?? "none"
8083
+ },
8084
+ suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-auth none"]
8085
+ });
8086
+ if (config.elixirJobs === "oban" && config.elixirOrm !== "ecto-sql") incompatibilityError({
8087
+ message: "Oban requires Ecto SQL with PostgreSQL in the generated Phoenix scaffold.",
8088
+ provided: {
8089
+ "elixir-jobs": "oban",
8090
+ "elixir-orm": config.elixirOrm ?? "none"
8091
+ },
8092
+ suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-jobs none"]
8093
+ });
8094
+ if (config.elixirRealtime === "live-view-streams" && config.elixirWebFramework !== "phoenix-live-view") incompatibilityError({
8095
+ message: "LiveView Streams require Phoenix LiveView.",
8096
+ provided: {
8097
+ "elixir-realtime": "live-view-streams",
8098
+ "elixir-web-framework": config.elixirWebFramework ?? "none"
8099
+ },
8100
+ suggestions: ["Use --elixir-web-framework phoenix-live-view", "Use --elixir-realtime channels"]
8101
+ });
8102
+ if (config.elixirApi === "absinthe" && !hasEcto) incompatibilityError({
8103
+ message: "Absinthe GraphQL requires Ecto in the current generated Phoenix scaffold.",
8104
+ provided: {
8105
+ "elixir-api": "absinthe",
8106
+ "elixir-orm": config.elixirOrm ?? "none"
8107
+ },
8108
+ suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-api rest"]
8109
+ });
8110
+ }
8111
+ function validateEmailConstraints(config) {
8112
+ if (!config.email || config.email === "none") return;
8113
+ if (config.ecosystem !== "typescript" && config.email !== "resend") incompatibilityError({
8114
+ message: "Only Resend email is available for non-TypeScript ecosystems.",
8115
+ provided: {
8116
+ ecosystem: config.ecosystem ?? "typescript",
8117
+ email: config.email
8118
+ },
8119
+ suggestions: ["Use --email resend", "Use --email none"]
8120
+ });
8121
+ if (config.ecosystem === "java" && config.email === "resend" && config.javaBuildTool === "none") incompatibilityError({
8122
+ message: "Resend email for Java requires Maven or Gradle to manage the SDK dependency.",
8123
+ provided: {
8124
+ "java-build-tool": "none",
8125
+ email: "resend"
8126
+ },
8127
+ suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
8128
+ });
8129
+ }
8130
+ function validateObservabilityConstraints(config) {
8131
+ if (!config.observability || config.observability === "none") return;
8132
+ if (config.ecosystem !== "typescript" && config.observability !== "sentry") incompatibilityError({
8133
+ message: "Only Sentry observability is available for non-TypeScript ecosystems.",
8134
+ provided: {
8135
+ ecosystem: config.ecosystem ?? "typescript",
8136
+ observability: config.observability
8137
+ },
8138
+ suggestions: ["Use --observability sentry", "Use --observability none"]
8139
+ });
8140
+ if (config.ecosystem === "java" && config.observability === "sentry" && config.javaBuildTool === "none") incompatibilityError({
8141
+ message: "Sentry observability for Java requires Maven or Gradle to manage the SDK dependency.",
8142
+ provided: {
8143
+ "java-build-tool": "none",
8144
+ observability: "sentry"
8145
+ },
8146
+ suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
8147
+ });
8148
+ }
8149
+ function validateCachingConstraints(config) {
8150
+ if (!config.caching || config.caching === "none") return;
8151
+ if (config.ecosystem !== "typescript" && config.caching !== "upstash-redis") incompatibilityError({
8152
+ message: "Only Upstash Redis caching is available for non-TypeScript ecosystems.",
8153
+ provided: {
8154
+ ecosystem: config.ecosystem ?? "typescript",
8155
+ caching: config.caching
8156
+ },
8157
+ suggestions: ["Use --caching upstash-redis", "Use --caching none"]
8158
+ });
8159
+ if (config.ecosystem === "java" && config.caching === "upstash-redis" && config.javaBuildTool === "none") incompatibilityError({
8160
+ message: "Upstash Redis caching for Java requires Maven or Gradle to manage the Redis client dependency.",
8161
+ provided: {
8162
+ "java-build-tool": "none",
8163
+ caching: "upstash-redis"
8164
+ },
8165
+ suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
8166
+ });
8167
+ }
8168
+ function validateSearchConstraints(config) {
8169
+ if (!config.search || config.search === "none") return;
8170
+ if (config.ecosystem !== "typescript" && config.search !== "meilisearch") incompatibilityError({
8171
+ message: "Only Meilisearch search is available for non-TypeScript ecosystems.",
8172
+ provided: {
8173
+ ecosystem: config.ecosystem ?? "typescript",
8174
+ search: config.search
8175
+ },
8176
+ suggestions: ["Use --search meilisearch", "Use --search none"]
8177
+ });
8178
+ if (config.ecosystem === "java" && config.search === "meilisearch" && config.javaBuildTool === "none") incompatibilityError({
8179
+ message: "Meilisearch search for Java requires Maven or Gradle to manage the SDK dependency.",
8180
+ provided: {
8181
+ "java-build-tool": "none",
8182
+ search: "meilisearch"
8183
+ },
8184
+ suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
8185
+ });
8186
+ }
6949
8187
  function validateShadcnConstraints(config, providedFlags) {
6950
8188
  const shadcnFlagMap = {
6951
8189
  shadcnBase: "--shadcn-base",
@@ -6966,6 +8204,16 @@ function validateShadcnConstraints(config, providedFlags) {
6966
8204
  suggestions: ["Add --ui-library shadcn-ui to use shadcn customization flags", "Remove the --shadcn-* flags if not using shadcn/ui"]
6967
8205
  });
6968
8206
  }
8207
+ function validatePythonApiConstraints(config) {
8208
+ if (config.ecosystem === "python" && config.pythonApi && config.pythonApi !== "none" && config.pythonWebFramework !== "django") incompatibilityError({
8209
+ message: "Python API frameworks require --python-web-framework django.",
8210
+ provided: {
8211
+ "python-web-framework": config.pythonWebFramework || "none",
8212
+ "python-api": config.pythonApi
8213
+ },
8214
+ suggestions: ["Use --python-web-framework django with --python-api django-rest-framework or django-ninja", "Set --python-api none for FastAPI, Flask, Litestar, or no Python web framework"]
8215
+ });
8216
+ }
6969
8217
  function validateFullConfig(config, providedFlags, options) {
6970
8218
  validateEcosystemAuthCompatibility(config, providedFlags);
6971
8219
  validateDatabaseOrmAuth(config, providedFlags);
@@ -6978,7 +8226,13 @@ function validateFullConfig(config, providedFlags, options) {
6978
8226
  validateBackendConstraints(config, providedFlags, options);
6979
8227
  validateFrontendConstraints(config, providedFlags);
6980
8228
  /* @__PURE__ */ validateApiConstraints(config, options);
8229
+ validatePythonApiConstraints(config);
8230
+ validateEmailConstraints(config);
8231
+ validateObservabilityConstraints(config);
8232
+ validateCachingConstraints(config);
8233
+ validateSearchConstraints(config);
6981
8234
  validateJavaConstraints(config, providedFlags);
8235
+ validateElixirConstraints(config);
6982
8236
  validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
6983
8237
  validateSelfBackendCompatibility(providedFlags, options, config);
6984
8238
  validateWorkersCompatibility(providedFlags, options, config);
@@ -7014,7 +8268,13 @@ function validateConfigForProgrammaticUse(config) {
7014
8268
  validateDatabaseOrmAuth(config);
7015
8269
  if (config.frontend && config.frontend.length > 0) ensureSingleWebAndNative(config.frontend);
7016
8270
  validateApiFrontendCompatibility(config.api, config.frontend, config.astroIntegration);
8271
+ validatePythonApiConstraints(config);
8272
+ validateEmailConstraints(config);
8273
+ validateObservabilityConstraints(config);
8274
+ validateCachingConstraints(config);
8275
+ validateSearchConstraints(config);
7017
8276
  validateJavaConstraints(config);
8277
+ validateElixirConstraints(config);
7018
8278
  validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
7019
8279
  if (config.addons && config.addons.length > 0) validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth, config.backend, config.runtime, config.ecosystem, config.rustFrontend, config.javaWebFramework, config.database);
7020
8280
  validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
@@ -8387,6 +9647,10 @@ async function displayPostInstallInstructions(config) {
8387
9647
  displayPythonInstructions(config);
8388
9648
  return;
8389
9649
  }
9650
+ if (ecosystem === "elixir") {
9651
+ displayElixirInstructions(config);
9652
+ return;
9653
+ }
8390
9654
  const isConvex = backend === "convex";
8391
9655
  const isBackendSelf = backend === "self";
8392
9656
  const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : packageManager === "yarn" ? "yarn" : "bun run";
@@ -8550,7 +9814,7 @@ function getBunWebNativeWarning() {
8550
9814
  }
8551
9815
  function getClerkInstructions(backend, frontend) {
8552
9816
  if (backend === "convex") return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Follow the guide: ${pc.underline("https://docs.convex.dev/auth/clerk")}\n${pc.cyan("•")} Set CLERK_JWT_ISSUER_DOMAIN in Convex Dashboard\n${pc.cyan("•")} Set CLERK_PUBLISHABLE_KEY in apps/*/.env`;
8553
- if (backend === "self" && (frontend.includes("next") || frontend.includes("tanstack-start"))) {
9817
+ if (backend === "self" && (frontend.includes("next") || frontend.includes("vinext") || frontend.includes("tanstack-start"))) {
8554
9818
  const publishableKeyVar = frontend.includes("next") ? "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY" : "VITE_CLERK_PUBLISHABLE_KEY";
8555
9819
  return `${pc.bold("Clerk Authentication Setup:")}\n${pc.cyan("•")} Create an application in ${pc.underline("https://dashboard.clerk.com/")}\n${pc.cyan("•")} Set ${publishableKeyVar} in ${pc.white("apps/web/.env")}\n${pc.cyan("•")} Set CLERK_SECRET_KEY in ${pc.white("apps/web/.env")}\n${pc.cyan("•")} Clerk middleware and a protected dashboard route are already generated`;
8556
9820
  }
@@ -8688,7 +9952,8 @@ function displayGoInstructions(config) {
8688
9952
  if (goApi && goApi !== "none") output += `${pc.cyan("•")} API: ${{ "grpc-go": "gRPC-Go" }[goApi] || goApi}\n`;
8689
9953
  if (goCli && goCli !== "none") output += `${pc.cyan("•")} CLI: ${{
8690
9954
  cobra: "Cobra",
8691
- bubbletea: "Bubble Tea"
9955
+ bubbletea: "Bubble Tea",
9956
+ "urfave-cli": "urfave/cli"
8692
9957
  }[goCli] || goCli}\n`;
8693
9958
  if (goLogging && goLogging !== "none") output += `${pc.cyan("•")} Logging: ${{
8694
9959
  zap: "Zap",
@@ -8790,6 +10055,7 @@ function displayJavaInstructions(config) {
8790
10055
  const { projectName, relativePath, depsInstalled, javaWebFramework, javaBuildTool, javaOrm, javaAuth, javaLibraries, javaTestingLibraries } = config;
8791
10056
  const cdCmd = `cd ${relativePath}`;
8792
10057
  const isSpringBoot = javaWebFramework === "spring-boot" && javaBuildTool !== "none";
10058
+ const isQuarkus = javaWebFramework === "quarkus" && javaBuildTool !== "none";
8793
10059
  const rawJavaLibraries = isSpringBoot ? javaLibraries.filter((library) => library !== "none") : [];
8794
10060
  const rawJavaLibrarySet = new Set(rawJavaLibraries);
8795
10061
  const jpaRequiredJavaLibraries = new Set(["flyway", "liquibase"]);
@@ -8801,7 +10067,7 @@ function displayJavaInstructions(config) {
8801
10067
  }
8802
10068
  const effectiveJavaTestingLibraries = javaBuildTool === "none" ? [] : javaTestingLibraries.filter((library) => library !== "none");
8803
10069
  const buildToolCommand = javaBuildTool === "none" ? null : javaBuildTool === "gradle" ? process.platform === "win32" ? "gradlew.bat" : "./gradlew" : process.platform === "win32" ? "mvnw.cmd" : "./mvnw";
8804
- const runCommand = buildToolCommand ? isSpringBoot ? javaBuildTool === "gradle" ? `${buildToolCommand} bootRun` : `${buildToolCommand} spring-boot:run` : javaBuildTool === "gradle" ? `${buildToolCommand} run` : `${buildToolCommand} exec:java` : null;
10070
+ const runCommand = buildToolCommand ? isSpringBoot ? javaBuildTool === "gradle" ? `${buildToolCommand} bootRun` : `${buildToolCommand} spring-boot:run` : isQuarkus ? javaBuildTool === "gradle" ? `${buildToolCommand} quarkusDev` : `${buildToolCommand} quarkus:dev` : javaBuildTool === "gradle" ? `${buildToolCommand} run` : `${buildToolCommand} exec:java` : null;
8805
10071
  const packageCommand = buildToolCommand ? javaBuildTool === "gradle" ? `${buildToolCommand} build` : `${buildToolCommand} package` : null;
8806
10072
  const sourceCompileCommand = buildToolCommand ? null : `javac -d out ${getJavaMainSourcePath(projectName)}`;
8807
10073
  const sourceRunCommand = buildToolCommand ? null : `java -cp out ${getJavaMainClass(projectName)}`;
@@ -8814,7 +10080,10 @@ function displayJavaInstructions(config) {
8814
10080
  output += `${pc.cyan(`${stepCounter++}.`)} ${sourceRunCommand}\n`;
8815
10081
  } else output += `${pc.cyan(`${stepCounter++}.`)} Add Maven or Gradle, then run the app\n`;
8816
10082
  output += `\n${pc.bold("Your Java project includes:")}\n`;
8817
- if (isSpringBoot) output += `${pc.cyan("•")} Web Framework: ${{ "spring-boot": "Spring Boot" }[javaWebFramework] || javaWebFramework}\n`;
10083
+ if (isSpringBoot || isQuarkus) output += `${pc.cyan("•")} Web Framework: ${{
10084
+ "spring-boot": "Spring Boot",
10085
+ quarkus: "Quarkus"
10086
+ }[javaWebFramework] || javaWebFramework}\n`;
8818
10087
  else output += `${pc.cyan("•")} Scaffold: Plain Java\n`;
8819
10088
  if (javaBuildTool && javaBuildTool !== "none") output += `${pc.cyan("•")} Build Tool: ${{
8820
10089
  maven: "Maven Wrapper",
@@ -8871,7 +10140,7 @@ function displayJavaInstructions(config) {
8871
10140
  consola$1.box(output);
8872
10141
  }
8873
10142
  function displayPythonInstructions(config) {
8874
- const { relativePath, depsInstalled, pythonWebFramework, pythonOrm, pythonValidation, pythonAi, pythonTaskQueue, pythonQuality } = config;
10143
+ const { relativePath, depsInstalled, pythonWebFramework, pythonOrm, pythonValidation, pythonAi, pythonApi, pythonTaskQueue, pythonQuality } = config;
8875
10144
  const cdCmd = `cd ${relativePath}`;
8876
10145
  let runCommand = "uv run uvicorn app.main:app --reload";
8877
10146
  if (pythonWebFramework === "django") runCommand = "uv run python manage.py runserver";
@@ -8906,14 +10175,25 @@ function displayPythonInstructions(config) {
8906
10175
  const aiList = pythonAi.filter((ai) => ai !== "none").map((ai) => aiNames[ai] || ai).join(", ");
8907
10176
  output += `${pc.cyan("•")} AI: ${aiList}\n`;
8908
10177
  }
10178
+ if (pythonApi && pythonApi !== "none") output += `${pc.cyan("•")} API Framework: ${{
10179
+ "django-rest-framework": "Django REST Framework",
10180
+ "django-ninja": "Django Ninja"
10181
+ }[pythonApi] || pythonApi}\n`;
8909
10182
  if (pythonTaskQueue && pythonTaskQueue !== "none") output += `${pc.cyan("•")} Task Queue: ${{ celery: "Celery" }[pythonTaskQueue] || pythonTaskQueue}\n`;
8910
- if (pythonQuality && pythonQuality !== "none") output += `${pc.cyan("•")} Code Quality: ${{ ruff: "Ruff" }[pythonQuality] || pythonQuality}\n`;
10183
+ if (pythonQuality && pythonQuality !== "none") output += `${pc.cyan("•")} Code Quality: ${{
10184
+ ruff: "Ruff",
10185
+ mypy: "mypy",
10186
+ pyright: "Pyright"
10187
+ }[pythonQuality] || pythonQuality}\n`;
8911
10188
  output += `\n${pc.bold("Common Python commands:")}\n`;
8912
10189
  output += `${pc.cyan("•")} Install: uv sync\n`;
8913
10190
  output += `${pc.cyan("•")} Run: ${runCommand}\n`;
8914
10191
  output += `${pc.cyan("•")} Test: uv run pytest\n`;
8915
- output += `${pc.cyan("")} Format: uv run ruff format .\n`;
8916
- output += `${pc.cyan("•")} Lint: uv run ruff check .\n`;
10192
+ if (pythonQuality === "ruff") {
10193
+ output += `${pc.cyan("•")} Format: uv run ruff format .\n`;
10194
+ output += `${pc.cyan("•")} Lint: uv run ruff check .\n`;
10195
+ } else if (pythonQuality === "mypy") output += `${pc.cyan("•")} Type check: uv run mypy src/app tests\n`;
10196
+ else if (pythonQuality === "pyright") output += `${pc.cyan("•")} Type check: uv run pyright\n`;
8917
10197
  output += `\n${pc.bold("Your project will be available at:")}\n`;
8918
10198
  output += `${pc.cyan("•")} API: http://localhost:8000\n`;
8919
10199
  output += `\n${pc.bold("Enjoying Better Fullstack?")} Help us grow — star the repo!\n`;
@@ -8921,6 +10201,36 @@ function displayPythonInstructions(config) {
8921
10201
  output += pc.dim("Your star helps other developers discover the project.");
8922
10202
  consola$1.box(output);
8923
10203
  }
10204
+ function displayElixirInstructions(config) {
10205
+ const { relativePath, depsInstalled, elixirWebFramework, elixirOrm, elixirApi, elixirRealtime, elixirJobs, elixirAuth, elixirDeploy } = config;
10206
+ let output = `${pc.bold("Project created successfully!")}\n\n`;
10207
+ output += `${pc.bold("Next steps:")}\n`;
10208
+ output += `${pc.cyan("1.")} cd ${relativePath}\n`;
10209
+ if (!depsInstalled) {
10210
+ output += `${pc.cyan("2.")} mix deps.get\n`;
10211
+ output += `${pc.cyan("3.")} mix ecto.setup\n`;
10212
+ output += `${pc.cyan("4.")} mix phx.server\n`;
10213
+ } else {
10214
+ output += `${pc.cyan("2.")} mix ecto.setup\n`;
10215
+ output += `${pc.cyan("3.")} mix phx.server\n`;
10216
+ }
10217
+ output += `\n${pc.bold("Selected Elixir stack:")}\n`;
10218
+ output += `${pc.cyan("•")} Web: ${elixirWebFramework}\n`;
10219
+ output += `${pc.cyan("•")} Database: ${elixirOrm}\n`;
10220
+ output += `${pc.cyan("•")} API: ${elixirApi}\n`;
10221
+ output += `${pc.cyan("•")} Realtime: ${elixirRealtime}\n`;
10222
+ output += `${pc.cyan("•")} Jobs: ${elixirJobs}\n`;
10223
+ output += `${pc.cyan("•")} Auth: ${elixirAuth}\n`;
10224
+ output += `${pc.cyan("•")} Deploy: ${elixirDeploy}\n`;
10225
+ output += `\n${pc.bold("Common Mix commands:")}\n`;
10226
+ output += `${pc.cyan("•")} Run: mix phx.server\n`;
10227
+ output += `${pc.cyan("•")} Test: mix test\n`;
10228
+ output += `${pc.cyan("•")} Format: mix format\n`;
10229
+ output += `${pc.cyan("•")} Compile: mix compile\n`;
10230
+ output += `\n${pc.bold("Your Phoenix app will be available at:")}\n`;
10231
+ output += `${pc.cyan("•")} Web: http://localhost:4000\n`;
10232
+ consola$1.box(output);
10233
+ }
8924
10234
 
8925
10235
  //#endregion
8926
10236
  //#region src/helpers/core/create-project.ts
@@ -8943,7 +10253,7 @@ async function createProject(options, cliInput = {}) {
8943
10253
  await writeBtsConfig(options);
8944
10254
  await formatProject(projectDir);
8945
10255
  if (!isSilent()) log.success("Project template successfully scaffolded!");
8946
- if (options.install && options.ecosystem === "typescript") await installDependencies({
10256
+ if (options.install && (options.ecosystem === "typescript" || options.ecosystem === "react-native")) await installDependencies({
8947
10257
  projectDir,
8948
10258
  packageManager: options.packageManager
8949
10259
  });
@@ -8952,6 +10262,7 @@ async function createProject(options, cliInput = {}) {
8952
10262
  if (options.install && options.ecosystem === "go") await runGoModTidy({ projectDir });
8953
10263
  if (options.install && options.ecosystem === "java" && options.javaBuildTool !== "none") if (options.javaBuildTool === "gradle") await runGradleTests({ projectDir });
8954
10264
  else await runMavenTests({ projectDir });
10265
+ if (options.install && options.ecosystem === "elixir") await runMixCompile({ projectDir });
8955
10266
  await initializeGit(projectDir, options.git);
8956
10267
  if (!isSilent()) await displayPostInstallInstructions({
8957
10268
  ...options,
@@ -8992,6 +10303,51 @@ async function ensurePackageManagerProjectFiles(projectDir, packageManager) {
8992
10303
 
8993
10304
  //#endregion
8994
10305
  //#region src/helpers/core/command-handlers.ts
10306
+ function getYesBaseConfig(flagConfig) {
10307
+ const baseConfig = getDefaultConfig();
10308
+ if (flagConfig.ecosystem !== "react-native") return baseConfig;
10309
+ return {
10310
+ ...baseConfig,
10311
+ backend: "none",
10312
+ runtime: "none",
10313
+ frontend: ["native-bare"],
10314
+ addons: [],
10315
+ examples: [],
10316
+ auth: "none",
10317
+ payments: "none",
10318
+ email: "none",
10319
+ fileUpload: "none",
10320
+ effect: "none",
10321
+ dbSetup: "none",
10322
+ api: "none",
10323
+ webDeploy: "none",
10324
+ serverDeploy: "none",
10325
+ cssFramework: "none",
10326
+ uiLibrary: "none",
10327
+ stateManagement: "none",
10328
+ forms: "none",
10329
+ testing: "none",
10330
+ realtime: "none",
10331
+ jobQueue: "none",
10332
+ animation: "none",
10333
+ logging: "none",
10334
+ observability: "none",
10335
+ featureFlags: "none",
10336
+ analytics: "none",
10337
+ cms: "none",
10338
+ caching: "none",
10339
+ i18n: "none",
10340
+ search: "none",
10341
+ fileStorage: "none",
10342
+ mobileNavigation: "expo-router",
10343
+ mobileUI: "none",
10344
+ mobileStorage: "none",
10345
+ mobileTesting: "none",
10346
+ mobilePush: "none",
10347
+ mobileOTA: "none",
10348
+ mobileDeepLinking: "none"
10349
+ };
10350
+ }
8995
10351
  function shouldPromptForVersionChannel(input) {
8996
10352
  if (input.yes || input.versionChannel !== void 0 || isSilent()) return false;
8997
10353
  return canPromptInteractively();
@@ -9094,11 +10450,19 @@ async function createProjectHandler(input, options = {}) {
9094
10450
  featureFlags: "none",
9095
10451
  analytics: "none",
9096
10452
  fileStorage: "none",
10453
+ mobileNavigation: "none",
10454
+ mobileUI: "none",
10455
+ mobileStorage: "none",
10456
+ mobileTesting: "none",
10457
+ mobilePush: "none",
10458
+ mobileOTA: "none",
10459
+ mobileDeepLinking: "none",
9097
10460
  pythonWebFramework: "none",
9098
10461
  pythonOrm: "none",
9099
10462
  pythonValidation: "none",
9100
10463
  pythonAi: [],
9101
10464
  pythonAuth: "none",
10465
+ pythonApi: "none",
9102
10466
  pythonTaskQueue: "none",
9103
10467
  pythonGraphql: "none",
9104
10468
  pythonQuality: "none",
@@ -9114,6 +10478,21 @@ async function createProjectHandler(input, options = {}) {
9114
10478
  javaAuth: "none",
9115
10479
  javaLibraries: [],
9116
10480
  javaTestingLibraries: [],
10481
+ elixirWebFramework: "none",
10482
+ elixirOrm: "none",
10483
+ elixirAuth: "none",
10484
+ elixirApi: "none",
10485
+ elixirRealtime: "none",
10486
+ elixirJobs: "none",
10487
+ elixirValidation: "none",
10488
+ elixirHttp: "none",
10489
+ elixirJson: "none",
10490
+ elixirEmail: "none",
10491
+ elixirCaching: "none",
10492
+ elixirObservability: "none",
10493
+ elixirTesting: "none",
10494
+ elixirQuality: "none",
10495
+ elixirDeploy: "none",
9117
10496
  aiDocs: []
9118
10497
  },
9119
10498
  reproducibleCommand: "",
@@ -9158,7 +10537,7 @@ async function createProjectHandler(input, options = {}) {
9158
10537
  if (cliInput.yes) {
9159
10538
  const flagConfig = processProvidedFlagsWithoutValidation(cliInput, finalBaseName);
9160
10539
  config = {
9161
- ...getDefaultConfig(),
10540
+ ...getYesBaseConfig(flagConfig),
9162
10541
  ...flagConfig,
9163
10542
  projectName: finalBaseName,
9164
10543
  projectDir: finalResolvedPath,
@@ -9555,17 +10934,21 @@ async function history(options) {
9555
10934
  */
9556
10935
  async function createVirtual(options) {
9557
10936
  try {
10937
+ const ecosystem = options.ecosystem || "typescript";
10938
+ const isReactNative = ecosystem === "react-native";
10939
+ const frontend = options.frontend || (isReactNative ? ["native-bare"] : ["tanstack-router"]);
10940
+ const hasNativeFrontend = frontend.some((item) => item === "native-bare" || item === "native-uniwind" || item === "native-unistyles");
9558
10941
  const result = await generateVirtualProject$1({
9559
10942
  config: {
10943
+ ecosystem,
9560
10944
  projectName: options.projectName || "my-project",
9561
10945
  projectDir: "/virtual",
9562
10946
  relativePath: "./virtual",
9563
- ecosystem: options.ecosystem || "typescript",
9564
10947
  database: options.database || "none",
9565
10948
  orm: options.orm || "none",
9566
- backend: options.backend || "hono",
9567
- runtime: options.runtime || "bun",
9568
- frontend: options.frontend || ["tanstack-router"],
10949
+ backend: options.backend || (isReactNative ? "none" : "hono"),
10950
+ runtime: options.runtime || (isReactNative ? "none" : "bun"),
10951
+ frontend,
9569
10952
  addons: options.addons || [],
9570
10953
  examples: options.examples || [],
9571
10954
  auth: options.auth || "none",
@@ -9578,11 +10961,11 @@ async function createVirtual(options) {
9578
10961
  versionChannel: options.versionChannel || "stable",
9579
10962
  install: false,
9580
10963
  dbSetup: options.dbSetup || "none",
9581
- api: options.api || "trpc",
10964
+ api: options.api || (isReactNative ? "none" : "trpc"),
9582
10965
  webDeploy: options.webDeploy || "none",
9583
10966
  serverDeploy: options.serverDeploy || "none",
9584
- cssFramework: options.cssFramework || "tailwind",
9585
- uiLibrary: options.uiLibrary || "shadcn-ui",
10967
+ cssFramework: options.cssFramework || (isReactNative ? "none" : "tailwind"),
10968
+ uiLibrary: options.uiLibrary || (isReactNative ? "none" : "shadcn-ui"),
9586
10969
  shadcnBase: options.shadcnBase ?? "radix",
9587
10970
  shadcnStyle: options.shadcnStyle ?? "nova",
9588
10971
  shadcnIconLibrary: options.shadcnIconLibrary ?? "lucide",
@@ -9592,8 +10975,8 @@ async function createVirtual(options) {
9592
10975
  shadcnRadius: options.shadcnRadius ?? "default",
9593
10976
  ai: options.ai || "none",
9594
10977
  stateManagement: options.stateManagement || "none",
9595
- forms: options.forms || "react-hook-form",
9596
- testing: options.testing || "vitest",
10978
+ forms: options.forms || (isReactNative ? "none" : "react-hook-form"),
10979
+ testing: options.testing || (isReactNative ? "none" : "vitest"),
9597
10980
  validation: options.validation || "zod",
9598
10981
  realtime: options.realtime || "none",
9599
10982
  jobQueue: options.jobQueue || "none",
@@ -9602,6 +10985,13 @@ async function createVirtual(options) {
9602
10985
  observability: options.observability || "none",
9603
10986
  featureFlags: options.featureFlags || "none",
9604
10987
  analytics: options.analytics || "none",
10988
+ mobileNavigation: options.mobileNavigation || (hasNativeFrontend ? "expo-router" : "none"),
10989
+ mobileUI: options.mobileUI || "none",
10990
+ mobileStorage: options.mobileStorage || "none",
10991
+ mobileTesting: options.mobileTesting || "none",
10992
+ mobilePush: options.mobilePush || "none",
10993
+ mobileOTA: options.mobileOTA || "none",
10994
+ mobileDeepLinking: options.mobileDeepLinking || (hasNativeFrontend ? "expo-linking" : "none"),
9605
10995
  cms: options.cms || "none",
9606
10996
  caching: options.caching || "none",
9607
10997
  i18n: options.i18n || "none",
@@ -9622,6 +11012,7 @@ async function createVirtual(options) {
9622
11012
  pythonValidation: options.pythonValidation || "none",
9623
11013
  pythonAi: options.pythonAi || [],
9624
11014
  pythonAuth: options.pythonAuth || "none",
11015
+ pythonApi: options.pythonApi || "none",
9625
11016
  pythonTaskQueue: options.pythonTaskQueue || "none",
9626
11017
  pythonGraphql: options.pythonGraphql || "none",
9627
11018
  pythonQuality: options.pythonQuality || "none",
@@ -9637,6 +11028,21 @@ async function createVirtual(options) {
9637
11028
  javaAuth: options.javaAuth || "none",
9638
11029
  javaLibraries: options.javaLibraries || [],
9639
11030
  javaTestingLibraries: options.javaTestingLibraries || (options.ecosystem === "java" ? ["junit5"] : []),
11031
+ elixirWebFramework: options.elixirWebFramework || (options.ecosystem === "elixir" ? "phoenix" : "none"),
11032
+ elixirOrm: options.elixirOrm || (options.ecosystem === "elixir" ? "ecto-sql" : "none"),
11033
+ elixirAuth: options.elixirAuth || "none",
11034
+ elixirApi: options.elixirApi || (options.ecosystem === "elixir" ? "rest" : "none"),
11035
+ elixirRealtime: options.elixirRealtime || (options.ecosystem === "elixir" ? "channels" : "none"),
11036
+ elixirJobs: options.elixirJobs || "none",
11037
+ elixirValidation: options.elixirValidation || (options.ecosystem === "elixir" ? "ecto-changesets" : "none"),
11038
+ elixirHttp: options.elixirHttp || (options.ecosystem === "elixir" ? "req" : "none"),
11039
+ elixirJson: options.elixirJson || (options.ecosystem === "elixir" ? "jason" : "none"),
11040
+ elixirEmail: options.elixirEmail || "none",
11041
+ elixirCaching: options.elixirCaching || "none",
11042
+ elixirObservability: options.elixirObservability || (options.ecosystem === "elixir" ? "telemetry" : "none"),
11043
+ elixirTesting: options.elixirTesting || (options.ecosystem === "elixir" ? "ex_unit" : "none"),
11044
+ elixirQuality: options.elixirQuality || (options.ecosystem === "elixir" ? "credo" : "none"),
11045
+ elixirDeploy: options.elixirDeploy || "none",
9640
11046
  aiDocs: options.aiDocs || ["claude-md"]
9641
11047
  },
9642
11048
  templates: EMBEDDED_TEMPLATES$1