create-better-fullstack 1.6.0 → 1.6.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/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-snHxP_EH.mjs";
4
- import { _ as setLastPromptShownUI, a as getPackageExecutionArgs, c as UserCancelledError, d as handleError, f as didLastPromptShowUI, g as setIsFirstPrompt$1, h as runWithContextAsync, l as exitCancelled, m as isSilent, o as addPackageDependency, p as isFirstPrompt, s as CLIError, t as setupAddons, u as exitWithError } from "./addons-setup--oB72n29.mjs";
3
+ import { a as DEFAULT_CONFIG, c as getDefaultConfig, i as getLatestCLIVersion, l as getUserPkgManager, n as updateBtsConfig, o as DEFAULT_UI_LIBRARY_BY_FRONTEND, r as writeBtsConfig, t as readBtsConfig } from "./bts-config-B_rZ4_sj.mjs";
4
+ import { _ as setIsFirstPrompt$1, a as canPromptInteractively, c as CLIError, d as exitWithError, f as handleError, g as runWithContextAsync, h as isSilent, l as UserCancelledError, m as isFirstPrompt, o as getPackageExecutionArgs, p as didLastPromptShowUI, s as addPackageDependency, t as setupAddons, u as exitCancelled, v as setLastPromptShownUI } from "./addons-setup-Cgup_RHm.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,11 +10,11 @@ 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, hasWebStyling, isExampleAIAllowed, isExampleChatSdkAllowed, isFrontendAllowedWithBackend, isWebFrontend, requiresChatSdkVercelAIForSelection, splitFrontends, validateAddonCompatibility } from "@better-fullstack/types";
13
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";
14
15
  import gradient from "gradient-string";
15
16
  import path$1 from "path";
16
17
  import { writeTreeToFilesystem } from "@better-fullstack/template-generator/fs-writer";
17
- import { allowedApisForFrontends, getCompatibleAddons, getCompatibleCSSFrameworks, getCompatibleUILibraries, getLocalWebDevPort, hasWebStyling, isExampleAIAllowed, isExampleChatSdkAllowed, isFrontendAllowedWithBackend, isWebFrontend, requiresChatSdkVercelAIForSelection, splitFrontends, validateAddonCompatibility } from "@better-fullstack/types";
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";
@@ -354,6 +354,100 @@ var types_exports = {};
354
354
  import * as import__better_fullstack_types from "@better-fullstack/types";
355
355
  __reExport(types_exports, import__better_fullstack_types);
356
356
 
357
+ //#endregion
358
+ //#region src/create-command-input.ts
359
+ const CreateCommandOptionsSchema = z.object({
360
+ template: types_exports.TemplateSchema.optional().describe("Use a predefined template"),
361
+ yes: z.boolean().optional().default(false).describe("Use default configuration"),
362
+ yolo: z.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
363
+ verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
364
+ dryRun: z.boolean().optional().default(false).describe("Preview generated file tree without writing to disk"),
365
+ ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript, rust, python, go, or java)"),
366
+ database: types_exports.DatabaseSchema.optional(),
367
+ orm: types_exports.ORMSchema.optional(),
368
+ auth: types_exports.AuthSchema.optional(),
369
+ payments: types_exports.PaymentsSchema.optional(),
370
+ email: types_exports.EmailSchema.optional(),
371
+ fileUpload: types_exports.FileUploadSchema.optional(),
372
+ effect: types_exports.EffectSchema.optional(),
373
+ stateManagement: types_exports.StateManagementSchema.optional(),
374
+ validation: types_exports.ValidationSchema.optional(),
375
+ forms: types_exports.FormsSchema.optional(),
376
+ testing: types_exports.TestingSchema.optional(),
377
+ ai: types_exports.AISchema.optional(),
378
+ realtime: types_exports.RealtimeSchema.optional(),
379
+ jobQueue: types_exports.JobQueueSchema.optional(),
380
+ animation: types_exports.AnimationSchema.optional(),
381
+ logging: types_exports.LoggingSchema.optional(),
382
+ observability: types_exports.ObservabilitySchema.optional(),
383
+ featureFlags: types_exports.FeatureFlagsSchema.optional().describe("Feature flags provider"),
384
+ analytics: types_exports.AnalyticsSchema.optional().describe("Privacy-focused analytics"),
385
+ cms: types_exports.CMSSchema.optional().describe("Headless CMS solution"),
386
+ caching: types_exports.CachingSchema.optional().describe("Caching solution"),
387
+ i18n: types_exports.I18nSchema.optional().describe("Internationalization (i18n) library"),
388
+ search: types_exports.SearchSchema.optional().describe("Search engine solution"),
389
+ fileStorage: types_exports.FileStorageSchema.optional().describe("File storage solution (S3, R2)"),
390
+ frontend: z.array(types_exports.FrontendSchema).optional(),
391
+ astroIntegration: types_exports.AstroIntegrationSchema.optional().describe("Astro UI framework integration (react, vue, svelte, solid)"),
392
+ addons: z.array(types_exports.AddonsSchema).optional(),
393
+ examples: z.array(types_exports.ExamplesSchema).optional(),
394
+ git: z.boolean().optional(),
395
+ packageManager: types_exports.PackageManagerSchema.optional(),
396
+ install: z.boolean().optional(),
397
+ versionChannel: types_exports.VersionChannelSchema.optional().describe("Dependency version channel (stable, latest, beta)"),
398
+ dbSetup: types_exports.DatabaseSetupSchema.optional(),
399
+ backend: types_exports.BackendSchema.optional(),
400
+ runtime: types_exports.RuntimeSchema.optional(),
401
+ api: types_exports.APISchema.optional(),
402
+ cssFramework: types_exports.CSSFrameworkSchema.optional(),
403
+ uiLibrary: types_exports.UILibrarySchema.optional(),
404
+ shadcnBase: types_exports.ShadcnBaseSchema.optional().describe("shadcn/ui headless library (radix, base)"),
405
+ shadcnStyle: types_exports.ShadcnStyleSchema.optional().describe("shadcn/ui visual style (vega, nova, maia, lyra, mira)"),
406
+ shadcnIconLibrary: types_exports.ShadcnIconLibrarySchema.optional().describe("shadcn/ui icon library (lucide, tabler, hugeicons, phosphor, remixicon)"),
407
+ shadcnColorTheme: types_exports.ShadcnColorThemeSchema.optional().describe("shadcn/ui color theme (neutral, blue, violet, etc.)"),
408
+ shadcnBaseColor: types_exports.ShadcnBaseColorSchema.optional().describe("shadcn/ui base neutral color (neutral, stone, zinc, gray)"),
409
+ shadcnFont: types_exports.ShadcnFontSchema.optional().describe("shadcn/ui font (inter, geist, figtree, etc.)"),
410
+ shadcnRadius: types_exports.ShadcnRadiusSchema.optional().describe("shadcn/ui border radius (default, none, small, medium, large)"),
411
+ webDeploy: types_exports.WebDeploySchema.optional(),
412
+ serverDeploy: types_exports.ServerDeploySchema.optional(),
413
+ directoryConflict: types_exports.DirectoryConflictSchema.optional(),
414
+ renderTitle: z.boolean().optional(),
415
+ disableAnalytics: z.boolean().optional().default(false).describe("Disable analytics"),
416
+ manualDb: z.boolean().optional().default(false).describe("Skip automatic/manual database setup prompt and use manual setup"),
417
+ rustWebFramework: types_exports.RustWebFrameworkSchema.optional().describe("Rust web framework (axum, actix-web)"),
418
+ rustFrontend: types_exports.RustFrontendSchema.optional().describe("Rust WASM frontend (leptos, dioxus)"),
419
+ rustOrm: types_exports.RustOrmSchema.optional().describe("Rust ORM/database (sea-orm, sqlx)"),
420
+ rustApi: types_exports.RustApiSchema.optional().describe("Rust API layer (tonic, async-graphql)"),
421
+ rustCli: types_exports.RustCliSchema.optional().describe("Rust CLI tools (clap, ratatui)"),
422
+ rustLibraries: z.array(types_exports.RustLibrariesSchema).optional().describe("Rust core libraries"),
423
+ rustLogging: types_exports.RustLoggingSchema.optional().describe("Rust logging (tracing, env-logger)"),
424
+ rustErrorHandling: types_exports.RustErrorHandlingSchema.optional().describe("Rust error handling (anyhow-thiserror, eyre)"),
425
+ rustCaching: types_exports.RustCachingSchema.optional().describe("Rust caching (moka, redis)"),
426
+ rustAuth: types_exports.RustAuthSchema.optional().describe("Rust auth (oauth2)"),
427
+ pythonWebFramework: types_exports.PythonWebFrameworkSchema.optional().describe("Python web framework (fastapi, django)"),
428
+ pythonOrm: types_exports.PythonOrmSchema.optional().describe("Python ORM/database (sqlalchemy, sqlmodel)"),
429
+ pythonValidation: types_exports.PythonValidationSchema.optional().describe("Python validation (pydantic)"),
430
+ pythonAi: z.array(types_exports.PythonAiSchema).optional().describe("Python AI/ML frameworks"),
431
+ pythonAuth: types_exports.PythonAuthSchema.optional().describe("Python auth library (authlib, jwt)"),
432
+ pythonTaskQueue: types_exports.PythonTaskQueueSchema.optional().describe("Python task queue (celery)"),
433
+ pythonGraphql: types_exports.PythonGraphqlSchema.optional().describe("Python GraphQL framework (strawberry)"),
434
+ pythonQuality: types_exports.PythonQualitySchema.optional().describe("Python code quality (ruff)"),
435
+ goWebFramework: types_exports.GoWebFrameworkSchema.optional().describe("Go web framework (gin, echo, fiber)"),
436
+ goOrm: types_exports.GoOrmSchema.optional().describe("Go ORM/database (gorm, sqlc)"),
437
+ goApi: types_exports.GoApiSchema.optional().describe("Go API layer (grpc-go)"),
438
+ goCli: types_exports.GoCliSchema.optional().describe("Go CLI tools (cobra, bubbletea)"),
439
+ goLogging: types_exports.GoLoggingSchema.optional().describe("Go logging (zap, zerolog, slog)"),
440
+ goAuth: types_exports.GoAuthSchema.optional().describe("Go auth (casbin, jwt)"),
441
+ javaWebFramework: types_exports.JavaWebFrameworkSchema.optional().describe("Java web framework (spring-boot, none)"),
442
+ javaBuildTool: types_exports.JavaBuildToolSchema.optional().describe("Java build tool (maven, gradle, none)"),
443
+ javaOrm: types_exports.JavaOrmSchema.optional().describe("Java ORM/database (spring-data-jpa)"),
444
+ javaAuth: types_exports.JavaAuthSchema.optional().describe("Java auth (spring-security)"),
445
+ javaLibraries: z.array(types_exports.JavaLibrariesSchema).optional().describe("Java application libraries"),
446
+ javaTestingLibraries: z.array(types_exports.JavaTestingLibrariesSchema).optional().describe("Java testing libraries"),
447
+ aiDocs: z.array(types_exports.AiDocsSchema).optional().describe("AI documentation files (claude-md, agents-md, cursorrules)")
448
+ });
449
+ const CreateCommandInputSchema = z.tuple([types_exports.ProjectNameSchema.optional(), CreateCommandOptionsSchema]);
450
+
357
451
  //#endregion
358
452
  //#region src/utils/error-formatter.ts
359
453
  function getCategoryTitle(category) {
@@ -1341,12 +1435,23 @@ async function applyDependencyVersionChannel(projectDir, channel) {
1341
1435
 
1342
1436
  //#endregion
1343
1437
  //#region src/helpers/core/install-dependencies.ts
1438
+ function getInstallEnvironment(packageManager) {
1439
+ if (packageManager !== "yarn") return;
1440
+ return {
1441
+ YARN_ENABLE_HARDENED_MODE: "0",
1442
+ YARN_ENABLE_IMMUTABLE_INSTALLS: "false"
1443
+ };
1444
+ }
1344
1445
  async function installDependencies({ projectDir, packageManager }) {
1345
1446
  const s = spinner();
1346
1447
  try {
1347
1448
  s.start(`Running ${packageManager} install...`);
1348
1449
  await $({
1349
1450
  cwd: projectDir,
1451
+ env: {
1452
+ ...process.env,
1453
+ ...getInstallEnvironment(packageManager)
1454
+ },
1350
1455
  stderr: "inherit"
1351
1456
  })`${packageManager} install`;
1352
1457
  s.stop("Dependencies installed successfully");
@@ -1397,6 +1502,36 @@ async function runGoModTidy({ projectDir }) {
1397
1502
  if (error instanceof Error) consola.error(pc.red(`go mod tidy error: ${error.message}`));
1398
1503
  }
1399
1504
  }
1505
+ async function runMavenTests({ projectDir }) {
1506
+ const s = spinner();
1507
+ const mvnw = process.platform === "win32" ? "mvnw.cmd" : "./mvnw";
1508
+ try {
1509
+ s.start("Running Maven tests...");
1510
+ await $({
1511
+ cwd: projectDir,
1512
+ stderr: "inherit"
1513
+ })`${mvnw} test`;
1514
+ s.stop("Maven tests completed");
1515
+ } catch (error) {
1516
+ s.stop(pc.red("Maven tests failed"));
1517
+ if (error instanceof Error) consola.error(pc.red(`Maven test error: ${error.message}`));
1518
+ }
1519
+ }
1520
+ async function runGradleTests({ projectDir }) {
1521
+ const s = spinner();
1522
+ const gradlew = process.platform === "win32" ? "gradlew.bat" : "./gradlew";
1523
+ try {
1524
+ s.start("Running Gradle tests...");
1525
+ await $({
1526
+ cwd: projectDir,
1527
+ stderr: "inherit"
1528
+ })`${gradlew} test`;
1529
+ s.stop("Gradle tests completed");
1530
+ } catch (error) {
1531
+ s.stop(pc.red("Gradle tests failed"));
1532
+ if (error instanceof Error) consola.error(pc.red(`Gradle test error: ${error.message}`));
1533
+ }
1534
+ }
1400
1535
 
1401
1536
  //#endregion
1402
1537
  //#region src/helpers/core/add-handler.ts
@@ -1535,70 +1670,104 @@ async function collectPackageJsonPaths(projectDir) {
1535
1670
  return results;
1536
1671
  }
1537
1672
 
1673
+ //#endregion
1674
+ //#region src/prompts/prompt-contract.ts
1675
+ function createStaticSinglePromptResolution(options, initialValue, selectedValue) {
1676
+ return selectedValue !== void 0 ? {
1677
+ shouldPrompt: false,
1678
+ mode: "single",
1679
+ options,
1680
+ autoValue: selectedValue
1681
+ } : {
1682
+ shouldPrompt: true,
1683
+ mode: "single",
1684
+ options,
1685
+ initialValue
1686
+ };
1687
+ }
1688
+ function createStaticMultiPromptResolution(options, initialValue, selectedValue) {
1689
+ return selectedValue !== void 0 ? {
1690
+ shouldPrompt: false,
1691
+ mode: "multiple",
1692
+ options,
1693
+ autoValue: selectedValue
1694
+ } : {
1695
+ shouldPrompt: true,
1696
+ mode: "multiple",
1697
+ options,
1698
+ initialValue
1699
+ };
1700
+ }
1701
+
1538
1702
  //#endregion
1539
1703
  //#region src/prompts/ai.ts
1704
+ const AI_PROMPT_OPTIONS = [
1705
+ {
1706
+ value: "vercel-ai",
1707
+ label: "Vercel AI SDK",
1708
+ hint: "The AI Toolkit for TypeScript - supports OpenAI, Anthropic, Google, etc."
1709
+ },
1710
+ {
1711
+ value: "mastra",
1712
+ label: "Mastra",
1713
+ hint: "TypeScript-native AI agent framework with workflows"
1714
+ },
1715
+ {
1716
+ value: "voltagent",
1717
+ label: "VoltAgent",
1718
+ hint: "AI Agent framework with memory, workflows, and observability"
1719
+ },
1720
+ {
1721
+ value: "langgraph",
1722
+ label: "LangGraph.js",
1723
+ hint: "Graph-based agent orchestration with stateful workflows"
1724
+ },
1725
+ {
1726
+ value: "openai-agents",
1727
+ label: "OpenAI Agents SDK",
1728
+ hint: "Official multi-agent framework with handoffs and guardrails"
1729
+ },
1730
+ {
1731
+ value: "google-adk",
1732
+ label: "Google ADK",
1733
+ hint: "Code-first agent development kit for building AI agents"
1734
+ },
1735
+ {
1736
+ value: "modelfusion",
1737
+ label: "ModelFusion",
1738
+ hint: "Type-safe AI library for multi-provider text generation"
1739
+ },
1740
+ {
1741
+ value: "langchain",
1742
+ label: "LangChain",
1743
+ hint: "Build context-aware reasoning applications"
1744
+ },
1745
+ {
1746
+ value: "llamaindex",
1747
+ label: "LlamaIndex",
1748
+ hint: "Data framework for LLM applications"
1749
+ },
1750
+ {
1751
+ value: "tanstack-ai",
1752
+ label: "TanStack AI",
1753
+ hint: "Unified LLM interface for AI-powered apps (Alpha)"
1754
+ },
1755
+ {
1756
+ value: "none",
1757
+ label: "None",
1758
+ hint: "No AI SDK"
1759
+ }
1760
+ ];
1761
+ function resolveAIPrompt(ai) {
1762
+ return createStaticSinglePromptResolution(AI_PROMPT_OPTIONS, "none", ai);
1763
+ }
1540
1764
  async function getAIChoice(ai) {
1541
- if (ai !== void 0) return ai;
1765
+ const resolution = resolveAIPrompt(ai);
1766
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
1542
1767
  const response = await navigableSelect({
1543
1768
  message: "Select AI SDK",
1544
- options: [
1545
- {
1546
- value: "vercel-ai",
1547
- label: "Vercel AI SDK",
1548
- hint: "The AI Toolkit for TypeScript - supports OpenAI, Anthropic, Google, etc."
1549
- },
1550
- {
1551
- value: "mastra",
1552
- label: "Mastra",
1553
- hint: "TypeScript-native AI agent framework with workflows"
1554
- },
1555
- {
1556
- value: "voltagent",
1557
- label: "VoltAgent",
1558
- hint: "AI Agent framework with memory, workflows, and observability"
1559
- },
1560
- {
1561
- value: "langgraph",
1562
- label: "LangGraph.js",
1563
- hint: "Graph-based agent orchestration with stateful workflows"
1564
- },
1565
- {
1566
- value: "openai-agents",
1567
- label: "OpenAI Agents SDK",
1568
- hint: "Official multi-agent framework with handoffs and guardrails"
1569
- },
1570
- {
1571
- value: "google-adk",
1572
- label: "Google ADK",
1573
- hint: "Code-first agent development kit for building AI agents"
1574
- },
1575
- {
1576
- value: "modelfusion",
1577
- label: "ModelFusion",
1578
- hint: "Type-safe AI library for multi-provider text generation"
1579
- },
1580
- {
1581
- value: "langchain",
1582
- label: "LangChain",
1583
- hint: "Build context-aware reasoning applications"
1584
- },
1585
- {
1586
- value: "llamaindex",
1587
- label: "LlamaIndex",
1588
- hint: "Data framework for LLM applications"
1589
- },
1590
- {
1591
- value: "tanstack-ai",
1592
- label: "TanStack AI",
1593
- hint: "Unified LLM interface for AI-powered apps (Alpha)"
1594
- },
1595
- {
1596
- value: "none",
1597
- label: "None",
1598
- hint: "No AI SDK"
1599
- }
1600
- ],
1601
- initialValue: "none"
1769
+ options: resolution.options,
1770
+ initialValue: resolution.initialValue
1602
1771
  });
1603
1772
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1604
1773
  return response;
@@ -1642,10 +1811,20 @@ async function getAiDocsChoice(aiDocs) {
1642
1811
 
1643
1812
  //#endregion
1644
1813
  //#region src/prompts/animation.ts
1645
- async function getAnimationChoice(animation, frontends) {
1646
- if (animation !== void 0) return animation;
1647
- const { web } = splitFrontends$1(frontends);
1648
- if (web.length === 0) return "none";
1814
+ function resolveAnimationPrompt(context = {}) {
1815
+ if (context.animation !== void 0) return {
1816
+ shouldPrompt: false,
1817
+ mode: "single",
1818
+ options: [],
1819
+ autoValue: context.animation
1820
+ };
1821
+ const { web } = splitFrontends$1(context.frontends);
1822
+ if (web.length === 0) return {
1823
+ shouldPrompt: false,
1824
+ mode: "single",
1825
+ options: [],
1826
+ autoValue: "none"
1827
+ };
1649
1828
  const isReact = web.some((f) => [
1650
1829
  "tanstack-router",
1651
1830
  "react-router",
@@ -1684,10 +1863,23 @@ async function getAnimationChoice(animation, frontends) {
1684
1863
  label: "None",
1685
1864
  hint: "Skip animation library setup"
1686
1865
  });
1687
- const response = await navigableSelect({
1688
- message: "Select animation library",
1866
+ return {
1867
+ shouldPrompt: true,
1868
+ mode: "single",
1689
1869
  options,
1690
1870
  initialValue: "none"
1871
+ };
1872
+ }
1873
+ async function getAnimationChoice(animation, frontends) {
1874
+ const resolution = resolveAnimationPrompt({
1875
+ animation,
1876
+ frontends
1877
+ });
1878
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
1879
+ const response = await navigableSelect({
1880
+ message: "Select animation library",
1881
+ options: resolution.options,
1882
+ initialValue: resolution.initialValue
1691
1883
  });
1692
1884
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1693
1885
  return response;
@@ -1695,47 +1887,72 @@ async function getAnimationChoice(animation, frontends) {
1695
1887
 
1696
1888
  //#endregion
1697
1889
  //#region src/prompts/api.ts
1698
- async function getApiChoice(Api, frontend, backend, astroIntegration) {
1699
- if (backend === "convex" || backend === "none") return "none";
1700
- const allowed = allowedApisForFrontends$1(frontend ?? [], astroIntegration);
1701
- if (Api) return allowed.includes(Api) ? Api : allowed[0];
1702
- const apiOptionMap = {
1703
- trpc: {
1704
- value: "trpc",
1705
- label: "tRPC",
1706
- hint: "End-to-end typesafe APIs made easy"
1707
- },
1708
- orpc: {
1709
- value: "orpc",
1710
- label: "oRPC",
1711
- hint: "End-to-end type-safe APIs that adhere to OpenAPI standards"
1712
- },
1713
- "ts-rest": {
1714
- value: "ts-rest",
1715
- label: "ts-rest",
1716
- hint: "RPC-like client, contract, and server implementation for REST APIs"
1717
- },
1718
- garph: {
1719
- value: "garph",
1720
- label: "Garph",
1721
- hint: "Fullstack GraphQL framework with end-to-end type safety"
1722
- },
1723
- "graphql-yoga": {
1724
- value: "graphql-yoga",
1725
- label: "GraphQL Yoga",
1726
- hint: "Batteries-included GraphQL server with Pothos schema builder"
1727
- },
1728
- none: {
1729
- value: "none",
1730
- label: "None",
1731
- hint: "No API layer (e.g. for full-stack frameworks like Next.js with Route Handlers)"
1732
- }
1890
+ const API_PROMPT_OPTION_MAP = {
1891
+ trpc: {
1892
+ value: "trpc",
1893
+ label: "tRPC",
1894
+ hint: "End-to-end typesafe APIs made easy"
1895
+ },
1896
+ orpc: {
1897
+ value: "orpc",
1898
+ label: "oRPC",
1899
+ hint: "End-to-end type-safe APIs that adhere to OpenAPI standards"
1900
+ },
1901
+ "ts-rest": {
1902
+ value: "ts-rest",
1903
+ label: "ts-rest",
1904
+ hint: "RPC-like client, contract, and server implementation for REST APIs"
1905
+ },
1906
+ garph: {
1907
+ value: "garph",
1908
+ label: "Garph",
1909
+ hint: "Fullstack GraphQL framework with end-to-end type safety"
1910
+ },
1911
+ "graphql-yoga": {
1912
+ value: "graphql-yoga",
1913
+ label: "GraphQL Yoga",
1914
+ hint: "Batteries-included GraphQL server with Pothos schema builder"
1915
+ },
1916
+ none: {
1917
+ value: "none",
1918
+ label: "None",
1919
+ hint: "No API layer (e.g. for full-stack frameworks like Next.js with Route Handlers)"
1920
+ }
1921
+ };
1922
+ function resolveApiPrompt(context = {}) {
1923
+ if (context.backend === "convex" || context.backend === "none") return {
1924
+ shouldPrompt: false,
1925
+ mode: "single",
1926
+ options: [],
1927
+ autoValue: "none"
1928
+ };
1929
+ const allowedOptions = allowedApisForFrontends$1(context.frontend ?? [], context.astroIntegration);
1930
+ const options = allowedOptions.map((value) => API_PROMPT_OPTION_MAP[value]);
1931
+ if (context.api !== void 0) return {
1932
+ shouldPrompt: false,
1933
+ mode: "single",
1934
+ options,
1935
+ autoValue: allowedOptions.includes(context.api) ? context.api : allowedOptions[0] ?? "none"
1733
1936
  };
1734
- const apiOptions = allowed.map((a) => apiOptionMap[a]);
1937
+ return {
1938
+ shouldPrompt: true,
1939
+ mode: "single",
1940
+ options,
1941
+ initialValue: allowedOptions[0] ?? "none"
1942
+ };
1943
+ }
1944
+ async function getApiChoice(api, frontend, backend, astroIntegration) {
1945
+ const resolution = resolveApiPrompt({
1946
+ api,
1947
+ frontend,
1948
+ backend,
1949
+ astroIntegration
1950
+ });
1951
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
1735
1952
  const apiType = await navigableSelect({
1736
1953
  message: "Select API type",
1737
- options: apiOptions,
1738
- initialValue: apiOptions[0].value
1954
+ options: resolution.options,
1955
+ initialValue: resolution.initialValue
1739
1956
  });
1740
1957
  if (isCancel$1(apiType)) return exitCancelled("Operation cancelled");
1741
1958
  return apiType;
@@ -1743,38 +1960,43 @@ async function getApiChoice(Api, frontend, backend, astroIntegration) {
1743
1960
 
1744
1961
  //#endregion
1745
1962
  //#region src/prompts/astro-integration.ts
1963
+ const ASTRO_INTEGRATION_PROMPT_OPTIONS = [
1964
+ {
1965
+ value: "react",
1966
+ label: "React",
1967
+ hint: "Full React component support (required for tRPC)"
1968
+ },
1969
+ {
1970
+ value: "vue",
1971
+ label: "Vue",
1972
+ hint: "Vue 3 component support"
1973
+ },
1974
+ {
1975
+ value: "svelte",
1976
+ label: "Svelte",
1977
+ hint: "Svelte component support"
1978
+ },
1979
+ {
1980
+ value: "solid",
1981
+ label: "Solid",
1982
+ hint: "SolidJS component support"
1983
+ },
1984
+ {
1985
+ value: "none",
1986
+ label: "None",
1987
+ hint: "Astro components only (no client-side JS framework)"
1988
+ }
1989
+ ];
1990
+ function resolveAstroIntegrationPrompt(astroIntegration) {
1991
+ return createStaticSinglePromptResolution(ASTRO_INTEGRATION_PROMPT_OPTIONS, "react", astroIntegration);
1992
+ }
1746
1993
  async function getAstroIntegrationChoice(astroIntegration) {
1747
- if (astroIntegration !== void 0) return astroIntegration;
1994
+ const resolution = resolveAstroIntegrationPrompt(astroIntegration);
1995
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
1748
1996
  const response = await navigableSelect({
1749
1997
  message: "Choose Astro UI framework integration",
1750
- options: [
1751
- {
1752
- value: "react",
1753
- label: "React",
1754
- hint: "Full React component support (required for tRPC)"
1755
- },
1756
- {
1757
- value: "vue",
1758
- label: "Vue",
1759
- hint: "Vue 3 component support"
1760
- },
1761
- {
1762
- value: "svelte",
1763
- label: "Svelte",
1764
- hint: "Svelte component support"
1765
- },
1766
- {
1767
- value: "solid",
1768
- label: "Solid",
1769
- hint: "SolidJS component support"
1770
- },
1771
- {
1772
- value: "none",
1773
- label: "None",
1774
- hint: "Astro components only (no client-side JS framework)"
1775
- }
1776
- ],
1777
- initialValue: "react"
1998
+ options: resolution.options,
1999
+ initialValue: resolution.initialValue
1778
2000
  });
1779
2001
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1780
2002
  return response;
@@ -1782,8 +2004,7 @@ async function getAstroIntegrationChoice(astroIntegration) {
1782
2004
 
1783
2005
  //#endregion
1784
2006
  //#region src/prompts/auth.ts
1785
- async function getAuthChoice(auth, backend, frontend, ecosystem = "typescript") {
1786
- if (auth !== void 0) return auth;
2007
+ function resolveAuthPrompt(context = {}) {
1787
2008
  const authOptionOrder = [
1788
2009
  { value: "better-auth" },
1789
2010
  { value: "go-better-auth" },
@@ -1795,23 +2016,49 @@ async function getAuthChoice(auth, backend, frontend, ecosystem = "typescript")
1795
2016
  { value: "none" }
1796
2017
  ];
1797
2018
  const supportedOptions = (0, types_exports.getSupportedCapabilityOptions)("auth", {
1798
- ecosystem,
1799
- backend,
1800
- frontend
2019
+ ecosystem: context.ecosystem ?? "typescript",
2020
+ backend: context.backend,
2021
+ frontend: context.frontend
1801
2022
  });
1802
2023
  const options = authOptionOrder.flatMap(({ value }) => {
1803
2024
  const option = supportedOptions.find((candidate) => candidate.id === value);
1804
- return option ? [option] : [];
1805
- });
1806
- if (options.length === 1 && options[0]?.id === "none") return "none";
1807
- const response = await navigableSelect({
1808
- message: "Select authentication provider",
1809
- options: options.map((option) => ({
2025
+ return option ? [{
1810
2026
  value: option.id,
1811
2027
  label: option.label,
1812
2028
  hint: option.promptHint
1813
- })),
1814
- initialValue: options.some((option) => option.id === DEFAULT_CONFIG.auth) ? DEFAULT_CONFIG.auth : options.find((option) => option.id !== "none")?.id ?? "none"
2029
+ }] : [];
2030
+ });
2031
+ if (context.auth !== void 0) return {
2032
+ shouldPrompt: false,
2033
+ mode: "single",
2034
+ options,
2035
+ autoValue: context.auth
2036
+ };
2037
+ if (options.length === 1 && options[0]?.value === "none") return {
2038
+ shouldPrompt: false,
2039
+ mode: "single",
2040
+ options,
2041
+ autoValue: "none"
2042
+ };
2043
+ return {
2044
+ shouldPrompt: true,
2045
+ mode: "single",
2046
+ options,
2047
+ initialValue: options.some((option) => option.value === DEFAULT_CONFIG.auth) ? DEFAULT_CONFIG.auth : options.find((option) => option.value !== "none")?.value ?? "none"
2048
+ };
2049
+ }
2050
+ async function getAuthChoice(auth, backend, frontend, ecosystem = "typescript") {
2051
+ const resolution = resolveAuthPrompt({
2052
+ auth,
2053
+ backend,
2054
+ frontend,
2055
+ ecosystem
2056
+ });
2057
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2058
+ const response = await navigableSelect({
2059
+ message: "Select authentication provider",
2060
+ options: resolution.options,
2061
+ initialValue: resolution.initialValue
1815
2062
  });
1816
2063
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1817
2064
  return response;
@@ -1827,67 +2074,108 @@ const FULLSTACK_FRONTENDS = [
1827
2074
  "svelte",
1828
2075
  "solid-start"
1829
2076
  ];
1830
- async function getBackendFrameworkChoice(backendFramework, frontends) {
1831
- if (backendFramework !== void 0) return backendFramework;
1832
- const hasIncompatibleFrontend = frontends?.some((f) => f === "solid" || f === "solid-start");
1833
- const hasFullstackFrontend = frontends?.some((f) => FULLSTACK_FRONTENDS.includes(f));
1834
- const backendOptions = [];
1835
- if (hasFullstackFrontend) backendOptions.push({
2077
+ const BACKEND_PROMPT_OPTIONS = [
2078
+ {
1836
2079
  value: "self",
1837
2080
  label: "Self (Fullstack)",
1838
2081
  hint: "Use frontend's built-in api routes"
1839
- });
1840
- backendOptions.push({
2082
+ },
2083
+ {
1841
2084
  value: "hono",
1842
2085
  label: "Hono",
1843
2086
  hint: "Lightweight, ultrafast web framework"
1844
- }, {
2087
+ },
2088
+ {
1845
2089
  value: "express",
1846
2090
  label: "Express",
1847
2091
  hint: "Fast, unopinionated, minimalist web framework for Node.js"
1848
- }, {
2092
+ },
2093
+ {
1849
2094
  value: "fastify",
1850
2095
  label: "Fastify",
1851
2096
  hint: "Fast, low-overhead web framework for Node.js"
1852
- }, {
2097
+ },
2098
+ {
1853
2099
  value: "elysia",
1854
2100
  label: "Elysia",
1855
2101
  hint: "Ergonomic web framework for building backend servers"
1856
- }, {
2102
+ },
2103
+ {
1857
2104
  value: "fets",
1858
2105
  label: "feTS",
1859
2106
  hint: "TypeScript HTTP Framework with e2e type-safety"
1860
- }, {
2107
+ },
2108
+ {
1861
2109
  value: "nestjs",
1862
2110
  label: "NestJS",
1863
2111
  hint: "Progressive Node.js framework for scalable applications"
1864
- }, {
2112
+ },
2113
+ {
1865
2114
  value: "adonisjs",
1866
2115
  label: "AdonisJS",
1867
2116
  hint: "Full-featured MVC framework for Node.js"
1868
- }, {
2117
+ },
2118
+ {
1869
2119
  value: "nitro",
1870
2120
  label: "Nitro",
1871
2121
  hint: "Universal server framework from UnJS"
1872
- }, {
2122
+ },
2123
+ {
1873
2124
  value: "encore",
1874
2125
  label: "Encore",
1875
2126
  hint: "Backend development platform with built-in infrastructure"
1876
- });
1877
- if (!hasIncompatibleFrontend) backendOptions.push({
2127
+ },
2128
+ {
1878
2129
  value: "convex",
1879
2130
  label: "Convex",
1880
2131
  hint: "Reactive backend-as-a-service platform"
1881
- });
1882
- backendOptions.push({
2132
+ },
2133
+ {
1883
2134
  value: "none",
1884
2135
  label: "None",
1885
2136
  hint: "No backend server"
2137
+ }
2138
+ ];
2139
+ function resolveBackendPrompt(context = {}) {
2140
+ const hasIncompatibleFrontend = context.frontends?.some((frontend) => frontend === "solid" || frontend === "solid-start");
2141
+ const hasFullstackFrontend = context.frontends?.some((frontend) => FULLSTACK_FRONTENDS.includes(frontend));
2142
+ const availableValues = new Set([
2143
+ ...hasFullstackFrontend ? ["self"] : [],
2144
+ "hono",
2145
+ "express",
2146
+ "fastify",
2147
+ "elysia",
2148
+ "fets",
2149
+ "nestjs",
2150
+ "adonisjs",
2151
+ "nitro",
2152
+ "encore",
2153
+ ...!hasIncompatibleFrontend ? ["convex"] : [],
2154
+ "none"
2155
+ ]);
2156
+ const options = BACKEND_PROMPT_OPTIONS.filter((option) => availableValues.has(option.value));
2157
+ return context.backendFramework !== void 0 ? {
2158
+ shouldPrompt: false,
2159
+ mode: "single",
2160
+ options,
2161
+ autoValue: context.backendFramework
2162
+ } : {
2163
+ shouldPrompt: true,
2164
+ mode: "single",
2165
+ options,
2166
+ initialValue: hasFullstackFrontend ? "self" : DEFAULT_CONFIG.backend
2167
+ };
2168
+ }
2169
+ async function getBackendFrameworkChoice(backendFramework, frontends) {
2170
+ const resolution = resolveBackendPrompt({
2171
+ backendFramework,
2172
+ frontends
1886
2173
  });
2174
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
1887
2175
  const response = await navigableSelect({
1888
2176
  message: "Select backend",
1889
- options: backendOptions,
1890
- initialValue: hasFullstackFrontend ? "self" : DEFAULT_CONFIG.backend
2177
+ options: resolution.options,
2178
+ initialValue: resolution.initialValue
1891
2179
  });
1892
2180
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1893
2181
  return response;
@@ -1895,21 +2183,44 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
1895
2183
 
1896
2184
  //#endregion
1897
2185
  //#region src/prompts/caching.ts
2186
+ const CACHING_PROMPT_OPTIONS = [{
2187
+ value: "upstash-redis",
2188
+ label: "Upstash Redis",
2189
+ hint: "Serverless Redis with REST API for edge and serverless"
2190
+ }, {
2191
+ value: "none",
2192
+ label: "None",
2193
+ hint: "Skip caching layer setup"
2194
+ }];
2195
+ function resolveCachingPrompt(context = {}) {
2196
+ if (context.backend === "none" || context.backend === "convex") return {
2197
+ shouldPrompt: false,
2198
+ mode: "single",
2199
+ options: [],
2200
+ autoValue: "none"
2201
+ };
2202
+ return context.caching !== void 0 ? {
2203
+ shouldPrompt: false,
2204
+ mode: "single",
2205
+ options: CACHING_PROMPT_OPTIONS,
2206
+ autoValue: context.caching
2207
+ } : {
2208
+ shouldPrompt: true,
2209
+ mode: "single",
2210
+ options: CACHING_PROMPT_OPTIONS,
2211
+ initialValue: "none"
2212
+ };
2213
+ }
1898
2214
  async function getCachingChoice(caching, backend) {
1899
- if (caching !== void 0) return caching;
1900
- if (backend === "none" || backend === "convex") return "none";
2215
+ const resolution = resolveCachingPrompt({
2216
+ caching,
2217
+ backend
2218
+ });
2219
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
1901
2220
  const response = await navigableSelect({
1902
2221
  message: "Select caching solution",
1903
- options: [{
1904
- value: "upstash-redis",
1905
- label: "Upstash Redis",
1906
- hint: "Serverless Redis with REST API for edge and serverless"
1907
- }, {
1908
- value: "none",
1909
- label: "None",
1910
- hint: "Skip caching layer setup"
1911
- }],
1912
- initialValue: "none"
2222
+ options: resolution.options,
2223
+ initialValue: resolution.initialValue
1913
2224
  });
1914
2225
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1915
2226
  return response;
@@ -1917,39 +2228,62 @@ async function getCachingChoice(caching, backend) {
1917
2228
 
1918
2229
  //#endregion
1919
2230
  //#region src/prompts/cms.ts
2231
+ const CMS_PROMPT_OPTIONS = [
2232
+ {
2233
+ value: "payload",
2234
+ label: "Payload",
2235
+ hint: "TypeScript-first headless CMS with Next.js integration"
2236
+ },
2237
+ {
2238
+ value: "sanity",
2239
+ label: "Sanity",
2240
+ hint: "Real-time collaborative CMS with schema-as-code"
2241
+ },
2242
+ {
2243
+ value: "strapi",
2244
+ label: "Strapi",
2245
+ hint: "Open-source headless CMS with admin panel"
2246
+ },
2247
+ {
2248
+ value: "tinacms",
2249
+ label: "TinaCMS",
2250
+ hint: "Git-backed headless CMS with visual editing"
2251
+ },
2252
+ {
2253
+ value: "none",
2254
+ label: "None",
2255
+ hint: "Skip headless CMS setup"
2256
+ }
2257
+ ];
2258
+ function resolveCMSPrompt(context = {}) {
2259
+ if (context.backend === "none" || context.backend === "convex") return {
2260
+ shouldPrompt: false,
2261
+ mode: "single",
2262
+ options: [],
2263
+ autoValue: "none"
2264
+ };
2265
+ return context.cms !== void 0 ? {
2266
+ shouldPrompt: false,
2267
+ mode: "single",
2268
+ options: CMS_PROMPT_OPTIONS,
2269
+ autoValue: context.cms
2270
+ } : {
2271
+ shouldPrompt: true,
2272
+ mode: "single",
2273
+ options: CMS_PROMPT_OPTIONS,
2274
+ initialValue: "none"
2275
+ };
2276
+ }
1920
2277
  async function getCMSChoice(cms, backend) {
1921
- if (cms !== void 0) return cms;
1922
- if (backend === "none" || backend === "convex") return "none";
2278
+ const resolution = resolveCMSPrompt({
2279
+ cms,
2280
+ backend
2281
+ });
2282
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
1923
2283
  const response = await navigableSelect({
1924
2284
  message: "Select headless CMS",
1925
- options: [
1926
- {
1927
- value: "payload",
1928
- label: "Payload",
1929
- hint: "TypeScript-first headless CMS with Next.js integration"
1930
- },
1931
- {
1932
- value: "sanity",
1933
- label: "Sanity",
1934
- hint: "Real-time collaborative CMS with schema-as-code"
1935
- },
1936
- {
1937
- value: "strapi",
1938
- label: "Strapi",
1939
- hint: "Open-source headless CMS with admin panel"
1940
- },
1941
- {
1942
- value: "tinacms",
1943
- label: "TinaCMS",
1944
- hint: "Git-backed headless CMS with visual editing"
1945
- },
1946
- {
1947
- value: "none",
1948
- label: "None",
1949
- hint: "Skip headless CMS setup"
1950
- }
1951
- ],
1952
- initialValue: "none"
2285
+ options: resolution.options,
2286
+ initialValue: resolution.initialValue
1953
2287
  });
1954
2288
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
1955
2289
  return response;
@@ -1979,17 +2313,39 @@ const CSS_FRAMEWORK_OPTIONS = {
1979
2313
  hint: "Plain CSS without preprocessors"
1980
2314
  }
1981
2315
  };
1982
- async function getCSSFrameworkChoice(cssFramework, uiLibrary) {
1983
- const compatibleFrameworks = getCompatibleCSSFrameworks$1(uiLibrary);
1984
- if (cssFramework !== void 0) return compatibleFrameworks.includes(cssFramework) ? cssFramework : compatibleFrameworks[0];
1985
- const selected = await navigableSelect({
1986
- message: "Select CSS framework",
2316
+ function resolveCSSFrameworkPrompt(context = {}) {
2317
+ const compatibleFrameworks = getCompatibleCSSFrameworks$1(context.uiLibrary);
2318
+ if (context.cssFramework !== void 0) return {
2319
+ shouldPrompt: false,
2320
+ mode: "single",
2321
+ options: compatibleFrameworks.map((fw) => ({
2322
+ value: fw,
2323
+ label: CSS_FRAMEWORK_OPTIONS[fw].label,
2324
+ hint: CSS_FRAMEWORK_OPTIONS[fw].hint
2325
+ })),
2326
+ autoValue: compatibleFrameworks.includes(context.cssFramework) ? context.cssFramework : compatibleFrameworks[0]
2327
+ };
2328
+ return {
2329
+ shouldPrompt: true,
2330
+ mode: "single",
1987
2331
  options: compatibleFrameworks.map((fw) => ({
1988
2332
  value: fw,
1989
2333
  label: CSS_FRAMEWORK_OPTIONS[fw].label,
1990
2334
  hint: CSS_FRAMEWORK_OPTIONS[fw].hint
1991
2335
  })),
1992
2336
  initialValue: compatibleFrameworks.includes("tailwind") ? "tailwind" : compatibleFrameworks[0]
2337
+ };
2338
+ }
2339
+ async function getCSSFrameworkChoice(cssFramework, uiLibrary) {
2340
+ const resolution = resolveCSSFrameworkPrompt({
2341
+ cssFramework,
2342
+ uiLibrary
2343
+ });
2344
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2345
+ const selected = await navigableSelect({
2346
+ message: "Select CSS framework",
2347
+ options: resolution.options,
2348
+ initialValue: resolution.initialValue
1993
2349
  });
1994
2350
  if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
1995
2351
  return selected;
@@ -1997,9 +2353,19 @@ async function getCSSFrameworkChoice(cssFramework, uiLibrary) {
1997
2353
 
1998
2354
  //#endregion
1999
2355
  //#region src/prompts/database.ts
2000
- async function getDatabaseChoice(database, backend, runtime) {
2001
- if (backend === "convex" || backend === "none") return "none";
2002
- if (database !== void 0) return database;
2356
+ function resolveDatabasePrompt(context = {}) {
2357
+ if (context.backend === "convex" || context.backend === "none") return {
2358
+ shouldPrompt: false,
2359
+ mode: "single",
2360
+ options: [],
2361
+ autoValue: "none"
2362
+ };
2363
+ if (context.database !== void 0) return {
2364
+ shouldPrompt: false,
2365
+ mode: "single",
2366
+ options: [],
2367
+ autoValue: context.database
2368
+ };
2003
2369
  const databaseOptions = [
2004
2370
  {
2005
2371
  value: "none",
@@ -2022,7 +2388,7 @@ async function getDatabaseChoice(database, backend, runtime) {
2022
2388
  hint: "popular open-source relational database system"
2023
2389
  }
2024
2390
  ];
2025
- if (runtime !== "workers") {
2391
+ if (context.runtime !== "workers") {
2026
2392
  databaseOptions.push({
2027
2393
  value: "mongodb",
2028
2394
  label: "MongoDB",
@@ -2039,10 +2405,24 @@ async function getDatabaseChoice(database, backend, runtime) {
2039
2405
  hint: "in-memory data store for caching, sessions, and real-time features"
2040
2406
  });
2041
2407
  }
2042
- const response = await navigableSelect({
2043
- message: "Select database",
2408
+ return {
2409
+ shouldPrompt: true,
2410
+ mode: "single",
2044
2411
  options: databaseOptions,
2045
2412
  initialValue: DEFAULT_CONFIG.database
2413
+ };
2414
+ }
2415
+ async function getDatabaseChoice(database, backend, runtime) {
2416
+ const resolution = resolveDatabasePrompt({
2417
+ database,
2418
+ backend,
2419
+ runtime
2420
+ });
2421
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2422
+ const response = await navigableSelect({
2423
+ message: "Select database",
2424
+ options: resolution.options,
2425
+ initialValue: resolution.initialValue
2046
2426
  });
2047
2427
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2048
2428
  return response;
@@ -2050,18 +2430,33 @@ async function getDatabaseChoice(database, backend, runtime) {
2050
2430
 
2051
2431
  //#endregion
2052
2432
  //#region src/prompts/database-setup.ts
2053
- async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
2054
- if (backend === "convex") return "none";
2055
- if (dbSetup !== void 0) return dbSetup;
2056
- if (databaseType === "none") return "none";
2433
+ function resolveDBSetupPrompt(context) {
2434
+ if (context.backend === "convex") return {
2435
+ shouldPrompt: false,
2436
+ mode: "single",
2437
+ options: [],
2438
+ autoValue: "none"
2439
+ };
2440
+ if (context.dbSetup !== void 0) return {
2441
+ shouldPrompt: false,
2442
+ mode: "single",
2443
+ options: [],
2444
+ autoValue: context.dbSetup
2445
+ };
2446
+ if (context.databaseType === "none") return {
2447
+ shouldPrompt: false,
2448
+ mode: "single",
2449
+ options: [],
2450
+ autoValue: "none"
2451
+ };
2057
2452
  let options = [];
2058
- if (databaseType === "sqlite") options = [
2453
+ if (context.databaseType === "sqlite") options = [
2059
2454
  {
2060
2455
  value: "turso",
2061
2456
  label: "Turso",
2062
2457
  hint: "SQLite for Production. Powered by libSQL"
2063
2458
  },
2064
- ...runtime === "workers" ? [{
2459
+ ...context.runtime === "workers" ? [{
2065
2460
  value: "d1",
2066
2461
  label: "Cloudflare D1",
2067
2462
  hint: "Cloudflare's managed, serverless database with SQLite's SQL semantics"
@@ -2072,7 +2467,7 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
2072
2467
  hint: "Manual setup"
2073
2468
  }
2074
2469
  ];
2075
- else if (databaseType === "postgres") options = [
2470
+ else if (context.databaseType === "postgres") options = [
2076
2471
  {
2077
2472
  value: "neon",
2078
2473
  label: "Neon Postgres",
@@ -2104,7 +2499,7 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
2104
2499
  hint: "Manual setup"
2105
2500
  }
2106
2501
  ];
2107
- else if (databaseType === "mysql") options = [
2502
+ else if (context.databaseType === "mysql") options = [
2108
2503
  {
2109
2504
  value: "planetscale",
2110
2505
  label: "PlanetScale",
@@ -2121,7 +2516,7 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
2121
2516
  hint: "Manual setup"
2122
2517
  }
2123
2518
  ];
2124
- else if (databaseType === "mongodb") options = [
2519
+ else if (context.databaseType === "mongodb") options = [
2125
2520
  {
2126
2521
  value: "mongodb-atlas",
2127
2522
  label: "MongoDB Atlas",
@@ -2138,7 +2533,7 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
2138
2533
  hint: "Manual setup"
2139
2534
  }
2140
2535
  ];
2141
- else if (databaseType === "redis") options = [
2536
+ else if (context.databaseType === "redis") options = [
2142
2537
  {
2143
2538
  value: "upstash",
2144
2539
  label: "Upstash",
@@ -2155,11 +2550,32 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
2155
2550
  hint: "Manual setup"
2156
2551
  }
2157
2552
  ];
2158
- else return "none";
2159
- const response = await navigableSelect({
2160
- message: `Select ${databaseType} setup option`,
2553
+ else return {
2554
+ shouldPrompt: false,
2555
+ mode: "single",
2556
+ options: [],
2557
+ autoValue: "none"
2558
+ };
2559
+ return {
2560
+ shouldPrompt: true,
2561
+ mode: "single",
2161
2562
  options,
2162
2563
  initialValue: "none"
2564
+ };
2565
+ }
2566
+ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
2567
+ const resolution = resolveDBSetupPrompt({
2568
+ databaseType,
2569
+ dbSetup,
2570
+ orm: _orm,
2571
+ backend,
2572
+ runtime
2573
+ });
2574
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2575
+ const response = await navigableSelect({
2576
+ message: `Select ${databaseType} setup option`,
2577
+ options: resolution.options,
2578
+ initialValue: resolution.initialValue
2163
2579
  });
2164
2580
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2165
2581
  return response;
@@ -2230,60 +2646,82 @@ async function getEffectChoice(effect) {
2230
2646
 
2231
2647
  //#endregion
2232
2648
  //#region src/prompts/email.ts
2649
+ const EMAIL_PROMPT_OPTIONS = [
2650
+ {
2651
+ value: "resend",
2652
+ label: "Resend",
2653
+ hint: "Email for developers. Includes React Email components."
2654
+ },
2655
+ {
2656
+ value: "react-email",
2657
+ label: "React Email",
2658
+ hint: "Build emails using React components (no sending service)."
2659
+ },
2660
+ {
2661
+ value: "nodemailer",
2662
+ label: "Nodemailer",
2663
+ hint: "Classic Node.js email sending library."
2664
+ },
2665
+ {
2666
+ value: "postmark",
2667
+ label: "Postmark",
2668
+ hint: "Transactional email service with high deliverability."
2669
+ },
2670
+ {
2671
+ value: "sendgrid",
2672
+ label: "SendGrid",
2673
+ hint: "Email delivery and marketing platform by Twilio."
2674
+ },
2675
+ {
2676
+ value: "aws-ses",
2677
+ label: "AWS SES",
2678
+ hint: "Amazon Simple Email Service for scalable email."
2679
+ },
2680
+ {
2681
+ value: "mailgun",
2682
+ label: "Mailgun",
2683
+ hint: "Email API service for sending and tracking emails."
2684
+ },
2685
+ {
2686
+ value: "plunk",
2687
+ label: "Plunk",
2688
+ hint: "Open-source email platform for developers."
2689
+ },
2690
+ {
2691
+ value: "none",
2692
+ label: "None",
2693
+ hint: "No email integration"
2694
+ }
2695
+ ];
2696
+ function resolveEmailPrompt(context = {}) {
2697
+ if (context.backend === "none" || context.backend === "convex") return {
2698
+ shouldPrompt: false,
2699
+ mode: "single",
2700
+ options: [],
2701
+ autoValue: "none"
2702
+ };
2703
+ return context.email !== void 0 ? {
2704
+ shouldPrompt: false,
2705
+ mode: "single",
2706
+ options: EMAIL_PROMPT_OPTIONS,
2707
+ autoValue: context.email
2708
+ } : {
2709
+ shouldPrompt: true,
2710
+ mode: "single",
2711
+ options: EMAIL_PROMPT_OPTIONS,
2712
+ initialValue: DEFAULT_CONFIG.email ?? "none"
2713
+ };
2714
+ }
2233
2715
  async function getEmailChoice(email, backend) {
2234
- if (email !== void 0) return email;
2235
- if (backend === "none") return "none";
2236
- if (backend === "convex") return "none";
2716
+ const resolution = resolveEmailPrompt({
2717
+ email,
2718
+ backend
2719
+ });
2720
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2237
2721
  const response = await navigableSelect({
2238
2722
  message: "Select email solution",
2239
- options: [
2240
- {
2241
- value: "resend",
2242
- label: "Resend",
2243
- hint: "Email for developers. Includes React Email components."
2244
- },
2245
- {
2246
- value: "react-email",
2247
- label: "React Email",
2248
- hint: "Build emails using React components (no sending service)."
2249
- },
2250
- {
2251
- value: "nodemailer",
2252
- label: "Nodemailer",
2253
- hint: "Classic Node.js email sending library."
2254
- },
2255
- {
2256
- value: "postmark",
2257
- label: "Postmark",
2258
- hint: "Transactional email service with high deliverability."
2259
- },
2260
- {
2261
- value: "sendgrid",
2262
- label: "SendGrid",
2263
- hint: "Email delivery and marketing platform by Twilio."
2264
- },
2265
- {
2266
- value: "aws-ses",
2267
- label: "AWS SES",
2268
- hint: "Amazon Simple Email Service for scalable email."
2269
- },
2270
- {
2271
- value: "mailgun",
2272
- label: "Mailgun",
2273
- hint: "Email API service for sending and tracking emails."
2274
- },
2275
- {
2276
- value: "plunk",
2277
- label: "Plunk",
2278
- hint: "Open-source email platform for developers."
2279
- },
2280
- {
2281
- value: "none",
2282
- label: "None",
2283
- hint: "No email integration"
2284
- }
2285
- ],
2286
- initialValue: DEFAULT_CONFIG.email ?? "none"
2723
+ options: resolution.options,
2724
+ initialValue: resolution.initialValue
2287
2725
  });
2288
2726
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2289
2727
  return response;
@@ -2349,34 +2787,57 @@ async function getFileStorageChoice(fileStorage, backend) {
2349
2787
 
2350
2788
  //#endregion
2351
2789
  //#region src/prompts/file-upload.ts
2790
+ const FILE_UPLOAD_PROMPT_OPTIONS = [
2791
+ {
2792
+ value: "uploadthing",
2793
+ label: "UploadThing",
2794
+ hint: "TypeScript-first file uploads with built-in validation"
2795
+ },
2796
+ {
2797
+ value: "filepond",
2798
+ label: "FilePond",
2799
+ hint: "Flexible file upload with image preview and drag & drop"
2800
+ },
2801
+ {
2802
+ value: "uppy",
2803
+ label: "Uppy",
2804
+ hint: "Modular file uploader with resumable uploads and plugins"
2805
+ },
2806
+ {
2807
+ value: "none",
2808
+ label: "None",
2809
+ hint: "Skip file upload integration"
2810
+ }
2811
+ ];
2812
+ function resolveFileUploadPrompt(context = {}) {
2813
+ if (context.backend === "none" || context.backend === "convex") return {
2814
+ shouldPrompt: false,
2815
+ mode: "single",
2816
+ options: [],
2817
+ autoValue: "none"
2818
+ };
2819
+ return context.fileUpload !== void 0 ? {
2820
+ shouldPrompt: false,
2821
+ mode: "single",
2822
+ options: FILE_UPLOAD_PROMPT_OPTIONS,
2823
+ autoValue: context.fileUpload
2824
+ } : {
2825
+ shouldPrompt: true,
2826
+ mode: "single",
2827
+ options: FILE_UPLOAD_PROMPT_OPTIONS,
2828
+ initialValue: "none"
2829
+ };
2830
+ }
2352
2831
  async function getFileUploadChoice(fileUpload, backend) {
2353
- if (fileUpload !== void 0) return fileUpload;
2354
- if (backend === "none" || backend === "convex") return "none";
2832
+ const resolution = resolveFileUploadPrompt({
2833
+ fileUpload,
2834
+ backend
2835
+ });
2836
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2355
2837
  const response = await navigableSelect({
2356
2838
  message: "Select file upload solution",
2357
- options: [
2358
- {
2359
- value: "uploadthing",
2360
- label: "UploadThing",
2361
- hint: "TypeScript-first file uploads with built-in validation"
2362
- },
2363
- {
2364
- value: "filepond",
2365
- label: "FilePond",
2366
- hint: "Flexible file upload with image preview and drag & drop"
2367
- },
2368
- {
2369
- value: "uppy",
2370
- label: "Uppy",
2371
- hint: "Modular file uploader with resumable uploads and plugins"
2372
- },
2373
- {
2374
- value: "none",
2375
- label: "None",
2376
- hint: "Skip file upload integration"
2377
- }
2378
- ],
2379
- initialValue: "none"
2839
+ options: resolution.options,
2840
+ initialValue: resolution.initialValue
2380
2841
  });
2381
2842
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2382
2843
  return response;
@@ -2384,10 +2845,20 @@ async function getFileUploadChoice(fileUpload, backend) {
2384
2845
 
2385
2846
  //#endregion
2386
2847
  //#region src/prompts/forms.ts
2387
- async function getFormsChoice(forms, frontends) {
2388
- if (forms !== void 0) return forms;
2389
- const { web } = splitFrontends$1(frontends);
2390
- if (web.length === 0) return "none";
2848
+ function resolveFormsPrompt(context = {}) {
2849
+ if (context.forms !== void 0) return {
2850
+ shouldPrompt: false,
2851
+ mode: "single",
2852
+ options: [],
2853
+ autoValue: context.forms
2854
+ };
2855
+ const { web } = splitFrontends$1(context.frontends);
2856
+ if (web.length === 0) return {
2857
+ shouldPrompt: false,
2858
+ mode: "single",
2859
+ options: [],
2860
+ autoValue: "none"
2861
+ };
2391
2862
  const isReact = web.some((f) => [
2392
2863
  "tanstack-router",
2393
2864
  "react-router",
@@ -2432,10 +2903,23 @@ async function getFormsChoice(forms, frontends) {
2432
2903
  label: "None",
2433
2904
  hint: "Build custom form handling"
2434
2905
  });
2906
+ return {
2907
+ shouldPrompt: true,
2908
+ mode: "single",
2909
+ options,
2910
+ initialValue: isReact ? "react-hook-form" : options.find((option) => option.value !== "none")?.value ?? "none"
2911
+ };
2912
+ }
2913
+ async function getFormsChoice(forms, frontends) {
2914
+ const resolution = resolveFormsPrompt({
2915
+ forms,
2916
+ frontends
2917
+ });
2918
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2435
2919
  const response = await navigableSelect({
2436
2920
  message: "Select form library",
2437
- options,
2438
- initialValue: isReact ? "react-hook-form" : "tanstack-form"
2921
+ options: resolution.options,
2922
+ initialValue: resolution.initialValue
2439
2923
  });
2440
2924
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2441
2925
  return response;
@@ -2443,8 +2927,119 @@ async function getFormsChoice(forms, frontends) {
2443
2927
 
2444
2928
  //#endregion
2445
2929
  //#region src/prompts/frontend.ts
2930
+ const WEB_FRONTEND_PROMPT_OPTIONS = [
2931
+ {
2932
+ value: "tanstack-router",
2933
+ label: "TanStack Router",
2934
+ hint: "Modern and scalable routing for React Applications"
2935
+ },
2936
+ {
2937
+ value: "react-router",
2938
+ label: "React Router",
2939
+ hint: "A user-obsessed, standards-focused, multi-strategy router"
2940
+ },
2941
+ {
2942
+ value: "react-vite",
2943
+ label: "React + Vite",
2944
+ hint: "Client-routed React SPA powered by Vite"
2945
+ },
2946
+ {
2947
+ value: "next",
2948
+ label: "Next.js",
2949
+ hint: "The React Framework for the Web"
2950
+ },
2951
+ {
2952
+ value: "nuxt",
2953
+ label: "Nuxt",
2954
+ hint: "The Progressive Web Framework for Vue.js"
2955
+ },
2956
+ {
2957
+ value: "svelte",
2958
+ label: "SvelteKit",
2959
+ hint: "Full-stack Svelte framework with SSR and server routes"
2960
+ },
2961
+ {
2962
+ value: "solid",
2963
+ label: "Solid",
2964
+ hint: "Simple and performant reactivity for building user interfaces"
2965
+ },
2966
+ {
2967
+ value: "solid-start",
2968
+ label: "SolidStart",
2969
+ hint: "Full-stack Solid framework with SSR and API routes"
2970
+ },
2971
+ {
2972
+ value: "astro",
2973
+ label: "Astro",
2974
+ hint: "Content-focused with Island Architecture"
2975
+ },
2976
+ {
2977
+ value: "tanstack-start",
2978
+ label: "TanStack Start",
2979
+ hint: "SSR, Server Functions, API Routes and more with TanStack Router"
2980
+ },
2981
+ {
2982
+ value: "qwik",
2983
+ label: "Qwik",
2984
+ hint: "Resumable framework with instant load times"
2985
+ },
2986
+ {
2987
+ value: "angular",
2988
+ label: "Angular",
2989
+ hint: "Enterprise-grade TypeScript framework by Google"
2990
+ },
2991
+ {
2992
+ value: "redwood",
2993
+ label: "RedwoodJS",
2994
+ hint: "Opinionated fullstack (React + GraphQL + Prisma)"
2995
+ },
2996
+ {
2997
+ value: "fresh",
2998
+ label: "Fresh",
2999
+ hint: "Deno-native framework with islands architecture"
3000
+ }
3001
+ ];
3002
+ const NATIVE_FRONTEND_PROMPT_OPTIONS = [
3003
+ {
3004
+ value: "native-bare",
3005
+ label: "Bare",
3006
+ hint: "Bare Expo without styling library"
3007
+ },
3008
+ {
3009
+ value: "native-uniwind",
3010
+ label: "Uniwind",
3011
+ hint: "Fastest Tailwind bindings for React Native with HeroUI Native"
3012
+ },
3013
+ {
3014
+ value: "native-unistyles",
3015
+ label: "Unistyles",
3016
+ hint: "Consistent styling for React Native"
3017
+ }
3018
+ ];
3019
+ function resolveFrontendPrompt(context = {}) {
3020
+ const options = [...WEB_FRONTEND_PROMPT_OPTIONS.filter((option) => isFrontendAllowedWithBackend$1(option.value, context.backend, context.auth)), ...NATIVE_FRONTEND_PROMPT_OPTIONS];
3021
+ return context.frontendOptions !== void 0 ? {
3022
+ shouldPrompt: false,
3023
+ mode: "multiple",
3024
+ options,
3025
+ autoValue: context.frontendOptions
3026
+ } : {
3027
+ shouldPrompt: true,
3028
+ mode: "multiple",
3029
+ options,
3030
+ initialValue: DEFAULT_CONFIG.frontend
3031
+ };
3032
+ }
2446
3033
  async function getFrontendChoice(frontendOptions, backend, auth) {
2447
- if (frontendOptions !== void 0) return frontendOptions;
3034
+ const resolution = resolveFrontendPrompt({
3035
+ frontendOptions,
3036
+ backend,
3037
+ auth
3038
+ });
3039
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? [];
3040
+ const allowedValues = new Set(resolution.options.map((option) => option.value));
3041
+ const initialValues = resolution.initialValue ?? DEFAULT_CONFIG.frontend;
3042
+ const initialTypes = [...initialValues.some((value) => WEB_FRONTEND_PROMPT_OPTIONS.some((option) => option.value === value)) ? ["web"] : [], ...initialValues.some((value) => NATIVE_FRONTEND_PROMPT_OPTIONS.some((option) => option.value === value)) ? ["native"] : []];
2448
3043
  while (true) {
2449
3044
  const wasFirstPrompt = isFirstPrompt();
2450
3045
  const frontendTypes = await navigableMultiselect({
@@ -2459,7 +3054,7 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
2459
3054
  hint: "Create a React Native/Expo app"
2460
3055
  }],
2461
3056
  required: false,
2462
- initialValues: ["web"]
3057
+ initialValues: initialTypes.length > 0 ? [...initialTypes] : ["web"]
2463
3058
  });
2464
3059
  if (isGoBack(frontendTypes)) return GO_BACK_SYMBOL;
2465
3060
  if (isCancel$1(frontendTypes)) return exitCancelled("Operation cancelled");
@@ -2467,81 +3062,12 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
2467
3062
  const result = [];
2468
3063
  let shouldRestart = false;
2469
3064
  if (frontendTypes.includes("web")) {
3065
+ const webOptions = WEB_FRONTEND_PROMPT_OPTIONS.filter((option) => allowedValues.has(option.value));
3066
+ const initialWebValue = initialValues.find((value) => webOptions.some((option) => option.value === value)) ?? webOptions[0]?.value;
2470
3067
  const webFramework = await navigableSelect({
2471
3068
  message: "Select web framework",
2472
- options: [
2473
- {
2474
- value: "tanstack-router",
2475
- label: "TanStack Router",
2476
- hint: "Modern and scalable routing for React Applications"
2477
- },
2478
- {
2479
- value: "react-router",
2480
- label: "React Router",
2481
- hint: "A user‑obsessed, standards‑focused, multi‑strategy router"
2482
- },
2483
- {
2484
- value: "react-vite",
2485
- label: "React + Vite",
2486
- hint: "Client-routed React SPA powered by Vite"
2487
- },
2488
- {
2489
- value: "next",
2490
- label: "Next.js",
2491
- hint: "The React Framework for the Web"
2492
- },
2493
- {
2494
- value: "nuxt",
2495
- label: "Nuxt",
2496
- hint: "The Progressive Web Framework for Vue.js"
2497
- },
2498
- {
2499
- value: "svelte",
2500
- label: "SvelteKit",
2501
- hint: "Full-stack Svelte framework with SSR and server routes"
2502
- },
2503
- {
2504
- value: "solid",
2505
- label: "Solid",
2506
- hint: "Simple and performant reactivity for building user interfaces"
2507
- },
2508
- {
2509
- value: "solid-start",
2510
- label: "SolidStart",
2511
- hint: "Full-stack Solid framework with SSR and API routes"
2512
- },
2513
- {
2514
- value: "astro",
2515
- label: "Astro",
2516
- hint: "Content-focused with Island Architecture"
2517
- },
2518
- {
2519
- value: "tanstack-start",
2520
- label: "TanStack Start",
2521
- hint: "SSR, Server Functions, API Routes and more with TanStack Router"
2522
- },
2523
- {
2524
- value: "qwik",
2525
- label: "Qwik",
2526
- hint: "Resumable framework with instant load times"
2527
- },
2528
- {
2529
- value: "angular",
2530
- label: "Angular",
2531
- hint: "Enterprise-grade TypeScript framework by Google"
2532
- },
2533
- {
2534
- value: "redwood",
2535
- label: "RedwoodJS",
2536
- hint: "Opinionated fullstack (React + GraphQL + Prisma)"
2537
- },
2538
- {
2539
- value: "fresh",
2540
- label: "Fresh",
2541
- hint: "Deno-native framework with islands architecture"
2542
- }
2543
- ].filter((option) => isFrontendAllowedWithBackend$1(option.value, backend, auth)),
2544
- initialValue: DEFAULT_CONFIG.frontend[0]
3069
+ options: webOptions,
3070
+ ...initialWebValue ? { initialValue: initialWebValue } : {}
2545
3071
  });
2546
3072
  if (isGoBack(webFramework)) shouldRestart = true;
2547
3073
  else if (isCancel$1(webFramework)) return exitCancelled("Operation cancelled");
@@ -2552,26 +3078,12 @@ async function getFrontendChoice(frontendOptions, backend, auth) {
2552
3078
  continue;
2553
3079
  }
2554
3080
  if (frontendTypes.includes("native")) {
3081
+ const nativeOptions = NATIVE_FRONTEND_PROMPT_OPTIONS.filter((option) => allowedValues.has(option.value));
3082
+ const initialNativeValue = initialValues.find((value) => nativeOptions.some((option) => option.value === value)) ?? nativeOptions[0]?.value;
2555
3083
  const nativeFramework = await navigableSelect({
2556
3084
  message: "Choose native",
2557
- options: [
2558
- {
2559
- value: "native-bare",
2560
- label: "Bare",
2561
- hint: "Bare Expo without styling library"
2562
- },
2563
- {
2564
- value: "native-uniwind",
2565
- label: "Uniwind",
2566
- hint: "Fastest Tailwind bindings for React Native with HeroUI Native"
2567
- },
2568
- {
2569
- value: "native-unistyles",
2570
- label: "Unistyles",
2571
- hint: "Consistent styling for React Native"
2572
- }
2573
- ],
2574
- initialValue: "native-bare"
3085
+ options: nativeOptions,
3086
+ ...initialNativeValue ? { initialValue: initialNativeValue } : {}
2575
3087
  });
2576
3088
  if (isGoBack(nativeFramework)) if (frontendTypes.includes("web")) shouldRestart = true;
2577
3089
  else {
@@ -2603,144 +3115,200 @@ async function getGitChoice(git) {
2603
3115
 
2604
3116
  //#endregion
2605
3117
  //#region src/prompts/go-ecosystem.ts
3118
+ const GO_WEB_FRAMEWORK_PROMPT_OPTIONS = [
3119
+ {
3120
+ value: "gin",
3121
+ label: "Gin",
3122
+ hint: "High-performance HTTP web framework with martini-like API"
3123
+ },
3124
+ {
3125
+ value: "echo",
3126
+ label: "Echo",
3127
+ hint: "High performance, minimalist Go web framework"
3128
+ },
3129
+ {
3130
+ value: "fiber",
3131
+ label: "Fiber",
3132
+ hint: "Express-inspired web framework built on Fasthttp"
3133
+ },
3134
+ {
3135
+ value: "chi",
3136
+ label: "Chi",
3137
+ hint: "Lightweight, zero-dependency router built on net/http"
3138
+ },
3139
+ {
3140
+ value: "none",
3141
+ label: "None",
3142
+ hint: "No web framework"
3143
+ }
3144
+ ];
3145
+ const GO_ORM_PROMPT_OPTIONS = [
3146
+ {
3147
+ value: "gorm",
3148
+ label: "GORM",
3149
+ hint: "The fantastic ORM library for Golang"
3150
+ },
3151
+ {
3152
+ value: "sqlc",
3153
+ label: "sqlc",
3154
+ hint: "Generate type-safe Go code from SQL"
3155
+ },
3156
+ {
3157
+ value: "ent",
3158
+ label: "Ent",
3159
+ hint: "Code-first ORM by Meta with graph traversal API, 15k+ stars"
3160
+ },
3161
+ {
3162
+ value: "none",
3163
+ label: "None",
3164
+ hint: "No ORM/database layer"
3165
+ }
3166
+ ];
3167
+ const GO_API_PROMPT_OPTIONS = [{
3168
+ value: "grpc-go",
3169
+ label: "gRPC-Go",
3170
+ hint: "The Go implementation of gRPC"
3171
+ }, {
3172
+ value: "none",
3173
+ label: "None",
3174
+ hint: "No API layer"
3175
+ }];
3176
+ const GO_CLI_PROMPT_OPTIONS = [
3177
+ {
3178
+ value: "cobra",
3179
+ label: "Cobra",
3180
+ hint: "Library for creating powerful modern CLI applications"
3181
+ },
3182
+ {
3183
+ value: "bubbletea",
3184
+ label: "Bubble Tea",
3185
+ hint: "Powerful TUI framework based on The Elm Architecture"
3186
+ },
3187
+ {
3188
+ value: "none",
3189
+ label: "None",
3190
+ hint: "No CLI tools"
3191
+ }
3192
+ ];
3193
+ const GO_LOGGING_PROMPT_OPTIONS = [
3194
+ {
3195
+ value: "zap",
3196
+ label: "Zap",
3197
+ hint: "Blazing fast, structured, leveled logging in Go"
3198
+ },
3199
+ {
3200
+ value: "zerolog",
3201
+ label: "Zerolog",
3202
+ hint: "Zero-allocation JSON logger, fastest in benchmarks"
3203
+ },
3204
+ {
3205
+ value: "slog",
3206
+ label: "slog",
3207
+ hint: "Go 1.21+ stdlib structured logging (no external dependency)"
3208
+ },
3209
+ {
3210
+ value: "none",
3211
+ label: "None",
3212
+ hint: "No logging library"
3213
+ }
3214
+ ];
3215
+ const GO_AUTH_PROMPT_OPTIONS = [
3216
+ {
3217
+ value: "casbin",
3218
+ label: "Casbin",
3219
+ hint: "Model-based authorization (ACL, RBAC, ABAC) via config files"
3220
+ },
3221
+ {
3222
+ value: "jwt",
3223
+ label: "golang-jwt",
3224
+ hint: "JWT token creation and validation with HMAC/RSA/ECDSA signing"
3225
+ },
3226
+ {
3227
+ value: "none",
3228
+ label: "None",
3229
+ hint: "No authentication library"
3230
+ }
3231
+ ];
3232
+ function resolveGoWebFrameworkPrompt(goWebFramework) {
3233
+ return createStaticSinglePromptResolution(GO_WEB_FRAMEWORK_PROMPT_OPTIONS, "gin", goWebFramework);
3234
+ }
2606
3235
  async function getGoWebFrameworkChoice(goWebFramework) {
2607
- if (goWebFramework !== void 0) return goWebFramework;
3236
+ const resolution = resolveGoWebFrameworkPrompt(goWebFramework);
3237
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2608
3238
  const response = await navigableSelect({
2609
3239
  message: "Select Go web framework",
2610
- options: [
2611
- {
2612
- value: "gin",
2613
- label: "Gin",
2614
- hint: "High-performance HTTP web framework with martini-like API"
2615
- },
2616
- {
2617
- value: "echo",
2618
- label: "Echo",
2619
- hint: "High performance, minimalist Go web framework"
2620
- },
2621
- {
2622
- value: "fiber",
2623
- label: "Fiber",
2624
- hint: "Express-inspired web framework built on Fasthttp"
2625
- },
2626
- {
2627
- value: "chi",
2628
- label: "Chi",
2629
- hint: "Lightweight, zero-dependency router built on net/http"
2630
- },
2631
- {
2632
- value: "none",
2633
- label: "None",
2634
- hint: "No web framework"
2635
- }
2636
- ],
2637
- initialValue: "gin"
3240
+ options: resolution.options,
3241
+ initialValue: resolution.initialValue
2638
3242
  });
2639
3243
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2640
3244
  return response;
2641
3245
  }
3246
+ function resolveGoOrmPrompt(goOrm) {
3247
+ return createStaticSinglePromptResolution(GO_ORM_PROMPT_OPTIONS, "gorm", goOrm);
3248
+ }
2642
3249
  async function getGoOrmChoice(goOrm) {
2643
- if (goOrm !== void 0) return goOrm;
3250
+ const resolution = resolveGoOrmPrompt(goOrm);
3251
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2644
3252
  const response = await navigableSelect({
2645
3253
  message: "Select Go ORM/database layer",
2646
- options: [
2647
- {
2648
- value: "gorm",
2649
- label: "GORM",
2650
- hint: "The fantastic ORM library for Golang"
2651
- },
2652
- {
2653
- value: "sqlc",
2654
- label: "sqlc",
2655
- hint: "Generate type-safe Go code from SQL"
2656
- },
2657
- {
2658
- value: "ent",
2659
- label: "Ent",
2660
- hint: "Code-first ORM by Meta with graph traversal API, 15k+ stars"
2661
- },
2662
- {
2663
- value: "none",
2664
- label: "None",
2665
- hint: "No ORM/database layer"
2666
- }
2667
- ],
2668
- initialValue: "gorm"
3254
+ options: resolution.options,
3255
+ initialValue: resolution.initialValue
2669
3256
  });
2670
3257
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2671
3258
  return response;
2672
3259
  }
3260
+ function resolveGoApiPrompt(goApi) {
3261
+ return createStaticSinglePromptResolution(GO_API_PROMPT_OPTIONS, "none", goApi);
3262
+ }
2673
3263
  async function getGoApiChoice(goApi) {
2674
- if (goApi !== void 0) return goApi;
3264
+ const resolution = resolveGoApiPrompt(goApi);
3265
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2675
3266
  const response = await navigableSelect({
2676
3267
  message: "Select Go API layer",
2677
- options: [{
2678
- value: "grpc-go",
2679
- label: "gRPC-Go",
2680
- hint: "The Go implementation of gRPC"
2681
- }, {
2682
- value: "none",
2683
- label: "None",
2684
- hint: "No API layer"
2685
- }],
2686
- initialValue: "none"
3268
+ options: resolution.options,
3269
+ initialValue: resolution.initialValue
2687
3270
  });
2688
3271
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2689
3272
  return response;
2690
3273
  }
3274
+ function resolveGoCliPrompt(goCli) {
3275
+ return createStaticSinglePromptResolution(GO_CLI_PROMPT_OPTIONS, "none", goCli);
3276
+ }
2691
3277
  async function getGoCliChoice(goCli) {
2692
- if (goCli !== void 0) return goCli;
3278
+ const resolution = resolveGoCliPrompt(goCli);
3279
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2693
3280
  const response = await navigableSelect({
2694
3281
  message: "Select Go CLI tools",
2695
- options: [
2696
- {
2697
- value: "cobra",
2698
- label: "Cobra",
2699
- hint: "Library for creating powerful modern CLI applications"
2700
- },
2701
- {
2702
- value: "bubbletea",
2703
- label: "Bubble Tea",
2704
- hint: "Powerful TUI framework based on The Elm Architecture"
2705
- },
2706
- {
2707
- value: "none",
2708
- label: "None",
2709
- hint: "No CLI tools"
2710
- }
2711
- ],
2712
- initialValue: "none"
3282
+ options: resolution.options,
3283
+ initialValue: resolution.initialValue
2713
3284
  });
2714
3285
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2715
3286
  return response;
2716
3287
  }
3288
+ function resolveGoLoggingPrompt(goLogging) {
3289
+ return createStaticSinglePromptResolution(GO_LOGGING_PROMPT_OPTIONS, "zap", goLogging);
3290
+ }
2717
3291
  async function getGoLoggingChoice(goLogging) {
2718
- if (goLogging !== void 0) return goLogging;
3292
+ const resolution = resolveGoLoggingPrompt(goLogging);
3293
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2719
3294
  const response = await navigableSelect({
2720
3295
  message: "Select Go logging library",
2721
- options: [
2722
- {
2723
- value: "zap",
2724
- label: "Zap",
2725
- hint: "Blazing fast, structured, leveled logging in Go"
2726
- },
2727
- {
2728
- value: "zerolog",
2729
- label: "Zerolog",
2730
- hint: "Zero-allocation JSON logger, fastest in benchmarks"
2731
- },
2732
- {
2733
- value: "slog",
2734
- label: "slog",
2735
- hint: "Go 1.21+ stdlib structured logging (no external dependency)"
2736
- },
2737
- {
2738
- value: "none",
2739
- label: "None",
2740
- hint: "No logging library"
2741
- }
2742
- ],
2743
- initialValue: "zap"
3296
+ options: resolution.options,
3297
+ initialValue: resolution.initialValue
3298
+ });
3299
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3300
+ return response;
3301
+ }
3302
+ function resolveGoAuthPrompt(goAuth) {
3303
+ return createStaticSinglePromptResolution(GO_AUTH_PROMPT_OPTIONS, "none", goAuth);
3304
+ }
3305
+ async function getGoAuthChoice(goAuth) {
3306
+ const resolution = resolveGoAuthPrompt(goAuth);
3307
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3308
+ const response = await navigableSelect({
3309
+ message: "Select Go authentication library",
3310
+ options: resolution.options,
3311
+ initialValue: resolution.initialValue
2744
3312
  });
2745
3313
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2746
3314
  return response;
@@ -2788,7 +3356,7 @@ async function commandExists(command) {
2788
3356
 
2789
3357
  //#endregion
2790
3358
  //#region src/prompts/install.ts
2791
- async function getinstallChoice(install, ecosystem) {
3359
+ async function getinstallChoice(install, ecosystem, javaBuildTool) {
2792
3360
  if (install !== void 0) return install;
2793
3361
  if (ecosystem === "rust") {
2794
3362
  if (!await commandExists("cargo")) {
@@ -2826,6 +3394,22 @@ async function getinstallChoice(install, ecosystem) {
2826
3394
  if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
2827
3395
  return response$1;
2828
3396
  }
3397
+ if (ecosystem === "java") {
3398
+ if (!await commandExists("java")) {
3399
+ log.warn("Java is not installed. Please install a JDK from https://adoptium.net/ or your preferred vendor.");
3400
+ return false;
3401
+ }
3402
+ if (javaBuildTool === "none") {
3403
+ log.warn("No Java build tool selected. Skipping Java install verification.");
3404
+ return false;
3405
+ }
3406
+ const response$1 = await navigableConfirm({
3407
+ message: `Run ${javaBuildTool === "gradle" ? "./gradlew test" : "./mvnw test"}?`,
3408
+ initialValue: DEFAULT_CONFIG.install
3409
+ });
3410
+ if (isCancel$1(response$1)) return exitCancelled("Operation cancelled");
3411
+ return response$1;
3412
+ }
2829
3413
  const response = await navigableConfirm({
2830
3414
  message: "Install dependencies?",
2831
3415
  initialValue: DEFAULT_CONFIG.install
@@ -2834,41 +3418,243 @@ async function getinstallChoice(install, ecosystem) {
2834
3418
  return response;
2835
3419
  }
2836
3420
 
3421
+ //#endregion
3422
+ //#region src/prompts/java-ecosystem.ts
3423
+ const JAVA_WEB_FRAMEWORK_PROMPT_OPTIONS = [{
3424
+ value: "spring-boot",
3425
+ label: "Spring Boot",
3426
+ hint: "Production-grade Java framework with embedded server and auto-configuration"
3427
+ }, {
3428
+ value: "none",
3429
+ label: "None",
3430
+ hint: "No Java web framework"
3431
+ }];
3432
+ const JAVA_BUILD_TOOL_PROMPT_OPTIONS = [
3433
+ {
3434
+ value: "maven",
3435
+ label: "Maven",
3436
+ hint: "Convention-based build tool with Maven Wrapper support"
3437
+ },
3438
+ {
3439
+ value: "gradle",
3440
+ label: "Gradle",
3441
+ hint: "Flexible build tool with Gradle Wrapper support"
3442
+ },
3443
+ {
3444
+ value: "none",
3445
+ label: "None",
3446
+ hint: "No Java build tool"
3447
+ }
3448
+ ];
3449
+ const JAVA_ORM_PROMPT_OPTIONS = [{
3450
+ value: "spring-data-jpa",
3451
+ label: "Spring Data JPA",
3452
+ hint: "Repository abstraction built on JPA/Hibernate"
3453
+ }, {
3454
+ value: "none",
3455
+ label: "None",
3456
+ hint: "No Java ORM/database layer"
3457
+ }];
3458
+ const JAVA_AUTH_PROMPT_OPTIONS = [{
3459
+ value: "spring-security",
3460
+ label: "Spring Security",
3461
+ hint: "Authentication and authorization for Spring applications"
3462
+ }, {
3463
+ value: "none",
3464
+ label: "None",
3465
+ hint: "No Java authentication library"
3466
+ }];
3467
+ const JAVA_TESTING_LIBRARY_PROMPT_OPTIONS = [
3468
+ {
3469
+ value: "junit5",
3470
+ label: "JUnit 5",
3471
+ hint: "Modern Java unit testing platform"
3472
+ },
3473
+ {
3474
+ value: "mockito",
3475
+ label: "Mockito",
3476
+ hint: "Mocking framework for isolated unit tests"
3477
+ },
3478
+ {
3479
+ value: "testcontainers",
3480
+ label: "Testcontainers",
3481
+ hint: "Disposable Docker-based integration tests"
3482
+ },
3483
+ {
3484
+ value: "none",
3485
+ label: "None",
3486
+ hint: "No extra testing libraries"
3487
+ }
3488
+ ];
3489
+ const JAVA_LIBRARY_PROMPT_OPTIONS = [
3490
+ {
3491
+ value: "spring-actuator",
3492
+ label: "Spring Actuator",
3493
+ hint: "Production health, metrics, and diagnostics endpoints"
3494
+ },
3495
+ {
3496
+ value: "spring-validation",
3497
+ label: "Spring Validation",
3498
+ hint: "Bean validation for request and entity constraints"
3499
+ },
3500
+ {
3501
+ value: "flyway",
3502
+ label: "Flyway",
3503
+ hint: "Versioned SQL database migrations for JPA-backed apps"
3504
+ },
3505
+ {
3506
+ value: "none",
3507
+ label: "None",
3508
+ hint: "No extra Java libraries"
3509
+ }
3510
+ ];
3511
+ function resolveJavaWebFrameworkPrompt(javaWebFramework) {
3512
+ return createStaticSinglePromptResolution(JAVA_WEB_FRAMEWORK_PROMPT_OPTIONS, "spring-boot", javaWebFramework);
3513
+ }
3514
+ async function getJavaWebFrameworkChoice(javaWebFramework) {
3515
+ const resolution = resolveJavaWebFrameworkPrompt(javaWebFramework);
3516
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3517
+ const response = await navigableSelect({
3518
+ message: "Select Java web framework",
3519
+ options: resolution.options,
3520
+ initialValue: resolution.initialValue
3521
+ });
3522
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3523
+ return response;
3524
+ }
3525
+ function resolveJavaBuildToolPrompt(javaBuildTool) {
3526
+ return createStaticSinglePromptResolution(JAVA_BUILD_TOOL_PROMPT_OPTIONS, "maven", javaBuildTool);
3527
+ }
3528
+ async function getJavaBuildToolChoice(javaBuildTool) {
3529
+ const resolution = resolveJavaBuildToolPrompt(javaBuildTool);
3530
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3531
+ const response = await navigableSelect({
3532
+ message: "Select Java build tool",
3533
+ options: resolution.options,
3534
+ initialValue: resolution.initialValue
3535
+ });
3536
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3537
+ return response;
3538
+ }
3539
+ function resolveJavaOrmPrompt(javaOrm) {
3540
+ return createStaticSinglePromptResolution(JAVA_ORM_PROMPT_OPTIONS, "none", javaOrm);
3541
+ }
3542
+ async function getJavaOrmChoice(javaOrm) {
3543
+ const resolution = resolveJavaOrmPrompt(javaOrm);
3544
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3545
+ const response = await navigableSelect({
3546
+ message: "Select Java ORM/database layer",
3547
+ options: resolution.options,
3548
+ initialValue: resolution.initialValue
3549
+ });
3550
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3551
+ return response;
3552
+ }
3553
+ function resolveJavaAuthPrompt(javaAuth) {
3554
+ return createStaticSinglePromptResolution(JAVA_AUTH_PROMPT_OPTIONS, "none", javaAuth);
3555
+ }
3556
+ async function getJavaAuthChoice(javaAuth) {
3557
+ const resolution = resolveJavaAuthPrompt(javaAuth);
3558
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3559
+ const response = await navigableSelect({
3560
+ message: "Select Java authentication library",
3561
+ options: resolution.options,
3562
+ initialValue: resolution.initialValue
3563
+ });
3564
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3565
+ return response;
3566
+ }
3567
+ function resolveJavaLibrariesPrompt(javaLibraries) {
3568
+ return createStaticMultiPromptResolution(JAVA_LIBRARY_PROMPT_OPTIONS, [], javaLibraries);
3569
+ }
3570
+ async function getJavaLibrariesChoice(javaLibraries) {
3571
+ const resolution = resolveJavaLibrariesPrompt(javaLibraries);
3572
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? [];
3573
+ const response = await navigableMultiselect({
3574
+ message: "Select Java application libraries",
3575
+ options: resolution.options,
3576
+ required: false,
3577
+ initialValues: resolution.initialValue
3578
+ });
3579
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3580
+ if (response.includes("none")) return [];
3581
+ return response;
3582
+ }
3583
+ function resolveJavaTestingLibrariesPrompt(javaTestingLibraries) {
3584
+ return createStaticMultiPromptResolution(JAVA_TESTING_LIBRARY_PROMPT_OPTIONS, ["junit5"], javaTestingLibraries);
3585
+ }
3586
+ async function getJavaTestingLibrariesChoice(javaTestingLibraries) {
3587
+ const resolution = resolveJavaTestingLibrariesPrompt(javaTestingLibraries);
3588
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? [];
3589
+ const response = await navigableMultiselect({
3590
+ message: "Select Java testing libraries",
3591
+ options: resolution.options,
3592
+ required: false,
3593
+ initialValues: resolution.initialValue
3594
+ });
3595
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3596
+ if (response.includes("none")) return [];
3597
+ return response;
3598
+ }
3599
+
2837
3600
  //#endregion
2838
3601
  //#region src/prompts/job-queue.ts
3602
+ const JOB_QUEUE_PROMPT_OPTIONS = [
3603
+ {
3604
+ value: "bullmq",
3605
+ label: "BullMQ",
3606
+ hint: "Redis-backed job queue for background tasks and scheduling"
3607
+ },
3608
+ {
3609
+ value: "trigger-dev",
3610
+ label: "Trigger.dev",
3611
+ hint: "Background jobs as code with serverless execution"
3612
+ },
3613
+ {
3614
+ value: "inngest",
3615
+ label: "Inngest",
3616
+ hint: "Event-driven functions with built-in queuing and scheduling"
3617
+ },
3618
+ {
3619
+ value: "temporal",
3620
+ label: "Temporal",
3621
+ hint: "Durable workflow orchestration for reliable distributed systems"
3622
+ },
3623
+ {
3624
+ value: "none",
3625
+ label: "None",
3626
+ hint: "Skip job queue/background worker setup"
3627
+ }
3628
+ ];
3629
+ function resolveJobQueuePrompt(context = {}) {
3630
+ if (context.backend === "none" || context.backend === "convex") return {
3631
+ shouldPrompt: false,
3632
+ mode: "single",
3633
+ options: [],
3634
+ autoValue: "none"
3635
+ };
3636
+ return context.jobQueue !== void 0 ? {
3637
+ shouldPrompt: false,
3638
+ mode: "single",
3639
+ options: JOB_QUEUE_PROMPT_OPTIONS,
3640
+ autoValue: context.jobQueue
3641
+ } : {
3642
+ shouldPrompt: true,
3643
+ mode: "single",
3644
+ options: JOB_QUEUE_PROMPT_OPTIONS,
3645
+ initialValue: "none"
3646
+ };
3647
+ }
2839
3648
  async function getJobQueueChoice(jobQueue, backend) {
2840
- if (jobQueue !== void 0) return jobQueue;
2841
- if (backend === "none" || backend === "convex") return "none";
3649
+ const resolution = resolveJobQueuePrompt({
3650
+ jobQueue,
3651
+ backend
3652
+ });
3653
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2842
3654
  const response = await navigableSelect({
2843
3655
  message: "Select job queue solution",
2844
- options: [
2845
- {
2846
- value: "bullmq",
2847
- label: "BullMQ",
2848
- hint: "Redis-backed job queue for background tasks and scheduling"
2849
- },
2850
- {
2851
- value: "trigger-dev",
2852
- label: "Trigger.dev",
2853
- hint: "Background jobs as code with serverless execution"
2854
- },
2855
- {
2856
- value: "inngest",
2857
- label: "Inngest",
2858
- hint: "Event-driven functions with built-in queuing and scheduling"
2859
- },
2860
- {
2861
- value: "temporal",
2862
- label: "Temporal",
2863
- hint: "Durable workflow orchestration for reliable distributed systems"
2864
- },
2865
- {
2866
- value: "none",
2867
- label: "None",
2868
- hint: "Skip job queue/background worker setup"
2869
- }
2870
- ],
2871
- initialValue: "none"
3656
+ options: resolution.options,
3657
+ initialValue: resolution.initialValue
2872
3658
  });
2873
3659
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2874
3660
  return response;
@@ -2876,29 +3662,52 @@ async function getJobQueueChoice(jobQueue, backend) {
2876
3662
 
2877
3663
  //#endregion
2878
3664
  //#region src/prompts/logging.ts
3665
+ const LOGGING_PROMPT_OPTIONS = [
3666
+ {
3667
+ value: "pino",
3668
+ label: "Pino",
3669
+ hint: "Fast JSON logger with minimal overhead"
3670
+ },
3671
+ {
3672
+ value: "winston",
3673
+ label: "Winston",
3674
+ hint: "Flexible logging library with multiple transports"
3675
+ },
3676
+ {
3677
+ value: "none",
3678
+ label: "None",
3679
+ hint: "Skip logging framework setup"
3680
+ }
3681
+ ];
3682
+ function resolveLoggingPrompt(context = {}) {
3683
+ if (context.backend === "none" || context.backend === "convex") return {
3684
+ shouldPrompt: false,
3685
+ mode: "single",
3686
+ options: [],
3687
+ autoValue: "none"
3688
+ };
3689
+ return context.logging !== void 0 ? {
3690
+ shouldPrompt: false,
3691
+ mode: "single",
3692
+ options: LOGGING_PROMPT_OPTIONS,
3693
+ autoValue: context.logging
3694
+ } : {
3695
+ shouldPrompt: true,
3696
+ mode: "single",
3697
+ options: LOGGING_PROMPT_OPTIONS,
3698
+ initialValue: "none"
3699
+ };
3700
+ }
2879
3701
  async function getLoggingChoice(logging, backend) {
2880
- if (logging !== void 0) return logging;
2881
- if (backend === "none" || backend === "convex") return "none";
3702
+ const resolution = resolveLoggingPrompt({
3703
+ logging,
3704
+ backend
3705
+ });
3706
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2882
3707
  const response = await navigableSelect({
2883
3708
  message: "Select logging framework",
2884
- options: [
2885
- {
2886
- value: "pino",
2887
- label: "Pino",
2888
- hint: "Fast JSON logger with minimal overhead"
2889
- },
2890
- {
2891
- value: "winston",
2892
- label: "Winston",
2893
- hint: "Flexible logging library with multiple transports"
2894
- },
2895
- {
2896
- value: "none",
2897
- label: "None",
2898
- hint: "Skip logging framework setup"
2899
- }
2900
- ],
2901
- initialValue: "none"
3709
+ options: resolution.options,
3710
+ initialValue: resolution.initialValue
2902
3711
  });
2903
3712
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2904
3713
  return response;
@@ -2963,34 +3772,57 @@ async function navigableGroup(prompts, opts) {
2963
3772
 
2964
3773
  //#endregion
2965
3774
  //#region src/prompts/observability.ts
3775
+ const OBSERVABILITY_PROMPT_OPTIONS = [
3776
+ {
3777
+ value: "opentelemetry",
3778
+ label: "OpenTelemetry",
3779
+ hint: "Observability framework for traces, metrics, and logs"
3780
+ },
3781
+ {
3782
+ value: "sentry",
3783
+ label: "Sentry",
3784
+ hint: "Error tracking and performance monitoring"
3785
+ },
3786
+ {
3787
+ value: "grafana",
3788
+ label: "Grafana",
3789
+ hint: "Prometheus metrics for Grafana dashboards and alerting"
3790
+ },
3791
+ {
3792
+ value: "none",
3793
+ label: "None",
3794
+ hint: "Skip observability/tracing setup"
3795
+ }
3796
+ ];
3797
+ function resolveObservabilityPrompt(context = {}) {
3798
+ if (context.backend === "none" || context.backend === "convex") return {
3799
+ shouldPrompt: false,
3800
+ mode: "single",
3801
+ options: [],
3802
+ autoValue: "none"
3803
+ };
3804
+ return context.observability !== void 0 ? {
3805
+ shouldPrompt: false,
3806
+ mode: "single",
3807
+ options: OBSERVABILITY_PROMPT_OPTIONS,
3808
+ autoValue: context.observability
3809
+ } : {
3810
+ shouldPrompt: true,
3811
+ mode: "single",
3812
+ options: OBSERVABILITY_PROMPT_OPTIONS,
3813
+ initialValue: "none"
3814
+ };
3815
+ }
2966
3816
  async function getObservabilityChoice(observability, backend) {
2967
- if (observability !== void 0) return observability;
2968
- if (backend === "none" || backend === "convex") return "none";
3817
+ const resolution = resolveObservabilityPrompt({
3818
+ observability,
3819
+ backend
3820
+ });
3821
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2969
3822
  const response = await navigableSelect({
2970
3823
  message: "Select observability solution",
2971
- options: [
2972
- {
2973
- value: "opentelemetry",
2974
- label: "OpenTelemetry",
2975
- hint: "Observability framework for traces, metrics, and logs"
2976
- },
2977
- {
2978
- value: "sentry",
2979
- label: "Sentry",
2980
- hint: "Error tracking and performance monitoring"
2981
- },
2982
- {
2983
- value: "grafana",
2984
- label: "Grafana",
2985
- hint: "Prometheus metrics for Grafana dashboards and alerting"
2986
- },
2987
- {
2988
- value: "none",
2989
- label: "None",
2990
- hint: "Skip observability/tracing setup"
2991
- }
2992
- ],
2993
- initialValue: "none"
3824
+ options: resolution.options,
3825
+ initialValue: resolution.initialValue
2994
3826
  });
2995
3827
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
2996
3828
  return response;
@@ -3035,15 +3867,29 @@ const ormOptions = {
3035
3867
  hint: "Mature ORM with wide adoption"
3036
3868
  }
3037
3869
  };
3038
- async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
3039
- if (backend === "convex") return "none";
3040
- if (!hasDatabase) return "none";
3041
- if (database === "edgedb") return "none";
3042
- if (database === "redis") return "none";
3043
- if (orm !== void 0) return orm;
3044
- const response = await navigableSelect({
3045
- message: "Select ORM",
3046
- options: database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [
3870
+ function resolveORMPrompt(context) {
3871
+ if (context.backend === "convex" || !context.hasDatabase) return {
3872
+ shouldPrompt: false,
3873
+ mode: "single",
3874
+ options: [],
3875
+ autoValue: "none"
3876
+ };
3877
+ if (context.database === "edgedb" || context.database === "redis") return {
3878
+ shouldPrompt: false,
3879
+ mode: "single",
3880
+ options: [],
3881
+ autoValue: "none"
3882
+ };
3883
+ if (context.orm !== void 0) return {
3884
+ shouldPrompt: false,
3885
+ mode: "single",
3886
+ options: [],
3887
+ autoValue: context.orm
3888
+ };
3889
+ return {
3890
+ shouldPrompt: true,
3891
+ mode: "single",
3892
+ options: context.database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [
3047
3893
  ormOptions.drizzle,
3048
3894
  ormOptions.prisma,
3049
3895
  ormOptions.typeorm,
@@ -3051,7 +3897,22 @@ async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
3051
3897
  ormOptions.mikroorm,
3052
3898
  ormOptions.sequelize
3053
3899
  ],
3054
- initialValue: database === "mongodb" ? "prisma" : runtime === "workers" ? "drizzle" : DEFAULT_CONFIG.orm
3900
+ initialValue: context.database === "mongodb" ? "prisma" : context.runtime === "workers" ? "drizzle" : DEFAULT_CONFIG.orm
3901
+ };
3902
+ }
3903
+ async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
3904
+ const resolution = resolveORMPrompt({
3905
+ orm,
3906
+ hasDatabase,
3907
+ database,
3908
+ backend,
3909
+ runtime
3910
+ });
3911
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3912
+ const response = await navigableSelect({
3913
+ message: "Select ORM",
3914
+ options: resolution.options,
3915
+ initialValue: resolution.initialValue
3055
3916
  });
3056
3917
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3057
3918
  return response;
@@ -3093,10 +3954,20 @@ async function getPackageManagerChoice(packageManager) {
3093
3954
 
3094
3955
  //#endregion
3095
3956
  //#region src/prompts/payments.ts
3096
- async function getPaymentsChoice(payments, auth, backend, frontends) {
3097
- if (payments !== void 0) return payments;
3098
- if (backend === "none") return "none";
3099
- const isPolarCompatible = auth === "better-auth" && (frontends?.length === 0 || splitFrontends$1(frontends).web.length > 0);
3957
+ function resolvePaymentsPrompt(context = {}) {
3958
+ if (context.payments !== void 0) return {
3959
+ shouldPrompt: false,
3960
+ mode: "single",
3961
+ options: [],
3962
+ autoValue: context.payments
3963
+ };
3964
+ if (context.backend === "none") return {
3965
+ shouldPrompt: false,
3966
+ mode: "single",
3967
+ options: [],
3968
+ autoValue: "none"
3969
+ };
3970
+ const isPolarCompatible = context.auth === "better-auth" && (context.frontends?.length === 0 || splitFrontends$1(context.frontends).web.length > 0);
3100
3971
  const options = [];
3101
3972
  if (isPolarCompatible) options.push({
3102
3973
  value: "polar",
@@ -3124,221 +3995,281 @@ async function getPaymentsChoice(payments, auth, backend, frontends) {
3124
3995
  label: "None",
3125
3996
  hint: "No payments integration"
3126
3997
  });
3127
- const response = await navigableSelect({
3128
- message: "Select payments provider",
3998
+ return {
3999
+ shouldPrompt: true,
4000
+ mode: "single",
3129
4001
  options,
3130
4002
  initialValue: DEFAULT_CONFIG.payments
3131
- });
3132
- if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3133
- return response;
4003
+ };
4004
+ }
4005
+ async function getPaymentsChoice(payments, auth, backend, frontends) {
4006
+ const resolution = resolvePaymentsPrompt({
4007
+ payments,
4008
+ auth,
4009
+ backend,
4010
+ frontends
4011
+ });
4012
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
4013
+ const response = await navigableSelect({
4014
+ message: "Select payments provider",
4015
+ options: resolution.options,
4016
+ initialValue: resolution.initialValue
4017
+ });
4018
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
4019
+ return response;
3134
4020
  }
3135
4021
 
3136
4022
  //#endregion
3137
4023
  //#region src/prompts/python-ecosystem.ts
4024
+ const PYTHON_WEB_FRAMEWORK_PROMPT_OPTIONS = [
4025
+ {
4026
+ value: "fastapi",
4027
+ label: "FastAPI",
4028
+ hint: "Modern, fast (high-performance) web framework for building APIs"
4029
+ },
4030
+ {
4031
+ value: "django",
4032
+ label: "Django",
4033
+ hint: "High-level Python web framework with batteries included"
4034
+ },
4035
+ {
4036
+ value: "flask",
4037
+ label: "Flask",
4038
+ hint: "Lightweight WSGI web framework with minimal boilerplate"
4039
+ },
4040
+ {
4041
+ value: "litestar",
4042
+ label: "Litestar",
4043
+ hint: "High-performance ASGI framework with class-based controllers"
4044
+ },
4045
+ {
4046
+ value: "none",
4047
+ label: "None",
4048
+ hint: "No web framework"
4049
+ }
4050
+ ];
4051
+ const PYTHON_ORM_PROMPT_OPTIONS = [
4052
+ {
4053
+ value: "sqlalchemy",
4054
+ label: "SQLAlchemy",
4055
+ hint: "The SQL toolkit and ORM for Python"
4056
+ },
4057
+ {
4058
+ value: "sqlmodel",
4059
+ label: "SQLModel",
4060
+ hint: "SQL databases in Python with Pydantic and SQLAlchemy"
4061
+ },
4062
+ {
4063
+ value: "tortoise-orm",
4064
+ label: "Tortoise ORM",
4065
+ hint: "Async-first ORM with Django-like API"
4066
+ },
4067
+ {
4068
+ value: "none",
4069
+ label: "None",
4070
+ hint: "No ORM/database layer"
4071
+ }
4072
+ ];
4073
+ const PYTHON_VALIDATION_PROMPT_OPTIONS = [{
4074
+ value: "pydantic",
4075
+ label: "Pydantic",
4076
+ hint: "Data validation using Python type hints"
4077
+ }, {
4078
+ value: "none",
4079
+ label: "None",
4080
+ hint: "No validation library"
4081
+ }];
4082
+ const PYTHON_AI_PROMPT_OPTIONS = [
4083
+ {
4084
+ value: "none",
4085
+ label: "None",
4086
+ hint: "No AI/ML framework"
4087
+ },
4088
+ {
4089
+ value: "langchain",
4090
+ label: "LangChain",
4091
+ hint: "Building applications with LLMs through composability"
4092
+ },
4093
+ {
4094
+ value: "llamaindex",
4095
+ label: "LlamaIndex",
4096
+ hint: "Data framework for LLM applications"
4097
+ },
4098
+ {
4099
+ value: "openai-sdk",
4100
+ label: "OpenAI SDK",
4101
+ hint: "Official OpenAI Python client"
4102
+ },
4103
+ {
4104
+ value: "anthropic-sdk",
4105
+ label: "Anthropic SDK",
4106
+ hint: "Official Anthropic Claude API client"
4107
+ },
4108
+ {
4109
+ value: "langgraph",
4110
+ label: "LangGraph",
4111
+ hint: "Graph-based agent orchestration"
4112
+ },
4113
+ {
4114
+ value: "crewai",
4115
+ label: "CrewAI",
4116
+ hint: "Multi-agent orchestration framework"
4117
+ }
4118
+ ];
4119
+ const PYTHON_AUTH_PROMPT_OPTIONS = [
4120
+ {
4121
+ value: "authlib",
4122
+ label: "Authlib",
4123
+ hint: "Comprehensive auth library — OAuth1/2, OIDC, JWS, JWK, JWT"
4124
+ },
4125
+ {
4126
+ value: "jwt",
4127
+ label: "JWT (python-jose)",
4128
+ hint: "Simple JWT token creation and verification"
4129
+ },
4130
+ {
4131
+ value: "none",
4132
+ label: "None",
4133
+ hint: "No authentication library"
4134
+ }
4135
+ ];
4136
+ const PYTHON_TASK_QUEUE_PROMPT_OPTIONS = [{
4137
+ value: "celery",
4138
+ label: "Celery",
4139
+ hint: "Distributed task queue for Python"
4140
+ }, {
4141
+ value: "none",
4142
+ label: "None",
4143
+ hint: "No task queue"
4144
+ }];
4145
+ const PYTHON_GRAPHQL_PROMPT_OPTIONS = [{
4146
+ value: "strawberry",
4147
+ label: "Strawberry",
4148
+ hint: "Python GraphQL library using dataclasses and type hints"
4149
+ }, {
4150
+ value: "none",
4151
+ label: "None",
4152
+ hint: "No GraphQL framework"
4153
+ }];
4154
+ const PYTHON_QUALITY_PROMPT_OPTIONS = [{
4155
+ value: "ruff",
4156
+ label: "Ruff",
4157
+ hint: "An extremely fast Python linter and formatter"
4158
+ }, {
4159
+ value: "none",
4160
+ label: "None",
4161
+ hint: "No code quality tools"
4162
+ }];
4163
+ function resolvePythonWebFrameworkPrompt(pythonWebFramework) {
4164
+ return createStaticSinglePromptResolution(PYTHON_WEB_FRAMEWORK_PROMPT_OPTIONS, "fastapi", pythonWebFramework);
4165
+ }
3138
4166
  async function getPythonWebFrameworkChoice(pythonWebFramework) {
3139
- if (pythonWebFramework !== void 0) return pythonWebFramework;
4167
+ const resolution = resolvePythonWebFrameworkPrompt(pythonWebFramework);
4168
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3140
4169
  const response = await navigableSelect({
3141
4170
  message: "Select Python web framework",
3142
- options: [
3143
- {
3144
- value: "fastapi",
3145
- label: "FastAPI",
3146
- hint: "Modern, fast (high-performance) web framework for building APIs"
3147
- },
3148
- {
3149
- value: "django",
3150
- label: "Django",
3151
- hint: "High-level Python web framework with batteries included"
3152
- },
3153
- {
3154
- value: "flask",
3155
- label: "Flask",
3156
- hint: "Lightweight WSGI web framework with minimal boilerplate"
3157
- },
3158
- {
3159
- value: "litestar",
3160
- label: "Litestar",
3161
- hint: "High-performance ASGI framework with class-based controllers"
3162
- },
3163
- {
3164
- value: "none",
3165
- label: "None",
3166
- hint: "No web framework"
3167
- }
3168
- ],
3169
- initialValue: "fastapi"
4171
+ options: resolution.options,
4172
+ initialValue: resolution.initialValue
3170
4173
  });
3171
4174
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3172
4175
  return response;
3173
4176
  }
4177
+ function resolvePythonOrmPrompt(pythonOrm) {
4178
+ return createStaticSinglePromptResolution(PYTHON_ORM_PROMPT_OPTIONS, "sqlalchemy", pythonOrm);
4179
+ }
3174
4180
  async function getPythonOrmChoice(pythonOrm) {
3175
- if (pythonOrm !== void 0) return pythonOrm;
4181
+ const resolution = resolvePythonOrmPrompt(pythonOrm);
4182
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3176
4183
  const response = await navigableSelect({
3177
4184
  message: "Select Python ORM/database layer",
3178
- options: [
3179
- {
3180
- value: "sqlalchemy",
3181
- label: "SQLAlchemy",
3182
- hint: "The SQL toolkit and ORM for Python"
3183
- },
3184
- {
3185
- value: "sqlmodel",
3186
- label: "SQLModel",
3187
- hint: "SQL databases in Python with Pydantic and SQLAlchemy"
3188
- },
3189
- {
3190
- value: "none",
3191
- label: "None",
3192
- hint: "No ORM/database layer"
3193
- }
3194
- ],
3195
- initialValue: "sqlalchemy"
4185
+ options: resolution.options,
4186
+ initialValue: resolution.initialValue
3196
4187
  });
3197
4188
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3198
4189
  return response;
3199
4190
  }
4191
+ function resolvePythonValidationPrompt(pythonValidation) {
4192
+ return createStaticSinglePromptResolution(PYTHON_VALIDATION_PROMPT_OPTIONS, "pydantic", pythonValidation);
4193
+ }
3200
4194
  async function getPythonValidationChoice(pythonValidation) {
3201
- if (pythonValidation !== void 0) return pythonValidation;
4195
+ const resolution = resolvePythonValidationPrompt(pythonValidation);
4196
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3202
4197
  const response = await navigableSelect({
3203
4198
  message: "Select Python validation library",
3204
- options: [{
3205
- value: "pydantic",
3206
- label: "Pydantic",
3207
- hint: "Data validation using Python type hints"
3208
- }, {
3209
- value: "none",
3210
- label: "None",
3211
- hint: "No validation library"
3212
- }],
3213
- initialValue: "pydantic"
4199
+ options: resolution.options,
4200
+ initialValue: resolution.initialValue
3214
4201
  });
3215
4202
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3216
4203
  return response;
3217
4204
  }
4205
+ function resolvePythonAiPrompt(pythonAi) {
4206
+ return createStaticMultiPromptResolution(PYTHON_AI_PROMPT_OPTIONS, [], pythonAi);
4207
+ }
3218
4208
  async function getPythonAiChoice(pythonAi) {
3219
- if (pythonAi !== void 0) return pythonAi;
4209
+ const resolution = resolvePythonAiPrompt(pythonAi);
4210
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? [];
3220
4211
  const response = await navigableMultiselect({
3221
4212
  message: "Select Python AI/ML frameworks",
3222
- options: [
3223
- {
3224
- value: "none",
3225
- label: "None",
3226
- hint: "No AI/ML framework"
3227
- },
3228
- {
3229
- value: "langchain",
3230
- label: "LangChain",
3231
- hint: "Building applications with LLMs through composability"
3232
- },
3233
- {
3234
- value: "llamaindex",
3235
- label: "LlamaIndex",
3236
- hint: "Data framework for LLM applications"
3237
- },
3238
- {
3239
- value: "openai-sdk",
3240
- label: "OpenAI SDK",
3241
- hint: "Official OpenAI Python client"
3242
- },
3243
- {
3244
- value: "anthropic-sdk",
3245
- label: "Anthropic SDK",
3246
- hint: "Official Anthropic Claude API client"
3247
- },
3248
- {
3249
- value: "langgraph",
3250
- label: "LangGraph",
3251
- hint: "Graph-based agent orchestration"
3252
- },
3253
- {
3254
- value: "crewai",
3255
- label: "CrewAI",
3256
- hint: "Multi-agent orchestration framework"
3257
- }
3258
- ],
4213
+ options: resolution.options,
3259
4214
  required: false,
3260
- initialValues: []
4215
+ initialValues: resolution.initialValue
3261
4216
  });
3262
4217
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3263
4218
  if (response.includes("none")) return [];
3264
4219
  return response;
3265
4220
  }
4221
+ function resolvePythonAuthPrompt(pythonAuth) {
4222
+ return createStaticSinglePromptResolution(PYTHON_AUTH_PROMPT_OPTIONS, "none", pythonAuth);
4223
+ }
3266
4224
  async function getPythonAuthChoice(pythonAuth) {
3267
- if (pythonAuth !== void 0) return pythonAuth;
4225
+ const resolution = resolvePythonAuthPrompt(pythonAuth);
4226
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3268
4227
  const response = await navigableSelect({
3269
4228
  message: "Select Python authentication library",
3270
- options: [
3271
- {
3272
- value: "authlib",
3273
- label: "Authlib",
3274
- hint: "Comprehensive auth library — OAuth1/2, OIDC, JWS, JWK, JWT"
3275
- },
3276
- {
3277
- value: "jwt",
3278
- label: "JWT (python-jose)",
3279
- hint: "Simple JWT token creation and verification"
3280
- },
3281
- {
3282
- value: "none",
3283
- label: "None",
3284
- hint: "No authentication library"
3285
- }
3286
- ],
3287
- initialValue: "none"
4229
+ options: resolution.options,
4230
+ initialValue: resolution.initialValue
3288
4231
  });
3289
4232
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3290
4233
  return response;
3291
4234
  }
4235
+ function resolvePythonTaskQueuePrompt(pythonTaskQueue) {
4236
+ return createStaticSinglePromptResolution(PYTHON_TASK_QUEUE_PROMPT_OPTIONS, "none", pythonTaskQueue);
4237
+ }
3292
4238
  async function getPythonTaskQueueChoice(pythonTaskQueue) {
3293
- if (pythonTaskQueue !== void 0) return pythonTaskQueue;
4239
+ const resolution = resolvePythonTaskQueuePrompt(pythonTaskQueue);
4240
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3294
4241
  const response = await navigableSelect({
3295
4242
  message: "Select Python task queue",
3296
- options: [{
3297
- value: "celery",
3298
- label: "Celery",
3299
- hint: "Distributed task queue for Python"
3300
- }, {
3301
- value: "none",
3302
- label: "None",
3303
- hint: "No task queue"
3304
- }],
3305
- initialValue: "none"
4243
+ options: resolution.options,
4244
+ initialValue: resolution.initialValue
3306
4245
  });
3307
4246
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3308
4247
  return response;
3309
4248
  }
4249
+ function resolvePythonGraphqlPrompt(pythonGraphql) {
4250
+ return createStaticSinglePromptResolution(PYTHON_GRAPHQL_PROMPT_OPTIONS, "none", pythonGraphql);
4251
+ }
3310
4252
  async function getPythonGraphqlChoice(pythonGraphql) {
3311
- if (pythonGraphql !== void 0) return pythonGraphql;
4253
+ const resolution = resolvePythonGraphqlPrompt(pythonGraphql);
4254
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3312
4255
  const response = await navigableSelect({
3313
4256
  message: "Select Python GraphQL framework",
3314
- options: [{
3315
- value: "strawberry",
3316
- label: "Strawberry",
3317
- hint: "Python GraphQL library using dataclasses and type hints"
3318
- }, {
3319
- value: "none",
3320
- label: "None",
3321
- hint: "No GraphQL framework"
3322
- }],
3323
- initialValue: "none"
4257
+ options: resolution.options,
4258
+ initialValue: resolution.initialValue
3324
4259
  });
3325
4260
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3326
4261
  return response;
3327
4262
  }
4263
+ function resolvePythonQualityPrompt(pythonQuality) {
4264
+ return createStaticSinglePromptResolution(PYTHON_QUALITY_PROMPT_OPTIONS, "ruff", pythonQuality);
4265
+ }
3328
4266
  async function getPythonQualityChoice(pythonQuality) {
3329
- if (pythonQuality !== void 0) return pythonQuality;
4267
+ const resolution = resolvePythonQualityPrompt(pythonQuality);
4268
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3330
4269
  const response = await navigableSelect({
3331
4270
  message: "Select Python code quality tool",
3332
- options: [{
3333
- value: "ruff",
3334
- label: "Ruff",
3335
- hint: "An extremely fast Python linter and formatter"
3336
- }, {
3337
- value: "none",
3338
- label: "None",
3339
- hint: "No code quality tools"
3340
- }],
3341
- initialValue: "ruff"
4271
+ options: resolution.options,
4272
+ initialValue: resolution.initialValue
3342
4273
  });
3343
4274
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3344
4275
  return response;
@@ -3346,49 +4277,72 @@ async function getPythonQualityChoice(pythonQuality) {
3346
4277
 
3347
4278
  //#endregion
3348
4279
  //#region src/prompts/realtime.ts
4280
+ const REALTIME_PROMPT_OPTIONS = [
4281
+ {
4282
+ value: "socket-io",
4283
+ label: "Socket.IO",
4284
+ hint: "Real-time bidirectional communication with fallbacks"
4285
+ },
4286
+ {
4287
+ value: "partykit",
4288
+ label: "PartyKit",
4289
+ hint: "Edge-native multiplayer infrastructure on Cloudflare"
4290
+ },
4291
+ {
4292
+ value: "ably",
4293
+ label: "Ably",
4294
+ hint: "Real-time messaging platform with pub/sub and presence"
4295
+ },
4296
+ {
4297
+ value: "pusher",
4298
+ label: "Pusher",
4299
+ hint: "Real-time communication APIs with channels and events"
4300
+ },
4301
+ {
4302
+ value: "liveblocks",
4303
+ label: "Liveblocks",
4304
+ hint: "Collaboration infrastructure for multiplayer experiences"
4305
+ },
4306
+ {
4307
+ value: "yjs",
4308
+ label: "Y.js",
4309
+ hint: "CRDT library for real-time collaboration with conflict-free sync"
4310
+ },
4311
+ {
4312
+ value: "none",
4313
+ label: "None",
4314
+ hint: "Skip real-time/WebSocket integration"
4315
+ }
4316
+ ];
4317
+ function resolveRealtimePrompt(context = {}) {
4318
+ if (context.backend === "none" || context.backend === "convex") return {
4319
+ shouldPrompt: false,
4320
+ mode: "single",
4321
+ options: [],
4322
+ autoValue: "none"
4323
+ };
4324
+ return context.realtime !== void 0 ? {
4325
+ shouldPrompt: false,
4326
+ mode: "single",
4327
+ options: REALTIME_PROMPT_OPTIONS,
4328
+ autoValue: context.realtime
4329
+ } : {
4330
+ shouldPrompt: true,
4331
+ mode: "single",
4332
+ options: REALTIME_PROMPT_OPTIONS,
4333
+ initialValue: "none"
4334
+ };
4335
+ }
3349
4336
  async function getRealtimeChoice(realtime, backend) {
3350
- if (realtime !== void 0) return realtime;
3351
- if (backend === "none" || backend === "convex") return "none";
4337
+ const resolution = resolveRealtimePrompt({
4338
+ realtime,
4339
+ backend
4340
+ });
4341
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3352
4342
  const response = await navigableSelect({
3353
4343
  message: "Select real-time solution",
3354
- options: [
3355
- {
3356
- value: "socket-io",
3357
- label: "Socket.IO",
3358
- hint: "Real-time bidirectional communication with fallbacks"
3359
- },
3360
- {
3361
- value: "partykit",
3362
- label: "PartyKit",
3363
- hint: "Edge-native multiplayer infrastructure on Cloudflare"
3364
- },
3365
- {
3366
- value: "ably",
3367
- label: "Ably",
3368
- hint: "Real-time messaging platform with pub/sub and presence"
3369
- },
3370
- {
3371
- value: "pusher",
3372
- label: "Pusher",
3373
- hint: "Real-time communication APIs with channels and events"
3374
- },
3375
- {
3376
- value: "liveblocks",
3377
- label: "Liveblocks",
3378
- hint: "Collaboration infrastructure for multiplayer experiences"
3379
- },
3380
- {
3381
- value: "yjs",
3382
- label: "Y.js",
3383
- hint: "CRDT library for real-time collaboration with conflict-free sync"
3384
- },
3385
- {
3386
- value: "none",
3387
- label: "None",
3388
- hint: "Skip real-time/WebSocket integration"
3389
- }
3390
- ],
3391
- initialValue: "none"
4344
+ options: resolution.options,
4345
+ initialValue: resolution.initialValue
3392
4346
  });
3393
4347
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3394
4348
  return response;
@@ -3396,27 +4350,53 @@ async function getRealtimeChoice(realtime, backend) {
3396
4350
 
3397
4351
  //#endregion
3398
4352
  //#region src/prompts/runtime.ts
3399
- async function getRuntimeChoice(runtime, backend) {
3400
- if (backend === "convex" || backend === "none" || backend === "self") return "none";
3401
- if (runtime !== void 0) return runtime;
3402
- const runtimeOptions = [{
4353
+ const RUNTIME_PROMPT_OPTIONS = [
4354
+ {
3403
4355
  value: "bun",
3404
4356
  label: "Bun",
3405
4357
  hint: "Fast all-in-one JavaScript runtime"
3406
- }, {
4358
+ },
4359
+ {
3407
4360
  value: "node",
3408
4361
  label: "Node.js",
3409
4362
  hint: "Traditional Node.js runtime"
3410
- }];
3411
- if (backend === "hono") runtimeOptions.push({
4363
+ },
4364
+ {
3412
4365
  value: "workers",
3413
4366
  label: "Cloudflare Workers",
3414
4367
  hint: "Edge runtime on Cloudflare's global network"
4368
+ }
4369
+ ];
4370
+ function resolveRuntimePrompt(context = {}) {
4371
+ if (context.backend === "convex" || context.backend === "none" || context.backend === "self") return {
4372
+ shouldPrompt: false,
4373
+ mode: "single",
4374
+ options: [],
4375
+ autoValue: "none"
4376
+ };
4377
+ const options = RUNTIME_PROMPT_OPTIONS.filter((option) => option.value !== "workers" || context.backend === "hono");
4378
+ return context.runtime !== void 0 ? {
4379
+ shouldPrompt: false,
4380
+ mode: "single",
4381
+ options,
4382
+ autoValue: context.runtime
4383
+ } : {
4384
+ shouldPrompt: true,
4385
+ mode: "single",
4386
+ options,
4387
+ initialValue: DEFAULT_CONFIG.runtime
4388
+ };
4389
+ }
4390
+ async function getRuntimeChoice(runtime, backend) {
4391
+ const resolution = resolveRuntimePrompt({
4392
+ runtime,
4393
+ backend
3415
4394
  });
4395
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3416
4396
  const response = await navigableSelect({
3417
4397
  message: "Select runtime",
3418
- options: runtimeOptions,
3419
- initialValue: DEFAULT_CONFIG.runtime
4398
+ options: resolution.options,
4399
+ initialValue: resolution.initialValue
3420
4400
  });
3421
4401
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3422
4402
  return response;
@@ -3424,261 +4404,324 @@ async function getRuntimeChoice(runtime, backend) {
3424
4404
 
3425
4405
  //#endregion
3426
4406
  //#region src/prompts/rust-ecosystem.ts
4407
+ const RUST_WEB_FRAMEWORK_PROMPT_OPTIONS = [
4408
+ {
4409
+ value: "axum",
4410
+ label: "Axum",
4411
+ hint: "Ergonomic and modular web framework from Tokio"
4412
+ },
4413
+ {
4414
+ value: "actix-web",
4415
+ label: "Actix Web",
4416
+ hint: "Powerful, pragmatic, and extremely fast web framework"
4417
+ },
4418
+ {
4419
+ value: "rocket",
4420
+ label: "Rocket",
4421
+ hint: "Convention-over-configuration web framework, 25k+ stars"
4422
+ },
4423
+ {
4424
+ value: "none",
4425
+ label: "None",
4426
+ hint: "No web framework"
4427
+ }
4428
+ ];
4429
+ const RUST_FRONTEND_PROMPT_OPTIONS = [
4430
+ {
4431
+ value: "leptos",
4432
+ label: "Leptos",
4433
+ hint: "Build fast web applications with Rust"
4434
+ },
4435
+ {
4436
+ value: "dioxus",
4437
+ label: "Dioxus",
4438
+ hint: "Fullstack, cross-platform UI library for Rust"
4439
+ },
4440
+ {
4441
+ value: "none",
4442
+ label: "None",
4443
+ hint: "No Rust frontend (API only)"
4444
+ }
4445
+ ];
4446
+ const RUST_ORM_PROMPT_OPTIONS = [
4447
+ {
4448
+ value: "sea-orm",
4449
+ label: "SeaORM",
4450
+ hint: "Async & dynamic ORM for Rust"
4451
+ },
4452
+ {
4453
+ value: "sqlx",
4454
+ label: "SQLx",
4455
+ hint: "Async SQL toolkit with compile-time checked queries"
4456
+ },
4457
+ {
4458
+ value: "diesel",
4459
+ label: "Diesel",
4460
+ hint: "Safe, extensible ORM with compile-time query validation"
4461
+ },
4462
+ {
4463
+ value: "none",
4464
+ label: "None",
4465
+ hint: "No database layer"
4466
+ }
4467
+ ];
4468
+ const RUST_API_PROMPT_OPTIONS = [
4469
+ {
4470
+ value: "tonic",
4471
+ label: "Tonic",
4472
+ hint: "gRPC implementation for Rust"
4473
+ },
4474
+ {
4475
+ value: "async-graphql",
4476
+ label: "async-graphql",
4477
+ hint: "High-performance GraphQL server library"
4478
+ },
4479
+ {
4480
+ value: "none",
4481
+ label: "None",
4482
+ hint: "REST API only"
4483
+ }
4484
+ ];
4485
+ const RUST_CLI_PROMPT_OPTIONS = [
4486
+ {
4487
+ value: "clap",
4488
+ label: "Clap",
4489
+ hint: "Command Line Argument Parser for Rust"
4490
+ },
4491
+ {
4492
+ value: "ratatui",
4493
+ label: "Ratatui",
4494
+ hint: "Build rich terminal user interfaces"
4495
+ },
4496
+ {
4497
+ value: "none",
4498
+ label: "None",
4499
+ hint: "No CLI tools"
4500
+ }
4501
+ ];
4502
+ const RUST_LIBRARIES_PROMPT_OPTIONS = [
4503
+ {
4504
+ value: "serde",
4505
+ label: "Serde",
4506
+ hint: "Serialization framework for Rust"
4507
+ },
4508
+ {
4509
+ value: "validator",
4510
+ label: "Validator",
4511
+ hint: "Struct validation derive macros"
4512
+ },
4513
+ {
4514
+ value: "jsonwebtoken",
4515
+ label: "jsonwebtoken",
4516
+ hint: "JWT encoding/decoding library"
4517
+ },
4518
+ {
4519
+ value: "argon2",
4520
+ label: "Argon2",
4521
+ hint: "Password hashing library"
4522
+ },
4523
+ {
4524
+ value: "tokio-test",
4525
+ label: "Tokio Test",
4526
+ hint: "Testing utilities for Tokio"
4527
+ },
4528
+ {
4529
+ value: "mockall",
4530
+ label: "Mockall",
4531
+ hint: "Powerful mocking library for Rust"
4532
+ }
4533
+ ];
4534
+ const RUST_LOGGING_PROMPT_OPTIONS = [
4535
+ {
4536
+ value: "tracing",
4537
+ label: "Tracing",
4538
+ hint: "Structured, composable instrumentation framework from Tokio"
4539
+ },
4540
+ {
4541
+ value: "env-logger",
4542
+ label: "env_logger",
4543
+ hint: "Simple logger configured via environment variables"
4544
+ },
4545
+ {
4546
+ value: "none",
4547
+ label: "None",
4548
+ hint: "No logging library"
4549
+ }
4550
+ ];
4551
+ const RUST_ERROR_HANDLING_PROMPT_OPTIONS = [
4552
+ {
4553
+ value: "anyhow-thiserror",
4554
+ label: "anyhow + thiserror",
4555
+ hint: "anyhow for application errors, thiserror for custom error types"
4556
+ },
4557
+ {
4558
+ value: "eyre",
4559
+ label: "eyre + color-eyre",
4560
+ hint: "Customizable error reports with pretty backtraces via color-eyre"
4561
+ },
4562
+ {
4563
+ value: "none",
4564
+ label: "None",
4565
+ hint: "No error handling library (uses standard library only)"
4566
+ }
4567
+ ];
4568
+ const RUST_CACHING_PROMPT_OPTIONS = [
4569
+ {
4570
+ value: "moka",
4571
+ label: "Moka",
4572
+ hint: "High-performance concurrent in-memory cache (Caffeine-inspired)"
4573
+ },
4574
+ {
4575
+ value: "redis",
4576
+ label: "Redis",
4577
+ hint: "Redis client with async support and connection pooling"
4578
+ },
4579
+ {
4580
+ value: "none",
4581
+ label: "None",
4582
+ hint: "No caching library"
4583
+ }
4584
+ ];
4585
+ function resolveRustWebFrameworkPrompt(rustWebFramework) {
4586
+ return createStaticSinglePromptResolution(RUST_WEB_FRAMEWORK_PROMPT_OPTIONS, "axum", rustWebFramework);
4587
+ }
3427
4588
  async function getRustWebFrameworkChoice(rustWebFramework) {
3428
- if (rustWebFramework !== void 0) return rustWebFramework;
4589
+ const resolution = resolveRustWebFrameworkPrompt(rustWebFramework);
4590
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3429
4591
  const response = await navigableSelect({
3430
4592
  message: "Select Rust web framework",
3431
- options: [
3432
- {
3433
- value: "axum",
3434
- label: "Axum",
3435
- hint: "Ergonomic and modular web framework from Tokio"
3436
- },
3437
- {
3438
- value: "actix-web",
3439
- label: "Actix Web",
3440
- hint: "Powerful, pragmatic, and extremely fast web framework"
3441
- },
3442
- {
3443
- value: "rocket",
3444
- label: "Rocket",
3445
- hint: "Convention-over-configuration web framework, 25k+ stars"
3446
- },
3447
- {
3448
- value: "none",
3449
- label: "None",
3450
- hint: "No web framework"
3451
- }
3452
- ],
3453
- initialValue: "axum"
4593
+ options: resolution.options,
4594
+ initialValue: resolution.initialValue
3454
4595
  });
3455
4596
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3456
4597
  return response;
3457
4598
  }
4599
+ function resolveRustFrontendPrompt(rustFrontend) {
4600
+ return createStaticSinglePromptResolution(RUST_FRONTEND_PROMPT_OPTIONS, "none", rustFrontend);
4601
+ }
3458
4602
  async function getRustFrontendChoice(rustFrontend) {
3459
- if (rustFrontend !== void 0) return rustFrontend;
4603
+ const resolution = resolveRustFrontendPrompt(rustFrontend);
4604
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3460
4605
  const response = await navigableSelect({
3461
4606
  message: "Select Rust frontend framework",
3462
- options: [
3463
- {
3464
- value: "leptos",
3465
- label: "Leptos",
3466
- hint: "Build fast web applications with Rust"
3467
- },
3468
- {
3469
- value: "dioxus",
3470
- label: "Dioxus",
3471
- hint: "Fullstack, cross-platform UI library for Rust"
3472
- },
3473
- {
3474
- value: "none",
3475
- label: "None",
3476
- hint: "No Rust frontend (API only)"
3477
- }
3478
- ],
3479
- initialValue: "none"
3480
- });
3481
- if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3482
- return response;
3483
- }
3484
- async function getRustOrmChoice(rustOrm) {
3485
- if (rustOrm !== void 0) return rustOrm;
3486
- const response = await navigableSelect({
3487
- message: "Select Rust ORM/database layer",
3488
- options: [
3489
- {
3490
- value: "sea-orm",
3491
- label: "SeaORM",
3492
- hint: "Async & dynamic ORM for Rust"
3493
- },
3494
- {
3495
- value: "sqlx",
3496
- label: "SQLx",
3497
- hint: "Async SQL toolkit with compile-time checked queries"
3498
- },
3499
- {
3500
- value: "diesel",
3501
- label: "Diesel",
3502
- hint: "Safe, extensible ORM with compile-time query validation"
3503
- },
3504
- {
3505
- value: "none",
3506
- label: "None",
3507
- hint: "No database layer"
3508
- }
3509
- ],
3510
- initialValue: "none"
4607
+ options: resolution.options,
4608
+ initialValue: resolution.initialValue
3511
4609
  });
3512
4610
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3513
4611
  return response;
3514
4612
  }
4613
+ function resolveRustOrmPrompt(rustOrm) {
4614
+ return createStaticSinglePromptResolution(RUST_ORM_PROMPT_OPTIONS, "none", rustOrm);
4615
+ }
4616
+ async function getRustOrmChoice(rustOrm) {
4617
+ const resolution = resolveRustOrmPrompt(rustOrm);
4618
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
4619
+ const response = await navigableSelect({
4620
+ message: "Select Rust ORM/database layer",
4621
+ options: resolution.options,
4622
+ initialValue: resolution.initialValue
4623
+ });
4624
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
4625
+ return response;
4626
+ }
4627
+ function resolveRustApiPrompt(rustApi) {
4628
+ return createStaticSinglePromptResolution(RUST_API_PROMPT_OPTIONS, "none", rustApi);
4629
+ }
3515
4630
  async function getRustApiChoice(rustApi) {
3516
- if (rustApi !== void 0) return rustApi;
4631
+ const resolution = resolveRustApiPrompt(rustApi);
4632
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3517
4633
  const response = await navigableSelect({
3518
4634
  message: "Select Rust API layer",
3519
- options: [
3520
- {
3521
- value: "tonic",
3522
- label: "Tonic",
3523
- hint: "gRPC implementation for Rust"
3524
- },
3525
- {
3526
- value: "async-graphql",
3527
- label: "async-graphql",
3528
- hint: "High-performance GraphQL server library"
3529
- },
3530
- {
3531
- value: "none",
3532
- label: "None",
3533
- hint: "REST API only"
3534
- }
3535
- ],
3536
- initialValue: "none"
4635
+ options: resolution.options,
4636
+ initialValue: resolution.initialValue
3537
4637
  });
3538
4638
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3539
4639
  return response;
3540
4640
  }
4641
+ function resolveRustCliPrompt(rustCli) {
4642
+ return createStaticSinglePromptResolution(RUST_CLI_PROMPT_OPTIONS, "none", rustCli);
4643
+ }
3541
4644
  async function getRustCliChoice(rustCli) {
3542
- if (rustCli !== void 0) return rustCli;
4645
+ const resolution = resolveRustCliPrompt(rustCli);
4646
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3543
4647
  const response = await navigableSelect({
3544
4648
  message: "Select Rust CLI tools",
3545
- options: [
3546
- {
3547
- value: "clap",
3548
- label: "Clap",
3549
- hint: "Command Line Argument Parser for Rust"
3550
- },
3551
- {
3552
- value: "ratatui",
3553
- label: "Ratatui",
3554
- hint: "Build rich terminal user interfaces"
3555
- },
3556
- {
3557
- value: "none",
3558
- label: "None",
3559
- hint: "No CLI tools"
3560
- }
3561
- ],
3562
- initialValue: "none"
4649
+ options: resolution.options,
4650
+ initialValue: resolution.initialValue
3563
4651
  });
3564
4652
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3565
4653
  return response;
3566
4654
  }
4655
+ function resolveRustLibrariesPrompt(rustLibraries) {
4656
+ return createStaticMultiPromptResolution(RUST_LIBRARIES_PROMPT_OPTIONS, [], rustLibraries);
4657
+ }
3567
4658
  async function getRustLibrariesChoice(rustLibraries) {
3568
- if (rustLibraries !== void 0) return rustLibraries;
4659
+ const resolution = resolveRustLibrariesPrompt(rustLibraries);
4660
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? [];
3569
4661
  const response = await navigableMultiselect({
3570
4662
  message: "Select Rust libraries",
3571
- options: [
3572
- {
3573
- value: "serde",
3574
- label: "Serde",
3575
- hint: "Serialization framework for Rust"
3576
- },
3577
- {
3578
- value: "validator",
3579
- label: "Validator",
3580
- hint: "Struct validation derive macros"
3581
- },
3582
- {
3583
- value: "jsonwebtoken",
3584
- label: "jsonwebtoken",
3585
- hint: "JWT encoding/decoding library"
3586
- },
3587
- {
3588
- value: "argon2",
3589
- label: "Argon2",
3590
- hint: "Password hashing library"
3591
- },
3592
- {
3593
- value: "tokio-test",
3594
- label: "Tokio Test",
3595
- hint: "Testing utilities for Tokio"
3596
- },
3597
- {
3598
- value: "mockall",
3599
- label: "Mockall",
3600
- hint: "Powerful mocking library for Rust"
3601
- }
3602
- ],
4663
+ options: resolution.options,
3603
4664
  required: false,
3604
- initialValues: ["serde"]
4665
+ initialValues: resolution.initialValue
3605
4666
  });
3606
4667
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3607
4668
  return response;
3608
4669
  }
4670
+ function resolveRustLoggingPrompt(rustLogging) {
4671
+ return createStaticSinglePromptResolution(RUST_LOGGING_PROMPT_OPTIONS, "tracing", rustLogging);
4672
+ }
3609
4673
  async function getRustLoggingChoice(rustLogging) {
3610
- if (rustLogging !== void 0) return rustLogging;
4674
+ const resolution = resolveRustLoggingPrompt(rustLogging);
4675
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3611
4676
  const response = await navigableSelect({
3612
4677
  message: "Select Rust logging library",
3613
- options: [
3614
- {
3615
- value: "tracing",
3616
- label: "Tracing",
3617
- hint: "Structured, composable instrumentation framework from Tokio"
3618
- },
3619
- {
3620
- value: "env-logger",
3621
- label: "env_logger",
3622
- hint: "Simple logger configured via environment variables"
3623
- },
3624
- {
3625
- value: "none",
3626
- label: "None",
3627
- hint: "No logging library"
3628
- }
3629
- ],
3630
- initialValue: "tracing"
4678
+ options: resolution.options,
4679
+ initialValue: resolution.initialValue
3631
4680
  });
3632
4681
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3633
4682
  return response;
3634
4683
  }
4684
+ function resolveRustErrorHandlingPrompt(rustErrorHandling) {
4685
+ return createStaticSinglePromptResolution(RUST_ERROR_HANDLING_PROMPT_OPTIONS, "anyhow-thiserror", rustErrorHandling);
4686
+ }
3635
4687
  async function getRustErrorHandlingChoice(rustErrorHandling) {
3636
- if (rustErrorHandling !== void 0) return rustErrorHandling;
4688
+ const resolution = resolveRustErrorHandlingPrompt(rustErrorHandling);
4689
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3637
4690
  const response = await navigableSelect({
3638
4691
  message: "Select Rust error handling library",
3639
- options: [
3640
- {
3641
- value: "anyhow-thiserror",
3642
- label: "anyhow + thiserror",
3643
- hint: "anyhow for application errors, thiserror for custom error types"
3644
- },
3645
- {
3646
- value: "eyre",
3647
- label: "eyre + color-eyre",
3648
- hint: "Customizable error reports with pretty backtraces via color-eyre"
3649
- },
3650
- {
3651
- value: "none",
3652
- label: "None",
3653
- hint: "No error handling library (uses standard library only)"
3654
- }
3655
- ],
3656
- initialValue: "anyhow-thiserror"
4692
+ options: resolution.options,
4693
+ initialValue: resolution.initialValue
3657
4694
  });
3658
4695
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
3659
4696
  return response;
3660
4697
  }
4698
+ function resolveRustCachingPrompt(rustCaching) {
4699
+ return createStaticSinglePromptResolution(RUST_CACHING_PROMPT_OPTIONS, "none", rustCaching);
4700
+ }
3661
4701
  async function getRustCachingChoice(rustCaching) {
3662
- if (rustCaching !== void 0) return rustCaching;
4702
+ const resolution = resolveRustCachingPrompt(rustCaching);
4703
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3663
4704
  const response = await navigableSelect({
3664
4705
  message: "Select Rust caching library",
3665
- options: [
3666
- {
3667
- value: "moka",
3668
- label: "Moka",
3669
- hint: "High-performance concurrent in-memory cache (Caffeine-inspired)"
3670
- },
3671
- {
3672
- value: "redis",
3673
- label: "Redis",
3674
- hint: "Redis client with async support and connection pooling"
3675
- },
3676
- {
3677
- value: "none",
3678
- label: "None",
3679
- hint: "No caching library"
3680
- }
3681
- ],
4706
+ options: resolution.options,
4707
+ initialValue: resolution.initialValue
4708
+ });
4709
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
4710
+ return response;
4711
+ }
4712
+ async function getRustAuthChoice(rustAuth) {
4713
+ if (rustAuth !== void 0) return rustAuth;
4714
+ const response = await navigableSelect({
4715
+ message: "Select Rust authentication library",
4716
+ options: [{
4717
+ value: "oauth2",
4718
+ label: "OAuth2",
4719
+ hint: "OAuth2 client with authorization code, PKCE, and token exchange flows"
4720
+ }, {
4721
+ value: "none",
4722
+ label: "None",
4723
+ hint: "No authentication library"
4724
+ }],
3682
4725
  initialValue: "none"
3683
4726
  });
3684
4727
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
@@ -4123,10 +5166,20 @@ async function promptShadcnRadius() {
4123
5166
 
4124
5167
  //#endregion
4125
5168
  //#region src/prompts/state-management.ts
4126
- async function getStateManagementChoice(stateManagement, frontends) {
4127
- if (stateManagement !== void 0) return stateManagement;
4128
- const { web } = splitFrontends$1(frontends);
4129
- if (web.length === 0) return "none";
5169
+ function resolveStateManagementPrompt(context = {}) {
5170
+ if (context.stateManagement !== void 0) return {
5171
+ shouldPrompt: false,
5172
+ mode: "single",
5173
+ options: [],
5174
+ autoValue: context.stateManagement
5175
+ };
5176
+ const { web } = splitFrontends$1(context.frontends);
5177
+ if (web.length === 0) return {
5178
+ shouldPrompt: false,
5179
+ mode: "single",
5180
+ options: [],
5181
+ autoValue: "none"
5182
+ };
4130
5183
  const isReact = web.some((f) => [
4131
5184
  "tanstack-router",
4132
5185
  "react-router",
@@ -4180,10 +5233,23 @@ async function getStateManagementChoice(stateManagement, frontends) {
4180
5233
  label: "None",
4181
5234
  hint: "Skip state management setup"
4182
5235
  });
4183
- const response = await navigableSelect({
4184
- message: "Select state management",
5236
+ return {
5237
+ shouldPrompt: true,
5238
+ mode: "single",
4185
5239
  options,
4186
5240
  initialValue: "none"
5241
+ };
5242
+ }
5243
+ async function getStateManagementChoice(stateManagement, frontends) {
5244
+ const resolution = resolveStateManagementPrompt({
5245
+ stateManagement,
5246
+ frontends
5247
+ });
5248
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
5249
+ const response = await navigableSelect({
5250
+ message: "Select state management",
5251
+ options: resolution.options,
5252
+ initialValue: resolution.initialValue
4187
5253
  });
4188
5254
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
4189
5255
  return response;
@@ -4191,43 +5257,48 @@ async function getStateManagementChoice(stateManagement, frontends) {
4191
5257
 
4192
5258
  //#endregion
4193
5259
  //#region src/prompts/testing.ts
5260
+ const TESTING_PROMPT_OPTIONS = [
5261
+ {
5262
+ value: "vitest",
5263
+ label: "Vitest",
5264
+ hint: "Blazing fast Vite-native unit test framework"
5265
+ },
5266
+ {
5267
+ value: "vitest-playwright",
5268
+ label: "Vitest + Playwright",
5269
+ hint: "Both unit and E2E testing for complete coverage"
5270
+ },
5271
+ {
5272
+ value: "playwright",
5273
+ label: "Playwright",
5274
+ hint: "End-to-end testing framework by Microsoft"
5275
+ },
5276
+ {
5277
+ value: "jest",
5278
+ label: "Jest",
5279
+ hint: "Classic testing framework with wide ecosystem"
5280
+ },
5281
+ {
5282
+ value: "cypress",
5283
+ label: "Cypress",
5284
+ hint: "E2E testing with time travel debugging"
5285
+ },
5286
+ {
5287
+ value: "none",
5288
+ label: "None",
5289
+ hint: "Skip testing framework setup"
5290
+ }
5291
+ ];
5292
+ function resolveTestingPrompt(testing) {
5293
+ return createStaticSinglePromptResolution(TESTING_PROMPT_OPTIONS, "vitest", testing);
5294
+ }
4194
5295
  async function getTestingChoice(testing) {
4195
- if (testing !== void 0) return testing;
5296
+ const resolution = resolveTestingPrompt(testing);
5297
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
4196
5298
  const response = await navigableSelect({
4197
5299
  message: "Select testing framework",
4198
- options: [
4199
- {
4200
- value: "vitest",
4201
- label: "Vitest",
4202
- hint: "Blazing fast Vite-native unit test framework"
4203
- },
4204
- {
4205
- value: "vitest-playwright",
4206
- label: "Vitest + Playwright",
4207
- hint: "Both unit and E2E testing for complete coverage"
4208
- },
4209
- {
4210
- value: "playwright",
4211
- label: "Playwright",
4212
- hint: "End-to-end testing framework by Microsoft"
4213
- },
4214
- {
4215
- value: "jest",
4216
- label: "Jest",
4217
- hint: "Classic testing framework with wide ecosystem"
4218
- },
4219
- {
4220
- value: "cypress",
4221
- label: "Cypress",
4222
- hint: "E2E testing with time travel debugging"
4223
- },
4224
- {
4225
- value: "none",
4226
- label: "None",
4227
- hint: "Skip testing framework setup"
4228
- }
4229
- ],
4230
- initialValue: "vitest"
5300
+ options: resolution.options,
5301
+ initialValue: resolution.initialValue
4231
5302
  });
4232
5303
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
4233
5304
  return response;
@@ -4285,21 +5356,48 @@ const UI_LIBRARY_OPTIONS = {
4285
5356
  hint: "No UI component library"
4286
5357
  }
4287
5358
  };
4288
- async function getUILibraryChoice(uiLibrary, frontends, astroIntegration) {
4289
- const { web } = splitFrontends$1(frontends);
4290
- if (web.length === 0) return "none";
4291
- const compatibleLibraries = getCompatibleUILibraries$1(frontends, astroIntegration);
4292
- if (uiLibrary !== void 0) return compatibleLibraries.includes(uiLibrary) ? uiLibrary : compatibleLibraries[0];
4293
- const options = compatibleLibraries.map((lib) => ({
4294
- value: lib,
4295
- label: UI_LIBRARY_OPTIONS[lib].label,
4296
- hint: UI_LIBRARY_OPTIONS[lib].hint
4297
- }));
5359
+ function resolveUILibraryPrompt(context = {}) {
5360
+ const { web } = splitFrontends$1(context.frontends);
5361
+ if (web.length === 0) return {
5362
+ shouldPrompt: false,
5363
+ mode: "single",
5364
+ options: [],
5365
+ autoValue: "none"
5366
+ };
5367
+ const compatibleLibraries = getCompatibleUILibraries$1(context.frontends, context.astroIntegration);
5368
+ if (context.uiLibrary !== void 0) return {
5369
+ shouldPrompt: false,
5370
+ mode: "single",
5371
+ options: compatibleLibraries.map((lib) => ({
5372
+ value: lib,
5373
+ label: UI_LIBRARY_OPTIONS[lib].label,
5374
+ hint: UI_LIBRARY_OPTIONS[lib].hint
5375
+ })),
5376
+ autoValue: compatibleLibraries.includes(context.uiLibrary) ? context.uiLibrary : compatibleLibraries[0]
5377
+ };
4298
5378
  const defaultLib = DEFAULT_UI_LIBRARY_BY_FRONTEND[web[0]];
5379
+ return {
5380
+ shouldPrompt: true,
5381
+ mode: "single",
5382
+ options: compatibleLibraries.map((lib) => ({
5383
+ value: lib,
5384
+ label: UI_LIBRARY_OPTIONS[lib].label,
5385
+ hint: UI_LIBRARY_OPTIONS[lib].hint
5386
+ })),
5387
+ initialValue: compatibleLibraries.includes(defaultLib) ? defaultLib : compatibleLibraries[0]
5388
+ };
5389
+ }
5390
+ async function getUILibraryChoice(uiLibrary, frontends, astroIntegration) {
5391
+ const resolution = resolveUILibraryPrompt({
5392
+ uiLibrary,
5393
+ frontends,
5394
+ astroIntegration
5395
+ });
5396
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
4299
5397
  const selected = await navigableSelect({
4300
5398
  message: "Select UI component library",
4301
- options,
4302
- initialValue: compatibleLibraries.includes(defaultLib) ? defaultLib : compatibleLibraries[0]
5399
+ options: resolution.options,
5400
+ initialValue: resolution.initialValue
4303
5401
  });
4304
5402
  if (isCancel$1(selected)) return exitCancelled("Operation cancelled");
4305
5403
  return selected;
@@ -4307,53 +5405,58 @@ async function getUILibraryChoice(uiLibrary, frontends, astroIntegration) {
4307
5405
 
4308
5406
  //#endregion
4309
5407
  //#region src/prompts/validation.ts
5408
+ const VALIDATION_PROMPT_OPTIONS = [
5409
+ {
5410
+ value: "zod",
5411
+ label: "Zod",
5412
+ hint: "TypeScript-first schema validation (recommended)"
5413
+ },
5414
+ {
5415
+ value: "valibot",
5416
+ label: "Valibot",
5417
+ hint: "Smaller bundle alternative to Zod (~1KB)"
5418
+ },
5419
+ {
5420
+ value: "arktype",
5421
+ label: "ArkType",
5422
+ hint: "TypeScript-first validation, 2-4x faster than Zod"
5423
+ },
5424
+ {
5425
+ value: "typebox",
5426
+ label: "TypeBox",
5427
+ hint: "JSON Schema type builder for TypeScript"
5428
+ },
5429
+ {
5430
+ value: "typia",
5431
+ label: "Typia",
5432
+ hint: "Super-fast validation via compile-time transform"
5433
+ },
5434
+ {
5435
+ value: "runtypes",
5436
+ label: "Runtypes",
5437
+ hint: "Runtime type validation with composable validators"
5438
+ },
5439
+ {
5440
+ value: "effect-schema",
5441
+ label: "@effect/schema",
5442
+ hint: "Effect ecosystem schema validation with powerful transformations"
5443
+ },
5444
+ {
5445
+ value: "none",
5446
+ label: "None",
5447
+ hint: "Use Zod internally only (no additional library)"
5448
+ }
5449
+ ];
5450
+ function resolveValidationPrompt(validation) {
5451
+ return createStaticSinglePromptResolution(VALIDATION_PROMPT_OPTIONS, "zod", validation);
5452
+ }
4310
5453
  async function getValidationChoice(validation) {
4311
- if (validation !== void 0) return validation;
5454
+ const resolution = resolveValidationPrompt(validation);
5455
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
4312
5456
  const response = await navigableSelect({
4313
5457
  message: "Select validation library",
4314
- options: [
4315
- {
4316
- value: "zod",
4317
- label: "Zod",
4318
- hint: "TypeScript-first schema validation (recommended)"
4319
- },
4320
- {
4321
- value: "valibot",
4322
- label: "Valibot",
4323
- hint: "Smaller bundle alternative to Zod (~1KB)"
4324
- },
4325
- {
4326
- value: "arktype",
4327
- label: "ArkType",
4328
- hint: "TypeScript-first validation, 2-4x faster than Zod"
4329
- },
4330
- {
4331
- value: "typebox",
4332
- label: "TypeBox",
4333
- hint: "JSON Schema type builder for TypeScript"
4334
- },
4335
- {
4336
- value: "typia",
4337
- label: "Typia",
4338
- hint: "Super-fast validation via compile-time transform"
4339
- },
4340
- {
4341
- value: "runtypes",
4342
- label: "Runtypes",
4343
- hint: "Runtime type validation with composable validators"
4344
- },
4345
- {
4346
- value: "effect-schema",
4347
- label: "@effect/schema",
4348
- hint: "Effect ecosystem schema validation with powerful transformations"
4349
- },
4350
- {
4351
- value: "none",
4352
- label: "None",
4353
- hint: "Use Zod internally only (no additional library)"
4354
- }
4355
- ],
4356
- initialValue: "zod"
5458
+ options: resolution.options,
5459
+ initialValue: resolution.initialValue
4357
5460
  });
4358
5461
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
4359
5462
  return response;
@@ -4622,6 +5725,10 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
4622
5725
  if (results.ecosystem !== "rust") return Promise.resolve("none");
4623
5726
  return getRustCachingChoice(flags.rustCaching);
4624
5727
  },
5728
+ rustAuth: ({ results }) => {
5729
+ if (results.ecosystem !== "rust") return Promise.resolve("none");
5730
+ return getRustAuthChoice(flags.rustAuth);
5731
+ },
4625
5732
  pythonWebFramework: ({ results }) => {
4626
5733
  if (results.ecosystem !== "python") return Promise.resolve("none");
4627
5734
  return getPythonWebFrameworkChoice(flags.pythonWebFramework);
@@ -4674,13 +5781,45 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
4674
5781
  if (results.ecosystem !== "go") return Promise.resolve("none");
4675
5782
  return getGoLoggingChoice(flags.goLogging);
4676
5783
  },
5784
+ goAuth: ({ results }) => {
5785
+ if (results.ecosystem !== "go") return Promise.resolve("none");
5786
+ return getGoAuthChoice(flags.goAuth);
5787
+ },
5788
+ javaWebFramework: ({ results }) => {
5789
+ if (results.ecosystem !== "java") return Promise.resolve("none");
5790
+ return getJavaWebFrameworkChoice(flags.javaWebFramework);
5791
+ },
5792
+ javaBuildTool: ({ results }) => {
5793
+ if (results.ecosystem !== "java") return Promise.resolve("none");
5794
+ return getJavaBuildToolChoice(flags.javaBuildTool);
5795
+ },
5796
+ javaOrm: ({ results }) => {
5797
+ if (results.ecosystem !== "java") return Promise.resolve("none");
5798
+ if (results.javaWebFramework !== "spring-boot" || results.javaBuildTool === "none") return Promise.resolve("none");
5799
+ return getJavaOrmChoice(flags.javaOrm);
5800
+ },
5801
+ javaAuth: ({ results }) => {
5802
+ if (results.ecosystem !== "java") return Promise.resolve("none");
5803
+ if (results.javaWebFramework !== "spring-boot" || results.javaBuildTool === "none") return Promise.resolve("none");
5804
+ return getJavaAuthChoice(flags.javaAuth);
5805
+ },
5806
+ javaLibraries: ({ results }) => {
5807
+ if (results.ecosystem !== "java") return Promise.resolve([]);
5808
+ if (results.javaWebFramework !== "spring-boot" || results.javaBuildTool === "none") return Promise.resolve([]);
5809
+ return getJavaLibrariesChoice(flags.javaLibraries);
5810
+ },
5811
+ javaTestingLibraries: ({ results }) => {
5812
+ if (results.ecosystem !== "java") return Promise.resolve([]);
5813
+ if (results.javaBuildTool === "none") return Promise.resolve([]);
5814
+ return getJavaTestingLibrariesChoice(flags.javaTestingLibraries);
5815
+ },
4677
5816
  aiDocs: () => getAiDocsChoice(flags.aiDocs),
4678
5817
  git: () => getGitChoice(flags.git),
4679
5818
  packageManager: ({ results }) => {
4680
- if (results.ecosystem === "rust" || results.ecosystem === "python" || results.ecosystem === "go") return Promise.resolve(flags.packageManager ?? getUserPkgManager());
5819
+ if (results.ecosystem === "rust" || results.ecosystem === "python" || results.ecosystem === "go" || results.ecosystem === "java") return Promise.resolve(flags.packageManager ?? getUserPkgManager());
4681
5820
  return getPackageManagerChoice(flags.packageManager);
4682
5821
  },
4683
- install: ({ results }) => getinstallChoice(flags.install, results.ecosystem)
5822
+ install: ({ results }) => getinstallChoice(flags.install, results.ecosystem, results.javaBuildTool)
4684
5823
  }, { onCancel: () => exitCancelled("Operation cancelled") });
4685
5824
  return {
4686
5825
  projectName,
@@ -4736,6 +5875,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
4736
5875
  rustLogging: result.rustLogging,
4737
5876
  rustErrorHandling: result.rustErrorHandling,
4738
5877
  rustCaching: result.rustCaching,
5878
+ rustAuth: result.rustAuth,
4739
5879
  pythonWebFramework: result.pythonWebFramework,
4740
5880
  pythonOrm: result.pythonOrm,
4741
5881
  pythonValidation: result.pythonValidation,
@@ -4749,6 +5889,13 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
4749
5889
  goApi: result.goApi,
4750
5890
  goCli: result.goCli,
4751
5891
  goLogging: result.goLogging,
5892
+ goAuth: result.goAuth,
5893
+ javaWebFramework: result.javaWebFramework,
5894
+ javaBuildTool: result.javaBuildTool,
5895
+ javaOrm: result.javaOrm,
5896
+ javaAuth: result.javaAuth,
5897
+ javaLibraries: result.javaLibraries,
5898
+ javaTestingLibraries: result.javaTestingLibraries,
4752
5899
  aiDocs: result.aiDocs
4753
5900
  };
4754
5901
  }
@@ -5028,6 +6175,7 @@ function getRustFlags(config) {
5028
6175
  flags.push(`--rust-logging ${config.rustLogging}`);
5029
6176
  flags.push(`--rust-error-handling ${config.rustErrorHandling}`);
5030
6177
  flags.push(`--rust-caching ${config.rustCaching}`);
6178
+ flags.push(`--rust-auth ${config.rustAuth}`);
5031
6179
  appendSharedNonTypeScriptFlags(flags, config);
5032
6180
  appendCommonFlags(flags, config);
5033
6181
  return flags;
@@ -5053,11 +6201,24 @@ function getGoFlags(config) {
5053
6201
  flags.push(`--go-api ${config.goApi}`);
5054
6202
  flags.push(`--go-cli ${config.goCli}`);
5055
6203
  flags.push(`--go-logging ${config.goLogging}`);
6204
+ flags.push(`--go-auth ${config.goAuth}`);
5056
6205
  flags.push(`--auth ${config.auth}`);
5057
6206
  appendSharedNonTypeScriptFlags(flags, config);
5058
6207
  appendCommonFlags(flags, config);
5059
6208
  return flags;
5060
6209
  }
6210
+ function getJavaFlags(config) {
6211
+ const flags = ["--ecosystem java"];
6212
+ flags.push(`--java-web-framework ${config.javaWebFramework}`);
6213
+ flags.push(`--java-build-tool ${config.javaBuildTool}`);
6214
+ flags.push(`--java-orm ${config.javaOrm}`);
6215
+ flags.push(`--java-auth ${config.javaAuth}`);
6216
+ flags.push(formatArrayFlag("java-libraries", config.javaLibraries));
6217
+ flags.push(formatArrayFlag("java-testing-libraries", config.javaTestingLibraries));
6218
+ appendSharedNonTypeScriptFlags(flags, config);
6219
+ appendCommonFlags(flags, config);
6220
+ return flags;
6221
+ }
5061
6222
  function generateReproducibleCommand(config) {
5062
6223
  let flags;
5063
6224
  switch (config.ecosystem) {
@@ -5070,6 +6231,9 @@ function generateReproducibleCommand(config) {
5070
6231
  case "go":
5071
6232
  flags = getGoFlags(config);
5072
6233
  break;
6234
+ case "java":
6235
+ flags = getJavaFlags(config);
6236
+ break;
5073
6237
  case "typescript":
5074
6238
  default:
5075
6239
  flags = getTypeScriptFlags(config);
@@ -5316,6 +6480,7 @@ function processFlags(options, projectName) {
5316
6480
  if (options.rustLogging !== void 0) config.rustLogging = options.rustLogging;
5317
6481
  if (options.rustErrorHandling !== void 0) config.rustErrorHandling = options.rustErrorHandling;
5318
6482
  if (options.rustCaching !== void 0) config.rustCaching = options.rustCaching;
6483
+ if (options.rustAuth !== void 0) config.rustAuth = options.rustAuth;
5319
6484
  if (options.pythonWebFramework !== void 0) config.pythonWebFramework = options.pythonWebFramework;
5320
6485
  if (options.pythonOrm !== void 0) config.pythonOrm = options.pythonOrm;
5321
6486
  if (options.pythonValidation !== void 0) config.pythonValidation = options.pythonValidation;
@@ -5329,6 +6494,13 @@ function processFlags(options, projectName) {
5329
6494
  if (options.goApi !== void 0) config.goApi = options.goApi;
5330
6495
  if (options.goCli !== void 0) config.goCli = options.goCli;
5331
6496
  if (options.goLogging !== void 0) config.goLogging = options.goLogging;
6497
+ if (options.goAuth !== void 0) config.goAuth = options.goAuth;
6498
+ if (options.javaWebFramework !== void 0) config.javaWebFramework = options.javaWebFramework;
6499
+ if (options.javaBuildTool !== void 0) config.javaBuildTool = options.javaBuildTool;
6500
+ if (options.javaOrm !== void 0) config.javaOrm = options.javaOrm;
6501
+ if (options.javaAuth !== void 0) config.javaAuth = options.javaAuth;
6502
+ if (options.javaLibraries !== void 0) config.javaLibraries = processArrayOption(options.javaLibraries);
6503
+ if (options.javaTestingLibraries !== void 0) config.javaTestingLibraries = processArrayOption(options.javaTestingLibraries);
5332
6504
  return config;
5333
6505
  }
5334
6506
  function getProvidedFlags(options) {
@@ -5342,6 +6514,11 @@ function validateArrayOptions(options) {
5342
6514
  validateNoneExclusivity(options.frontend, "frontend options");
5343
6515
  validateNoneExclusivity(options.addons, "addons");
5344
6516
  validateNoneExclusivity(options.examples, "examples");
6517
+ validateNoneExclusivity(options.aiDocs, "ai docs");
6518
+ validateNoneExclusivity(options.rustLibraries, "rust libraries");
6519
+ validateNoneExclusivity(options.pythonAi, "python ai libraries");
6520
+ validateNoneExclusivity(options.javaLibraries, "java libraries");
6521
+ validateNoneExclusivity(options.javaTestingLibraries, "java testing libraries");
5345
6522
  }
5346
6523
 
5347
6524
  //#endregion
@@ -5777,6 +6954,41 @@ function validateFrontendConstraints(config, providedFlags) {
5777
6954
  validateWebDeployRequiresWebFrontend(config.webDeploy, hasWebFrontendFlag);
5778
6955
  }
5779
6956
  function validateApiConstraints(_config, _options) {}
6957
+ function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set()) {
6958
+ if (config.ecosystem !== "java") return;
6959
+ const hasSpringBoot = config.javaWebFramework === "spring-boot";
6960
+ const hasNoBuildTool = config.javaBuildTool === "none";
6961
+ const hasJavaLibraries = (config.javaLibraries ?? []).some((library) => library !== "none");
6962
+ const hasJavaTestingLibraries = (config.javaTestingLibraries ?? []).some((library) => library !== "none");
6963
+ const hasSpringOnlyFeatures = config.javaOrm !== "none" || config.javaAuth !== "none" || hasJavaLibraries;
6964
+ if (hasNoBuildTool && hasSpringBoot) incompatibilityError({
6965
+ message: "Spring Boot requires Maven or Gradle in the Java scaffold.",
6966
+ provided: {
6967
+ "java-web-framework": config.javaWebFramework ?? "none",
6968
+ "java-build-tool": config.javaBuildTool ?? "none"
6969
+ },
6970
+ suggestions: ["Use --java-build-tool maven or --java-build-tool gradle with Spring Boot", "Use --java-web-framework none for a plain Java source-only scaffold"]
6971
+ });
6972
+ if ((config.javaWebFramework === "none" || hasNoBuildTool) && hasSpringOnlyFeatures) incompatibilityError({
6973
+ message: "Spring-only Java features require the Spring Boot scaffold with Maven or Gradle.",
6974
+ provided: {
6975
+ "java-web-framework": config.javaWebFramework ?? "none",
6976
+ "java-build-tool": config.javaBuildTool ?? "none",
6977
+ "java-orm": config.javaOrm ?? "none",
6978
+ "java-auth": config.javaAuth ?? "none",
6979
+ "java-libraries": (config.javaLibraries ?? []).join(" ") || "none"
6980
+ },
6981
+ 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"]
6982
+ });
6983
+ if (hasNoBuildTool && hasJavaTestingLibraries) incompatibilityError({
6984
+ message: "Java testing libraries require Maven or Gradle to manage test dependencies.",
6985
+ provided: {
6986
+ "java-build-tool": config.javaBuildTool ?? "none",
6987
+ "java-testing-libraries": (config.javaTestingLibraries ?? []).join(" ") || "none"
6988
+ },
6989
+ 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"]
6990
+ });
6991
+ }
5780
6992
  function validateShadcnConstraints(config, providedFlags) {
5781
6993
  const shadcnFlagMap = {
5782
6994
  shadcnBase: "--shadcn-base",
@@ -5809,6 +7021,7 @@ function validateFullConfig(config, providedFlags, options) {
5809
7021
  validateBackendConstraints(config, providedFlags, options);
5810
7022
  validateFrontendConstraints(config, providedFlags);
5811
7023
  /* @__PURE__ */ validateApiConstraints(config, options);
7024
+ validateJavaConstraints(config, providedFlags);
5812
7025
  validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
5813
7026
  validateSelfBackendCompatibility(providedFlags, options, config);
5814
7027
  validateWorkersCompatibility(providedFlags, options, config);
@@ -5844,6 +7057,7 @@ function validateConfigForProgrammaticUse(config) {
5844
7057
  validateDatabaseOrmAuth(config);
5845
7058
  if (config.frontend && config.frontend.length > 0) ensureSingleWebAndNative(config.frontend);
5846
7059
  validateApiFrontendCompatibility(config.api, config.frontend, config.astroIntegration);
7060
+ validateJavaConstraints(config);
5847
7061
  validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
5848
7062
  if (config.addons && config.addons.length > 0) validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
5849
7063
  validateExamplesCompatibility(config.examples ?? [], config.backend, config.frontend ?? [], config.runtime, config.ai);
@@ -6150,6 +7364,12 @@ async function setupMongoDBAtlas(config, cliInput) {
6150
7364
  displayManualSetupInstructions$4();
6151
7365
  return;
6152
7366
  }
7367
+ if (!canPromptInteractively()) {
7368
+ log.info("MongoDB Atlas manual setup selected (non-interactive mode)");
7369
+ await writeEnvFile$4(projectDir, backend);
7370
+ displayManualSetupInstructions$4();
7371
+ return;
7372
+ }
6153
7373
  const mode = await select({
6154
7374
  message: "MongoDB Atlas setup: choose mode",
6155
7375
  options: [{
@@ -6302,6 +7522,11 @@ async function setupNeonPostgres(config, cliInput) {
6302
7522
  displayManualSetupInstructions$3(backend === "self" ? "apps/web" : "apps/server");
6303
7523
  return;
6304
7524
  }
7525
+ if (!canPromptInteractively()) {
7526
+ await writeEnvFile$3(projectDir, backend);
7527
+ displayManualSetupInstructions$3(backend === "self" ? "apps/web" : "apps/server");
7528
+ return;
7529
+ }
6305
7530
  const mode = await select({
6306
7531
  message: "Neon setup: choose mode",
6307
7532
  options: [{
@@ -6346,7 +7571,7 @@ async function setupNeonPostgres(config, cliInput) {
6346
7571
  const regionId = await select({
6347
7572
  message: "Select a region for your Neon project:",
6348
7573
  options: NEON_REGIONS,
6349
- initialValue: NEON_REGIONS[0].value
7574
+ initialValue: NEON_REGIONS[0]?.value ?? "aws-us-east-1"
6350
7575
  });
6351
7576
  if (isCancel(projectName) || isCancel(regionId)) return exitCancelled("Operation cancelled");
6352
7577
  const neonConfig = await createNeonProject(projectName, regionId, packageManager);
@@ -6455,11 +7680,11 @@ const AVAILABLE_REGIONS = [
6455
7680
  async function setupWithCreateDb(serverDir, packageManager) {
6456
7681
  try {
6457
7682
  log.info("Starting Prisma Postgres setup with create-db.");
6458
- const selectedRegion = await select({
7683
+ const selectedRegion = canPromptInteractively() ? await select({
6459
7684
  message: "Select your preferred region:",
6460
7685
  options: AVAILABLE_REGIONS,
6461
7686
  initialValue: "ap-southeast-1"
6462
- });
7687
+ }) : "ap-southeast-1";
6463
7688
  if (isCancel(selectedRegion)) return null;
6464
7689
  const createDbArgs = getPackageExecutionArgs(packageManager, `create-db@latest --json --region ${selectedRegion}`);
6465
7690
  const s = spinner();
@@ -6522,6 +7747,11 @@ async function setupPrismaPostgres(config, cliInput) {
6522
7747
  displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
6523
7748
  return;
6524
7749
  }
7750
+ if (!canPromptInteractively()) {
7751
+ await writeEnvFile$2(projectDir, backend);
7752
+ displayManualSetupInstructions$2(backend === "self" ? "apps/web" : "apps/server");
7753
+ return;
7754
+ }
6525
7755
  const setupMode = await select({
6526
7756
  message: "Prisma Postgres setup: choose mode",
6527
7757
  options: [{
@@ -6657,6 +7887,11 @@ async function setupSupabase(config, cliInput) {
6657
7887
  await writeSupabaseEnvFile(projectDir, backend, "");
6658
7888
  return;
6659
7889
  }
7890
+ if (!canPromptInteractively()) {
7891
+ displayManualSupabaseInstructions();
7892
+ await writeSupabaseEnvFile(projectDir, backend, "");
7893
+ return;
7894
+ }
6660
7895
  const mode = await select({
6661
7896
  message: "Supabase setup: choose mode",
6662
7897
  options: [{
@@ -6846,6 +8081,11 @@ async function setupTurso(config, cliInput) {
6846
8081
  displayManualSetupInstructions$1();
6847
8082
  return;
6848
8083
  }
8084
+ if (!canPromptInteractively()) {
8085
+ await writeEnvFile$1(projectDir, backend);
8086
+ displayManualSetupInstructions$1();
8087
+ return;
8088
+ }
6849
8089
  const mode = await select({
6850
8090
  message: "Turso setup: choose mode",
6851
8091
  options: [{
@@ -6968,6 +8208,12 @@ async function setupUpstash(config, cliInput) {
6968
8208
  displayManualSetupInstructions();
6969
8209
  return;
6970
8210
  }
8211
+ if (!canPromptInteractively()) {
8212
+ log.info("Upstash Redis manual setup selected (non-interactive mode)");
8213
+ await writeEnvFile(projectDir, backend);
8214
+ displayManualSetupInstructions();
8215
+ return;
8216
+ }
6971
8217
  const mode = await select({
6972
8218
  message: "Upstash Redis setup: choose mode",
6973
8219
  options: [{
@@ -7176,6 +8422,10 @@ async function displayPostInstallInstructions(config) {
7176
8422
  displayGoInstructions(config);
7177
8423
  return;
7178
8424
  }
8425
+ if (ecosystem === "java") {
8426
+ displayJavaInstructions(config);
8427
+ return;
8428
+ }
7179
8429
  if (ecosystem === "python") {
7180
8430
  displayPythonInstructions(config);
7181
8431
  return;
@@ -7445,6 +8695,8 @@ function displayRustInstructions(config) {
7445
8695
  moka: "Moka",
7446
8696
  redis: "Redis"
7447
8697
  }[rustCaching] || rustCaching}\n`;
8698
+ const { rustAuth } = config;
8699
+ if (rustAuth && rustAuth !== "none") output += `${pc.cyan("•")} Auth: ${{ oauth2: "OAuth2" }[rustAuth] || rustAuth}\n`;
7448
8700
  output += `\n${pc.bold("Common Cargo commands:")}\n`;
7449
8701
  output += `${pc.cyan("•")} Build: cargo build\n`;
7450
8702
  output += `${pc.cyan("•")} Run: cargo run\n`;
@@ -7458,7 +8710,7 @@ function displayRustInstructions(config) {
7458
8710
  consola$1.box(output);
7459
8711
  }
7460
8712
  function displayGoInstructions(config) {
7461
- const { relativePath, depsInstalled, goWebFramework, goOrm, goApi, goCli, goLogging } = config;
8713
+ const { relativePath, depsInstalled, goWebFramework, goOrm, goApi, goCli, goLogging, goAuth } = config;
7462
8714
  const cdCmd = `cd ${relativePath}`;
7463
8715
  let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
7464
8716
  let stepCounter = 2;
@@ -7486,6 +8738,10 @@ function displayGoInstructions(config) {
7486
8738
  zerolog: "Zerolog",
7487
8739
  slog: "slog"
7488
8740
  }[goLogging] || goLogging}\n`;
8741
+ if (goAuth && goAuth !== "none") output += `${pc.cyan("•")} Auth: ${{
8742
+ casbin: "Casbin",
8743
+ jwt: "golang-jwt"
8744
+ }[goAuth] || goAuth}\n`;
7489
8745
  output += `\n${pc.bold("Common Go commands:")}\n`;
7490
8746
  output += `${pc.cyan("•")} Build: go build ./...\n`;
7491
8747
  output += `${pc.cyan("•")} Run: go run cmd/server/main.go\n`;
@@ -7501,6 +8757,143 @@ function displayGoInstructions(config) {
7501
8757
  output += pc.dim("Your star helps other developers discover the project.");
7502
8758
  consola$1.box(output);
7503
8759
  }
8760
+ const JAVA_GROUP_ID = "com.example";
8761
+ const JAVA_RESERVED_WORDS = new Set([
8762
+ "abstract",
8763
+ "assert",
8764
+ "boolean",
8765
+ "break",
8766
+ "byte",
8767
+ "case",
8768
+ "catch",
8769
+ "char",
8770
+ "class",
8771
+ "const",
8772
+ "continue",
8773
+ "default",
8774
+ "do",
8775
+ "double",
8776
+ "else",
8777
+ "enum",
8778
+ "extends",
8779
+ "final",
8780
+ "finally",
8781
+ "float",
8782
+ "for",
8783
+ "goto",
8784
+ "if",
8785
+ "implements",
8786
+ "import",
8787
+ "instanceof",
8788
+ "int",
8789
+ "interface",
8790
+ "long",
8791
+ "native",
8792
+ "new",
8793
+ "non-sealed",
8794
+ "package",
8795
+ "private",
8796
+ "protected",
8797
+ "public",
8798
+ "return",
8799
+ "sealed",
8800
+ "short",
8801
+ "static",
8802
+ "strictfp",
8803
+ "super",
8804
+ "switch",
8805
+ "synchronized",
8806
+ "this",
8807
+ "throw",
8808
+ "throws",
8809
+ "transient",
8810
+ "try",
8811
+ "void",
8812
+ "volatile",
8813
+ "while",
8814
+ "yield",
8815
+ "record",
8816
+ "permits",
8817
+ "true",
8818
+ "false",
8819
+ "null"
8820
+ ]);
8821
+ function sanitizeJavaPackageSuffix(projectName) {
8822
+ const alphanumericOnly = projectName.trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
8823
+ const withLetterPrefix = /^[a-z]/.test(alphanumericOnly) ? alphanumericOnly : `app${alphanumericOnly}`;
8824
+ return (JAVA_RESERVED_WORDS.has(withLetterPrefix) ? `app${withLetterPrefix}` : withLetterPrefix) || "app";
8825
+ }
8826
+ function getJavaMainClass(projectName) {
8827
+ return `${JAVA_GROUP_ID}.${sanitizeJavaPackageSuffix(projectName)}.Application`;
8828
+ }
8829
+ function getJavaMainSourcePath(projectName) {
8830
+ return `src/main/java/${getJavaMainClass(projectName).replace(/\./g, "/")}.java`;
8831
+ }
8832
+ function displayJavaInstructions(config) {
8833
+ const { projectName, relativePath, depsInstalled, javaWebFramework, javaBuildTool, javaOrm, javaAuth, javaLibraries, javaTestingLibraries } = config;
8834
+ const cdCmd = `cd ${relativePath}`;
8835
+ const isSpringBoot = javaWebFramework === "spring-boot" && javaBuildTool !== "none";
8836
+ const effectiveJavaLibraries = isSpringBoot ? javaLibraries.filter((library) => library !== "none") : [];
8837
+ const effectiveJavaTestingLibraries = javaBuildTool === "none" ? [] : javaTestingLibraries.filter((library) => library !== "none");
8838
+ const buildToolCommand = javaBuildTool === "none" ? null : javaBuildTool === "gradle" ? process.platform === "win32" ? "gradlew.bat" : "./gradlew" : process.platform === "win32" ? "mvnw.cmd" : "./mvnw";
8839
+ const runCommand = buildToolCommand ? isSpringBoot ? javaBuildTool === "gradle" ? `${buildToolCommand} bootRun` : `${buildToolCommand} spring-boot:run` : javaBuildTool === "gradle" ? `${buildToolCommand} run` : `${buildToolCommand} exec:java` : null;
8840
+ const packageCommand = buildToolCommand ? javaBuildTool === "gradle" ? `${buildToolCommand} build` : `${buildToolCommand} package` : null;
8841
+ const sourceCompileCommand = buildToolCommand ? null : `javac -d out ${getJavaMainSourcePath(projectName)}`;
8842
+ const sourceRunCommand = buildToolCommand ? null : `java -cp out ${getJavaMainClass(projectName)}`;
8843
+ let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
8844
+ let stepCounter = 2;
8845
+ if (!depsInstalled && buildToolCommand && effectiveJavaTestingLibraries.length > 0) output += `${pc.cyan(`${stepCounter++}.`)} ${buildToolCommand} test\n`;
8846
+ if (runCommand) output += `${pc.cyan(`${stepCounter++}.`)} ${runCommand}\n`;
8847
+ else if (sourceCompileCommand && sourceRunCommand) {
8848
+ output += `${pc.cyan(`${stepCounter++}.`)} ${sourceCompileCommand}\n`;
8849
+ output += `${pc.cyan(`${stepCounter++}.`)} ${sourceRunCommand}\n`;
8850
+ } else output += `${pc.cyan(`${stepCounter++}.`)} Add Maven or Gradle, then run the app\n`;
8851
+ output += `\n${pc.bold("Your Java project includes:")}\n`;
8852
+ if (isSpringBoot) output += `${pc.cyan("•")} Web Framework: ${{ "spring-boot": "Spring Boot" }[javaWebFramework] || javaWebFramework}\n`;
8853
+ else output += `${pc.cyan("•")} Scaffold: Plain Java\n`;
8854
+ if (javaBuildTool && javaBuildTool !== "none") output += `${pc.cyan("•")} Build Tool: ${{
8855
+ maven: "Maven Wrapper",
8856
+ gradle: "Gradle Wrapper"
8857
+ }[javaBuildTool] || javaBuildTool}\n`;
8858
+ else output += `${pc.cyan("•")} Build Tool: None\n`;
8859
+ if (isSpringBoot && javaOrm && javaOrm !== "none") output += `${pc.cyan("•")} ORM: ${{ "spring-data-jpa": "Spring Data JPA" }[javaOrm] || javaOrm}\n`;
8860
+ if (isSpringBoot && javaAuth && javaAuth !== "none") output += `${pc.cyan("•")} Auth: ${{ "spring-security": "Spring Security" }[javaAuth] || javaAuth}\n`;
8861
+ if (effectiveJavaLibraries.length > 0) {
8862
+ const libraryNames = {
8863
+ "spring-actuator": "Spring Actuator",
8864
+ "spring-validation": "Spring Validation",
8865
+ flyway: "Flyway"
8866
+ };
8867
+ const libraryList = effectiveJavaLibraries.map((library) => libraryNames[library] || library).join(", ");
8868
+ output += `${pc.cyan("•")} Libraries: ${libraryList}\n`;
8869
+ }
8870
+ if (effectiveJavaTestingLibraries.length > 0) {
8871
+ const testingNames = {
8872
+ junit5: "JUnit 5",
8873
+ mockito: "Mockito",
8874
+ testcontainers: "Testcontainers"
8875
+ };
8876
+ const testingList = effectiveJavaTestingLibraries.map((library) => testingNames[library] || library).join(", ");
8877
+ output += `${pc.cyan("•")} Testing: ${testingList}\n`;
8878
+ }
8879
+ output += `\n${pc.bold("Common Java commands:")}\n`;
8880
+ if (buildToolCommand && runCommand && packageCommand) {
8881
+ if (effectiveJavaTestingLibraries.length > 0) output += `${pc.cyan("•")} Test: ${buildToolCommand} test\n`;
8882
+ output += `${pc.cyan("•")} Run: ${runCommand}\n`;
8883
+ output += `${pc.cyan("•")} Package: ${packageCommand}\n`;
8884
+ } else if (sourceCompileCommand && sourceRunCommand) {
8885
+ output += `${pc.cyan("•")} Compile: ${sourceCompileCommand}\n`;
8886
+ output += `${pc.cyan("•")} Run: ${sourceRunCommand}\n`;
8887
+ } else output += `${pc.cyan("•")} Configure Maven or Gradle before running build commands\n`;
8888
+ if (isSpringBoot) {
8889
+ output += `\n${pc.bold("Your project will be available at:")}\n`;
8890
+ output += `${pc.cyan("•")} API: http://localhost:8080\n`;
8891
+ }
8892
+ output += `\n${pc.bold("Enjoying Better Fullstack?")} Help us grow — star the repo!\n`;
8893
+ output += `${pc.cyan("https://github.com/Marve10s/Better-Fullstack")}\n`;
8894
+ output += pc.dim("Your star helps other developers discover the project.");
8895
+ consola$1.box(output);
8896
+ }
7504
8897
  function displayPythonInstructions(config) {
7505
8898
  const { relativePath, depsInstalled, pythonWebFramework, pythonOrm, pythonValidation, pythonAi, pythonTaskQueue, pythonQuality } = config;
7506
8899
  const cdCmd = `cd ${relativePath}`;
@@ -7521,7 +8914,8 @@ function displayPythonInstructions(config) {
7521
8914
  }[pythonWebFramework] || pythonWebFramework}\n`;
7522
8915
  if (pythonOrm && pythonOrm !== "none") output += `${pc.cyan("•")} ORM: ${{
7523
8916
  sqlalchemy: "SQLAlchemy",
7524
- sqlmodel: "SQLModel"
8917
+ sqlmodel: "SQLModel",
8918
+ "tortoise-orm": "Tortoise ORM"
7525
8919
  }[pythonOrm] || pythonOrm}\n`;
7526
8920
  if (pythonValidation && pythonValidation !== "none") output += `${pc.cyan("•")} Validation: ${{ pydantic: "Pydantic" }[pythonValidation] || pythonValidation}\n`;
7527
8921
  if (pythonAi && pythonAi.length > 0 && !pythonAi.includes("none")) {
@@ -7566,6 +8960,7 @@ async function createProject(options, cliInput = {}) {
7566
8960
  if (!result.success || !result.tree) throw new Error(result.error || "Failed to generate project templates");
7567
8961
  await writeTreeToFilesystem(result.tree, projectDir);
7568
8962
  await setPackageManagerVersion(projectDir, options.packageManager);
8963
+ await ensurePackageManagerProjectFiles(projectDir, options.packageManager);
7569
8964
  if (!isConvex && options.database !== "none") await setupDatabase(options, cliInput);
7570
8965
  if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
7571
8966
  await applyDependencyVersionChannel(projectDir, options.versionChannel);
@@ -7579,6 +8974,8 @@ async function createProject(options, cliInput = {}) {
7579
8974
  if (options.install && options.ecosystem === "rust") await runCargoBuild({ projectDir });
7580
8975
  if (options.install && options.ecosystem === "python") await runUvSync({ projectDir });
7581
8976
  if (options.install && options.ecosystem === "go") await runGoModTidy({ projectDir });
8977
+ if (options.install && options.ecosystem === "java" && options.javaBuildTool !== "none") if (options.javaBuildTool === "gradle") await runGradleTests({ projectDir });
8978
+ else await runMavenTests({ projectDir });
7582
8979
  await initializeGit(projectDir, options.git);
7583
8980
  if (!isSilent()) await displayPostInstallInstructions({
7584
8981
  ...options,
@@ -7610,12 +9007,18 @@ async function setPackageManagerVersion(projectDir, packageManager) {
7610
9007
  await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
7611
9008
  }
7612
9009
  }
9010
+ async function ensurePackageManagerProjectFiles(projectDir, packageManager) {
9011
+ if (packageManager !== "yarn") return;
9012
+ const yarnLockPath = path.join(projectDir, "yarn.lock");
9013
+ if (await fs.pathExists(yarnLockPath)) return;
9014
+ await fs.writeFile(yarnLockPath, "");
9015
+ }
7613
9016
 
7614
9017
  //#endregion
7615
9018
  //#region src/helpers/core/command-handlers.ts
7616
9019
  function shouldPromptForVersionChannel(input) {
7617
9020
  if (input.yes || input.versionChannel !== void 0 || isSilent()) return false;
7618
- return process.stdin.isTTY === true && process.stdout.isTTY === true && process.env.CI !== "true";
9021
+ return canPromptInteractively();
7619
9022
  }
7620
9023
  async function createProjectHandler(input, options = {}) {
7621
9024
  const { silent = false } = options;
@@ -7707,6 +9110,7 @@ async function createProjectHandler(input, options = {}) {
7707
9110
  rustLogging: "none",
7708
9111
  rustErrorHandling: "none",
7709
9112
  rustCaching: "none",
9113
+ rustAuth: "none",
7710
9114
  cms: "none",
7711
9115
  caching: "none",
7712
9116
  i18n: "none",
@@ -7727,6 +9131,13 @@ async function createProjectHandler(input, options = {}) {
7727
9131
  goApi: "none",
7728
9132
  goCli: "none",
7729
9133
  goLogging: "none",
9134
+ goAuth: "none",
9135
+ javaWebFramework: "none",
9136
+ javaBuildTool: "none",
9137
+ javaOrm: "none",
9138
+ javaAuth: "none",
9139
+ javaLibraries: [],
9140
+ javaTestingLibraries: [],
7730
9141
  aiDocs: []
7731
9142
  },
7732
9143
  reproducibleCommand: "",
@@ -7996,88 +9407,7 @@ const router = os.router({
7996
9407
  description: "Scaffold a new Better Fullstack project from 270+ compatible stack options",
7997
9408
  default: true,
7998
9409
  negateBooleans: true
7999
- }).input(z.tuple([types_exports.ProjectNameSchema.optional(), z.object({
8000
- template: types_exports.TemplateSchema.optional().describe("Use a predefined template"),
8001
- yes: z.boolean().optional().default(false).describe("Use default configuration"),
8002
- yolo: z.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
8003
- verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
8004
- dryRun: z.boolean().optional().default(false).describe("Preview generated file tree without writing to disk"),
8005
- ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript, rust, python, or go)"),
8006
- database: types_exports.DatabaseSchema.optional(),
8007
- orm: types_exports.ORMSchema.optional(),
8008
- auth: types_exports.AuthSchema.optional(),
8009
- payments: types_exports.PaymentsSchema.optional(),
8010
- email: types_exports.EmailSchema.optional(),
8011
- fileUpload: types_exports.FileUploadSchema.optional(),
8012
- effect: types_exports.EffectSchema.optional(),
8013
- stateManagement: types_exports.StateManagementSchema.optional(),
8014
- validation: types_exports.ValidationSchema.optional(),
8015
- forms: types_exports.FormsSchema.optional(),
8016
- testing: types_exports.TestingSchema.optional(),
8017
- ai: types_exports.AISchema.optional(),
8018
- realtime: types_exports.RealtimeSchema.optional(),
8019
- jobQueue: types_exports.JobQueueSchema.optional(),
8020
- animation: types_exports.AnimationSchema.optional(),
8021
- logging: types_exports.LoggingSchema.optional(),
8022
- observability: types_exports.ObservabilitySchema.optional(),
8023
- featureFlags: types_exports.FeatureFlagsSchema.optional().describe("Feature flags provider"),
8024
- analytics: types_exports.AnalyticsSchema.optional().describe("Privacy-focused analytics"),
8025
- cms: types_exports.CMSSchema.optional().describe("Headless CMS solution"),
8026
- caching: types_exports.CachingSchema.optional().describe("Caching solution"),
8027
- i18n: types_exports.I18nSchema.optional().describe("Internationalization (i18n) library"),
8028
- search: types_exports.SearchSchema.optional().describe("Search engine solution"),
8029
- fileStorage: types_exports.FileStorageSchema.optional().describe("File storage solution (S3, R2)"),
8030
- frontend: z.array(types_exports.FrontendSchema).optional(),
8031
- astroIntegration: types_exports.AstroIntegrationSchema.optional().describe("Astro UI framework integration (react, vue, svelte, solid)"),
8032
- addons: z.array(types_exports.AddonsSchema).optional(),
8033
- examples: z.array(types_exports.ExamplesSchema).optional(),
8034
- git: z.boolean().optional(),
8035
- packageManager: types_exports.PackageManagerSchema.optional(),
8036
- install: z.boolean().optional(),
8037
- versionChannel: types_exports.VersionChannelSchema.optional().describe("Dependency version channel (stable, latest, beta)"),
8038
- dbSetup: types_exports.DatabaseSetupSchema.optional(),
8039
- backend: types_exports.BackendSchema.optional(),
8040
- runtime: types_exports.RuntimeSchema.optional(),
8041
- api: types_exports.APISchema.optional(),
8042
- cssFramework: types_exports.CSSFrameworkSchema.optional(),
8043
- uiLibrary: types_exports.UILibrarySchema.optional(),
8044
- shadcnBase: types_exports.ShadcnBaseSchema.optional().describe("shadcn/ui headless library (radix, base)"),
8045
- shadcnStyle: types_exports.ShadcnStyleSchema.optional().describe("shadcn/ui visual style (vega, nova, maia, lyra, mira)"),
8046
- shadcnIconLibrary: types_exports.ShadcnIconLibrarySchema.optional().describe("shadcn/ui icon library (lucide, tabler, hugeicons, phosphor, remixicon)"),
8047
- shadcnColorTheme: types_exports.ShadcnColorThemeSchema.optional().describe("shadcn/ui color theme (neutral, blue, violet, etc.)"),
8048
- shadcnBaseColor: types_exports.ShadcnBaseColorSchema.optional().describe("shadcn/ui base neutral color (neutral, stone, zinc, gray)"),
8049
- shadcnFont: types_exports.ShadcnFontSchema.optional().describe("shadcn/ui font (inter, geist, figtree, etc.)"),
8050
- shadcnRadius: types_exports.ShadcnRadiusSchema.optional().describe("shadcn/ui border radius (default, none, small, medium, large)"),
8051
- webDeploy: types_exports.WebDeploySchema.optional(),
8052
- serverDeploy: types_exports.ServerDeploySchema.optional(),
8053
- directoryConflict: types_exports.DirectoryConflictSchema.optional(),
8054
- renderTitle: z.boolean().optional(),
8055
- disableAnalytics: z.boolean().optional().default(false).describe("Disable analytics"),
8056
- manualDb: z.boolean().optional().default(false).describe("Skip automatic/manual database setup prompt and use manual setup"),
8057
- rustWebFramework: types_exports.RustWebFrameworkSchema.optional().describe("Rust web framework (axum, actix-web)"),
8058
- rustFrontend: types_exports.RustFrontendSchema.optional().describe("Rust WASM frontend (leptos, dioxus)"),
8059
- rustOrm: types_exports.RustOrmSchema.optional().describe("Rust ORM/database (sea-orm, sqlx)"),
8060
- rustApi: types_exports.RustApiSchema.optional().describe("Rust API layer (tonic, async-graphql)"),
8061
- rustCli: types_exports.RustCliSchema.optional().describe("Rust CLI tools (clap, ratatui)"),
8062
- rustLibraries: z.array(types_exports.RustLibrariesSchema).optional().describe("Rust core libraries"),
8063
- rustLogging: types_exports.RustLoggingSchema.optional().describe("Rust logging (tracing, env-logger)"),
8064
- rustErrorHandling: types_exports.RustErrorHandlingSchema.optional().describe("Rust error handling (anyhow-thiserror, eyre)"),
8065
- rustCaching: types_exports.RustCachingSchema.optional().describe("Rust caching (moka, redis)"),
8066
- pythonWebFramework: types_exports.PythonWebFrameworkSchema.optional().describe("Python web framework (fastapi, django)"),
8067
- pythonOrm: types_exports.PythonOrmSchema.optional().describe("Python ORM/database (sqlalchemy, sqlmodel)"),
8068
- pythonValidation: types_exports.PythonValidationSchema.optional().describe("Python validation (pydantic)"),
8069
- pythonAi: z.array(types_exports.PythonAiSchema).optional().describe("Python AI/ML frameworks"),
8070
- pythonAuth: types_exports.PythonAuthSchema.optional().describe("Python auth library (authlib, jwt)"),
8071
- pythonTaskQueue: types_exports.PythonTaskQueueSchema.optional().describe("Python task queue (celery)"),
8072
- pythonGraphql: types_exports.PythonGraphqlSchema.optional().describe("Python GraphQL framework (strawberry)"),
8073
- pythonQuality: types_exports.PythonQualitySchema.optional().describe("Python code quality (ruff)"),
8074
- goWebFramework: types_exports.GoWebFrameworkSchema.optional().describe("Go web framework (gin, echo, fiber)"),
8075
- goOrm: types_exports.GoOrmSchema.optional().describe("Go ORM/database (gorm, sqlc)"),
8076
- goApi: types_exports.GoApiSchema.optional().describe("Go API layer (grpc-go)"),
8077
- goCli: types_exports.GoCliSchema.optional().describe("Go CLI tools (cobra, bubbletea)"),
8078
- goLogging: types_exports.GoLoggingSchema.optional().describe("Go logging (zap, zerolog, slog)"),
8079
- aiDocs: z.array(types_exports.AiDocsSchema).optional().describe("AI documentation files (claude-md, agents-md, cursorrules)")
8080
- })])).handler(async ({ input }) => {
9410
+ }).input(CreateCommandInputSchema).handler(async ({ input }) => {
8081
9411
  const [projectName, options] = input;
8082
9412
  const result = await createProjectHandler({
8083
9413
  projectName,
@@ -8309,6 +9639,7 @@ async function createVirtual(options) {
8309
9639
  rustLogging: options.rustLogging || (options.ecosystem === "rust" ? "tracing" : "none"),
8310
9640
  rustErrorHandling: options.rustErrorHandling || (options.ecosystem === "rust" ? "anyhow-thiserror" : "none"),
8311
9641
  rustCaching: options.rustCaching || "none",
9642
+ rustAuth: options.rustAuth || "none",
8312
9643
  pythonWebFramework: options.pythonWebFramework || "none",
8313
9644
  pythonOrm: options.pythonOrm || "none",
8314
9645
  pythonValidation: options.pythonValidation || "none",
@@ -8322,6 +9653,13 @@ async function createVirtual(options) {
8322
9653
  goApi: options.goApi || "none",
8323
9654
  goCli: options.goCli || "none",
8324
9655
  goLogging: options.goLogging || "none",
9656
+ goAuth: options.goAuth || "none",
9657
+ javaWebFramework: options.javaWebFramework || (options.ecosystem === "java" ? "spring-boot" : "none"),
9658
+ javaBuildTool: options.javaBuildTool || (options.ecosystem === "java" ? "maven" : "none"),
9659
+ javaOrm: options.javaOrm || "none",
9660
+ javaAuth: options.javaAuth || "none",
9661
+ javaLibraries: options.javaLibraries || [],
9662
+ javaTestingLibraries: options.javaTestingLibraries || (options.ecosystem === "java" ? ["junit5"] : []),
8325
9663
  aiDocs: options.aiDocs || ["claude-md"]
8326
9664
  },
8327
9665
  templates: EMBEDDED_TEMPLATES$1