create-better-fullstack 2.1.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{add-handler-BjTmOwEN.mjs → add-handler-9F-AsGM-.mjs} +4 -4
- package/dist/addons-setup-C8eaCaH5.mjs +5 -0
- package/dist/{addons-setup-uSvqHagW.mjs → addons-setup-C_xrNtkL.mjs} +1 -2
- package/dist/cli.mjs +2 -2
- package/dist/compatibility-rules-D7zYNVjC.mjs +372 -0
- package/dist/config-validation-C4glouQh.mjs +1512 -0
- package/dist/{doctor-Bm9qvkya.mjs → doctor-BFSSbS-U.mjs} +2 -3
- package/dist/{bts-config-InNcw1aJ.mjs → errors-D9yiiGVq.mjs} +126 -5
- package/dist/{generated-checks-CUhFFfVo.mjs → generated-checks-DUvVXWId.mjs} +1 -1
- package/dist/index.mjs +7 -6
- package/dist/{install-dependencies-CDjTNvIV.mjs → install-dependencies-DDGF-zDG.mjs} +5 -371
- package/dist/mcp-BXLhb7wW.mjs +7 -0
- package/dist/mcp-entry.mjs +1217 -175
- package/dist/run-BmRKR2wG.mjs +11 -0
- package/dist/{run-Cqy-n1iT.mjs → run-DRzP53v3.mjs} +9 -1082
- package/package.json +3 -3
- package/dist/addons-setup-CSmj5ClW.mjs +0 -6
- package/dist/errors-ns_o2OKg.mjs +0 -86
- package/dist/mcp-BaLHDRdC.mjs +0 -5
- package/dist/run-DYWSKowD.mjs +0 -10
- package/dist/templates-B351F1yX.mjs +0 -489
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { a as handleError, m as readBtsConfig } from "./errors-D9yiiGVq.mjs";
|
|
3
3
|
import { t as renderTitle } from "./render-title-zvyKC1ej.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import { t as runGeneratedChecks } from "./generated-checks-CUhFFfVo.mjs";
|
|
4
|
+
import { t as runGeneratedChecks } from "./generated-checks-DUvVXWId.mjs";
|
|
6
5
|
import { intro, log, spinner } from "@clack/prompts";
|
|
7
6
|
import pc from "picocolors";
|
|
8
7
|
import fs from "fs-extra";
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { t as __reExport } from "./chunk-CCII7kTE.mjs";
|
|
3
|
+
import { cancel } from "@clack/prompts";
|
|
4
|
+
import pc from "picocolors";
|
|
3
5
|
import fs from "fs-extra";
|
|
4
6
|
import path from "node:path";
|
|
5
7
|
import * as JSONC from "jsonc-parser";
|
|
6
8
|
import { createCliDefaultProjectConfigBase } from "@better-fullstack/types";
|
|
7
9
|
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
11
|
+
import consola from "consola";
|
|
8
12
|
|
|
9
13
|
//#region src/types.ts
|
|
10
14
|
var types_exports = {};
|
|
@@ -231,13 +235,25 @@ const getLatestCLIVersion = () => {
|
|
|
231
235
|
//#endregion
|
|
232
236
|
//#region src/utils/bts-config.ts
|
|
233
237
|
const BTS_CONFIG_FILE = "bts.jsonc";
|
|
238
|
+
const MOBILE_CONFIG_FIELDS = [
|
|
239
|
+
"mobileNavigation",
|
|
240
|
+
"mobileUI",
|
|
241
|
+
"mobileStorage",
|
|
242
|
+
"mobileTesting",
|
|
243
|
+
"mobilePush",
|
|
244
|
+
"mobileOTA",
|
|
245
|
+
"mobileDeepLinking"
|
|
246
|
+
];
|
|
234
247
|
function normalizeGraphConfigForPersistence(projectConfig, stackParts) {
|
|
235
248
|
if (!stackParts) return projectConfig;
|
|
236
249
|
const legacyConfig = (0, types_exports.stackPartsToLegacyProjectConfigPartial)(stackParts);
|
|
237
250
|
const selectedEcosystems = new Set(stackParts.filter((part) => part.source !== "provided").map((part) => part.ecosystem));
|
|
251
|
+
selectedEcosystems.add(projectConfig.ecosystem);
|
|
252
|
+
const hasSelectedEcosystemStackParts = stackParts.some((part) => part.source !== "provided" && part.ecosystem !== "universal");
|
|
238
253
|
const normalized = {
|
|
239
254
|
...projectConfig,
|
|
240
|
-
...legacyConfig
|
|
255
|
+
...legacyConfig,
|
|
256
|
+
ecosystem: hasSelectedEcosystemStackParts ? legacyConfig.ecosystem ?? projectConfig.ecosystem : projectConfig.ecosystem
|
|
241
257
|
};
|
|
242
258
|
if (!selectedEcosystems.has("rust")) {
|
|
243
259
|
normalized.rustWebFramework = "none";
|
|
@@ -285,6 +301,7 @@ function normalizeGraphConfigForPersistence(projectConfig, stackParts) {
|
|
|
285
301
|
normalized.goConfig = "none";
|
|
286
302
|
normalized.goObservability = "none";
|
|
287
303
|
}
|
|
304
|
+
if (selectedEcosystems.has("go") && projectConfig.auth === "go-better-auth" && legacyConfig.auth === "none") normalized.auth = projectConfig.auth;
|
|
288
305
|
if (!selectedEcosystems.has("java")) {
|
|
289
306
|
normalized.javaWebFramework = "none";
|
|
290
307
|
normalized.javaBuildTool = "none";
|
|
@@ -326,7 +343,15 @@ function normalizeGraphConfigForPersistence(projectConfig, stackParts) {
|
|
|
326
343
|
normalized.elixirDeploy = "none";
|
|
327
344
|
normalized.elixirLibraries = [];
|
|
328
345
|
}
|
|
329
|
-
|
|
346
|
+
const projectHasNativeFrontend = projectConfig.frontend.some((frontend) => frontend.startsWith("native-"));
|
|
347
|
+
const normalizedHasNativeFrontend = normalized.frontend.some((frontend) => frontend.startsWith("native-"));
|
|
348
|
+
const hasNativeFrontend = projectHasNativeFrontend || normalizedHasNativeFrontend;
|
|
349
|
+
if (hasNativeFrontend) for (const field of MOBILE_CONFIG_FIELDS) {
|
|
350
|
+
const projectValue = projectConfig[field];
|
|
351
|
+
const legacyValue = legacyConfig[field];
|
|
352
|
+
if (projectValue !== void 0 && projectValue !== "none" && legacyValue === "none") normalized[field] = projectValue;
|
|
353
|
+
}
|
|
354
|
+
if (!selectedEcosystems.has("react-native") && !hasNativeFrontend) {
|
|
330
355
|
normalized.mobileNavigation = "none";
|
|
331
356
|
normalized.mobileUI = "none";
|
|
332
357
|
normalized.mobileStorage = "none";
|
|
@@ -425,8 +450,16 @@ function buildBtsConfigForPersistence(projectConfig, metadata = {}) {
|
|
|
425
450
|
api: persistedConfig.api,
|
|
426
451
|
webDeploy: persistedConfig.webDeploy,
|
|
427
452
|
serverDeploy: persistedConfig.serverDeploy,
|
|
453
|
+
astroIntegration: persistedConfig.astroIntegration,
|
|
428
454
|
cssFramework: persistedConfig.cssFramework,
|
|
429
455
|
uiLibrary: persistedConfig.uiLibrary,
|
|
456
|
+
shadcnBase: persistedConfig.shadcnBase,
|
|
457
|
+
shadcnStyle: persistedConfig.shadcnStyle,
|
|
458
|
+
shadcnIconLibrary: persistedConfig.shadcnIconLibrary,
|
|
459
|
+
shadcnColorTheme: persistedConfig.shadcnColorTheme,
|
|
460
|
+
shadcnBaseColor: persistedConfig.shadcnBaseColor,
|
|
461
|
+
shadcnFont: persistedConfig.shadcnFont,
|
|
462
|
+
shadcnRadius: persistedConfig.shadcnRadius,
|
|
430
463
|
realtime: persistedConfig.realtime,
|
|
431
464
|
jobQueue: persistedConfig.jobQueue,
|
|
432
465
|
animation: persistedConfig.animation,
|
|
@@ -538,8 +571,8 @@ function previewBtsConfigUpdate(currentConfig, updates) {
|
|
|
538
571
|
createdAt: currentConfig.createdAt
|
|
539
572
|
});
|
|
540
573
|
}
|
|
541
|
-
async function writeBtsConfig(projectConfig) {
|
|
542
|
-
const btsConfig = buildBtsConfigForPersistence(projectConfig);
|
|
574
|
+
async function writeBtsConfig(projectConfig, metadata = {}) {
|
|
575
|
+
const btsConfig = buildBtsConfigForPersistence(projectConfig, metadata);
|
|
543
576
|
const baseContent = {
|
|
544
577
|
$schema: "https://better-fullstack-web.vercel.app/schema.json",
|
|
545
578
|
version: btsConfig.version,
|
|
@@ -572,8 +605,16 @@ async function writeBtsConfig(projectConfig) {
|
|
|
572
605
|
api: btsConfig.api,
|
|
573
606
|
webDeploy: btsConfig.webDeploy,
|
|
574
607
|
serverDeploy: btsConfig.serverDeploy,
|
|
608
|
+
astroIntegration: btsConfig.astroIntegration,
|
|
575
609
|
cssFramework: btsConfig.cssFramework,
|
|
576
610
|
uiLibrary: btsConfig.uiLibrary,
|
|
611
|
+
shadcnBase: btsConfig.shadcnBase,
|
|
612
|
+
shadcnStyle: btsConfig.shadcnStyle,
|
|
613
|
+
shadcnIconLibrary: btsConfig.shadcnIconLibrary,
|
|
614
|
+
shadcnColorTheme: btsConfig.shadcnColorTheme,
|
|
615
|
+
shadcnBaseColor: btsConfig.shadcnBaseColor,
|
|
616
|
+
shadcnFont: btsConfig.shadcnFont,
|
|
617
|
+
shadcnRadius: btsConfig.shadcnRadius,
|
|
577
618
|
realtime: btsConfig.realtime,
|
|
578
619
|
jobQueue: btsConfig.jobQueue,
|
|
579
620
|
animation: btsConfig.animation,
|
|
@@ -593,6 +634,7 @@ async function writeBtsConfig(projectConfig) {
|
|
|
593
634
|
rateLimit: btsConfig.rateLimit,
|
|
594
635
|
i18n: btsConfig.i18n,
|
|
595
636
|
search: btsConfig.search,
|
|
637
|
+
vectorDb: btsConfig.vectorDb,
|
|
596
638
|
fileStorage: btsConfig.fileStorage,
|
|
597
639
|
rustWebFramework: btsConfig.rustWebFramework,
|
|
598
640
|
rustFrontend: btsConfig.rustFrontend,
|
|
@@ -756,4 +798,83 @@ async function updateBtsConfig(projectDir, updates) {
|
|
|
756
798
|
}
|
|
757
799
|
|
|
758
800
|
//#endregion
|
|
759
|
-
|
|
801
|
+
//#region src/utils/context.ts
|
|
802
|
+
const cliStorage = new AsyncLocalStorage();
|
|
803
|
+
function defaultContext() {
|
|
804
|
+
return {
|
|
805
|
+
navigation: {
|
|
806
|
+
isFirstPrompt: false,
|
|
807
|
+
lastPromptShownUI: false
|
|
808
|
+
},
|
|
809
|
+
silent: false
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
function getContext() {
|
|
813
|
+
const ctx = cliStorage.getStore();
|
|
814
|
+
if (!ctx) return defaultContext();
|
|
815
|
+
return ctx;
|
|
816
|
+
}
|
|
817
|
+
function tryGetContext() {
|
|
818
|
+
return cliStorage.getStore();
|
|
819
|
+
}
|
|
820
|
+
function isSilent() {
|
|
821
|
+
return getContext().silent;
|
|
822
|
+
}
|
|
823
|
+
function isFirstPrompt() {
|
|
824
|
+
return getContext().navigation.isFirstPrompt;
|
|
825
|
+
}
|
|
826
|
+
function didLastPromptShowUI() {
|
|
827
|
+
return getContext().navigation.lastPromptShownUI;
|
|
828
|
+
}
|
|
829
|
+
function setIsFirstPrompt(value) {
|
|
830
|
+
const ctx = tryGetContext();
|
|
831
|
+
if (ctx) ctx.navigation.isFirstPrompt = value;
|
|
832
|
+
}
|
|
833
|
+
function setLastPromptShownUI(value) {
|
|
834
|
+
const ctx = tryGetContext();
|
|
835
|
+
if (ctx) ctx.navigation.lastPromptShownUI = value;
|
|
836
|
+
}
|
|
837
|
+
async function runWithContextAsync(options, fn) {
|
|
838
|
+
const ctx = {
|
|
839
|
+
navigation: {
|
|
840
|
+
isFirstPrompt: false,
|
|
841
|
+
lastPromptShownUI: false
|
|
842
|
+
},
|
|
843
|
+
silent: options.silent ?? false
|
|
844
|
+
};
|
|
845
|
+
return cliStorage.run(ctx, fn);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
//#endregion
|
|
849
|
+
//#region src/utils/errors.ts
|
|
850
|
+
var UserCancelledError = class extends Error {
|
|
851
|
+
constructor(message = "Operation cancelled") {
|
|
852
|
+
super(message);
|
|
853
|
+
this.name = "UserCancelledError";
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
var CLIError = class extends Error {
|
|
857
|
+
constructor(message) {
|
|
858
|
+
super(message);
|
|
859
|
+
this.name = "CLIError";
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
function exitWithError(message) {
|
|
863
|
+
if (isSilent()) throw new CLIError(message);
|
|
864
|
+
consola.error(pc.red(message));
|
|
865
|
+
process.exit(1);
|
|
866
|
+
}
|
|
867
|
+
function exitCancelled(message = "Operation cancelled") {
|
|
868
|
+
if (isSilent()) throw new UserCancelledError(message);
|
|
869
|
+
cancel(pc.red(message));
|
|
870
|
+
process.exit(1);
|
|
871
|
+
}
|
|
872
|
+
function handleError(error, fallbackMessage) {
|
|
873
|
+
const message = error instanceof Error ? error.message : fallbackMessage || String(error);
|
|
874
|
+
if (isSilent()) throw error instanceof Error ? error : new Error(message);
|
|
875
|
+
consola.error(pc.red(message));
|
|
876
|
+
process.exit(1);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
//#endregion
|
|
880
|
+
export { types_exports as A, getEffectiveStack as C, getGraphSummary as D, getGraphPart as E, getPrimaryGraphPart as O, getUserPkgManager as S, getGraphBackendUrl as T, writeBtsConfig as _, handleError as a, DEFAULT_UI_LIBRARY_BY_FRONTEND as b, isSilent as c, setLastPromptShownUI as d, buildBtsConfigForPersistence as f, updateBtsConfig as g, readBtsConfigFromFile as h, exitWithError as i, hasGraphPart as k, runWithContextAsync as l, readBtsConfig as m, UserCancelledError as n, didLastPromptShowUI as o, previewBtsConfigUpdate as p, exitCancelled as r, isFirstPrompt as s, CLIError as t, setIsFirstPrompt as u, getLatestCLIVersion as v, getGraphBackendDeployInstructions as w, getDefaultConfig as x, DEFAULT_CONFIG as y };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { O as getPrimaryGraphPart } from "./errors-D9yiiGVq.mjs";
|
|
3
3
|
import { log, spinner } from "@clack/prompts";
|
|
4
4
|
import pc from "picocolors";
|
|
5
5
|
import path from "node:path";
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./
|
|
3
|
-
import { a as docs, c as sponsors, i as createBtsCli, l as telemetry, n as builder, o as history, r as create, s as router, t as add } from "./run-
|
|
2
|
+
import "./errors-D9yiiGVq.mjs";
|
|
3
|
+
import { a as docs, c as sponsors, i as createBtsCli, l as telemetry, n as builder, o as history, r as create, s as router, t as add } from "./run-DRzP53v3.mjs";
|
|
4
4
|
import "./render-title-zvyKC1ej.mjs";
|
|
5
|
-
import "./
|
|
6
|
-
import "./
|
|
7
|
-
import "./
|
|
8
|
-
import "./
|
|
5
|
+
import "./addons-setup-C_xrNtkL.mjs";
|
|
6
|
+
import "./config-validation-C4glouQh.mjs";
|
|
7
|
+
import "./compatibility-rules-D7zYNVjC.mjs";
|
|
8
|
+
import "./install-dependencies-DDGF-zDG.mjs";
|
|
9
|
+
import "./generated-checks-DUvVXWId.mjs";
|
|
9
10
|
import { EMBEDDED_TEMPLATES, TEMPLATE_COUNT, VirtualFileSystem, generateVirtualProject } from "@better-fullstack/template-generator";
|
|
10
11
|
|
|
11
12
|
//#region src/index.ts
|
|
@@ -1,380 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { A as types_exports, d as setLastPromptShownUI, r as exitCancelled, s as isFirstPrompt, u as setIsFirstPrompt$1, y as DEFAULT_CONFIG } from "./errors-D9yiiGVq.mjs";
|
|
3
|
+
import { m as validateAddonCompatibility, r as getCompatibleAddons } from "./compatibility-rules-D7zYNVjC.mjs";
|
|
4
4
|
import { log, spinner } from "@clack/prompts";
|
|
5
5
|
import pc from "picocolors";
|
|
6
6
|
import fs from "fs-extra";
|
|
7
7
|
import path from "node:path";
|
|
8
|
-
import { allowedApisForFrontends, getAIFrontendCompatibilityIssue, getApiFrontendCompatibilityIssue, getCompatibleAddons, getCompatibleCSSFrameworks, getCompatibleUILibraries, getUnsupportedWebDeployFrontend, hasDockerComposeCompatibleFrontend, hasWebStyling, isBackendUtilsCompatibleBackend, isExampleAIAllowed, isExampleChatSdkAllowed, isFrontendAllowedWithBackend, isWebFrontend, requiresChatSdkVercelAIForSelection, splitFrontends, validateAddonCompatibility } from "@better-fullstack/types";
|
|
9
8
|
import consola from "consola";
|
|
10
9
|
import { ConfirmPrompt, GroupMultiSelectPrompt, MultiSelectPrompt, SelectPrompt, isCancel as isCancel$1 } from "@clack/core";
|
|
11
10
|
import { $ } from "execa";
|
|
12
11
|
|
|
13
|
-
//#region src/utils/error-formatter.ts
|
|
14
|
-
function getCategoryTitle(category) {
|
|
15
|
-
return {
|
|
16
|
-
incompatibility: "Incompatible Options",
|
|
17
|
-
"invalid-selection": "Invalid Selection",
|
|
18
|
-
"missing-requirement": "Missing Requirement",
|
|
19
|
-
constraint: "Constraint Violation"
|
|
20
|
-
}[category];
|
|
21
|
-
}
|
|
22
|
-
function displayStructuredError(error) {
|
|
23
|
-
if (isSilent()) throw new CLIError(error.message);
|
|
24
|
-
const lines = [];
|
|
25
|
-
lines.push(pc.bold(pc.red(getCategoryTitle(error.category))));
|
|
26
|
-
lines.push("");
|
|
27
|
-
lines.push(error.message);
|
|
28
|
-
if (error.provided && Object.keys(error.provided).length > 0) {
|
|
29
|
-
lines.push("");
|
|
30
|
-
lines.push(pc.dim("You provided:"));
|
|
31
|
-
for (const [key, value] of Object.entries(error.provided)) {
|
|
32
|
-
const displayValue = Array.isArray(value) ? value.join(", ") : value;
|
|
33
|
-
lines.push(` ${pc.cyan("--" + key)} ${displayValue}`);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
if (error.suggestions.length > 0) {
|
|
37
|
-
lines.push("");
|
|
38
|
-
lines.push(pc.dim("Suggestions:"));
|
|
39
|
-
for (const suggestion of error.suggestions) lines.push(` ${pc.green("•")} ${suggestion}`);
|
|
40
|
-
}
|
|
41
|
-
consola.box({
|
|
42
|
-
title: pc.red("Error"),
|
|
43
|
-
message: lines.join("\n"),
|
|
44
|
-
style: { borderColor: "red" }
|
|
45
|
-
});
|
|
46
|
-
process.exit(1);
|
|
47
|
-
}
|
|
48
|
-
function incompatibilityError(opts) {
|
|
49
|
-
return displayStructuredError({
|
|
50
|
-
category: "incompatibility",
|
|
51
|
-
...opts
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
function invalidSelectionError(opts) {
|
|
55
|
-
return displayStructuredError({
|
|
56
|
-
category: "invalid-selection",
|
|
57
|
-
...opts
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
function missingRequirementError(opts) {
|
|
61
|
-
return displayStructuredError({
|
|
62
|
-
category: "missing-requirement",
|
|
63
|
-
...opts
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
function constraintError(opts) {
|
|
67
|
-
return displayStructuredError({
|
|
68
|
-
category: "constraint",
|
|
69
|
-
...opts
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
//#endregion
|
|
74
|
-
//#region src/utils/compatibility-rules.ts
|
|
75
|
-
function isWebFrontend$1(value) {
|
|
76
|
-
return isWebFrontend(value);
|
|
77
|
-
}
|
|
78
|
-
function splitFrontends$1(values = []) {
|
|
79
|
-
return splitFrontends(values);
|
|
80
|
-
}
|
|
81
|
-
function ensureSingleWebAndNative(frontends) {
|
|
82
|
-
const { web, native } = splitFrontends$1(frontends);
|
|
83
|
-
if (web.length > 1) invalidSelectionError({
|
|
84
|
-
message: "Only one web framework can be selected per project.",
|
|
85
|
-
provided: { frontend: web },
|
|
86
|
-
suggestions: ["Keep one web framework and remove the others", "Use separate projects for multiple web frameworks"]
|
|
87
|
-
});
|
|
88
|
-
if (native.length > 1) invalidSelectionError({
|
|
89
|
-
message: "Only one native framework can be selected per project.",
|
|
90
|
-
provided: { frontend: native },
|
|
91
|
-
suggestions: ["Keep one native framework and remove the others", "Choose: native-bare, native-uniwind, or native-unistyles"]
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
const FULLSTACK_FRONTENDS = [
|
|
95
|
-
"next",
|
|
96
|
-
"vinext",
|
|
97
|
-
"tanstack-start",
|
|
98
|
-
"astro",
|
|
99
|
-
"nuxt",
|
|
100
|
-
"svelte",
|
|
101
|
-
"solid-start"
|
|
102
|
-
];
|
|
103
|
-
function validateSelfBackendCompatibility(providedFlags, options, config) {
|
|
104
|
-
const backend = config.backend || options.backend;
|
|
105
|
-
const frontends = config.frontend || options.frontend || [];
|
|
106
|
-
if (backend === "self") {
|
|
107
|
-
const { web, native } = splitFrontends$1(frontends);
|
|
108
|
-
if (!(web.length === 1 && FULLSTACK_FRONTENDS.includes(web[0]))) exitWithError("Backend 'self' (fullstack) only supports Next.js, Vinext, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend vinext, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, or --frontend solid-start.");
|
|
109
|
-
if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-bare, native-uniwind, native-unistyles");
|
|
110
|
-
}
|
|
111
|
-
const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS.includes(f));
|
|
112
|
-
if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) only supports Next.js, Vinext, TanStack Start, Astro, Nuxt, SvelteKit, or SolidStart frontends. Please use --frontend next, --frontend vinext, --frontend tanstack-start, --frontend astro, --frontend nuxt, --frontend svelte, --frontend solid-start, or choose a different backend.");
|
|
113
|
-
}
|
|
114
|
-
const WORKERS_COMPATIBLE_BACKENDS = [
|
|
115
|
-
"hono",
|
|
116
|
-
"nitro",
|
|
117
|
-
"fets"
|
|
118
|
-
];
|
|
119
|
-
function validateWorkersCompatibility(providedFlags, options, config) {
|
|
120
|
-
if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && !WORKERS_COMPATIBLE_BACKENDS.includes(config.backend)) incompatibilityError({
|
|
121
|
-
message: "In Better-Fullstack, Cloudflare Workers runtime is currently supported only with compatible backends (Hono, Nitro, or Fets).",
|
|
122
|
-
provided: {
|
|
123
|
-
runtime: "workers",
|
|
124
|
-
backend: config.backend
|
|
125
|
-
},
|
|
126
|
-
suggestions: [
|
|
127
|
-
"Use --backend hono",
|
|
128
|
-
"Use --backend nitro",
|
|
129
|
-
"Use --backend fets",
|
|
130
|
-
"Choose a different runtime (node, bun)"
|
|
131
|
-
]
|
|
132
|
-
});
|
|
133
|
-
if (providedFlags.has("backend") && config.backend && !WORKERS_COMPATIBLE_BACKENDS.includes(config.backend) && config.runtime === "workers") incompatibilityError({
|
|
134
|
-
message: `In Better-Fullstack, backend '${config.backend}' is currently not available with Cloudflare Workers runtime.`,
|
|
135
|
-
provided: {
|
|
136
|
-
backend: config.backend,
|
|
137
|
-
runtime: "workers"
|
|
138
|
-
},
|
|
139
|
-
suggestions: ["Use --backend hono, --backend nitro, or --backend fets", "Choose a different runtime (node, bun)"]
|
|
140
|
-
});
|
|
141
|
-
if (providedFlags.has("runtime") && options.runtime === "workers" && config.database === "mongodb") incompatibilityError({
|
|
142
|
-
message: "In Better-Fullstack, Cloudflare Workers runtime is currently not available with MongoDB.",
|
|
143
|
-
provided: {
|
|
144
|
-
runtime: "workers",
|
|
145
|
-
database: "mongodb"
|
|
146
|
-
},
|
|
147
|
-
suggestions: ["Use a different database (postgres, sqlite, mysql)", "Choose a different runtime (node, bun)"]
|
|
148
|
-
});
|
|
149
|
-
if (providedFlags.has("runtime") && options.runtime === "workers" && config.dbSetup === "docker") incompatibilityError({
|
|
150
|
-
message: "In Better-Fullstack, Cloudflare Workers runtime is currently not available with Docker database setup.",
|
|
151
|
-
provided: {
|
|
152
|
-
runtime: "workers",
|
|
153
|
-
"db-setup": "docker"
|
|
154
|
-
},
|
|
155
|
-
suggestions: ["Use --db-setup d1 for SQLite", "Choose a different runtime (node, bun)"]
|
|
156
|
-
});
|
|
157
|
-
if (providedFlags.has("database") && config.database === "mongodb" && config.runtime === "workers") incompatibilityError({
|
|
158
|
-
message: "In Better-Fullstack, MongoDB is currently not available with Cloudflare Workers runtime.",
|
|
159
|
-
provided: {
|
|
160
|
-
database: "mongodb",
|
|
161
|
-
runtime: "workers"
|
|
162
|
-
},
|
|
163
|
-
suggestions: ["Use a different database (postgres, sqlite, mysql)", "Choose a different runtime (node, bun)"]
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
function validateApiFrontendCompatibility(api, frontends = [], astroIntegration) {
|
|
167
|
-
const issue = getApiFrontendCompatibilityIssue(api, frontends, astroIntegration);
|
|
168
|
-
if (!issue) return;
|
|
169
|
-
incompatibilityError({
|
|
170
|
-
message: issue.message,
|
|
171
|
-
provided: issue.provided ?? {},
|
|
172
|
-
suggestions: issue.suggestions ?? []
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
function isFrontendAllowedWithBackend$1(frontend, backend, auth) {
|
|
176
|
-
return isFrontendAllowedWithBackend(frontend, backend, auth);
|
|
177
|
-
}
|
|
178
|
-
function allowedApisForFrontends$1(frontends = [], astroIntegration) {
|
|
179
|
-
return allowedApisForFrontends(frontends, astroIntegration);
|
|
180
|
-
}
|
|
181
|
-
function isExampleAIAllowed$1(backend, frontends = []) {
|
|
182
|
-
return isExampleAIAllowed(backend, frontends);
|
|
183
|
-
}
|
|
184
|
-
function isExampleChatSdkAllowed$1(backend, frontends = [], runtime) {
|
|
185
|
-
return isExampleChatSdkAllowed(backend, frontends, runtime);
|
|
186
|
-
}
|
|
187
|
-
function requiresChatSdkVercelAI(backend, frontends = [], runtime) {
|
|
188
|
-
return requiresChatSdkVercelAIForSelection(backend, frontends, runtime);
|
|
189
|
-
}
|
|
190
|
-
function validateWebDeployRequiresWebFrontend(webDeploy, hasWebFrontendFlag) {
|
|
191
|
-
if (webDeploy && webDeploy !== "none" && !hasWebFrontendFlag) exitWithError("'--web-deploy' requires a web frontend. Please select a web frontend or set '--web-deploy none'.");
|
|
192
|
-
}
|
|
193
|
-
function validateWebDeployFrontendTemplates(webDeploy, frontends = []) {
|
|
194
|
-
const blocked = getUnsupportedWebDeployFrontend(webDeploy, frontends);
|
|
195
|
-
if (blocked) exitWithError(`${webDeploy === "render" ? "Render" : "Netlify"} deployment is not yet wired up for the '${blocked}' frontend. Choose a different web deploy target or frontend.`);
|
|
196
|
-
}
|
|
197
|
-
function validateServerDeployRequiresBackend(serverDeploy, backend, hasGraphBackend = false) {
|
|
198
|
-
if (serverDeploy && serverDeploy !== "none" && !hasGraphBackend && (!backend || backend === "none")) exitWithError("'--server-deploy' requires a backend. Please select a backend or set '--server-deploy none'.");
|
|
199
|
-
}
|
|
200
|
-
function validateAddonCompatibility$1(addon, frontend, _auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database) {
|
|
201
|
-
const baseCompatibility = validateAddonCompatibility(addon, frontend, _auth);
|
|
202
|
-
if (!baseCompatibility.isCompatible) return baseCompatibility;
|
|
203
|
-
if (addon === "backend-utils") {
|
|
204
|
-
if (ecosystem !== void 0 && ecosystem !== "typescript") return {
|
|
205
|
-
isCompatible: false,
|
|
206
|
-
reason: "Backend Utils requires a TypeScript server stack"
|
|
207
|
-
};
|
|
208
|
-
if (backend !== void 0 && !isBackendUtilsCompatibleBackend(backend)) return {
|
|
209
|
-
isCompatible: false,
|
|
210
|
-
reason: "Backend Utils requires a Hono, Express, Fastify, Elysia, feTS, or NestJS backend"
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
if (addon === "docker-compose" || addon === "devcontainer") {
|
|
214
|
-
const label = addon === "devcontainer" ? "DevContainer" : "docker-compose";
|
|
215
|
-
const title = addon === "devcontainer" ? "DevContainer" : "Docker Compose";
|
|
216
|
-
if (backend === "convex") return {
|
|
217
|
-
isCompatible: false,
|
|
218
|
-
reason: `${label} is not compatible with Convex backend (managed service)`
|
|
219
|
-
};
|
|
220
|
-
if (runtime === "workers") return {
|
|
221
|
-
isCompatible: false,
|
|
222
|
-
reason: `${label} is not compatible with Cloudflare Workers runtime`
|
|
223
|
-
};
|
|
224
|
-
if (ecosystem !== void 0 && ![
|
|
225
|
-
"typescript",
|
|
226
|
-
"python",
|
|
227
|
-
"go",
|
|
228
|
-
"rust",
|
|
229
|
-
"java"
|
|
230
|
-
].includes(ecosystem)) return {
|
|
231
|
-
isCompatible: false,
|
|
232
|
-
reason: `${title} currently supports TypeScript, Python, Go, Rust, or Java projects`
|
|
233
|
-
};
|
|
234
|
-
if (ecosystem === "typescript" && !hasDockerComposeCompatibleFrontend(frontend)) return {
|
|
235
|
-
isCompatible: false,
|
|
236
|
-
reason: `${title} currently supports Next.js, Vinext, TanStack Router, React Router, React Vite, Solid, or Astro`
|
|
237
|
-
};
|
|
238
|
-
if (ecosystem === "typescript" && backend === "self" && !frontend.includes("next") && !frontend.includes("vinext")) return {
|
|
239
|
-
isCompatible: false,
|
|
240
|
-
reason: `${title} self-backend support currently requires Next.js or Vinext`
|
|
241
|
-
};
|
|
242
|
-
if (ecosystem === "rust" && rustFrontend && rustFrontend !== "none") return {
|
|
243
|
-
isCompatible: false,
|
|
244
|
-
reason: `${title} for Rust currently supports server-only projects`
|
|
245
|
-
};
|
|
246
|
-
if (ecosystem === "java" && javaWebFramework && javaWebFramework !== "spring-boot") return {
|
|
247
|
-
isCompatible: false,
|
|
248
|
-
reason: `${title} for Java currently requires Spring Boot`
|
|
249
|
-
};
|
|
250
|
-
if (ecosystem === "python" && database && database !== "none" && database !== "sqlite" && database !== "postgres") return {
|
|
251
|
-
isCompatible: false,
|
|
252
|
-
reason: `${title} for Python ORM projects currently supports SQLite defaults or Postgres`
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
return { isCompatible: true };
|
|
256
|
-
}
|
|
257
|
-
function getCompatibleAddons$1(allAddons, frontend, existingAddons = [], auth, backend, runtime) {
|
|
258
|
-
return getCompatibleAddons(allAddons, frontend, existingAddons, auth).filter((addon) => {
|
|
259
|
-
const { isCompatible } = validateAddonCompatibility$1(addon, frontend, auth, backend, runtime);
|
|
260
|
-
return isCompatible;
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
function validateAddonsAgainstFrontends(addons = [], frontends = [], auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database) {
|
|
264
|
-
if (addons.includes("nx") && addons.includes("turborepo")) exitWithError("Nx and Turborepo are alternative workspace runners. Choose one addon.");
|
|
265
|
-
for (const addon of addons) {
|
|
266
|
-
if (addon === "none") continue;
|
|
267
|
-
const { isCompatible, reason } = validateAddonCompatibility$1(addon, frontends, auth, backend, runtime, ecosystem, rustFrontend, javaWebFramework, database);
|
|
268
|
-
if (!isCompatible) exitWithError(`Incompatible addon/frontend combination: ${reason}`);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
function validatePaymentsCompatibility(payments, auth, _backend, frontends = []) {
|
|
272
|
-
if (!payments || payments === "none") return;
|
|
273
|
-
if (payments === "dodo" && frontends.includes("react-vite")) exitWithError("Dodo Payments are not yet supported for React + Vite projects.");
|
|
274
|
-
if (payments === "polar") {
|
|
275
|
-
if (!auth || auth === "none" || auth !== "better-auth" && auth !== "better-auth-organizations") exitWithError("Polar payments requires Better Auth. Please use '--auth better-auth' or choose a different payments provider.");
|
|
276
|
-
const { web } = splitFrontends$1(frontends);
|
|
277
|
-
if (web.length === 0 && frontends.length > 0) exitWithError("Polar payments requires a web frontend or no frontend. Please select a web frontend or choose a different payments provider.");
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
function validateExamplesCompatibility(examples, backend, frontend, runtime, ai) {
|
|
281
|
-
const examplesArr = examples ?? [];
|
|
282
|
-
if (examplesArr.length === 0 || examplesArr.includes("none")) return;
|
|
283
|
-
if (examplesArr.includes("tanstack-showcase")) {
|
|
284
|
-
const showcaseFrontends = ["tanstack-router", "tanstack-start"];
|
|
285
|
-
if (!(frontend ?? []).some((f) => showcaseFrontends.includes(f))) exitWithError("The 'tanstack-showcase' example requires TanStack Router or TanStack Start frontend.");
|
|
286
|
-
}
|
|
287
|
-
if (examplesArr.includes("ai") && (frontend ?? []).includes("solid")) exitWithError("The 'ai' example is not compatible with the Solid frontend.");
|
|
288
|
-
if (examplesArr.includes("ai") && (frontend ?? []).includes("solid-start")) exitWithError("The 'ai' example is not compatible with the SolidStart frontend.");
|
|
289
|
-
if (examplesArr.includes("ai") && backend === "convex") {
|
|
290
|
-
const frontendArr = frontend ?? [];
|
|
291
|
-
const includesNuxt = frontendArr.includes("nuxt");
|
|
292
|
-
const includesSvelte = frontendArr.includes("svelte");
|
|
293
|
-
if (includesNuxt || includesSvelte) exitWithError("The 'ai' example with Convex backend only supports React-based frontends (Next.js, TanStack Router, TanStack Start, React Router, React + Vite). Svelte and Nuxt are not supported with Convex AI.");
|
|
294
|
-
}
|
|
295
|
-
if (examplesArr.includes("chat-sdk")) {
|
|
296
|
-
const frontendArr = frontend ?? [];
|
|
297
|
-
if (frontendArr.includes("react-vite")) exitWithError("The 'chat-sdk' example is not yet supported for React + Vite projects.");
|
|
298
|
-
if (!isExampleChatSdkAllowed$1(backend, frontendArr, runtime)) {
|
|
299
|
-
if (backend === "none") exitWithError("The 'chat-sdk' example requires a backend.");
|
|
300
|
-
if (backend === "convex") exitWithError("The 'chat-sdk' example is not supported with the Convex backend in v1. Use self backend (Next.js, TanStack Start, Nuxt) or Hono with Node runtime.");
|
|
301
|
-
if (backend === "self") exitWithError("The 'chat-sdk' example with self backend only supports Next.js, TanStack Start, or Nuxt frontends in v1.");
|
|
302
|
-
if (backend === "hono" && runtime !== "node") exitWithError("The 'chat-sdk' example with Hono requires '--runtime node' in v1 (Bun/Workers not supported yet).");
|
|
303
|
-
exitWithError("The 'chat-sdk' example is only supported with self backend (Next.js, TanStack Start, Nuxt) or Hono with Node runtime in v1.");
|
|
304
|
-
}
|
|
305
|
-
if (requiresChatSdkVercelAI(backend, frontendArr, runtime) && ai && ai !== "vercel-ai") exitWithError("The 'chat-sdk' example requires '--ai vercel-ai' for the selected stack in v1 (Nuxt Discord and Hono GitHub profiles).");
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Validates that TanStack AI is only used with compatible frontends (React or Solid).
|
|
310
|
-
* Server-side @tanstack/ai core works anywhere, but client adapters only exist for React and Solid.
|
|
311
|
-
*/
|
|
312
|
-
function validateAIFrontendCompatibility(ai, frontends = []) {
|
|
313
|
-
const issue = getAIFrontendCompatibilityIssue(ai, frontends);
|
|
314
|
-
if (!issue) return;
|
|
315
|
-
exitWithError(issue.message);
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Validates that a UI library is compatible with the selected frontend(s)
|
|
319
|
-
*/
|
|
320
|
-
function validateUILibraryFrontendCompatibility(uiLibrary, frontends = [], astroIntegration) {
|
|
321
|
-
if (!uiLibrary || uiLibrary === "none") return;
|
|
322
|
-
const { web } = splitFrontends$1(frontends);
|
|
323
|
-
if (web.length === 0) return;
|
|
324
|
-
const compatible = getCompatibleUILibraries(frontends, astroIntegration);
|
|
325
|
-
if (!compatible.includes(uiLibrary)) {
|
|
326
|
-
const isAstroNonReact = web.includes("astro") && astroIntegration !== "react";
|
|
327
|
-
const supportsAstroReact = getCompatibleUILibraries(["astro"], "react").includes(uiLibrary);
|
|
328
|
-
if (isAstroNonReact && supportsAstroReact) {
|
|
329
|
-
incompatibilityError({
|
|
330
|
-
message: `UI library '${uiLibrary}' requires React.`,
|
|
331
|
-
provided: {
|
|
332
|
-
"ui-library": uiLibrary,
|
|
333
|
-
"astro-integration": astroIntegration || "none"
|
|
334
|
-
},
|
|
335
|
-
suggestions: ["Use --astro-integration react", "Choose a different UI library (daisyui, ark-ui)"]
|
|
336
|
-
});
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
incompatibilityError({
|
|
340
|
-
message: `UI library '${uiLibrary}' is not compatible with the selected frontend.`,
|
|
341
|
-
provided: {
|
|
342
|
-
"ui-library": uiLibrary,
|
|
343
|
-
frontend: frontends
|
|
344
|
-
},
|
|
345
|
-
suggestions: [`Supported choices for this stack: ${compatible.join(", ")}`, "Choose a different UI library"]
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* Validates that a UI library is compatible with the selected CSS framework
|
|
351
|
-
*/
|
|
352
|
-
function validateUILibraryCSSFrameworkCompatibility(uiLibrary, cssFramework) {
|
|
353
|
-
if (!uiLibrary || uiLibrary === "none") return;
|
|
354
|
-
if (!cssFramework) return;
|
|
355
|
-
const supported = getCompatibleCSSFrameworks(uiLibrary);
|
|
356
|
-
if (!supported.includes(cssFramework)) exitWithError(`UI library '${uiLibrary}' is not compatible with '${cssFramework}' CSS framework. Supported CSS frameworks: ${supported.join(", ")}`);
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* Gets list of UI libraries compatible with the selected frontend(s)
|
|
360
|
-
*/
|
|
361
|
-
function getCompatibleUILibraries$1(frontends = [], astroIntegration) {
|
|
362
|
-
return getCompatibleUILibraries(frontends, astroIntegration);
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Gets list of CSS frameworks compatible with the selected UI library
|
|
366
|
-
*/
|
|
367
|
-
function getCompatibleCSSFrameworks$1(uiLibrary) {
|
|
368
|
-
return getCompatibleCSSFrameworks(uiLibrary);
|
|
369
|
-
}
|
|
370
|
-
/**
|
|
371
|
-
* Checks if a frontend has web styling (excludes native-only frontends)
|
|
372
|
-
*/
|
|
373
|
-
function hasWebStyling$1(frontends = []) {
|
|
374
|
-
return hasWebStyling(frontends);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
//#endregion
|
|
378
12
|
//#region src/utils/navigation.ts
|
|
379
13
|
const GO_BACK_SYMBOL = Symbol("clack:goBack");
|
|
380
14
|
function isGoBack(value) {
|
|
@@ -777,10 +411,10 @@ function getAddonGroup(addon) {
|
|
|
777
411
|
return Object.entries(ADDON_GROUPS).find(([, addons]) => addons.includes(addon))?.[0];
|
|
778
412
|
}
|
|
779
413
|
function validateAddonCompatibilityForPrompt(addon, frontends, auth, backend, runtime) {
|
|
780
|
-
return validateAddonCompatibility
|
|
414
|
+
return validateAddonCompatibility(addon, frontends, auth, backend, runtime);
|
|
781
415
|
}
|
|
782
416
|
function getCompatibleAddonsForPrompt(allAddons, frontends, existingAddons = [], auth, backend, runtime) {
|
|
783
|
-
return getCompatibleAddons
|
|
417
|
+
return getCompatibleAddons(allAddons, frontends, existingAddons, auth, backend, runtime);
|
|
784
418
|
}
|
|
785
419
|
async function getAddonsChoice(addons, frontends, auth, backend, runtime) {
|
|
786
420
|
if (addons !== void 0) return addons;
|
|
@@ -1213,4 +847,4 @@ async function runMixCompile({ projectDir }) {
|
|
|
1213
847
|
}
|
|
1214
848
|
|
|
1215
849
|
//#endregion
|
|
1216
|
-
export {
|
|
850
|
+
export { isGoBack as _, runMavenTests as a, applyDependencyVersionChannel as c, isCancel$1 as d, navigableConfirm as f, GO_BACK_SYMBOL as g, setIsFirstPrompt as h, runGradleTests as i, getAddonsChoice as l, navigableSelect as m, runCargoBuild as n, runMixCompile as o, navigableMultiselect as p, runGoModTidy as r, runUvSync as s, installDependencies as t, getAddonsToAdd as u };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "./errors-D9yiiGVq.mjs";
|
|
3
|
+
import "./config-validation-C4glouQh.mjs";
|
|
4
|
+
import "./compatibility-rules-D7zYNVjC.mjs";
|
|
5
|
+
import { i as startMcpServer, n as MCP_STACK_UPDATE_SCHEMA, r as getMcpGraphPreview, t as MCP_PLAN_CREATE_SCHEMA } from "./mcp-entry.mjs";
|
|
6
|
+
|
|
7
|
+
export { startMcpServer };
|