create-better-fullstack 2.1.5 → 2.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{add-handler-BNSL6HdM.mjs → add-handler-CuPmSJRM.mjs} +13 -5
- package/dist/{addons-setup-CyrP1IV-.mjs → addons-setup-C_lxdJqU.mjs} +8 -1
- package/dist/addons-setup-DEPfsn6z.mjs +6 -0
- package/dist/{errors-Cyol8zbN.mjs → bts-config-BceXPcpI.mjs} +3 -84
- package/dist/cli.mjs +2 -2
- package/dist/{templates-CnTOtKjm.mjs → config-processing-B_1wTe3g.mjs} +57 -2
- package/dist/{doctor-DBoq7bZ9.mjs → doctor-DucDyWfl.mjs} +3 -2
- package/dist/errors-ns_o2OKg.mjs +86 -0
- package/dist/{file-formatter-B3dsev2l.mjs → file-formatter-XU6ti05V.mjs} +66 -6
- package/dist/gen-DWx3Xu_K.mjs +274 -0
- package/dist/{generated-checks-C8hn9w2i.mjs → generated-checks-Dt4Xqp1x.mjs} +1 -1
- package/dist/index.d.mts +171 -79
- package/dist/index.mjs +15 -8
- package/dist/{install-dependencies-CgNh-aOy.mjs → install-dependencies-DHoYa3P-.mjs} +75 -21
- package/dist/mcp-D9O5zgAA.mjs +8 -0
- package/dist/mcp-entry.mjs +153 -16
- package/dist/registry-CxeEOPot.mjs +394 -0
- package/dist/run-3AkXloH1.mjs +13 -0
- package/dist/{run-BYse4yJy.mjs → run-D80ZtSO8.mjs} +361 -88
- package/dist/scaffold-manifest-GV1fbhpD.mjs +123 -0
- package/dist/update-C9_x2yBF.mjs +401 -0
- package/package.json +3 -3
- package/dist/addons-setup-DyMm42a5.mjs +0 -5
- package/dist/mcp-CsW3i66c.mjs +0 -6
- package/dist/run-_cf_sFwM.mjs +0 -10
- /package/dist/{update-deps-D5OG0KmJ.mjs → update-deps-DLZAuT3V.mjs} +0 -0
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { c as DEFAULT_CONFIG, d as getUserPkgManager, g as getGraphSummary, h as getGraphPart, i as readBtsConfigFromFile, l as DEFAULT_UI_LIBRARY_BY_FRONTEND, m as getGraphBackendUrl, o as writeBtsConfig, p as getGraphBackendDeployInstructions, s as getLatestCLIVersion, t as buildBtsConfigForPersistence, u as getDefaultConfig, v as hasGraphPart, y as types_exports } from "./bts-config-BceXPcpI.mjs";
|
|
3
3
|
import { t as renderTitle } from "./render-title-zvyKC1ej.mjs";
|
|
4
|
-
import { i as
|
|
5
|
-
import {
|
|
6
|
-
import { _ as
|
|
7
|
-
import { n as
|
|
8
|
-
import { n as
|
|
4
|
+
import { a as handleError, c as isSilent, d as setLastPromptShownUI, i as exitWithError, l as runWithContextAsync, n as UserCancelledError, o as didLastPromptShowUI, r as exitCancelled, s as isFirstPrompt, t as CLIError, u as setIsFirstPrompt } from "./errors-ns_o2OKg.mjs";
|
|
5
|
+
import { i as canPromptInteractively, n as getPackageExecutionArgs, r as addPackageDependency, t as setupAddons } from "./addons-setup-C_lxdJqU.mjs";
|
|
6
|
+
import { _ as CreateCommandInputSchema, a as allowedApisForFrontends$1, c as getCompatibleUILibraries$1, d as isExampleChatSdkAllowed$1, f as isFrontendAllowedWithBackend$1, h as splitFrontends$1, i as validateFullConfig, l as hasWebStyling$1, m as requiresChatSdkVercelAI, n as formatProject, p as isWebFrontend$1, r as validateConfigForProgrammaticUse, s as getCompatibleCSSFrameworks$1, u as isExampleAIAllowed$1, v as CreateCommandOptionsSchema } from "./file-formatter-XU6ti05V.mjs";
|
|
7
|
+
import { _ as isGoBack, a as runMavenTests, c as applyDependencyVersionChannel, d as isCancel$1, f as navigableConfirm, g as GO_BACK_SYMBOL, h as setIsFirstPrompt$1, i as runGradleTests, l as getAddonsChoice, m as navigableSelect, n as runCargoBuild, o as runMixCompile, p as navigableMultiselect, r as runGoModTidy, s as runUvSync, t as installDependencies } from "./install-dependencies-DHoYa3P-.mjs";
|
|
8
|
+
import { n as commandExists, t as runGeneratedChecks } from "./generated-checks-Dt4Xqp1x.mjs";
|
|
9
|
+
import { a as getTemplateConfig, i as validateArrayOptions, n as getProvidedFlags, o as getTemplateDescription, r as processFlags, s as generateReproducibleCommand, t as applyEffectBackendDefaults } from "./config-processing-B_1wTe3g.mjs";
|
|
10
|
+
import { r as recordScaffoldManifest } from "./scaffold-manifest-GV1fbhpD.mjs";
|
|
9
11
|
import { cancel, confirm, intro, isCancel, log, outro, select, spinner, text } from "@clack/prompts";
|
|
10
12
|
import { createRouterClient, os } from "@orpc/server";
|
|
11
13
|
import pc from "picocolors";
|
|
@@ -17,7 +19,6 @@ import path from "node:path";
|
|
|
17
19
|
import { formatStackPartSpec, getLocalWebDevPort } from "@better-fullstack/types";
|
|
18
20
|
import consola, { consola as consola$1 } from "consola";
|
|
19
21
|
import { $, execa } from "execa";
|
|
20
|
-
import { cliInputToProjectConfigPartial } from "@better-fullstack/types/stack-translation";
|
|
21
22
|
import os$1 from "node:os";
|
|
22
23
|
|
|
23
24
|
//#region src/utils/project-history.ts
|
|
@@ -792,6 +793,11 @@ const BACKEND_PROMPT_OPTIONS = [
|
|
|
792
793
|
label: "feTS",
|
|
793
794
|
hint: "TypeScript HTTP Framework with e2e type-safety"
|
|
794
795
|
},
|
|
796
|
+
{
|
|
797
|
+
value: "effect",
|
|
798
|
+
label: "Effect",
|
|
799
|
+
hint: "Effect-first service backend with layers and schema"
|
|
800
|
+
},
|
|
795
801
|
{
|
|
796
802
|
value: "nestjs",
|
|
797
803
|
label: "NestJS",
|
|
@@ -833,6 +839,7 @@ function resolveBackendPrompt(context = {}) {
|
|
|
833
839
|
"fastify",
|
|
834
840
|
"elysia",
|
|
835
841
|
"fets",
|
|
842
|
+
"effect",
|
|
836
843
|
"nestjs",
|
|
837
844
|
"adonisjs",
|
|
838
845
|
"nitro",
|
|
@@ -870,15 +877,24 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
|
|
|
870
877
|
|
|
871
878
|
//#endregion
|
|
872
879
|
//#region src/prompts/caching.ts
|
|
873
|
-
const CACHING_PROMPT_OPTIONS = [
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
880
|
+
const CACHING_PROMPT_OPTIONS = [
|
|
881
|
+
{
|
|
882
|
+
value: "upstash-redis",
|
|
883
|
+
label: "Upstash Redis",
|
|
884
|
+
hint: "Serverless Redis with REST API for edge and serverless"
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
value: "redis",
|
|
888
|
+
label: "Redis",
|
|
889
|
+
hint: "Self-hosted Redis via ioredis (TCP) — full command surface"
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
value: "none",
|
|
893
|
+
label: "None",
|
|
894
|
+
hint: "Skip caching layer setup"
|
|
895
|
+
}
|
|
896
|
+
];
|
|
897
|
+
const NON_TS_CACHING_PROMPT_OPTIONS = CACHING_PROMPT_OPTIONS.filter((option) => option.value !== "redis");
|
|
882
898
|
function resolveCachingPrompt(context = {}) {
|
|
883
899
|
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
884
900
|
shouldPrompt: false,
|
|
@@ -889,12 +905,12 @@ function resolveCachingPrompt(context = {}) {
|
|
|
889
905
|
if (context.ecosystem && context.ecosystem !== "typescript") return context.caching !== void 0 ? {
|
|
890
906
|
shouldPrompt: false,
|
|
891
907
|
mode: "single",
|
|
892
|
-
options:
|
|
908
|
+
options: NON_TS_CACHING_PROMPT_OPTIONS,
|
|
893
909
|
autoValue: context.caching
|
|
894
910
|
} : {
|
|
895
911
|
shouldPrompt: true,
|
|
896
912
|
mode: "single",
|
|
897
|
-
options:
|
|
913
|
+
options: NON_TS_CACHING_PROMPT_OPTIONS,
|
|
898
914
|
initialValue: "none"
|
|
899
915
|
};
|
|
900
916
|
if (context.backend === "none" || context.backend === "convex") return {
|
|
@@ -1968,22 +1984,22 @@ async function getEcosystemChoice(ecosystem) {
|
|
|
1968
1984
|
async function getEffectChoice(effect) {
|
|
1969
1985
|
if (effect !== void 0) return effect;
|
|
1970
1986
|
const response = await navigableSelect({
|
|
1971
|
-
message: "Select Effect
|
|
1987
|
+
message: "Select Effect services",
|
|
1972
1988
|
options: [
|
|
1973
1989
|
{
|
|
1974
1990
|
value: "effect",
|
|
1975
|
-
label: "Effect
|
|
1976
|
-
hint: "
|
|
1991
|
+
label: "Effect Core",
|
|
1992
|
+
hint: "Add Effect services and layers to the selected stack"
|
|
1977
1993
|
},
|
|
1978
1994
|
{
|
|
1979
1995
|
value: "effect-full",
|
|
1980
|
-
label: "Effect
|
|
1981
|
-
hint: "
|
|
1996
|
+
label: "Effect Platform + SQL",
|
|
1997
|
+
hint: "Core plus Platform, browser/server adapters, SQL, and Vitest integration"
|
|
1982
1998
|
},
|
|
1983
1999
|
{
|
|
1984
2000
|
value: "none",
|
|
1985
2001
|
label: "None",
|
|
1986
|
-
hint: "No Effect
|
|
2002
|
+
hint: "No additional Effect services"
|
|
1987
2003
|
}
|
|
1988
2004
|
],
|
|
1989
2005
|
initialValue: "none"
|
|
@@ -2135,6 +2151,11 @@ async function getFileStorageChoice(fileStorage, backend) {
|
|
|
2135
2151
|
label: "Cloudinary",
|
|
2136
2152
|
hint: "Image and media storage with transformations"
|
|
2137
2153
|
},
|
|
2154
|
+
{
|
|
2155
|
+
value: "supabase-storage",
|
|
2156
|
+
label: "Supabase Storage",
|
|
2157
|
+
hint: "S3-compatible object storage with signed URLs and RLS"
|
|
2158
|
+
},
|
|
2138
2159
|
{
|
|
2139
2160
|
value: "none",
|
|
2140
2161
|
label: "None",
|
|
@@ -2515,6 +2536,11 @@ const GO_WEB_FRAMEWORK_PROMPT_OPTIONS = [
|
|
|
2515
2536
|
label: "Chi",
|
|
2516
2537
|
hint: "Lightweight, zero-dependency router built on net/http"
|
|
2517
2538
|
},
|
|
2539
|
+
{
|
|
2540
|
+
value: "stdlib",
|
|
2541
|
+
label: "net/http",
|
|
2542
|
+
hint: "Go 1.22+ standard-library routing (ServeMux), zero dependencies"
|
|
2543
|
+
},
|
|
2518
2544
|
{
|
|
2519
2545
|
value: "none",
|
|
2520
2546
|
label: "None",
|
|
@@ -2537,6 +2563,11 @@ const GO_ORM_PROMPT_OPTIONS = [
|
|
|
2537
2563
|
label: "Ent",
|
|
2538
2564
|
hint: "Code-first ORM by Meta with graph traversal API, 15k+ stars"
|
|
2539
2565
|
},
|
|
2566
|
+
{
|
|
2567
|
+
value: "bun",
|
|
2568
|
+
label: "Bun",
|
|
2569
|
+
hint: "SQL-first Go ORM by uptrace with a lightweight query builder"
|
|
2570
|
+
},
|
|
2540
2571
|
{
|
|
2541
2572
|
value: "none",
|
|
2542
2573
|
label: "None",
|
|
@@ -2913,10 +2944,19 @@ const PARAGLIDE_FRONTENDS = new Set([
|
|
|
2913
2944
|
async function getI18nChoice(i18n, frontend) {
|
|
2914
2945
|
if (i18n !== void 0) return i18n;
|
|
2915
2946
|
const hasNext = frontend?.includes("next") ?? false;
|
|
2947
|
+
const hasParaglideFrontend = frontend?.some((f) => PARAGLIDE_FRONTENDS.has(f)) ?? false;
|
|
2948
|
+
const hasIntlayerFrontend = frontend?.some((f) => [
|
|
2949
|
+
"next",
|
|
2950
|
+
"vinext",
|
|
2951
|
+
"tanstack-router",
|
|
2952
|
+
"tanstack-start",
|
|
2953
|
+
"react-router",
|
|
2954
|
+
"react-vite"
|
|
2955
|
+
].includes(f)) ?? false;
|
|
2916
2956
|
const response = await navigableSelect({
|
|
2917
2957
|
message: "Select internationalization (i18n) library",
|
|
2918
2958
|
options: [
|
|
2919
|
-
...
|
|
2959
|
+
...hasParaglideFrontend ? [{
|
|
2920
2960
|
value: "paraglide",
|
|
2921
2961
|
label: "Paraglide",
|
|
2922
2962
|
hint: "Type-safe, compiler-based i18n for modern web frontends"
|
|
@@ -2931,6 +2971,11 @@ async function getI18nChoice(i18n, frontend) {
|
|
|
2931
2971
|
label: "next-intl",
|
|
2932
2972
|
hint: "Lightweight i18n for Next.js with App Router support"
|
|
2933
2973
|
}] : [],
|
|
2974
|
+
...hasIntlayerFrontend ? [{
|
|
2975
|
+
value: "intlayer",
|
|
2976
|
+
label: "Intlayer",
|
|
2977
|
+
hint: "Component-level, type-safe i18n for React, Next.js, and Vite"
|
|
2978
|
+
}] : [],
|
|
2934
2979
|
{
|
|
2935
2980
|
value: "none",
|
|
2936
2981
|
label: "None",
|
|
@@ -3032,12 +3077,26 @@ const JAVA_WEB_FRAMEWORK_PROMPT_OPTIONS = [
|
|
|
3032
3077
|
label: "Quarkus",
|
|
3033
3078
|
hint: "Cloud-native Java framework optimized for fast startup and lower memory use"
|
|
3034
3079
|
},
|
|
3080
|
+
{
|
|
3081
|
+
value: "micronaut",
|
|
3082
|
+
label: "Micronaut",
|
|
3083
|
+
hint: "Modern JVM framework with compile-time DI/AOP and fast startup, low memory"
|
|
3084
|
+
},
|
|
3035
3085
|
{
|
|
3036
3086
|
value: "none",
|
|
3037
3087
|
label: "None",
|
|
3038
3088
|
hint: "No Java web framework"
|
|
3039
3089
|
}
|
|
3040
3090
|
];
|
|
3091
|
+
const JAVA_LANGUAGE_PROMPT_OPTIONS = [{
|
|
3092
|
+
value: "java",
|
|
3093
|
+
label: "Java",
|
|
3094
|
+
hint: "Classic Java sources (default)"
|
|
3095
|
+
}, {
|
|
3096
|
+
value: "kotlin",
|
|
3097
|
+
label: "Kotlin",
|
|
3098
|
+
hint: "Idiomatic Kotlin sources (Spring Boot only)"
|
|
3099
|
+
}];
|
|
3041
3100
|
const JAVA_BUILD_TOOL_PROMPT_OPTIONS = [
|
|
3042
3101
|
{
|
|
3043
3102
|
value: "maven",
|
|
@@ -3257,6 +3316,20 @@ async function getJavaWebFrameworkChoice(javaWebFramework) {
|
|
|
3257
3316
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
3258
3317
|
return response;
|
|
3259
3318
|
}
|
|
3319
|
+
function resolveJavaLanguagePrompt(javaLanguage) {
|
|
3320
|
+
return createStaticSinglePromptResolution(JAVA_LANGUAGE_PROMPT_OPTIONS, "java", javaLanguage);
|
|
3321
|
+
}
|
|
3322
|
+
async function getJavaLanguageChoice(javaLanguage) {
|
|
3323
|
+
const resolution = resolveJavaLanguagePrompt(javaLanguage);
|
|
3324
|
+
if (!resolution.shouldPrompt) return resolution.autoValue ?? "java";
|
|
3325
|
+
const response = await navigableSelect({
|
|
3326
|
+
message: "Select JVM language",
|
|
3327
|
+
options: resolution.options,
|
|
3328
|
+
initialValue: resolution.initialValue
|
|
3329
|
+
});
|
|
3330
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
3331
|
+
return response;
|
|
3332
|
+
}
|
|
3260
3333
|
function resolveJavaBuildToolPrompt(javaBuildTool) {
|
|
3261
3334
|
return createStaticSinglePromptResolution(JAVA_BUILD_TOOL_PROMPT_OPTIONS, "maven", javaBuildTool);
|
|
3262
3335
|
}
|
|
@@ -3331,24 +3404,45 @@ async function getJavaTestingLibrariesChoice(javaTestingLibraries) {
|
|
|
3331
3404
|
if (response.includes("none")) return [];
|
|
3332
3405
|
return response;
|
|
3333
3406
|
}
|
|
3334
|
-
const JAVA_API_PROMPT_OPTIONS = [
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3407
|
+
const JAVA_API_PROMPT_OPTIONS = [
|
|
3408
|
+
{
|
|
3409
|
+
value: "spring-graphql",
|
|
3410
|
+
label: "Spring for GraphQL",
|
|
3411
|
+
hint: "GraphQL schema + annotated controllers on Spring Boot"
|
|
3412
|
+
},
|
|
3413
|
+
{
|
|
3414
|
+
value: "openapi-generator",
|
|
3415
|
+
label: "OpenAPI Generator",
|
|
3416
|
+
hint: "Generate Spring API interfaces + models from an OpenAPI spec at build time"
|
|
3417
|
+
},
|
|
3418
|
+
{
|
|
3419
|
+
value: "grpc",
|
|
3420
|
+
label: "gRPC",
|
|
3421
|
+
hint: "grpc-java service (protoc codegen) started alongside Spring Boot"
|
|
3422
|
+
},
|
|
3423
|
+
{
|
|
3424
|
+
value: "none",
|
|
3425
|
+
label: "None",
|
|
3426
|
+
hint: "REST controllers only"
|
|
3427
|
+
}
|
|
3428
|
+
];
|
|
3429
|
+
const JAVA_LOGGING_PROMPT_OPTIONS = [
|
|
3430
|
+
{
|
|
3431
|
+
value: "logback",
|
|
3432
|
+
label: "Logback",
|
|
3433
|
+
hint: "Spring Boot default logger with explicit logback-spring.xml"
|
|
3434
|
+
},
|
|
3435
|
+
{
|
|
3436
|
+
value: "log4j2",
|
|
3437
|
+
label: "Log4j2",
|
|
3438
|
+
hint: "High-performance Apache logging with async loggers (Spring Boot, Micronaut, plain Java)"
|
|
3439
|
+
},
|
|
3440
|
+
{
|
|
3441
|
+
value: "none",
|
|
3442
|
+
label: "None",
|
|
3443
|
+
hint: "Spring Boot logging defaults"
|
|
3444
|
+
}
|
|
3445
|
+
];
|
|
3352
3446
|
function resolveJavaApiPrompt(javaApi) {
|
|
3353
3447
|
return createStaticSinglePromptResolution(JAVA_API_PROMPT_OPTIONS, "none", javaApi);
|
|
3354
3448
|
}
|
|
@@ -4211,6 +4305,16 @@ const RUST_WEB_FRAMEWORK_PROMPT_OPTIONS = [
|
|
|
4211
4305
|
label: "Rocket",
|
|
4212
4306
|
hint: "Convention-over-configuration web framework, 25k+ stars"
|
|
4213
4307
|
},
|
|
4308
|
+
{
|
|
4309
|
+
value: "poem",
|
|
4310
|
+
label: "Poem",
|
|
4311
|
+
hint: "Full-featured, fast web framework built on Tokio and hyper"
|
|
4312
|
+
},
|
|
4313
|
+
{
|
|
4314
|
+
value: "loco",
|
|
4315
|
+
label: "Loco",
|
|
4316
|
+
hint: "The Rails of Rust: opinionated full-stack framework (runs DB-free too)"
|
|
4317
|
+
},
|
|
4214
4318
|
{
|
|
4215
4319
|
value: "none",
|
|
4216
4320
|
label: "None",
|
|
@@ -5903,6 +6007,42 @@ async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
|
|
|
5903
6007
|
return response;
|
|
5904
6008
|
}
|
|
5905
6009
|
|
|
6010
|
+
//#endregion
|
|
6011
|
+
//#region src/prompts/workspace-shape.ts
|
|
6012
|
+
const SINGLE_APP_WEB_FRONTENDS = new Set(["next", "tanstack-start"]);
|
|
6013
|
+
/**
|
|
6014
|
+
* The flat single-app layout only makes sense for a "thin self" app: backend
|
|
6015
|
+
* `self` with exactly one Next.js or TanStack Start web frontend. For anything
|
|
6016
|
+
* else we never prompt and resolve to `monorepo` (the compatibility engine and
|
|
6017
|
+
* generator also normalize non-qualifying stacks back to monorepo).
|
|
6018
|
+
*/
|
|
6019
|
+
function stackCanUseSingleApp(backend, frontend) {
|
|
6020
|
+
if (backend !== "self") return false;
|
|
6021
|
+
const webFrontends = frontend.filter((f) => f && f !== "none");
|
|
6022
|
+
if (webFrontends.length !== 1) return false;
|
|
6023
|
+
return SINGLE_APP_WEB_FRONTENDS.has(webFrontends[0]);
|
|
6024
|
+
}
|
|
6025
|
+
async function getWorkspaceShapeChoice(workspaceShape, backend, frontend) {
|
|
6026
|
+
if (!stackCanUseSingleApp(backend, frontend ?? [])) return "monorepo";
|
|
6027
|
+
if (workspaceShape !== void 0) return workspaceShape;
|
|
6028
|
+
if (!canPromptInteractively()) return "monorepo";
|
|
6029
|
+
const response = await navigableSelect({
|
|
6030
|
+
message: "Choose workspace layout",
|
|
6031
|
+
options: [{
|
|
6032
|
+
value: "monorepo",
|
|
6033
|
+
label: "Monorepo",
|
|
6034
|
+
hint: "apps/* + packages/* workspaces (recommended)"
|
|
6035
|
+
}, {
|
|
6036
|
+
value: "single-app",
|
|
6037
|
+
label: "Single app",
|
|
6038
|
+
hint: "Flat app at the repo root (no workspaces)"
|
|
6039
|
+
}],
|
|
6040
|
+
initialValue: "monorepo"
|
|
6041
|
+
});
|
|
6042
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
6043
|
+
return response;
|
|
6044
|
+
}
|
|
6045
|
+
|
|
5906
6046
|
//#endregion
|
|
5907
6047
|
//#region src/prompts/payments.ts
|
|
5908
6048
|
function resolvePaymentsPrompt(context = {}) {
|
|
@@ -5912,13 +6052,30 @@ function resolvePaymentsPrompt(context = {}) {
|
|
|
5912
6052
|
options: [],
|
|
5913
6053
|
autoValue: context.payments
|
|
5914
6054
|
};
|
|
5915
|
-
if (context.backend === "none") return {
|
|
5916
|
-
shouldPrompt: false,
|
|
5917
|
-
mode: "single",
|
|
5918
|
-
options: [],
|
|
5919
|
-
autoValue: "none"
|
|
5920
|
-
};
|
|
5921
6055
|
const isPolarCompatible = (context.auth === "better-auth" || context.auth === "better-auth-organizations") && (context.frontends?.length === 0 || splitFrontends$1(context.frontends).web.length > 0);
|
|
6056
|
+
const isRevenueCatCompatible = (context.frontends ?? []).some((frontend) => frontend === "native-bare" || frontend === "native-uniwind" || frontend === "native-unistyles");
|
|
6057
|
+
if (context.backend === "none") {
|
|
6058
|
+
if (isRevenueCatCompatible) return {
|
|
6059
|
+
shouldPrompt: true,
|
|
6060
|
+
mode: "single",
|
|
6061
|
+
options: [{
|
|
6062
|
+
value: "revenuecat",
|
|
6063
|
+
label: "RevenueCat",
|
|
6064
|
+
hint: "In-app subscriptions and cross-platform monetization for mobile."
|
|
6065
|
+
}, {
|
|
6066
|
+
value: "none",
|
|
6067
|
+
label: "None",
|
|
6068
|
+
hint: "No payments integration"
|
|
6069
|
+
}],
|
|
6070
|
+
initialValue: DEFAULT_CONFIG.payments
|
|
6071
|
+
};
|
|
6072
|
+
return {
|
|
6073
|
+
shouldPrompt: false,
|
|
6074
|
+
mode: "single",
|
|
6075
|
+
options: [],
|
|
6076
|
+
autoValue: "none"
|
|
6077
|
+
};
|
|
6078
|
+
}
|
|
5922
6079
|
const options = [];
|
|
5923
6080
|
if (isPolarCompatible) options.push({
|
|
5924
6081
|
value: "polar",
|
|
@@ -5941,11 +6098,28 @@ function resolvePaymentsPrompt(context = {}) {
|
|
|
5941
6098
|
value: "dodo",
|
|
5942
6099
|
label: "Dodo Payments",
|
|
5943
6100
|
hint: "Simple payment infrastructure for developers."
|
|
6101
|
+
}, {
|
|
6102
|
+
value: "creem",
|
|
6103
|
+
label: "Creem",
|
|
6104
|
+
hint: "Cheapest merchant-of-record payments & billing, with a Better Auth plugin."
|
|
6105
|
+
}, {
|
|
6106
|
+
value: "autumn",
|
|
6107
|
+
label: "Autumn",
|
|
6108
|
+
hint: "Usage-based pricing & billing for SaaS and AI apps."
|
|
6109
|
+
}, {
|
|
6110
|
+
value: "commet",
|
|
6111
|
+
label: "Commet",
|
|
6112
|
+
hint: "All-in-one plan-first billing for SaaS and AI products."
|
|
5944
6113
|
}, {
|
|
5945
6114
|
value: "none",
|
|
5946
6115
|
label: "None",
|
|
5947
6116
|
hint: "No payments integration"
|
|
5948
6117
|
});
|
|
6118
|
+
if (isRevenueCatCompatible) options.push({
|
|
6119
|
+
value: "revenuecat",
|
|
6120
|
+
label: "RevenueCat",
|
|
6121
|
+
hint: "In-app subscriptions and cross-platform monetization for mobile."
|
|
6122
|
+
});
|
|
5949
6123
|
return {
|
|
5950
6124
|
shouldPrompt: true,
|
|
5951
6125
|
mode: "single",
|
|
@@ -6185,6 +6359,24 @@ const SEARCH_PROMPT_OPTIONS = [
|
|
|
6185
6359
|
}
|
|
6186
6360
|
];
|
|
6187
6361
|
const NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS = SEARCH_PROMPT_OPTIONS.filter((option) => option.value === "meilisearch" || option.value === "none");
|
|
6362
|
+
const PYTHON_SEARCH_PROMPT_OPTIONS = SEARCH_PROMPT_OPTIONS.filter((option) => option.value === "meilisearch" || option.value === "elasticsearch" || option.value === "none");
|
|
6363
|
+
const GO_SEARCH_PROMPT_OPTIONS = [
|
|
6364
|
+
{
|
|
6365
|
+
value: "meilisearch",
|
|
6366
|
+
label: "Meilisearch",
|
|
6367
|
+
hint: "Lightning-fast search engine with typo tolerance"
|
|
6368
|
+
},
|
|
6369
|
+
{
|
|
6370
|
+
value: "bleve",
|
|
6371
|
+
label: "Bleve",
|
|
6372
|
+
hint: "Embedded, server-less full-text search for Go (no external service)"
|
|
6373
|
+
},
|
|
6374
|
+
{
|
|
6375
|
+
value: "none",
|
|
6376
|
+
label: "None",
|
|
6377
|
+
hint: "Skip search engine setup"
|
|
6378
|
+
}
|
|
6379
|
+
];
|
|
6188
6380
|
function resolveSearchPrompt(context = {}) {
|
|
6189
6381
|
if (context.ecosystem === "react-native" || context.ecosystem === "elixir") return {
|
|
6190
6382
|
shouldPrompt: false,
|
|
@@ -6192,7 +6384,7 @@ function resolveSearchPrompt(context = {}) {
|
|
|
6192
6384
|
options: [],
|
|
6193
6385
|
autoValue: "none"
|
|
6194
6386
|
};
|
|
6195
|
-
const options = context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS : SEARCH_PROMPT_OPTIONS;
|
|
6387
|
+
const options = context.ecosystem === "go" ? GO_SEARCH_PROMPT_OPTIONS : context.ecosystem === "python" ? PYTHON_SEARCH_PROMPT_OPTIONS : context.ecosystem && context.ecosystem !== "typescript" ? NON_TYPESCRIPT_SEARCH_PROMPT_OPTIONS : SEARCH_PROMPT_OPTIONS;
|
|
6196
6388
|
if ((!context.ecosystem || context.ecosystem === "typescript") && (context.backend === "none" || context.backend === "convex")) return {
|
|
6197
6389
|
shouldPrompt: false,
|
|
6198
6390
|
mode: "single",
|
|
@@ -6578,7 +6770,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
6578
6770
|
return Promise.resolve("none");
|
|
6579
6771
|
},
|
|
6580
6772
|
payments: ({ results }) => {
|
|
6581
|
-
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
6773
|
+
if (results.ecosystem !== "typescript" && results.ecosystem !== "react-native") return Promise.resolve("none");
|
|
6582
6774
|
return getPaymentsChoice(flags.payments, results.auth, results.backend, results.frontend);
|
|
6583
6775
|
},
|
|
6584
6776
|
email: ({ results }) => {
|
|
@@ -6587,6 +6779,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
6587
6779
|
},
|
|
6588
6780
|
effect: ({ results }) => {
|
|
6589
6781
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
6782
|
+
if (results.backend === "effect" && flags.effect === void 0) return Promise.resolve("effect-full");
|
|
6590
6783
|
return getEffectChoice(flags.effect);
|
|
6591
6784
|
},
|
|
6592
6785
|
addons: ({ results }) => {
|
|
@@ -6619,6 +6812,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
6619
6812
|
},
|
|
6620
6813
|
validation: ({ results }) => {
|
|
6621
6814
|
if (results.ecosystem !== "typescript") return Promise.resolve("none");
|
|
6815
|
+
if (results.backend === "effect" && flags.validation === void 0) return Promise.resolve("effect-schema");
|
|
6622
6816
|
return getValidationChoice(flags.validation);
|
|
6623
6817
|
},
|
|
6624
6818
|
forms: ({ results }) => {
|
|
@@ -6895,6 +7089,13 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
6895
7089
|
if (results.ecosystem !== "java") return Promise.resolve("none");
|
|
6896
7090
|
return getJavaWebFrameworkChoice(flags.javaWebFramework);
|
|
6897
7091
|
},
|
|
7092
|
+
javaLanguage: ({ results }) => {
|
|
7093
|
+
if (results.ecosystem !== "java") return Promise.resolve("java");
|
|
7094
|
+
if (results.javaWebFramework !== "spring-boot") return Promise.resolve("java");
|
|
7095
|
+
if (flags.javaLanguage !== void 0) return getJavaLanguageChoice(flags.javaLanguage);
|
|
7096
|
+
if (flags.javaWebFramework !== void 0) return Promise.resolve("java");
|
|
7097
|
+
return getJavaLanguageChoice(flags.javaLanguage);
|
|
7098
|
+
},
|
|
6898
7099
|
javaBuildTool: ({ results }) => {
|
|
6899
7100
|
if (results.ecosystem !== "java") return Promise.resolve("none");
|
|
6900
7101
|
return getJavaBuildToolChoice(flags.javaBuildTool);
|
|
@@ -7046,6 +7247,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
7046
7247
|
},
|
|
7047
7248
|
aiDocs: () => getAiDocsChoice(flags.aiDocs),
|
|
7048
7249
|
git: () => getGitChoice(flags.git),
|
|
7250
|
+
workspaceShape: ({ results }) => getWorkspaceShapeChoice(flags.workspaceShape, results.backend, results.frontend),
|
|
7049
7251
|
packageManager: ({ results }) => {
|
|
7050
7252
|
if (results.ecosystem === "rust" || results.ecosystem === "python" || results.ecosystem === "go" || results.ecosystem === "java" || results.ecosystem === "dotnet" || results.ecosystem === "elixir") return Promise.resolve(flags.packageManager ?? getUserPkgManager());
|
|
7051
7253
|
return getPackageManagerChoice(flags.packageManager);
|
|
@@ -7073,6 +7275,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
7073
7275
|
examples: result.examples,
|
|
7074
7276
|
git: result.git,
|
|
7075
7277
|
packageManager: result.packageManager,
|
|
7278
|
+
workspaceShape: result.workspaceShape,
|
|
7076
7279
|
install: result.install,
|
|
7077
7280
|
dbSetup: result.dbSetup,
|
|
7078
7281
|
api: result.api,
|
|
@@ -7147,6 +7350,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
7147
7350
|
goConfig: result.goConfig,
|
|
7148
7351
|
goObservability: result.goObservability,
|
|
7149
7352
|
javaWebFramework: result.javaWebFramework,
|
|
7353
|
+
javaLanguage: result.javaLanguage,
|
|
7150
7354
|
javaBuildTool: result.javaBuildTool,
|
|
7151
7355
|
javaOrm: result.javaOrm,
|
|
7152
7356
|
javaAuth: result.javaAuth,
|
|
@@ -7585,35 +7789,6 @@ async function setupProjectDirectory(finalPathInput, shouldClearDirectory) {
|
|
|
7585
7789
|
};
|
|
7586
7790
|
}
|
|
7587
7791
|
|
|
7588
|
-
//#endregion
|
|
7589
|
-
//#region src/utils/config-processing.ts
|
|
7590
|
-
function deriveProjectName(projectName, projectDirectory) {
|
|
7591
|
-
if (projectName) return projectName;
|
|
7592
|
-
if (projectDirectory) return path.basename(path.resolve(process.cwd(), projectDirectory));
|
|
7593
|
-
return "";
|
|
7594
|
-
}
|
|
7595
|
-
function processFlags(options, projectName) {
|
|
7596
|
-
const derivedName = deriveProjectName(projectName, options.projectDirectory);
|
|
7597
|
-
return cliInputToProjectConfigPartial(options, projectName || derivedName);
|
|
7598
|
-
}
|
|
7599
|
-
function getProvidedFlags(options) {
|
|
7600
|
-
return new Set(Object.keys(options).filter((key) => options[key] !== void 0));
|
|
7601
|
-
}
|
|
7602
|
-
function validateNoneExclusivity(options, optionName) {
|
|
7603
|
-
if (!options || options.length === 0) return;
|
|
7604
|
-
if (options.includes("none") && options.length > 1) throw new Error(`Cannot combine 'none' with other ${optionName}.`);
|
|
7605
|
-
}
|
|
7606
|
-
function validateArrayOptions(options) {
|
|
7607
|
-
validateNoneExclusivity(options.frontend, "frontend options");
|
|
7608
|
-
validateNoneExclusivity(options.addons, "addons");
|
|
7609
|
-
validateNoneExclusivity(options.examples, "examples");
|
|
7610
|
-
validateNoneExclusivity(options.aiDocs, "ai docs");
|
|
7611
|
-
validateNoneExclusivity(options.rustLibraries, "rust libraries");
|
|
7612
|
-
validateNoneExclusivity(options.pythonAi, "python ai libraries");
|
|
7613
|
-
validateNoneExclusivity(options.javaLibraries, "java libraries");
|
|
7614
|
-
validateNoneExclusivity(options.javaTestingLibraries, "java testing libraries");
|
|
7615
|
-
}
|
|
7616
|
-
|
|
7617
7792
|
//#endregion
|
|
7618
7793
|
//#region src/validation.ts
|
|
7619
7794
|
const CORE_STACK_FLAGS = new Set([
|
|
@@ -7658,6 +7833,7 @@ function extractAndValidateProjectName(projectName, projectDirectory, throwOnErr
|
|
|
7658
7833
|
function processAndValidateFlags(options, providedFlags, projectName) {
|
|
7659
7834
|
if (options.yolo) {
|
|
7660
7835
|
const cfg = processFlags(options, projectName);
|
|
7836
|
+
applyEffectBackendDefaults(cfg, getProvidedFlags(options));
|
|
7661
7837
|
const validatedProjectName$1 = extractAndValidateProjectName(projectName, options.projectDirectory, true);
|
|
7662
7838
|
if (validatedProjectName$1) cfg.projectName = validatedProjectName$1;
|
|
7663
7839
|
return cfg;
|
|
@@ -7669,6 +7845,7 @@ function processAndValidateFlags(options, providedFlags, projectName) {
|
|
|
7669
7845
|
exitWithError(error instanceof Error ? error.message : String(error));
|
|
7670
7846
|
}
|
|
7671
7847
|
const config = processFlags(options, projectName);
|
|
7848
|
+
applyEffectBackendDefaults(config, providedFlags);
|
|
7672
7849
|
const validatedProjectName = extractAndValidateProjectName(projectName, options.projectDirectory, false);
|
|
7673
7850
|
if (validatedProjectName) config.projectName = validatedProjectName;
|
|
7674
7851
|
validateFullConfig(config, providedFlags, options);
|
|
@@ -7683,7 +7860,9 @@ function processProvidedFlagsWithoutValidation(options, projectName) {
|
|
|
7683
7860
|
exitWithError(error instanceof Error ? error.message : String(error));
|
|
7684
7861
|
}
|
|
7685
7862
|
}
|
|
7863
|
+
const providedFlags = getProvidedFlags(options);
|
|
7686
7864
|
const config = processFlags(options, projectName);
|
|
7865
|
+
applyEffectBackendDefaults(config, providedFlags);
|
|
7687
7866
|
const validatedProjectName = extractAndValidateProjectName(projectName, options.projectDirectory, true);
|
|
7688
7867
|
if (validatedProjectName) config.projectName = validatedProjectName;
|
|
7689
7868
|
return config;
|
|
@@ -8955,6 +9134,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
8955
9134
|
const clerkInstructions = config.auth === "clerk" ? getClerkInstructions(config.backend, config.frontend ?? []) : "";
|
|
8956
9135
|
const authSetupInstructions = getAuthSetupInstructions(config.auth, backend, config.frontend ?? []);
|
|
8957
9136
|
const polarInstructions = config.payments === "polar" ? getPolarInstructions(backend, packageManager) : "";
|
|
9137
|
+
const revenueCatInstructions = config.payments === "revenuecat" ? getRevenueCatInstructions(backend, packageManager) : "";
|
|
8958
9138
|
const paymentSetupInstructions = getPaymentSetupInstructions(config.payments, backend);
|
|
8959
9139
|
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
8960
9140
|
const vercelDeployInstructions = getVercelDeployInstructions(webDeploy, serverDeploy, backend);
|
|
@@ -9032,6 +9212,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
9032
9212
|
if (authSetupInstructions) output += `\n${authSetupInstructions.trim()}\n`;
|
|
9033
9213
|
if (betterAuthConvexInstructions) output += `\n${betterAuthConvexInstructions.trim()}\n`;
|
|
9034
9214
|
if (polarInstructions) output += `\n${polarInstructions.trim()}\n`;
|
|
9215
|
+
if (revenueCatInstructions) output += `\n${revenueCatInstructions.trim()}\n`;
|
|
9035
9216
|
if (paymentSetupInstructions) output += `\n${paymentSetupInstructions.trim()}\n`;
|
|
9036
9217
|
if (noOrmWarning) output += `\n${noOrmWarning.trim()}\n`;
|
|
9037
9218
|
if (bunWebNativeWarning) output += `\n${bunWebNativeWarning.trim()}\n`;
|
|
@@ -9152,6 +9333,9 @@ function getPaymentSetupInstructions(payments, backend) {
|
|
|
9152
9333
|
if (payments === "lemon-squeezy") return `${pc.bold("Lemon Squeezy Setup:")}\n${pc.cyan("•")} Get API key from ${pc.underline("https://app.lemonsqueezy.com/settings/api")}\n${pc.cyan("•")} Set ${pc.white("LEMONSQUEEZY_API_KEY")} and ${pc.white("LEMONSQUEEZY_STORE_ID")} in ${pc.white(envPath)}`;
|
|
9153
9334
|
if (payments === "paddle") return `${pc.bold("Paddle Setup:")}\n${pc.cyan("•")} Get API keys from ${pc.underline("https://vendors.paddle.com")}\n${pc.cyan("•")} Set ${pc.white("PADDLE_API_KEY")} and ${pc.white("PADDLE_WEBHOOK_SECRET")} in ${pc.white(envPath)}`;
|
|
9154
9335
|
if (payments === "dodo") return `${pc.bold("Dodo Payments Setup:")}\n${pc.cyan("•")} Get API key from ${pc.underline("https://app.dodopayments.com")}\n${pc.cyan("•")} Set ${pc.white("DODO_PAYMENTS_API_KEY")} and ${pc.white("DODO_PAYMENTS_WEBHOOK_SECRET")} in ${pc.white(envPath)}`;
|
|
9336
|
+
if (payments === "creem") return `${pc.bold("Creem Setup:")}\n${pc.cyan("•")} Get your API key from ${pc.underline("https://docs.creem.io")} (Developers > API Keys)\n${pc.cyan("•")} Set ${pc.white("CREEM_API_KEY")}, ${pc.white("CREEM_WEBHOOK_SECRET")}, and ${pc.white("CREEM_ENVIRONMENT")} (test|prod) in ${pc.white(envPath)}`;
|
|
9337
|
+
if (payments === "autumn") return `${pc.bold("Autumn Setup:")}\n${pc.cyan("•")} Sign up at ${pc.underline("https://useautumn.com")} and connect Stripe\n${pc.cyan("•")} Set ${pc.white("AUTUMN_SECRET_KEY")} in ${pc.white(envPath)} and mount handleAutumnRequest at ${pc.white("/api/autumn/*")}\n${pc.cyan("•")} Define plans/features with ${pc.white("npx atmn@latest")} (Autumn is webhookless)`;
|
|
9338
|
+
if (payments === "commet") return `${pc.bold("Commet Setup:")}\n${pc.cyan("•")} Get your API key from ${pc.underline("https://commet.co")} (Settings > API Keys)\n${pc.cyan("•")} Set ${pc.white("COMMET_API_KEY")} and ${pc.white("COMMET_WEBHOOK_SECRET")} in ${pc.white(envPath)}`;
|
|
9155
9339
|
return "";
|
|
9156
9340
|
}
|
|
9157
9341
|
function getBetterAuthConvexInstructions(hasWeb, webPort, packageManager) {
|
|
@@ -9166,6 +9350,14 @@ function getPolarInstructions(backend, packageManager) {
|
|
|
9166
9350
|
const envPath = backend === "self" ? "apps/web/.env" : "apps/server/.env";
|
|
9167
9351
|
return `${pc.bold("Polar Payments Setup:")}\n${pc.cyan("•")} Get access token & product ID from ${pc.underline("https://sandbox.polar.sh/")}\n${pc.cyan("•")} Set POLAR_ACCESS_TOKEN in ${envPath}`;
|
|
9168
9352
|
}
|
|
9353
|
+
function getRevenueCatInstructions(backend, packageManager) {
|
|
9354
|
+
const base = `${pc.bold("RevenueCat Payments Setup:")}\n${pc.cyan("•")} Create a project, entitlement, and offering in ${pc.underline("https://app.revenuecat.com/")}\n${pc.cyan("•")} Set the public SDK keys in ${pc.white("apps/native/.env")}:\n${pc.white(" EXPO_PUBLIC_REVENUECAT_IOS_KEY=appl_your_ios_key")}\n${pc.white(" EXPO_PUBLIC_REVENUECAT_ANDROID_KEY=goog_your_android_key")}\n${pc.white(" EXPO_PUBLIC_REVENUECAT_ENTITLEMENT_ID=pro")}`;
|
|
9355
|
+
if (backend === "convex") {
|
|
9356
|
+
const cmd = packageManager === "npm" ? "npx" : packageManager;
|
|
9357
|
+
return `${base}\n${pc.cyan("•")} Set the webhook secret (min 32 chars) from ${pc.white("packages/backend")}:\n${pc.white(" cd packages/backend")}\n${pc.white(` ${cmd} convex env set REVENUECAT_WEBHOOK_AUTH your_webhook_secret`)}\n${pc.cyan("•")} Configure a RevenueCat webhook to ${pc.white("https://<your-convex-site-url>/webhooks/revenuecat")} using the same value as the Authorization header`;
|
|
9358
|
+
}
|
|
9359
|
+
return base;
|
|
9360
|
+
}
|
|
9169
9361
|
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
|
|
9170
9362
|
const instructions = [];
|
|
9171
9363
|
const isBackendSelf = backend === "self";
|
|
@@ -9596,6 +9788,7 @@ async function createProject(options, cliInput = {}) {
|
|
|
9596
9788
|
await applyDependencyVersionChannel(projectDir, options.versionChannel);
|
|
9597
9789
|
await writeBtsConfig(options);
|
|
9598
9790
|
await formatProject(projectDir);
|
|
9791
|
+
await recordScaffoldManifest(projectDir);
|
|
9599
9792
|
if (!isSilent()) log.success("Project template successfully scaffolded!");
|
|
9600
9793
|
if (options.install && (options.ecosystem === "typescript" || options.ecosystem === "react-native")) {
|
|
9601
9794
|
const result$1 = await installDependencies({
|
|
@@ -10204,8 +10397,12 @@ const AddCommandInputSchema = CreateCommandOptionsSchema.omit({
|
|
|
10204
10397
|
directoryConflict: true,
|
|
10205
10398
|
renderTitle: true,
|
|
10206
10399
|
disableAnalytics: true,
|
|
10207
|
-
manualDb: true
|
|
10208
|
-
|
|
10400
|
+
manualDb: true,
|
|
10401
|
+
workspaceShape: true
|
|
10402
|
+
}).extend({
|
|
10403
|
+
projectDir: z.string().optional().describe("Project directory (defaults to current)"),
|
|
10404
|
+
acknowledgeArchitectureChange: z.boolean().optional().describe("Acknowledge that this update replaces an existing database/orm/auth/api/backend/runtime choice (data/schema migration required)")
|
|
10405
|
+
});
|
|
10209
10406
|
const ProjectCheckInputSchema = z.tuple([z.string().optional().describe("Project directory to diagnose (defaults to current directory)"), z.object({
|
|
10210
10407
|
skipChecks: z.boolean().optional().default(false).describe("Skip the ecosystem build/type checks (config + deps + env only)"),
|
|
10211
10408
|
json: z.boolean().optional().default(false).describe("Output the diagnosis as JSON")
|
|
@@ -10251,7 +10448,7 @@ const router = os.router({
|
|
|
10251
10448
|
}
|
|
10252
10449
|
}),
|
|
10253
10450
|
add: os.meta({ description: "Add addons, deploy targets, or stack capabilities to an existing Better Fullstack project using its bts.jsonc config" }).input(AddCommandInputSchema).handler(async ({ input }) => {
|
|
10254
|
-
const { addHandler } = await import("./add-handler-
|
|
10451
|
+
const { addHandler } = await import("./add-handler-CuPmSJRM.mjs");
|
|
10255
10452
|
await addHandler(input);
|
|
10256
10453
|
}),
|
|
10257
10454
|
history: os.meta({ description: "Show history of scaffolded projects with reproducible commands" }).input(z.object({
|
|
@@ -10279,7 +10476,7 @@ const router = os.router({
|
|
|
10279
10476
|
ecosystem: z.string().optional().describe("Filter by ecosystem (effect, tanstack, prisma, etc.)"),
|
|
10280
10477
|
"list-ecosystems": z.boolean().default(false).describe("List available ecosystems")
|
|
10281
10478
|
})).handler(async ({ input }) => {
|
|
10282
|
-
const { updateDepsHandler, showEcosystems } = await import("./update-deps-
|
|
10479
|
+
const { updateDepsHandler, showEcosystems } = await import("./update-deps-DLZAuT3V.mjs");
|
|
10283
10480
|
if (input["list-ecosystems"]) {
|
|
10284
10481
|
showEcosystems();
|
|
10285
10482
|
return;
|
|
@@ -10291,13 +10488,63 @@ const router = os.router({
|
|
|
10291
10488
|
ecosystem: input.ecosystem
|
|
10292
10489
|
});
|
|
10293
10490
|
}),
|
|
10491
|
+
gen: os.meta({ description: "Generate in-project code for an existing Better Fullstack project (e.g. `gen resource <name>` / `gen route <name>` for a new trpc/orpc API resource router)" }).input(z.tuple([
|
|
10492
|
+
z.enum(["resource", "route"]).describe("What to generate: resource (alias: route)"),
|
|
10493
|
+
z.string().describe("Name of the resource/route (e.g. post)"),
|
|
10494
|
+
z.object({
|
|
10495
|
+
dir: z.string().optional().describe("Project directory (defaults to current)"),
|
|
10496
|
+
dryRun: z.boolean().default(false).describe("Print the planned changes without writing any files")
|
|
10497
|
+
})
|
|
10498
|
+
])).handler(async ({ input }) => {
|
|
10499
|
+
const [kind, name, options] = input;
|
|
10500
|
+
const { genCommand } = await import("./gen-DWx3Xu_K.mjs");
|
|
10501
|
+
await genCommand({
|
|
10502
|
+
kind,
|
|
10503
|
+
name,
|
|
10504
|
+
dir: options.dir,
|
|
10505
|
+
dryRun: options.dryRun
|
|
10506
|
+
});
|
|
10507
|
+
}),
|
|
10508
|
+
registry: os.meta({ description: "Manage community/private capability packs for an existing Better Fullstack project (`registry add <source>` installs a pack from a local path or file:// URL; `registry list` shows installed packs)" }).input(z.tuple([
|
|
10509
|
+
z.enum(["add", "list"]).optional().default("list").describe("Action to perform: add (install a pack) or list (default)"),
|
|
10510
|
+
z.string().optional().describe("Pack source: a local path or file:// URL (required for `add`)"),
|
|
10511
|
+
z.object({
|
|
10512
|
+
projectDir: z.string().optional().describe("Project directory (defaults to current)"),
|
|
10513
|
+
json: z.boolean().optional().default(false).describe("Output the result as JSON"),
|
|
10514
|
+
dryRun: z.boolean().optional().default(false).describe("Preview the install without writing any files")
|
|
10515
|
+
})
|
|
10516
|
+
])).handler(async ({ input }) => {
|
|
10517
|
+
const [action, source, options] = input;
|
|
10518
|
+
const { registryHandler } = await import("./registry-CxeEOPot.mjs");
|
|
10519
|
+
await registryHandler({
|
|
10520
|
+
action,
|
|
10521
|
+
source,
|
|
10522
|
+
projectDir: options.projectDir,
|
|
10523
|
+
json: options.json,
|
|
10524
|
+
dryRun: options.dryRun
|
|
10525
|
+
});
|
|
10526
|
+
}),
|
|
10527
|
+
update: os.meta({ description: "Re-apply the current bundled templates to an existing Better Fullstack project, classifying template drift vs. your local edits from the bts.lock.json scaffold baseline. Default is a dry-run plan; `--apply` writes safe drift patches + new files. Distinct from the maintainer `update-deps` command." }).input(z.tuple([z.string().optional().describe("Project directory to update (defaults to current directory)"), z.object({
|
|
10528
|
+
dryRun: z.boolean().optional().default(false).describe("Preview the plan without writing (default behavior)"),
|
|
10529
|
+
apply: z.boolean().optional().default(false).describe("Write safe template-drift patches and new files, refreshing the baseline"),
|
|
10530
|
+
check: z.boolean().optional().default(false).describe("Exit non-zero when actionable template drift exists (CI gate)"),
|
|
10531
|
+
json: z.boolean().optional().default(false).describe("Output the plan as JSON"),
|
|
10532
|
+
recordBaseline: z.boolean().optional().default(false).describe("Adopt the current on-disk state as the scaffold baseline (for projects created before the update engine)")
|
|
10533
|
+
})])).handler(async ({ input }) => {
|
|
10534
|
+
const [projectDir, options] = input;
|
|
10535
|
+
const { updateCommand } = await import("./update-C9_x2yBF.mjs");
|
|
10536
|
+
await updateCommand({
|
|
10537
|
+
projectDir,
|
|
10538
|
+
...options
|
|
10539
|
+
});
|
|
10540
|
+
}),
|
|
10294
10541
|
mcp: os.meta({ description: "Start the Better Fullstack MCP server so AI agents can inspect the schema, plan stacks, and scaffold projects over stdio" }).handler(async () => {
|
|
10295
10542
|
log.message("MCP server is started via the 'mcp' subcommand intercepted in cli.ts.");
|
|
10296
10543
|
log.message("Run: create-better-fullstack mcp");
|
|
10297
10544
|
}),
|
|
10298
10545
|
doctor: os.meta({ description: "Diagnose a scaffolded Better Fullstack project: verify its bts.jsonc, installed dependencies, required env vars, and run ecosystem build/type checks" }).input(ProjectCheckInputSchema).handler(async ({ input }) => {
|
|
10299
10546
|
const [projectDir, options] = input;
|
|
10300
|
-
const { doctorCommand } = await import("./doctor-
|
|
10547
|
+
const { doctorCommand } = await import("./doctor-DucDyWfl.mjs");
|
|
10301
10548
|
await doctorCommand({
|
|
10302
10549
|
projectDir,
|
|
10303
10550
|
...options
|
|
@@ -10305,11 +10552,28 @@ const router = os.router({
|
|
|
10305
10552
|
}),
|
|
10306
10553
|
check: os.meta({ description: "Check a scaffolded Better Fullstack project for config, dependency, env, and generated build/type drift" }).input(ProjectCheckInputSchema).handler(async ({ input }) => {
|
|
10307
10554
|
const [projectDir, options] = input;
|
|
10308
|
-
const { doctorCommand } = await import("./doctor-
|
|
10555
|
+
const { doctorCommand } = await import("./doctor-DucDyWfl.mjs");
|
|
10309
10556
|
await doctorCommand({
|
|
10310
10557
|
projectDir,
|
|
10311
10558
|
...options
|
|
10312
10559
|
});
|
|
10560
|
+
}),
|
|
10561
|
+
recommend: os.meta({ description: "Recommend a stack from a natural-language brief (prompt-to-stack): prints the suggested config, the rationale, and a ready-to-run create command" }).input(z.object({
|
|
10562
|
+
brief: z.string().min(1).describe("Natural-language description, e.g. \"a SaaS with Postgres, auth and payments\""),
|
|
10563
|
+
ecosystem: z.string().optional().describe("Force an ecosystem (typescript, react-native, rust, go, python, java, ...)"),
|
|
10564
|
+
json: z.boolean().default(false).describe("Output the recommendation as JSON")
|
|
10565
|
+
})).handler(async ({ input }) => {
|
|
10566
|
+
const { recommendStackFromBrief } = await import("./mcp-D9O5zgAA.mjs");
|
|
10567
|
+
const result = recommendStackFromBrief(input.brief, input.ecosystem);
|
|
10568
|
+
if (input.json) {
|
|
10569
|
+
log.message(JSON.stringify(result, null, 2));
|
|
10570
|
+
return;
|
|
10571
|
+
}
|
|
10572
|
+
log.message("Recommended stack:");
|
|
10573
|
+
for (const line of result.rationale) log.message(` • ${line}`);
|
|
10574
|
+
log.message(`\nConfig: ${JSON.stringify(result.input)}`);
|
|
10575
|
+
if (result.matchedPreset) log.message(`Nearest preset: ${result.matchedPreset}`);
|
|
10576
|
+
log.message("\nReview, then scaffold with: create-better-fullstack create <name> [flags]");
|
|
10313
10577
|
})
|
|
10314
10578
|
});
|
|
10315
10579
|
const caller = createRouterClient(router, { context: {} });
|
|
@@ -10375,7 +10639,7 @@ async function builder() {
|
|
|
10375
10639
|
return caller.builder();
|
|
10376
10640
|
}
|
|
10377
10641
|
async function add(input) {
|
|
10378
|
-
const { addHandler } = await import("./add-handler-
|
|
10642
|
+
const { addHandler } = await import("./add-handler-CuPmSJRM.mjs");
|
|
10379
10643
|
return addHandler(input, { silent: true });
|
|
10380
10644
|
}
|
|
10381
10645
|
async function history(options) {
|
|
@@ -10400,6 +10664,15 @@ async function check(projectDir, options) {
|
|
|
10400
10664
|
json: options?.json ?? false
|
|
10401
10665
|
}]);
|
|
10402
10666
|
}
|
|
10667
|
+
async function update(projectDir, options) {
|
|
10668
|
+
return caller.update([projectDir, {
|
|
10669
|
+
dryRun: options?.dryRun ?? false,
|
|
10670
|
+
apply: options?.apply ?? false,
|
|
10671
|
+
check: options?.check ?? false,
|
|
10672
|
+
json: options?.json ?? false,
|
|
10673
|
+
recordBaseline: options?.recordBaseline ?? false
|
|
10674
|
+
}]);
|
|
10675
|
+
}
|
|
10403
10676
|
|
|
10404
10677
|
//#endregion
|
|
10405
|
-
export { createBtsCli as a, history as c, telemetry as d, create as i, router as l, builder as n, docs as o, check as r, doctor as s, add as t, sponsors as u };
|
|
10678
|
+
export { createBtsCli as a, history as c, telemetry as d, update as f, create as i, router as l, builder as n, docs as o, check as r, doctor as s, add as t, sponsors as u };
|