create-better-fullstack 1.5.2 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{addons-setup-1jZXP0D3.mjs → addons-setup-Bll5VFJd.mjs} +48 -17
- package/dist/addons-setup-CWGwxN68.mjs +5 -0
- package/dist/{bts-config-BCe8SqYV.mjs → bts-config-BYD8mHt-.mjs} +3 -0
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +201 -7
- package/dist/index.mjs +239 -98
- package/dist/{mcp-C_X1WfCg.mjs → mcp-CRjipp-w.mjs} +1 -1
- package/dist/mcp-entry.mjs +9 -4
- package/package.json +7 -7
- package/dist/addons-setup-AbN693PV.mjs +0 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { s as dependencyVersionMap, t as readBtsConfig } from "./bts-config-
|
|
2
|
+
import { s as dependencyVersionMap, t as readBtsConfig } from "./bts-config-BYD8mHt-.mjs";
|
|
3
3
|
import { autocompleteMultiselect, cancel, group, isCancel, log, multiselect, select, spinner } from "@clack/prompts";
|
|
4
4
|
import pc from "picocolors";
|
|
5
5
|
import fs from "fs-extra";
|
|
@@ -255,6 +255,35 @@ function shouldSkipExternalCommands() {
|
|
|
255
255
|
return normalized === "1" || normalized === "true" || normalized === "yes";
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region src/helpers/addons/retry-install.ts
|
|
260
|
+
const DEFAULT_MAX_ATTEMPTS = 3;
|
|
261
|
+
const DEFAULT_INITIAL_DELAY_MS = 750;
|
|
262
|
+
const RETRY_BACKOFF_MULTIPLIER = 2;
|
|
263
|
+
function defaultSleep(ms) {
|
|
264
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
265
|
+
}
|
|
266
|
+
function formatDelay(ms) {
|
|
267
|
+
return ms >= 1e3 ? `${(ms / 1e3).toFixed(ms % 1e3 === 0 ? 0 : 1)}s` : `${ms}ms`;
|
|
268
|
+
}
|
|
269
|
+
async function runInstallWithRetries({ description, run, maxAttempts = DEFAULT_MAX_ATTEMPTS, initialDelayMs = DEFAULT_INITIAL_DELAY_MS, sleep = defaultSleep }) {
|
|
270
|
+
let delayMs = initialDelayMs;
|
|
271
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
|
|
272
|
+
await run();
|
|
273
|
+
return true;
|
|
274
|
+
} catch (error) {
|
|
275
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
276
|
+
if (attempt === maxAttempts) {
|
|
277
|
+
log.warn(pc.yellow(`Warning: Could not ${description} after ${maxAttempts} attempts: ${message}`));
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
log.warn(pc.yellow(`Warning: Failed to ${description} (attempt ${attempt}/${maxAttempts}): ${message}. Retrying in ${formatDelay(delayMs)}...`));
|
|
281
|
+
await sleep(delayMs);
|
|
282
|
+
delayMs *= RETRY_BACKOFF_MULTIPLIER;
|
|
283
|
+
}
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
|
|
258
287
|
//#endregion
|
|
259
288
|
//#region src/helpers/addons/mcp-setup.ts
|
|
260
289
|
const MCP_AGENTS = [
|
|
@@ -489,14 +518,15 @@ async function setupMcp(config) {
|
|
|
489
518
|
...globalFlags,
|
|
490
519
|
"-y"
|
|
491
520
|
];
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
521
|
+
await runInstallWithRetries({
|
|
522
|
+
description: `install MCP server '${server.name}'`,
|
|
523
|
+
run: async () => {
|
|
524
|
+
await $({
|
|
525
|
+
cwd: projectDir,
|
|
526
|
+
env: { CI: "true" }
|
|
527
|
+
})`${args}`;
|
|
528
|
+
}
|
|
529
|
+
});
|
|
500
530
|
}
|
|
501
531
|
installSpinner.stop("MCP servers installed");
|
|
502
532
|
}
|
|
@@ -912,14 +942,15 @@ async function setupSkills(config) {
|
|
|
912
942
|
...agentFlags,
|
|
913
943
|
"-y"
|
|
914
944
|
];
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
945
|
+
await runInstallWithRetries({
|
|
946
|
+
description: `install skills from ${source}`,
|
|
947
|
+
run: async () => {
|
|
948
|
+
await $({
|
|
949
|
+
cwd: projectDir,
|
|
950
|
+
env: { CI: "true" }
|
|
951
|
+
})`${args}`;
|
|
952
|
+
}
|
|
953
|
+
});
|
|
923
954
|
}
|
|
924
955
|
installSpinner.stop("Skills installed");
|
|
925
956
|
}
|
|
@@ -72,6 +72,7 @@ const DEFAULT_CONFIG_BASE = {
|
|
|
72
72
|
rustApi: "none",
|
|
73
73
|
rustCli: "none",
|
|
74
74
|
rustLibraries: [],
|
|
75
|
+
rustLogging: "tracing",
|
|
75
76
|
pythonWebFramework: "fastapi",
|
|
76
77
|
pythonOrm: "sqlalchemy",
|
|
77
78
|
pythonValidation: "pydantic",
|
|
@@ -181,6 +182,7 @@ async function writeBtsConfig(projectConfig) {
|
|
|
181
182
|
rustApi: projectConfig.rustApi,
|
|
182
183
|
rustCli: projectConfig.rustCli,
|
|
183
184
|
rustLibraries: projectConfig.rustLibraries,
|
|
185
|
+
rustLogging: projectConfig.rustLogging,
|
|
184
186
|
pythonWebFramework: projectConfig.pythonWebFramework,
|
|
185
187
|
pythonOrm: projectConfig.pythonOrm,
|
|
186
188
|
pythonValidation: projectConfig.pythonValidation,
|
|
@@ -241,6 +243,7 @@ async function writeBtsConfig(projectConfig) {
|
|
|
241
243
|
rustApi: btsConfig.rustApi,
|
|
242
244
|
rustCli: btsConfig.rustCli,
|
|
243
245
|
rustLibraries: btsConfig.rustLibraries,
|
|
246
|
+
rustLogging: btsConfig.rustLogging,
|
|
244
247
|
pythonWebFramework: btsConfig.pythonWebFramework,
|
|
245
248
|
pythonOrm: btsConfig.pythonOrm,
|
|
246
249
|
pythonValidation: btsConfig.pythonValidation,
|
package/dist/cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
//#region src/cli.ts
|
|
3
|
-
if (process.argv[2] === "mcp" && process.argv.length === 3) import("./mcp-
|
|
3
|
+
if (process.argv[2] === "mcp" && process.argv.length === 3) import("./mcp-CRjipp-w.mjs").then((m) => m.startMcpServer());
|
|
4
4
|
else import("./index.mjs").then((m) => m.createBtsCli().run());
|
|
5
5
|
|
|
6
6
|
//#endregion
|
package/dist/index.d.mts
CHANGED
|
@@ -30,6 +30,7 @@ declare const router: {
|
|
|
30
30
|
yes: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
31
31
|
yolo: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
32
32
|
verbose: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
33
|
+
dryRun: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
33
34
|
ecosystem: z.ZodOptional<z.ZodEnum<{
|
|
34
35
|
typescript: "typescript";
|
|
35
36
|
rust: "rust";
|
|
@@ -208,6 +209,7 @@ declare const router: {
|
|
|
208
209
|
meilisearch: "meilisearch";
|
|
209
210
|
typesense: "typesense";
|
|
210
211
|
elasticsearch: "elasticsearch";
|
|
212
|
+
algolia: "algolia";
|
|
211
213
|
}>>;
|
|
212
214
|
fileStorage: z.ZodOptional<z.ZodEnum<{
|
|
213
215
|
none: "none";
|
|
@@ -472,10 +474,16 @@ declare const router: {
|
|
|
472
474
|
"tokio-test": "tokio-test";
|
|
473
475
|
mockall: "mockall";
|
|
474
476
|
}>>>;
|
|
477
|
+
rustLogging: z.ZodOptional<z.ZodEnum<{
|
|
478
|
+
none: "none";
|
|
479
|
+
tracing: "tracing";
|
|
480
|
+
"env-logger": "env-logger";
|
|
481
|
+
}>>;
|
|
475
482
|
pythonWebFramework: z.ZodOptional<z.ZodEnum<{
|
|
476
483
|
none: "none";
|
|
477
484
|
fastapi: "fastapi";
|
|
478
485
|
django: "django";
|
|
486
|
+
flask: "flask";
|
|
479
487
|
}>>;
|
|
480
488
|
pythonOrm: z.ZodOptional<z.ZodEnum<{
|
|
481
489
|
none: "none";
|
|
@@ -507,6 +515,7 @@ declare const router: {
|
|
|
507
515
|
none: "none";
|
|
508
516
|
gin: "gin";
|
|
509
517
|
echo: "echo";
|
|
518
|
+
fiber: "fiber";
|
|
510
519
|
}>>;
|
|
511
520
|
goOrm: z.ZodOptional<z.ZodEnum<{
|
|
512
521
|
none: "none";
|
|
@@ -575,7 +584,90 @@ declare const router: {
|
|
|
575
584
|
analytics: "none" | "plausible" | "umami";
|
|
576
585
|
cms: "none" | "payload" | "sanity" | "strapi" | "tinacms";
|
|
577
586
|
caching: "none" | "upstash-redis";
|
|
578
|
-
search: "none" | "meilisearch" | "typesense" | "elasticsearch";
|
|
587
|
+
search: "none" | "meilisearch" | "typesense" | "elasticsearch" | "algolia";
|
|
588
|
+
fileStorage: "none" | "s3" | "r2";
|
|
589
|
+
rustWebFramework: "none" | "axum" | "actix-web";
|
|
590
|
+
rustFrontend: "none" | "leptos" | "dioxus";
|
|
591
|
+
rustOrm: "none" | "sea-orm" | "sqlx";
|
|
592
|
+
rustApi: "none" | "tonic" | "async-graphql";
|
|
593
|
+
rustCli: "none" | "clap" | "ratatui";
|
|
594
|
+
rustLibraries: ("none" | "serde" | "validator" | "jsonwebtoken" | "argon2" | "tokio-test" | "mockall")[];
|
|
595
|
+
rustLogging: "none" | "tracing" | "env-logger";
|
|
596
|
+
pythonWebFramework: "none" | "fastapi" | "django" | "flask";
|
|
597
|
+
pythonOrm: "none" | "sqlalchemy" | "sqlmodel";
|
|
598
|
+
pythonValidation: "none" | "pydantic";
|
|
599
|
+
pythonAi: ("none" | "langgraph" | "langchain" | "llamaindex" | "openai-sdk" | "anthropic-sdk" | "crewai")[];
|
|
600
|
+
pythonTaskQueue: "none" | "celery";
|
|
601
|
+
pythonQuality: "none" | "ruff";
|
|
602
|
+
goWebFramework: "none" | "gin" | "echo" | "fiber";
|
|
603
|
+
goOrm: "none" | "gorm" | "sqlc";
|
|
604
|
+
goApi: "none" | "grpc-go";
|
|
605
|
+
goCli: "none" | "cobra" | "bubbletea";
|
|
606
|
+
goLogging: "none" | "zap";
|
|
607
|
+
aiDocs: ("none" | "claude-md" | "agents-md" | "cursorrules")[];
|
|
608
|
+
astroIntegration?: "none" | "svelte" | "solid" | "react" | "vue" | undefined;
|
|
609
|
+
shadcnBase?: "radix" | "base" | undefined;
|
|
610
|
+
shadcnStyle?: "vega" | "nova" | "maia" | "lyra" | "mira" | undefined;
|
|
611
|
+
shadcnIconLibrary?: "lucide" | "tabler" | "hugeicons" | "phosphor" | "remixicon" | undefined;
|
|
612
|
+
shadcnColorTheme?: "neutral" | "stone" | "zinc" | "gray" | "amber" | "blue" | "cyan" | "emerald" | "fuchsia" | "green" | "indigo" | "lime" | "orange" | "pink" | "purple" | "red" | "rose" | "sky" | "teal" | "violet" | "yellow" | undefined;
|
|
613
|
+
shadcnBaseColor?: "neutral" | "stone" | "zinc" | "gray" | undefined;
|
|
614
|
+
shadcnFont?: "inter" | "geist" | "noto-sans" | "nunito-sans" | "figtree" | "roboto" | "raleway" | "dm-sans" | "public-sans" | "outfit" | "jetbrains-mono" | "geist-mono" | undefined;
|
|
615
|
+
shadcnRadius?: "default" | "none" | "small" | "medium" | "large" | undefined;
|
|
616
|
+
};
|
|
617
|
+
reproducibleCommand: string;
|
|
618
|
+
timeScaffolded: string;
|
|
619
|
+
elapsedTimeMs: number;
|
|
620
|
+
projectDirectory: string;
|
|
621
|
+
relativePath: string;
|
|
622
|
+
dryRun: boolean;
|
|
623
|
+
fileCount: number;
|
|
624
|
+
directoryCount: number;
|
|
625
|
+
files: string[];
|
|
626
|
+
error?: undefined;
|
|
627
|
+
} | {
|
|
628
|
+
success: boolean;
|
|
629
|
+
projectConfig: {
|
|
630
|
+
projectName: string;
|
|
631
|
+
projectDir: string;
|
|
632
|
+
relativePath: string;
|
|
633
|
+
ecosystem: "typescript" | "rust" | "python" | "go";
|
|
634
|
+
database: "none" | "sqlite" | "postgres" | "mysql" | "mongodb" | "edgedb" | "redis";
|
|
635
|
+
orm: "none" | "drizzle" | "prisma" | "mongoose" | "typeorm" | "kysely" | "mikroorm" | "sequelize";
|
|
636
|
+
backend: "none" | "hono" | "express" | "fastify" | "elysia" | "fets" | "nestjs" | "adonisjs" | "nitro" | "encore" | "convex" | "self";
|
|
637
|
+
runtime: "none" | "bun" | "node" | "workers";
|
|
638
|
+
frontend: ("none" | "tanstack-router" | "react-router" | "react-vite" | "tanstack-start" | "next" | "nuxt" | "native-bare" | "native-uniwind" | "native-unistyles" | "svelte" | "solid" | "solid-start" | "astro" | "qwik" | "angular" | "redwood" | "fresh")[];
|
|
639
|
+
addons: ("none" | "pwa" | "tauri" | "starlight" | "biome" | "lefthook" | "husky" | "ruler" | "mcp" | "skills" | "turborepo" | "fumadocs" | "ultracite" | "oxlint" | "opentui" | "wxt" | "msw" | "storybook" | "tanstack-query" | "tanstack-table" | "tanstack-virtual" | "tanstack-db" | "tanstack-pacer")[];
|
|
640
|
+
examples: ("ai" | "none" | "chat-sdk" | "tanstack-showcase")[];
|
|
641
|
+
auth: "none" | "better-auth" | "go-better-auth" | "clerk" | "nextauth" | "stack-auth" | "supabase-auth" | "auth0";
|
|
642
|
+
payments: "none" | "polar" | "stripe" | "lemon-squeezy" | "paddle" | "dodo";
|
|
643
|
+
git: boolean;
|
|
644
|
+
packageManager: "bun" | "npm" | "pnpm" | "yarn";
|
|
645
|
+
versionChannel: "stable" | "latest" | "beta";
|
|
646
|
+
install: boolean;
|
|
647
|
+
dbSetup: "none" | "turso" | "neon" | "prisma-postgres" | "planetscale" | "mongodb-atlas" | "supabase" | "upstash" | "d1" | "docker";
|
|
648
|
+
api: "none" | "trpc" | "orpc" | "ts-rest" | "garph";
|
|
649
|
+
webDeploy: "none" | "docker" | "cloudflare" | "fly" | "railway" | "sst";
|
|
650
|
+
serverDeploy: "none" | "docker" | "cloudflare" | "fly" | "railway" | "sst";
|
|
651
|
+
ai: "none" | "langgraph" | "langchain" | "llamaindex" | "vercel-ai" | "mastra" | "voltagent" | "openai-agents" | "google-adk" | "modelfusion" | "tanstack-ai";
|
|
652
|
+
effect: "effect" | "none" | "effect-full";
|
|
653
|
+
stateManagement: "none" | "zustand" | "jotai" | "nanostores" | "redux-toolkit" | "mobx" | "xstate" | "valtio" | "tanstack-store" | "legend-state";
|
|
654
|
+
forms: "none" | "tanstack-form" | "react-hook-form" | "formik" | "final-form" | "conform" | "modular-forms";
|
|
655
|
+
testing: "none" | "vitest" | "playwright" | "vitest-playwright" | "jest" | "cypress";
|
|
656
|
+
email: "none" | "react-email" | "resend" | "nodemailer" | "postmark" | "sendgrid" | "aws-ses" | "mailgun" | "plunk";
|
|
657
|
+
cssFramework: "none" | "tailwind" | "scss" | "less" | "postcss-only";
|
|
658
|
+
uiLibrary: "none" | "shadcn-ui" | "daisyui" | "radix-ui" | "headless-ui" | "park-ui" | "chakra-ui" | "nextui" | "mantine" | "base-ui" | "ark-ui" | "react-aria";
|
|
659
|
+
validation: "none" | "zod" | "valibot" | "arktype" | "typebox" | "typia" | "runtypes" | "effect-schema";
|
|
660
|
+
realtime: "none" | "socket-io" | "partykit" | "ably" | "pusher" | "liveblocks" | "yjs";
|
|
661
|
+
jobQueue: "none" | "bullmq" | "trigger-dev" | "inngest" | "temporal";
|
|
662
|
+
animation: "none" | "framer-motion" | "gsap" | "react-spring" | "auto-animate" | "lottie";
|
|
663
|
+
fileUpload: "none" | "uploadthing" | "filepond" | "uppy";
|
|
664
|
+
logging: "none" | "pino" | "winston";
|
|
665
|
+
observability: "none" | "opentelemetry" | "sentry" | "grafana";
|
|
666
|
+
featureFlags: "none" | "growthbook" | "posthog";
|
|
667
|
+
analytics: "none" | "plausible" | "umami";
|
|
668
|
+
cms: "none" | "payload" | "sanity" | "strapi" | "tinacms";
|
|
669
|
+
caching: "none" | "upstash-redis";
|
|
670
|
+
search: "none" | "meilisearch" | "typesense" | "elasticsearch" | "algolia";
|
|
579
671
|
fileStorage: "none" | "s3" | "r2";
|
|
580
672
|
rustWebFramework: "none" | "axum" | "actix-web";
|
|
581
673
|
rustFrontend: "none" | "leptos" | "dioxus";
|
|
@@ -583,13 +675,14 @@ declare const router: {
|
|
|
583
675
|
rustApi: "none" | "tonic" | "async-graphql";
|
|
584
676
|
rustCli: "none" | "clap" | "ratatui";
|
|
585
677
|
rustLibraries: ("none" | "serde" | "validator" | "jsonwebtoken" | "argon2" | "tokio-test" | "mockall")[];
|
|
586
|
-
|
|
678
|
+
rustLogging: "none" | "tracing" | "env-logger";
|
|
679
|
+
pythonWebFramework: "none" | "fastapi" | "django" | "flask";
|
|
587
680
|
pythonOrm: "none" | "sqlalchemy" | "sqlmodel";
|
|
588
681
|
pythonValidation: "none" | "pydantic";
|
|
589
682
|
pythonAi: ("none" | "langgraph" | "langchain" | "llamaindex" | "openai-sdk" | "anthropic-sdk" | "crewai")[];
|
|
590
683
|
pythonTaskQueue: "none" | "celery";
|
|
591
684
|
pythonQuality: "none" | "ruff";
|
|
592
|
-
goWebFramework: "none" | "gin" | "echo";
|
|
685
|
+
goWebFramework: "none" | "gin" | "echo" | "fiber";
|
|
593
686
|
goOrm: "none" | "gorm" | "sqlc";
|
|
594
687
|
goApi: "none" | "grpc-go";
|
|
595
688
|
goCli: "none" | "cobra" | "bubbletea";
|
|
@@ -609,6 +702,10 @@ declare const router: {
|
|
|
609
702
|
elapsedTimeMs: number;
|
|
610
703
|
projectDirectory: string;
|
|
611
704
|
relativePath: string;
|
|
705
|
+
dryRun?: undefined;
|
|
706
|
+
fileCount?: undefined;
|
|
707
|
+
directoryCount?: undefined;
|
|
708
|
+
files?: undefined;
|
|
612
709
|
error?: undefined;
|
|
613
710
|
} | {
|
|
614
711
|
success: boolean;
|
|
@@ -619,6 +716,10 @@ declare const router: {
|
|
|
619
716
|
elapsedTimeMs: number;
|
|
620
717
|
projectDirectory: string;
|
|
621
718
|
relativePath: string;
|
|
719
|
+
dryRun?: undefined;
|
|
720
|
+
fileCount?: undefined;
|
|
721
|
+
directoryCount?: undefined;
|
|
722
|
+
files?: undefined;
|
|
622
723
|
} | undefined, {
|
|
623
724
|
success: boolean;
|
|
624
725
|
projectConfig: {
|
|
@@ -662,7 +763,90 @@ declare const router: {
|
|
|
662
763
|
analytics: "none" | "plausible" | "umami";
|
|
663
764
|
cms: "none" | "payload" | "sanity" | "strapi" | "tinacms";
|
|
664
765
|
caching: "none" | "upstash-redis";
|
|
665
|
-
search: "none" | "meilisearch" | "typesense" | "elasticsearch";
|
|
766
|
+
search: "none" | "meilisearch" | "typesense" | "elasticsearch" | "algolia";
|
|
767
|
+
fileStorage: "none" | "s3" | "r2";
|
|
768
|
+
rustWebFramework: "none" | "axum" | "actix-web";
|
|
769
|
+
rustFrontend: "none" | "leptos" | "dioxus";
|
|
770
|
+
rustOrm: "none" | "sea-orm" | "sqlx";
|
|
771
|
+
rustApi: "none" | "tonic" | "async-graphql";
|
|
772
|
+
rustCli: "none" | "clap" | "ratatui";
|
|
773
|
+
rustLibraries: ("none" | "serde" | "validator" | "jsonwebtoken" | "argon2" | "tokio-test" | "mockall")[];
|
|
774
|
+
rustLogging: "none" | "tracing" | "env-logger";
|
|
775
|
+
pythonWebFramework: "none" | "fastapi" | "django" | "flask";
|
|
776
|
+
pythonOrm: "none" | "sqlalchemy" | "sqlmodel";
|
|
777
|
+
pythonValidation: "none" | "pydantic";
|
|
778
|
+
pythonAi: ("none" | "langgraph" | "langchain" | "llamaindex" | "openai-sdk" | "anthropic-sdk" | "crewai")[];
|
|
779
|
+
pythonTaskQueue: "none" | "celery";
|
|
780
|
+
pythonQuality: "none" | "ruff";
|
|
781
|
+
goWebFramework: "none" | "gin" | "echo" | "fiber";
|
|
782
|
+
goOrm: "none" | "gorm" | "sqlc";
|
|
783
|
+
goApi: "none" | "grpc-go";
|
|
784
|
+
goCli: "none" | "cobra" | "bubbletea";
|
|
785
|
+
goLogging: "none" | "zap";
|
|
786
|
+
aiDocs: ("none" | "claude-md" | "agents-md" | "cursorrules")[];
|
|
787
|
+
astroIntegration?: "none" | "svelte" | "solid" | "react" | "vue" | undefined;
|
|
788
|
+
shadcnBase?: "radix" | "base" | undefined;
|
|
789
|
+
shadcnStyle?: "vega" | "nova" | "maia" | "lyra" | "mira" | undefined;
|
|
790
|
+
shadcnIconLibrary?: "lucide" | "tabler" | "hugeicons" | "phosphor" | "remixicon" | undefined;
|
|
791
|
+
shadcnColorTheme?: "neutral" | "stone" | "zinc" | "gray" | "amber" | "blue" | "cyan" | "emerald" | "fuchsia" | "green" | "indigo" | "lime" | "orange" | "pink" | "purple" | "red" | "rose" | "sky" | "teal" | "violet" | "yellow" | undefined;
|
|
792
|
+
shadcnBaseColor?: "neutral" | "stone" | "zinc" | "gray" | undefined;
|
|
793
|
+
shadcnFont?: "inter" | "geist" | "noto-sans" | "nunito-sans" | "figtree" | "roboto" | "raleway" | "dm-sans" | "public-sans" | "outfit" | "jetbrains-mono" | "geist-mono" | undefined;
|
|
794
|
+
shadcnRadius?: "default" | "none" | "small" | "medium" | "large" | undefined;
|
|
795
|
+
};
|
|
796
|
+
reproducibleCommand: string;
|
|
797
|
+
timeScaffolded: string;
|
|
798
|
+
elapsedTimeMs: number;
|
|
799
|
+
projectDirectory: string;
|
|
800
|
+
relativePath: string;
|
|
801
|
+
dryRun: boolean;
|
|
802
|
+
fileCount: number;
|
|
803
|
+
directoryCount: number;
|
|
804
|
+
files: string[];
|
|
805
|
+
error?: undefined;
|
|
806
|
+
} | {
|
|
807
|
+
success: boolean;
|
|
808
|
+
projectConfig: {
|
|
809
|
+
projectName: string;
|
|
810
|
+
projectDir: string;
|
|
811
|
+
relativePath: string;
|
|
812
|
+
ecosystem: "typescript" | "rust" | "python" | "go";
|
|
813
|
+
database: "none" | "sqlite" | "postgres" | "mysql" | "mongodb" | "edgedb" | "redis";
|
|
814
|
+
orm: "none" | "drizzle" | "prisma" | "mongoose" | "typeorm" | "kysely" | "mikroorm" | "sequelize";
|
|
815
|
+
backend: "none" | "hono" | "express" | "fastify" | "elysia" | "fets" | "nestjs" | "adonisjs" | "nitro" | "encore" | "convex" | "self";
|
|
816
|
+
runtime: "none" | "bun" | "node" | "workers";
|
|
817
|
+
frontend: ("none" | "tanstack-router" | "react-router" | "react-vite" | "tanstack-start" | "next" | "nuxt" | "native-bare" | "native-uniwind" | "native-unistyles" | "svelte" | "solid" | "solid-start" | "astro" | "qwik" | "angular" | "redwood" | "fresh")[];
|
|
818
|
+
addons: ("none" | "pwa" | "tauri" | "starlight" | "biome" | "lefthook" | "husky" | "ruler" | "mcp" | "skills" | "turborepo" | "fumadocs" | "ultracite" | "oxlint" | "opentui" | "wxt" | "msw" | "storybook" | "tanstack-query" | "tanstack-table" | "tanstack-virtual" | "tanstack-db" | "tanstack-pacer")[];
|
|
819
|
+
examples: ("ai" | "none" | "chat-sdk" | "tanstack-showcase")[];
|
|
820
|
+
auth: "none" | "better-auth" | "go-better-auth" | "clerk" | "nextauth" | "stack-auth" | "supabase-auth" | "auth0";
|
|
821
|
+
payments: "none" | "polar" | "stripe" | "lemon-squeezy" | "paddle" | "dodo";
|
|
822
|
+
git: boolean;
|
|
823
|
+
packageManager: "bun" | "npm" | "pnpm" | "yarn";
|
|
824
|
+
versionChannel: "stable" | "latest" | "beta";
|
|
825
|
+
install: boolean;
|
|
826
|
+
dbSetup: "none" | "turso" | "neon" | "prisma-postgres" | "planetscale" | "mongodb-atlas" | "supabase" | "upstash" | "d1" | "docker";
|
|
827
|
+
api: "none" | "trpc" | "orpc" | "ts-rest" | "garph";
|
|
828
|
+
webDeploy: "none" | "docker" | "cloudflare" | "fly" | "railway" | "sst";
|
|
829
|
+
serverDeploy: "none" | "docker" | "cloudflare" | "fly" | "railway" | "sst";
|
|
830
|
+
ai: "none" | "langgraph" | "langchain" | "llamaindex" | "vercel-ai" | "mastra" | "voltagent" | "openai-agents" | "google-adk" | "modelfusion" | "tanstack-ai";
|
|
831
|
+
effect: "effect" | "none" | "effect-full";
|
|
832
|
+
stateManagement: "none" | "zustand" | "jotai" | "nanostores" | "redux-toolkit" | "mobx" | "xstate" | "valtio" | "tanstack-store" | "legend-state";
|
|
833
|
+
forms: "none" | "tanstack-form" | "react-hook-form" | "formik" | "final-form" | "conform" | "modular-forms";
|
|
834
|
+
testing: "none" | "vitest" | "playwright" | "vitest-playwright" | "jest" | "cypress";
|
|
835
|
+
email: "none" | "react-email" | "resend" | "nodemailer" | "postmark" | "sendgrid" | "aws-ses" | "mailgun" | "plunk";
|
|
836
|
+
cssFramework: "none" | "tailwind" | "scss" | "less" | "postcss-only";
|
|
837
|
+
uiLibrary: "none" | "shadcn-ui" | "daisyui" | "radix-ui" | "headless-ui" | "park-ui" | "chakra-ui" | "nextui" | "mantine" | "base-ui" | "ark-ui" | "react-aria";
|
|
838
|
+
validation: "none" | "zod" | "valibot" | "arktype" | "typebox" | "typia" | "runtypes" | "effect-schema";
|
|
839
|
+
realtime: "none" | "socket-io" | "partykit" | "ably" | "pusher" | "liveblocks" | "yjs";
|
|
840
|
+
jobQueue: "none" | "bullmq" | "trigger-dev" | "inngest" | "temporal";
|
|
841
|
+
animation: "none" | "framer-motion" | "gsap" | "react-spring" | "auto-animate" | "lottie";
|
|
842
|
+
fileUpload: "none" | "uploadthing" | "filepond" | "uppy";
|
|
843
|
+
logging: "none" | "pino" | "winston";
|
|
844
|
+
observability: "none" | "opentelemetry" | "sentry" | "grafana";
|
|
845
|
+
featureFlags: "none" | "growthbook" | "posthog";
|
|
846
|
+
analytics: "none" | "plausible" | "umami";
|
|
847
|
+
cms: "none" | "payload" | "sanity" | "strapi" | "tinacms";
|
|
848
|
+
caching: "none" | "upstash-redis";
|
|
849
|
+
search: "none" | "meilisearch" | "typesense" | "elasticsearch" | "algolia";
|
|
666
850
|
fileStorage: "none" | "s3" | "r2";
|
|
667
851
|
rustWebFramework: "none" | "axum" | "actix-web";
|
|
668
852
|
rustFrontend: "none" | "leptos" | "dioxus";
|
|
@@ -670,13 +854,14 @@ declare const router: {
|
|
|
670
854
|
rustApi: "none" | "tonic" | "async-graphql";
|
|
671
855
|
rustCli: "none" | "clap" | "ratatui";
|
|
672
856
|
rustLibraries: ("none" | "serde" | "validator" | "jsonwebtoken" | "argon2" | "tokio-test" | "mockall")[];
|
|
673
|
-
|
|
857
|
+
rustLogging: "none" | "tracing" | "env-logger";
|
|
858
|
+
pythonWebFramework: "none" | "fastapi" | "django" | "flask";
|
|
674
859
|
pythonOrm: "none" | "sqlalchemy" | "sqlmodel";
|
|
675
860
|
pythonValidation: "none" | "pydantic";
|
|
676
861
|
pythonAi: ("none" | "langgraph" | "langchain" | "llamaindex" | "openai-sdk" | "anthropic-sdk" | "crewai")[];
|
|
677
862
|
pythonTaskQueue: "none" | "celery";
|
|
678
863
|
pythonQuality: "none" | "ruff";
|
|
679
|
-
goWebFramework: "none" | "gin" | "echo";
|
|
864
|
+
goWebFramework: "none" | "gin" | "echo" | "fiber";
|
|
680
865
|
goOrm: "none" | "gorm" | "sqlc";
|
|
681
866
|
goApi: "none" | "grpc-go";
|
|
682
867
|
goCli: "none" | "cobra" | "bubbletea";
|
|
@@ -696,6 +881,10 @@ declare const router: {
|
|
|
696
881
|
elapsedTimeMs: number;
|
|
697
882
|
projectDirectory: string;
|
|
698
883
|
relativePath: string;
|
|
884
|
+
dryRun?: undefined;
|
|
885
|
+
fileCount?: undefined;
|
|
886
|
+
directoryCount?: undefined;
|
|
887
|
+
files?: undefined;
|
|
699
888
|
error?: undefined;
|
|
700
889
|
} | {
|
|
701
890
|
success: boolean;
|
|
@@ -706,6 +895,10 @@ declare const router: {
|
|
|
706
895
|
elapsedTimeMs: number;
|
|
707
896
|
projectDirectory: string;
|
|
708
897
|
relativePath: string;
|
|
898
|
+
dryRun?: undefined;
|
|
899
|
+
fileCount?: undefined;
|
|
900
|
+
directoryCount?: undefined;
|
|
901
|
+
files?: undefined;
|
|
709
902
|
} | undefined>, _orpc_server0.MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
|
|
710
903
|
sponsors: _orpc_server0.Procedure<_orpc_server0.MergedInitialContext<Record<never, never>, Record<never, never>, Record<never, never>>, Record<never, never>, _orpc_server0.Schema<unknown, unknown>, _orpc_server0.Schema<void, void>, _orpc_server0.MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
|
|
711
904
|
docs: _orpc_server0.Procedure<_orpc_server0.MergedInitialContext<Record<never, never>, Record<never, never>, Record<never, never>>, Record<never, never>, _orpc_server0.Schema<unknown, unknown>, _orpc_server0.Schema<void, void>, _orpc_server0.MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
|
|
@@ -876,10 +1069,11 @@ type RustApi = import__better_fullstack_types.RustApi;
|
|
|
876
1069
|
type RustCli = import__better_fullstack_types.RustCli;
|
|
877
1070
|
type RustFrontend = import__better_fullstack_types.RustFrontend;
|
|
878
1071
|
type RustLibraries = import__better_fullstack_types.RustLibraries;
|
|
1072
|
+
type RustLogging = import__better_fullstack_types.RustLogging;
|
|
879
1073
|
type RustOrm = import__better_fullstack_types.RustOrm;
|
|
880
1074
|
type RustWebFramework = import__better_fullstack_types.RustWebFramework;
|
|
881
1075
|
type ServerDeploy = import__better_fullstack_types.ServerDeploy;
|
|
882
1076
|
type Template = import__better_fullstack_types.Template;
|
|
883
1077
|
type UILibrary = import__better_fullstack_types.UILibrary;
|
|
884
1078
|
type WebDeploy = import__better_fullstack_types.WebDeploy;
|
|
885
|
-
export { type API, type AddInput, type AddResult, type Addons, type AiDocs, type Analytics, type Animation, type Auth, type Backend, type BetterTStackConfig, type CMS, type CSSFramework, type Caching, type CreateInput, type Database, type DatabaseSetup, type DirectoryConflict, EMBEDDED_TEMPLATES, type Ecosystem, type Effect, type Examples, type Frontend, type GeneratorOptions, type GeneratorResult, type GoApi, type GoCli, type GoLogging, type GoOrm, type GoWebFramework, type InitResult, type Logging, type ORM, type PackageManager, type Payments, type PythonAi, type PythonOrm, type PythonQuality, type PythonTaskQueue, type PythonValidation, type PythonWebFramework, type Realtime, type Runtime, type RustApi, type RustCli, type RustFrontend, type RustLibraries, type RustOrm, type RustWebFramework, type ServerDeploy, TEMPLATE_COUNT, type Template, type UILibrary, type VirtualDirectory, type VirtualFile, VirtualFileSystem, type VirtualFileTree, type VirtualNode, type WebDeploy, add, builder, create, createBtsCli, createVirtual, docs, generateVirtualProject, history, router, sponsors };
|
|
1079
|
+
export { type API, type AddInput, type AddResult, type Addons, type AiDocs, type Analytics, type Animation, type Auth, type Backend, type BetterTStackConfig, type CMS, type CSSFramework, type Caching, type CreateInput, type Database, type DatabaseSetup, type DirectoryConflict, EMBEDDED_TEMPLATES, type Ecosystem, type Effect, type Examples, type Frontend, type GeneratorOptions, type GeneratorResult, type GoApi, type GoCli, type GoLogging, type GoOrm, type GoWebFramework, type InitResult, type Logging, type ORM, type PackageManager, type Payments, type PythonAi, type PythonOrm, type PythonQuality, type PythonTaskQueue, type PythonValidation, type PythonWebFramework, type Realtime, type Runtime, type RustApi, type RustCli, type RustFrontend, type RustLibraries, type RustLogging, type RustOrm, type RustWebFramework, type ServerDeploy, TEMPLATE_COUNT, type Template, type UILibrary, type VirtualDirectory, type VirtualFile, VirtualFileSystem, type VirtualFileTree, type VirtualNode, type WebDeploy, add, builder, create, createBtsCli, createVirtual, docs, generateVirtualProject, history, router, sponsors };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as __reExport } from "./chunk-CCII7kTE.mjs";
|
|
3
|
-
import { a as DEFAULT_CONFIG, c as getDefaultConfig, i as getLatestCLIVersion, l as getUserPkgManager, n as updateBtsConfig, o as DEFAULT_UI_LIBRARY_BY_FRONTEND, r as writeBtsConfig, t as readBtsConfig } from "./bts-config-
|
|
4
|
-
import { _ as setLastPromptShownUI, a as getPackageExecutionArgs, c as UserCancelledError, d as handleError, f as didLastPromptShowUI, g as setIsFirstPrompt$1, h as runWithContextAsync, l as exitCancelled, m as isSilent, o as addPackageDependency, p as isFirstPrompt, s as CLIError, t as setupAddons, u as exitWithError } from "./addons-setup-
|
|
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-BYD8mHt-.mjs";
|
|
4
|
+
import { _ as setLastPromptShownUI, a as getPackageExecutionArgs, c as UserCancelledError, d as handleError, f as didLastPromptShowUI, g as setIsFirstPrompt$1, h as runWithContextAsync, l as exitCancelled, m as isSilent, o as addPackageDependency, p as isFirstPrompt, s as CLIError, t as setupAddons, u as exitWithError } from "./addons-setup-Bll5VFJd.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";
|
|
@@ -2613,6 +2613,11 @@ async function getGoWebFrameworkChoice(goWebFramework) {
|
|
|
2613
2613
|
label: "Echo",
|
|
2614
2614
|
hint: "High performance, minimalist Go web framework"
|
|
2615
2615
|
},
|
|
2616
|
+
{
|
|
2617
|
+
value: "fiber",
|
|
2618
|
+
label: "Fiber",
|
|
2619
|
+
hint: "Express-inspired web framework built on Fasthttp"
|
|
2620
|
+
},
|
|
2616
2621
|
{
|
|
2617
2622
|
value: "none",
|
|
2618
2623
|
label: "None",
|
|
@@ -3088,6 +3093,11 @@ async function getPythonWebFrameworkChoice(pythonWebFramework) {
|
|
|
3088
3093
|
label: "Django",
|
|
3089
3094
|
hint: "High-level Python web framework with batteries included"
|
|
3090
3095
|
},
|
|
3096
|
+
{
|
|
3097
|
+
value: "flask",
|
|
3098
|
+
label: "Flask",
|
|
3099
|
+
hint: "Lightweight WSGI web framework with minimal boilerplate"
|
|
3100
|
+
},
|
|
3091
3101
|
{
|
|
3092
3102
|
value: "none",
|
|
3093
3103
|
label: "None",
|
|
@@ -3480,6 +3490,32 @@ async function getRustLibrariesChoice(rustLibraries) {
|
|
|
3480
3490
|
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
3481
3491
|
return response;
|
|
3482
3492
|
}
|
|
3493
|
+
async function getRustLoggingChoice(rustLogging) {
|
|
3494
|
+
if (rustLogging !== void 0) return rustLogging;
|
|
3495
|
+
const response = await navigableSelect({
|
|
3496
|
+
message: "Select Rust logging library",
|
|
3497
|
+
options: [
|
|
3498
|
+
{
|
|
3499
|
+
value: "tracing",
|
|
3500
|
+
label: "Tracing",
|
|
3501
|
+
hint: "Structured, composable instrumentation framework from Tokio"
|
|
3502
|
+
},
|
|
3503
|
+
{
|
|
3504
|
+
value: "env-logger",
|
|
3505
|
+
label: "env_logger",
|
|
3506
|
+
hint: "Simple logger configured via environment variables"
|
|
3507
|
+
},
|
|
3508
|
+
{
|
|
3509
|
+
value: "none",
|
|
3510
|
+
label: "None",
|
|
3511
|
+
hint: "No logging library"
|
|
3512
|
+
}
|
|
3513
|
+
],
|
|
3514
|
+
initialValue: "tracing"
|
|
3515
|
+
});
|
|
3516
|
+
if (isCancel$1(response)) return exitCancelled("Operation cancelled");
|
|
3517
|
+
return response;
|
|
3518
|
+
}
|
|
3483
3519
|
|
|
3484
3520
|
//#endregion
|
|
3485
3521
|
//#region src/prompts/search.ts
|
|
@@ -3504,6 +3540,11 @@ async function getSearchChoice(search, backend) {
|
|
|
3504
3540
|
label: "Elasticsearch",
|
|
3505
3541
|
hint: "Distributed search and analytics engine with local and cloud deployments"
|
|
3506
3542
|
},
|
|
3543
|
+
{
|
|
3544
|
+
value: "algolia",
|
|
3545
|
+
label: "Algolia",
|
|
3546
|
+
hint: "Hosted search API with instant results, typo tolerance, and analytics"
|
|
3547
|
+
},
|
|
3507
3548
|
{
|
|
3508
3549
|
value: "none",
|
|
3509
3550
|
label: "None",
|
|
@@ -4389,6 +4430,10 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
4389
4430
|
if (results.ecosystem !== "rust") return Promise.resolve([]);
|
|
4390
4431
|
return getRustLibrariesChoice(flags.rustLibraries);
|
|
4391
4432
|
},
|
|
4433
|
+
rustLogging: ({ results }) => {
|
|
4434
|
+
if (results.ecosystem !== "rust") return Promise.resolve("none");
|
|
4435
|
+
return getRustLoggingChoice(flags.rustLogging);
|
|
4436
|
+
},
|
|
4392
4437
|
pythonWebFramework: ({ results }) => {
|
|
4393
4438
|
if (results.ecosystem !== "python") return Promise.resolve("none");
|
|
4394
4439
|
return getPythonWebFrameworkChoice(flags.pythonWebFramework);
|
|
@@ -4491,6 +4536,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
|
|
|
4491
4536
|
rustApi: result.rustApi,
|
|
4492
4537
|
rustCli: result.rustCli,
|
|
4493
4538
|
rustLibraries: result.rustLibraries,
|
|
4539
|
+
rustLogging: result.rustLogging,
|
|
4494
4540
|
pythonWebFramework: result.pythonWebFramework,
|
|
4495
4541
|
pythonOrm: result.pythonOrm,
|
|
4496
4542
|
pythonValidation: result.pythonValidation,
|
|
@@ -4777,6 +4823,7 @@ function getRustFlags(config) {
|
|
|
4777
4823
|
flags.push(`--rust-api ${config.rustApi}`);
|
|
4778
4824
|
flags.push(`--rust-cli ${config.rustCli}`);
|
|
4779
4825
|
flags.push(formatArrayFlag("rust-libraries", config.rustLibraries));
|
|
4826
|
+
flags.push(`--rust-logging ${config.rustLogging}`);
|
|
4780
4827
|
appendSharedNonTypeScriptFlags(flags, config);
|
|
4781
4828
|
appendCommonFlags(flags, config);
|
|
4782
4829
|
return flags;
|
|
@@ -6925,7 +6972,9 @@ async function displayPostInstallInstructions(config) {
|
|
|
6925
6972
|
const pwaInstructions = addons?.includes("pwa") && (frontend?.includes("react-router") || frontend?.includes("react-vite")) ? getPwaInstructions() : "";
|
|
6926
6973
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
6927
6974
|
const clerkInstructions = config.auth === "clerk" ? getClerkInstructions(config.backend, config.frontend ?? []) : "";
|
|
6928
|
-
const
|
|
6975
|
+
const authSetupInstructions = getAuthSetupInstructions(config.auth, backend, config.frontend ?? []);
|
|
6976
|
+
const polarInstructions = config.payments === "polar" ? getPolarInstructions(backend, packageManager) : "";
|
|
6977
|
+
const paymentSetupInstructions = getPaymentSetupInstructions(config.payments, backend);
|
|
6929
6978
|
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
6930
6979
|
const hasWeb = frontend?.some((f) => WEB_FRAMEWORKS.includes(f));
|
|
6931
6980
|
const hasNative = frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles");
|
|
@@ -6974,8 +7023,10 @@ async function displayPostInstallInstructions(config) {
|
|
|
6974
7023
|
if (alchemyDeployInstructions) output += `\n${alchemyDeployInstructions.trim()}\n`;
|
|
6975
7024
|
if (starlightInstructions) output += `\n${starlightInstructions.trim()}\n`;
|
|
6976
7025
|
if (clerkInstructions) output += `\n${clerkInstructions.trim()}\n`;
|
|
7026
|
+
if (authSetupInstructions) output += `\n${authSetupInstructions.trim()}\n`;
|
|
6977
7027
|
if (betterAuthConvexInstructions) output += `\n${betterAuthConvexInstructions.trim()}\n`;
|
|
6978
7028
|
if (polarInstructions) output += `\n${polarInstructions.trim()}\n`;
|
|
7029
|
+
if (paymentSetupInstructions) output += `\n${paymentSetupInstructions.trim()}\n`;
|
|
6979
7030
|
if (noOrmWarning) output += `\n${noOrmWarning.trim()}\n`;
|
|
6980
7031
|
if (bunWebNativeWarning) output += `\n${bunWebNativeWarning.trim()}\n`;
|
|
6981
7032
|
output += `\n${pc.bold("Enjoying Better Fullstack?")} Help us grow — star the repo!\n`;
|
|
@@ -7023,9 +7074,15 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
|
|
|
7023
7074
|
if (database === "mysql" && orm === "drizzle") instructions.push(`${pc.yellow("NOTE:")} Enable foreign key constraints in PlanetScale database settings`);
|
|
7024
7075
|
if (database === "mysql" && orm === "prisma") instructions.push(`${pc.yellow("NOTE:")} How to handle Prisma migrations with PlanetScale:\n https://github.com/prisma/prisma/issues/7292`);
|
|
7025
7076
|
}
|
|
7026
|
-
if (dbSetup === "turso" && orm === "prisma") instructions.push(`${pc.yellow("NOTE:")}
|
|
7077
|
+
if (dbSetup === "turso" && orm === "prisma") instructions.push(`${pc.yellow("NOTE:")} Turso + Prisma migrations require the Turso CLI.\n 1. Install and authenticate the Turso CLI:\n ${pc.underline("https://docs.turso.tech/cli/installation")}\n 2. Confirm ${pc.white("DATABASE_URL")} and ${pc.white("TURSO_AUTH_TOKEN")} are set in your env file\n 3. Generate Prisma Client: ${`${runCmd} db:generate`}\n 4. Follow the official migration workflow:\n ${pc.underline("https://docs.turso.tech/sdk/ts/orm/prisma")}`);
|
|
7078
|
+
if (dbSetup === "turso" && orm === "drizzle") instructions.push(`${pc.yellow("NOTE:")} Turso + Drizzle requires the Turso CLI for database management.\n 1. Install and authenticate the Turso CLI:\n ${pc.underline("https://docs.turso.tech/cli/installation")}\n 2. Confirm ${pc.white("DATABASE_URL")} and ${pc.white("TURSO_AUTH_TOKEN")} are set in your env file\n 3. Push schema: ${`${runCmd} db:push`}\n 4. Docs: ${pc.underline("https://orm.drizzle.team/docs/get-started/turso-new")}`);
|
|
7079
|
+
if (dbSetup === "neon") instructions.push(`${pc.yellow("NOTE:")} Set your Neon ${pc.white("DATABASE_URL")} in your env file.\n Dashboard: ${pc.underline("https://console.neon.tech")}`);
|
|
7080
|
+
if (dbSetup === "supabase") instructions.push(`${pc.yellow("NOTE:")} Set your Supabase ${pc.white("DATABASE_URL")} in your env file.\n Dashboard: ${pc.underline("https://supabase.com/dashboard")}`);
|
|
7081
|
+
if (dbSetup === "prisma-postgres") instructions.push(`${pc.yellow("NOTE:")} Get your Prisma Postgres connection string from the console.\n Console: ${pc.underline("https://console.prisma.io")}`);
|
|
7082
|
+
if (dbSetup === "mongodb-atlas") instructions.push(`${pc.yellow("NOTE:")} Set your Atlas ${pc.white("DATABASE_URL")} in your env file.\n Dashboard: ${pc.underline("https://cloud.mongodb.com")}`);
|
|
7083
|
+
if (dbSetup === "upstash") instructions.push(`${pc.yellow("NOTE:")} Set your Upstash Redis credentials in your env file.\n Console: ${pc.underline("https://console.upstash.com")}`);
|
|
7027
7084
|
if (orm === "prisma") {
|
|
7028
|
-
if (database === "mongodb" && dbSetup === "docker") instructions.push(`${pc.yellow("WARNING:")} Prisma + MongoDB + Docker
|
|
7085
|
+
if (database === "mongodb" && dbSetup === "docker") instructions.push(`${pc.yellow("WARNING:")} Prisma + MongoDB + Docker can be unreliable in local development.\n • Start MongoDB first and wait until the container is ready before Prisma commands\n • If ${`${runCmd} db:push`} keeps failing locally, switch to MongoDB Atlas for a supported Prisma workflow:\n ${pc.underline("https://www.mongodb.com/atlas")}`);
|
|
7029
7086
|
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
7030
7087
|
if (!(dbSetup === "d1" && serverDeploy === "cloudflare")) {
|
|
7031
7088
|
instructions.push(`${pc.cyan("•")} Generate Prisma Client: ${`${runCmd} db:generate`}`);
|
|
@@ -7038,6 +7095,8 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
|
|
|
7038
7095
|
if (!(dbSetup === "d1" && serverDeploy === "cloudflare")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
|
|
7039
7096
|
} else if (orm === "mongoose") {
|
|
7040
7097
|
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
7098
|
+
} else if (orm === "typeorm" || orm === "mikroorm" || orm === "sequelize" || orm === "kysely") {
|
|
7099
|
+
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
7041
7100
|
} else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
|
|
7042
7101
|
return instructions.length ? `${pc.bold("Database commands:")}\n${instructions.join("\n")}` : "";
|
|
7043
7102
|
}
|
|
@@ -7064,6 +7123,24 @@ function getClerkInstructions(backend, frontend) {
|
|
|
7064
7123
|
}
|
|
7065
7124
|
return "";
|
|
7066
7125
|
}
|
|
7126
|
+
function getAuthSetupInstructions(auth, backend, frontend) {
|
|
7127
|
+
if (auth === "clerk" || auth === "better-auth" || auth === "go-better-auth" || auth === "none") return "";
|
|
7128
|
+
const envPath = backend === "self" ? "apps/web/.env" : "apps/server/.env";
|
|
7129
|
+
if (auth === "nextauth") return `${pc.bold("NextAuth.js Setup:")}\n${pc.cyan("•")} Generate a secret: ${pc.white("npx auth secret")}\n${pc.cyan("•")} Set ${pc.white("AUTH_SECRET")} in ${pc.white(envPath)}\n${pc.cyan("•")} Configure your OAuth providers (e.g. ${pc.white("AUTH_GITHUB_ID")}, ${pc.white("AUTH_GITHUB_SECRET")})\n${pc.cyan("•")} Docs: ${pc.underline("https://authjs.dev/getting-started")}`;
|
|
7130
|
+
if (auth === "stack-auth") return `${pc.bold("Stack Auth Setup:")}\n${pc.cyan("•")} Create a project at ${pc.underline("https://app.stack-auth.com")}\n${pc.cyan("•")} Set ${pc.white("NEXT_PUBLIC_STACK_PROJECT_ID")}, ${pc.white("NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY")},\n and ${pc.white("STACK_SECRET_SERVER_KEY")} in ${pc.white(envPath)}`;
|
|
7131
|
+
if (auth === "supabase-auth") return `${pc.bold("Supabase Auth Setup:")}\n${pc.cyan("•")} Create a project at ${pc.underline("https://supabase.com/dashboard")}\n${pc.cyan("•")} Set ${pc.white("NEXT_PUBLIC_SUPABASE_URL")}, ${pc.white("NEXT_PUBLIC_SUPABASE_ANON_KEY")},\n and ${pc.white("SUPABASE_SERVICE_ROLE_KEY")} in ${pc.white(envPath)}`;
|
|
7132
|
+
if (auth === "auth0") return `${pc.bold("Auth0 Setup:")}\n${pc.cyan("•")} Create an application at ${pc.underline("https://manage.auth0.com")}\n${pc.cyan("•")} Set ${pc.white("AUTH0_SECRET")}, ${pc.white("AUTH0_CLIENT_ID")}, ${pc.white("AUTH0_CLIENT_SECRET")},\n and ${pc.white("AUTH0_ISSUER_BASE_URL")} in ${pc.white(envPath)}\n${pc.cyan("•")} Docs: ${pc.underline("https://auth0.com/docs/quickstart")}`;
|
|
7133
|
+
return "";
|
|
7134
|
+
}
|
|
7135
|
+
function getPaymentSetupInstructions(payments, backend) {
|
|
7136
|
+
if (payments === "polar" || payments === "none") return "";
|
|
7137
|
+
const envPath = backend === "self" ? "apps/web/.env" : "apps/server/.env";
|
|
7138
|
+
if (payments === "stripe") return `${pc.bold("Stripe Setup:")}\n${pc.cyan("•")} Get API keys from ${pc.underline("https://dashboard.stripe.com/apikeys")}\n${pc.cyan("•")} Set ${pc.white("STRIPE_SECRET_KEY")} and ${pc.white("STRIPE_WEBHOOK_SECRET")} in ${pc.white(envPath)}\n${pc.cyan("•")} For local webhooks: ${pc.white("stripe listen --forward-to localhost:3000/api/webhooks/stripe")}`;
|
|
7139
|
+
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)}`;
|
|
7140
|
+
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)}`;
|
|
7141
|
+
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)}`;
|
|
7142
|
+
return "";
|
|
7143
|
+
}
|
|
7067
7144
|
function getBetterAuthConvexInstructions(hasWeb, webPort, packageManager) {
|
|
7068
7145
|
const cmd = packageManager === "npm" ? "npx" : packageManager === "yarn" ? "yarn dlx" : packageManager;
|
|
7069
7146
|
return `${pc.bold("Better Auth + Convex Setup:")}\n${pc.cyan("•")} Set environment variables from ${pc.white("packages/backend")}:\n${pc.white(" cd packages/backend")}\n${pc.white(` ${cmd} convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)`)}\n` + (hasWeb ? `${pc.white(` ${cmd} convex env set SITE_URL http://localhost:${webPort}`)}\n` : "");
|
|
@@ -7115,6 +7192,11 @@ function displayRustInstructions(config) {
|
|
|
7115
7192
|
clap: "Clap",
|
|
7116
7193
|
ratatui: "Ratatui"
|
|
7117
7194
|
}[rustCli] || rustCli}\n`;
|
|
7195
|
+
const { rustLogging } = config;
|
|
7196
|
+
if (rustLogging && rustLogging !== "none") output += `${pc.cyan("•")} Logging: ${{
|
|
7197
|
+
tracing: "Tracing",
|
|
7198
|
+
"env-logger": "env_logger"
|
|
7199
|
+
}[rustLogging] || rustLogging}\n`;
|
|
7118
7200
|
output += `\n${pc.bold("Common Cargo commands:")}\n`;
|
|
7119
7201
|
output += `${pc.cyan("•")} Build: cargo build\n`;
|
|
7120
7202
|
output += `${pc.cyan("•")} Run: cargo run\n`;
|
|
@@ -7137,7 +7219,8 @@ function displayGoInstructions(config) {
|
|
|
7137
7219
|
output += `\n${pc.bold("Your Go project includes:")}\n`;
|
|
7138
7220
|
if (goWebFramework && goWebFramework !== "none") output += `${pc.cyan("•")} Web Framework: ${{
|
|
7139
7221
|
gin: "Gin",
|
|
7140
|
-
echo: "Echo"
|
|
7222
|
+
echo: "Echo",
|
|
7223
|
+
fiber: "Fiber"
|
|
7141
7224
|
}[goWebFramework] || goWebFramework}\n`;
|
|
7142
7225
|
if (goOrm && goOrm !== "none") output += `${pc.cyan("•")} Database: ${{
|
|
7143
7226
|
gorm: "GORM",
|
|
@@ -7169,6 +7252,7 @@ function displayPythonInstructions(config) {
|
|
|
7169
7252
|
const cdCmd = `cd ${relativePath}`;
|
|
7170
7253
|
let runCommand = "uv run uvicorn app.main:app --reload";
|
|
7171
7254
|
if (pythonWebFramework === "django") runCommand = "uv run python manage.py runserver";
|
|
7255
|
+
else if (pythonWebFramework === "flask") runCommand = "uv run flask --app app.main run --reload";
|
|
7172
7256
|
let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
|
|
7173
7257
|
let stepCounter = 2;
|
|
7174
7258
|
if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} uv sync\n`;
|
|
@@ -7176,7 +7260,8 @@ function displayPythonInstructions(config) {
|
|
|
7176
7260
|
output += `\n${pc.bold("Your Python project includes:")}\n`;
|
|
7177
7261
|
if (pythonWebFramework && pythonWebFramework !== "none") output += `${pc.cyan("•")} Web Framework: ${{
|
|
7178
7262
|
fastapi: "FastAPI",
|
|
7179
|
-
django: "Django"
|
|
7263
|
+
django: "Django",
|
|
7264
|
+
flask: "Flask"
|
|
7180
7265
|
}[pythonWebFramework] || pythonWebFramework}\n`;
|
|
7181
7266
|
if (pythonOrm && pythonOrm !== "none") output += `${pc.cyan("•")} ORM: ${{
|
|
7182
7267
|
sqlalchemy: "SQLAlchemy",
|
|
@@ -7298,93 +7383,104 @@ async function createProjectHandler(input, options = {}) {
|
|
|
7298
7383
|
currentPathInput = defaultName;
|
|
7299
7384
|
} else currentPathInput = await getProjectName(input.projectName);
|
|
7300
7385
|
const versionChannel = shouldPromptForVersionChannel(input) ? await getVersionChannelChoice() : input.versionChannel ?? "stable";
|
|
7301
|
-
let
|
|
7302
|
-
let
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7386
|
+
let finalResolvedPath;
|
|
7387
|
+
let finalBaseName;
|
|
7388
|
+
if (input.dryRun) {
|
|
7389
|
+
finalBaseName = path.basename(currentPathInput);
|
|
7390
|
+
finalResolvedPath = path.resolve(process.cwd(), currentPathInput);
|
|
7391
|
+
} else {
|
|
7392
|
+
let finalPathInput;
|
|
7393
|
+
let shouldClearDirectory;
|
|
7394
|
+
try {
|
|
7395
|
+
if (input.directoryConflict) {
|
|
7396
|
+
const result = await handleDirectoryConflictProgrammatically(currentPathInput, input.directoryConflict);
|
|
7397
|
+
finalPathInput = result.finalPathInput;
|
|
7398
|
+
shouldClearDirectory = result.shouldClearDirectory;
|
|
7399
|
+
} else {
|
|
7400
|
+
const result = await handleDirectoryConflict(currentPathInput);
|
|
7401
|
+
finalPathInput = result.finalPathInput;
|
|
7402
|
+
shouldClearDirectory = result.shouldClearDirectory;
|
|
7403
|
+
}
|
|
7404
|
+
} catch (error) {
|
|
7405
|
+
if (error instanceof UserCancelledError || error instanceof CLIError) throw error;
|
|
7406
|
+
return {
|
|
7407
|
+
success: false,
|
|
7408
|
+
projectConfig: {
|
|
7409
|
+
projectName: "",
|
|
7410
|
+
projectDir: "",
|
|
7411
|
+
relativePath: "",
|
|
7412
|
+
ecosystem: "typescript",
|
|
7413
|
+
database: "none",
|
|
7414
|
+
orm: "none",
|
|
7415
|
+
backend: "none",
|
|
7416
|
+
runtime: "none",
|
|
7417
|
+
frontend: [],
|
|
7418
|
+
addons: [],
|
|
7419
|
+
examples: [],
|
|
7420
|
+
auth: "none",
|
|
7421
|
+
payments: "none",
|
|
7422
|
+
email: "none",
|
|
7423
|
+
fileUpload: "none",
|
|
7424
|
+
effect: "none",
|
|
7425
|
+
git: false,
|
|
7426
|
+
packageManager: "npm",
|
|
7427
|
+
versionChannel: "stable",
|
|
7428
|
+
install: false,
|
|
7429
|
+
dbSetup: "none",
|
|
7430
|
+
api: "none",
|
|
7431
|
+
webDeploy: "none",
|
|
7432
|
+
serverDeploy: "none",
|
|
7433
|
+
cssFramework: "none",
|
|
7434
|
+
uiLibrary: "none",
|
|
7435
|
+
ai: "none",
|
|
7436
|
+
stateManagement: "none",
|
|
7437
|
+
validation: "zod",
|
|
7438
|
+
forms: "react-hook-form",
|
|
7439
|
+
testing: "vitest",
|
|
7440
|
+
realtime: "none",
|
|
7441
|
+
jobQueue: "none",
|
|
7442
|
+
animation: "none",
|
|
7443
|
+
logging: "none",
|
|
7444
|
+
observability: "none",
|
|
7445
|
+
rustWebFramework: "none",
|
|
7446
|
+
rustFrontend: "none",
|
|
7447
|
+
rustOrm: "none",
|
|
7448
|
+
rustApi: "none",
|
|
7449
|
+
rustCli: "none",
|
|
7450
|
+
rustLibraries: [],
|
|
7451
|
+
rustLogging: "none",
|
|
7452
|
+
cms: "none",
|
|
7453
|
+
caching: "none",
|
|
7454
|
+
search: "none",
|
|
7455
|
+
featureFlags: "none",
|
|
7456
|
+
analytics: "none",
|
|
7457
|
+
fileStorage: "none",
|
|
7458
|
+
pythonWebFramework: "none",
|
|
7459
|
+
pythonOrm: "none",
|
|
7460
|
+
pythonValidation: "none",
|
|
7461
|
+
pythonAi: [],
|
|
7462
|
+
pythonTaskQueue: "none",
|
|
7463
|
+
pythonQuality: "none",
|
|
7464
|
+
goWebFramework: "none",
|
|
7465
|
+
goOrm: "none",
|
|
7466
|
+
goApi: "none",
|
|
7467
|
+
goCli: "none",
|
|
7468
|
+
goLogging: "none",
|
|
7469
|
+
aiDocs: []
|
|
7470
|
+
},
|
|
7471
|
+
reproducibleCommand: "",
|
|
7472
|
+
timeScaffolded,
|
|
7473
|
+
elapsedTimeMs: Date.now() - startTime,
|
|
7474
|
+
projectDirectory: "",
|
|
7320
7475
|
relativePath: "",
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
examples: [],
|
|
7329
|
-
auth: "none",
|
|
7330
|
-
payments: "none",
|
|
7331
|
-
email: "none",
|
|
7332
|
-
fileUpload: "none",
|
|
7333
|
-
effect: "none",
|
|
7334
|
-
git: false,
|
|
7335
|
-
packageManager: "npm",
|
|
7336
|
-
versionChannel: "stable",
|
|
7337
|
-
install: false,
|
|
7338
|
-
dbSetup: "none",
|
|
7339
|
-
api: "none",
|
|
7340
|
-
webDeploy: "none",
|
|
7341
|
-
serverDeploy: "none",
|
|
7342
|
-
cssFramework: "none",
|
|
7343
|
-
uiLibrary: "none",
|
|
7344
|
-
ai: "none",
|
|
7345
|
-
stateManagement: "none",
|
|
7346
|
-
validation: "zod",
|
|
7347
|
-
forms: "react-hook-form",
|
|
7348
|
-
testing: "vitest",
|
|
7349
|
-
realtime: "none",
|
|
7350
|
-
jobQueue: "none",
|
|
7351
|
-
animation: "none",
|
|
7352
|
-
logging: "none",
|
|
7353
|
-
observability: "none",
|
|
7354
|
-
rustWebFramework: "none",
|
|
7355
|
-
rustFrontend: "none",
|
|
7356
|
-
rustOrm: "none",
|
|
7357
|
-
rustApi: "none",
|
|
7358
|
-
rustCli: "none",
|
|
7359
|
-
rustLibraries: [],
|
|
7360
|
-
cms: "none",
|
|
7361
|
-
caching: "none",
|
|
7362
|
-
search: "none",
|
|
7363
|
-
featureFlags: "none",
|
|
7364
|
-
analytics: "none",
|
|
7365
|
-
fileStorage: "none",
|
|
7366
|
-
pythonWebFramework: "none",
|
|
7367
|
-
pythonOrm: "none",
|
|
7368
|
-
pythonValidation: "none",
|
|
7369
|
-
pythonAi: [],
|
|
7370
|
-
pythonTaskQueue: "none",
|
|
7371
|
-
pythonQuality: "none",
|
|
7372
|
-
goWebFramework: "none",
|
|
7373
|
-
goOrm: "none",
|
|
7374
|
-
goApi: "none",
|
|
7375
|
-
goCli: "none",
|
|
7376
|
-
goLogging: "none",
|
|
7377
|
-
aiDocs: []
|
|
7378
|
-
},
|
|
7379
|
-
reproducibleCommand: "",
|
|
7380
|
-
timeScaffolded,
|
|
7381
|
-
elapsedTimeMs: Date.now() - startTime,
|
|
7382
|
-
projectDirectory: "",
|
|
7383
|
-
relativePath: "",
|
|
7384
|
-
error: error instanceof Error ? error.message : String(error)
|
|
7385
|
-
};
|
|
7476
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7477
|
+
};
|
|
7478
|
+
}
|
|
7479
|
+
const setupResult = await setupProjectDirectory(finalPathInput, shouldClearDirectory);
|
|
7480
|
+
finalResolvedPath = setupResult.finalResolvedPath;
|
|
7481
|
+
finalBaseName = setupResult.finalBaseName;
|
|
7482
|
+
currentPathInput = finalPathInput;
|
|
7386
7483
|
}
|
|
7387
|
-
const { finalResolvedPath, finalBaseName } = await setupProjectDirectory(finalPathInput, shouldClearDirectory);
|
|
7388
7484
|
const originalInput = {
|
|
7389
7485
|
...input,
|
|
7390
7486
|
projectDirectory: input.projectName
|
|
@@ -7418,7 +7514,7 @@ async function createProjectHandler(input, options = {}) {
|
|
|
7418
7514
|
...flagConfig,
|
|
7419
7515
|
projectName: finalBaseName,
|
|
7420
7516
|
projectDir: finalResolvedPath,
|
|
7421
|
-
relativePath:
|
|
7517
|
+
relativePath: currentPathInput,
|
|
7422
7518
|
versionChannel
|
|
7423
7519
|
};
|
|
7424
7520
|
validateConfigCompatibility(config, providedFlags, cliInput);
|
|
@@ -7437,12 +7533,54 @@ async function createProjectHandler(input, options = {}) {
|
|
|
7437
7533
|
log.message("");
|
|
7438
7534
|
}
|
|
7439
7535
|
config = {
|
|
7440
|
-
...await gatherConfig(flagConfig, finalBaseName, finalResolvedPath,
|
|
7536
|
+
...await gatherConfig(flagConfig, finalBaseName, finalResolvedPath, currentPathInput),
|
|
7441
7537
|
versionChannel
|
|
7442
7538
|
};
|
|
7443
7539
|
}
|
|
7444
7540
|
const preflight = validatePreflightConfig(config);
|
|
7445
7541
|
if (preflight.hasWarnings && !isSilent()) displayPreflightWarnings(preflight);
|
|
7542
|
+
if (input.dryRun) {
|
|
7543
|
+
const result = await generateVirtualProject$1({
|
|
7544
|
+
config,
|
|
7545
|
+
templates: EMBEDDED_TEMPLATES$1
|
|
7546
|
+
});
|
|
7547
|
+
if (!result.success || !result.tree) throw new Error(result.error || "Failed to generate project templates");
|
|
7548
|
+
const files = [];
|
|
7549
|
+
function walk(nodes, prefix) {
|
|
7550
|
+
for (const node of nodes) {
|
|
7551
|
+
const current = prefix ? `${prefix}/${node.name}` : node.name;
|
|
7552
|
+
if (node.type === "directory" && node.children) walk(node.children, current);
|
|
7553
|
+
else files.push(current);
|
|
7554
|
+
}
|
|
7555
|
+
}
|
|
7556
|
+
walk(result.tree.root.children, "");
|
|
7557
|
+
if (!isSilent()) {
|
|
7558
|
+
log.info(pc.bold(pc.cyan(`Dry run complete — ${result.tree.fileCount} files in ${result.tree.directoryCount} directories`)));
|
|
7559
|
+
log.message("");
|
|
7560
|
+
log.message(pc.bold("Files that would be created:"));
|
|
7561
|
+
for (const file of files) log.message(pc.dim(` ${file}`));
|
|
7562
|
+
}
|
|
7563
|
+
const reproducibleCommand$1 = generateReproducibleCommand(config);
|
|
7564
|
+
if (!isSilent()) {
|
|
7565
|
+
log.message("");
|
|
7566
|
+
log.success(pc.blue(`You can reproduce this setup with the following command:\n${reproducibleCommand$1}`));
|
|
7567
|
+
}
|
|
7568
|
+
const elapsedTimeMs$1 = Date.now() - startTime;
|
|
7569
|
+
if (!isSilent()) outro(pc.magenta("No files were written (dry run)."));
|
|
7570
|
+
return {
|
|
7571
|
+
success: true,
|
|
7572
|
+
projectConfig: config,
|
|
7573
|
+
reproducibleCommand: reproducibleCommand$1,
|
|
7574
|
+
timeScaffolded,
|
|
7575
|
+
elapsedTimeMs: elapsedTimeMs$1,
|
|
7576
|
+
projectDirectory: config.projectDir,
|
|
7577
|
+
relativePath: config.relativePath,
|
|
7578
|
+
dryRun: true,
|
|
7579
|
+
fileCount: result.tree.fileCount,
|
|
7580
|
+
directoryCount: result.tree.directoryCount,
|
|
7581
|
+
files
|
|
7582
|
+
};
|
|
7583
|
+
}
|
|
7446
7584
|
await createProject(config, { manualDb: cliInput.manualDb ?? input.manualDb });
|
|
7447
7585
|
const reproducibleCommand = generateReproducibleCommand(config);
|
|
7448
7586
|
if (!isSilent()) log.success(pc.blue(`You can reproduce this setup with the following command:\n${reproducibleCommand}`));
|
|
@@ -7594,7 +7732,7 @@ function displaySponsorsBox(sponsors$1) {
|
|
|
7594
7732
|
//#region src/index.ts
|
|
7595
7733
|
const router = os.router({
|
|
7596
7734
|
create: os.meta({
|
|
7597
|
-
description: "
|
|
7735
|
+
description: "Scaffold a new Better Fullstack project from 270+ compatible stack options",
|
|
7598
7736
|
default: true,
|
|
7599
7737
|
negateBooleans: true
|
|
7600
7738
|
}).input(z.tuple([types_exports.ProjectNameSchema.optional(), z.object({
|
|
@@ -7602,6 +7740,7 @@ const router = os.router({
|
|
|
7602
7740
|
yes: z.boolean().optional().default(false).describe("Use default configuration"),
|
|
7603
7741
|
yolo: z.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
|
|
7604
7742
|
verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
|
|
7743
|
+
dryRun: z.boolean().optional().default(false).describe("Preview generated file tree without writing to disk"),
|
|
7605
7744
|
ecosystem: types_exports.EcosystemSchema.optional().describe("Language ecosystem (typescript, rust, python, or go)"),
|
|
7606
7745
|
database: types_exports.DatabaseSchema.optional(),
|
|
7607
7746
|
orm: types_exports.ORMSchema.optional(),
|
|
@@ -7659,13 +7798,14 @@ const router = os.router({
|
|
|
7659
7798
|
rustApi: types_exports.RustApiSchema.optional().describe("Rust API layer (tonic, async-graphql)"),
|
|
7660
7799
|
rustCli: types_exports.RustCliSchema.optional().describe("Rust CLI tools (clap, ratatui)"),
|
|
7661
7800
|
rustLibraries: z.array(types_exports.RustLibrariesSchema).optional().describe("Rust core libraries"),
|
|
7801
|
+
rustLogging: types_exports.RustLoggingSchema.optional().describe("Rust logging (tracing, env-logger)"),
|
|
7662
7802
|
pythonWebFramework: types_exports.PythonWebFrameworkSchema.optional().describe("Python web framework (fastapi, django)"),
|
|
7663
7803
|
pythonOrm: types_exports.PythonOrmSchema.optional().describe("Python ORM/database (sqlalchemy, sqlmodel)"),
|
|
7664
7804
|
pythonValidation: types_exports.PythonValidationSchema.optional().describe("Python validation (pydantic)"),
|
|
7665
7805
|
pythonAi: z.array(types_exports.PythonAiSchema).optional().describe("Python AI/ML frameworks"),
|
|
7666
7806
|
pythonTaskQueue: types_exports.PythonTaskQueueSchema.optional().describe("Python task queue (celery)"),
|
|
7667
7807
|
pythonQuality: types_exports.PythonQualitySchema.optional().describe("Python code quality (ruff)"),
|
|
7668
|
-
goWebFramework: types_exports.GoWebFrameworkSchema.optional().describe("Go web framework (gin, echo)"),
|
|
7808
|
+
goWebFramework: types_exports.GoWebFrameworkSchema.optional().describe("Go web framework (gin, echo, fiber)"),
|
|
7669
7809
|
goOrm: types_exports.GoOrmSchema.optional().describe("Go ORM/database (gorm, sqlc)"),
|
|
7670
7810
|
goApi: types_exports.GoApiSchema.optional().describe("Go API layer (grpc-go)"),
|
|
7671
7811
|
goCli: types_exports.GoCliSchema.optional().describe("Go CLI tools (cobra, bubbletea)"),
|
|
@@ -7706,7 +7846,7 @@ const router = os.router({
|
|
|
7706
7846
|
log.message(`Please visit ${BUILDER_URL}`);
|
|
7707
7847
|
}
|
|
7708
7848
|
}),
|
|
7709
|
-
add: os.meta({ description: "Add addons or deployment targets to an existing Better Fullstack project" }).input(z.object({
|
|
7849
|
+
add: os.meta({ description: "Add addons or deployment targets to an existing Better Fullstack project using its bts.jsonc config" }).input(z.object({
|
|
7710
7850
|
addons: z.array(types_exports.AddonsSchema).optional().describe("Addons to add"),
|
|
7711
7851
|
webDeploy: types_exports.WebDeploySchema.optional().describe("Web deployment option to set"),
|
|
7712
7852
|
serverDeploy: types_exports.ServerDeploySchema.optional().describe("Server deployment option to set"),
|
|
@@ -7741,7 +7881,7 @@ const router = os.router({
|
|
|
7741
7881
|
ecosystem: input.ecosystem
|
|
7742
7882
|
});
|
|
7743
7883
|
}),
|
|
7744
|
-
mcp: os.meta({ description: "Start MCP server
|
|
7884
|
+
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 () => {
|
|
7745
7885
|
log.message("MCP server is started via the 'mcp' subcommand intercepted in cli.ts.");
|
|
7746
7886
|
log.message("Run: create-better-fullstack mcp");
|
|
7747
7887
|
})
|
|
@@ -7899,6 +8039,7 @@ async function createVirtual(options) {
|
|
|
7899
8039
|
rustApi: options.rustApi || "none",
|
|
7900
8040
|
rustCli: options.rustCli || "none",
|
|
7901
8041
|
rustLibraries: options.rustLibraries || [],
|
|
8042
|
+
rustLogging: options.rustLogging || (options.ecosystem === "rust" ? "tracing" : "none"),
|
|
7902
8043
|
pythonWebFramework: options.pythonWebFramework || "none",
|
|
7903
8044
|
pythonOrm: options.pythonOrm || "none",
|
|
7904
8045
|
pythonValidation: options.pythonValidation || "none",
|
package/dist/mcp-entry.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as getLatestCLIVersion, r as writeBtsConfig, t as readBtsConfig } from "./bts-config-
|
|
2
|
+
import { i as getLatestCLIVersion, r as writeBtsConfig, t as readBtsConfig } from "./bts-config-BYD8mHt-.mjs";
|
|
3
3
|
import z from "zod";
|
|
4
|
-
import { AISchema, APISchema, AddonsSchema, AnalyticsSchema, AnimationSchema, AstroIntegrationSchema, AuthSchema, BackendSchema, CMSSchema, CSSFrameworkSchema, CachingSchema, DatabaseSchema, DatabaseSetupSchema, EcosystemSchema, EffectSchema, EmailSchema, ExamplesSchema, FeatureFlagsSchema, FileStorageSchema, FileUploadSchema, FormsSchema, FrontendSchema, GoApiSchema, GoCliSchema, GoLoggingSchema, GoOrmSchema, GoWebFrameworkSchema, JobQueueSchema, LoggingSchema, ORMSchema, ObservabilitySchema, PackageManagerSchema, PaymentsSchema, PythonAiSchema, PythonOrmSchema, PythonQualitySchema, PythonTaskQueueSchema, PythonValidationSchema, PythonWebFrameworkSchema, RealtimeSchema, RuntimeSchema, RustApiSchema, RustCliSchema, RustFrontendSchema, RustLibrariesSchema, RustOrmSchema, RustWebFrameworkSchema, SearchSchema, ServerDeploySchema, StateManagementSchema, TestingSchema, UILibrarySchema, ValidationSchema, WebDeploySchema, analyzeStackCompatibility } from "@better-fullstack/types";
|
|
4
|
+
import { AISchema, APISchema, AddonsSchema, AnalyticsSchema, AnimationSchema, AstroIntegrationSchema, AuthSchema, BackendSchema, CMSSchema, CSSFrameworkSchema, CachingSchema, DatabaseSchema, DatabaseSetupSchema, EcosystemSchema, EffectSchema, EmailSchema, ExamplesSchema, FeatureFlagsSchema, FileStorageSchema, FileUploadSchema, FormsSchema, FrontendSchema, GoApiSchema, GoCliSchema, GoLoggingSchema, GoOrmSchema, GoWebFrameworkSchema, JobQueueSchema, LoggingSchema, ORMSchema, ObservabilitySchema, PackageManagerSchema, PaymentsSchema, PythonAiSchema, PythonOrmSchema, PythonQualitySchema, PythonTaskQueueSchema, PythonValidationSchema, PythonWebFrameworkSchema, RealtimeSchema, RuntimeSchema, RustApiSchema, RustCliSchema, RustFrontendSchema, RustLibrariesSchema, RustLoggingSchema, RustOrmSchema, RustWebFrameworkSchema, SearchSchema, ServerDeploySchema, StateManagementSchema, TestingSchema, UILibrarySchema, ValidationSchema, WebDeploySchema, analyzeStackCompatibility } from "@better-fullstack/types";
|
|
5
5
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
7
|
|
|
@@ -114,6 +114,7 @@ const SCHEMA_MAP = {
|
|
|
114
114
|
rustApi: RustApiSchema,
|
|
115
115
|
rustCli: RustCliSchema,
|
|
116
116
|
rustLibraries: RustLibrariesSchema,
|
|
117
|
+
rustLogging: RustLoggingSchema,
|
|
117
118
|
pythonWebFramework: PythonWebFrameworkSchema,
|
|
118
119
|
pythonOrm: PythonOrmSchema,
|
|
119
120
|
pythonValidation: PythonValidationSchema,
|
|
@@ -165,7 +166,8 @@ const ECOSYSTEM_CATEGORIES = {
|
|
|
165
166
|
"rustOrm",
|
|
166
167
|
"rustApi",
|
|
167
168
|
"rustCli",
|
|
168
|
-
"rustLibraries"
|
|
169
|
+
"rustLibraries",
|
|
170
|
+
"rustLogging"
|
|
169
171
|
],
|
|
170
172
|
python: [
|
|
171
173
|
"pythonWebFramework",
|
|
@@ -302,6 +304,7 @@ function buildProjectConfig(input, overrides) {
|
|
|
302
304
|
rustApi: input.rustApi ?? "none",
|
|
303
305
|
rustCli: input.rustCli ?? "none",
|
|
304
306
|
rustLibraries: input.rustLibraries ?? [],
|
|
307
|
+
rustLogging: input.rustLogging ?? "none",
|
|
305
308
|
pythonWebFramework: input.pythonWebFramework ?? "none",
|
|
306
309
|
pythonOrm: input.pythonOrm ?? "none",
|
|
307
310
|
pythonValidation: input.pythonValidation ?? "none",
|
|
@@ -390,6 +393,7 @@ function buildCompatibilityInput(input) {
|
|
|
390
393
|
rustApi: input.rustApi ?? "none",
|
|
391
394
|
rustCli: input.rustCli ?? "none",
|
|
392
395
|
rustLibraries: (input.rustLibraries ?? []).join(",") || "none",
|
|
396
|
+
rustLogging: input.rustLogging ?? "none",
|
|
393
397
|
pythonWebFramework: input.pythonWebFramework ?? "none",
|
|
394
398
|
pythonOrm: input.pythonOrm ?? "none",
|
|
395
399
|
pythonValidation: input.pythonValidation ?? "none",
|
|
@@ -592,6 +596,7 @@ async function startMcpServer() {
|
|
|
592
596
|
rustApi: RustApiSchema.optional().describe("Rust API layer"),
|
|
593
597
|
rustCli: RustCliSchema.optional().describe("Rust CLI framework"),
|
|
594
598
|
rustLibraries: z.array(RustLibrariesSchema).optional().describe("Rust libraries"),
|
|
599
|
+
rustLogging: RustLoggingSchema.optional().describe("Rust logging library"),
|
|
595
600
|
pythonWebFramework: PythonWebFrameworkSchema.optional().describe("Python web framework"),
|
|
596
601
|
pythonOrm: PythonOrmSchema.optional().describe("Python ORM"),
|
|
597
602
|
pythonValidation: PythonValidationSchema.optional().describe("Python validation"),
|
|
@@ -671,7 +676,7 @@ async function startMcpServer() {
|
|
|
671
676
|
await writeBtsConfig(config);
|
|
672
677
|
let addonWarnings = [];
|
|
673
678
|
if (config.addons.length > 0 && config.addons[0] !== "none") {
|
|
674
|
-
const { setupAddons } = await import("./addons-setup-
|
|
679
|
+
const { setupAddons } = await import("./addons-setup-CWGwxN68.mjs");
|
|
675
680
|
addonWarnings = await setupAddons(config);
|
|
676
681
|
}
|
|
677
682
|
const installCmd = getInstallCommand(input.ecosystem ?? "typescript", projectName, input.packageManager);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-fullstack",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"description": "Scaffold production-ready fullstack apps in seconds. Pick your stack from 270+ options — the CLI wires everything together.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"check-types": "tsc --noEmit",
|
|
102
102
|
"test": "bun test ./test/*.test.ts",
|
|
103
103
|
"test:watch": "bun test --watch",
|
|
104
|
-
"test:coverage": "bun test --coverage",
|
|
104
|
+
"test:coverage": "bun test --coverage ./test/*.test.ts",
|
|
105
105
|
"test:ci": "CI=1 bun test --bail=5",
|
|
106
106
|
"test:e2e": "E2E=1 bun test test/e2e/e2e.e2e.ts --timeout 600000",
|
|
107
107
|
"test:integration": "bun test test/e2e/cli-interaction.test.ts test/e2e/cli-binary.test.ts",
|
|
@@ -112,10 +112,10 @@
|
|
|
112
112
|
"prepublishOnly": "npm run build"
|
|
113
113
|
},
|
|
114
114
|
"dependencies": {
|
|
115
|
-
"@better-fullstack/template-generator": "^1.5.
|
|
116
|
-
"@better-fullstack/types": "^1.5.
|
|
115
|
+
"@better-fullstack/template-generator": "^1.5.3",
|
|
116
|
+
"@better-fullstack/types": "^1.5.3",
|
|
117
117
|
"@clack/core": "^0.5.0",
|
|
118
|
-
"@clack/prompts": "^1.
|
|
118
|
+
"@clack/prompts": "^1.2.0",
|
|
119
119
|
"@orpc/server": "^1.13.13",
|
|
120
120
|
"consola": "^3.4.2",
|
|
121
121
|
"env-paths": "^4.0.0",
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"oxfmt": "^0.19.0",
|
|
128
128
|
"picocolors": "^1.1.1",
|
|
129
129
|
"tinyglobby": "^0.2.15",
|
|
130
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
130
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
131
131
|
"trpc-cli": "^0.12.1",
|
|
132
132
|
"ts-morph": "^27.0.2",
|
|
133
133
|
"yaml": "^2.8.3",
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
"devDependencies": {
|
|
137
137
|
"@types/bun": "^1.3.11",
|
|
138
138
|
"@types/fs-extra": "^11.0.4",
|
|
139
|
-
"@types/node": "^25.5.
|
|
139
|
+
"@types/node": "^25.5.2",
|
|
140
140
|
"publint": "^0.3.18",
|
|
141
141
|
"tsdown": "^0.18.2",
|
|
142
142
|
"typescript": "^5.9.3"
|