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.
- package/dist/{add-handler-ztNjzoMN.mjs → add-handler-CE3XIs3y.mjs} +8 -8
- package/dist/{addons-setup-LaAj43NP.mjs → addons-setup-DR924M7b.mjs} +1 -1
- package/dist/addons-setup-Du2VSv8V.mjs +7 -0
- package/dist/{analytics-DVltG11u.mjs → analytics-BXlOG6u6.mjs} +1 -1
- package/dist/{bts-config-DQVWvPDs.mjs → bts-config-B_HhcMXL.mjs} +30 -0
- package/dist/cli.mjs +2 -2
- package/dist/{config-processing-D9-F2Us9.mjs → config-processing-DJisMeW-.mjs} +17 -1
- package/dist/{doctor-a4ca3SMd.mjs → doctor-Cc7sLuow.mjs} +2 -2
- package/dist/{file-formatter-gvmrpd-g.mjs → file-formatter-BgbfpYf4.mjs} +442 -22
- package/dist/{gen-CCClL7Ve.mjs → gen-BZ0QCxCC.mjs} +1 -1
- package/dist/{generated-checks-BV9jol5h.mjs → generated-checks-CbioGjCh.mjs} +1 -1
- package/dist/index.d.mts +461 -183
- package/dist/index.mjs +20 -9
- package/dist/{install-dependencies-RoUyaE8o.mjs → install-dependencies-D__bmW4k.mjs} +86 -18
- package/dist/{mcp-492OkjcS.mjs → mcp-DNKAo8nO.mjs} +5 -5
- package/dist/mcp-entry.mjs +37 -225
- package/dist/{registry-DSf2CEaU.mjs → registry-Cm3D45oT.mjs} +1 -1
- package/dist/run-C_S7ujfY.mjs +15 -0
- package/dist/{run-T6g1jkfy.mjs → run-DmN7QRt5.mjs} +1155 -292
- package/dist/scaffold-manifest-ByKtMhTd.mjs +4 -0
- package/dist/{scaffold-manifest-Dyi0voqE.mjs → scaffold-manifest-CNFz-lmH.mjs} +34 -5
- package/dist/{update-DJ8CI5KW.mjs → update-B270pWHc.mjs} +182 -25
- package/package.json +6 -6
- package/dist/addons-setup-DnLjAzTw.mjs +0 -7
- package/dist/run-CD8f7PHh.mjs +0 -15
- 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-
|
|
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
|
|
114
|
-
goOrm: types_exports.GoOrmSchema.optional().describe("Go ORM/database
|
|
115
|
-
goApi: types_exports.GoApiSchema.optional().describe("Go API layer
|
|
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
|
|
119
|
-
goTesting: z.array(types_exports.GoTestingSchema).optional().describe("Go testing libraries
|
|
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
|
|
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
|
|
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,
|
|
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 =
|
|
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
|
|
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: [
|
|
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
|
|
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 { f as getPrimaryGraphPart } from "./bts-config-
|
|
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";
|