create-better-fullstack 2.0.0 → 2.0.2
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-BPAZM8Zo.mjs +149 -0
- package/dist/addons-setup-CV5uZyhH.mjs +5 -0
- package/dist/{addons-setup-DHoByttt.mjs → addons-setup-HSghQS7c.mjs} +29 -33
- package/dist/bts-config-Bg1Qea9Y.mjs +737 -0
- package/dist/cli.mjs +2 -2
- package/dist/index.d.mts +404 -96
- package/dist/index.mjs +38 -11545
- package/dist/install-dependencies-D6-GO3BZ.mjs +1139 -0
- package/dist/mcp-58r70ZcL.mjs +5 -0
- package/dist/mcp-entry.mjs +377 -543
- package/dist/run-DQlymC4z.mjs +11546 -0
- package/dist/run-QRBymn-p.mjs +7 -0
- package/dist/update-deps-CLebIM70.mjs +189 -0
- package/package.json +7 -10
- package/dist/addons-setup-Ca069cmy.mjs +0 -5
- package/dist/bts-config-BMniWIbd.mjs +0 -497
- package/dist/mcp-YvDMaVXB.mjs +0 -5
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { t as __reExport } from "./chunk-CCII7kTE.mjs";
|
|
3
|
+
import fs from "fs-extra";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { createCliDefaultProjectConfigBase } from "@better-fullstack/types";
|
|
7
|
+
import * as JSONC from "jsonc-parser";
|
|
8
|
+
|
|
9
|
+
//#region src/utils/get-package-manager.ts
|
|
10
|
+
const getUserPkgManager = () => {
|
|
11
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
12
|
+
if (userAgent?.startsWith("pnpm")) return "pnpm";
|
|
13
|
+
if (userAgent?.startsWith("bun")) return "bun";
|
|
14
|
+
if (userAgent?.startsWith("yarn")) return "yarn";
|
|
15
|
+
return "npm";
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/constants.ts
|
|
20
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
21
|
+
const distPath = path.dirname(__filename);
|
|
22
|
+
const PKG_ROOT = path.join(distPath, "../");
|
|
23
|
+
const DEFAULT_CONFIG_BASE = createCliDefaultProjectConfigBase(getUserPkgManager());
|
|
24
|
+
function getDefaultConfig() {
|
|
25
|
+
return {
|
|
26
|
+
...DEFAULT_CONFIG_BASE,
|
|
27
|
+
projectDir: path.resolve(process.cwd(), DEFAULT_CONFIG_BASE.projectName),
|
|
28
|
+
packageManager: getUserPkgManager(),
|
|
29
|
+
frontend: [...DEFAULT_CONFIG_BASE.frontend],
|
|
30
|
+
addons: [...DEFAULT_CONFIG_BASE.addons],
|
|
31
|
+
examples: [...DEFAULT_CONFIG_BASE.examples],
|
|
32
|
+
rustLibraries: [...DEFAULT_CONFIG_BASE.rustLibraries],
|
|
33
|
+
pythonAi: [...DEFAULT_CONFIG_BASE.pythonAi],
|
|
34
|
+
javaLibraries: [...DEFAULT_CONFIG_BASE.javaLibraries],
|
|
35
|
+
javaTestingLibraries: [...DEFAULT_CONFIG_BASE.javaTestingLibraries],
|
|
36
|
+
aiDocs: [...DEFAULT_CONFIG_BASE.aiDocs]
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const DEFAULT_CONFIG = getDefaultConfig();
|
|
40
|
+
/**
|
|
41
|
+
* Default UI library for each frontend framework
|
|
42
|
+
* Falls back based on what's compatible
|
|
43
|
+
*/
|
|
44
|
+
const DEFAULT_UI_LIBRARY_BY_FRONTEND = {
|
|
45
|
+
"tanstack-router": "shadcn-ui",
|
|
46
|
+
"react-router": "shadcn-ui",
|
|
47
|
+
"react-vite": "shadcn-ui",
|
|
48
|
+
"tanstack-start": "shadcn-ui",
|
|
49
|
+
next: "shadcn-ui",
|
|
50
|
+
vinext: "shadcn-ui",
|
|
51
|
+
nuxt: "daisyui",
|
|
52
|
+
svelte: "daisyui",
|
|
53
|
+
solid: "daisyui",
|
|
54
|
+
"solid-start": "daisyui",
|
|
55
|
+
astro: "daisyui",
|
|
56
|
+
qwik: "daisyui",
|
|
57
|
+
angular: "daisyui",
|
|
58
|
+
redwood: "daisyui",
|
|
59
|
+
fresh: "daisyui",
|
|
60
|
+
"native-bare": "none",
|
|
61
|
+
"native-uniwind": "none",
|
|
62
|
+
"native-unistyles": "none",
|
|
63
|
+
none: "none"
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/utils/get-latest-cli-version.ts
|
|
68
|
+
const getLatestCLIVersion = () => {
|
|
69
|
+
const packageJsonPath = path.join(PKG_ROOT, "package.json");
|
|
70
|
+
return fs.readJSONSync(packageJsonPath).version ?? "1.0.0";
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/types.ts
|
|
75
|
+
var types_exports = {};
|
|
76
|
+
import * as import__better_fullstack_types from "@better-fullstack/types";
|
|
77
|
+
__reExport(types_exports, import__better_fullstack_types);
|
|
78
|
+
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/utils/graph-summary.ts
|
|
81
|
+
const FRONTEND_LABELS = {
|
|
82
|
+
next: "Next.js web app",
|
|
83
|
+
astro: "Astro web app",
|
|
84
|
+
"react-vite": "React + Vite app",
|
|
85
|
+
"react-router": "React Router app",
|
|
86
|
+
"tanstack-router": "TanStack Router app",
|
|
87
|
+
"tanstack-start": "TanStack Start app",
|
|
88
|
+
nuxt: "Nuxt app",
|
|
89
|
+
svelte: "SvelteKit app",
|
|
90
|
+
solid: "Solid app",
|
|
91
|
+
"solid-start": "SolidStart app",
|
|
92
|
+
qwik: "Qwik app",
|
|
93
|
+
angular: "Angular app",
|
|
94
|
+
redwood: "RedwoodJS app",
|
|
95
|
+
fresh: "Fresh app"
|
|
96
|
+
};
|
|
97
|
+
const BACKEND_LABELS = {
|
|
98
|
+
"rust:axum": "Rust Axum API",
|
|
99
|
+
"rust:actix-web": "Rust Actix Web API",
|
|
100
|
+
"rust:rocket": "Rust Rocket API",
|
|
101
|
+
"rust:warp": "Rust Warp API",
|
|
102
|
+
"rust:poem": "Rust Poem API",
|
|
103
|
+
"rust:salvo": "Rust Salvo API",
|
|
104
|
+
"python:fastapi": "Python FastAPI API",
|
|
105
|
+
"python:django": "Python Django API",
|
|
106
|
+
"python:flask": "Python Flask API",
|
|
107
|
+
"python:litestar": "Python Litestar API",
|
|
108
|
+
"go:gin": "Go Gin API",
|
|
109
|
+
"go:echo": "Go Echo API",
|
|
110
|
+
"go:fiber": "Go Fiber API",
|
|
111
|
+
"go:chi": "Go Chi API",
|
|
112
|
+
"java:spring-boot": "Java Spring Boot API",
|
|
113
|
+
"java:quarkus": "Java Quarkus API",
|
|
114
|
+
"elixir:phoenix": "Elixir Phoenix API",
|
|
115
|
+
"elixir:phoenix-live-view": "Elixir Phoenix LiveView API"
|
|
116
|
+
};
|
|
117
|
+
const TOOL_LABELS = {
|
|
118
|
+
postgres: "Postgres",
|
|
119
|
+
sqlite: "SQLite",
|
|
120
|
+
mysql: "MySQL",
|
|
121
|
+
mongodb: "MongoDB",
|
|
122
|
+
redis: "Redis",
|
|
123
|
+
edgedb: "EdgeDB",
|
|
124
|
+
gorm: "GORM",
|
|
125
|
+
sqlc: "sqlc",
|
|
126
|
+
ent: "Ent",
|
|
127
|
+
"ecto-sql": "Ecto SQL",
|
|
128
|
+
ecto: "Ecto",
|
|
129
|
+
sqlx: "SQLx",
|
|
130
|
+
"sea-orm": "SeaORM",
|
|
131
|
+
sqlalchemy: "SQLAlchemy",
|
|
132
|
+
sqlmodel: "SQLModel",
|
|
133
|
+
"django-orm": "Django ORM",
|
|
134
|
+
"spring-data-jpa": "Spring Data JPA"
|
|
135
|
+
};
|
|
136
|
+
function getSelectedGraphParts(config) {
|
|
137
|
+
return (config.stackParts ?? []).filter((part) => part.source !== "provided");
|
|
138
|
+
}
|
|
139
|
+
function getGraphPart(config, role, ecosystem) {
|
|
140
|
+
return getSelectedGraphParts(config).find((part) => part.role === role && (!ecosystem || part.ecosystem === ecosystem));
|
|
141
|
+
}
|
|
142
|
+
function hasGraphPart(config, role, ecosystem) {
|
|
143
|
+
return Boolean(getGraphPart(config, role, ecosystem));
|
|
144
|
+
}
|
|
145
|
+
function getPrimaryGraphPart(config, role, ecosystem) {
|
|
146
|
+
return getSelectedGraphParts(config).find((part) => part.role === role && !part.ownerPartId && (!ecosystem || part.ecosystem === ecosystem));
|
|
147
|
+
}
|
|
148
|
+
function getGraphBackendUrl(config) {
|
|
149
|
+
switch (getPrimaryGraphPart(config, "backend")?.ecosystem) {
|
|
150
|
+
case "elixir": return "http://localhost:4000";
|
|
151
|
+
case "rust": return "http://localhost:3000";
|
|
152
|
+
case "python": return "http://localhost:8000";
|
|
153
|
+
case "go":
|
|
154
|
+
case "java": return "http://localhost:8080";
|
|
155
|
+
default: return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function getEffectiveStack(config) {
|
|
159
|
+
const effectiveStack = {};
|
|
160
|
+
const parts = getSelectedGraphParts(config);
|
|
161
|
+
const partsById = new Map(parts.map((part) => [part.id, part]));
|
|
162
|
+
for (const part of parts) {
|
|
163
|
+
const owner = part.ownerPartId ? partsById.get(part.ownerPartId) : void 0;
|
|
164
|
+
const key = owner ? `${owner.role}.${part.role}` : part.role;
|
|
165
|
+
effectiveStack[key] = `${part.ecosystem}:${part.toolId}`;
|
|
166
|
+
}
|
|
167
|
+
return effectiveStack;
|
|
168
|
+
}
|
|
169
|
+
function labelFor(part) {
|
|
170
|
+
if (!part) return null;
|
|
171
|
+
if (part.role === "frontend" && part.ecosystem === "typescript") return FRONTEND_LABELS[part.toolId] ?? `${part.toolId} web app`;
|
|
172
|
+
if (part.role === "backend") return BACKEND_LABELS[`${part.ecosystem}:${part.toolId}`] ?? `${part.ecosystem} ${part.toolId} API`;
|
|
173
|
+
return TOOL_LABELS[part.toolId] ?? part.toolId;
|
|
174
|
+
}
|
|
175
|
+
function getGraphSummary(config) {
|
|
176
|
+
const selectedParts = getSelectedGraphParts(config);
|
|
177
|
+
if (selectedParts.length === 0) return null;
|
|
178
|
+
const frontend = getPrimaryGraphPart(config, "frontend");
|
|
179
|
+
const backend = getPrimaryGraphPart(config, "backend");
|
|
180
|
+
const database = getPrimaryGraphPart(config, "database");
|
|
181
|
+
const orm = selectedParts.find((part) => part.role === "orm");
|
|
182
|
+
const segments = [labelFor(frontend), labelFor(backend)].filter(Boolean);
|
|
183
|
+
const databaseLabel = backend?.ecosystem === "java" && orm?.toolId === "spring-data-jpa" ? "H2 dev database" : labelFor(database);
|
|
184
|
+
const dataLabel = [labelFor(orm), databaseLabel].filter(Boolean).join("/");
|
|
185
|
+
if (dataLabel) segments.push(dataLabel);
|
|
186
|
+
return segments.length > 0 ? segments.join(" + ") : selectedParts.map((part) => (0, types_exports.formatStackPartSpec)(part, selectedParts)).join(" + ");
|
|
187
|
+
}
|
|
188
|
+
function getGraphBackendDeployInstructions(config) {
|
|
189
|
+
const backend = getPrimaryGraphPart(config, "backend");
|
|
190
|
+
if (!backend || config.serverDeploy === "none") return "";
|
|
191
|
+
const targetPath = backend.targetPath ?? "apps/server";
|
|
192
|
+
const backendLabel = labelFor(backend) ?? `${backend.ecosystem}:${backend.toolId}`;
|
|
193
|
+
switch (config.serverDeploy) {
|
|
194
|
+
case "railway": return [
|
|
195
|
+
"Server deployment with Railway:",
|
|
196
|
+
`* Backend: ${backendLabel}`,
|
|
197
|
+
`* Config: ${targetPath}/railway.toml`,
|
|
198
|
+
`* Deploy: cd ${targetPath} && railway up`
|
|
199
|
+
].join("\n");
|
|
200
|
+
case "docker": return [
|
|
201
|
+
"Server deployment with Docker:",
|
|
202
|
+
`* Backend: ${backendLabel}`,
|
|
203
|
+
`* Build: cd ${targetPath} && docker build -t better-fullstack-server .`
|
|
204
|
+
].join("\n");
|
|
205
|
+
case "fly": return [
|
|
206
|
+
"Server deployment with Fly:",
|
|
207
|
+
`* Backend: ${backendLabel}`,
|
|
208
|
+
`* Deploy: cd ${targetPath} && fly launch`
|
|
209
|
+
].join("\n");
|
|
210
|
+
case "vercel": return [
|
|
211
|
+
"Server deployment with Vercel:",
|
|
212
|
+
`* Backend: ${backendLabel}`,
|
|
213
|
+
`* Deploy from: ${targetPath}`
|
|
214
|
+
].join("\n");
|
|
215
|
+
case "netlify": return [
|
|
216
|
+
"Server deployment with Netlify Functions:",
|
|
217
|
+
`* Backend: ${backendLabel}`,
|
|
218
|
+
`* Config: ${targetPath}/netlify.toml`,
|
|
219
|
+
`* Functions: ${targetPath}/netlify/functions`
|
|
220
|
+
].join("\n");
|
|
221
|
+
case "cloudflare":
|
|
222
|
+
case "sst": return [
|
|
223
|
+
`Server deployment with ${config.serverDeploy}:`,
|
|
224
|
+
`* Backend: ${backendLabel}`,
|
|
225
|
+
`* Review generated files in ${targetPath}`
|
|
226
|
+
].join("\n");
|
|
227
|
+
default: return "";
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region src/utils/bts-config.ts
|
|
233
|
+
const BTS_CONFIG_FILE = "bts.jsonc";
|
|
234
|
+
function normalizeGraphConfigForPersistence(projectConfig, stackParts) {
|
|
235
|
+
if (!stackParts) return projectConfig;
|
|
236
|
+
const legacyConfig = (0, types_exports.stackPartsToLegacyProjectConfigPartial)(stackParts);
|
|
237
|
+
const selectedEcosystems = new Set(stackParts.filter((part) => part.source !== "provided").map((part) => part.ecosystem));
|
|
238
|
+
const normalized = {
|
|
239
|
+
...projectConfig,
|
|
240
|
+
...legacyConfig
|
|
241
|
+
};
|
|
242
|
+
if (!selectedEcosystems.has("rust")) {
|
|
243
|
+
normalized.rustWebFramework = "none";
|
|
244
|
+
normalized.rustFrontend = "none";
|
|
245
|
+
normalized.rustOrm = "none";
|
|
246
|
+
normalized.rustApi = "none";
|
|
247
|
+
normalized.rustCli = "none";
|
|
248
|
+
normalized.rustLibraries = [];
|
|
249
|
+
normalized.rustLogging = "none";
|
|
250
|
+
normalized.rustErrorHandling = "none";
|
|
251
|
+
normalized.rustCaching = "none";
|
|
252
|
+
normalized.rustAuth = "none";
|
|
253
|
+
normalized.rustRealtime = "none";
|
|
254
|
+
normalized.rustMessageQueue = "none";
|
|
255
|
+
normalized.rustObservability = "none";
|
|
256
|
+
normalized.rustTemplating = "none";
|
|
257
|
+
}
|
|
258
|
+
if (!selectedEcosystems.has("python")) {
|
|
259
|
+
normalized.pythonWebFramework = "none";
|
|
260
|
+
normalized.pythonOrm = "none";
|
|
261
|
+
normalized.pythonValidation = "none";
|
|
262
|
+
normalized.pythonAi = [];
|
|
263
|
+
normalized.pythonAuth = "none";
|
|
264
|
+
normalized.pythonApi = "none";
|
|
265
|
+
normalized.pythonTaskQueue = "none";
|
|
266
|
+
normalized.pythonGraphql = "none";
|
|
267
|
+
normalized.pythonQuality = "none";
|
|
268
|
+
normalized.pythonTesting = [];
|
|
269
|
+
normalized.pythonCaching = "none";
|
|
270
|
+
normalized.pythonRealtime = "none";
|
|
271
|
+
normalized.pythonObservability = "none";
|
|
272
|
+
normalized.pythonCli = [];
|
|
273
|
+
}
|
|
274
|
+
if (!selectedEcosystems.has("go")) {
|
|
275
|
+
normalized.goWebFramework = "none";
|
|
276
|
+
normalized.goOrm = "none";
|
|
277
|
+
normalized.goApi = "none";
|
|
278
|
+
normalized.goCli = "none";
|
|
279
|
+
normalized.goLogging = "none";
|
|
280
|
+
normalized.goAuth = "none";
|
|
281
|
+
normalized.goTesting = [];
|
|
282
|
+
normalized.goRealtime = "none";
|
|
283
|
+
normalized.goMessageQueue = "none";
|
|
284
|
+
normalized.goCaching = "none";
|
|
285
|
+
normalized.goConfig = "none";
|
|
286
|
+
normalized.goObservability = "none";
|
|
287
|
+
}
|
|
288
|
+
if (!selectedEcosystems.has("java")) {
|
|
289
|
+
normalized.javaWebFramework = "none";
|
|
290
|
+
normalized.javaBuildTool = "none";
|
|
291
|
+
normalized.javaOrm = "none";
|
|
292
|
+
normalized.javaAuth = "none";
|
|
293
|
+
normalized.javaApi = "none";
|
|
294
|
+
normalized.javaLogging = "none";
|
|
295
|
+
normalized.javaLibraries = [];
|
|
296
|
+
normalized.javaTestingLibraries = [];
|
|
297
|
+
}
|
|
298
|
+
if (!selectedEcosystems.has("dotnet")) {
|
|
299
|
+
normalized.dotnetWebFramework = "none";
|
|
300
|
+
normalized.dotnetOrm = "none";
|
|
301
|
+
normalized.dotnetAuth = "none";
|
|
302
|
+
normalized.dotnetApi = "none";
|
|
303
|
+
normalized.dotnetTesting = [];
|
|
304
|
+
normalized.dotnetJobQueue = "none";
|
|
305
|
+
normalized.dotnetRealtime = "none";
|
|
306
|
+
normalized.dotnetObservability = [];
|
|
307
|
+
normalized.dotnetValidation = "none";
|
|
308
|
+
normalized.dotnetCaching = "none";
|
|
309
|
+
normalized.dotnetDeploy = "none";
|
|
310
|
+
}
|
|
311
|
+
if (!selectedEcosystems.has("elixir")) {
|
|
312
|
+
normalized.elixirWebFramework = "none";
|
|
313
|
+
normalized.elixirOrm = "none";
|
|
314
|
+
normalized.elixirAuth = "none";
|
|
315
|
+
normalized.elixirApi = "none";
|
|
316
|
+
normalized.elixirRealtime = "none";
|
|
317
|
+
normalized.elixirJobs = "none";
|
|
318
|
+
normalized.elixirValidation = "none";
|
|
319
|
+
normalized.elixirHttp = "none";
|
|
320
|
+
normalized.elixirJson = "none";
|
|
321
|
+
normalized.elixirEmail = "none";
|
|
322
|
+
normalized.elixirCaching = "none";
|
|
323
|
+
normalized.elixirObservability = "none";
|
|
324
|
+
normalized.elixirTesting = "none";
|
|
325
|
+
normalized.elixirQuality = "none";
|
|
326
|
+
normalized.elixirDeploy = "none";
|
|
327
|
+
normalized.elixirLibraries = [];
|
|
328
|
+
}
|
|
329
|
+
if (!selectedEcosystems.has("react-native")) {
|
|
330
|
+
normalized.mobileNavigation = "none";
|
|
331
|
+
normalized.mobileUI = "none";
|
|
332
|
+
normalized.mobileStorage = "none";
|
|
333
|
+
normalized.mobileTesting = "none";
|
|
334
|
+
normalized.mobilePush = "none";
|
|
335
|
+
normalized.mobileOTA = "none";
|
|
336
|
+
normalized.mobileDeepLinking = "none";
|
|
337
|
+
}
|
|
338
|
+
return normalized;
|
|
339
|
+
}
|
|
340
|
+
function findSelectedPrimaryPart(stackParts, role, ecosystem) {
|
|
341
|
+
return stackParts.find((part) => part.source !== "provided" && part.role === role && !part.ownerPartId && (!ecosystem || part.ecosystem === ecosystem));
|
|
342
|
+
}
|
|
343
|
+
function isAddonGraphPart(part) {
|
|
344
|
+
const binding = (0, types_exports.getAddonStackPartBinding)(part.toolId);
|
|
345
|
+
return binding !== void 0 && part.role === binding.role && part.ecosystem === binding.ecosystem;
|
|
346
|
+
}
|
|
347
|
+
function hasMatchingStackPart(stackParts, part) {
|
|
348
|
+
return stackParts.some((candidate) => candidate.source !== "provided" && candidate.role === part.role && candidate.ecosystem === part.ecosystem && candidate.toolId === part.toolId && candidate.ownerPartId === part.ownerPartId);
|
|
349
|
+
}
|
|
350
|
+
function syncAddonStackParts(stackParts, addons) {
|
|
351
|
+
const addonSet = new Set(addons.filter((addon) => addon !== "none"));
|
|
352
|
+
const frontend = findSelectedPrimaryPart(stackParts, "frontend", "typescript");
|
|
353
|
+
const next = stackParts.filter((part) => part.source === "provided" || !isAddonGraphPart(part) || addonSet.has(part.toolId));
|
|
354
|
+
for (const addon of addonSet) {
|
|
355
|
+
const binding = (0, types_exports.getAddonStackPartBinding)(addon);
|
|
356
|
+
if (!binding) continue;
|
|
357
|
+
const ownerPartId = binding.ownerRole === "frontend" ? frontend?.id : void 0;
|
|
358
|
+
if (binding.ownerRole === "frontend" && !ownerPartId) continue;
|
|
359
|
+
const part = (0, types_exports.createStackPart)({
|
|
360
|
+
role: binding.role,
|
|
361
|
+
ecosystem: binding.ecosystem,
|
|
362
|
+
toolId: addon,
|
|
363
|
+
ownerPartId,
|
|
364
|
+
source: "selected"
|
|
365
|
+
});
|
|
366
|
+
if (!hasMatchingStackPart(next, part)) next.push(part);
|
|
367
|
+
}
|
|
368
|
+
return next;
|
|
369
|
+
}
|
|
370
|
+
function syncOwnedDeployStackPart(stackParts, ownerRole, deployValue) {
|
|
371
|
+
if (deployValue === void 0) return [...stackParts];
|
|
372
|
+
const owner = findSelectedPrimaryPart(stackParts, ownerRole, "typescript");
|
|
373
|
+
const next = stackParts.filter((part) => part.source === "provided" || part.role !== "deploy" || part.ecosystem !== "typescript" || part.ownerPartId !== owner?.id);
|
|
374
|
+
if (!owner || deployValue === "none") return next;
|
|
375
|
+
next.push((0, types_exports.createStackPart)({
|
|
376
|
+
role: "deploy",
|
|
377
|
+
ecosystem: "typescript",
|
|
378
|
+
toolId: deployValue,
|
|
379
|
+
ownerPartId: owner.id,
|
|
380
|
+
source: "selected"
|
|
381
|
+
}));
|
|
382
|
+
return next;
|
|
383
|
+
}
|
|
384
|
+
function syncUpdatedStackParts(stackParts, updates) {
|
|
385
|
+
if (!stackParts) return void 0;
|
|
386
|
+
let next = [...stackParts];
|
|
387
|
+
if (updates.addons) next = syncAddonStackParts(next, updates.addons);
|
|
388
|
+
next = syncOwnedDeployStackPart(next, "frontend", updates.webDeploy);
|
|
389
|
+
next = syncOwnedDeployStackPart(next, "backend", updates.serverDeploy);
|
|
390
|
+
return next;
|
|
391
|
+
}
|
|
392
|
+
function buildBtsConfigForPersistence(projectConfig, metadata = {}) {
|
|
393
|
+
const stackParts = projectConfig.stackParts ?? (0, types_exports.legacyProjectConfigToStackParts)(projectConfig);
|
|
394
|
+
const persistedConfig = normalizeGraphConfigForPersistence(projectConfig, stackParts);
|
|
395
|
+
const graphSummary = stackParts.length > 0 ? getGraphSummary({ stackParts }) : null;
|
|
396
|
+
const effectiveStack = stackParts.length > 0 ? getEffectiveStack({ stackParts }) : void 0;
|
|
397
|
+
return {
|
|
398
|
+
version: metadata.version ?? getLatestCLIVersion(),
|
|
399
|
+
createdAt: metadata.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
400
|
+
...graphSummary ? {
|
|
401
|
+
graphSummary,
|
|
402
|
+
effectiveStack
|
|
403
|
+
} : {},
|
|
404
|
+
ecosystem: persistedConfig.ecosystem,
|
|
405
|
+
database: persistedConfig.database,
|
|
406
|
+
orm: persistedConfig.orm,
|
|
407
|
+
backend: persistedConfig.backend,
|
|
408
|
+
runtime: persistedConfig.runtime,
|
|
409
|
+
frontend: persistedConfig.frontend,
|
|
410
|
+
addons: persistedConfig.addons,
|
|
411
|
+
examples: persistedConfig.examples,
|
|
412
|
+
auth: persistedConfig.auth,
|
|
413
|
+
payments: persistedConfig.payments,
|
|
414
|
+
email: persistedConfig.email,
|
|
415
|
+
fileUpload: persistedConfig.fileUpload,
|
|
416
|
+
effect: persistedConfig.effect,
|
|
417
|
+
ai: persistedConfig.ai,
|
|
418
|
+
stateManagement: persistedConfig.stateManagement,
|
|
419
|
+
validation: persistedConfig.validation,
|
|
420
|
+
forms: persistedConfig.forms,
|
|
421
|
+
testing: persistedConfig.testing,
|
|
422
|
+
packageManager: persistedConfig.packageManager,
|
|
423
|
+
versionChannel: persistedConfig.versionChannel,
|
|
424
|
+
dbSetup: persistedConfig.dbSetup,
|
|
425
|
+
api: persistedConfig.api,
|
|
426
|
+
webDeploy: persistedConfig.webDeploy,
|
|
427
|
+
serverDeploy: persistedConfig.serverDeploy,
|
|
428
|
+
cssFramework: persistedConfig.cssFramework,
|
|
429
|
+
uiLibrary: persistedConfig.uiLibrary,
|
|
430
|
+
realtime: persistedConfig.realtime,
|
|
431
|
+
jobQueue: persistedConfig.jobQueue,
|
|
432
|
+
animation: persistedConfig.animation,
|
|
433
|
+
logging: persistedConfig.logging,
|
|
434
|
+
observability: persistedConfig.observability,
|
|
435
|
+
featureFlags: persistedConfig.featureFlags,
|
|
436
|
+
analytics: persistedConfig.analytics,
|
|
437
|
+
mobileNavigation: persistedConfig.mobileNavigation,
|
|
438
|
+
mobileUI: persistedConfig.mobileUI,
|
|
439
|
+
mobileStorage: persistedConfig.mobileStorage,
|
|
440
|
+
mobileTesting: persistedConfig.mobileTesting,
|
|
441
|
+
mobilePush: persistedConfig.mobilePush,
|
|
442
|
+
mobileOTA: persistedConfig.mobileOTA,
|
|
443
|
+
mobileDeepLinking: persistedConfig.mobileDeepLinking,
|
|
444
|
+
cms: persistedConfig.cms,
|
|
445
|
+
caching: persistedConfig.caching,
|
|
446
|
+
rateLimit: persistedConfig.rateLimit,
|
|
447
|
+
i18n: persistedConfig.i18n,
|
|
448
|
+
search: persistedConfig.search,
|
|
449
|
+
fileStorage: persistedConfig.fileStorage,
|
|
450
|
+
rustWebFramework: persistedConfig.rustWebFramework,
|
|
451
|
+
rustFrontend: persistedConfig.rustFrontend,
|
|
452
|
+
rustOrm: persistedConfig.rustOrm,
|
|
453
|
+
rustApi: persistedConfig.rustApi,
|
|
454
|
+
rustCli: persistedConfig.rustCli,
|
|
455
|
+
rustLibraries: persistedConfig.rustLibraries,
|
|
456
|
+
rustLogging: persistedConfig.rustLogging,
|
|
457
|
+
rustErrorHandling: persistedConfig.rustErrorHandling,
|
|
458
|
+
rustCaching: persistedConfig.rustCaching,
|
|
459
|
+
rustAuth: persistedConfig.rustAuth,
|
|
460
|
+
rustRealtime: persistedConfig.rustRealtime,
|
|
461
|
+
rustMessageQueue: persistedConfig.rustMessageQueue,
|
|
462
|
+
rustObservability: persistedConfig.rustObservability,
|
|
463
|
+
rustTemplating: persistedConfig.rustTemplating,
|
|
464
|
+
pythonWebFramework: persistedConfig.pythonWebFramework,
|
|
465
|
+
pythonOrm: persistedConfig.pythonOrm,
|
|
466
|
+
pythonValidation: persistedConfig.pythonValidation,
|
|
467
|
+
pythonAi: persistedConfig.pythonAi,
|
|
468
|
+
pythonAuth: persistedConfig.pythonAuth,
|
|
469
|
+
pythonApi: persistedConfig.pythonApi,
|
|
470
|
+
pythonTaskQueue: persistedConfig.pythonTaskQueue,
|
|
471
|
+
pythonGraphql: persistedConfig.pythonGraphql,
|
|
472
|
+
pythonQuality: persistedConfig.pythonQuality,
|
|
473
|
+
pythonTesting: persistedConfig.pythonTesting,
|
|
474
|
+
pythonCaching: persistedConfig.pythonCaching,
|
|
475
|
+
pythonRealtime: persistedConfig.pythonRealtime,
|
|
476
|
+
pythonObservability: persistedConfig.pythonObservability,
|
|
477
|
+
pythonCli: persistedConfig.pythonCli,
|
|
478
|
+
goWebFramework: persistedConfig.goWebFramework,
|
|
479
|
+
goOrm: persistedConfig.goOrm,
|
|
480
|
+
goApi: persistedConfig.goApi,
|
|
481
|
+
goCli: persistedConfig.goCli,
|
|
482
|
+
goLogging: persistedConfig.goLogging,
|
|
483
|
+
goAuth: persistedConfig.goAuth,
|
|
484
|
+
goTesting: persistedConfig.goTesting,
|
|
485
|
+
goRealtime: persistedConfig.goRealtime,
|
|
486
|
+
goMessageQueue: persistedConfig.goMessageQueue,
|
|
487
|
+
goCaching: persistedConfig.goCaching,
|
|
488
|
+
goConfig: persistedConfig.goConfig,
|
|
489
|
+
goObservability: persistedConfig.goObservability,
|
|
490
|
+
javaWebFramework: persistedConfig.javaWebFramework,
|
|
491
|
+
javaBuildTool: persistedConfig.javaBuildTool,
|
|
492
|
+
javaOrm: persistedConfig.javaOrm,
|
|
493
|
+
javaAuth: persistedConfig.javaAuth,
|
|
494
|
+
javaApi: persistedConfig.javaApi,
|
|
495
|
+
javaLogging: persistedConfig.javaLogging,
|
|
496
|
+
javaLibraries: persistedConfig.javaLibraries,
|
|
497
|
+
javaTestingLibraries: persistedConfig.javaTestingLibraries,
|
|
498
|
+
dotnetWebFramework: persistedConfig.dotnetWebFramework,
|
|
499
|
+
dotnetOrm: persistedConfig.dotnetOrm,
|
|
500
|
+
dotnetAuth: persistedConfig.dotnetAuth,
|
|
501
|
+
dotnetApi: persistedConfig.dotnetApi,
|
|
502
|
+
dotnetTesting: persistedConfig.dotnetTesting,
|
|
503
|
+
dotnetJobQueue: persistedConfig.dotnetJobQueue,
|
|
504
|
+
dotnetRealtime: persistedConfig.dotnetRealtime,
|
|
505
|
+
dotnetObservability: persistedConfig.dotnetObservability,
|
|
506
|
+
dotnetValidation: persistedConfig.dotnetValidation,
|
|
507
|
+
dotnetCaching: persistedConfig.dotnetCaching,
|
|
508
|
+
dotnetDeploy: persistedConfig.dotnetDeploy,
|
|
509
|
+
elixirWebFramework: persistedConfig.elixirWebFramework,
|
|
510
|
+
elixirOrm: persistedConfig.elixirOrm,
|
|
511
|
+
elixirAuth: persistedConfig.elixirAuth,
|
|
512
|
+
elixirApi: persistedConfig.elixirApi,
|
|
513
|
+
elixirRealtime: persistedConfig.elixirRealtime,
|
|
514
|
+
elixirJobs: persistedConfig.elixirJobs,
|
|
515
|
+
elixirValidation: persistedConfig.elixirValidation,
|
|
516
|
+
elixirHttp: persistedConfig.elixirHttp,
|
|
517
|
+
elixirJson: persistedConfig.elixirJson,
|
|
518
|
+
elixirEmail: persistedConfig.elixirEmail,
|
|
519
|
+
elixirCaching: persistedConfig.elixirCaching,
|
|
520
|
+
elixirObservability: persistedConfig.elixirObservability,
|
|
521
|
+
elixirTesting: persistedConfig.elixirTesting,
|
|
522
|
+
elixirQuality: persistedConfig.elixirQuality,
|
|
523
|
+
elixirDeploy: persistedConfig.elixirDeploy,
|
|
524
|
+
elixirLibraries: persistedConfig.elixirLibraries,
|
|
525
|
+
aiDocs: persistedConfig.aiDocs,
|
|
526
|
+
stackParts
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
function previewBtsConfigUpdate(currentConfig, updates) {
|
|
530
|
+
const updatedStackParts = syncUpdatedStackParts(currentConfig.stackParts, updates);
|
|
531
|
+
return buildBtsConfigForPersistence({
|
|
532
|
+
...currentConfig,
|
|
533
|
+
...updates,
|
|
534
|
+
...updatedStackParts ? { stackParts: updatedStackParts } : {}
|
|
535
|
+
}, {
|
|
536
|
+
version: currentConfig.version,
|
|
537
|
+
createdAt: currentConfig.createdAt
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
async function writeBtsConfig(projectConfig) {
|
|
541
|
+
const btsConfig = buildBtsConfigForPersistence(projectConfig);
|
|
542
|
+
const baseContent = {
|
|
543
|
+
$schema: "https://better-fullstack-web.vercel.app/schema.json",
|
|
544
|
+
version: btsConfig.version,
|
|
545
|
+
createdAt: btsConfig.createdAt,
|
|
546
|
+
...btsConfig.graphSummary ? {
|
|
547
|
+
graphSummary: btsConfig.graphSummary,
|
|
548
|
+
effectiveStack: btsConfig.effectiveStack
|
|
549
|
+
} : {},
|
|
550
|
+
ecosystem: btsConfig.ecosystem,
|
|
551
|
+
database: btsConfig.database,
|
|
552
|
+
orm: btsConfig.orm,
|
|
553
|
+
backend: btsConfig.backend,
|
|
554
|
+
runtime: btsConfig.runtime,
|
|
555
|
+
frontend: btsConfig.frontend,
|
|
556
|
+
addons: btsConfig.addons,
|
|
557
|
+
examples: btsConfig.examples,
|
|
558
|
+
auth: btsConfig.auth,
|
|
559
|
+
payments: btsConfig.payments,
|
|
560
|
+
email: btsConfig.email,
|
|
561
|
+
fileUpload: btsConfig.fileUpload,
|
|
562
|
+
effect: btsConfig.effect,
|
|
563
|
+
ai: btsConfig.ai,
|
|
564
|
+
stateManagement: btsConfig.stateManagement,
|
|
565
|
+
validation: btsConfig.validation,
|
|
566
|
+
forms: btsConfig.forms,
|
|
567
|
+
testing: btsConfig.testing,
|
|
568
|
+
packageManager: btsConfig.packageManager,
|
|
569
|
+
versionChannel: btsConfig.versionChannel,
|
|
570
|
+
dbSetup: btsConfig.dbSetup,
|
|
571
|
+
api: btsConfig.api,
|
|
572
|
+
webDeploy: btsConfig.webDeploy,
|
|
573
|
+
serverDeploy: btsConfig.serverDeploy,
|
|
574
|
+
cssFramework: btsConfig.cssFramework,
|
|
575
|
+
uiLibrary: btsConfig.uiLibrary,
|
|
576
|
+
realtime: btsConfig.realtime,
|
|
577
|
+
jobQueue: btsConfig.jobQueue,
|
|
578
|
+
animation: btsConfig.animation,
|
|
579
|
+
logging: btsConfig.logging,
|
|
580
|
+
observability: btsConfig.observability,
|
|
581
|
+
featureFlags: btsConfig.featureFlags,
|
|
582
|
+
analytics: btsConfig.analytics,
|
|
583
|
+
mobileNavigation: btsConfig.mobileNavigation,
|
|
584
|
+
mobileUI: btsConfig.mobileUI,
|
|
585
|
+
mobileStorage: btsConfig.mobileStorage,
|
|
586
|
+
mobileTesting: btsConfig.mobileTesting,
|
|
587
|
+
mobilePush: btsConfig.mobilePush,
|
|
588
|
+
mobileOTA: btsConfig.mobileOTA,
|
|
589
|
+
mobileDeepLinking: btsConfig.mobileDeepLinking,
|
|
590
|
+
cms: btsConfig.cms,
|
|
591
|
+
caching: btsConfig.caching,
|
|
592
|
+
rateLimit: btsConfig.rateLimit,
|
|
593
|
+
i18n: btsConfig.i18n,
|
|
594
|
+
search: btsConfig.search,
|
|
595
|
+
fileStorage: btsConfig.fileStorage,
|
|
596
|
+
rustWebFramework: btsConfig.rustWebFramework,
|
|
597
|
+
rustFrontend: btsConfig.rustFrontend,
|
|
598
|
+
rustOrm: btsConfig.rustOrm,
|
|
599
|
+
rustApi: btsConfig.rustApi,
|
|
600
|
+
rustCli: btsConfig.rustCli,
|
|
601
|
+
rustLibraries: btsConfig.rustLibraries,
|
|
602
|
+
rustLogging: btsConfig.rustLogging,
|
|
603
|
+
rustErrorHandling: btsConfig.rustErrorHandling,
|
|
604
|
+
rustCaching: btsConfig.rustCaching,
|
|
605
|
+
rustAuth: btsConfig.rustAuth,
|
|
606
|
+
rustRealtime: btsConfig.rustRealtime,
|
|
607
|
+
rustMessageQueue: btsConfig.rustMessageQueue,
|
|
608
|
+
rustObservability: btsConfig.rustObservability,
|
|
609
|
+
rustTemplating: btsConfig.rustTemplating,
|
|
610
|
+
pythonWebFramework: btsConfig.pythonWebFramework,
|
|
611
|
+
pythonOrm: btsConfig.pythonOrm,
|
|
612
|
+
pythonValidation: btsConfig.pythonValidation,
|
|
613
|
+
pythonAi: btsConfig.pythonAi,
|
|
614
|
+
pythonAuth: btsConfig.pythonAuth,
|
|
615
|
+
pythonApi: btsConfig.pythonApi ?? "none",
|
|
616
|
+
pythonTaskQueue: btsConfig.pythonTaskQueue,
|
|
617
|
+
pythonGraphql: btsConfig.pythonGraphql,
|
|
618
|
+
pythonQuality: btsConfig.pythonQuality,
|
|
619
|
+
pythonTesting: btsConfig.pythonTesting,
|
|
620
|
+
pythonCaching: btsConfig.pythonCaching,
|
|
621
|
+
pythonRealtime: btsConfig.pythonRealtime,
|
|
622
|
+
pythonObservability: btsConfig.pythonObservability,
|
|
623
|
+
pythonCli: btsConfig.pythonCli,
|
|
624
|
+
goWebFramework: btsConfig.goWebFramework,
|
|
625
|
+
goOrm: btsConfig.goOrm,
|
|
626
|
+
goApi: btsConfig.goApi,
|
|
627
|
+
goCli: btsConfig.goCli,
|
|
628
|
+
goLogging: btsConfig.goLogging,
|
|
629
|
+
goAuth: btsConfig.goAuth,
|
|
630
|
+
goTesting: btsConfig.goTesting,
|
|
631
|
+
goRealtime: btsConfig.goRealtime,
|
|
632
|
+
goMessageQueue: btsConfig.goMessageQueue,
|
|
633
|
+
goCaching: btsConfig.goCaching,
|
|
634
|
+
goConfig: btsConfig.goConfig,
|
|
635
|
+
goObservability: btsConfig.goObservability,
|
|
636
|
+
javaWebFramework: btsConfig.javaWebFramework,
|
|
637
|
+
javaBuildTool: btsConfig.javaBuildTool,
|
|
638
|
+
javaOrm: btsConfig.javaOrm,
|
|
639
|
+
javaAuth: btsConfig.javaAuth,
|
|
640
|
+
javaApi: btsConfig.javaApi,
|
|
641
|
+
javaLogging: btsConfig.javaLogging,
|
|
642
|
+
javaLibraries: btsConfig.javaLibraries,
|
|
643
|
+
javaTestingLibraries: btsConfig.javaTestingLibraries,
|
|
644
|
+
dotnetWebFramework: btsConfig.dotnetWebFramework,
|
|
645
|
+
dotnetOrm: btsConfig.dotnetOrm,
|
|
646
|
+
dotnetAuth: btsConfig.dotnetAuth,
|
|
647
|
+
dotnetApi: btsConfig.dotnetApi,
|
|
648
|
+
dotnetTesting: btsConfig.dotnetTesting,
|
|
649
|
+
dotnetJobQueue: btsConfig.dotnetJobQueue,
|
|
650
|
+
dotnetRealtime: btsConfig.dotnetRealtime,
|
|
651
|
+
dotnetObservability: btsConfig.dotnetObservability,
|
|
652
|
+
dotnetValidation: btsConfig.dotnetValidation,
|
|
653
|
+
dotnetCaching: btsConfig.dotnetCaching,
|
|
654
|
+
dotnetDeploy: btsConfig.dotnetDeploy,
|
|
655
|
+
elixirWebFramework: btsConfig.elixirWebFramework,
|
|
656
|
+
elixirOrm: btsConfig.elixirOrm,
|
|
657
|
+
elixirAuth: btsConfig.elixirAuth,
|
|
658
|
+
elixirApi: btsConfig.elixirApi,
|
|
659
|
+
elixirRealtime: btsConfig.elixirRealtime,
|
|
660
|
+
elixirJobs: btsConfig.elixirJobs,
|
|
661
|
+
elixirValidation: btsConfig.elixirValidation,
|
|
662
|
+
elixirHttp: btsConfig.elixirHttp,
|
|
663
|
+
elixirJson: btsConfig.elixirJson,
|
|
664
|
+
elixirEmail: btsConfig.elixirEmail,
|
|
665
|
+
elixirCaching: btsConfig.elixirCaching,
|
|
666
|
+
elixirObservability: btsConfig.elixirObservability,
|
|
667
|
+
elixirTesting: btsConfig.elixirTesting,
|
|
668
|
+
elixirQuality: btsConfig.elixirQuality,
|
|
669
|
+
elixirDeploy: btsConfig.elixirDeploy,
|
|
670
|
+
elixirLibraries: btsConfig.elixirLibraries,
|
|
671
|
+
aiDocs: btsConfig.aiDocs,
|
|
672
|
+
stackParts: btsConfig.stackParts
|
|
673
|
+
};
|
|
674
|
+
let configContent = JSON.stringify(baseContent);
|
|
675
|
+
const formatResult = JSONC.format(configContent, void 0, {
|
|
676
|
+
tabSize: 2,
|
|
677
|
+
insertSpaces: true,
|
|
678
|
+
eol: "\n"
|
|
679
|
+
});
|
|
680
|
+
configContent = JSONC.applyEdits(configContent, formatResult);
|
|
681
|
+
const finalContent = `// Better Fullstack configuration file
|
|
682
|
+
// safe to delete
|
|
683
|
+
${btsConfig.stackParts?.length ? "// stackParts is the source of truth; graphSummary/effectiveStack summarize it for humans and tools.\n// Top-level option fields are a derived compatibility cache for older integrations.\n" : ""}
|
|
684
|
+
|
|
685
|
+
${configContent}`;
|
|
686
|
+
const configPath = path.join(projectConfig.projectDir, BTS_CONFIG_FILE);
|
|
687
|
+
await fs.writeFile(configPath, finalContent, "utf-8");
|
|
688
|
+
}
|
|
689
|
+
async function readBtsConfig(projectDir) {
|
|
690
|
+
try {
|
|
691
|
+
const configPath = path.join(projectDir, BTS_CONFIG_FILE);
|
|
692
|
+
if (!await fs.pathExists(configPath)) return null;
|
|
693
|
+
const configContent = await fs.readFile(configPath, "utf-8");
|
|
694
|
+
const errors = [];
|
|
695
|
+
const config = JSONC.parse(configContent, errors, {
|
|
696
|
+
allowTrailingComma: true,
|
|
697
|
+
disallowComments: false
|
|
698
|
+
});
|
|
699
|
+
if (errors.length > 0) {
|
|
700
|
+
console.warn("Warning: Found errors parsing bts.jsonc:", errors);
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
return buildBtsConfigForPersistence(config, {
|
|
704
|
+
version: config.version,
|
|
705
|
+
createdAt: config.createdAt
|
|
706
|
+
});
|
|
707
|
+
} catch {
|
|
708
|
+
return null;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
async function updateBtsConfig(projectDir, updates) {
|
|
712
|
+
try {
|
|
713
|
+
const configPath = path.join(projectDir, BTS_CONFIG_FILE);
|
|
714
|
+
if (!await fs.pathExists(configPath)) return;
|
|
715
|
+
const configContent = await fs.readFile(configPath, "utf-8");
|
|
716
|
+
const errors = [];
|
|
717
|
+
const currentConfig = JSONC.parse(configContent, errors, {
|
|
718
|
+
allowTrailingComma: true,
|
|
719
|
+
disallowComments: false
|
|
720
|
+
});
|
|
721
|
+
let modifiedContent = configContent;
|
|
722
|
+
const nextConfig = errors.length === 0 ? previewBtsConfigUpdate(currentConfig, updates) : void 0;
|
|
723
|
+
const persistedUpdates = nextConfig ? Object.fromEntries(Object.entries(nextConfig).filter(([key]) => key !== "version" && key !== "createdAt")) : updates;
|
|
724
|
+
for (const [key, value] of Object.entries(persistedUpdates)) {
|
|
725
|
+
const editResult = JSONC.modify(modifiedContent, [key], value, { formattingOptions: {
|
|
726
|
+
tabSize: 2,
|
|
727
|
+
insertSpaces: true,
|
|
728
|
+
eol: "\n"
|
|
729
|
+
} });
|
|
730
|
+
modifiedContent = JSONC.applyEdits(modifiedContent, editResult);
|
|
731
|
+
}
|
|
732
|
+
await fs.writeFile(configPath, modifiedContent, "utf-8");
|
|
733
|
+
} catch {}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
//#endregion
|
|
737
|
+
export { getUserPkgManager as _, getEffectiveStack as a, getGraphPart as c, hasGraphPart as d, types_exports as f, getDefaultConfig as g, DEFAULT_UI_LIBRARY_BY_FRONTEND as h, writeBtsConfig as i, getGraphSummary as l, DEFAULT_CONFIG as m, readBtsConfig as n, getGraphBackendDeployInstructions as o, getLatestCLIVersion as p, updateBtsConfig as r, getGraphBackendUrl as s, previewBtsConfigUpdate as t, getPrimaryGraphPart as u };
|