create-better-fullstack 2.1.8 → 2.3.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/README.md +4 -4
- package/dist/{add-handler-ztNjzoMN.mjs → add-handler-z4NCxoq6.mjs} +9 -8
- package/dist/addons-setup-COE1e1db.mjs +7 -0
- package/dist/{addons-setup-LaAj43NP.mjs → addons-setup-DKtiNexf.mjs} +1 -1
- package/dist/{analytics-DVltG11u.mjs → analytics-BD8hZvIj.mjs} +1 -1
- package/dist/{bts-config-DQVWvPDs.mjs → bts-config-Bwd6GB7B.mjs} +59 -0
- package/dist/cli.mjs +2 -2
- package/dist/command-exists-DtX9ldL8.mjs +15 -0
- package/dist/{config-processing-D9-F2Us9.mjs → config-processing-B-RT_mVE.mjs} +32 -3
- package/dist/{doctor-a4ca3SMd.mjs → doctor-LmNs-hDr.mjs} +4 -3
- package/dist/{file-formatter-gvmrpd-g.mjs → file-formatter-DxF56-mO.mjs} +550 -24
- package/dist/{gen-CCClL7Ve.mjs → gen-CQfQU0uZ.mjs} +1 -1
- package/dist/{generated-checks-BV9jol5h.mjs → generated-checks-BUcI5VM2.mjs} +55 -22
- package/dist/index.d.mts +741 -211
- package/dist/index.mjs +30 -9
- package/dist/{install-dependencies-RoUyaE8o.mjs → install-dependencies-CJiUklm3.mjs} +132 -23
- package/dist/{mcp-492OkjcS.mjs → mcp-BBammItk.mjs} +5 -5
- package/dist/mcp-entry.mjs +76 -236
- package/dist/{registry-DSf2CEaU.mjs → registry-D0JfLCoH.mjs} +1 -1
- package/dist/{run-T6g1jkfy.mjs → run-BAIeghcU.mjs} +1948 -318
- package/dist/run-CAl4L4uU.mjs +16 -0
- package/dist/scaffold-manifest-CQwvyttq.mjs +4 -0
- package/dist/{scaffold-manifest-Dyi0voqE.mjs → scaffold-manifest-Dr3Anmlf.mjs} +34 -5
- package/dist/{update-DJ8CI5KW.mjs → update-CnrBdWrh.mjs} +182 -25
- package/package.json +24 -7
- package/dist/addons-setup-DnLjAzTw.mjs +0 -7
- package/dist/run-CD8f7PHh.mjs +0 -15
- package/dist/scaffold-manifest-DGRyepdb.mjs +0 -4
- /package/dist/{update-deps-aD-iQw4U.mjs → update-deps-G3FgqBlW.mjs} +0 -0
|
@@ -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-Bwd6GB7B.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";
|
|
@@ -54,6 +54,7 @@ const CreateCommandOptionsSchema = z.object({
|
|
|
54
54
|
mobilePush: types_exports.MobilePushSchema.optional().describe("Mobile push notifications (expo-notifications)"),
|
|
55
55
|
mobileOTA: types_exports.MobileOTASchema.optional().describe("Mobile OTA updates (expo-updates)"),
|
|
56
56
|
mobileDeepLinking: types_exports.MobileDeepLinkingSchema.optional().describe("Mobile deep linking (expo-linking)"),
|
|
57
|
+
mobileLibraries: z.array(types_exports.MobileLibrariesSchema).optional().describe("Mobile application libraries"),
|
|
57
58
|
frontend: z.array(types_exports.FrontendSchema).optional(),
|
|
58
59
|
astroIntegration: types_exports.AstroIntegrationSchema.optional().describe("Astro UI framework integration (react, vue, svelte, solid)"),
|
|
59
60
|
addons: z.array(types_exports.AddonsSchema).optional(),
|
|
@@ -110,18 +111,31 @@ const CreateCommandOptionsSchema = z.object({
|
|
|
110
111
|
pythonRealtime: types_exports.PythonRealtimeSchema.optional().describe("Python realtime (python-socketio, websockets)"),
|
|
111
112
|
pythonObservability: types_exports.PythonObservabilitySchema.optional().describe("Python observability (opentelemetry)"),
|
|
112
113
|
pythonCli: z.array(types_exports.PythonCliSchema).optional().describe("Python CLI tooling (typer, click, rich)"),
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
pythonCloudSdk: types_exports.PythonCloudSdkSchema.optional().describe("Python cloud SDK (boto3)"),
|
|
115
|
+
pythonHttpClient: types_exports.PythonHttpClientSchema.optional().describe("Python HTTP client (requests)"),
|
|
116
|
+
pythonData: z.array(types_exports.PythonDataSchema).optional().describe("Python data and scientific libraries (numpy, pandas, scipy)"),
|
|
117
|
+
pythonMedia: types_exports.PythonMediaSchema.optional().describe("Python media library (pillow)"),
|
|
118
|
+
pythonServer: types_exports.PythonServerSchema.optional().describe("Python production server (gunicorn)"),
|
|
119
|
+
pythonPackageManager: types_exports.PythonPackageManagerSchema.optional().describe("Python package manager (uv, poetry)"),
|
|
120
|
+
pythonMessageQueue: types_exports.PythonMessageQueueSchema.optional().describe("Python message queue client (confluent-kafka)"),
|
|
121
|
+
goWebFramework: types_exports.GoWebFrameworkSchema.optional().describe("Go web framework"),
|
|
122
|
+
goOrm: types_exports.GoOrmSchema.optional().describe("Go ORM/database layer"),
|
|
123
|
+
goApi: types_exports.GoApiSchema.optional().describe("Go API layer"),
|
|
116
124
|
goCli: types_exports.GoCliSchema.optional().describe("Go CLI tools (cobra, bubbletea, urfave-cli)"),
|
|
117
125
|
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
|
|
126
|
+
goAuth: types_exports.GoAuthSchema.optional().describe("Go authentication library"),
|
|
127
|
+
goTesting: z.array(types_exports.GoTestingSchema).optional().describe("Go testing libraries"),
|
|
120
128
|
goRealtime: types_exports.GoRealtimeSchema.optional().describe("Go realtime library (gorilla-websocket, centrifuge)"),
|
|
121
|
-
goMessageQueue: types_exports.GoMessageQueueSchema.optional().describe("Go message queue
|
|
129
|
+
goMessageQueue: types_exports.GoMessageQueueSchema.optional().describe("Go message queue"),
|
|
122
130
|
goCaching: types_exports.GoCachingSchema.optional().describe("Go caching library (redis, ristretto)"),
|
|
123
131
|
goConfig: types_exports.GoConfigSchema.optional().describe("Go config management (viper, koanf)"),
|
|
124
|
-
goObservability: types_exports.GoObservabilitySchema.optional().describe("Go observability
|
|
132
|
+
goObservability: types_exports.GoObservabilitySchema.optional().describe("Go observability"),
|
|
133
|
+
goValidation: types_exports.GoValidationSchema.optional().describe("Go validation"),
|
|
134
|
+
goQuality: types_exports.GoQualitySchema.optional().describe("Go code quality"),
|
|
135
|
+
goMigrations: types_exports.GoMigrationsSchema.optional().describe("Go database migrations"),
|
|
136
|
+
goTemplating: types_exports.GoTemplatingSchema.optional().describe("Go templating"),
|
|
137
|
+
goProtoTooling: types_exports.GoProtoToolingSchema.optional().describe("Go protobuf tooling"),
|
|
138
|
+
goDI: types_exports.GoDISchema.optional().describe("Go dependency injection"),
|
|
125
139
|
javaWebFramework: types_exports.JavaWebFrameworkSchema.optional().describe("Java web framework (spring-boot, quarkus, none)"),
|
|
126
140
|
javaLanguage: types_exports.JavaLanguageSchema.optional().describe("JVM language (java, kotlin)"),
|
|
127
141
|
javaBuildTool: types_exports.JavaBuildToolSchema.optional().describe("Java build tool (maven, gradle, none)"),
|
|
@@ -142,6 +156,7 @@ const CreateCommandOptionsSchema = z.object({
|
|
|
142
156
|
dotnetValidation: types_exports.DotnetValidationSchema.optional().describe(".NET validation (fluentvalidation, data-annotations)"),
|
|
143
157
|
dotnetCaching: types_exports.DotnetCachingSchema.optional().describe(".NET caching (redis, memory-cache, none)"),
|
|
144
158
|
dotnetDeploy: types_exports.DotnetDeploySchema.optional().describe(".NET deploy target (docker, azure, aws, none)"),
|
|
159
|
+
dotnetLibraries: z.array(types_exports.DotnetLibrariesSchema).optional().describe(".NET application libraries"),
|
|
145
160
|
elixirWebFramework: types_exports.ElixirWebFrameworkSchema.optional().describe("Elixir web framework (phoenix, phoenix-live-view, none)"),
|
|
146
161
|
elixirOrm: types_exports.ElixirOrmSchema.optional().describe("Elixir ORM/database (ecto, ecto-sql, none)"),
|
|
147
162
|
elixirAuth: types_exports.ElixirAuthSchema.optional().describe("Elixir auth (phx-gen-auth, ueberauth, guardian, none)"),
|
|
@@ -156,6 +171,11 @@ const CreateCommandOptionsSchema = z.object({
|
|
|
156
171
|
elixirObservability: types_exports.ElixirObservabilitySchema.optional().describe("Elixir observability (telemetry, opentelemetry, prom_ex, none)"),
|
|
157
172
|
elixirTesting: types_exports.ElixirTestingSchema.optional().describe("Elixir testing (ex_unit, mox, bypass, wallaby, none)"),
|
|
158
173
|
elixirQuality: types_exports.ElixirQualitySchema.optional().describe("Elixir code quality (credo, dialyxir, sobelow, none)"),
|
|
174
|
+
elixirI18n: types_exports.ElixirI18nSchema.optional().describe("Elixir localization (gettext, none)"),
|
|
175
|
+
elixirHttpServer: types_exports.ElixirHttpServerSchema.optional().describe("Elixir HTTP server (bandit, cowboy, none)"),
|
|
176
|
+
elixirApplicationFramework: types_exports.ElixirApplicationFrameworkSchema.optional().describe("Elixir application framework (ash, none)"),
|
|
177
|
+
elixirDocumentation: types_exports.ElixirDocumentationSchema.optional().describe("Elixir documentation tooling (ex_doc, none)"),
|
|
178
|
+
elixirClustering: types_exports.ElixirClusteringSchema.optional().describe("Elixir clustering (libcluster, none)"),
|
|
159
179
|
elixirDeploy: types_exports.ElixirDeploySchema.optional().describe("Elixir deploy target (docker, fly, gigalixir, mix-release, none)"),
|
|
160
180
|
elixirLibraries: z.array(types_exports.ElixirLibrariesSchema).optional().describe("Elixir libraries (broadway, nx)"),
|
|
161
181
|
aiDocs: z.array(types_exports.AiDocsSchema).optional().describe("AI documentation files (claude-md, agents-md, cursorrules)")
|
|
@@ -350,9 +370,25 @@ function validateWebDeployFrontendTemplates(webDeploy, frontends = []) {
|
|
|
350
370
|
function validateServerDeployRequiresBackend(serverDeploy, backend, hasGraphBackend = false) {
|
|
351
371
|
if (serverDeploy && serverDeploy !== "none" && !hasGraphBackend && (!backend || backend === "none")) exitWithError("'--server-deploy' requires a backend. Please select a backend or set '--server-deploy none'.");
|
|
352
372
|
}
|
|
353
|
-
function validateAddonCompatibility$1(addon, frontend, _auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database) {
|
|
373
|
+
function validateAddonCompatibility$1(addon, frontend, _auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database, api) {
|
|
354
374
|
const baseCompatibility = validateAddonCompatibility(addon, frontend, _auth);
|
|
355
375
|
if (!baseCompatibility.isCompatible) return baseCompatibility;
|
|
376
|
+
if ((addon === "graphql-codegen" || addon === "apollo-client") && api !== void 0 && ![
|
|
377
|
+
"garph",
|
|
378
|
+
"graphql-yoga",
|
|
379
|
+
"apollo-server"
|
|
380
|
+
].includes(api) && !(addon === "graphql-codegen" && frontend.includes("redwood"))) return {
|
|
381
|
+
isCompatible: false,
|
|
382
|
+
reason: `${addon} requires a GraphQL API selection`
|
|
383
|
+
};
|
|
384
|
+
if (addon === "openapi-typescript" && api !== void 0 && api !== "openapi") return {
|
|
385
|
+
isCompatible: false,
|
|
386
|
+
reason: "openapi-typescript requires the OpenAPI API selection"
|
|
387
|
+
};
|
|
388
|
+
if (addon === "openapi-typescript" && backend === "self") return {
|
|
389
|
+
isCompatible: false,
|
|
390
|
+
reason: "openapi-typescript requires a standalone backend that exposes an OpenAPI schema"
|
|
391
|
+
};
|
|
356
392
|
if (addon === "backend-utils") {
|
|
357
393
|
if (ecosystem !== void 0 && ecosystem !== "typescript") return {
|
|
358
394
|
isCompatible: false,
|
|
@@ -400,29 +436,31 @@ function validateAddonCompatibility$1(addon, frontend, _auth, backend, runtime,
|
|
|
400
436
|
isCompatible: false,
|
|
401
437
|
reason: `${title} for Java currently requires Spring Boot`
|
|
402
438
|
};
|
|
403
|
-
if (ecosystem === "python" && database && database !== "none" && database !== "sqlite" && database !== "postgres") return {
|
|
439
|
+
if (ecosystem === "python" && database && database !== "none" && database !== "sqlite" && database !== "postgres" && database !== "mongodb") return {
|
|
404
440
|
isCompatible: false,
|
|
405
|
-
reason: `${title} for Python ORM projects currently supports SQLite
|
|
441
|
+
reason: `${title} for Python ORM projects currently supports SQLite, Postgres, or MongoDB`
|
|
406
442
|
};
|
|
407
443
|
}
|
|
408
444
|
return { isCompatible: true };
|
|
409
445
|
}
|
|
410
|
-
function getCompatibleAddons$1(allAddons, frontend, existingAddons = [], auth, backend, runtime) {
|
|
446
|
+
function getCompatibleAddons$1(allAddons, frontend, existingAddons = [], auth, backend, runtime, api) {
|
|
411
447
|
return getCompatibleAddons(allAddons, frontend, existingAddons, auth).filter((addon) => {
|
|
412
|
-
const { isCompatible } = validateAddonCompatibility$1(addon, frontend, auth, backend, runtime);
|
|
448
|
+
const { isCompatible } = validateAddonCompatibility$1(addon, frontend, auth, backend, runtime, void 0, void 0, void 0, void 0, api);
|
|
413
449
|
return isCompatible;
|
|
414
450
|
});
|
|
415
451
|
}
|
|
416
|
-
function validateAddonsAgainstFrontends(addons = [], frontends = [], auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database) {
|
|
452
|
+
function validateAddonsAgainstFrontends(addons = [], frontends = [], auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database, api) {
|
|
417
453
|
if (addons.includes("nx") && addons.includes("turborepo")) exitWithError("Nx and Turborepo are alternative workspace runners. Choose one addon.");
|
|
418
454
|
for (const addon of addons) {
|
|
419
455
|
if (addon === "none") continue;
|
|
420
|
-
const { isCompatible, reason } = validateAddonCompatibility$1(addon, frontends, auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database);
|
|
456
|
+
const { isCompatible, reason } = validateAddonCompatibility$1(addon, frontends, auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database, api);
|
|
421
457
|
if (!isCompatible) exitWithError(`Incompatible addon/frontend combination: ${reason}`);
|
|
422
458
|
}
|
|
423
459
|
}
|
|
424
|
-
function validatePaymentsCompatibility(payments, auth,
|
|
460
|
+
function validatePaymentsCompatibility(payments, auth, backend, frontends = []) {
|
|
425
461
|
if (!payments || payments === "none") return;
|
|
462
|
+
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.");
|
|
463
|
+
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
464
|
if (payments === "dodo" && frontends.includes("react-vite")) exitWithError("Dodo Payments are not yet supported for React + Vite projects.");
|
|
427
465
|
if (payments === "polar") {
|
|
428
466
|
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 +509,25 @@ function validateAIFrontendCompatibility(ai, frontends = []) {
|
|
|
471
509
|
if (!issue) return;
|
|
472
510
|
exitWithError(issue.message);
|
|
473
511
|
}
|
|
512
|
+
function validateAIBackendCompatibility(ai, backend) {
|
|
513
|
+
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.");
|
|
514
|
+
}
|
|
515
|
+
function validateRealtimeCompatibility(realtime, backend) {
|
|
516
|
+
if (realtime === "ws" && backend !== "express") exitWithError("The ws integration is currently wired for the Express backend.");
|
|
517
|
+
}
|
|
518
|
+
function validateCSSFrameworkFrontendCompatibility(cssFramework, frontends = []) {
|
|
519
|
+
if (cssFramework !== "styled-components") return;
|
|
520
|
+
const reactFrontends = [
|
|
521
|
+
"tanstack-router",
|
|
522
|
+
"react-router",
|
|
523
|
+
"react-vite",
|
|
524
|
+
"tanstack-start",
|
|
525
|
+
"next",
|
|
526
|
+
"vinext",
|
|
527
|
+
"redwood"
|
|
528
|
+
];
|
|
529
|
+
if (!frontends.some((frontend) => reactFrontends.includes(frontend))) exitWithError("styled-components requires a React frontend.");
|
|
530
|
+
}
|
|
474
531
|
/**
|
|
475
532
|
* Validates that a UI library is compatible with the selected frontend(s)
|
|
476
533
|
*/
|
|
@@ -521,8 +578,8 @@ function getCompatibleUILibraries$1(frontends = [], astroIntegration) {
|
|
|
521
578
|
/**
|
|
522
579
|
* Gets list of CSS frameworks compatible with the selected UI library
|
|
523
580
|
*/
|
|
524
|
-
function getCompatibleCSSFrameworks$1(uiLibrary) {
|
|
525
|
-
return getCompatibleCSSFrameworks(uiLibrary);
|
|
581
|
+
function getCompatibleCSSFrameworks$1(uiLibrary, frontends = []) {
|
|
582
|
+
return getCompatibleCSSFrameworks(uiLibrary, frontends);
|
|
526
583
|
}
|
|
527
584
|
/**
|
|
528
585
|
* Checks if a frontend has web styling (excludes native-only frontends)
|
|
@@ -530,6 +587,346 @@ function getCompatibleCSSFrameworks$1(uiLibrary) {
|
|
|
530
587
|
function hasWebStyling$1(frontends = []) {
|
|
531
588
|
return hasWebStyling(frontends);
|
|
532
589
|
}
|
|
590
|
+
function validateRustExpansionCompatibility(config) {
|
|
591
|
+
if (config.ecosystem !== "rust") return;
|
|
592
|
+
const framework = config.rustWebFramework ?? "none";
|
|
593
|
+
const api = config.rustApi ?? "none";
|
|
594
|
+
const auth = config.rustAuth ?? "none";
|
|
595
|
+
if ((framework === "warp" || framework === "salvo") && !["none", "jsonrpsee"].includes(api)) incompatibilityError({
|
|
596
|
+
message: "Warp and Salvo currently support REST or the standalone jsonrpsee server.",
|
|
597
|
+
provided: {
|
|
598
|
+
"rust-web-framework": framework,
|
|
599
|
+
"rust-api": api
|
|
600
|
+
},
|
|
601
|
+
suggestions: ["Use --rust-api jsonrpsee or --rust-api none", "Use Axum, Actix Web, Rocket, or Poem for Tonic/async-graphql"]
|
|
602
|
+
});
|
|
603
|
+
if (framework === "loco" && api === "jsonrpsee") incompatibilityError({
|
|
604
|
+
message: "Loco owns the server boot sequence and cannot start the generated jsonrpsee server.",
|
|
605
|
+
provided: {
|
|
606
|
+
"rust-web-framework": framework,
|
|
607
|
+
"rust-api": api
|
|
608
|
+
},
|
|
609
|
+
suggestions: ["Use --rust-api none", "Choose Axum, Actix Web, Rocket, Poem, Warp, or Salvo"]
|
|
610
|
+
});
|
|
611
|
+
if (auth === "tower-sessions" && framework !== "axum") incompatibilityError({
|
|
612
|
+
message: "The generated tower-sessions middleware is wired specifically for Axum.",
|
|
613
|
+
provided: {
|
|
614
|
+
"rust-web-framework": framework,
|
|
615
|
+
"rust-auth": auth
|
|
616
|
+
},
|
|
617
|
+
suggestions: ["Use --rust-web-framework axum", "Choose --rust-auth openidconnect or none"]
|
|
618
|
+
});
|
|
619
|
+
const orm = config.rustOrm ?? "none";
|
|
620
|
+
if (auth === "torii" && orm === "rusqlite") incompatibilityError({
|
|
621
|
+
message: "Torii's sqlx-based SQLite storage conflicts with rusqlite: both link the native sqlite3 library and cargo permits only one linker.",
|
|
622
|
+
provided: {
|
|
623
|
+
"rust-orm": orm,
|
|
624
|
+
"rust-auth": auth
|
|
625
|
+
},
|
|
626
|
+
suggestions: ["Use --rust-orm sqlx, sea-orm, or diesel with Torii", "Choose --rust-auth none"]
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
//#endregion
|
|
631
|
+
//#region src/utils/stack-compatibility.ts
|
|
632
|
+
function asString(value, fallback = "none") {
|
|
633
|
+
return typeof value === "string" ? value : fallback;
|
|
634
|
+
}
|
|
635
|
+
function asStringArray(value) {
|
|
636
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
637
|
+
}
|
|
638
|
+
function getCompatibilityBackend(config, webFrontend) {
|
|
639
|
+
if (config.backend !== "self") return asString(config.backend, "hono");
|
|
640
|
+
if (webFrontend.includes("next")) return "self-next";
|
|
641
|
+
if (webFrontend.includes("vinext")) return "self-vinext";
|
|
642
|
+
if (webFrontend.includes("tanstack-start")) return "self-tanstack-start";
|
|
643
|
+
if (webFrontend.includes("astro")) return "self-astro";
|
|
644
|
+
if (webFrontend.includes("nuxt")) return "self-nuxt";
|
|
645
|
+
if (webFrontend.includes("svelte")) return "self-svelte";
|
|
646
|
+
if (webFrontend.includes("solid-start")) return "self-solid-start";
|
|
647
|
+
return "self";
|
|
648
|
+
}
|
|
649
|
+
function getProjectBackendFromCompatibility(backend) {
|
|
650
|
+
return backend.startsWith("self-") ? "self" : backend;
|
|
651
|
+
}
|
|
652
|
+
function hasSelectedTypeScriptBackendPart(config) {
|
|
653
|
+
return config.stackParts?.some((part) => part.source !== "provided" && !part.ownerPartId && part.role === "backend" && part.ecosystem === "typescript" && part.toolId !== "none") ?? false;
|
|
654
|
+
}
|
|
655
|
+
function getCompatibilityEcosystem(config) {
|
|
656
|
+
if (config.ecosystem === "react-native" && hasSelectedTypeScriptBackendPart(config)) return "typescript";
|
|
657
|
+
return config.ecosystem ?? "typescript";
|
|
658
|
+
}
|
|
659
|
+
function buildCompatibilityInputFromConfig(config) {
|
|
660
|
+
const frontend = asStringArray(config.frontend);
|
|
661
|
+
const addons = asStringArray(config.addons);
|
|
662
|
+
const webFrontend = frontend.filter((item) => !item.startsWith("native-") && item !== "none");
|
|
663
|
+
const nativeFrontend = frontend.filter((item) => item.startsWith("native-"));
|
|
664
|
+
const codeQuality = [];
|
|
665
|
+
const documentation = [];
|
|
666
|
+
const appPlatforms = [];
|
|
667
|
+
for (const addon of addons) {
|
|
668
|
+
const binding = (0, types_exports.getAddonStackPartBinding)(addon);
|
|
669
|
+
if (binding?.role === "codeQuality") codeQuality.push(addon);
|
|
670
|
+
else if (binding?.role === "documentation") documentation.push(addon);
|
|
671
|
+
else if (addon !== "none") appPlatforms.push(addon);
|
|
672
|
+
}
|
|
673
|
+
return {
|
|
674
|
+
ecosystem: getCompatibilityEcosystem(config),
|
|
675
|
+
projectName: config.projectName ?? null,
|
|
676
|
+
webFrontend,
|
|
677
|
+
nativeFrontend,
|
|
678
|
+
astroIntegration: asString(config.astroIntegration),
|
|
679
|
+
runtime: asString(config.runtime, "bun"),
|
|
680
|
+
backend: getCompatibilityBackend(config, webFrontend),
|
|
681
|
+
database: asString(config.database),
|
|
682
|
+
orm: asString(config.orm),
|
|
683
|
+
dbSetup: asString(config.dbSetup),
|
|
684
|
+
auth: asString(config.auth),
|
|
685
|
+
payments: asString(config.payments),
|
|
686
|
+
email: asString(config.email),
|
|
687
|
+
fileUpload: asString(config.fileUpload),
|
|
688
|
+
logging: asString(config.logging),
|
|
689
|
+
observability: asString(config.observability),
|
|
690
|
+
featureFlags: asString(config.featureFlags),
|
|
691
|
+
analytics: asString(config.analytics),
|
|
692
|
+
backendLibraries: asString(config.effect),
|
|
693
|
+
stateManagement: asString(config.stateManagement),
|
|
694
|
+
forms: asString(config.forms),
|
|
695
|
+
validation: asString(config.validation),
|
|
696
|
+
testing: asString(config.testing),
|
|
697
|
+
realtime: asString(config.realtime),
|
|
698
|
+
jobQueue: asString(config.jobQueue),
|
|
699
|
+
caching: asString(config.caching),
|
|
700
|
+
rateLimit: asString(config.rateLimit),
|
|
701
|
+
animation: asString(config.animation),
|
|
702
|
+
cssFramework: asString(config.cssFramework),
|
|
703
|
+
uiLibrary: asString(config.uiLibrary),
|
|
704
|
+
shadcnBase: asString(config.shadcnBase, "radix"),
|
|
705
|
+
shadcnStyle: asString(config.shadcnStyle, "nova"),
|
|
706
|
+
shadcnIconLibrary: asString(config.shadcnIconLibrary, "lucide"),
|
|
707
|
+
shadcnColorTheme: asString(config.shadcnColorTheme, "neutral"),
|
|
708
|
+
shadcnBaseColor: asString(config.shadcnBaseColor, "neutral"),
|
|
709
|
+
shadcnFont: asString(config.shadcnFont, "inter"),
|
|
710
|
+
shadcnRadius: asString(config.shadcnRadius, "default"),
|
|
711
|
+
cms: asString(config.cms),
|
|
712
|
+
i18n: asString(config.i18n),
|
|
713
|
+
search: asString(config.search),
|
|
714
|
+
vectorDb: asString(config.vectorDb),
|
|
715
|
+
fileStorage: asString(config.fileStorage),
|
|
716
|
+
mobileNavigation: asString(config.mobileNavigation),
|
|
717
|
+
mobileUI: asString(config.mobileUI),
|
|
718
|
+
mobileStorage: asString(config.mobileStorage),
|
|
719
|
+
mobileTesting: asString(config.mobileTesting),
|
|
720
|
+
mobilePush: asString(config.mobilePush),
|
|
721
|
+
mobileOTA: asString(config.mobileOTA),
|
|
722
|
+
mobileDeepLinking: asString(config.mobileDeepLinking),
|
|
723
|
+
mobileLibraries: asStringArray(config.mobileLibraries),
|
|
724
|
+
codeQuality,
|
|
725
|
+
documentation,
|
|
726
|
+
appPlatforms,
|
|
727
|
+
packageManager: asString(config.packageManager, "bun"),
|
|
728
|
+
workspaceShape: asString(config.workspaceShape, "monorepo"),
|
|
729
|
+
versionChannel: asString(config.versionChannel, "stable"),
|
|
730
|
+
examples: asStringArray(config.examples),
|
|
731
|
+
aiSdk: asString(config.ai),
|
|
732
|
+
aiDocs: asStringArray(config.aiDocs),
|
|
733
|
+
git: "false",
|
|
734
|
+
install: "false",
|
|
735
|
+
api: asString(config.api),
|
|
736
|
+
webDeploy: asString(config.webDeploy),
|
|
737
|
+
serverDeploy: asString(config.serverDeploy),
|
|
738
|
+
yolo: "false",
|
|
739
|
+
rustWebFramework: asString(config.rustWebFramework),
|
|
740
|
+
rustFrontend: asString(config.rustFrontend),
|
|
741
|
+
rustOrm: asString(config.rustOrm),
|
|
742
|
+
rustApi: asString(config.rustApi),
|
|
743
|
+
rustCli: asString(config.rustCli),
|
|
744
|
+
rustLibraries: asStringArray(config.rustLibraries),
|
|
745
|
+
rustLogging: asString(config.rustLogging),
|
|
746
|
+
rustErrorHandling: asString(config.rustErrorHandling),
|
|
747
|
+
rustCaching: asString(config.rustCaching),
|
|
748
|
+
rustAuth: asString(config.rustAuth),
|
|
749
|
+
rustRealtime: asString(config.rustRealtime),
|
|
750
|
+
rustMessageQueue: asString(config.rustMessageQueue),
|
|
751
|
+
rustObservability: asString(config.rustObservability),
|
|
752
|
+
rustTemplating: asString(config.rustTemplating),
|
|
753
|
+
pythonWebFramework: asString(config.pythonWebFramework),
|
|
754
|
+
pythonOrm: asString(config.pythonOrm),
|
|
755
|
+
pythonValidation: asString(config.pythonValidation),
|
|
756
|
+
pythonAi: asStringArray(config.pythonAi),
|
|
757
|
+
pythonAuth: asString(config.pythonAuth),
|
|
758
|
+
pythonApi: asString(config.pythonApi),
|
|
759
|
+
pythonTaskQueue: asString(config.pythonTaskQueue),
|
|
760
|
+
pythonGraphql: asString(config.pythonGraphql),
|
|
761
|
+
pythonQuality: asString(config.pythonQuality),
|
|
762
|
+
pythonTesting: asStringArray(config.pythonTesting),
|
|
763
|
+
pythonCaching: asString(config.pythonCaching),
|
|
764
|
+
pythonRealtime: asString(config.pythonRealtime),
|
|
765
|
+
pythonObservability: asString(config.pythonObservability),
|
|
766
|
+
pythonCli: asStringArray(config.pythonCli),
|
|
767
|
+
pythonCloudSdk: asString(config.pythonCloudSdk),
|
|
768
|
+
pythonHttpClient: asString(config.pythonHttpClient),
|
|
769
|
+
pythonData: asStringArray(config.pythonData),
|
|
770
|
+
pythonMedia: asString(config.pythonMedia),
|
|
771
|
+
pythonServer: asString(config.pythonServer),
|
|
772
|
+
pythonPackageManager: asString(config.pythonPackageManager),
|
|
773
|
+
pythonMessageQueue: asString(config.pythonMessageQueue),
|
|
774
|
+
goWebFramework: asString(config.goWebFramework),
|
|
775
|
+
goOrm: asString(config.goOrm),
|
|
776
|
+
goApi: asString(config.goApi),
|
|
777
|
+
goCli: asString(config.goCli),
|
|
778
|
+
goLogging: asString(config.goLogging),
|
|
779
|
+
goAuth: asString(config.goAuth),
|
|
780
|
+
goTesting: asStringArray(config.goTesting),
|
|
781
|
+
goRealtime: asString(config.goRealtime),
|
|
782
|
+
goMessageQueue: asString(config.goMessageQueue),
|
|
783
|
+
goCaching: asString(config.goCaching),
|
|
784
|
+
goConfig: asString(config.goConfig),
|
|
785
|
+
goObservability: asString(config.goObservability),
|
|
786
|
+
goValidation: asString(config.goValidation),
|
|
787
|
+
goQuality: asString(config.goQuality),
|
|
788
|
+
goMigrations: asString(config.goMigrations),
|
|
789
|
+
goTemplating: asString(config.goTemplating),
|
|
790
|
+
goProtoTooling: asString(config.goProtoTooling),
|
|
791
|
+
goDI: asString(config.goDI),
|
|
792
|
+
javaLanguage: asString(config.javaLanguage, "java"),
|
|
793
|
+
javaWebFramework: asString(config.javaWebFramework),
|
|
794
|
+
javaBuildTool: asString(config.javaBuildTool),
|
|
795
|
+
javaOrm: asString(config.javaOrm),
|
|
796
|
+
javaAuth: asString(config.javaAuth),
|
|
797
|
+
javaApi: asString(config.javaApi),
|
|
798
|
+
javaLogging: asString(config.javaLogging),
|
|
799
|
+
javaLibraries: asStringArray(config.javaLibraries),
|
|
800
|
+
javaTestingLibraries: asStringArray(config.javaTestingLibraries),
|
|
801
|
+
dotnetWebFramework: asString(config.dotnetWebFramework),
|
|
802
|
+
dotnetOrm: asString(config.dotnetOrm),
|
|
803
|
+
dotnetAuth: asString(config.dotnetAuth),
|
|
804
|
+
dotnetApi: asString(config.dotnetApi),
|
|
805
|
+
dotnetTesting: asStringArray(config.dotnetTesting),
|
|
806
|
+
dotnetJobQueue: asString(config.dotnetJobQueue),
|
|
807
|
+
dotnetRealtime: asString(config.dotnetRealtime),
|
|
808
|
+
dotnetObservability: asStringArray(config.dotnetObservability),
|
|
809
|
+
dotnetValidation: asString(config.dotnetValidation),
|
|
810
|
+
dotnetCaching: asString(config.dotnetCaching),
|
|
811
|
+
dotnetDeploy: asString(config.dotnetDeploy),
|
|
812
|
+
dotnetLibraries: asStringArray(config.dotnetLibraries),
|
|
813
|
+
elixirWebFramework: asString(config.elixirWebFramework),
|
|
814
|
+
elixirOrm: asString(config.elixirOrm),
|
|
815
|
+
elixirAuth: asString(config.elixirAuth),
|
|
816
|
+
elixirApi: asString(config.elixirApi),
|
|
817
|
+
elixirRealtime: asString(config.elixirRealtime),
|
|
818
|
+
elixirJobs: asString(config.elixirJobs),
|
|
819
|
+
elixirValidation: asString(config.elixirValidation),
|
|
820
|
+
elixirHttp: asString(config.elixirHttp),
|
|
821
|
+
elixirJson: asString(config.elixirJson),
|
|
822
|
+
elixirEmail: asString(config.elixirEmail),
|
|
823
|
+
elixirCaching: asString(config.elixirCaching),
|
|
824
|
+
elixirObservability: asString(config.elixirObservability),
|
|
825
|
+
elixirTesting: asString(config.elixirTesting),
|
|
826
|
+
elixirQuality: asString(config.elixirQuality),
|
|
827
|
+
elixirI18n: asString(config.elixirI18n),
|
|
828
|
+
elixirHttpServer: asString(config.elixirHttpServer),
|
|
829
|
+
elixirApplicationFramework: asString(config.elixirApplicationFramework),
|
|
830
|
+
elixirDocumentation: asString(config.elixirDocumentation),
|
|
831
|
+
elixirClustering: asString(config.elixirClustering),
|
|
832
|
+
elixirDeploy: asString(config.elixirDeploy),
|
|
833
|
+
elixirLibraries: asStringArray(config.elixirLibraries)
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
function compatibilityChangesToProjectConfig(adjusted) {
|
|
837
|
+
const frontend = [...adjusted.webFrontend, ...adjusted.nativeFrontend];
|
|
838
|
+
const ignoredCompatibilityKeys = new Set([
|
|
839
|
+
"webFrontend",
|
|
840
|
+
"nativeFrontend",
|
|
841
|
+
"codeQuality",
|
|
842
|
+
"documentation",
|
|
843
|
+
"appPlatforms",
|
|
844
|
+
"aiSdk",
|
|
845
|
+
"backendLibraries",
|
|
846
|
+
"projectName",
|
|
847
|
+
"git",
|
|
848
|
+
"install",
|
|
849
|
+
"yolo"
|
|
850
|
+
]);
|
|
851
|
+
const changes = {};
|
|
852
|
+
for (const [key, value] of Object.entries(adjusted)) if (!ignoredCompatibilityKeys.has(key)) changes[key] = value;
|
|
853
|
+
changes.frontend = frontend;
|
|
854
|
+
changes.addons = [
|
|
855
|
+
...adjusted.codeQuality,
|
|
856
|
+
...adjusted.documentation,
|
|
857
|
+
...adjusted.appPlatforms
|
|
858
|
+
];
|
|
859
|
+
changes.ai = adjusted.aiSdk;
|
|
860
|
+
changes.effect = adjusted.backendLibraries;
|
|
861
|
+
changes.backend = getProjectBackendFromCompatibility(adjusted.backend);
|
|
862
|
+
return changes;
|
|
863
|
+
}
|
|
864
|
+
const EMPTY_ADJUSTMENT_RESULT = {
|
|
865
|
+
changes: {},
|
|
866
|
+
adjustments: []
|
|
867
|
+
};
|
|
868
|
+
const EXPLICIT_NONE_ARRAY_KEYS = new Set([
|
|
869
|
+
"frontend",
|
|
870
|
+
"addons",
|
|
871
|
+
"examples",
|
|
872
|
+
"aiDocs"
|
|
873
|
+
]);
|
|
874
|
+
const COMPAT_KEY_DISPLAY = {
|
|
875
|
+
webFrontend: "frontend",
|
|
876
|
+
nativeFrontend: "frontend",
|
|
877
|
+
aiSdk: "ai",
|
|
878
|
+
backendLibraries: "effect"
|
|
879
|
+
};
|
|
880
|
+
function formatCompatValue(value) {
|
|
881
|
+
if (Array.isArray(value)) {
|
|
882
|
+
const items = value.filter((item) => item !== "none");
|
|
883
|
+
return items.length > 0 ? items.join(", ") : "none";
|
|
884
|
+
}
|
|
885
|
+
return String(value);
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Runs the same auto-adjust engine the web builder and MCP flows use over a
|
|
889
|
+
* (possibly partial) ProjectConfig and maps the result back to config keys.
|
|
890
|
+
*
|
|
891
|
+
* - Only TypeScript/React Native configs are analyzed (mirrors stack-update).
|
|
892
|
+
* - Graph-driven configs (`--part`) are skipped; the stack graph owns those.
|
|
893
|
+
* - With `onlyDefinedKeys`, adjustments are applied only to keys the caller
|
|
894
|
+
* actually set (used for the flag path, where unset keys are resolved by
|
|
895
|
+
* prompts/defaults later and must not be pinned by the engine's defaults).
|
|
896
|
+
*/
|
|
897
|
+
function resolveCompatibilityAdjustments(config, options = {}) {
|
|
898
|
+
const ecosystem = config.ecosystem ?? "typescript";
|
|
899
|
+
if (ecosystem !== "typescript" && ecosystem !== "react-native") return EMPTY_ADJUSTMENT_RESULT;
|
|
900
|
+
if (config.stackParts?.length) return EMPTY_ADJUSTMENT_RESULT;
|
|
901
|
+
const input = buildCompatibilityInputFromConfig(config);
|
|
902
|
+
const result = (0, types_exports.analyzeStackCompatibility)(input);
|
|
903
|
+
if (!result.adjustedStack) return EMPTY_ADJUSTMENT_RESULT;
|
|
904
|
+
const mapped = compatibilityChangesToProjectConfig(result.adjustedStack);
|
|
905
|
+
const mappedInput = compatibilityChangesToProjectConfig(input);
|
|
906
|
+
const changes = {};
|
|
907
|
+
for (const [key, value] of Object.entries(mapped)) {
|
|
908
|
+
if (value === void 0) continue;
|
|
909
|
+
if (options.onlyDefinedKeys && config[key] === void 0) continue;
|
|
910
|
+
const next = Array.isArray(value) ? value.filter((item) => item !== "none") : value;
|
|
911
|
+
const before = mappedInput[key];
|
|
912
|
+
const previous = Array.isArray(before) ? before.filter((item) => item !== "none") : before;
|
|
913
|
+
if (JSON.stringify(next) !== JSON.stringify(previous)) changes[key] = Array.isArray(next) && next.length === 0 && EXPLICIT_NONE_ARRAY_KEYS.has(key) ? ["none"] : next;
|
|
914
|
+
}
|
|
915
|
+
if (Object.keys(changes).length === 0) return EMPTY_ADJUSTMENT_RESULT;
|
|
916
|
+
const adjustedStack = result.adjustedStack;
|
|
917
|
+
const inputRecord = input;
|
|
918
|
+
return {
|
|
919
|
+
changes,
|
|
920
|
+
adjustments: result.changes.map((change) => {
|
|
921
|
+
const label = COMPAT_KEY_DISPLAY[change.category] ?? change.category;
|
|
922
|
+
const from = inputRecord[change.category];
|
|
923
|
+
const to = adjustedStack[change.category];
|
|
924
|
+
const fromLabel = formatCompatValue(from);
|
|
925
|
+
const toLabel = formatCompatValue(to);
|
|
926
|
+
return from !== void 0 && fromLabel !== toLabel ? `${label}: ${fromLabel} → ${toLabel} — ${change.message}` : `${label}: ${change.message}`;
|
|
927
|
+
})
|
|
928
|
+
};
|
|
929
|
+
}
|
|
533
930
|
|
|
534
931
|
//#endregion
|
|
535
932
|
//#region src/utils/peer-dependency-conflicts.ts
|
|
@@ -1073,6 +1470,28 @@ function validateEffectBackendConstraints(config) {
|
|
|
1073
1470
|
suggestions: ["Use --validation effect-schema"]
|
|
1074
1471
|
});
|
|
1075
1472
|
}
|
|
1473
|
+
function validateScopedLibraryFlags(config) {
|
|
1474
|
+
const mobileLibraries = (config.mobileLibraries ?? []).filter((lib) => lib !== "none");
|
|
1475
|
+
const hasNativeFrontend = (config.frontend ?? []).some((f) => String(f).startsWith("native-")) || config.stackParts?.some((part) => part.ecosystem === "react-native");
|
|
1476
|
+
if (mobileLibraries.length > 0 && !hasNativeFrontend) incompatibilityError({
|
|
1477
|
+
message: "Mobile libraries require a native Expo frontend.",
|
|
1478
|
+
provided: {
|
|
1479
|
+
frontend: (config.frontend ?? []).join(" ") || "none",
|
|
1480
|
+
"mobile-libraries": mobileLibraries.join(" ")
|
|
1481
|
+
},
|
|
1482
|
+
suggestions: ["Add a native frontend (native-bare, native-uniwind, native-unistyles)", "Remove --mobile-libraries for web-only stacks"]
|
|
1483
|
+
});
|
|
1484
|
+
const dotnetLibraries = (config.dotnetLibraries ?? []).filter((lib) => lib !== "none");
|
|
1485
|
+
const hasDotnetPart = config.ecosystem === "dotnet" || config.stackParts?.some((part) => part.ecosystem === "dotnet");
|
|
1486
|
+
if (dotnetLibraries.length > 0 && !hasDotnetPart) incompatibilityError({
|
|
1487
|
+
message: ".NET libraries require a .NET backend.",
|
|
1488
|
+
provided: {
|
|
1489
|
+
ecosystem: config.ecosystem ?? "none",
|
|
1490
|
+
"dotnet-libraries": dotnetLibraries.join(" ")
|
|
1491
|
+
},
|
|
1492
|
+
suggestions: ["Use --ecosystem dotnet (or add a .NET graph part) with --dotnet-libraries", "Remove --dotnet-libraries for non-.NET stacks"]
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1076
1495
|
function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set()) {
|
|
1077
1496
|
if (config.ecosystem !== "java") return;
|
|
1078
1497
|
const hasSpringBoot = config.javaWebFramework === "spring-boot";
|
|
@@ -1115,7 +1534,11 @@ function validateElixirConstraints(config) {
|
|
|
1115
1534
|
if (config.ecosystem !== "elixir") return;
|
|
1116
1535
|
const hasPhoenix = config.elixirWebFramework !== "none";
|
|
1117
1536
|
const hasEcto = config.elixirOrm !== "none";
|
|
1118
|
-
const hasEctoSql =
|
|
1537
|
+
const hasEctoSql = [
|
|
1538
|
+
"ecto-sql",
|
|
1539
|
+
"myxql",
|
|
1540
|
+
"ecto_sqlite3"
|
|
1541
|
+
].includes(config.elixirOrm ?? "none");
|
|
1119
1542
|
if (!hasPhoenix) {
|
|
1120
1543
|
const phoenixOnlySelections = [
|
|
1121
1544
|
{
|
|
@@ -1160,13 +1583,51 @@ function validateElixirConstraints(config) {
|
|
|
1160
1583
|
provided: { "elixir-json": "none" },
|
|
1161
1584
|
suggestions: ["Use --elixir-json jason"]
|
|
1162
1585
|
});
|
|
1586
|
+
if (hasPhoenix && config.elixirHttpServer === "none") incompatibilityError({
|
|
1587
|
+
message: "Phoenix requires an HTTP server adapter.",
|
|
1588
|
+
provided: {
|
|
1589
|
+
"elixir-web-framework": config.elixirWebFramework ?? "none",
|
|
1590
|
+
"elixir-http-server": "none"
|
|
1591
|
+
},
|
|
1592
|
+
suggestions: [
|
|
1593
|
+
"Use --elixir-http-server bandit",
|
|
1594
|
+
"Use --elixir-http-server cowboy",
|
|
1595
|
+
"Use --elixir-web-framework none"
|
|
1596
|
+
]
|
|
1597
|
+
});
|
|
1163
1598
|
if (config.elixirAuth === "phx-gen-auth" && !hasEctoSql) incompatibilityError({
|
|
1164
|
-
message: "phx.gen.auth requires Ecto SQL
|
|
1599
|
+
message: "phx.gen.auth requires an Ecto SQL repository in the generated Phoenix scaffold.",
|
|
1165
1600
|
provided: {
|
|
1166
1601
|
"elixir-auth": "phx-gen-auth",
|
|
1167
1602
|
"elixir-orm": config.elixirOrm ?? "none"
|
|
1168
1603
|
},
|
|
1169
|
-
suggestions: [
|
|
1604
|
+
suggestions: [
|
|
1605
|
+
"Use --elixir-orm ecto-sql",
|
|
1606
|
+
"Use --elixir-orm myxql",
|
|
1607
|
+
"Use --elixir-orm ecto_sqlite3",
|
|
1608
|
+
"Use --elixir-auth none"
|
|
1609
|
+
]
|
|
1610
|
+
});
|
|
1611
|
+
if (config.elixirAuth === "pow" && (!hasPhoenix || !hasEctoSql)) incompatibilityError({
|
|
1612
|
+
message: "Pow requires Phoenix and an Ecto SQL repository.",
|
|
1613
|
+
provided: {
|
|
1614
|
+
"elixir-web-framework": config.elixirWebFramework ?? "none",
|
|
1615
|
+
"elixir-auth": "pow",
|
|
1616
|
+
"elixir-orm": config.elixirOrm ?? "none"
|
|
1617
|
+
},
|
|
1618
|
+
suggestions: [
|
|
1619
|
+
"Use --elixir-web-framework phoenix",
|
|
1620
|
+
"Use --elixir-orm ecto-sql",
|
|
1621
|
+
"Use --elixir-auth none"
|
|
1622
|
+
]
|
|
1623
|
+
});
|
|
1624
|
+
if (config.elixirTesting === "ex_machina" && !hasEctoSql) incompatibilityError({
|
|
1625
|
+
message: "ExMachina requires an Ecto SQL repository.",
|
|
1626
|
+
provided: {
|
|
1627
|
+
"elixir-testing": "ex_machina",
|
|
1628
|
+
"elixir-orm": config.elixirOrm ?? "none"
|
|
1629
|
+
},
|
|
1630
|
+
suggestions: ["Use --elixir-orm ecto-sql", "Use --elixir-testing none"]
|
|
1170
1631
|
});
|
|
1171
1632
|
if (config.elixirJobs === "oban" && config.elixirOrm !== "ecto-sql") incompatibilityError({
|
|
1172
1633
|
message: "Oban requires Ecto SQL with PostgreSQL in the generated Phoenix scaffold.",
|
|
@@ -1343,6 +1804,59 @@ function validatePythonApiConstraints(config) {
|
|
|
1343
1804
|
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"]
|
|
1344
1805
|
});
|
|
1345
1806
|
}
|
|
1807
|
+
function validatePythonExpansionConstraints(config) {
|
|
1808
|
+
const pythonConfig = config.ecosystem === "python" ? config : config.stackParts?.some((part) => part.role === "backend" && part.ecosystem === "python" && part.source !== "provided") ? (0, types_exports.stackGraphToLegacyProjectConfigForEcosystem)(config, "python") : void 0;
|
|
1809
|
+
if (!pythonConfig) return;
|
|
1810
|
+
config = pythonConfig;
|
|
1811
|
+
if (config.pythonOrm === "pymongo" && config.database !== "mongodb") incompatibilityError({
|
|
1812
|
+
message: "PyMongo requires --database mongodb.",
|
|
1813
|
+
provided: {
|
|
1814
|
+
database: config.database || "none",
|
|
1815
|
+
"python-orm": "pymongo"
|
|
1816
|
+
},
|
|
1817
|
+
suggestions: ["Add --database mongodb when using --python-orm pymongo", "Choose another --python-orm for relational databases"]
|
|
1818
|
+
});
|
|
1819
|
+
if (config.database === "mongodb" && config.pythonOrm !== "pymongo" && config.pythonOrm !== "none" && config.pythonOrm !== void 0) incompatibilityError({
|
|
1820
|
+
message: "MongoDB requires --python-orm pymongo (relational ORMs cannot use it).",
|
|
1821
|
+
provided: {
|
|
1822
|
+
database: "mongodb",
|
|
1823
|
+
"python-orm": config.pythonOrm
|
|
1824
|
+
},
|
|
1825
|
+
suggestions: ["Use --python-orm pymongo with --database mongodb", "Choose a relational database (postgres, sqlite, mysql) for this ORM"]
|
|
1826
|
+
});
|
|
1827
|
+
if ((config.pythonWebFramework === "none" || config.pythonWebFramework === "aiohttp" || config.pythonWebFramework === "starlette" || config.pythonWebFramework === "streamlit") && config.pythonGraphql !== void 0 && config.pythonGraphql !== "none") incompatibilityError({
|
|
1828
|
+
message: "Python GraphQL is only wired for FastAPI, Django, Flask, and Litestar.",
|
|
1829
|
+
provided: {
|
|
1830
|
+
"python-web-framework": config.pythonWebFramework,
|
|
1831
|
+
"python-graphql": config.pythonGraphql
|
|
1832
|
+
},
|
|
1833
|
+
suggestions: ["Use FastAPI, Django, Flask, or Litestar with --python-graphql", "Set --python-graphql none"]
|
|
1834
|
+
});
|
|
1835
|
+
if ((config.pythonWebFramework === "none" || config.pythonWebFramework === "aiohttp" || config.pythonWebFramework === "starlette" || config.pythonWebFramework === "streamlit") && config.pythonAuth !== void 0 && config.pythonAuth !== "none") incompatibilityError({
|
|
1836
|
+
message: `Python auth routes are not wired for ${config.pythonWebFramework}.`,
|
|
1837
|
+
provided: {
|
|
1838
|
+
"python-web-framework": config.pythonWebFramework,
|
|
1839
|
+
"python-auth": config.pythonAuth
|
|
1840
|
+
},
|
|
1841
|
+
suggestions: ["Use FastAPI, Django, Flask, or Litestar with --python-auth", "Set --python-auth none"]
|
|
1842
|
+
});
|
|
1843
|
+
if ((config.pythonWebFramework === "streamlit" || config.pythonWebFramework === "none") && config.pythonObservability === "prometheus-client") incompatibilityError({
|
|
1844
|
+
message: "prometheus-client needs an HTTP server framework to expose /metrics.",
|
|
1845
|
+
provided: {
|
|
1846
|
+
"python-web-framework": config.pythonWebFramework,
|
|
1847
|
+
"python-observability": "prometheus-client"
|
|
1848
|
+
},
|
|
1849
|
+
suggestions: ["Use FastAPI, Django, Flask, Litestar, Starlette, or aiohttp", "Set --python-observability none"]
|
|
1850
|
+
});
|
|
1851
|
+
if (config.pythonServer === "gunicorn" && (config.pythonWebFramework === "streamlit" || config.pythonWebFramework === "none")) incompatibilityError({
|
|
1852
|
+
message: "Gunicorn requires a WSGI, ASGI, or aiohttp Python application.",
|
|
1853
|
+
provided: {
|
|
1854
|
+
"python-web-framework": config.pythonWebFramework,
|
|
1855
|
+
"python-server": "gunicorn"
|
|
1856
|
+
},
|
|
1857
|
+
suggestions: ["Use FastAPI, Django, Flask, Litestar, or aiohttp with --python-server gunicorn", "Set --python-server none for Streamlit or framework-free projects"]
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1346
1860
|
function validateI18nConstraints(config) {
|
|
1347
1861
|
if (config.i18n !== "intlayer") return;
|
|
1348
1862
|
const { web } = splitFrontends$1(config.frontend ?? []);
|
|
@@ -1374,6 +1888,8 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
1374
1888
|
validateFrontendConstraints(config, providedFlags);
|
|
1375
1889
|
validateApiConstraints(config, options);
|
|
1376
1890
|
validatePythonApiConstraints(config);
|
|
1891
|
+
validatePythonExpansionConstraints(config);
|
|
1892
|
+
validateRustExpansionCompatibility(config);
|
|
1377
1893
|
validateEmailConstraints(config);
|
|
1378
1894
|
validateObservabilityConstraints(config);
|
|
1379
1895
|
validateCachingConstraints(config);
|
|
@@ -1381,6 +1897,7 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
1381
1897
|
validateSearchConstraints(config);
|
|
1382
1898
|
validateJavaConstraints(config, providedFlags);
|
|
1383
1899
|
validateElixirConstraints(config);
|
|
1900
|
+
validateScopedLibraryFlags(config);
|
|
1384
1901
|
validateI18nConstraints(config);
|
|
1385
1902
|
const hasGraphBackend = config.stackParts?.some((part) => part.role === "backend" && !part.ownerPartId && part.source !== "provided" && part.ecosystem !== "typescript" && part.ecosystem !== "react-native" && part.ecosystem !== "universal");
|
|
1386
1903
|
if (!(providedFlags.has("serverDeploy") && !options.yes && !options.part?.length && options.ecosystem === void 0 && options.backend === void 0 && config.stackParts === void 0)) validateServerDeployRequiresBackend(config.serverDeploy, config.backend, Boolean(hasGraphBackend));
|
|
@@ -1419,12 +1936,15 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
1419
1936
|
});
|
|
1420
1937
|
}
|
|
1421
1938
|
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);
|
|
1939
|
+
validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth, config.backend, config.runtime, config.ecosystem, config.rustFrontend, config.javaWebFramework, config.database, config.api);
|
|
1423
1940
|
config.addons = [...new Set(config.addons)];
|
|
1424
1941
|
}
|
|
1425
1942
|
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
|
|
1426
1943
|
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend ?? []);
|
|
1427
1944
|
validateAIFrontendCompatibility(config.ai, config.frontend ?? []);
|
|
1945
|
+
validateAIBackendCompatibility(config.ai, config.backend);
|
|
1946
|
+
validateRealtimeCompatibility(config.realtime, config.backend);
|
|
1947
|
+
validateCSSFrameworkFrontendCompatibility(config.cssFramework, config.frontend ?? []);
|
|
1428
1948
|
validateUILibraryFrontendCompatibility(config.uiLibrary, config.frontend ?? [], config.astroIntegration);
|
|
1429
1949
|
validateUILibraryCSSFrameworkCompatibility(config.uiLibrary, config.cssFramework);
|
|
1430
1950
|
validateShadcnConstraints(config, providedFlags);
|
|
@@ -1442,6 +1962,7 @@ function validateConfigForProgrammaticUse(config) {
|
|
|
1442
1962
|
if (config.frontend && config.frontend.length > 0) ensureSingleWebAndNative(config.frontend);
|
|
1443
1963
|
validateApiFrontendCompatibility(config.api, config.frontend, config.astroIntegration);
|
|
1444
1964
|
validatePythonApiConstraints(config);
|
|
1965
|
+
validatePythonExpansionConstraints(config);
|
|
1445
1966
|
validateEmailConstraints(config);
|
|
1446
1967
|
validateObservabilityConstraints(config);
|
|
1447
1968
|
validateCachingConstraints(config);
|
|
@@ -1449,11 +1970,16 @@ function validateConfigForProgrammaticUse(config) {
|
|
|
1449
1970
|
validateSearchConstraints(config);
|
|
1450
1971
|
validateJavaConstraints(config);
|
|
1451
1972
|
validateElixirConstraints(config);
|
|
1973
|
+
validateRustExpansionCompatibility(config);
|
|
1974
|
+
validateScopedLibraryFlags(config);
|
|
1452
1975
|
validateI18nConstraints(config);
|
|
1453
1976
|
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);
|
|
1977
|
+
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
1978
|
validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
|
|
1456
1979
|
validateAIFrontendCompatibility(config.ai, config.frontend ?? []);
|
|
1980
|
+
validateAIBackendCompatibility(config.ai, config.backend);
|
|
1981
|
+
validateRealtimeCompatibility(config.realtime, config.backend);
|
|
1982
|
+
validateCSSFrameworkFrontendCompatibility(config.cssFramework, config.frontend ?? []);
|
|
1457
1983
|
validateUILibraryFrontendCompatibility(config.uiLibrary, config.frontend ?? [], config.astroIntegration);
|
|
1458
1984
|
validateUILibraryCSSFrameworkCompatibility(config.uiLibrary, config.cssFramework);
|
|
1459
1985
|
validatePeerDependencies(config);
|
|
@@ -1497,4 +2023,4 @@ async function formatProject(projectDir) {
|
|
|
1497
2023
|
}
|
|
1498
2024
|
|
|
1499
2025
|
//#endregion
|
|
1500
|
-
export { CreateCommandInputSchema as
|
|
2026
|
+
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 };
|