create-better-fullstack 1.6.3 → 1.7.1
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/addons-setup-BdKQJ1h6.mjs +5 -0
- package/dist/{addons-setup-DQa6TRrx.mjs → addons-setup-CGhYT2qC.mjs} +1 -1
- package/dist/{bts-config-B_rZ4_sj.mjs → bts-config-bOXo9tbL.mjs} +3 -0
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +99 -52
- package/dist/index.mjs +507 -275
- package/dist/{mcp-CuEEG8e5.mjs → mcp-BxEilSGM.mjs} +1 -1
- package/dist/mcp-entry.mjs +116 -15
- package/package.json +10 -10
- package/dist/addons-setup-CBK1Htlc.mjs +0 -5
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as __reExport } from "./chunk-CCII7kTE.mjs";
|
|
3
|
-
import { a as DEFAULT_CONFIG, c as getDefaultConfig, i as getLatestCLIVersion, l as getUserPkgManager, n as updateBtsConfig, o as DEFAULT_UI_LIBRARY_BY_FRONTEND, r as writeBtsConfig, t as readBtsConfig } from "./bts-config-
|
|
4
|
-
import { _ as setIsFirstPrompt$1, a as canPromptInteractively, c as CLIError, d as exitWithError, f as handleError, g as runWithContextAsync, h as isSilent, l as UserCancelledError, m as isFirstPrompt, o as getPackageExecutionArgs, p as didLastPromptShowUI, s as addPackageDependency, t as setupAddons, u as exitCancelled, v as setLastPromptShownUI } from "./addons-setup-
|
|
3
|
+
import { a as DEFAULT_CONFIG, c as getDefaultConfig, i as getLatestCLIVersion, l as getUserPkgManager, n as updateBtsConfig, o as DEFAULT_UI_LIBRARY_BY_FRONTEND, r as writeBtsConfig, t as readBtsConfig } from "./bts-config-bOXo9tbL.mjs";
|
|
4
|
+
import { _ as setIsFirstPrompt$1, a as canPromptInteractively, c as CLIError, d as exitWithError, f as handleError, g as runWithContextAsync, h as isSilent, l as UserCancelledError, m as isFirstPrompt, o as getPackageExecutionArgs, p as didLastPromptShowUI, s as addPackageDependency, t as setupAddons, u as exitCancelled, v as setLastPromptShownUI } from "./addons-setup-CGhYT2qC.mjs";
|
|
5
5
|
import { cancel, confirm, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
|
|
6
6
|
import { createRouterClient, os } from "@orpc/server";
|
|
7
7
|
import pc from "picocolors";
|
|
@@ -10,7 +10,7 @@ import z from "zod";
|
|
|
10
10
|
import envPaths from "env-paths";
|
|
11
11
|
import fs from "fs-extra";
|
|
12
12
|
import path from "node:path";
|
|
13
|
-
import { allowedApisForFrontends, getCompatibleAddons, getCompatibleCSSFrameworks, getCompatibleUILibraries, getLocalWebDevPort, hasDockerComposeCompatibleFrontend, hasWebStyling, isExampleAIAllowed, isExampleChatSdkAllowed, isFrontendAllowedWithBackend, isWebFrontend, requiresChatSdkVercelAIForSelection, splitFrontends, validateAddonCompatibility } from "@better-fullstack/types";
|
|
13
|
+
import { allowedApisForFrontends, getAIFrontendCompatibilityIssue, getApiFrontendCompatibilityIssue, getCompatibleAddons, getCompatibleCSSFrameworks, getCompatibleUILibraries, getLocalWebDevPort, hasDockerComposeCompatibleFrontend, hasWebStyling, isExampleAIAllowed, isExampleChatSdkAllowed, isFrontendAllowedWithBackend, isWebFrontend, requiresChatSdkVercelAIForSelection, splitFrontends, validateAddonCompatibility } from "@better-fullstack/types";
|
|
14
14
|
import { ECOSYSTEM_GROUPS, EMBEDDED_TEMPLATES, EMBEDDED_TEMPLATES as EMBEDDED_TEMPLATES$1, TEMPLATE_COUNT, VirtualFileSystem, VirtualFileSystem as VirtualFileSystem$1, checkAllVersions, generateCliReport, generateVirtualProject, generateVirtualProject as generateVirtualProject$1, listEcosystems, processAddonTemplates, processAddonsDeps, validatePreflightConfig } from "@better-fullstack/template-generator";
|
|
15
15
|
import gradient from "gradient-string";
|
|
16
16
|
import path$1 from "path";
|
|
@@ -18,6 +18,7 @@ import { writeTreeToFilesystem } from "@better-fullstack/template-generator/fs-w
|
|
|
18
18
|
import consola, { consola as consola$1 } from "consola";
|
|
19
19
|
import { ConfirmPrompt, GroupMultiSelectPrompt, MultiSelectPrompt, SelectPrompt, isCancel as isCancel$1 } from "@clack/core";
|
|
20
20
|
import { $, execa } from "execa";
|
|
21
|
+
import { cliInputToProjectConfigPartial } from "@better-fullstack/types/stack-translation";
|
|
21
22
|
import os$1 from "node:os";
|
|
22
23
|
import { format } from "oxfmt";
|
|
23
24
|
|
|
@@ -429,16 +430,17 @@ const CreateCommandOptionsSchema = z.object({
|
|
|
429
430
|
pythonValidation: types_exports.PythonValidationSchema.optional().describe("Python validation (pydantic)"),
|
|
430
431
|
pythonAi: z.array(types_exports.PythonAiSchema).optional().describe("Python AI/ML frameworks"),
|
|
431
432
|
pythonAuth: types_exports.PythonAuthSchema.optional().describe("Python auth library (authlib, jwt)"),
|
|
433
|
+
pythonApi: types_exports.PythonApiSchema.optional().describe("Python API framework (django-rest-framework, django-ninja)"),
|
|
432
434
|
pythonTaskQueue: types_exports.PythonTaskQueueSchema.optional().describe("Python task queue (celery)"),
|
|
433
435
|
pythonGraphql: types_exports.PythonGraphqlSchema.optional().describe("Python GraphQL framework (strawberry)"),
|
|
434
|
-
pythonQuality: types_exports.PythonQualitySchema.optional().describe("Python code quality (ruff)"),
|
|
436
|
+
pythonQuality: types_exports.PythonQualitySchema.optional().describe("Python code quality (ruff, mypy, pyright)"),
|
|
435
437
|
goWebFramework: types_exports.GoWebFrameworkSchema.optional().describe("Go web framework (gin, echo, fiber)"),
|
|
436
438
|
goOrm: types_exports.GoOrmSchema.optional().describe("Go ORM/database (gorm, sqlc)"),
|
|
437
439
|
goApi: types_exports.GoApiSchema.optional().describe("Go API layer (grpc-go)"),
|
|
438
|
-
goCli: types_exports.GoCliSchema.optional().describe("Go CLI tools (cobra, bubbletea)"),
|
|
440
|
+
goCli: types_exports.GoCliSchema.optional().describe("Go CLI tools (cobra, bubbletea, urfave-cli)"),
|
|
439
441
|
goLogging: types_exports.GoLoggingSchema.optional().describe("Go logging (zap, zerolog, slog)"),
|
|
440
442
|
goAuth: types_exports.GoAuthSchema.optional().describe("Go auth (casbin, jwt)"),
|
|
441
|
-
javaWebFramework: types_exports.JavaWebFrameworkSchema.optional().describe("Java web framework (spring-boot, none)"),
|
|
443
|
+
javaWebFramework: types_exports.JavaWebFrameworkSchema.optional().describe("Java web framework (spring-boot, quarkus, none)"),
|
|
442
444
|
javaBuildTool: types_exports.JavaBuildToolSchema.optional().describe("Java build tool (maven, gradle, none)"),
|
|
443
445
|
javaOrm: types_exports.JavaOrmSchema.optional().describe("Java ORM/database (spring-data-jpa)"),
|
|
444
446
|
javaAuth: types_exports.JavaAuthSchema.optional().describe("Java auth (spring-security)"),
|
|
@@ -602,74 +604,12 @@ function validateWorkersCompatibility(providedFlags, options, config) {
|
|
|
602
604
|
});
|
|
603
605
|
}
|
|
604
606
|
function validateApiFrontendCompatibility(api, frontends = [], astroIntegration) {
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
const includesRedwood = frontends.includes("redwood");
|
|
612
|
-
const includesFresh = frontends.includes("fresh");
|
|
613
|
-
const includesSolidStart = frontends.includes("solid-start");
|
|
614
|
-
if ((includesNuxt || includesSvelte || includesSolid || includesSolidStart) && (api === "trpc" || api === "ts-rest" || api === "garph")) {
|
|
615
|
-
const apiName = api === "trpc" ? "tRPC" : api === "ts-rest" ? "ts-rest" : "garph";
|
|
616
|
-
const incompatibleFrontend = includesNuxt ? "nuxt" : includesSvelte ? "svelte" : includesSolid ? "solid" : "solid-start";
|
|
617
|
-
incompatibilityError({
|
|
618
|
-
message: `${apiName} API requires React-based frontends.`,
|
|
619
|
-
provided: {
|
|
620
|
-
api,
|
|
621
|
-
frontend: incompatibleFrontend
|
|
622
|
-
},
|
|
623
|
-
suggestions: [
|
|
624
|
-
"Use --api orpc (works with all frontends)",
|
|
625
|
-
"Use --api none",
|
|
626
|
-
"Choose next, react-router, react-vite, or tanstack-start"
|
|
627
|
-
]
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
if (includesQwik && api && api !== "none") incompatibilityError({
|
|
631
|
-
message: "Qwik has built-in server capabilities and doesn't support external APIs.",
|
|
632
|
-
provided: {
|
|
633
|
-
api,
|
|
634
|
-
frontend: "qwik"
|
|
635
|
-
},
|
|
636
|
-
suggestions: ["Use --api none with Qwik"]
|
|
637
|
-
});
|
|
638
|
-
if (includesAngular && api && api !== "none") incompatibilityError({
|
|
639
|
-
message: "Angular has built-in HttpClient and doesn't support external APIs.",
|
|
640
|
-
provided: {
|
|
641
|
-
api,
|
|
642
|
-
frontend: "angular"
|
|
643
|
-
},
|
|
644
|
-
suggestions: ["Use --api none with Angular"]
|
|
645
|
-
});
|
|
646
|
-
if (includesRedwood && api && api !== "none") incompatibilityError({
|
|
647
|
-
message: "RedwoodJS has built-in GraphQL API and doesn't support external APIs.",
|
|
648
|
-
provided: {
|
|
649
|
-
api,
|
|
650
|
-
frontend: "redwood"
|
|
651
|
-
},
|
|
652
|
-
suggestions: ["Use --api none with RedwoodJS"]
|
|
653
|
-
});
|
|
654
|
-
if (includesFresh && api && api !== "none") incompatibilityError({
|
|
655
|
-
message: "Fresh has built-in server capabilities and doesn't support external APIs.",
|
|
656
|
-
provided: {
|
|
657
|
-
api,
|
|
658
|
-
frontend: "fresh"
|
|
659
|
-
},
|
|
660
|
-
suggestions: ["Use --api none with Fresh"]
|
|
661
|
-
});
|
|
662
|
-
if (includesAstro && astroIntegration && astroIntegration !== "react" && (api === "trpc" || api === "ts-rest" || api === "garph")) incompatibilityError({
|
|
663
|
-
message: `${api === "trpc" ? "tRPC" : api === "ts-rest" ? "ts-rest" : "garph"} API requires React integration with Astro.`,
|
|
664
|
-
provided: {
|
|
665
|
-
api,
|
|
666
|
-
"astro-integration": astroIntegration
|
|
667
|
-
},
|
|
668
|
-
suggestions: [
|
|
669
|
-
"Use --api orpc (works with all Astro integrations)",
|
|
670
|
-
"Use --api none",
|
|
671
|
-
"Use --astro-integration react"
|
|
672
|
-
]
|
|
607
|
+
const issue = getApiFrontendCompatibilityIssue(api, frontends, astroIntegration);
|
|
608
|
+
if (!issue) return;
|
|
609
|
+
incompatibilityError({
|
|
610
|
+
message: issue.message,
|
|
611
|
+
provided: issue.provided ?? {},
|
|
612
|
+
suggestions: issue.suggestions ?? []
|
|
673
613
|
});
|
|
674
614
|
}
|
|
675
615
|
function isFrontendAllowedWithBackend$1(frontend, backend, auth) {
|
|
@@ -783,18 +723,9 @@ function validateExamplesCompatibility(examples, backend, frontend, runtime, ai)
|
|
|
783
723
|
* Server-side @tanstack/ai core works anywhere, but client adapters only exist for React and Solid.
|
|
784
724
|
*/
|
|
785
725
|
function validateAIFrontendCompatibility(ai, frontends = []) {
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
"react-router",
|
|
790
|
-
"react-vite",
|
|
791
|
-
"tanstack-start",
|
|
792
|
-
"next",
|
|
793
|
-
"redwood",
|
|
794
|
-
"solid",
|
|
795
|
-
"solid-start"
|
|
796
|
-
];
|
|
797
|
-
if (!frontends.some((f) => compatibleFrontends.includes(f))) exitWithError("TanStack AI requires React or Solid frontend (no Vue/Svelte/Angular adapter yet). Please use a React-based frontend (Next.js, TanStack Router, React Router, etc.) or Solid.");
|
|
726
|
+
const issue = getAIFrontendCompatibilityIssue(ai, frontends);
|
|
727
|
+
if (!issue) return;
|
|
728
|
+
exitWithError(issue.message);
|
|
798
729
|
}
|
|
799
730
|
/**
|
|
800
731
|
* Validates that a UI library is compatible with the selected frontend(s)
|
|
@@ -1482,16 +1413,20 @@ async function applyDependencyVersionChannel(projectDir, channel) {
|
|
|
1482
1413
|
//#endregion
|
|
1483
1414
|
//#region src/helpers/core/install-dependencies.ts
|
|
1484
1415
|
function getInstallEnvironment(packageManager) {
|
|
1485
|
-
if (packageManager
|
|
1486
|
-
return {
|
|
1416
|
+
if (packageManager === "yarn") return {
|
|
1487
1417
|
YARN_ENABLE_HARDENED_MODE: "0",
|
|
1488
1418
|
YARN_ENABLE_IMMUTABLE_INSTALLS: "false"
|
|
1489
1419
|
};
|
|
1490
1420
|
}
|
|
1421
|
+
function getInstallArgs(packageManager) {
|
|
1422
|
+
if (packageManager === "pnpm") return ["install", "--dangerously-allow-all-builds"];
|
|
1423
|
+
return ["install"];
|
|
1424
|
+
}
|
|
1491
1425
|
async function installDependencies({ projectDir, packageManager }) {
|
|
1492
1426
|
const s = spinner();
|
|
1493
1427
|
try {
|
|
1494
1428
|
s.start(`Running ${packageManager} install...`);
|
|
1429
|
+
const installArgs = getInstallArgs(packageManager);
|
|
1495
1430
|
await $({
|
|
1496
1431
|
cwd: projectDir,
|
|
1497
1432
|
env: {
|
|
@@ -1499,7 +1434,7 @@ async function installDependencies({ projectDir, packageManager }) {
|
|
|
1499
1434
|
...getInstallEnvironment(packageManager)
|
|
1500
1435
|
},
|
|
1501
1436
|
stderr: "inherit"
|
|
1502
|
-
})`${packageManager}
|
|
1437
|
+
})`${packageManager} ${installArgs}`;
|
|
1503
1438
|
s.stop("Dependencies installed successfully");
|
|
1504
1439
|
} catch (error) {
|
|
1505
1440
|
s.stop(pc.red("Failed to install dependencies"));
|
|
@@ -2244,6 +2179,17 @@ const CACHING_PROMPT_OPTIONS = [{
|
|
|
2244
2179
|
hint: "Skip caching layer setup"
|
|
2245
2180
|
}];
|
|
2246
2181
|
function resolveCachingPrompt(context = {}) {
|
|
2182
|
+
if (context.ecosystem && context.ecosystem !== "typescript") return context.caching !== void 0 ? {
|
|
2183
|
+
shouldPrompt: false,
|
|
2184
|
+
mode: "single",
|
|
2185
|
+
options: CACHING_PROMPT_OPTIONS,
|
|
2186
|
+
autoValue: context.caching
|
|
2187
|
+
} : {
|
|
2188
|
+
shouldPrompt: true,
|
|
2189
|
+
mode: "single",
|
|
2190
|
+
options: CACHING_PROMPT_OPTIONS,
|
|
2191
|
+
initialValue: "none"
|
|
2192
|
+
};
|
|
2247
2193
|
if (context.backend === "none" || context.backend === "convex") return {
|
|
2248
2194
|
shouldPrompt: false,
|
|
2249
2195
|
mode: "single",
|
|
@@ -2262,10 +2208,11 @@ function resolveCachingPrompt(context = {}) {
|
|
|
2262
2208
|
initialValue: "none"
|
|
2263
2209
|
};
|
|
2264
2210
|
}
|
|
2265
|
-
async function getCachingChoice(caching, backend) {
|
|
2211
|
+
async function getCachingChoice(caching, backend, ecosystem) {
|
|
2266
2212
|
const resolution = resolveCachingPrompt({
|
|
2267
2213
|
caching,
|
|
2268
|
-
backend
|
|
2214
|
+
backend,
|
|
2215
|
+
ecosystem
|
|
2269
2216
|
});
|
|
2270
2217
|
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
2271
2218
|
const response = await navigableSelect({
|
|
@@ -2744,8 +2691,10 @@ const EMAIL_PROMPT_OPTIONS = [
|
|
|
2744
2691
|
hint: "No email integration"
|
|
2745
2692
|
}
|
|
2746
2693
|
];
|
|
2694
|
+
const NON_TYPESCRIPT_EMAIL_PROMPT_OPTIONS = EMAIL_PROMPT_OPTIONS.filter((option) => option.value === "resend" || option.value === "none");
|
|
2747
2695
|
function resolveEmailPrompt(context = {}) {
|
|
2748
|
-
|
|
2696
|
+
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_EMAIL_PROMPT_OPTIONS : EMAIL_PROMPT_OPTIONS;
|
|
2697
|
+
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
2749
2698
|
shouldPrompt: false,
|
|
2750
2699
|
mode: "single",
|
|
2751
2700
|
options: [],
|
|
@@ -2754,19 +2703,20 @@ function resolveEmailPrompt(context = {}) {
|
|
|
2754
2703
|
return context.email !== void 0 ? {
|
|
2755
2704
|
shouldPrompt: false,
|
|
2756
2705
|
mode: "single",
|
|
2757
|
-
options
|
|
2706
|
+
options,
|
|
2758
2707
|
autoValue: context.email
|
|
2759
2708
|
} : {
|
|
2760
2709
|
shouldPrompt: true,
|
|
2761
2710
|
mode: "single",
|
|
2762
|
-
options
|
|
2711
|
+
options,
|
|
2763
2712
|
initialValue: DEFAULT_CONFIG.email ?? "none"
|
|
2764
2713
|
};
|
|
2765
2714
|
}
|
|
2766
|
-
async function getEmailChoice(email, backend) {
|
|
2715
|
+
async function getEmailChoice(email, backend, ecosystem) {
|
|
2767
2716
|
const resolution = resolveEmailPrompt({
|
|
2768
2717
|
email,
|
|
2769
|
-
backend
|
|
2718
|
+
backend,
|
|
2719
|
+
ecosystem
|
|
2770
2720
|
});
|
|
2771
2721
|
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
2772
2722
|
const response = await navigableSelect({
|
|
@@ -3235,6 +3185,11 @@ const GO_CLI_PROMPT_OPTIONS = [
|
|
|
3235
3185
|
label: "Bubble Tea",
|
|
3236
3186
|
hint: "Powerful TUI framework based on The Elm Architecture"
|
|
3237
3187
|
},
|
|
3188
|
+
{
|
|
3189
|
+
value: "urfave-cli",
|
|
3190
|
+
label: "urfave/cli",
|
|
3191
|
+
hint: "Declarative CLI framework with commands, flags, and shell completion"
|
|
3192
|
+
},
|
|
3238
3193
|
{
|
|
3239
3194
|
value: "none",
|
|
3240
3195
|
label: "None",
|
|
@@ -3257,6 +3212,11 @@ const GO_LOGGING_PROMPT_OPTIONS = [
|
|
|
3257
3212
|
label: "slog",
|
|
3258
3213
|
hint: "Go 1.21+ stdlib structured logging (no external dependency)"
|
|
3259
3214
|
},
|
|
3215
|
+
{
|
|
3216
|
+
value: "logrus",
|
|
3217
|
+
label: "Logrus",
|
|
3218
|
+
hint: "Classic structured logger with hooks and formatter ecosystem"
|
|
3219
|
+
},
|
|
3260
3220
|
{
|
|
3261
3221
|
value: "none",
|
|
3262
3222
|
label: "None",
|
|
@@ -3471,15 +3431,23 @@ async function getinstallChoice(install, ecosystem, javaBuildTool) {
|
|
|
3471
3431
|
|
|
3472
3432
|
//#endregion
|
|
3473
3433
|
//#region src/prompts/java-ecosystem.ts
|
|
3474
|
-
const JAVA_WEB_FRAMEWORK_PROMPT_OPTIONS = [
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3434
|
+
const JAVA_WEB_FRAMEWORK_PROMPT_OPTIONS = [
|
|
3435
|
+
{
|
|
3436
|
+
value: "spring-boot",
|
|
3437
|
+
label: "Spring Boot",
|
|
3438
|
+
hint: "Production-grade Java framework with embedded server and auto-configuration"
|
|
3439
|
+
},
|
|
3440
|
+
{
|
|
3441
|
+
value: "quarkus",
|
|
3442
|
+
label: "Quarkus",
|
|
3443
|
+
hint: "Cloud-native Java framework optimized for fast startup and lower memory use"
|
|
3444
|
+
},
|
|
3445
|
+
{
|
|
3446
|
+
value: "none",
|
|
3447
|
+
label: "None",
|
|
3448
|
+
hint: "No Java web framework"
|
|
3449
|
+
}
|
|
3450
|
+
];
|
|
3483
3451
|
const JAVA_BUILD_TOOL_PROMPT_OPTIONS = [
|
|
3484
3452
|
{
|
|
3485
3453
|
value: "maven",
|
|
@@ -3608,6 +3576,46 @@ const JAVA_LIBRARY_PROMPT_OPTIONS = [
|
|
|
3608
3576
|
label: "Caffeine",
|
|
3609
3577
|
hint: "High-performance in-memory caching through Spring Cache"
|
|
3610
3578
|
},
|
|
3579
|
+
{
|
|
3580
|
+
value: "resilience4j",
|
|
3581
|
+
label: "Resilience4j",
|
|
3582
|
+
hint: "Fault tolerance patterns for retries, circuit breakers, and rate limiting"
|
|
3583
|
+
},
|
|
3584
|
+
{
|
|
3585
|
+
value: "spring-webflux",
|
|
3586
|
+
label: "Spring WebFlux",
|
|
3587
|
+
hint: "Reactive HTTP stack for Spring applications"
|
|
3588
|
+
},
|
|
3589
|
+
{
|
|
3590
|
+
value: "spring-batch",
|
|
3591
|
+
label: "Spring Batch",
|
|
3592
|
+
hint: "Batch processing framework for ETL and scheduled jobs"
|
|
3593
|
+
},
|
|
3594
|
+
{
|
|
3595
|
+
value: "spring-kafka",
|
|
3596
|
+
label: "Spring for Apache Kafka",
|
|
3597
|
+
hint: "Kafka producer, consumer, and listener integration"
|
|
3598
|
+
},
|
|
3599
|
+
{
|
|
3600
|
+
value: "spring-mail",
|
|
3601
|
+
label: "Spring Mail",
|
|
3602
|
+
hint: "Email support through Jakarta Mail and Spring abstractions"
|
|
3603
|
+
},
|
|
3604
|
+
{
|
|
3605
|
+
value: "spring-devtools",
|
|
3606
|
+
label: "Spring Boot DevTools",
|
|
3607
|
+
hint: "Developer-time restart and local productivity support"
|
|
3608
|
+
},
|
|
3609
|
+
{
|
|
3610
|
+
value: "micrometer-prometheus",
|
|
3611
|
+
label: "Micrometer Prometheus",
|
|
3612
|
+
hint: "Prometheus metrics registry for Micrometer and Actuator"
|
|
3613
|
+
},
|
|
3614
|
+
{
|
|
3615
|
+
value: "thymeleaf",
|
|
3616
|
+
label: "Thymeleaf",
|
|
3617
|
+
hint: "Server-rendered HTML templates for Spring MVC apps"
|
|
3618
|
+
},
|
|
3611
3619
|
{
|
|
3612
3620
|
value: "none",
|
|
3613
3621
|
label: "None",
|
|
@@ -3900,8 +3908,10 @@ const OBSERVABILITY_PROMPT_OPTIONS = [
|
|
|
3900
3908
|
hint: "Skip observability/tracing setup"
|
|
3901
3909
|
}
|
|
3902
3910
|
];
|
|
3911
|
+
const NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS = OBSERVABILITY_PROMPT_OPTIONS.filter((option) => option.value === "sentry" || option.value === "none");
|
|
3903
3912
|
function resolveObservabilityPrompt(context = {}) {
|
|
3904
|
-
|
|
3913
|
+
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS : OBSERVABILITY_PROMPT_OPTIONS;
|
|
3914
|
+
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
3905
3915
|
shouldPrompt: false,
|
|
3906
3916
|
mode: "single",
|
|
3907
3917
|
options: [],
|
|
@@ -3910,19 +3920,20 @@ function resolveObservabilityPrompt(context = {}) {
|
|
|
3910
3920
|
return context.observability !== void 0 ? {
|
|
3911
3921
|
shouldPrompt: false,
|
|
3912
3922
|
mode: "single",
|
|
3913
|
-
options
|
|
3923
|
+
options,
|
|
3914
3924
|
autoValue: context.observability
|
|
3915
3925
|
} : {
|
|
3916
3926
|
shouldPrompt: true,
|
|
3917
3927
|
mode: "single",
|
|
3918
|
-
options
|
|
3928
|
+
options,
|
|
3919
3929
|
initialValue: "none"
|
|
3920
3930
|
};
|
|
3921
3931
|
}
|
|
3922
|
-
async function getObservabilityChoice(observability, backend) {
|
|
3932
|
+
async function getObservabilityChoice(observability, backend, ecosystem) {
|
|
3923
3933
|
const resolution = resolveObservabilityPrompt({
|
|
3924
3934
|
observability,
|
|
3925
|
-
backend
|
|
3935
|
+
backend,
|
|
3936
|
+
ecosystem
|
|
3926
3937
|
});
|
|
3927
3938
|
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
3928
3939
|
const response = await navigableSelect({
|
|
@@ -4220,6 +4231,11 @@ const PYTHON_AI_PROMPT_OPTIONS = [
|
|
|
4220
4231
|
value: "crewai",
|
|
4221
4232
|
label: "CrewAI",
|
|
4222
4233
|
hint: "Multi-agent orchestration framework"
|
|
4234
|
+
},
|
|
4235
|
+
{
|
|
4236
|
+
value: "haystack",
|
|
4237
|
+
label: "Haystack",
|
|
4238
|
+
hint: "Composable LLM pipelines, RAG, and search applications"
|
|
4223
4239
|
}
|
|
4224
4240
|
];
|
|
4225
4241
|
const PYTHON_AUTH_PROMPT_OPTIONS = [
|
|
@@ -4239,33 +4255,89 @@ const PYTHON_AUTH_PROMPT_OPTIONS = [
|
|
|
4239
4255
|
hint: "No authentication library"
|
|
4240
4256
|
}
|
|
4241
4257
|
];
|
|
4242
|
-
const
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
},
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4258
|
+
const PYTHON_API_PROMPT_OPTIONS = [
|
|
4259
|
+
{
|
|
4260
|
+
value: "django-rest-framework",
|
|
4261
|
+
label: "Django REST Framework",
|
|
4262
|
+
hint: "Mature, widely used toolkit for building Django REST APIs"
|
|
4263
|
+
},
|
|
4264
|
+
{
|
|
4265
|
+
value: "django-ninja",
|
|
4266
|
+
label: "Django Ninja",
|
|
4267
|
+
hint: "FastAPI-style Django APIs with type hints and automatic OpenAPI docs"
|
|
4268
|
+
},
|
|
4269
|
+
{
|
|
4270
|
+
value: "none",
|
|
4271
|
+
label: "None",
|
|
4272
|
+
hint: "No additional Python API framework"
|
|
4273
|
+
}
|
|
4274
|
+
];
|
|
4275
|
+
const PYTHON_TASK_QUEUE_PROMPT_OPTIONS = [
|
|
4276
|
+
{
|
|
4277
|
+
value: "celery",
|
|
4278
|
+
label: "Celery",
|
|
4279
|
+
hint: "Distributed task queue for Python"
|
|
4280
|
+
},
|
|
4281
|
+
{
|
|
4282
|
+
value: "rq",
|
|
4283
|
+
label: "RQ",
|
|
4284
|
+
hint: "Simple Redis-backed job queue for Python"
|
|
4285
|
+
},
|
|
4286
|
+
{
|
|
4287
|
+
value: "dramatiq",
|
|
4288
|
+
label: "Dramatiq",
|
|
4289
|
+
hint: "Distributed task processing with Redis or RabbitMQ brokers"
|
|
4290
|
+
},
|
|
4291
|
+
{
|
|
4292
|
+
value: "huey",
|
|
4293
|
+
label: "Huey",
|
|
4294
|
+
hint: "Lightweight task queue with Redis-backed scheduling"
|
|
4295
|
+
},
|
|
4296
|
+
{
|
|
4297
|
+
value: "none",
|
|
4298
|
+
label: "None",
|
|
4299
|
+
hint: "No task queue"
|
|
4300
|
+
}
|
|
4301
|
+
];
|
|
4302
|
+
const PYTHON_GRAPHQL_PROMPT_OPTIONS = [
|
|
4303
|
+
{
|
|
4304
|
+
value: "strawberry",
|
|
4305
|
+
label: "Strawberry",
|
|
4306
|
+
hint: "Python GraphQL library using dataclasses and type hints"
|
|
4307
|
+
},
|
|
4308
|
+
{
|
|
4309
|
+
value: "ariadne",
|
|
4310
|
+
label: "Ariadne",
|
|
4311
|
+
hint: "Schema-first GraphQL server library for Python"
|
|
4312
|
+
},
|
|
4313
|
+
{
|
|
4314
|
+
value: "none",
|
|
4315
|
+
label: "None",
|
|
4316
|
+
hint: "No GraphQL framework"
|
|
4317
|
+
}
|
|
4318
|
+
];
|
|
4319
|
+
const PYTHON_QUALITY_PROMPT_OPTIONS = [
|
|
4320
|
+
{
|
|
4321
|
+
value: "ruff",
|
|
4322
|
+
label: "Ruff",
|
|
4323
|
+
hint: "An extremely fast Python linter and formatter"
|
|
4324
|
+
},
|
|
4325
|
+
{
|
|
4326
|
+
value: "mypy",
|
|
4327
|
+
label: "mypy",
|
|
4328
|
+
hint: "Static type checker for Python"
|
|
4329
|
+
},
|
|
4330
|
+
{
|
|
4331
|
+
value: "pyright",
|
|
4332
|
+
label: "Pyright",
|
|
4333
|
+
hint: "Fast Python type checker from Microsoft"
|
|
4334
|
+
},
|
|
4335
|
+
{
|
|
4336
|
+
value: "none",
|
|
4337
|
+
label: "None",
|
|
4338
|
+
hint: "No code quality tools"
|
|
4339
|
+
}
|
|
4340
|
+
];
|
|
4269
4341
|
function resolvePythonWebFrameworkPrompt(pythonWebFramework) {
|
|
4270
4342
|
return createStaticSinglePromptResolution(PYTHON_WEB_FRAMEWORK_PROMPT_OPTIONS, "fastapi", pythonWebFramework);
|
|
4271
4343
|
}
|
|
@@ -4338,6 +4410,20 @@ async function getPythonAuthChoice(pythonAuth) {
|
|
|
4338
4410
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4339
4411
|
return response;
|
|
4340
4412
|
}
|
|
4413
|
+
function resolvePythonApiPrompt(pythonApi) {
|
|
4414
|
+
return createStaticSinglePromptResolution(PYTHON_API_PROMPT_OPTIONS, "none", pythonApi);
|
|
4415
|
+
}
|
|
4416
|
+
async function getPythonApiChoice(pythonApi) {
|
|
4417
|
+
const resolution = resolvePythonApiPrompt(pythonApi);
|
|
4418
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
4419
|
+
const response = await navigableSelect({
|
|
4420
|
+
message: "Select Python API framework",
|
|
4421
|
+
options: resolution.options,
|
|
4422
|
+
initialValue: resolution.initialValue
|
|
4423
|
+
});
|
|
4424
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4425
|
+
return response;
|
|
4426
|
+
}
|
|
4341
4427
|
function resolvePythonTaskQueuePrompt(pythonTaskQueue) {
|
|
4342
4428
|
return createStaticSinglePromptResolution(PYTHON_TASK_QUEUE_PROMPT_OPTIONS, "none", pythonTaskQueue);
|
|
4343
4429
|
}
|
|
@@ -4611,6 +4697,51 @@ const RUST_LIBRARIES_PROMPT_OPTIONS = [
|
|
|
4611
4697
|
label: "Serde",
|
|
4612
4698
|
hint: "Serialization framework for Rust"
|
|
4613
4699
|
},
|
|
4700
|
+
{
|
|
4701
|
+
value: "uuid",
|
|
4702
|
+
label: "uuid",
|
|
4703
|
+
hint: "UUID generation and parsing with Serde support"
|
|
4704
|
+
},
|
|
4705
|
+
{
|
|
4706
|
+
value: "chrono",
|
|
4707
|
+
label: "Chrono",
|
|
4708
|
+
hint: "Date and time library with Serde support"
|
|
4709
|
+
},
|
|
4710
|
+
{
|
|
4711
|
+
value: "reqwest",
|
|
4712
|
+
label: "Reqwest",
|
|
4713
|
+
hint: "Ergonomic HTTP client with JSON and Rustls TLS support"
|
|
4714
|
+
},
|
|
4715
|
+
{
|
|
4716
|
+
value: "config",
|
|
4717
|
+
label: "config",
|
|
4718
|
+
hint: "Layered configuration from files, environment, and defaults"
|
|
4719
|
+
},
|
|
4720
|
+
{
|
|
4721
|
+
value: "dashmap",
|
|
4722
|
+
label: "DashMap",
|
|
4723
|
+
hint: "Concurrent hash map for shared mutable state"
|
|
4724
|
+
},
|
|
4725
|
+
{
|
|
4726
|
+
value: "parking-lot",
|
|
4727
|
+
label: "parking_lot",
|
|
4728
|
+
hint: "Compact, fast synchronization primitives"
|
|
4729
|
+
},
|
|
4730
|
+
{
|
|
4731
|
+
value: "secrecy",
|
|
4732
|
+
label: "Secrecy",
|
|
4733
|
+
hint: "Secret value wrapper that avoids accidental exposure"
|
|
4734
|
+
},
|
|
4735
|
+
{
|
|
4736
|
+
value: "tokio-util",
|
|
4737
|
+
label: "Tokio Util",
|
|
4738
|
+
hint: "Tokio utilities for codecs, cancellation tokens, and IO helpers"
|
|
4739
|
+
},
|
|
4740
|
+
{
|
|
4741
|
+
value: "utoipa",
|
|
4742
|
+
label: "utoipa",
|
|
4743
|
+
hint: "OpenAPI documentation generation from Rust types"
|
|
4744
|
+
},
|
|
4614
4745
|
{
|
|
4615
4746
|
value: "validator",
|
|
4616
4747
|
label: "Validator",
|
|
@@ -4635,6 +4766,16 @@ const RUST_LIBRARIES_PROMPT_OPTIONS = [
|
|
|
4635
4766
|
value: "mockall",
|
|
4636
4767
|
label: "Mockall",
|
|
4637
4768
|
hint: "Powerful mocking library for Rust"
|
|
4769
|
+
},
|
|
4770
|
+
{
|
|
4771
|
+
value: "proptest",
|
|
4772
|
+
label: "Proptest",
|
|
4773
|
+
hint: "Property-based testing for Rust"
|
|
4774
|
+
},
|
|
4775
|
+
{
|
|
4776
|
+
value: "insta",
|
|
4777
|
+
label: "Insta",
|
|
4778
|
+
hint: "Snapshot testing for Rust"
|
|
4638
4779
|
}
|
|
4639
4780
|
];
|
|
4640
4781
|
const RUST_LOGGING_PROMPT_OPTIONS = [
|
|
@@ -4836,39 +4977,65 @@ async function getRustAuthChoice(rustAuth) {
|
|
|
4836
4977
|
|
|
4837
4978
|
//#endregion
|
|
4838
4979
|
//#region src/prompts/search.ts
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4980
|
+
const SEARCH_PROMPT_OPTIONS = [
|
|
4981
|
+
{
|
|
4982
|
+
value: "meilisearch",
|
|
4983
|
+
label: "Meilisearch",
|
|
4984
|
+
hint: "Lightning-fast search engine with typo tolerance"
|
|
4985
|
+
},
|
|
4986
|
+
{
|
|
4987
|
+
value: "typesense",
|
|
4988
|
+
label: "Typesense",
|
|
4989
|
+
hint: "Fast, typo-tolerant search with built-in vector search"
|
|
4990
|
+
},
|
|
4991
|
+
{
|
|
4992
|
+
value: "elasticsearch",
|
|
4993
|
+
label: "Elasticsearch",
|
|
4994
|
+
hint: "Distributed search and analytics engine with local and cloud deployments"
|
|
4995
|
+
},
|
|
4996
|
+
{
|
|
4997
|
+
value: "algolia",
|
|
4998
|
+
label: "Algolia",
|
|
4999
|
+
hint: "Hosted search API with instant results, typo tolerance, and analytics"
|
|
5000
|
+
},
|
|
5001
|
+
{
|
|
5002
|
+
value: "none",
|
|
5003
|
+
label: "None",
|
|
5004
|
+
hint: "Skip search engine setup"
|
|
5005
|
+
}
|
|
5006
|
+
];
|
|
5007
|
+
const NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS = SEARCH_PROMPT_OPTIONS.filter((option) => option.value === "meilisearch" || option.value === "none");
|
|
5008
|
+
function resolveSearchPrompt(context = {}) {
|
|
5009
|
+
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS : SEARCH_PROMPT_OPTIONS;
|
|
5010
|
+
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
5011
|
+
shouldPrompt: false,
|
|
5012
|
+
mode: "single",
|
|
5013
|
+
options: [],
|
|
5014
|
+
autoValue: "none"
|
|
5015
|
+
};
|
|
5016
|
+
return context.search !== void 0 ? {
|
|
5017
|
+
shouldPrompt: false,
|
|
5018
|
+
mode: "single",
|
|
5019
|
+
options,
|
|
5020
|
+
autoValue: context.search
|
|
5021
|
+
} : {
|
|
5022
|
+
shouldPrompt: true,
|
|
5023
|
+
mode: "single",
|
|
5024
|
+
options,
|
|
5025
|
+
initialValue: "none"
|
|
5026
|
+
};
|
|
5027
|
+
}
|
|
5028
|
+
async function getSearchChoice(search, backend, ecosystem) {
|
|
5029
|
+
const resolution = resolveSearchPrompt({
|
|
5030
|
+
search,
|
|
5031
|
+
backend,
|
|
5032
|
+
ecosystem
|
|
5033
|
+
});
|
|
5034
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
|
|
4842
5035
|
const response = await navigableSelect({
|
|
4843
5036
|
message: "Select search engine",
|
|
4844
|
-
options:
|
|
4845
|
-
|
|
4846
|
-
value: "meilisearch",
|
|
4847
|
-
label: "Meilisearch",
|
|
4848
|
-
hint: "Lightning-fast search engine with typo tolerance"
|
|
4849
|
-
},
|
|
4850
|
-
{
|
|
4851
|
-
value: "typesense",
|
|
4852
|
-
label: "Typesense",
|
|
4853
|
-
hint: "Fast, typo-tolerant search with built-in vector search"
|
|
4854
|
-
},
|
|
4855
|
-
{
|
|
4856
|
-
value: "elasticsearch",
|
|
4857
|
-
label: "Elasticsearch",
|
|
4858
|
-
hint: "Distributed search and analytics engine with local and cloud deployments"
|
|
4859
|
-
},
|
|
4860
|
-
{
|
|
4861
|
-
value: "algolia",
|
|
4862
|
-
label: "Algolia",
|
|
4863
|
-
hint: "Hosted search API with instant results, typo tolerance, and analytics"
|
|
4864
|
-
},
|
|
4865
|
-
{
|
|
4866
|
-
value: "none",
|
|
4867
|
-
label: "None",
|
|
4868
|
-
hint: "Skip search engine setup"
|
|
4869
|
-
}
|
|
4870
|
-
],
|
|
4871
|
-
initialValue: "none"
|
|
5037
|
+
options: resolution.options,
|
|
5038
|
+
initialValue: resolution.initialValue
|
|
4872
5039
|
});
|
|
4873
5040
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
4874
5041
|
return response;
|
|
@@ -4920,6 +5087,16 @@ const STYLE_OPTIONS = [
|
|
|
4920
5087
|
value: "mira",
|
|
4921
5088
|
label: "Mira",
|
|
4922
5089
|
hint: "Dense, made for data-heavy interfaces"
|
|
5090
|
+
},
|
|
5091
|
+
{
|
|
5092
|
+
value: "luma",
|
|
5093
|
+
label: "Luma",
|
|
5094
|
+
hint: "Modern shadcn/ui v4 preset"
|
|
5095
|
+
},
|
|
5096
|
+
{
|
|
5097
|
+
value: "sera",
|
|
5098
|
+
label: "Sera",
|
|
5099
|
+
hint: "Modern shadcn/ui v4 preset"
|
|
4923
5100
|
}
|
|
4924
5101
|
];
|
|
4925
5102
|
const ICON_LIBRARY_OPTIONS = [
|
|
@@ -4947,6 +5124,16 @@ const ICON_LIBRARY_OPTIONS = [
|
|
|
4947
5124
|
value: "remixicon",
|
|
4948
5125
|
label: "Remix Icon",
|
|
4949
5126
|
hint: "Open-source neutral style icons"
|
|
5127
|
+
},
|
|
5128
|
+
{
|
|
5129
|
+
value: "heroicons",
|
|
5130
|
+
label: "Heroicons",
|
|
5131
|
+
hint: "Tailwind Labs SVG icon set"
|
|
5132
|
+
},
|
|
5133
|
+
{
|
|
5134
|
+
value: "react-icons",
|
|
5135
|
+
label: "React Icons",
|
|
5136
|
+
hint: "Popular wrapper for many icon packs"
|
|
4950
5137
|
}
|
|
4951
5138
|
];
|
|
4952
5139
|
const COLOR_THEME_OPTIONS = [
|
|
@@ -5445,6 +5632,14 @@ const UI_LIBRARY_OPTIONS = {
|
|
|
5445
5632
|
label: "Mantine",
|
|
5446
5633
|
hint: "Full-featured React component library with 120+ components"
|
|
5447
5634
|
},
|
|
5635
|
+
mui: {
|
|
5636
|
+
label: "MUI",
|
|
5637
|
+
hint: "Popular React component library implementing Material Design"
|
|
5638
|
+
},
|
|
5639
|
+
antd: {
|
|
5640
|
+
label: "Ant Design",
|
|
5641
|
+
hint: "Enterprise-class React UI component library"
|
|
5642
|
+
},
|
|
5448
5643
|
"base-ui": {
|
|
5449
5644
|
label: "Base UI",
|
|
5450
5645
|
hint: "Unstyled, accessible components from MUI team (Radix successor)"
|
|
@@ -5695,8 +5890,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5695
5890
|
return getPaymentsChoice(flags.payments, results.auth, results.backend, results.frontend);
|
|
5696
5891
|
},
|
|
5697
5892
|
email: ({ results }) => {
|
|
5698
|
-
|
|
5699
|
-
return getEmailChoice(flags.email, results.backend);
|
|
5893
|
+
return getEmailChoice(flags.email, results.backend, results.ecosystem);
|
|
5700
5894
|
},
|
|
5701
5895
|
effect: ({ results }) => {
|
|
5702
5896
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
@@ -5767,8 +5961,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5767
5961
|
return getLoggingChoice(flags.logging, results.backend);
|
|
5768
5962
|
},
|
|
5769
5963
|
observability: ({ results }) => {
|
|
5770
|
-
|
|
5771
|
-
return getObservabilityChoice(flags.observability, results.backend);
|
|
5964
|
+
return getObservabilityChoice(flags.observability, results.backend, results.ecosystem);
|
|
5772
5965
|
},
|
|
5773
5966
|
featureFlags: ({ results }) => {
|
|
5774
5967
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
@@ -5783,16 +5976,14 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5783
5976
|
return getCMSChoice(flags.cms, results.backend);
|
|
5784
5977
|
},
|
|
5785
5978
|
caching: ({ results }) => {
|
|
5786
|
-
|
|
5787
|
-
return getCachingChoice(flags.caching, results.backend);
|
|
5979
|
+
return getCachingChoice(flags.caching, results.backend, results.ecosystem);
|
|
5788
5980
|
},
|
|
5789
5981
|
i18n: ({ results }) => {
|
|
5790
5982
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
5791
5983
|
return getI18nChoice(flags.i18n, results.frontend);
|
|
5792
5984
|
},
|
|
5793
5985
|
search: ({ results }) => {
|
|
5794
|
-
|
|
5795
|
-
return getSearchChoice(flags.search, results.backend);
|
|
5986
|
+
return getSearchChoice(flags.search, results.backend, results.ecosystem);
|
|
5796
5987
|
},
|
|
5797
5988
|
fileStorage: ({ results }) => {
|
|
5798
5989
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
@@ -5858,6 +6049,11 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5858
6049
|
if (results.ecosystem !== "python") return Promise.resolve("none");
|
|
5859
6050
|
return getPythonAuthChoice(flags.pythonAuth);
|
|
5860
6051
|
},
|
|
6052
|
+
pythonApi: ({ results }) => {
|
|
6053
|
+
if (results.ecosystem !== "python") return Promise.resolve("none");
|
|
6054
|
+
if (results.pythonWebFramework !== "django") return Promise.resolve("none");
|
|
6055
|
+
return getPythonApiChoice(flags.pythonApi);
|
|
6056
|
+
},
|
|
5861
6057
|
pythonTaskQueue: ({ results }) => {
|
|
5862
6058
|
if (results.ecosystem !== "python") return Promise.resolve("none");
|
|
5863
6059
|
return getPythonTaskQueueChoice(flags.pythonTaskQueue);
|
|
@@ -5990,6 +6186,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
5990
6186
|
pythonValidation: result.pythonValidation,
|
|
5991
6187
|
pythonAi: result.pythonAi,
|
|
5992
6188
|
pythonAuth: result.pythonAuth,
|
|
6189
|
+
pythonApi: result.pythonApi,
|
|
5993
6190
|
pythonTaskQueue: result.pythonTaskQueue,
|
|
5994
6191
|
pythonGraphql: result.pythonGraphql,
|
|
5995
6192
|
pythonQuality: result.pythonQuality,
|
|
@@ -6217,6 +6414,10 @@ function appendCommonFlags(flags, config) {
|
|
|
6217
6414
|
flags.push(config.install ? "--install" : "--no-install");
|
|
6218
6415
|
}
|
|
6219
6416
|
function appendSharedNonTypeScriptFlags(flags, config) {
|
|
6417
|
+
flags.push(`--email ${config.email}`);
|
|
6418
|
+
flags.push(`--observability ${config.observability}`);
|
|
6419
|
+
flags.push(`--caching ${config.caching}`);
|
|
6420
|
+
flags.push(`--search ${config.search}`);
|
|
6220
6421
|
flags.push(formatArrayFlag("addons", config.addons));
|
|
6221
6422
|
flags.push(formatArrayFlag("examples", config.examples));
|
|
6222
6423
|
flags.push(`--db-setup ${config.dbSetup}`);
|
|
@@ -6258,6 +6459,7 @@ function getTypeScriptFlags(config) {
|
|
|
6258
6459
|
flags.push(`--job-queue ${config.jobQueue}`);
|
|
6259
6460
|
flags.push(`--logging ${config.logging}`);
|
|
6260
6461
|
flags.push(`--observability ${config.observability}`);
|
|
6462
|
+
flags.push(`--feature-flags ${config.featureFlags}`);
|
|
6261
6463
|
flags.push(`--caching ${config.caching}`);
|
|
6262
6464
|
flags.push(`--i18n ${config.i18n}`);
|
|
6263
6465
|
flags.push(`--cms ${config.cms}`);
|
|
@@ -6296,6 +6498,7 @@ function getPythonFlags(config) {
|
|
|
6296
6498
|
flags.push(`--python-validation ${config.pythonValidation}`);
|
|
6297
6499
|
flags.push(formatArrayFlag("python-ai", config.pythonAi));
|
|
6298
6500
|
flags.push(`--python-auth ${config.pythonAuth}`);
|
|
6501
|
+
flags.push(`--python-api ${config.pythonApi}`);
|
|
6299
6502
|
flags.push(`--python-task-queue ${config.pythonTaskQueue}`);
|
|
6300
6503
|
flags.push(`--python-graphql ${config.pythonGraphql}`);
|
|
6301
6504
|
flags.push(`--python-quality ${config.pythonQuality}`);
|
|
@@ -6517,100 +6720,14 @@ function getTemplateDescription(template) {
|
|
|
6517
6720
|
|
|
6518
6721
|
//#endregion
|
|
6519
6722
|
//#region src/utils/config-processing.ts
|
|
6520
|
-
function processArrayOption(options) {
|
|
6521
|
-
if (!options || options.length === 0) return [];
|
|
6522
|
-
if (options.includes("none")) return [];
|
|
6523
|
-
return options.filter((item) => item !== "none");
|
|
6524
|
-
}
|
|
6525
6723
|
function deriveProjectName(projectName, projectDirectory) {
|
|
6526
6724
|
if (projectName) return projectName;
|
|
6527
6725
|
if (projectDirectory) return path.basename(path.resolve(process.cwd(), projectDirectory));
|
|
6528
6726
|
return "";
|
|
6529
6727
|
}
|
|
6530
6728
|
function processFlags(options, projectName) {
|
|
6531
|
-
const config = {};
|
|
6532
|
-
if (options.ecosystem) config.ecosystem = options.ecosystem;
|
|
6533
|
-
if (options.api) config.api = options.api;
|
|
6534
|
-
if (options.backend) config.backend = options.backend;
|
|
6535
|
-
if (options.database) config.database = options.database;
|
|
6536
|
-
if (options.orm) config.orm = options.orm;
|
|
6537
|
-
if (options.auth !== void 0) config.auth = options.auth;
|
|
6538
|
-
if (options.payments !== void 0) config.payments = options.payments;
|
|
6539
|
-
if (options.email !== void 0) config.email = options.email;
|
|
6540
|
-
if (options.effect !== void 0) config.effect = options.effect;
|
|
6541
|
-
if (options.stateManagement !== void 0) config.stateManagement = options.stateManagement;
|
|
6542
|
-
if (options.validation !== void 0) config.validation = options.validation;
|
|
6543
|
-
if (options.realtime !== void 0) config.realtime = options.realtime;
|
|
6544
|
-
if (options.jobQueue !== void 0) config.jobQueue = options.jobQueue;
|
|
6545
|
-
if (options.animation !== void 0) config.animation = options.animation;
|
|
6546
|
-
if (options.ai !== void 0) config.ai = options.ai;
|
|
6547
|
-
if (options.forms !== void 0) config.forms = options.forms;
|
|
6548
|
-
if (options.testing !== void 0) config.testing = options.testing;
|
|
6549
|
-
if (options.logging !== void 0) config.logging = options.logging;
|
|
6550
|
-
if (options.observability !== void 0) config.observability = options.observability;
|
|
6551
|
-
if (options.cms !== void 0) config.cms = options.cms;
|
|
6552
|
-
if (options.caching !== void 0) config.caching = options.caching;
|
|
6553
|
-
if (options.i18n !== void 0) config.i18n = options.i18n;
|
|
6554
|
-
if (options.search !== void 0) config.search = options.search;
|
|
6555
|
-
if (options.fileStorage !== void 0) config.fileStorage = options.fileStorage;
|
|
6556
|
-
if (options.analytics !== void 0) config.analytics = options.analytics;
|
|
6557
|
-
if (options.featureFlags !== void 0) config.featureFlags = options.featureFlags;
|
|
6558
|
-
if (options.fileUpload !== void 0) config.fileUpload = options.fileUpload;
|
|
6559
|
-
if (options.git !== void 0) config.git = options.git;
|
|
6560
|
-
if (options.install !== void 0) config.install = options.install;
|
|
6561
|
-
if (options.runtime) config.runtime = options.runtime;
|
|
6562
|
-
if (options.dbSetup) config.dbSetup = options.dbSetup;
|
|
6563
|
-
if (options.packageManager) config.packageManager = options.packageManager;
|
|
6564
|
-
if (options.versionChannel) config.versionChannel = options.versionChannel;
|
|
6565
|
-
if (options.webDeploy) config.webDeploy = options.webDeploy;
|
|
6566
|
-
if (options.serverDeploy) config.serverDeploy = options.serverDeploy;
|
|
6567
6729
|
const derivedName = deriveProjectName(projectName, options.projectDirectory);
|
|
6568
|
-
|
|
6569
|
-
if (options.frontend && options.frontend.length > 0) config.frontend = processArrayOption(options.frontend);
|
|
6570
|
-
if (options.astroIntegration) config.astroIntegration = options.astroIntegration;
|
|
6571
|
-
if (options.cssFramework) config.cssFramework = options.cssFramework;
|
|
6572
|
-
if (options.uiLibrary) config.uiLibrary = options.uiLibrary;
|
|
6573
|
-
if (options.shadcnBase) config.shadcnBase = options.shadcnBase;
|
|
6574
|
-
if (options.shadcnStyle) config.shadcnStyle = options.shadcnStyle;
|
|
6575
|
-
if (options.shadcnIconLibrary) config.shadcnIconLibrary = options.shadcnIconLibrary;
|
|
6576
|
-
if (options.shadcnColorTheme) config.shadcnColorTheme = options.shadcnColorTheme;
|
|
6577
|
-
if (options.shadcnBaseColor) config.shadcnBaseColor = options.shadcnBaseColor;
|
|
6578
|
-
if (options.shadcnFont) config.shadcnFont = options.shadcnFont;
|
|
6579
|
-
if (options.shadcnRadius) config.shadcnRadius = options.shadcnRadius;
|
|
6580
|
-
if (options.addons && options.addons.length > 0) config.addons = processArrayOption(options.addons);
|
|
6581
|
-
if (options.examples && options.examples.length > 0) config.examples = processArrayOption(options.examples);
|
|
6582
|
-
if (options.aiDocs !== void 0) config.aiDocs = processArrayOption(options.aiDocs);
|
|
6583
|
-
if (options.rustWebFramework !== void 0) config.rustWebFramework = options.rustWebFramework;
|
|
6584
|
-
if (options.rustFrontend !== void 0) config.rustFrontend = options.rustFrontend;
|
|
6585
|
-
if (options.rustOrm !== void 0) config.rustOrm = options.rustOrm;
|
|
6586
|
-
if (options.rustApi !== void 0) config.rustApi = options.rustApi;
|
|
6587
|
-
if (options.rustCli !== void 0) config.rustCli = options.rustCli;
|
|
6588
|
-
if (options.rustLibraries !== void 0) config.rustLibraries = processArrayOption(options.rustLibraries);
|
|
6589
|
-
if (options.rustLogging !== void 0) config.rustLogging = options.rustLogging;
|
|
6590
|
-
if (options.rustErrorHandling !== void 0) config.rustErrorHandling = options.rustErrorHandling;
|
|
6591
|
-
if (options.rustCaching !== void 0) config.rustCaching = options.rustCaching;
|
|
6592
|
-
if (options.rustAuth !== void 0) config.rustAuth = options.rustAuth;
|
|
6593
|
-
if (options.pythonWebFramework !== void 0) config.pythonWebFramework = options.pythonWebFramework;
|
|
6594
|
-
if (options.pythonOrm !== void 0) config.pythonOrm = options.pythonOrm;
|
|
6595
|
-
if (options.pythonValidation !== void 0) config.pythonValidation = options.pythonValidation;
|
|
6596
|
-
if (options.pythonAi !== void 0) config.pythonAi = processArrayOption(options.pythonAi);
|
|
6597
|
-
if (options.pythonAuth !== void 0) config.pythonAuth = options.pythonAuth;
|
|
6598
|
-
if (options.pythonTaskQueue !== void 0) config.pythonTaskQueue = options.pythonTaskQueue;
|
|
6599
|
-
if (options.pythonGraphql !== void 0) config.pythonGraphql = options.pythonGraphql;
|
|
6600
|
-
if (options.pythonQuality !== void 0) config.pythonQuality = options.pythonQuality;
|
|
6601
|
-
if (options.goWebFramework !== void 0) config.goWebFramework = options.goWebFramework;
|
|
6602
|
-
if (options.goOrm !== void 0) config.goOrm = options.goOrm;
|
|
6603
|
-
if (options.goApi !== void 0) config.goApi = options.goApi;
|
|
6604
|
-
if (options.goCli !== void 0) config.goCli = options.goCli;
|
|
6605
|
-
if (options.goLogging !== void 0) config.goLogging = options.goLogging;
|
|
6606
|
-
if (options.goAuth !== void 0) config.goAuth = options.goAuth;
|
|
6607
|
-
if (options.javaWebFramework !== void 0) config.javaWebFramework = options.javaWebFramework;
|
|
6608
|
-
if (options.javaBuildTool !== void 0) config.javaBuildTool = options.javaBuildTool;
|
|
6609
|
-
if (options.javaOrm !== void 0) config.javaOrm = options.javaOrm;
|
|
6610
|
-
if (options.javaAuth !== void 0) config.javaAuth = options.javaAuth;
|
|
6611
|
-
if (options.javaLibraries !== void 0) config.javaLibraries = processArrayOption(options.javaLibraries);
|
|
6612
|
-
if (options.javaTestingLibraries !== void 0) config.javaTestingLibraries = processArrayOption(options.javaTestingLibraries);
|
|
6613
|
-
return config;
|
|
6730
|
+
return cliInputToProjectConfigPartial(options, projectName || derivedName);
|
|
6614
6731
|
}
|
|
6615
6732
|
function getProvidedFlags(options) {
|
|
6616
6733
|
return new Set(Object.keys(options).filter((key) => options[key] !== void 0));
|
|
@@ -7066,19 +7183,20 @@ function validateApiConstraints(_config, _options) {}
|
|
|
7066
7183
|
function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set()) {
|
|
7067
7184
|
if (config.ecosystem !== "java") return;
|
|
7068
7185
|
const hasSpringBoot = config.javaWebFramework === "spring-boot";
|
|
7186
|
+
const hasJavaWebFramework = config.javaWebFramework !== "none";
|
|
7069
7187
|
const hasNoBuildTool = config.javaBuildTool === "none";
|
|
7070
7188
|
const hasJavaLibraries = (config.javaLibraries ?? []).some((library) => library !== "none");
|
|
7071
7189
|
const hasJavaTestingLibraries = (config.javaTestingLibraries ?? []).some((library) => library !== "none");
|
|
7072
7190
|
const hasSpringOnlyFeatures = config.javaOrm !== "none" || config.javaAuth !== "none" || hasJavaLibraries;
|
|
7073
|
-
if (hasNoBuildTool &&
|
|
7074
|
-
message: "
|
|
7191
|
+
if (hasNoBuildTool && hasJavaWebFramework) incompatibilityError({
|
|
7192
|
+
message: "Java web frameworks require Maven or Gradle in the Java scaffold.",
|
|
7075
7193
|
provided: {
|
|
7076
7194
|
"java-web-framework": config.javaWebFramework ?? "none",
|
|
7077
7195
|
"java-build-tool": config.javaBuildTool ?? "none"
|
|
7078
7196
|
},
|
|
7079
|
-
suggestions: ["Use --java-build-tool maven or --java-build-tool gradle with
|
|
7197
|
+
suggestions: ["Use --java-build-tool maven or --java-build-tool gradle with Java web frameworks", "Use --java-web-framework none for a plain Java source-only scaffold"]
|
|
7080
7198
|
});
|
|
7081
|
-
if ((
|
|
7199
|
+
if ((!hasSpringBoot || hasNoBuildTool) && hasSpringOnlyFeatures) incompatibilityError({
|
|
7082
7200
|
message: "Spring-only Java features require the Spring Boot scaffold with Maven or Gradle.",
|
|
7083
7201
|
provided: {
|
|
7084
7202
|
"java-web-framework": config.javaWebFramework ?? "none",
|
|
@@ -7087,7 +7205,7 @@ function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set
|
|
|
7087
7205
|
"java-auth": config.javaAuth ?? "none",
|
|
7088
7206
|
"java-libraries": (config.javaLibraries ?? []).join(" ") || "none"
|
|
7089
7207
|
},
|
|
7090
|
-
suggestions: ["Use --java-web-framework spring-boot and a real build tool for Spring features", "Clear --java-orm, --java-auth, and --java-libraries when using plain Java"]
|
|
7208
|
+
suggestions: ["Use --java-web-framework spring-boot and a real build tool for Spring features", "Clear --java-orm, --java-auth, and --java-libraries when using plain Java or Quarkus"]
|
|
7091
7209
|
});
|
|
7092
7210
|
if (hasNoBuildTool && hasJavaTestingLibraries) incompatibilityError({
|
|
7093
7211
|
message: "Java testing libraries require Maven or Gradle to manage test dependencies.",
|
|
@@ -7098,6 +7216,82 @@ function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set
|
|
|
7098
7216
|
suggestions: ["Use --java-build-tool maven or --java-build-tool gradle to enable JUnit/Mockito/Testcontainers", "Set --java-testing-libraries none for a source-only plain Java scaffold"]
|
|
7099
7217
|
});
|
|
7100
7218
|
}
|
|
7219
|
+
function validateEmailConstraints(config) {
|
|
7220
|
+
if (!config.email || config.email === "none") return;
|
|
7221
|
+
if (config.ecosystem !== "typescript" && config.email !== "resend") incompatibilityError({
|
|
7222
|
+
message: "Only Resend email is available for non-TypeScript ecosystems.",
|
|
7223
|
+
provided: {
|
|
7224
|
+
ecosystem: config.ecosystem ?? "typescript",
|
|
7225
|
+
email: config.email
|
|
7226
|
+
},
|
|
7227
|
+
suggestions: ["Use --email resend", "Use --email none"]
|
|
7228
|
+
});
|
|
7229
|
+
if (config.ecosystem === "java" && config.email === "resend" && config.javaBuildTool === "none") incompatibilityError({
|
|
7230
|
+
message: "Resend email for Java requires Maven or Gradle to manage the SDK dependency.",
|
|
7231
|
+
provided: {
|
|
7232
|
+
"java-build-tool": "none",
|
|
7233
|
+
email: "resend"
|
|
7234
|
+
},
|
|
7235
|
+
suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
|
|
7236
|
+
});
|
|
7237
|
+
}
|
|
7238
|
+
function validateObservabilityConstraints(config) {
|
|
7239
|
+
if (!config.observability || config.observability === "none") return;
|
|
7240
|
+
if (config.ecosystem !== "typescript" && config.observability !== "sentry") incompatibilityError({
|
|
7241
|
+
message: "Only Sentry observability is available for non-TypeScript ecosystems.",
|
|
7242
|
+
provided: {
|
|
7243
|
+
ecosystem: config.ecosystem ?? "typescript",
|
|
7244
|
+
observability: config.observability
|
|
7245
|
+
},
|
|
7246
|
+
suggestions: ["Use --observability sentry", "Use --observability none"]
|
|
7247
|
+
});
|
|
7248
|
+
if (config.ecosystem === "java" && config.observability === "sentry" && config.javaBuildTool === "none") incompatibilityError({
|
|
7249
|
+
message: "Sentry observability for Java requires Maven or Gradle to manage the SDK dependency.",
|
|
7250
|
+
provided: {
|
|
7251
|
+
"java-build-tool": "none",
|
|
7252
|
+
observability: "sentry"
|
|
7253
|
+
},
|
|
7254
|
+
suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
|
|
7255
|
+
});
|
|
7256
|
+
}
|
|
7257
|
+
function validateCachingConstraints(config) {
|
|
7258
|
+
if (!config.caching || config.caching === "none") return;
|
|
7259
|
+
if (config.ecosystem !== "typescript" && config.caching !== "upstash-redis") incompatibilityError({
|
|
7260
|
+
message: "Only Upstash Redis caching is available for non-TypeScript ecosystems.",
|
|
7261
|
+
provided: {
|
|
7262
|
+
ecosystem: config.ecosystem ?? "typescript",
|
|
7263
|
+
caching: config.caching
|
|
7264
|
+
},
|
|
7265
|
+
suggestions: ["Use --caching upstash-redis", "Use --caching none"]
|
|
7266
|
+
});
|
|
7267
|
+
if (config.ecosystem === "java" && config.caching === "upstash-redis" && config.javaBuildTool === "none") incompatibilityError({
|
|
7268
|
+
message: "Upstash Redis caching for Java requires Maven or Gradle to manage the Redis client dependency.",
|
|
7269
|
+
provided: {
|
|
7270
|
+
"java-build-tool": "none",
|
|
7271
|
+
caching: "upstash-redis"
|
|
7272
|
+
},
|
|
7273
|
+
suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
|
|
7274
|
+
});
|
|
7275
|
+
}
|
|
7276
|
+
function validateSearchConstraints(config) {
|
|
7277
|
+
if (!config.search || config.search === "none") return;
|
|
7278
|
+
if (config.ecosystem !== "typescript" && config.search !== "meilisearch") incompatibilityError({
|
|
7279
|
+
message: "Only Meilisearch search is available for non-TypeScript ecosystems.",
|
|
7280
|
+
provided: {
|
|
7281
|
+
ecosystem: config.ecosystem ?? "typescript",
|
|
7282
|
+
search: config.search
|
|
7283
|
+
},
|
|
7284
|
+
suggestions: ["Use --search meilisearch", "Use --search none"]
|
|
7285
|
+
});
|
|
7286
|
+
if (config.ecosystem === "java" && config.search === "meilisearch" && config.javaBuildTool === "none") incompatibilityError({
|
|
7287
|
+
message: "Meilisearch search for Java requires Maven or Gradle to manage the SDK dependency.",
|
|
7288
|
+
provided: {
|
|
7289
|
+
"java-build-tool": "none",
|
|
7290
|
+
search: "meilisearch"
|
|
7291
|
+
},
|
|
7292
|
+
suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
|
|
7293
|
+
});
|
|
7294
|
+
}
|
|
7101
7295
|
function validateShadcnConstraints(config, providedFlags) {
|
|
7102
7296
|
const shadcnFlagMap = {
|
|
7103
7297
|
shadcnBase: "--shadcn-base",
|
|
@@ -7118,6 +7312,16 @@ function validateShadcnConstraints(config, providedFlags) {
|
|
|
7118
7312
|
suggestions: ["Add --ui-library shadcn-ui to use shadcn customization flags", "Remove the --shadcn-* flags if not using shadcn/ui"]
|
|
7119
7313
|
});
|
|
7120
7314
|
}
|
|
7315
|
+
function validatePythonApiConstraints(config) {
|
|
7316
|
+
if (config.ecosystem === "python" && config.pythonApi && config.pythonApi !== "none" && config.pythonWebFramework !== "django") incompatibilityError({
|
|
7317
|
+
message: "Python API frameworks require --python-web-framework django.",
|
|
7318
|
+
provided: {
|
|
7319
|
+
"python-web-framework": config.pythonWebFramework || "none",
|
|
7320
|
+
"python-api": config.pythonApi
|
|
7321
|
+
},
|
|
7322
|
+
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"]
|
|
7323
|
+
});
|
|
7324
|
+
}
|
|
7121
7325
|
function validateFullConfig(config, providedFlags, options) {
|
|
7122
7326
|
validateEcosystemAuthCompatibility(config, providedFlags);
|
|
7123
7327
|
validateDatabaseOrmAuth(config, providedFlags);
|
|
@@ -7130,6 +7334,11 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
7130
7334
|
validateBackendConstraints(config, providedFlags, options);
|
|
7131
7335
|
validateFrontendConstraints(config, providedFlags);
|
|
7132
7336
|
/* @__PURE__ */ validateApiConstraints(config, options);
|
|
7337
|
+
validatePythonApiConstraints(config);
|
|
7338
|
+
validateEmailConstraints(config);
|
|
7339
|
+
validateObservabilityConstraints(config);
|
|
7340
|
+
validateCachingConstraints(config);
|
|
7341
|
+
validateSearchConstraints(config);
|
|
7133
7342
|
validateJavaConstraints(config, providedFlags);
|
|
7134
7343
|
validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
|
|
7135
7344
|
validateSelfBackendCompatibility(providedFlags, options, config);
|
|
@@ -7166,6 +7375,11 @@ function validateConfigForProgrammaticUse(config) {
|
|
|
7166
7375
|
validateDatabaseOrmAuth(config);
|
|
7167
7376
|
if (config.frontend && config.frontend.length > 0) ensureSingleWebAndNative(config.frontend);
|
|
7168
7377
|
validateApiFrontendCompatibility(config.api, config.frontend, config.astroIntegration);
|
|
7378
|
+
validatePythonApiConstraints(config);
|
|
7379
|
+
validateEmailConstraints(config);
|
|
7380
|
+
validateObservabilityConstraints(config);
|
|
7381
|
+
validateCachingConstraints(config);
|
|
7382
|
+
validateSearchConstraints(config);
|
|
7169
7383
|
validateJavaConstraints(config);
|
|
7170
7384
|
validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
|
|
7171
7385
|
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);
|
|
@@ -8840,7 +9054,8 @@ function displayGoInstructions(config) {
|
|
|
8840
9054
|
if (goApi && goApi !== "none") output += `${pc.cyan("•")} API: ${{ "grpc-go": "gRPC-Go" }[goApi] || goApi}\n`;
|
|
8841
9055
|
if (goCli && goCli !== "none") output += `${pc.cyan("•")} CLI: ${{
|
|
8842
9056
|
cobra: "Cobra",
|
|
8843
|
-
bubbletea: "Bubble Tea"
|
|
9057
|
+
bubbletea: "Bubble Tea",
|
|
9058
|
+
"urfave-cli": "urfave/cli"
|
|
8844
9059
|
}[goCli] || goCli}\n`;
|
|
8845
9060
|
if (goLogging && goLogging !== "none") output += `${pc.cyan("•")} Logging: ${{
|
|
8846
9061
|
zap: "Zap",
|
|
@@ -8942,6 +9157,7 @@ function displayJavaInstructions(config) {
|
|
|
8942
9157
|
const { projectName, relativePath, depsInstalled, javaWebFramework, javaBuildTool, javaOrm, javaAuth, javaLibraries, javaTestingLibraries } = config;
|
|
8943
9158
|
const cdCmd = `cd ${relativePath}`;
|
|
8944
9159
|
const isSpringBoot = javaWebFramework === "spring-boot" && javaBuildTool !== "none";
|
|
9160
|
+
const isQuarkus = javaWebFramework === "quarkus" && javaBuildTool !== "none";
|
|
8945
9161
|
const rawJavaLibraries = isSpringBoot ? javaLibraries.filter((library) => library !== "none") : [];
|
|
8946
9162
|
const rawJavaLibrarySet = new Set(rawJavaLibraries);
|
|
8947
9163
|
const jpaRequiredJavaLibraries = new Set(["flyway", "liquibase"]);
|
|
@@ -8953,7 +9169,7 @@ function displayJavaInstructions(config) {
|
|
|
8953
9169
|
}
|
|
8954
9170
|
const effectiveJavaTestingLibraries = javaBuildTool === "none" ? [] : javaTestingLibraries.filter((library) => library !== "none");
|
|
8955
9171
|
const buildToolCommand = javaBuildTool === "none" ? null : javaBuildTool === "gradle" ? process.platform === "win32" ? "gradlew.bat" : "./gradlew" : process.platform === "win32" ? "mvnw.cmd" : "./mvnw";
|
|
8956
|
-
const runCommand = buildToolCommand ? isSpringBoot ? javaBuildTool === "gradle" ? `${buildToolCommand} bootRun` : `${buildToolCommand} spring-boot:run` : javaBuildTool === "gradle" ? `${buildToolCommand} run` : `${buildToolCommand} exec:java` : null;
|
|
9172
|
+
const runCommand = buildToolCommand ? isSpringBoot ? javaBuildTool === "gradle" ? `${buildToolCommand} bootRun` : `${buildToolCommand} spring-boot:run` : isQuarkus ? javaBuildTool === "gradle" ? `${buildToolCommand} quarkusDev` : `${buildToolCommand} quarkus:dev` : javaBuildTool === "gradle" ? `${buildToolCommand} run` : `${buildToolCommand} exec:java` : null;
|
|
8957
9173
|
const packageCommand = buildToolCommand ? javaBuildTool === "gradle" ? `${buildToolCommand} build` : `${buildToolCommand} package` : null;
|
|
8958
9174
|
const sourceCompileCommand = buildToolCommand ? null : `javac -d out ${getJavaMainSourcePath(projectName)}`;
|
|
8959
9175
|
const sourceRunCommand = buildToolCommand ? null : `java -cp out ${getJavaMainClass(projectName)}`;
|
|
@@ -8966,7 +9182,10 @@ function displayJavaInstructions(config) {
|
|
|
8966
9182
|
output += `${pc.cyan(`${stepCounter++}.`)} ${sourceRunCommand}\n`;
|
|
8967
9183
|
} else output += `${pc.cyan(`${stepCounter++}.`)} Add Maven or Gradle, then run the app\n`;
|
|
8968
9184
|
output += `\n${pc.bold("Your Java project includes:")}\n`;
|
|
8969
|
-
if (isSpringBoot) output += `${pc.cyan("•")} Web Framework: ${{
|
|
9185
|
+
if (isSpringBoot || isQuarkus) output += `${pc.cyan("•")} Web Framework: ${{
|
|
9186
|
+
"spring-boot": "Spring Boot",
|
|
9187
|
+
quarkus: "Quarkus"
|
|
9188
|
+
}[javaWebFramework] || javaWebFramework}\n`;
|
|
8970
9189
|
else output += `${pc.cyan("•")} Scaffold: Plain Java\n`;
|
|
8971
9190
|
if (javaBuildTool && javaBuildTool !== "none") output += `${pc.cyan("•")} Build Tool: ${{
|
|
8972
9191
|
maven: "Maven Wrapper",
|
|
@@ -9023,7 +9242,7 @@ function displayJavaInstructions(config) {
|
|
|
9023
9242
|
consola$1.box(output);
|
|
9024
9243
|
}
|
|
9025
9244
|
function displayPythonInstructions(config) {
|
|
9026
|
-
const { relativePath, depsInstalled, pythonWebFramework, pythonOrm, pythonValidation, pythonAi, pythonTaskQueue, pythonQuality } = config;
|
|
9245
|
+
const { relativePath, depsInstalled, pythonWebFramework, pythonOrm, pythonValidation, pythonAi, pythonApi, pythonTaskQueue, pythonQuality } = config;
|
|
9027
9246
|
const cdCmd = `cd ${relativePath}`;
|
|
9028
9247
|
let runCommand = "uv run uvicorn app.main:app --reload";
|
|
9029
9248
|
if (pythonWebFramework === "django") runCommand = "uv run python manage.py runserver";
|
|
@@ -9058,14 +9277,25 @@ function displayPythonInstructions(config) {
|
|
|
9058
9277
|
const aiList = pythonAi.filter((ai) => ai !== "none").map((ai) => aiNames[ai] || ai).join(", ");
|
|
9059
9278
|
output += `${pc.cyan("•")} AI: ${aiList}\n`;
|
|
9060
9279
|
}
|
|
9280
|
+
if (pythonApi && pythonApi !== "none") output += `${pc.cyan("•")} API Framework: ${{
|
|
9281
|
+
"django-rest-framework": "Django REST Framework",
|
|
9282
|
+
"django-ninja": "Django Ninja"
|
|
9283
|
+
}[pythonApi] || pythonApi}\n`;
|
|
9061
9284
|
if (pythonTaskQueue && pythonTaskQueue !== "none") output += `${pc.cyan("•")} Task Queue: ${{ celery: "Celery" }[pythonTaskQueue] || pythonTaskQueue}\n`;
|
|
9062
|
-
if (pythonQuality && pythonQuality !== "none") output += `${pc.cyan("•")} Code Quality: ${{
|
|
9285
|
+
if (pythonQuality && pythonQuality !== "none") output += `${pc.cyan("•")} Code Quality: ${{
|
|
9286
|
+
ruff: "Ruff",
|
|
9287
|
+
mypy: "mypy",
|
|
9288
|
+
pyright: "Pyright"
|
|
9289
|
+
}[pythonQuality] || pythonQuality}\n`;
|
|
9063
9290
|
output += `\n${pc.bold("Common Python commands:")}\n`;
|
|
9064
9291
|
output += `${pc.cyan("•")} Install: uv sync\n`;
|
|
9065
9292
|
output += `${pc.cyan("•")} Run: ${runCommand}\n`;
|
|
9066
9293
|
output += `${pc.cyan("•")} Test: uv run pytest\n`;
|
|
9067
|
-
|
|
9068
|
-
|
|
9294
|
+
if (pythonQuality === "ruff") {
|
|
9295
|
+
output += `${pc.cyan("•")} Format: uv run ruff format .\n`;
|
|
9296
|
+
output += `${pc.cyan("•")} Lint: uv run ruff check .\n`;
|
|
9297
|
+
} else if (pythonQuality === "mypy") output += `${pc.cyan("•")} Type check: uv run mypy src/app tests\n`;
|
|
9298
|
+
else if (pythonQuality === "pyright") output += `${pc.cyan("•")} Type check: uv run pyright\n`;
|
|
9069
9299
|
output += `\n${pc.bold("Your project will be available at:")}\n`;
|
|
9070
9300
|
output += `${pc.cyan("•")} API: http://localhost:8000\n`;
|
|
9071
9301
|
output += `\n${pc.bold("Enjoying Better Fullstack?")} Help us grow — star the repo!\n`;
|
|
@@ -9251,6 +9481,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
9251
9481
|
pythonValidation: "none",
|
|
9252
9482
|
pythonAi: [],
|
|
9253
9483
|
pythonAuth: "none",
|
|
9484
|
+
pythonApi: "none",
|
|
9254
9485
|
pythonTaskQueue: "none",
|
|
9255
9486
|
pythonGraphql: "none",
|
|
9256
9487
|
pythonQuality: "none",
|
|
@@ -9774,6 +10005,7 @@ async function createVirtual(options) {
|
|
|
9774
10005
|
pythonValidation: options.pythonValidation || "none",
|
|
9775
10006
|
pythonAi: options.pythonAi || [],
|
|
9776
10007
|
pythonAuth: options.pythonAuth || "none",
|
|
10008
|
+
pythonApi: options.pythonApi || "none",
|
|
9777
10009
|
pythonTaskQueue: options.pythonTaskQueue || "none",
|
|
9778
10010
|
pythonGraphql: options.pythonGraphql || "none",
|
|
9779
10011
|
pythonQuality: options.pythonQuality || "none",
|