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.
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { r as readBtsConfig } from "./bts-config-InNcw1aJ.mjs";
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 { a as handleError } from "./errors-ns_o2OKg.mjs";
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
- if (!selectedEcosystems.has("react-native")) {
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
- export { getPrimaryGraphPart as _, updateBtsConfig as a, DEFAULT_CONFIG as c, getUserPkgManager as d, getEffectiveStack as f, getGraphSummary as g, getGraphPart as h, readBtsConfigFromFile as i, DEFAULT_UI_LIBRARY_BY_FRONTEND as l, getGraphBackendUrl as m, previewBtsConfigUpdate as n, writeBtsConfig as o, getGraphBackendDeployInstructions as p, readBtsConfig as r, getLatestCLIVersion as s, buildBtsConfigForPersistence as t, getDefaultConfig as u, hasGraphPart as v, types_exports as y };
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 { _ as getPrimaryGraphPart } from "./bts-config-InNcw1aJ.mjs";
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 "./bts-config-InNcw1aJ.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-Cqy-n1iT.mjs";
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 "./errors-ns_o2OKg.mjs";
6
- import "./addons-setup-uSvqHagW.mjs";
7
- import "./install-dependencies-CDjTNvIV.mjs";
8
- import "./generated-checks-CUhFFfVo.mjs";
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 { c as DEFAULT_CONFIG, y as types_exports } from "./bts-config-InNcw1aJ.mjs";
3
- import { c as isSilent, d as setLastPromptShownUI, i as exitWithError, r as exitCancelled, s as isFirstPrompt, t as CLIError, u as setIsFirstPrompt$1 } from "./errors-ns_o2OKg.mjs";
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$1(addon, frontends, auth, backend, runtime);
414
+ return validateAddonCompatibility(addon, frontends, auth, backend, runtime);
781
415
  }
782
416
  function getCompatibleAddonsForPrompt(allAddons, frontends, existingAddons = [], auth, backend, runtime) {
783
- return getCompatibleAddons$1(allAddons, frontends, existingAddons, auth, backend, runtime);
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 { validateAddonsAgainstFrontends as A, validateWorkersCompatibility as B, isExampleAIAllowed$1 as C, requiresChatSdkVercelAI as D, isWebFrontend$1 as E, validateServerDeployRequiresBackend as F, incompatibilityError as H, validateUILibraryCSSFrameworkCompatibility as I, validateUILibraryFrontendCompatibility as L, validateExamplesCompatibility as M, validatePaymentsCompatibility as N, splitFrontends$1 as O, validateSelfBackendCompatibility as P, validateWebDeployFrontendTemplates as R, hasWebStyling$1 as S, isFrontendAllowedWithBackend$1 as T, missingRequirementError as U, constraintError as V, isGoBack as _, runMavenTests as a, getCompatibleCSSFrameworks$1 as b, applyDependencyVersionChannel as c, isCancel$1 as d, navigableConfirm as f, GO_BACK_SYMBOL as g, setIsFirstPrompt as h, runGradleTests as i, validateApiFrontendCompatibility as j, validateAIFrontendCompatibility as k, 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, allowedApisForFrontends$1 as v, isExampleChatSdkAllowed$1 as w, getCompatibleUILibraries$1 as x, ensureSingleWebAndNative as y, validateWebDeployRequiresWebFrontend as z };
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 };