create-better-fullstack 2.1.8 → 2.2.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.
Files changed (26) hide show
  1. package/dist/{add-handler-ztNjzoMN.mjs → add-handler-CE3XIs3y.mjs} +8 -8
  2. package/dist/{addons-setup-LaAj43NP.mjs → addons-setup-DR924M7b.mjs} +1 -1
  3. package/dist/addons-setup-Du2VSv8V.mjs +7 -0
  4. package/dist/{analytics-DVltG11u.mjs → analytics-BXlOG6u6.mjs} +1 -1
  5. package/dist/{bts-config-DQVWvPDs.mjs → bts-config-B_HhcMXL.mjs} +30 -0
  6. package/dist/cli.mjs +2 -2
  7. package/dist/{config-processing-D9-F2Us9.mjs → config-processing-DJisMeW-.mjs} +17 -1
  8. package/dist/{doctor-a4ca3SMd.mjs → doctor-Cc7sLuow.mjs} +2 -2
  9. package/dist/{file-formatter-gvmrpd-g.mjs → file-formatter-BgbfpYf4.mjs} +442 -22
  10. package/dist/{gen-CCClL7Ve.mjs → gen-BZ0QCxCC.mjs} +1 -1
  11. package/dist/{generated-checks-BV9jol5h.mjs → generated-checks-CbioGjCh.mjs} +1 -1
  12. package/dist/index.d.mts +461 -183
  13. package/dist/index.mjs +20 -9
  14. package/dist/{install-dependencies-RoUyaE8o.mjs → install-dependencies-D__bmW4k.mjs} +86 -18
  15. package/dist/{mcp-492OkjcS.mjs → mcp-DNKAo8nO.mjs} +5 -5
  16. package/dist/mcp-entry.mjs +37 -225
  17. package/dist/{registry-DSf2CEaU.mjs → registry-Cm3D45oT.mjs} +1 -1
  18. package/dist/run-C_S7ujfY.mjs +15 -0
  19. package/dist/{run-T6g1jkfy.mjs → run-DmN7QRt5.mjs} +1155 -292
  20. package/dist/scaffold-manifest-ByKtMhTd.mjs +4 -0
  21. package/dist/{scaffold-manifest-Dyi0voqE.mjs → scaffold-manifest-CNFz-lmH.mjs} +34 -5
  22. package/dist/{update-DJ8CI5KW.mjs → update-B270pWHc.mjs} +182 -25
  23. package/package.json +6 -6
  24. package/dist/addons-setup-DnLjAzTw.mjs +0 -7
  25. package/dist/run-CD8f7PHh.mjs +0 -15
  26. package/dist/scaffold-manifest-DGRyepdb.mjs +0 -4
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { b as types_exports } from "./bts-config-DQVWvPDs.mjs";
2
+ import { b as types_exports } from "./bts-config-B_HhcMXL.mjs";
3
3
  import { c as isSilent, i as exitWithError, t as CLIError } from "./errors-ns_o2OKg.mjs";
4
4
  import pc from "picocolors";
5
5
  import z from "zod";
@@ -110,18 +110,24 @@ const CreateCommandOptionsSchema = z.object({
110
110
  pythonRealtime: types_exports.PythonRealtimeSchema.optional().describe("Python realtime (python-socketio, websockets)"),
111
111
  pythonObservability: types_exports.PythonObservabilitySchema.optional().describe("Python observability (opentelemetry)"),
112
112
  pythonCli: z.array(types_exports.PythonCliSchema).optional().describe("Python CLI tooling (typer, click, rich)"),
113
- goWebFramework: types_exports.GoWebFrameworkSchema.optional().describe("Go web framework (gin, echo, fiber)"),
114
- goOrm: types_exports.GoOrmSchema.optional().describe("Go ORM/database (gorm, sqlc)"),
115
- goApi: types_exports.GoApiSchema.optional().describe("Go API layer (grpc-go)"),
113
+ goWebFramework: types_exports.GoWebFrameworkSchema.optional().describe("Go web framework"),
114
+ goOrm: types_exports.GoOrmSchema.optional().describe("Go ORM/database layer"),
115
+ goApi: types_exports.GoApiSchema.optional().describe("Go API layer"),
116
116
  goCli: types_exports.GoCliSchema.optional().describe("Go CLI tools (cobra, bubbletea, urfave-cli)"),
117
117
  goLogging: types_exports.GoLoggingSchema.optional().describe("Go logging (zap, zerolog, slog)"),
118
- goAuth: types_exports.GoAuthSchema.optional().describe("Go auth (casbin, jwt, goth)"),
119
- goTesting: z.array(types_exports.GoTestingSchema).optional().describe("Go testing libraries (testify, gomock)"),
118
+ goAuth: types_exports.GoAuthSchema.optional().describe("Go authentication library"),
119
+ goTesting: z.array(types_exports.GoTestingSchema).optional().describe("Go testing libraries"),
120
120
  goRealtime: types_exports.GoRealtimeSchema.optional().describe("Go realtime library (gorilla-websocket, centrifuge)"),
121
- goMessageQueue: types_exports.GoMessageQueueSchema.optional().describe("Go message queue (nats, watermill)"),
121
+ goMessageQueue: types_exports.GoMessageQueueSchema.optional().describe("Go message queue"),
122
122
  goCaching: types_exports.GoCachingSchema.optional().describe("Go caching library (redis, ristretto)"),
123
123
  goConfig: types_exports.GoConfigSchema.optional().describe("Go config management (viper, koanf)"),
124
- goObservability: types_exports.GoObservabilitySchema.optional().describe("Go observability (opentelemetry)"),
124
+ goObservability: types_exports.GoObservabilitySchema.optional().describe("Go observability"),
125
+ goValidation: types_exports.GoValidationSchema.optional().describe("Go validation"),
126
+ goQuality: types_exports.GoQualitySchema.optional().describe("Go code quality"),
127
+ goMigrations: types_exports.GoMigrationsSchema.optional().describe("Go database migrations"),
128
+ goTemplating: types_exports.GoTemplatingSchema.optional().describe("Go templating"),
129
+ goProtoTooling: types_exports.GoProtoToolingSchema.optional().describe("Go protobuf tooling"),
130
+ goDI: types_exports.GoDISchema.optional().describe("Go dependency injection"),
125
131
  javaWebFramework: types_exports.JavaWebFrameworkSchema.optional().describe("Java web framework (spring-boot, quarkus, none)"),
126
132
  javaLanguage: types_exports.JavaLanguageSchema.optional().describe("JVM language (java, kotlin)"),
127
133
  javaBuildTool: types_exports.JavaBuildToolSchema.optional().describe("Java build tool (maven, gradle, none)"),
@@ -156,6 +162,11 @@ const CreateCommandOptionsSchema = z.object({
156
162
  elixirObservability: types_exports.ElixirObservabilitySchema.optional().describe("Elixir observability (telemetry, opentelemetry, prom_ex, none)"),
157
163
  elixirTesting: types_exports.ElixirTestingSchema.optional().describe("Elixir testing (ex_unit, mox, bypass, wallaby, none)"),
158
164
  elixirQuality: types_exports.ElixirQualitySchema.optional().describe("Elixir code quality (credo, dialyxir, sobelow, none)"),
165
+ elixirI18n: types_exports.ElixirI18nSchema.optional().describe("Elixir localization (gettext, none)"),
166
+ elixirHttpServer: types_exports.ElixirHttpServerSchema.optional().describe("Elixir HTTP server (bandit, cowboy, none)"),
167
+ elixirApplicationFramework: types_exports.ElixirApplicationFrameworkSchema.optional().describe("Elixir application framework (ash, none)"),
168
+ elixirDocumentation: types_exports.ElixirDocumentationSchema.optional().describe("Elixir documentation tooling (ex_doc, none)"),
169
+ elixirClustering: types_exports.ElixirClusteringSchema.optional().describe("Elixir clustering (libcluster, none)"),
159
170
  elixirDeploy: types_exports.ElixirDeploySchema.optional().describe("Elixir deploy target (docker, fly, gigalixir, mix-release, none)"),
160
171
  elixirLibraries: z.array(types_exports.ElixirLibrariesSchema).optional().describe("Elixir libraries (broadway, nx)"),
161
172
  aiDocs: z.array(types_exports.AiDocsSchema).optional().describe("AI documentation files (claude-md, agents-md, cursorrules)")
@@ -350,9 +361,25 @@ function validateWebDeployFrontendTemplates(webDeploy, frontends = []) {
350
361
  function validateServerDeployRequiresBackend(serverDeploy, backend, hasGraphBackend = false) {
351
362
  if (serverDeploy && serverDeploy !== "none" && !hasGraphBackend && (!backend || backend === "none")) exitWithError("'--server-deploy' requires a backend. Please select a backend or set '--server-deploy none'.");
352
363
  }
353
- function validateAddonCompatibility$1(addon, frontend, _auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database) {
364
+ function validateAddonCompatibility$1(addon, frontend, _auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database, api) {
354
365
  const baseCompatibility = validateAddonCompatibility(addon, frontend, _auth);
355
366
  if (!baseCompatibility.isCompatible) return baseCompatibility;
367
+ if ((addon === "graphql-codegen" || addon === "apollo-client") && api !== void 0 && ![
368
+ "garph",
369
+ "graphql-yoga",
370
+ "apollo-server"
371
+ ].includes(api) && !(addon === "graphql-codegen" && frontend.includes("redwood"))) return {
372
+ isCompatible: false,
373
+ reason: `${addon} requires a GraphQL API selection`
374
+ };
375
+ if (addon === "openapi-typescript" && api !== void 0 && api !== "openapi") return {
376
+ isCompatible: false,
377
+ reason: "openapi-typescript requires the OpenAPI API selection"
378
+ };
379
+ if (addon === "openapi-typescript" && backend === "self") return {
380
+ isCompatible: false,
381
+ reason: "openapi-typescript requires a standalone backend that exposes an OpenAPI schema"
382
+ };
356
383
  if (addon === "backend-utils") {
357
384
  if (ecosystem !== void 0 && ecosystem !== "typescript") return {
358
385
  isCompatible: false,
@@ -407,22 +434,24 @@ function validateAddonCompatibility$1(addon, frontend, _auth, backend, runtime,
407
434
  }
408
435
  return { isCompatible: true };
409
436
  }
410
- function getCompatibleAddons$1(allAddons, frontend, existingAddons = [], auth, backend, runtime) {
437
+ function getCompatibleAddons$1(allAddons, frontend, existingAddons = [], auth, backend, runtime, api) {
411
438
  return getCompatibleAddons(allAddons, frontend, existingAddons, auth).filter((addon) => {
412
- const { isCompatible } = validateAddonCompatibility$1(addon, frontend, auth, backend, runtime);
439
+ const { isCompatible } = validateAddonCompatibility$1(addon, frontend, auth, backend, runtime, void 0, void 0, void 0, void 0, api);
413
440
  return isCompatible;
414
441
  });
415
442
  }
416
- function validateAddonsAgainstFrontends(addons = [], frontends = [], auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database) {
443
+ function validateAddonsAgainstFrontends(addons = [], frontends = [], auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database, api) {
417
444
  if (addons.includes("nx") && addons.includes("turborepo")) exitWithError("Nx and Turborepo are alternative workspace runners. Choose one addon.");
418
445
  for (const addon of addons) {
419
446
  if (addon === "none") continue;
420
- const { isCompatible, reason } = validateAddonCompatibility$1(addon, frontends, auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database);
447
+ const { isCompatible, reason } = validateAddonCompatibility$1(addon, frontends, auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database, api);
421
448
  if (!isCompatible) exitWithError(`Incompatible addon/frontend combination: ${reason}`);
422
449
  }
423
450
  }
424
- function validatePaymentsCompatibility(payments, auth, _backend, frontends = []) {
451
+ function validatePaymentsCompatibility(payments, auth, backend, frontends = []) {
425
452
  if (!payments || payments === "none") return;
453
+ if (payments === "paypal" && !frontends.some((frontend) => frontend !== "none" && isWebFrontend$1(frontend))) exitWithError("PayPal requires a web frontend. Please choose a web frontend or a different payments provider.");
454
+ if (payments === "paypal" && (backend === "none" || backend === "convex")) exitWithError("PayPal checkout requires a standalone or fullstack backend. Please choose a server backend or a different payments provider.");
426
455
  if (payments === "dodo" && frontends.includes("react-vite")) exitWithError("Dodo Payments are not yet supported for React + Vite projects.");
427
456
  if (payments === "polar") {
428
457
  if (!auth || auth === "none" || auth !== "better-auth" && auth !== "better-auth-organizations") exitWithError("Polar payments requires Better Auth. Please use '--auth better-auth' or choose a different payments provider.");
@@ -471,6 +500,25 @@ function validateAIFrontendCompatibility(ai, frontends = []) {
471
500
  if (!issue) return;
472
501
  exitWithError(issue.message);
473
502
  }
503
+ function validateAIBackendCompatibility(ai, backend) {
504
+ if (ai && ["openai-sdk", "anthropic-sdk"].includes(ai) && (backend === "none" || backend === "convex")) exitWithError("Direct AI provider SDKs require a standalone or fullstack backend. Please choose a server backend or another AI integration.");
505
+ }
506
+ function validateRealtimeCompatibility(realtime, backend) {
507
+ if (realtime === "ws" && backend !== "express") exitWithError("The ws integration is currently wired for the Express backend.");
508
+ }
509
+ function validateCSSFrameworkFrontendCompatibility(cssFramework, frontends = []) {
510
+ if (cssFramework !== "styled-components") return;
511
+ const reactFrontends = [
512
+ "tanstack-router",
513
+ "react-router",
514
+ "react-vite",
515
+ "tanstack-start",
516
+ "next",
517
+ "vinext",
518
+ "redwood"
519
+ ];
520
+ if (!frontends.some((frontend) => reactFrontends.includes(frontend))) exitWithError("styled-components requires a React frontend.");
521
+ }
474
522
  /**
475
523
  * Validates that a UI library is compatible with the selected frontend(s)
476
524
  */
@@ -521,8 +569,8 @@ function getCompatibleUILibraries$1(frontends = [], astroIntegration) {
521
569
  /**
522
570
  * Gets list of CSS frameworks compatible with the selected UI library
523
571
  */
524
- function getCompatibleCSSFrameworks$1(uiLibrary) {
525
- return getCompatibleCSSFrameworks(uiLibrary);
572
+ function getCompatibleCSSFrameworks$1(uiLibrary, frontends = []) {
573
+ return getCompatibleCSSFrameworks(uiLibrary, frontends);
526
574
  }
527
575
  /**
528
576
  * Checks if a frontend has web styling (excludes native-only frontends)
@@ -530,6 +578,328 @@ function getCompatibleCSSFrameworks$1(uiLibrary) {
530
578
  function hasWebStyling$1(frontends = []) {
531
579
  return hasWebStyling(frontends);
532
580
  }
581
+ function validateRustExpansionCompatibility(config) {
582
+ if (config.ecosystem !== "rust") return;
583
+ const framework = config.rustWebFramework ?? "none";
584
+ const api = config.rustApi ?? "none";
585
+ const auth = config.rustAuth ?? "none";
586
+ if ((framework === "warp" || framework === "salvo") && !["none", "jsonrpsee"].includes(api)) incompatibilityError({
587
+ message: "Warp and Salvo currently support REST or the standalone jsonrpsee server.",
588
+ provided: {
589
+ "rust-web-framework": framework,
590
+ "rust-api": api
591
+ },
592
+ suggestions: ["Use --rust-api jsonrpsee or --rust-api none", "Use Axum, Actix Web, Rocket, or Poem for Tonic/async-graphql"]
593
+ });
594
+ if (framework === "loco" && api === "jsonrpsee") incompatibilityError({
595
+ message: "Loco owns the server boot sequence and cannot start the generated jsonrpsee server.",
596
+ provided: {
597
+ "rust-web-framework": framework,
598
+ "rust-api": api
599
+ },
600
+ suggestions: ["Use --rust-api none", "Choose Axum, Actix Web, Rocket, Poem, Warp, or Salvo"]
601
+ });
602
+ if (auth === "tower-sessions" && framework !== "axum") incompatibilityError({
603
+ message: "The generated tower-sessions middleware is wired specifically for Axum.",
604
+ provided: {
605
+ "rust-web-framework": framework,
606
+ "rust-auth": auth
607
+ },
608
+ suggestions: ["Use --rust-web-framework axum", "Choose --rust-auth openidconnect or none"]
609
+ });
610
+ }
611
+
612
+ //#endregion
613
+ //#region src/utils/stack-compatibility.ts
614
+ function asString(value, fallback = "none") {
615
+ return typeof value === "string" ? value : fallback;
616
+ }
617
+ function asStringArray(value) {
618
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
619
+ }
620
+ function getCompatibilityBackend(config, webFrontend) {
621
+ if (config.backend !== "self") return asString(config.backend, "hono");
622
+ if (webFrontend.includes("next")) return "self-next";
623
+ if (webFrontend.includes("vinext")) return "self-vinext";
624
+ if (webFrontend.includes("tanstack-start")) return "self-tanstack-start";
625
+ if (webFrontend.includes("astro")) return "self-astro";
626
+ if (webFrontend.includes("nuxt")) return "self-nuxt";
627
+ if (webFrontend.includes("svelte")) return "self-svelte";
628
+ if (webFrontend.includes("solid-start")) return "self-solid-start";
629
+ return "self";
630
+ }
631
+ function getProjectBackendFromCompatibility(backend) {
632
+ return backend.startsWith("self-") ? "self" : backend;
633
+ }
634
+ function hasSelectedTypeScriptBackendPart(config) {
635
+ return config.stackParts?.some((part) => part.source !== "provided" && !part.ownerPartId && part.role === "backend" && part.ecosystem === "typescript" && part.toolId !== "none") ?? false;
636
+ }
637
+ function getCompatibilityEcosystem(config) {
638
+ if (config.ecosystem === "react-native" && hasSelectedTypeScriptBackendPart(config)) return "typescript";
639
+ return config.ecosystem ?? "typescript";
640
+ }
641
+ function buildCompatibilityInputFromConfig(config) {
642
+ const frontend = asStringArray(config.frontend);
643
+ const addons = asStringArray(config.addons);
644
+ const webFrontend = frontend.filter((item) => !item.startsWith("native-") && item !== "none");
645
+ const nativeFrontend = frontend.filter((item) => item.startsWith("native-"));
646
+ const codeQuality = [];
647
+ const documentation = [];
648
+ const appPlatforms = [];
649
+ for (const addon of addons) {
650
+ const binding = (0, types_exports.getAddonStackPartBinding)(addon);
651
+ if (binding?.role === "codeQuality") codeQuality.push(addon);
652
+ else if (binding?.role === "documentation") documentation.push(addon);
653
+ else if (addon !== "none") appPlatforms.push(addon);
654
+ }
655
+ return {
656
+ ecosystem: getCompatibilityEcosystem(config),
657
+ projectName: config.projectName ?? null,
658
+ webFrontend,
659
+ nativeFrontend,
660
+ astroIntegration: asString(config.astroIntegration),
661
+ runtime: asString(config.runtime, "bun"),
662
+ backend: getCompatibilityBackend(config, webFrontend),
663
+ database: asString(config.database),
664
+ orm: asString(config.orm),
665
+ dbSetup: asString(config.dbSetup),
666
+ auth: asString(config.auth),
667
+ payments: asString(config.payments),
668
+ email: asString(config.email),
669
+ fileUpload: asString(config.fileUpload),
670
+ logging: asString(config.logging),
671
+ observability: asString(config.observability),
672
+ featureFlags: asString(config.featureFlags),
673
+ analytics: asString(config.analytics),
674
+ backendLibraries: asString(config.effect),
675
+ stateManagement: asString(config.stateManagement),
676
+ forms: asString(config.forms),
677
+ validation: asString(config.validation),
678
+ testing: asString(config.testing),
679
+ realtime: asString(config.realtime),
680
+ jobQueue: asString(config.jobQueue),
681
+ caching: asString(config.caching),
682
+ rateLimit: asString(config.rateLimit),
683
+ animation: asString(config.animation),
684
+ cssFramework: asString(config.cssFramework),
685
+ uiLibrary: asString(config.uiLibrary),
686
+ shadcnBase: asString(config.shadcnBase, "radix"),
687
+ shadcnStyle: asString(config.shadcnStyle, "nova"),
688
+ shadcnIconLibrary: asString(config.shadcnIconLibrary, "lucide"),
689
+ shadcnColorTheme: asString(config.shadcnColorTheme, "neutral"),
690
+ shadcnBaseColor: asString(config.shadcnBaseColor, "neutral"),
691
+ shadcnFont: asString(config.shadcnFont, "inter"),
692
+ shadcnRadius: asString(config.shadcnRadius, "default"),
693
+ cms: asString(config.cms),
694
+ i18n: asString(config.i18n),
695
+ search: asString(config.search),
696
+ vectorDb: asString(config.vectorDb),
697
+ fileStorage: asString(config.fileStorage),
698
+ mobileNavigation: asString(config.mobileNavigation),
699
+ mobileUI: asString(config.mobileUI),
700
+ mobileStorage: asString(config.mobileStorage),
701
+ mobileTesting: asString(config.mobileTesting),
702
+ mobilePush: asString(config.mobilePush),
703
+ mobileOTA: asString(config.mobileOTA),
704
+ mobileDeepLinking: asString(config.mobileDeepLinking),
705
+ codeQuality,
706
+ documentation,
707
+ appPlatforms,
708
+ packageManager: asString(config.packageManager, "bun"),
709
+ workspaceShape: asString(config.workspaceShape, "monorepo"),
710
+ versionChannel: asString(config.versionChannel, "stable"),
711
+ examples: asStringArray(config.examples),
712
+ aiSdk: asString(config.ai),
713
+ aiDocs: asStringArray(config.aiDocs),
714
+ git: "false",
715
+ install: "false",
716
+ api: asString(config.api),
717
+ webDeploy: asString(config.webDeploy),
718
+ serverDeploy: asString(config.serverDeploy),
719
+ yolo: "false",
720
+ rustWebFramework: asString(config.rustWebFramework),
721
+ rustFrontend: asString(config.rustFrontend),
722
+ rustOrm: asString(config.rustOrm),
723
+ rustApi: asString(config.rustApi),
724
+ rustCli: asString(config.rustCli),
725
+ rustLibraries: asStringArray(config.rustLibraries),
726
+ rustLogging: asString(config.rustLogging),
727
+ rustErrorHandling: asString(config.rustErrorHandling),
728
+ rustCaching: asString(config.rustCaching),
729
+ rustAuth: asString(config.rustAuth),
730
+ rustRealtime: asString(config.rustRealtime),
731
+ rustMessageQueue: asString(config.rustMessageQueue),
732
+ rustObservability: asString(config.rustObservability),
733
+ rustTemplating: asString(config.rustTemplating),
734
+ pythonWebFramework: asString(config.pythonWebFramework),
735
+ pythonOrm: asString(config.pythonOrm),
736
+ pythonValidation: asString(config.pythonValidation),
737
+ pythonAi: asStringArray(config.pythonAi),
738
+ pythonAuth: asString(config.pythonAuth),
739
+ pythonApi: asString(config.pythonApi),
740
+ pythonTaskQueue: asString(config.pythonTaskQueue),
741
+ pythonGraphql: asString(config.pythonGraphql),
742
+ pythonQuality: asString(config.pythonQuality),
743
+ pythonTesting: asStringArray(config.pythonTesting),
744
+ pythonCaching: asString(config.pythonCaching),
745
+ pythonRealtime: asString(config.pythonRealtime),
746
+ pythonObservability: asString(config.pythonObservability),
747
+ pythonCli: asStringArray(config.pythonCli),
748
+ goWebFramework: asString(config.goWebFramework),
749
+ goOrm: asString(config.goOrm),
750
+ goApi: asString(config.goApi),
751
+ goCli: asString(config.goCli),
752
+ goLogging: asString(config.goLogging),
753
+ goAuth: asString(config.goAuth),
754
+ goTesting: asStringArray(config.goTesting),
755
+ goRealtime: asString(config.goRealtime),
756
+ goMessageQueue: asString(config.goMessageQueue),
757
+ goCaching: asString(config.goCaching),
758
+ goConfig: asString(config.goConfig),
759
+ goObservability: asString(config.goObservability),
760
+ goValidation: asString(config.goValidation),
761
+ goQuality: asString(config.goQuality),
762
+ goMigrations: asString(config.goMigrations),
763
+ goTemplating: asString(config.goTemplating),
764
+ goProtoTooling: asString(config.goProtoTooling),
765
+ goDI: asString(config.goDI),
766
+ javaLanguage: asString(config.javaLanguage, "java"),
767
+ javaWebFramework: asString(config.javaWebFramework),
768
+ javaBuildTool: asString(config.javaBuildTool),
769
+ javaOrm: asString(config.javaOrm),
770
+ javaAuth: asString(config.javaAuth),
771
+ javaApi: asString(config.javaApi),
772
+ javaLogging: asString(config.javaLogging),
773
+ javaLibraries: asStringArray(config.javaLibraries),
774
+ javaTestingLibraries: asStringArray(config.javaTestingLibraries),
775
+ dotnetWebFramework: asString(config.dotnetWebFramework),
776
+ dotnetOrm: asString(config.dotnetOrm),
777
+ dotnetAuth: asString(config.dotnetAuth),
778
+ dotnetApi: asString(config.dotnetApi),
779
+ dotnetTesting: asStringArray(config.dotnetTesting),
780
+ dotnetJobQueue: asString(config.dotnetJobQueue),
781
+ dotnetRealtime: asString(config.dotnetRealtime),
782
+ dotnetObservability: asStringArray(config.dotnetObservability),
783
+ dotnetValidation: asString(config.dotnetValidation),
784
+ dotnetCaching: asString(config.dotnetCaching),
785
+ dotnetDeploy: asString(config.dotnetDeploy),
786
+ elixirWebFramework: asString(config.elixirWebFramework),
787
+ elixirOrm: asString(config.elixirOrm),
788
+ elixirAuth: asString(config.elixirAuth),
789
+ elixirApi: asString(config.elixirApi),
790
+ elixirRealtime: asString(config.elixirRealtime),
791
+ elixirJobs: asString(config.elixirJobs),
792
+ elixirValidation: asString(config.elixirValidation),
793
+ elixirHttp: asString(config.elixirHttp),
794
+ elixirJson: asString(config.elixirJson),
795
+ elixirEmail: asString(config.elixirEmail),
796
+ elixirCaching: asString(config.elixirCaching),
797
+ elixirObservability: asString(config.elixirObservability),
798
+ elixirTesting: asString(config.elixirTesting),
799
+ elixirQuality: asString(config.elixirQuality),
800
+ elixirI18n: asString(config.elixirI18n),
801
+ elixirHttpServer: asString(config.elixirHttpServer),
802
+ elixirApplicationFramework: asString(config.elixirApplicationFramework),
803
+ elixirDocumentation: asString(config.elixirDocumentation),
804
+ elixirClustering: asString(config.elixirClustering),
805
+ elixirDeploy: asString(config.elixirDeploy),
806
+ elixirLibraries: asStringArray(config.elixirLibraries)
807
+ };
808
+ }
809
+ function compatibilityChangesToProjectConfig(adjusted) {
810
+ const frontend = [...adjusted.webFrontend, ...adjusted.nativeFrontend];
811
+ const ignoredCompatibilityKeys = new Set([
812
+ "webFrontend",
813
+ "nativeFrontend",
814
+ "codeQuality",
815
+ "documentation",
816
+ "appPlatforms",
817
+ "aiSdk",
818
+ "backendLibraries",
819
+ "projectName",
820
+ "git",
821
+ "install",
822
+ "yolo"
823
+ ]);
824
+ const changes = {};
825
+ for (const [key, value] of Object.entries(adjusted)) if (!ignoredCompatibilityKeys.has(key)) changes[key] = value;
826
+ changes.frontend = frontend;
827
+ changes.addons = [
828
+ ...adjusted.codeQuality,
829
+ ...adjusted.documentation,
830
+ ...adjusted.appPlatforms
831
+ ];
832
+ changes.ai = adjusted.aiSdk;
833
+ changes.effect = adjusted.backendLibraries;
834
+ changes.backend = getProjectBackendFromCompatibility(adjusted.backend);
835
+ return changes;
836
+ }
837
+ const EMPTY_ADJUSTMENT_RESULT = {
838
+ changes: {},
839
+ adjustments: []
840
+ };
841
+ const EXPLICIT_NONE_ARRAY_KEYS = new Set([
842
+ "frontend",
843
+ "addons",
844
+ "examples",
845
+ "aiDocs"
846
+ ]);
847
+ const COMPAT_KEY_DISPLAY = {
848
+ webFrontend: "frontend",
849
+ nativeFrontend: "frontend",
850
+ aiSdk: "ai",
851
+ backendLibraries: "effect"
852
+ };
853
+ function formatCompatValue(value) {
854
+ if (Array.isArray(value)) {
855
+ const items = value.filter((item) => item !== "none");
856
+ return items.length > 0 ? items.join(", ") : "none";
857
+ }
858
+ return String(value);
859
+ }
860
+ /**
861
+ * Runs the same auto-adjust engine the web builder and MCP flows use over a
862
+ * (possibly partial) ProjectConfig and maps the result back to config keys.
863
+ *
864
+ * - Only TypeScript/React Native configs are analyzed (mirrors stack-update).
865
+ * - Graph-driven configs (`--part`) are skipped; the stack graph owns those.
866
+ * - With `onlyDefinedKeys`, adjustments are applied only to keys the caller
867
+ * actually set (used for the flag path, where unset keys are resolved by
868
+ * prompts/defaults later and must not be pinned by the engine's defaults).
869
+ */
870
+ function resolveCompatibilityAdjustments(config, options = {}) {
871
+ const ecosystem = config.ecosystem ?? "typescript";
872
+ if (ecosystem !== "typescript" && ecosystem !== "react-native") return EMPTY_ADJUSTMENT_RESULT;
873
+ if (config.stackParts?.length) return EMPTY_ADJUSTMENT_RESULT;
874
+ const input = buildCompatibilityInputFromConfig(config);
875
+ const result = (0, types_exports.analyzeStackCompatibility)(input);
876
+ if (!result.adjustedStack) return EMPTY_ADJUSTMENT_RESULT;
877
+ const mapped = compatibilityChangesToProjectConfig(result.adjustedStack);
878
+ const mappedInput = compatibilityChangesToProjectConfig(input);
879
+ const changes = {};
880
+ for (const [key, value] of Object.entries(mapped)) {
881
+ if (value === void 0) continue;
882
+ if (options.onlyDefinedKeys && config[key] === void 0) continue;
883
+ const next = Array.isArray(value) ? value.filter((item) => item !== "none") : value;
884
+ const before = mappedInput[key];
885
+ const previous = Array.isArray(before) ? before.filter((item) => item !== "none") : before;
886
+ if (JSON.stringify(next) !== JSON.stringify(previous)) changes[key] = Array.isArray(next) && next.length === 0 && EXPLICIT_NONE_ARRAY_KEYS.has(key) ? ["none"] : next;
887
+ }
888
+ if (Object.keys(changes).length === 0) return EMPTY_ADJUSTMENT_RESULT;
889
+ const adjustedStack = result.adjustedStack;
890
+ const inputRecord = input;
891
+ return {
892
+ changes,
893
+ adjustments: result.changes.map((change) => {
894
+ const label = COMPAT_KEY_DISPLAY[change.category] ?? change.category;
895
+ const from = inputRecord[change.category];
896
+ const to = adjustedStack[change.category];
897
+ const fromLabel = formatCompatValue(from);
898
+ const toLabel = formatCompatValue(to);
899
+ return from !== void 0 && fromLabel !== toLabel ? `${label}: ${fromLabel} → ${toLabel} — ${change.message}` : `${label}: ${change.message}`;
900
+ })
901
+ };
902
+ }
533
903
 
534
904
  //#endregion
535
905
  //#region src/utils/peer-dependency-conflicts.ts
@@ -1115,7 +1485,11 @@ function validateElixirConstraints(config) {
1115
1485
  if (config.ecosystem !== "elixir") return;
1116
1486
  const hasPhoenix = config.elixirWebFramework !== "none";
1117
1487
  const hasEcto = config.elixirOrm !== "none";
1118
- const hasEctoSql = config.elixirOrm === "ecto-sql";
1488
+ const hasEctoSql = [
1489
+ "ecto-sql",
1490
+ "myxql",
1491
+ "ecto_sqlite3"
1492
+ ].includes(config.elixirOrm ?? "none");
1119
1493
  if (!hasPhoenix) {
1120
1494
  const phoenixOnlySelections = [
1121
1495
  {
@@ -1160,13 +1534,51 @@ function validateElixirConstraints(config) {
1160
1534
  provided: { "elixir-json": "none" },
1161
1535
  suggestions: ["Use --elixir-json jason"]
1162
1536
  });
1537
+ if (hasPhoenix && config.elixirHttpServer === "none") incompatibilityError({
1538
+ message: "Phoenix requires an HTTP server adapter.",
1539
+ provided: {
1540
+ "elixir-web-framework": config.elixirWebFramework ?? "none",
1541
+ "elixir-http-server": "none"
1542
+ },
1543
+ suggestions: [
1544
+ "Use --elixir-http-server bandit",
1545
+ "Use --elixir-http-server cowboy",
1546
+ "Use --elixir-web-framework none"
1547
+ ]
1548
+ });
1163
1549
  if (config.elixirAuth === "phx-gen-auth" && !hasEctoSql) incompatibilityError({
1164
- message: "phx.gen.auth requires Ecto SQL with PostgreSQL in the generated Phoenix scaffold.",
1550
+ message: "phx.gen.auth requires an Ecto SQL repository in the generated Phoenix scaffold.",
1165
1551
  provided: {
1166
1552
  "elixir-auth": "phx-gen-auth",
1167
1553
  "elixir-orm": config.elixirOrm ?? "none"
1168
1554
  },
1169
- suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-auth none"]
1555
+ suggestions: [
1556
+ "Use --elixir-orm ecto-sql",
1557
+ "Use --elixir-orm myxql",
1558
+ "Use --elixir-orm ecto_sqlite3",
1559
+ "Use --elixir-auth none"
1560
+ ]
1561
+ });
1562
+ if (config.elixirAuth === "pow" && (!hasPhoenix || !hasEctoSql)) incompatibilityError({
1563
+ message: "Pow requires Phoenix and an Ecto SQL repository.",
1564
+ provided: {
1565
+ "elixir-web-framework": config.elixirWebFramework ?? "none",
1566
+ "elixir-auth": "pow",
1567
+ "elixir-orm": config.elixirOrm ?? "none"
1568
+ },
1569
+ suggestions: [
1570
+ "Use --elixir-web-framework phoenix",
1571
+ "Use --elixir-orm ecto-sql",
1572
+ "Use --elixir-auth none"
1573
+ ]
1574
+ });
1575
+ if (config.elixirTesting === "ex_machina" && !hasEctoSql) incompatibilityError({
1576
+ message: "ExMachina requires an Ecto SQL repository.",
1577
+ provided: {
1578
+ "elixir-testing": "ex_machina",
1579
+ "elixir-orm": config.elixirOrm ?? "none"
1580
+ },
1581
+ suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-testing none"]
1170
1582
  });
1171
1583
  if (config.elixirJobs === "oban" && config.elixirOrm !== "ecto-sql") incompatibilityError({
1172
1584
  message: "Oban requires Ecto SQL with PostgreSQL in the generated Phoenix scaffold.",
@@ -1374,6 +1786,7 @@ function validateFullConfig(config, providedFlags, options) {
1374
1786
  validateFrontendConstraints(config, providedFlags);
1375
1787
  validateApiConstraints(config, options);
1376
1788
  validatePythonApiConstraints(config);
1789
+ validateRustExpansionCompatibility(config);
1377
1790
  validateEmailConstraints(config);
1378
1791
  validateObservabilityConstraints(config);
1379
1792
  validateCachingConstraints(config);
@@ -1419,12 +1832,15 @@ function validateFullConfig(config, providedFlags, options) {
1419
1832
  });
1420
1833
  }
1421
1834
  if (config.addons && config.addons.length > 0) {
1422
- validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth, config.backend, config.runtime, config.ecosystem, config.rustFrontend, config.javaWebFramework, config.database);
1835
+ validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth, config.backend, config.runtime, config.ecosystem, config.rustFrontend, config.javaWebFramework, config.database, config.api);
1423
1836
  config.addons = [...new Set(config.addons)];
1424
1837
  }
1425
1838
  validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
1426
1839
  validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend ?? []);
1427
1840
  validateAIFrontendCompatibility(config.ai, config.frontend ?? []);
1841
+ validateAIBackendCompatibility(config.ai, config.backend);
1842
+ validateRealtimeCompatibility(config.realtime, config.backend);
1843
+ validateCSSFrameworkFrontendCompatibility(config.cssFramework, config.frontend ?? []);
1428
1844
  validateUILibraryFrontendCompatibility(config.uiLibrary, config.frontend ?? [], config.astroIntegration);
1429
1845
  validateUILibraryCSSFrameworkCompatibility(config.uiLibrary, config.cssFramework);
1430
1846
  validateShadcnConstraints(config, providedFlags);
@@ -1449,11 +1865,15 @@ function validateConfigForProgrammaticUse(config) {
1449
1865
  validateSearchConstraints(config);
1450
1866
  validateJavaConstraints(config);
1451
1867
  validateElixirConstraints(config);
1868
+ validateRustExpansionCompatibility(config);
1452
1869
  validateI18nConstraints(config);
1453
1870
  validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
1454
- 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);
1871
+ 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, config.api);
1455
1872
  validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
1456
1873
  validateAIFrontendCompatibility(config.ai, config.frontend ?? []);
1874
+ validateAIBackendCompatibility(config.ai, config.backend);
1875
+ validateRealtimeCompatibility(config.realtime, config.backend);
1876
+ validateCSSFrameworkFrontendCompatibility(config.cssFramework, config.frontend ?? []);
1457
1877
  validateUILibraryFrontendCompatibility(config.uiLibrary, config.frontend ?? [], config.astroIntegration);
1458
1878
  validateUILibraryCSSFrameworkCompatibility(config.uiLibrary, config.cssFramework);
1459
1879
  validatePeerDependencies(config);
@@ -1497,4 +1917,4 @@ async function formatProject(projectDir) {
1497
1917
  }
1498
1918
 
1499
1919
  //#endregion
1500
- export { CreateCommandInputSchema as _, allowedApisForFrontends$1 as a, getCompatibleUILibraries$1 as c, isExampleChatSdkAllowed$1 as d, isFrontendAllowedWithBackend$1 as f, validateAddonCompatibility$1 as g, splitFrontends$1 as h, validateFullConfig as i, hasWebStyling$1 as l, requiresChatSdkVercelAI as m, formatProject as n, getCompatibleAddons$1 as o, isWebFrontend$1 as p, validateConfigForProgrammaticUse as r, getCompatibleCSSFrameworks$1 as s, formatCode as t, isExampleAIAllowed$1 as u, CreateCommandOptionsSchema as v };
1920
+ export { CreateCommandInputSchema as C, validateAddonCompatibility$1 as S, isExampleChatSdkAllowed$1 as _, asString as a, requiresChatSdkVercelAI as b, compatibilityChangesToProjectConfig as c, allowedApisForFrontends$1 as d, getCompatibleAddons$1 as f, isExampleAIAllowed$1 as g, hasWebStyling$1 as h, validateFullConfig as i, getCompatibilityBackend as l, getCompatibleUILibraries$1 as m, formatProject as n, asStringArray as o, getCompatibleCSSFrameworks$1 as p, validateConfigForProgrammaticUse as r, buildCompatibilityInputFromConfig as s, formatCode as t, resolveCompatibilityAdjustments as u, isFrontendAllowedWithBackend$1 as v, CreateCommandOptionsSchema as w, splitFrontends$1 as x, isWebFrontend$1 as y };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { r as readBtsConfig } from "./bts-config-DQVWvPDs.mjs";
2
+ import { r as readBtsConfig } from "./bts-config-B_HhcMXL.mjs";
3
3
  import { log } from "@clack/prompts";
4
4
  import pc from "picocolors";
5
5
  import fs from "fs-extra";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { f as getPrimaryGraphPart } from "./bts-config-DQVWvPDs.mjs";
2
+ import { f as getPrimaryGraphPart } from "./bts-config-B_HhcMXL.mjs";
3
3
  import { log, spinner } from "@clack/prompts";
4
4
  import pc from "picocolors";
5
5
  import path from "node:path";