create-better-fullstack 1.7.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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-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-DQa6TRrx.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-bOXo9tbL.mjs";
4
+ import { _ as setIsFirstPrompt$1, a as canPromptInteractively, c as CLIError, d as exitWithError, f as handleError, g as runWithContextAsync, h as isSilent, l as UserCancelledError, m as isFirstPrompt, o as getPackageExecutionArgs, p as didLastPromptShowUI, s as addPackageDependency, t as setupAddons, u as exitCancelled, v as setLastPromptShownUI } from "./addons-setup-CGhYT2qC.mjs";
5
5
  import { cancel, confirm, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
6
6
  import { createRouterClient, os } from "@orpc/server";
7
7
  import pc from "picocolors";
@@ -430,16 +430,17 @@ const CreateCommandOptionsSchema = z.object({
430
430
  pythonValidation: types_exports.PythonValidationSchema.optional().describe("Python validation (pydantic)"),
431
431
  pythonAi: z.array(types_exports.PythonAiSchema).optional().describe("Python AI/ML frameworks"),
432
432
  pythonAuth: types_exports.PythonAuthSchema.optional().describe("Python auth library (authlib, jwt)"),
433
+ pythonApi: types_exports.PythonApiSchema.optional().describe("Python API framework (django-rest-framework, django-ninja)"),
433
434
  pythonTaskQueue: types_exports.PythonTaskQueueSchema.optional().describe("Python task queue (celery)"),
434
435
  pythonGraphql: types_exports.PythonGraphqlSchema.optional().describe("Python GraphQL framework (strawberry)"),
435
- pythonQuality: types_exports.PythonQualitySchema.optional().describe("Python code quality (ruff)"),
436
+ pythonQuality: types_exports.PythonQualitySchema.optional().describe("Python code quality (ruff, mypy, pyright)"),
436
437
  goWebFramework: types_exports.GoWebFrameworkSchema.optional().describe("Go web framework (gin, echo, fiber)"),
437
438
  goOrm: types_exports.GoOrmSchema.optional().describe("Go ORM/database (gorm, sqlc)"),
438
439
  goApi: types_exports.GoApiSchema.optional().describe("Go API layer (grpc-go)"),
439
- goCli: types_exports.GoCliSchema.optional().describe("Go CLI tools (cobra, bubbletea)"),
440
+ goCli: types_exports.GoCliSchema.optional().describe("Go CLI tools (cobra, bubbletea, urfave-cli)"),
440
441
  goLogging: types_exports.GoLoggingSchema.optional().describe("Go logging (zap, zerolog, slog)"),
441
442
  goAuth: types_exports.GoAuthSchema.optional().describe("Go auth (casbin, jwt)"),
442
- javaWebFramework: types_exports.JavaWebFrameworkSchema.optional().describe("Java web framework (spring-boot, none)"),
443
+ javaWebFramework: types_exports.JavaWebFrameworkSchema.optional().describe("Java web framework (spring-boot, quarkus, none)"),
443
444
  javaBuildTool: types_exports.JavaBuildToolSchema.optional().describe("Java build tool (maven, gradle, none)"),
444
445
  javaOrm: types_exports.JavaOrmSchema.optional().describe("Java ORM/database (spring-data-jpa)"),
445
446
  javaAuth: types_exports.JavaAuthSchema.optional().describe("Java auth (spring-security)"),
@@ -2178,6 +2179,17 @@ const CACHING_PROMPT_OPTIONS = [{
2178
2179
  hint: "Skip caching layer setup"
2179
2180
  }];
2180
2181
  function resolveCachingPrompt(context = {}) {
2182
+ if (context.ecosystem && context.ecosystem !== "typescript") return context.caching !== void 0 ? {
2183
+ shouldPrompt: false,
2184
+ mode: "single",
2185
+ options: CACHING_PROMPT_OPTIONS,
2186
+ autoValue: context.caching
2187
+ } : {
2188
+ shouldPrompt: true,
2189
+ mode: "single",
2190
+ options: CACHING_PROMPT_OPTIONS,
2191
+ initialValue: "none"
2192
+ };
2181
2193
  if (context.backend === "none" || context.backend === "convex") return {
2182
2194
  shouldPrompt: false,
2183
2195
  mode: "single",
@@ -2196,10 +2208,11 @@ function resolveCachingPrompt(context = {}) {
2196
2208
  initialValue: "none"
2197
2209
  };
2198
2210
  }
2199
- async function getCachingChoice(caching, backend) {
2211
+ async function getCachingChoice(caching, backend, ecosystem) {
2200
2212
  const resolution = resolveCachingPrompt({
2201
2213
  caching,
2202
- backend
2214
+ backend,
2215
+ ecosystem
2203
2216
  });
2204
2217
  if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2205
2218
  const response = await navigableSelect({
@@ -2678,8 +2691,10 @@ const EMAIL_PROMPT_OPTIONS = [
2678
2691
  hint: "No email integration"
2679
2692
  }
2680
2693
  ];
2694
+ const NON_TYPESCRIPT_EMAIL_PROMPT_OPTIONS = EMAIL_PROMPT_OPTIONS.filter((option) => option.value === "resend" || option.value === "none");
2681
2695
  function resolveEmailPrompt(context = {}) {
2682
- if (context.backend === "none" || context.backend === "convex") return {
2696
+ const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_EMAIL_PROMPT_OPTIONS : EMAIL_PROMPT_OPTIONS;
2697
+ if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
2683
2698
  shouldPrompt: false,
2684
2699
  mode: "single",
2685
2700
  options: [],
@@ -2688,19 +2703,20 @@ function resolveEmailPrompt(context = {}) {
2688
2703
  return context.email !== void 0 ? {
2689
2704
  shouldPrompt: false,
2690
2705
  mode: "single",
2691
- options: EMAIL_PROMPT_OPTIONS,
2706
+ options,
2692
2707
  autoValue: context.email
2693
2708
  } : {
2694
2709
  shouldPrompt: true,
2695
2710
  mode: "single",
2696
- options: EMAIL_PROMPT_OPTIONS,
2711
+ options,
2697
2712
  initialValue: DEFAULT_CONFIG.email ?? "none"
2698
2713
  };
2699
2714
  }
2700
- async function getEmailChoice(email, backend) {
2715
+ async function getEmailChoice(email, backend, ecosystem) {
2701
2716
  const resolution = resolveEmailPrompt({
2702
2717
  email,
2703
- backend
2718
+ backend,
2719
+ ecosystem
2704
2720
  });
2705
2721
  if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
2706
2722
  const response = await navigableSelect({
@@ -3169,6 +3185,11 @@ const GO_CLI_PROMPT_OPTIONS = [
3169
3185
  label: "Bubble Tea",
3170
3186
  hint: "Powerful TUI framework based on The Elm Architecture"
3171
3187
  },
3188
+ {
3189
+ value: "urfave-cli",
3190
+ label: "urfave/cli",
3191
+ hint: "Declarative CLI framework with commands, flags, and shell completion"
3192
+ },
3172
3193
  {
3173
3194
  value: "none",
3174
3195
  label: "None",
@@ -3191,6 +3212,11 @@ const GO_LOGGING_PROMPT_OPTIONS = [
3191
3212
  label: "slog",
3192
3213
  hint: "Go 1.21+ stdlib structured logging (no external dependency)"
3193
3214
  },
3215
+ {
3216
+ value: "logrus",
3217
+ label: "Logrus",
3218
+ hint: "Classic structured logger with hooks and formatter ecosystem"
3219
+ },
3194
3220
  {
3195
3221
  value: "none",
3196
3222
  label: "None",
@@ -3405,15 +3431,23 @@ async function getinstallChoice(install, ecosystem, javaBuildTool) {
3405
3431
 
3406
3432
  //#endregion
3407
3433
  //#region src/prompts/java-ecosystem.ts
3408
- const JAVA_WEB_FRAMEWORK_PROMPT_OPTIONS = [{
3409
- value: "spring-boot",
3410
- label: "Spring Boot",
3411
- hint: "Production-grade Java framework with embedded server and auto-configuration"
3412
- }, {
3413
- value: "none",
3414
- label: "None",
3415
- hint: "No Java web framework"
3416
- }];
3434
+ const JAVA_WEB_FRAMEWORK_PROMPT_OPTIONS = [
3435
+ {
3436
+ value: "spring-boot",
3437
+ label: "Spring Boot",
3438
+ hint: "Production-grade Java framework with embedded server and auto-configuration"
3439
+ },
3440
+ {
3441
+ value: "quarkus",
3442
+ label: "Quarkus",
3443
+ hint: "Cloud-native Java framework optimized for fast startup and lower memory use"
3444
+ },
3445
+ {
3446
+ value: "none",
3447
+ label: "None",
3448
+ hint: "No Java web framework"
3449
+ }
3450
+ ];
3417
3451
  const JAVA_BUILD_TOOL_PROMPT_OPTIONS = [
3418
3452
  {
3419
3453
  value: "maven",
@@ -3542,6 +3576,46 @@ const JAVA_LIBRARY_PROMPT_OPTIONS = [
3542
3576
  label: "Caffeine",
3543
3577
  hint: "High-performance in-memory caching through Spring Cache"
3544
3578
  },
3579
+ {
3580
+ value: "resilience4j",
3581
+ label: "Resilience4j",
3582
+ hint: "Fault tolerance patterns for retries, circuit breakers, and rate limiting"
3583
+ },
3584
+ {
3585
+ value: "spring-webflux",
3586
+ label: "Spring WebFlux",
3587
+ hint: "Reactive HTTP stack for Spring applications"
3588
+ },
3589
+ {
3590
+ value: "spring-batch",
3591
+ label: "Spring Batch",
3592
+ hint: "Batch processing framework for ETL and scheduled jobs"
3593
+ },
3594
+ {
3595
+ value: "spring-kafka",
3596
+ label: "Spring for Apache Kafka",
3597
+ hint: "Kafka producer, consumer, and listener integration"
3598
+ },
3599
+ {
3600
+ value: "spring-mail",
3601
+ label: "Spring Mail",
3602
+ hint: "Email support through Jakarta Mail and Spring abstractions"
3603
+ },
3604
+ {
3605
+ value: "spring-devtools",
3606
+ label: "Spring Boot DevTools",
3607
+ hint: "Developer-time restart and local productivity support"
3608
+ },
3609
+ {
3610
+ value: "micrometer-prometheus",
3611
+ label: "Micrometer Prometheus",
3612
+ hint: "Prometheus metrics registry for Micrometer and Actuator"
3613
+ },
3614
+ {
3615
+ value: "thymeleaf",
3616
+ label: "Thymeleaf",
3617
+ hint: "Server-rendered HTML templates for Spring MVC apps"
3618
+ },
3545
3619
  {
3546
3620
  value: "none",
3547
3621
  label: "None",
@@ -3834,8 +3908,10 @@ const OBSERVABILITY_PROMPT_OPTIONS = [
3834
3908
  hint: "Skip observability/tracing setup"
3835
3909
  }
3836
3910
  ];
3911
+ const NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS = OBSERVABILITY_PROMPT_OPTIONS.filter((option) => option.value === "sentry" || option.value === "none");
3837
3912
  function resolveObservabilityPrompt(context = {}) {
3838
- if (context.backend === "none" || context.backend === "convex") return {
3913
+ const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_OBSERVABILITY_PROMPT_OPTIONS : OBSERVABILITY_PROMPT_OPTIONS;
3914
+ if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
3839
3915
  shouldPrompt: false,
3840
3916
  mode: "single",
3841
3917
  options: [],
@@ -3844,19 +3920,20 @@ function resolveObservabilityPrompt(context = {}) {
3844
3920
  return context.observability !== void 0 ? {
3845
3921
  shouldPrompt: false,
3846
3922
  mode: "single",
3847
- options: OBSERVABILITY_PROMPT_OPTIONS,
3923
+ options,
3848
3924
  autoValue: context.observability
3849
3925
  } : {
3850
3926
  shouldPrompt: true,
3851
3927
  mode: "single",
3852
- options: OBSERVABILITY_PROMPT_OPTIONS,
3928
+ options,
3853
3929
  initialValue: "none"
3854
3930
  };
3855
3931
  }
3856
- async function getObservabilityChoice(observability, backend) {
3932
+ async function getObservabilityChoice(observability, backend, ecosystem) {
3857
3933
  const resolution = resolveObservabilityPrompt({
3858
3934
  observability,
3859
- backend
3935
+ backend,
3936
+ ecosystem
3860
3937
  });
3861
3938
  if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
3862
3939
  const response = await navigableSelect({
@@ -4154,6 +4231,11 @@ const PYTHON_AI_PROMPT_OPTIONS = [
4154
4231
  value: "crewai",
4155
4232
  label: "CrewAI",
4156
4233
  hint: "Multi-agent orchestration framework"
4234
+ },
4235
+ {
4236
+ value: "haystack",
4237
+ label: "Haystack",
4238
+ hint: "Composable LLM pipelines, RAG, and search applications"
4157
4239
  }
4158
4240
  ];
4159
4241
  const PYTHON_AUTH_PROMPT_OPTIONS = [
@@ -4173,33 +4255,89 @@ const PYTHON_AUTH_PROMPT_OPTIONS = [
4173
4255
  hint: "No authentication library"
4174
4256
  }
4175
4257
  ];
4176
- const PYTHON_TASK_QUEUE_PROMPT_OPTIONS = [{
4177
- value: "celery",
4178
- label: "Celery",
4179
- hint: "Distributed task queue for Python"
4180
- }, {
4181
- value: "none",
4182
- label: "None",
4183
- hint: "No task queue"
4184
- }];
4185
- const PYTHON_GRAPHQL_PROMPT_OPTIONS = [{
4186
- value: "strawberry",
4187
- label: "Strawberry",
4188
- hint: "Python GraphQL library using dataclasses and type hints"
4189
- }, {
4190
- value: "none",
4191
- label: "None",
4192
- hint: "No GraphQL framework"
4193
- }];
4194
- const PYTHON_QUALITY_PROMPT_OPTIONS = [{
4195
- value: "ruff",
4196
- label: "Ruff",
4197
- hint: "An extremely fast Python linter and formatter"
4198
- }, {
4199
- value: "none",
4200
- label: "None",
4201
- hint: "No code quality tools"
4202
- }];
4258
+ const PYTHON_API_PROMPT_OPTIONS = [
4259
+ {
4260
+ value: "django-rest-framework",
4261
+ label: "Django REST Framework",
4262
+ hint: "Mature, widely used toolkit for building Django REST APIs"
4263
+ },
4264
+ {
4265
+ value: "django-ninja",
4266
+ label: "Django Ninja",
4267
+ hint: "FastAPI-style Django APIs with type hints and automatic OpenAPI docs"
4268
+ },
4269
+ {
4270
+ value: "none",
4271
+ label: "None",
4272
+ hint: "No additional Python API framework"
4273
+ }
4274
+ ];
4275
+ const PYTHON_TASK_QUEUE_PROMPT_OPTIONS = [
4276
+ {
4277
+ value: "celery",
4278
+ label: "Celery",
4279
+ hint: "Distributed task queue for Python"
4280
+ },
4281
+ {
4282
+ value: "rq",
4283
+ label: "RQ",
4284
+ hint: "Simple Redis-backed job queue for Python"
4285
+ },
4286
+ {
4287
+ value: "dramatiq",
4288
+ label: "Dramatiq",
4289
+ hint: "Distributed task processing with Redis or RabbitMQ brokers"
4290
+ },
4291
+ {
4292
+ value: "huey",
4293
+ label: "Huey",
4294
+ hint: "Lightweight task queue with Redis-backed scheduling"
4295
+ },
4296
+ {
4297
+ value: "none",
4298
+ label: "None",
4299
+ hint: "No task queue"
4300
+ }
4301
+ ];
4302
+ const PYTHON_GRAPHQL_PROMPT_OPTIONS = [
4303
+ {
4304
+ value: "strawberry",
4305
+ label: "Strawberry",
4306
+ hint: "Python GraphQL library using dataclasses and type hints"
4307
+ },
4308
+ {
4309
+ value: "ariadne",
4310
+ label: "Ariadne",
4311
+ hint: "Schema-first GraphQL server library for Python"
4312
+ },
4313
+ {
4314
+ value: "none",
4315
+ label: "None",
4316
+ hint: "No GraphQL framework"
4317
+ }
4318
+ ];
4319
+ const PYTHON_QUALITY_PROMPT_OPTIONS = [
4320
+ {
4321
+ value: "ruff",
4322
+ label: "Ruff",
4323
+ hint: "An extremely fast Python linter and formatter"
4324
+ },
4325
+ {
4326
+ value: "mypy",
4327
+ label: "mypy",
4328
+ hint: "Static type checker for Python"
4329
+ },
4330
+ {
4331
+ value: "pyright",
4332
+ label: "Pyright",
4333
+ hint: "Fast Python type checker from Microsoft"
4334
+ },
4335
+ {
4336
+ value: "none",
4337
+ label: "None",
4338
+ hint: "No code quality tools"
4339
+ }
4340
+ ];
4203
4341
  function resolvePythonWebFrameworkPrompt(pythonWebFramework) {
4204
4342
  return createStaticSinglePromptResolution(PYTHON_WEB_FRAMEWORK_PROMPT_OPTIONS, "fastapi", pythonWebFramework);
4205
4343
  }
@@ -4272,6 +4410,20 @@ async function getPythonAuthChoice(pythonAuth) {
4272
4410
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
4273
4411
  return response;
4274
4412
  }
4413
+ function resolvePythonApiPrompt(pythonApi) {
4414
+ return createStaticSinglePromptResolution(PYTHON_API_PROMPT_OPTIONS, "none", pythonApi);
4415
+ }
4416
+ async function getPythonApiChoice(pythonApi) {
4417
+ const resolution = resolvePythonApiPrompt(pythonApi);
4418
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
4419
+ const response = await navigableSelect({
4420
+ message: "Select Python API framework",
4421
+ options: resolution.options,
4422
+ initialValue: resolution.initialValue
4423
+ });
4424
+ if (isCancel$1(response)) return exitCancelled("Operation cancelled");
4425
+ return response;
4426
+ }
4275
4427
  function resolvePythonTaskQueuePrompt(pythonTaskQueue) {
4276
4428
  return createStaticSinglePromptResolution(PYTHON_TASK_QUEUE_PROMPT_OPTIONS, "none", pythonTaskQueue);
4277
4429
  }
@@ -4545,6 +4697,51 @@ const RUST_LIBRARIES_PROMPT_OPTIONS = [
4545
4697
  label: "Serde",
4546
4698
  hint: "Serialization framework for Rust"
4547
4699
  },
4700
+ {
4701
+ value: "uuid",
4702
+ label: "uuid",
4703
+ hint: "UUID generation and parsing with Serde support"
4704
+ },
4705
+ {
4706
+ value: "chrono",
4707
+ label: "Chrono",
4708
+ hint: "Date and time library with Serde support"
4709
+ },
4710
+ {
4711
+ value: "reqwest",
4712
+ label: "Reqwest",
4713
+ hint: "Ergonomic HTTP client with JSON and Rustls TLS support"
4714
+ },
4715
+ {
4716
+ value: "config",
4717
+ label: "config",
4718
+ hint: "Layered configuration from files, environment, and defaults"
4719
+ },
4720
+ {
4721
+ value: "dashmap",
4722
+ label: "DashMap",
4723
+ hint: "Concurrent hash map for shared mutable state"
4724
+ },
4725
+ {
4726
+ value: "parking-lot",
4727
+ label: "parking_lot",
4728
+ hint: "Compact, fast synchronization primitives"
4729
+ },
4730
+ {
4731
+ value: "secrecy",
4732
+ label: "Secrecy",
4733
+ hint: "Secret value wrapper that avoids accidental exposure"
4734
+ },
4735
+ {
4736
+ value: "tokio-util",
4737
+ label: "Tokio Util",
4738
+ hint: "Tokio utilities for codecs, cancellation tokens, and IO helpers"
4739
+ },
4740
+ {
4741
+ value: "utoipa",
4742
+ label: "utoipa",
4743
+ hint: "OpenAPI documentation generation from Rust types"
4744
+ },
4548
4745
  {
4549
4746
  value: "validator",
4550
4747
  label: "Validator",
@@ -4569,6 +4766,16 @@ const RUST_LIBRARIES_PROMPT_OPTIONS = [
4569
4766
  value: "mockall",
4570
4767
  label: "Mockall",
4571
4768
  hint: "Powerful mocking library for Rust"
4769
+ },
4770
+ {
4771
+ value: "proptest",
4772
+ label: "Proptest",
4773
+ hint: "Property-based testing for Rust"
4774
+ },
4775
+ {
4776
+ value: "insta",
4777
+ label: "Insta",
4778
+ hint: "Snapshot testing for Rust"
4572
4779
  }
4573
4780
  ];
4574
4781
  const RUST_LOGGING_PROMPT_OPTIONS = [
@@ -4770,39 +4977,65 @@ async function getRustAuthChoice(rustAuth) {
4770
4977
 
4771
4978
  //#endregion
4772
4979
  //#region src/prompts/search.ts
4773
- async function getSearchChoice(search, backend) {
4774
- if (search !== void 0) return search;
4775
- if (backend === "none" || backend === "convex") return "none";
4980
+ const SEARCH_PROMPT_OPTIONS = [
4981
+ {
4982
+ value: "meilisearch",
4983
+ label: "Meilisearch",
4984
+ hint: "Lightning-fast search engine with typo tolerance"
4985
+ },
4986
+ {
4987
+ value: "typesense",
4988
+ label: "Typesense",
4989
+ hint: "Fast, typo-tolerant search with built-in vector search"
4990
+ },
4991
+ {
4992
+ value: "elasticsearch",
4993
+ label: "Elasticsearch",
4994
+ hint: "Distributed search and analytics engine with local and cloud deployments"
4995
+ },
4996
+ {
4997
+ value: "algolia",
4998
+ label: "Algolia",
4999
+ hint: "Hosted search API with instant results, typo tolerance, and analytics"
5000
+ },
5001
+ {
5002
+ value: "none",
5003
+ label: "None",
5004
+ hint: "Skip search engine setup"
5005
+ }
5006
+ ];
5007
+ const NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS = SEARCH_PROMPT_OPTIONS.filter((option) => option.value === "meilisearch" || option.value === "none");
5008
+ function resolveSearchPrompt(context = {}) {
5009
+ const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS : SEARCH_PROMPT_OPTIONS;
5010
+ if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
5011
+ shouldPrompt: false,
5012
+ mode: "single",
5013
+ options: [],
5014
+ autoValue: "none"
5015
+ };
5016
+ return context.search !== void 0 ? {
5017
+ shouldPrompt: false,
5018
+ mode: "single",
5019
+ options,
5020
+ autoValue: context.search
5021
+ } : {
5022
+ shouldPrompt: true,
5023
+ mode: "single",
5024
+ options,
5025
+ initialValue: "none"
5026
+ };
5027
+ }
5028
+ async function getSearchChoice(search, backend, ecosystem) {
5029
+ const resolution = resolveSearchPrompt({
5030
+ search,
5031
+ backend,
5032
+ ecosystem
5033
+ });
5034
+ if (!resolution.shouldPrompt) return resolution.autoValue ?? "none";
4776
5035
  const response = await navigableSelect({
4777
5036
  message: "Select search engine",
4778
- options: [
4779
- {
4780
- value: "meilisearch",
4781
- label: "Meilisearch",
4782
- hint: "Lightning-fast search engine with typo tolerance"
4783
- },
4784
- {
4785
- value: "typesense",
4786
- label: "Typesense",
4787
- hint: "Fast, typo-tolerant search with built-in vector search"
4788
- },
4789
- {
4790
- value: "elasticsearch",
4791
- label: "Elasticsearch",
4792
- hint: "Distributed search and analytics engine with local and cloud deployments"
4793
- },
4794
- {
4795
- value: "algolia",
4796
- label: "Algolia",
4797
- hint: "Hosted search API with instant results, typo tolerance, and analytics"
4798
- },
4799
- {
4800
- value: "none",
4801
- label: "None",
4802
- hint: "Skip search engine setup"
4803
- }
4804
- ],
4805
- initialValue: "none"
5037
+ options: resolution.options,
5038
+ initialValue: resolution.initialValue
4806
5039
  });
4807
5040
  if (isCancel$1(response)) return exitCancelled("Operation cancelled");
4808
5041
  return response;
@@ -4854,6 +5087,16 @@ const STYLE_OPTIONS = [
4854
5087
  value: "mira",
4855
5088
  label: "Mira",
4856
5089
  hint: "Dense, made for data-heavy interfaces"
5090
+ },
5091
+ {
5092
+ value: "luma",
5093
+ label: "Luma",
5094
+ hint: "Modern shadcn/ui v4 preset"
5095
+ },
5096
+ {
5097
+ value: "sera",
5098
+ label: "Sera",
5099
+ hint: "Modern shadcn/ui v4 preset"
4857
5100
  }
4858
5101
  ];
4859
5102
  const ICON_LIBRARY_OPTIONS = [
@@ -4881,6 +5124,16 @@ const ICON_LIBRARY_OPTIONS = [
4881
5124
  value: "remixicon",
4882
5125
  label: "Remix Icon",
4883
5126
  hint: "Open-source neutral style icons"
5127
+ },
5128
+ {
5129
+ value: "heroicons",
5130
+ label: "Heroicons",
5131
+ hint: "Tailwind Labs SVG icon set"
5132
+ },
5133
+ {
5134
+ value: "react-icons",
5135
+ label: "React Icons",
5136
+ hint: "Popular wrapper for many icon packs"
4884
5137
  }
4885
5138
  ];
4886
5139
  const COLOR_THEME_OPTIONS = [
@@ -5379,6 +5632,14 @@ const UI_LIBRARY_OPTIONS = {
5379
5632
  label: "Mantine",
5380
5633
  hint: "Full-featured React component library with 120+ components"
5381
5634
  },
5635
+ mui: {
5636
+ label: "MUI",
5637
+ hint: "Popular React component library implementing Material Design"
5638
+ },
5639
+ antd: {
5640
+ label: "Ant Design",
5641
+ hint: "Enterprise-class React UI component library"
5642
+ },
5382
5643
  "base-ui": {
5383
5644
  label: "Base UI",
5384
5645
  hint: "Unstyled, accessible components from MUI team (Radix successor)"
@@ -5629,8 +5890,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5629
5890
  return getPaymentsChoice(flags.payments, results.auth, results.backend, results.frontend);
5630
5891
  },
5631
5892
  email: ({ results }) => {
5632
- if (results.ecosystem !== "typescript") return Promise.resolve("none");
5633
- return getEmailChoice(flags.email, results.backend);
5893
+ return getEmailChoice(flags.email, results.backend, results.ecosystem);
5634
5894
  },
5635
5895
  effect: ({ results }) => {
5636
5896
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
@@ -5701,8 +5961,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5701
5961
  return getLoggingChoice(flags.logging, results.backend);
5702
5962
  },
5703
5963
  observability: ({ results }) => {
5704
- if (results.ecosystem !== "typescript") return Promise.resolve("none");
5705
- return getObservabilityChoice(flags.observability, results.backend);
5964
+ return getObservabilityChoice(flags.observability, results.backend, results.ecosystem);
5706
5965
  },
5707
5966
  featureFlags: ({ results }) => {
5708
5967
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
@@ -5717,16 +5976,14 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5717
5976
  return getCMSChoice(flags.cms, results.backend);
5718
5977
  },
5719
5978
  caching: ({ results }) => {
5720
- if (results.ecosystem !== "typescript") return Promise.resolve("none");
5721
- return getCachingChoice(flags.caching, results.backend);
5979
+ return getCachingChoice(flags.caching, results.backend, results.ecosystem);
5722
5980
  },
5723
5981
  i18n: ({ results }) => {
5724
5982
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
5725
5983
  return getI18nChoice(flags.i18n, results.frontend);
5726
5984
  },
5727
5985
  search: ({ results }) => {
5728
- if (results.ecosystem !== "typescript") return Promise.resolve("none");
5729
- return getSearchChoice(flags.search, results.backend);
5986
+ return getSearchChoice(flags.search, results.backend, results.ecosystem);
5730
5987
  },
5731
5988
  fileStorage: ({ results }) => {
5732
5989
  if (results.ecosystem !== "typescript") return Promise.resolve("none");
@@ -5792,6 +6049,11 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5792
6049
  if (results.ecosystem !== "python") return Promise.resolve("none");
5793
6050
  return getPythonAuthChoice(flags.pythonAuth);
5794
6051
  },
6052
+ pythonApi: ({ results }) => {
6053
+ if (results.ecosystem !== "python") return Promise.resolve("none");
6054
+ if (results.pythonWebFramework !== "django") return Promise.resolve("none");
6055
+ return getPythonApiChoice(flags.pythonApi);
6056
+ },
5795
6057
  pythonTaskQueue: ({ results }) => {
5796
6058
  if (results.ecosystem !== "python") return Promise.resolve("none");
5797
6059
  return getPythonTaskQueueChoice(flags.pythonTaskQueue);
@@ -5924,6 +6186,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
5924
6186
  pythonValidation: result.pythonValidation,
5925
6187
  pythonAi: result.pythonAi,
5926
6188
  pythonAuth: result.pythonAuth,
6189
+ pythonApi: result.pythonApi,
5927
6190
  pythonTaskQueue: result.pythonTaskQueue,
5928
6191
  pythonGraphql: result.pythonGraphql,
5929
6192
  pythonQuality: result.pythonQuality,
@@ -6151,6 +6414,10 @@ function appendCommonFlags(flags, config) {
6151
6414
  flags.push(config.install ? "--install" : "--no-install");
6152
6415
  }
6153
6416
  function appendSharedNonTypeScriptFlags(flags, config) {
6417
+ flags.push(`--email ${config.email}`);
6418
+ flags.push(`--observability ${config.observability}`);
6419
+ flags.push(`--caching ${config.caching}`);
6420
+ flags.push(`--search ${config.search}`);
6154
6421
  flags.push(formatArrayFlag("addons", config.addons));
6155
6422
  flags.push(formatArrayFlag("examples", config.examples));
6156
6423
  flags.push(`--db-setup ${config.dbSetup}`);
@@ -6192,6 +6459,7 @@ function getTypeScriptFlags(config) {
6192
6459
  flags.push(`--job-queue ${config.jobQueue}`);
6193
6460
  flags.push(`--logging ${config.logging}`);
6194
6461
  flags.push(`--observability ${config.observability}`);
6462
+ flags.push(`--feature-flags ${config.featureFlags}`);
6195
6463
  flags.push(`--caching ${config.caching}`);
6196
6464
  flags.push(`--i18n ${config.i18n}`);
6197
6465
  flags.push(`--cms ${config.cms}`);
@@ -6230,6 +6498,7 @@ function getPythonFlags(config) {
6230
6498
  flags.push(`--python-validation ${config.pythonValidation}`);
6231
6499
  flags.push(formatArrayFlag("python-ai", config.pythonAi));
6232
6500
  flags.push(`--python-auth ${config.pythonAuth}`);
6501
+ flags.push(`--python-api ${config.pythonApi}`);
6233
6502
  flags.push(`--python-task-queue ${config.pythonTaskQueue}`);
6234
6503
  flags.push(`--python-graphql ${config.pythonGraphql}`);
6235
6504
  flags.push(`--python-quality ${config.pythonQuality}`);
@@ -6914,19 +7183,20 @@ function validateApiConstraints(_config, _options) {}
6914
7183
  function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set()) {
6915
7184
  if (config.ecosystem !== "java") return;
6916
7185
  const hasSpringBoot = config.javaWebFramework === "spring-boot";
7186
+ const hasJavaWebFramework = config.javaWebFramework !== "none";
6917
7187
  const hasNoBuildTool = config.javaBuildTool === "none";
6918
7188
  const hasJavaLibraries = (config.javaLibraries ?? []).some((library) => library !== "none");
6919
7189
  const hasJavaTestingLibraries = (config.javaTestingLibraries ?? []).some((library) => library !== "none");
6920
7190
  const hasSpringOnlyFeatures = config.javaOrm !== "none" || config.javaAuth !== "none" || hasJavaLibraries;
6921
- if (hasNoBuildTool && hasSpringBoot) incompatibilityError({
6922
- message: "Spring Boot requires Maven or Gradle in the Java scaffold.",
7191
+ if (hasNoBuildTool && hasJavaWebFramework) incompatibilityError({
7192
+ message: "Java web frameworks require Maven or Gradle in the Java scaffold.",
6923
7193
  provided: {
6924
7194
  "java-web-framework": config.javaWebFramework ?? "none",
6925
7195
  "java-build-tool": config.javaBuildTool ?? "none"
6926
7196
  },
6927
- 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"]
7197
+ suggestions: ["Use --java-build-tool maven or --java-build-tool gradle with Java web frameworks", "Use --java-web-framework none for a plain Java source-only scaffold"]
6928
7198
  });
6929
- if ((config.javaWebFramework === "none" || hasNoBuildTool) && hasSpringOnlyFeatures) incompatibilityError({
7199
+ if ((!hasSpringBoot || hasNoBuildTool) && hasSpringOnlyFeatures) incompatibilityError({
6930
7200
  message: "Spring-only Java features require the Spring Boot scaffold with Maven or Gradle.",
6931
7201
  provided: {
6932
7202
  "java-web-framework": config.javaWebFramework ?? "none",
@@ -6935,7 +7205,7 @@ function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set
6935
7205
  "java-auth": config.javaAuth ?? "none",
6936
7206
  "java-libraries": (config.javaLibraries ?? []).join(" ") || "none"
6937
7207
  },
6938
- suggestions: ["Use --java-web-framework spring-boot and a real build tool for Spring features", "Clear --java-orm, --java-auth, and --java-libraries when using plain Java"]
7208
+ suggestions: ["Use --java-web-framework spring-boot and a real build tool for Spring features", "Clear --java-orm, --java-auth, and --java-libraries when using plain Java or Quarkus"]
6939
7209
  });
6940
7210
  if (hasNoBuildTool && hasJavaTestingLibraries) incompatibilityError({
6941
7211
  message: "Java testing libraries require Maven or Gradle to manage test dependencies.",
@@ -6946,6 +7216,82 @@ function validateJavaConstraints(config, providedFlags = /* @__PURE__ */ new Set
6946
7216
  suggestions: ["Use --java-build-tool maven or --java-build-tool gradle to enable JUnit/Mockito/Testcontainers", "Set --java-testing-libraries none for a source-only plain Java scaffold"]
6947
7217
  });
6948
7218
  }
7219
+ function validateEmailConstraints(config) {
7220
+ if (!config.email || config.email === "none") return;
7221
+ if (config.ecosystem !== "typescript" && config.email !== "resend") incompatibilityError({
7222
+ message: "Only Resend email is available for non-TypeScript ecosystems.",
7223
+ provided: {
7224
+ ecosystem: config.ecosystem ?? "typescript",
7225
+ email: config.email
7226
+ },
7227
+ suggestions: ["Use --email resend", "Use --email none"]
7228
+ });
7229
+ if (config.ecosystem === "java" && config.email === "resend" && config.javaBuildTool === "none") incompatibilityError({
7230
+ message: "Resend email for Java requires Maven or Gradle to manage the SDK dependency.",
7231
+ provided: {
7232
+ "java-build-tool": "none",
7233
+ email: "resend"
7234
+ },
7235
+ suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
7236
+ });
7237
+ }
7238
+ function validateObservabilityConstraints(config) {
7239
+ if (!config.observability || config.observability === "none") return;
7240
+ if (config.ecosystem !== "typescript" && config.observability !== "sentry") incompatibilityError({
7241
+ message: "Only Sentry observability is available for non-TypeScript ecosystems.",
7242
+ provided: {
7243
+ ecosystem: config.ecosystem ?? "typescript",
7244
+ observability: config.observability
7245
+ },
7246
+ suggestions: ["Use --observability sentry", "Use --observability none"]
7247
+ });
7248
+ if (config.ecosystem === "java" && config.observability === "sentry" && config.javaBuildTool === "none") incompatibilityError({
7249
+ message: "Sentry observability for Java requires Maven or Gradle to manage the SDK dependency.",
7250
+ provided: {
7251
+ "java-build-tool": "none",
7252
+ observability: "sentry"
7253
+ },
7254
+ suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
7255
+ });
7256
+ }
7257
+ function validateCachingConstraints(config) {
7258
+ if (!config.caching || config.caching === "none") return;
7259
+ if (config.ecosystem !== "typescript" && config.caching !== "upstash-redis") incompatibilityError({
7260
+ message: "Only Upstash Redis caching is available for non-TypeScript ecosystems.",
7261
+ provided: {
7262
+ ecosystem: config.ecosystem ?? "typescript",
7263
+ caching: config.caching
7264
+ },
7265
+ suggestions: ["Use --caching upstash-redis", "Use --caching none"]
7266
+ });
7267
+ if (config.ecosystem === "java" && config.caching === "upstash-redis" && config.javaBuildTool === "none") incompatibilityError({
7268
+ message: "Upstash Redis caching for Java requires Maven or Gradle to manage the Redis client dependency.",
7269
+ provided: {
7270
+ "java-build-tool": "none",
7271
+ caching: "upstash-redis"
7272
+ },
7273
+ suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
7274
+ });
7275
+ }
7276
+ function validateSearchConstraints(config) {
7277
+ if (!config.search || config.search === "none") return;
7278
+ if (config.ecosystem !== "typescript" && config.search !== "meilisearch") incompatibilityError({
7279
+ message: "Only Meilisearch search is available for non-TypeScript ecosystems.",
7280
+ provided: {
7281
+ ecosystem: config.ecosystem ?? "typescript",
7282
+ search: config.search
7283
+ },
7284
+ suggestions: ["Use --search meilisearch", "Use --search none"]
7285
+ });
7286
+ if (config.ecosystem === "java" && config.search === "meilisearch" && config.javaBuildTool === "none") incompatibilityError({
7287
+ message: "Meilisearch search for Java requires Maven or Gradle to manage the SDK dependency.",
7288
+ provided: {
7289
+ "java-build-tool": "none",
7290
+ search: "meilisearch"
7291
+ },
7292
+ suggestions: ["Use --java-build-tool maven", "Use --java-build-tool gradle"]
7293
+ });
7294
+ }
6949
7295
  function validateShadcnConstraints(config, providedFlags) {
6950
7296
  const shadcnFlagMap = {
6951
7297
  shadcnBase: "--shadcn-base",
@@ -6966,6 +7312,16 @@ function validateShadcnConstraints(config, providedFlags) {
6966
7312
  suggestions: ["Add --ui-library shadcn-ui to use shadcn customization flags", "Remove the --shadcn-* flags if not using shadcn/ui"]
6967
7313
  });
6968
7314
  }
7315
+ function validatePythonApiConstraints(config) {
7316
+ if (config.ecosystem === "python" && config.pythonApi && config.pythonApi !== "none" && config.pythonWebFramework !== "django") incompatibilityError({
7317
+ message: "Python API frameworks require --python-web-framework django.",
7318
+ provided: {
7319
+ "python-web-framework": config.pythonWebFramework || "none",
7320
+ "python-api": config.pythonApi
7321
+ },
7322
+ suggestions: ["Use --python-web-framework django with --python-api django-rest-framework or django-ninja", "Set --python-api none for FastAPI, Flask, Litestar, or no Python web framework"]
7323
+ });
7324
+ }
6969
7325
  function validateFullConfig(config, providedFlags, options) {
6970
7326
  validateEcosystemAuthCompatibility(config, providedFlags);
6971
7327
  validateDatabaseOrmAuth(config, providedFlags);
@@ -6978,6 +7334,11 @@ function validateFullConfig(config, providedFlags, options) {
6978
7334
  validateBackendConstraints(config, providedFlags, options);
6979
7335
  validateFrontendConstraints(config, providedFlags);
6980
7336
  /* @__PURE__ */ validateApiConstraints(config, options);
7337
+ validatePythonApiConstraints(config);
7338
+ validateEmailConstraints(config);
7339
+ validateObservabilityConstraints(config);
7340
+ validateCachingConstraints(config);
7341
+ validateSearchConstraints(config);
6981
7342
  validateJavaConstraints(config, providedFlags);
6982
7343
  validateServerDeployRequiresBackend(config.serverDeploy, config.backend);
6983
7344
  validateSelfBackendCompatibility(providedFlags, options, config);
@@ -7014,6 +7375,11 @@ function validateConfigForProgrammaticUse(config) {
7014
7375
  validateDatabaseOrmAuth(config);
7015
7376
  if (config.frontend && config.frontend.length > 0) ensureSingleWebAndNative(config.frontend);
7016
7377
  validateApiFrontendCompatibility(config.api, config.frontend, config.astroIntegration);
7378
+ validatePythonApiConstraints(config);
7379
+ validateEmailConstraints(config);
7380
+ validateObservabilityConstraints(config);
7381
+ validateCachingConstraints(config);
7382
+ validateSearchConstraints(config);
7017
7383
  validateJavaConstraints(config);
7018
7384
  validatePaymentsCompatibility(config.payments, config.auth, config.backend, config.frontend);
7019
7385
  if (config.addons && config.addons.length > 0) validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth, config.backend, config.runtime, config.ecosystem, config.rustFrontend, config.javaWebFramework, config.database);
@@ -8688,7 +9054,8 @@ function displayGoInstructions(config) {
8688
9054
  if (goApi && goApi !== "none") output += `${pc.cyan("•")} API: ${{ "grpc-go": "gRPC-Go" }[goApi] || goApi}\n`;
8689
9055
  if (goCli && goCli !== "none") output += `${pc.cyan("•")} CLI: ${{
8690
9056
  cobra: "Cobra",
8691
- bubbletea: "Bubble Tea"
9057
+ bubbletea: "Bubble Tea",
9058
+ "urfave-cli": "urfave/cli"
8692
9059
  }[goCli] || goCli}\n`;
8693
9060
  if (goLogging && goLogging !== "none") output += `${pc.cyan("•")} Logging: ${{
8694
9061
  zap: "Zap",
@@ -8790,6 +9157,7 @@ function displayJavaInstructions(config) {
8790
9157
  const { projectName, relativePath, depsInstalled, javaWebFramework, javaBuildTool, javaOrm, javaAuth, javaLibraries, javaTestingLibraries } = config;
8791
9158
  const cdCmd = `cd ${relativePath}`;
8792
9159
  const isSpringBoot = javaWebFramework === "spring-boot" && javaBuildTool !== "none";
9160
+ const isQuarkus = javaWebFramework === "quarkus" && javaBuildTool !== "none";
8793
9161
  const rawJavaLibraries = isSpringBoot ? javaLibraries.filter((library) => library !== "none") : [];
8794
9162
  const rawJavaLibrarySet = new Set(rawJavaLibraries);
8795
9163
  const jpaRequiredJavaLibraries = new Set(["flyway", "liquibase"]);
@@ -8801,7 +9169,7 @@ function displayJavaInstructions(config) {
8801
9169
  }
8802
9170
  const effectiveJavaTestingLibraries = javaBuildTool === "none" ? [] : javaTestingLibraries.filter((library) => library !== "none");
8803
9171
  const buildToolCommand = javaBuildTool === "none" ? null : javaBuildTool === "gradle" ? process.platform === "win32" ? "gradlew.bat" : "./gradlew" : process.platform === "win32" ? "mvnw.cmd" : "./mvnw";
8804
- const runCommand = buildToolCommand ? isSpringBoot ? javaBuildTool === "gradle" ? `${buildToolCommand} bootRun` : `${buildToolCommand} spring-boot:run` : javaBuildTool === "gradle" ? `${buildToolCommand} run` : `${buildToolCommand} exec:java` : null;
9172
+ const runCommand = buildToolCommand ? isSpringBoot ? javaBuildTool === "gradle" ? `${buildToolCommand} bootRun` : `${buildToolCommand} spring-boot:run` : isQuarkus ? javaBuildTool === "gradle" ? `${buildToolCommand} quarkusDev` : `${buildToolCommand} quarkus:dev` : javaBuildTool === "gradle" ? `${buildToolCommand} run` : `${buildToolCommand} exec:java` : null;
8805
9173
  const packageCommand = buildToolCommand ? javaBuildTool === "gradle" ? `${buildToolCommand} build` : `${buildToolCommand} package` : null;
8806
9174
  const sourceCompileCommand = buildToolCommand ? null : `javac -d out ${getJavaMainSourcePath(projectName)}`;
8807
9175
  const sourceRunCommand = buildToolCommand ? null : `java -cp out ${getJavaMainClass(projectName)}`;
@@ -8814,7 +9182,10 @@ function displayJavaInstructions(config) {
8814
9182
  output += `${pc.cyan(`${stepCounter++}.`)} ${sourceRunCommand}\n`;
8815
9183
  } else output += `${pc.cyan(`${stepCounter++}.`)} Add Maven or Gradle, then run the app\n`;
8816
9184
  output += `\n${pc.bold("Your Java project includes:")}\n`;
8817
- if (isSpringBoot) output += `${pc.cyan("•")} Web Framework: ${{ "spring-boot": "Spring Boot" }[javaWebFramework] || javaWebFramework}\n`;
9185
+ if (isSpringBoot || isQuarkus) output += `${pc.cyan("•")} Web Framework: ${{
9186
+ "spring-boot": "Spring Boot",
9187
+ quarkus: "Quarkus"
9188
+ }[javaWebFramework] || javaWebFramework}\n`;
8818
9189
  else output += `${pc.cyan("•")} Scaffold: Plain Java\n`;
8819
9190
  if (javaBuildTool && javaBuildTool !== "none") output += `${pc.cyan("•")} Build Tool: ${{
8820
9191
  maven: "Maven Wrapper",
@@ -8871,7 +9242,7 @@ function displayJavaInstructions(config) {
8871
9242
  consola$1.box(output);
8872
9243
  }
8873
9244
  function displayPythonInstructions(config) {
8874
- const { relativePath, depsInstalled, pythonWebFramework, pythonOrm, pythonValidation, pythonAi, pythonTaskQueue, pythonQuality } = config;
9245
+ const { relativePath, depsInstalled, pythonWebFramework, pythonOrm, pythonValidation, pythonAi, pythonApi, pythonTaskQueue, pythonQuality } = config;
8875
9246
  const cdCmd = `cd ${relativePath}`;
8876
9247
  let runCommand = "uv run uvicorn app.main:app --reload";
8877
9248
  if (pythonWebFramework === "django") runCommand = "uv run python manage.py runserver";
@@ -8906,14 +9277,25 @@ function displayPythonInstructions(config) {
8906
9277
  const aiList = pythonAi.filter((ai) => ai !== "none").map((ai) => aiNames[ai] || ai).join(", ");
8907
9278
  output += `${pc.cyan("•")} AI: ${aiList}\n`;
8908
9279
  }
9280
+ if (pythonApi && pythonApi !== "none") output += `${pc.cyan("•")} API Framework: ${{
9281
+ "django-rest-framework": "Django REST Framework",
9282
+ "django-ninja": "Django Ninja"
9283
+ }[pythonApi] || pythonApi}\n`;
8909
9284
  if (pythonTaskQueue && pythonTaskQueue !== "none") output += `${pc.cyan("•")} Task Queue: ${{ celery: "Celery" }[pythonTaskQueue] || pythonTaskQueue}\n`;
8910
- if (pythonQuality && pythonQuality !== "none") output += `${pc.cyan("•")} Code Quality: ${{ ruff: "Ruff" }[pythonQuality] || pythonQuality}\n`;
9285
+ if (pythonQuality && pythonQuality !== "none") output += `${pc.cyan("•")} Code Quality: ${{
9286
+ ruff: "Ruff",
9287
+ mypy: "mypy",
9288
+ pyright: "Pyright"
9289
+ }[pythonQuality] || pythonQuality}\n`;
8911
9290
  output += `\n${pc.bold("Common Python commands:")}\n`;
8912
9291
  output += `${pc.cyan("•")} Install: uv sync\n`;
8913
9292
  output += `${pc.cyan("•")} Run: ${runCommand}\n`;
8914
9293
  output += `${pc.cyan("•")} Test: uv run pytest\n`;
8915
- output += `${pc.cyan("")} Format: uv run ruff format .\n`;
8916
- output += `${pc.cyan("•")} Lint: uv run ruff check .\n`;
9294
+ if (pythonQuality === "ruff") {
9295
+ output += `${pc.cyan("•")} Format: uv run ruff format .\n`;
9296
+ output += `${pc.cyan("•")} Lint: uv run ruff check .\n`;
9297
+ } else if (pythonQuality === "mypy") output += `${pc.cyan("•")} Type check: uv run mypy src/app tests\n`;
9298
+ else if (pythonQuality === "pyright") output += `${pc.cyan("•")} Type check: uv run pyright\n`;
8917
9299
  output += `\n${pc.bold("Your project will be available at:")}\n`;
8918
9300
  output += `${pc.cyan("•")} API: http://localhost:8000\n`;
8919
9301
  output += `\n${pc.bold("Enjoying Better Fullstack?")} Help us grow — star the repo!\n`;
@@ -9099,6 +9481,7 @@ async function createProjectHandler(input, options = {}) {
9099
9481
  pythonValidation: "none",
9100
9482
  pythonAi: [],
9101
9483
  pythonAuth: "none",
9484
+ pythonApi: "none",
9102
9485
  pythonTaskQueue: "none",
9103
9486
  pythonGraphql: "none",
9104
9487
  pythonQuality: "none",
@@ -9622,6 +10005,7 @@ async function createVirtual(options) {
9622
10005
  pythonValidation: options.pythonValidation || "none",
9623
10006
  pythonAi: options.pythonAi || [],
9624
10007
  pythonAuth: options.pythonAuth || "none",
10008
+ pythonApi: options.pythonApi || "none",
9625
10009
  pythonTaskQueue: options.pythonTaskQueue || "none",
9626
10010
  pythonGraphql: options.pythonGraphql || "none",
9627
10011
  pythonQuality: options.pythonQuality || "none",