@launchsecure/launch-kit 0.0.17 → 0.0.19
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/chart-client/assets/index--120d9P9.css +1 -0
- package/dist/chart-client/assets/index-C8ANseEa.js +441 -0
- package/dist/chart-client/index.html +2 -2
- package/dist/client/assets/index-Bf8zdL3x.css +32 -0
- package/dist/client/assets/index-Ds9UP_cj.js +291 -0
- package/dist/client/index.html +2 -2
- package/dist/council-client/assets/index-CofZh7pS.css +1 -0
- package/dist/council-client/assets/index-Dc41S-R2.js +198 -0
- package/dist/council-client/index.html +21 -0
- package/dist/deck-client/assets/{_baseUniq-BbqvoK-V.js → _baseUniq-DsfOm3t_.js} +1 -1
- package/dist/deck-client/assets/{arc-CMtYsIZt.js → arc-NJuvkBv1.js} +1 -1
- package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-BEN5hESa.js → architectureDiagram-Q4EWVU46-BgrcgZs0.js} +1 -1
- package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-BV4CZ6k8.js → blockDiagram-DXYQGD6D-C3XoLi15.js} +1 -1
- package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-fLcBXqdD.js → c4Diagram-AHTNJAMY-FX2PjLfb.js} +1 -1
- package/dist/deck-client/assets/channel-ChQjD1T1.js +1 -0
- package/dist/deck-client/assets/{chunk-4BX2VUAB-BO_19zwB.js → chunk-4BX2VUAB-D0aqsJV0.js} +1 -1
- package/dist/deck-client/assets/{chunk-4TB4RGXK-iYegd5fu.js → chunk-4TB4RGXK-7qRCCAgK.js} +1 -1
- package/dist/deck-client/assets/{chunk-55IACEB6-DM3QwYFL.js → chunk-55IACEB6-DfHG-iqb.js} +1 -1
- package/dist/deck-client/assets/{chunk-EDXVE4YY-DGznOul1.js → chunk-EDXVE4YY-DrR52j3B.js} +1 -1
- package/dist/deck-client/assets/{chunk-FMBD7UC4-DsANJqYW.js → chunk-FMBD7UC4-D5KSGATB.js} +1 -1
- package/dist/deck-client/assets/{chunk-OYMX7WX6-6PGH1F7d.js → chunk-OYMX7WX6-M7hsLRNU.js} +1 -1
- package/dist/deck-client/assets/{chunk-QZHKN3VN-Dihf0Uq7.js → chunk-QZHKN3VN-1ynAWO2m.js} +1 -1
- package/dist/deck-client/assets/{chunk-YZCP3GAM-Cali2wW5.js → chunk-YZCP3GAM-S2-nGw3D.js} +1 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-B_9iqK1S.js +1 -0
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-B_9iqK1S.js +1 -0
- package/dist/deck-client/assets/clone-BYt1AMfz.js +1 -0
- package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-DsRY4vbI.js → cose-bilkent-S5V4N54A-BcMwozS2.js} +1 -1
- package/dist/deck-client/assets/cytoscape.esm-BQk4lpUV.js +331 -0
- package/dist/deck-client/assets/{dagre-KV5264BT-DJIKE_pI.js → dagre-KV5264BT-DtKMhl_1.js} +1 -1
- package/dist/deck-client/assets/{diagram-5BDNPKRD-Ckgli1SP.js → diagram-5BDNPKRD-1plH69us.js} +1 -1
- package/dist/deck-client/assets/{diagram-G4DWMVQ6-CozcDzae.js → diagram-G4DWMVQ6-D_o-BHO3.js} +1 -1
- package/dist/deck-client/assets/{diagram-MMDJMWI5-xVSwW3f_.js → diagram-MMDJMWI5-ClZ1LIx6.js} +1 -1
- package/dist/deck-client/assets/{diagram-TYMM5635-CJeZVY-P.js → diagram-TYMM5635-B8dKHfRh.js} +1 -1
- package/dist/deck-client/assets/{erDiagram-SMLLAGMA-j4wjAERH.js → erDiagram-SMLLAGMA-CY2aCH7-.js} +1 -1
- package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-CVLZ1efi.js → flowDiagram-DWJPFMVM-DZZWHti8.js} +1 -1
- package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-CcIJ7pkP.js → ganttDiagram-T4ZO3ILL-OwGGa6Lu.js} +1 -1
- package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-BZRhQX-a.js → gitGraphDiagram-UUTBAWPF-GKyWD4Qt.js} +1 -1
- package/dist/deck-client/assets/{graph-D0l25xfo.js → graph-CORzYQdB.js} +1 -1
- package/dist/deck-client/assets/index-765AIQ9z.css +1 -0
- package/dist/deck-client/assets/{index-BXcoHWVM.js → index-hiIpM7EP.js} +93 -93
- package/dist/deck-client/assets/{infoDiagram-42DDH7IO-BLwgxnYT.js → infoDiagram-42DDH7IO-DmgqJCcF.js} +1 -1
- package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-BfOLoWv3.js → ishikawaDiagram-UXIWVN3A-D-1v7knu.js} +1 -1
- package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-CPuL6C9h.js → journeyDiagram-VCZTEJTY-CYrGQE7b.js} +1 -1
- package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-D3uf7_tx.js → kanban-definition-6JOO6SKY-BJFDWiH-.js} +1 -1
- package/dist/deck-client/assets/{layout-CzToiXdK.js → layout-BTFFcaxF.js} +1 -1
- package/dist/deck-client/assets/{linear-BU36t460.js → linear-DAbl6COS.js} +1 -1
- package/dist/deck-client/assets/{min-DX_q-lqP.js → min-oWHBrFBm.js} +1 -1
- package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-Ccty4O16.js → mindmap-definition-QFDTVHPH-BTCB0VLO.js} +1 -1
- package/dist/deck-client/assets/{pieDiagram-DEJITSTG-DVjsvH19.js → pieDiagram-DEJITSTG-CUZChWNA.js} +1 -1
- package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-DtOXFVW9.js → quadrantDiagram-34T5L4WZ-4M1Um_e4.js} +1 -1
- package/dist/deck-client/assets/{requirementDiagram-MS252O5E-BbO_kKg6.js → requirementDiagram-MS252O5E-DLzQZ0B3.js} +1 -1
- package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-qbbj-CmC.js → sankeyDiagram-XADWPNL6-DcNgzV3E.js} +1 -1
- package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-JNKZAgfQ.js → sequenceDiagram-FGHM5R23-CAcI2vC9.js} +1 -1
- package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-dtFalcNx.js → stateDiagram-FHFEXIEX-CntjTTm5.js} +1 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-YiaphOU_.js +1 -0
- package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-Dpp5nqSJ.js → timeline-definition-GMOUNBTQ-D8zrit4U.js} +1 -1
- package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-D8qEutX7.js → vennDiagram-DHZGUBPP-C4SuFPgo.js} +1 -1
- package/dist/deck-client/assets/{wardley-RL74JXVD-BwMqiNcL.js → wardley-RL74JXVD-B3F-Olcq.js} +58 -58
- package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-Bxl9X3CK.js → wardleyDiagram-NUSXRM2D-kj73r6f-.js} +1 -1
- package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-DYcvxLhi.js → xychartDiagram-5P7HB3ND-CC_d_Ey3.js} +1 -1
- package/dist/deck-client/index.html +2 -2
- package/dist/server/chart-serve.js +452 -244
- package/dist/server/cli.js +558 -750
- package/dist/server/council-entry.js +1418 -0
- package/dist/server/council-serve.js +1039 -0
- package/dist/server/graph-mcp-entry.js +486 -695
- package/package.json +10 -9
- package/dist/chart-client/assets/index-BUhuLBaw.js +0 -441
- package/dist/chart-client/assets/index-CWRZxjqR.css +0 -1
- package/dist/client/assets/index-CAAipH3V.js +0 -291
- package/dist/client/assets/index-DtbN793z.css +0 -32
- package/dist/deck-client/assets/channel-Nf-B3Qor.js +0 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-3i3-miMR.js +0 -1
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-3i3-miMR.js +0 -1
- package/dist/deck-client/assets/clone-DXBuQlG8.js +0 -1
- package/dist/deck-client/assets/cytoscape.esm-BiciSPf8.js +0 -331
- package/dist/deck-client/assets/index-Cdh-f3-c.css +0 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CviYYulW.js +0 -1
|
@@ -155,36 +155,248 @@ var init_config = __esm({
|
|
|
155
155
|
}
|
|
156
156
|
});
|
|
157
157
|
|
|
158
|
+
// src/server/graph/core/walk.ts
|
|
159
|
+
function walk(dir, exts) {
|
|
160
|
+
const results = [];
|
|
161
|
+
if (!(0, import_node_fs3.existsSync)(dir)) return results;
|
|
162
|
+
for (const entry of (0, import_node_fs3.readdirSync)(dir, { withFileTypes: true })) {
|
|
163
|
+
const full = (0, import_node_path3.join)(dir, entry.name);
|
|
164
|
+
if (entry.isDirectory()) {
|
|
165
|
+
results.push(...walk(full, exts));
|
|
166
|
+
} else if (exts.includes((0, import_node_path3.extname)(entry.name))) {
|
|
167
|
+
results.push(full);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return results;
|
|
171
|
+
}
|
|
172
|
+
function walkWithIgnore(dir, exts, opts = {}) {
|
|
173
|
+
const results = [];
|
|
174
|
+
if (!(0, import_node_fs3.existsSync)(dir)) return results;
|
|
175
|
+
const skip = opts.extraIgnore ? /* @__PURE__ */ new Set([...DEFAULT_IGNORE_DIRS, ...opts.extraIgnore]) : DEFAULT_IGNORE_DIRS;
|
|
176
|
+
for (const entry of (0, import_node_fs3.readdirSync)(dir, { withFileTypes: true })) {
|
|
177
|
+
if (entry.isDirectory()) {
|
|
178
|
+
if (skip.has(entry.name)) continue;
|
|
179
|
+
results.push(...walkWithIgnore((0, import_node_path3.join)(dir, entry.name), exts, opts));
|
|
180
|
+
} else if (exts.includes((0, import_node_path3.extname)(entry.name))) {
|
|
181
|
+
results.push((0, import_node_path3.join)(dir, entry.name));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return results;
|
|
185
|
+
}
|
|
186
|
+
var import_node_fs3, import_node_path3, DEFAULT_IGNORE_DIRS;
|
|
187
|
+
var init_walk = __esm({
|
|
188
|
+
"src/server/graph/core/walk.ts"() {
|
|
189
|
+
"use strict";
|
|
190
|
+
import_node_fs3 = require("node:fs");
|
|
191
|
+
import_node_path3 = require("node:path");
|
|
192
|
+
DEFAULT_IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
193
|
+
"node_modules",
|
|
194
|
+
".git",
|
|
195
|
+
".next",
|
|
196
|
+
".launchsecure",
|
|
197
|
+
".claude",
|
|
198
|
+
"dist",
|
|
199
|
+
"build",
|
|
200
|
+
"out",
|
|
201
|
+
".turbo",
|
|
202
|
+
".vercel",
|
|
203
|
+
"coverage"
|
|
204
|
+
]);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
158
208
|
// src/server/graph/core/resolve-paths.ts
|
|
159
|
-
function
|
|
160
|
-
if (
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
209
|
+
function hasSqlFiles(dir) {
|
|
210
|
+
if (!(0, import_node_fs4.existsSync)(dir)) return false;
|
|
211
|
+
try {
|
|
212
|
+
return (0, import_node_fs4.readdirSync)(dir, { withFileTypes: true }).some(
|
|
213
|
+
(e) => e.isFile() && e.name.endsWith(".sql")
|
|
214
|
+
);
|
|
215
|
+
} catch {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
164
218
|
}
|
|
165
|
-
function
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
219
|
+
function hasNestedMigrationSql(dir) {
|
|
220
|
+
if (!(0, import_node_fs4.existsSync)(dir)) return false;
|
|
221
|
+
try {
|
|
222
|
+
return (0, import_node_fs4.readdirSync)(dir, { withFileTypes: true }).some(
|
|
223
|
+
(e) => e.isDirectory() && (0, import_node_fs4.existsSync)((0, import_node_path4.join)(dir, e.name, "migration.sql"))
|
|
224
|
+
);
|
|
225
|
+
} catch {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function resolveDbFromDir(dir) {
|
|
230
|
+
if (!(0, import_node_fs4.existsSync)(dir)) return { kind: "none", schemaPath: null, migrationsDir: null };
|
|
231
|
+
const schemaPath = (0, import_node_path4.join)(dir, "schema.prisma");
|
|
232
|
+
if ((0, import_node_fs4.existsSync)(schemaPath)) {
|
|
233
|
+
const migrationsDir2 = (0, import_node_path4.join)(dir, "migrations");
|
|
234
|
+
return {
|
|
235
|
+
kind: "prisma",
|
|
236
|
+
schemaPath,
|
|
237
|
+
migrationsDir: (0, import_node_fs4.existsSync)(migrationsDir2) ? migrationsDir2 : null
|
|
238
|
+
};
|
|
171
239
|
}
|
|
172
|
-
const
|
|
173
|
-
if ((
|
|
174
|
-
return {
|
|
240
|
+
const migrationsDir = (0, import_node_path4.join)(dir, "migrations");
|
|
241
|
+
if (hasSqlFiles(migrationsDir) || hasNestedMigrationSql(migrationsDir)) {
|
|
242
|
+
return { kind: "sql-migrations", migrationsDir, schemaPath: null };
|
|
175
243
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
return { srcDir: rootDir, appDir: rootApp, apiDir: (0, import_node_path3.join)(rootApp, "api"), dbDir };
|
|
244
|
+
if (hasSqlFiles(dir) || hasNestedMigrationSql(dir)) {
|
|
245
|
+
return { kind: "sql-migrations", migrationsDir: dir, schemaPath: null };
|
|
179
246
|
}
|
|
247
|
+
return { kind: "none", schemaPath: null, migrationsDir: null };
|
|
248
|
+
}
|
|
249
|
+
function detectDbConfig(rootDir, config) {
|
|
250
|
+
if (config.paths?.dbDir) {
|
|
251
|
+
return resolveDbFromDir((0, import_node_path4.join)(rootDir, config.paths.dbDir));
|
|
252
|
+
}
|
|
253
|
+
const candidates = ["prisma", "supabase", "drizzle", (0, import_node_path4.join)("db", "migrations"), "migrations"];
|
|
254
|
+
for (const c of candidates) {
|
|
255
|
+
const dir = (0, import_node_path4.join)(rootDir, c);
|
|
256
|
+
const resolved = resolveDbFromDir(dir);
|
|
257
|
+
if (resolved.kind !== "none") return resolved;
|
|
258
|
+
}
|
|
259
|
+
return { kind: "none", schemaPath: null, migrationsDir: null };
|
|
260
|
+
}
|
|
261
|
+
function detectDbDir(rootDir, config, dbConfig) {
|
|
262
|
+
if (config.paths?.dbDir) return (0, import_node_path4.join)(rootDir, config.paths.dbDir);
|
|
263
|
+
if (dbConfig.kind === "prisma") return (0, import_node_path4.dirname)(dbConfig.schemaPath);
|
|
264
|
+
if (dbConfig.kind === "sql-migrations") return dbConfig.migrationsDir;
|
|
180
265
|
return null;
|
|
181
266
|
}
|
|
182
|
-
|
|
267
|
+
function dirHasTSFiles(dir) {
|
|
268
|
+
if (!(0, import_node_fs4.existsSync)(dir)) return false;
|
|
269
|
+
try {
|
|
270
|
+
const stack = [dir];
|
|
271
|
+
while (stack.length > 0) {
|
|
272
|
+
const cur = stack.pop();
|
|
273
|
+
const entries = (0, import_node_fs4.readdirSync)(cur, { withFileTypes: true });
|
|
274
|
+
for (const e of entries) {
|
|
275
|
+
if (e.isFile() && (e.name.endsWith(".ts") || e.name.endsWith(".tsx"))) return true;
|
|
276
|
+
if (e.isDirectory() && !e.name.startsWith(".") && !DEFAULT_IGNORE_DIRS.has(e.name)) {
|
|
277
|
+
stack.push((0, import_node_path4.join)(cur, e.name));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
} catch {
|
|
282
|
+
}
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
function collectCodeBearingChildren(dir, extraSkip) {
|
|
286
|
+
if (!(0, import_node_fs4.existsSync)(dir)) return [];
|
|
287
|
+
const out = [];
|
|
288
|
+
try {
|
|
289
|
+
for (const entry of (0, import_node_fs4.readdirSync)(dir, { withFileTypes: true })) {
|
|
290
|
+
if (!entry.isDirectory()) continue;
|
|
291
|
+
if (entry.name.startsWith(".")) continue;
|
|
292
|
+
if (NON_SOURCE_DIRS.has(entry.name)) continue;
|
|
293
|
+
if (extraSkip?.has(entry.name)) continue;
|
|
294
|
+
const full = (0, import_node_path4.join)(dir, entry.name);
|
|
295
|
+
if (dirHasTSFiles(full)) out.push(full);
|
|
296
|
+
}
|
|
297
|
+
} catch {
|
|
298
|
+
}
|
|
299
|
+
return out;
|
|
300
|
+
}
|
|
301
|
+
function detectSrcRoots(rootDir, srcDir, appDir, config) {
|
|
302
|
+
if (config.paths?.srcRoots && config.paths.srcRoots.length > 0) {
|
|
303
|
+
const roots2 = /* @__PURE__ */ new Set();
|
|
304
|
+
roots2.add(appDir);
|
|
305
|
+
for (const r of config.paths.srcRoots) {
|
|
306
|
+
const abs = (0, import_node_path4.isAbsolute)(r) ? r : (0, import_node_path4.resolve)(rootDir, r);
|
|
307
|
+
roots2.add(abs);
|
|
308
|
+
}
|
|
309
|
+
return [...roots2];
|
|
310
|
+
}
|
|
311
|
+
const roots = /* @__PURE__ */ new Set();
|
|
312
|
+
roots.add(appDir);
|
|
313
|
+
for (const c of collectCodeBearingChildren(srcDir)) roots.add(c);
|
|
314
|
+
if (srcDir !== rootDir) {
|
|
315
|
+
const skipSrcWrapper = /* @__PURE__ */ new Set([(0, import_node_path4.basename)(srcDir)]);
|
|
316
|
+
for (const c of collectCodeBearingChildren(rootDir, skipSrcWrapper)) roots.add(c);
|
|
317
|
+
}
|
|
318
|
+
return [...roots];
|
|
319
|
+
}
|
|
320
|
+
function detectConventionFiles(rootDir, srcDir) {
|
|
321
|
+
const out = [];
|
|
322
|
+
const seen = /* @__PURE__ */ new Set();
|
|
323
|
+
const dirs = srcDir === rootDir ? [rootDir] : [srcDir, rootDir];
|
|
324
|
+
for (const dir of dirs) {
|
|
325
|
+
for (const name of CONVENTION_NAMES) {
|
|
326
|
+
const full = (0, import_node_path4.join)(dir, name);
|
|
327
|
+
if (!seen.has(full) && (0, import_node_fs4.existsSync)(full)) {
|
|
328
|
+
try {
|
|
329
|
+
if ((0, import_node_fs4.statSync)(full).isFile()) {
|
|
330
|
+
seen.add(full);
|
|
331
|
+
out.push(full);
|
|
332
|
+
}
|
|
333
|
+
} catch {
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return out;
|
|
339
|
+
}
|
|
340
|
+
function resolveProjectPaths(rootDir, config) {
|
|
341
|
+
let srcDir;
|
|
342
|
+
let appDir;
|
|
343
|
+
if (config.paths?.appDir) {
|
|
344
|
+
appDir = (0, import_node_path4.join)(rootDir, config.paths.appDir);
|
|
345
|
+
srcDir = config.paths.srcDir ? (0, import_node_path4.join)(rootDir, config.paths.srcDir) : (0, import_node_path4.dirname)(appDir);
|
|
346
|
+
} else {
|
|
347
|
+
const srcApp = (0, import_node_path4.join)(rootDir, "src", "app");
|
|
348
|
+
const rootApp = (0, import_node_path4.join)(rootDir, "app");
|
|
349
|
+
if ((0, import_node_fs4.existsSync)(srcApp)) {
|
|
350
|
+
srcDir = (0, import_node_path4.join)(rootDir, "src");
|
|
351
|
+
appDir = srcApp;
|
|
352
|
+
} else if ((0, import_node_fs4.existsSync)(rootApp)) {
|
|
353
|
+
srcDir = rootDir;
|
|
354
|
+
appDir = rootApp;
|
|
355
|
+
} else {
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const apiDir = (0, import_node_path4.join)(appDir, "api");
|
|
360
|
+
const dbConfig = detectDbConfig(rootDir, config);
|
|
361
|
+
const dbDir = detectDbDir(rootDir, config, dbConfig);
|
|
362
|
+
const srcRoots = detectSrcRoots(rootDir, srcDir, appDir, config);
|
|
363
|
+
const conventionFiles = detectConventionFiles(rootDir, srcDir);
|
|
364
|
+
return { srcDir, appDir, apiDir, dbDir, srcRoots, conventionFiles, dbConfig };
|
|
365
|
+
}
|
|
366
|
+
var import_node_fs4, import_node_path4, NON_SOURCE_DIRS, CONVENTION_NAMES;
|
|
183
367
|
var init_resolve_paths = __esm({
|
|
184
368
|
"src/server/graph/core/resolve-paths.ts"() {
|
|
185
369
|
"use strict";
|
|
186
|
-
|
|
187
|
-
|
|
370
|
+
import_node_fs4 = require("node:fs");
|
|
371
|
+
import_node_path4 = require("node:path");
|
|
372
|
+
init_walk();
|
|
373
|
+
NON_SOURCE_DIRS = /* @__PURE__ */ new Set([
|
|
374
|
+
...DEFAULT_IGNORE_DIRS,
|
|
375
|
+
// DB conventions (handled by db parsers)
|
|
376
|
+
"prisma",
|
|
377
|
+
"supabase",
|
|
378
|
+
"drizzle",
|
|
379
|
+
"migrations",
|
|
380
|
+
// Web assets
|
|
381
|
+
"public",
|
|
382
|
+
"static",
|
|
383
|
+
"assets",
|
|
384
|
+
// Docs
|
|
385
|
+
"docs",
|
|
386
|
+
"documentation",
|
|
387
|
+
// Test dirs (project tests aren't part of the structural graph)
|
|
388
|
+
"tests",
|
|
389
|
+
"__tests__",
|
|
390
|
+
"e2e",
|
|
391
|
+
"playwright",
|
|
392
|
+
"cypress",
|
|
393
|
+
// Monorepo workspace roots — separate graph projects per .launchchart.json
|
|
394
|
+
"packages",
|
|
395
|
+
"apps",
|
|
396
|
+
"services",
|
|
397
|
+
"libs"
|
|
398
|
+
]);
|
|
399
|
+
CONVENTION_NAMES = ["middleware.ts", "middleware.tsx", "instrumentation.ts", "instrumentation.tsx"];
|
|
188
400
|
}
|
|
189
401
|
});
|
|
190
402
|
|
|
@@ -224,15 +436,15 @@ function getQuery(name) {
|
|
|
224
436
|
ensureInit();
|
|
225
437
|
const cached = queryCache.get(name);
|
|
226
438
|
if (cached) return cached;
|
|
227
|
-
const scmPath = (0,
|
|
228
|
-
const scm = (0,
|
|
439
|
+
const scmPath = (0, import_node_path5.join)(queriesDir, `${name}.scm`);
|
|
440
|
+
const scm = (0, import_node_fs5.readFileSync)(scmPath, "utf-8");
|
|
229
441
|
const query = tsxLanguage.query(scm);
|
|
230
442
|
queryCache.set(name, query);
|
|
231
443
|
return query;
|
|
232
444
|
}
|
|
233
445
|
function parseSource(absPath) {
|
|
234
446
|
ensureInit();
|
|
235
|
-
const content = (0,
|
|
447
|
+
const content = (0, import_node_fs5.readFileSync)(absPath, "utf-8");
|
|
236
448
|
return parserInstance.parse(content);
|
|
237
449
|
}
|
|
238
450
|
function parseCodeTS(code) {
|
|
@@ -697,17 +909,17 @@ function extractDeep(absPath) {
|
|
|
697
909
|
}
|
|
698
910
|
return { elements, stateVars, conditions, variables, responses, params };
|
|
699
911
|
}
|
|
700
|
-
var
|
|
912
|
+
var import_node_fs5, import_node_path5, tsxLanguage, parserInstance, initPromise, initialized, queriesDir, queryCache, PRISMA_MUTATION_METHODS_BUILTIN, DB_IDENTIFIERS_FALLBACK, extraDbIdentifiers, extraMutationMethods;
|
|
701
913
|
var init_ts_extractor = __esm({
|
|
702
914
|
"src/server/graph/core/ts-extractor.ts"() {
|
|
703
915
|
"use strict";
|
|
704
|
-
|
|
705
|
-
|
|
916
|
+
import_node_fs5 = require("node:fs");
|
|
917
|
+
import_node_path5 = require("node:path");
|
|
706
918
|
initialized = false;
|
|
707
919
|
queriesDir = (() => {
|
|
708
|
-
const srcPath = (0,
|
|
920
|
+
const srcPath = (0, import_node_path5.join)((0, import_node_path5.dirname)(__filename), "..", "queries");
|
|
709
921
|
if (require("fs").existsSync(srcPath)) return srcPath;
|
|
710
|
-
return (0,
|
|
922
|
+
return (0, import_node_path5.join)((0, import_node_path5.dirname)(__filename), "graph", "queries");
|
|
711
923
|
})();
|
|
712
924
|
queryCache = /* @__PURE__ */ new Map();
|
|
713
925
|
PRISMA_MUTATION_METHODS_BUILTIN = [
|
|
@@ -728,48 +940,26 @@ var init_ts_extractor = __esm({
|
|
|
728
940
|
});
|
|
729
941
|
|
|
730
942
|
// src/server/graph/parsers/ts/typescript-project.ts
|
|
731
|
-
function
|
|
732
|
-
const
|
|
733
|
-
if (
|
|
734
|
-
|
|
735
|
-
const full = (0, import_node_path5.join)(dir, entry.name);
|
|
736
|
-
if (entry.isDirectory()) {
|
|
737
|
-
results.push(...walk(full, exts));
|
|
738
|
-
} else if (exts.includes((0, import_node_path5.extname)(entry.name))) {
|
|
739
|
-
results.push(full);
|
|
740
|
-
}
|
|
943
|
+
function toNodeId(srcDir, rootDir, absPath) {
|
|
944
|
+
const relFromSrc = (0, import_node_path6.relative)(srcDir, absPath).replace(/\\/g, "/");
|
|
945
|
+
if (relFromSrc.startsWith("..")) {
|
|
946
|
+
return (0, import_node_path6.relative)(rootDir, absPath).replace(/\\/g, "/");
|
|
741
947
|
}
|
|
742
|
-
return
|
|
743
|
-
}
|
|
744
|
-
function walkWithIgnore(dir, exts, ignoreDirs) {
|
|
745
|
-
const results = [];
|
|
746
|
-
if (!(0, import_node_fs5.existsSync)(dir)) return results;
|
|
747
|
-
for (const entry of (0, import_node_fs5.readdirSync)(dir, { withFileTypes: true })) {
|
|
748
|
-
if (entry.isDirectory()) {
|
|
749
|
-
if (ignoreDirs.has(entry.name)) continue;
|
|
750
|
-
results.push(...walkWithIgnore((0, import_node_path5.join)(dir, entry.name), exts, ignoreDirs));
|
|
751
|
-
} else if (exts.includes((0, import_node_path5.extname)(entry.name))) {
|
|
752
|
-
results.push((0, import_node_path5.join)(dir, entry.name));
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
return results;
|
|
756
|
-
}
|
|
757
|
-
function toNodeId(srcDir, absPath) {
|
|
758
|
-
return (0, import_node_path5.relative)(srcDir, absPath).replace(/\\/g, "/");
|
|
948
|
+
return relFromSrc;
|
|
759
949
|
}
|
|
760
950
|
function resolveImport(srcDir, specifier) {
|
|
761
951
|
if (!specifier.startsWith("@/")) return null;
|
|
762
952
|
const rel = specifier.slice(2);
|
|
763
|
-
const base = (0,
|
|
764
|
-
for (const c of [base, base + ".ts", base + ".tsx", (0,
|
|
765
|
-
if ((0,
|
|
953
|
+
const base = (0, import_node_path6.join)(srcDir, rel);
|
|
954
|
+
for (const c of [base, base + ".ts", base + ".tsx", (0, import_node_path6.join)(base, "index.ts"), (0, import_node_path6.join)(base, "index.tsx")]) {
|
|
955
|
+
if ((0, import_node_fs6.existsSync)(c) && (0, import_node_fs6.statSync)(c).isFile()) return c;
|
|
766
956
|
}
|
|
767
957
|
return null;
|
|
768
958
|
}
|
|
769
959
|
function resolveRelativeImport(fromFile, specifier) {
|
|
770
|
-
const base = (0,
|
|
771
|
-
for (const c of [base, base + ".ts", base + ".tsx", (0,
|
|
772
|
-
if ((0,
|
|
960
|
+
const base = (0, import_node_path6.join)((0, import_node_path6.dirname)(fromFile), specifier);
|
|
961
|
+
for (const c of [base, base + ".ts", base + ".tsx", (0, import_node_path6.join)(base, "index.ts"), (0, import_node_path6.join)(base, "index.tsx")]) {
|
|
962
|
+
if ((0, import_node_fs6.existsSync)(c) && (0, import_node_fs6.statSync)(c).isFile()) return c;
|
|
773
963
|
}
|
|
774
964
|
return null;
|
|
775
965
|
}
|
|
@@ -790,7 +980,7 @@ function resolveBarrelMap(barrelAbsPath, parsedByPath, memo, visiting) {
|
|
|
790
980
|
const resolved = resolveRelativeImport(barrelAbsPath, re.from);
|
|
791
981
|
if (!resolved) continue;
|
|
792
982
|
if (re.isWildcard) {
|
|
793
|
-
const targetBn = (0,
|
|
983
|
+
const targetBn = (0, import_node_path6.basename)(resolved);
|
|
794
984
|
const targetIsBarrel = targetBn === "index.ts" || targetBn === "index.tsx";
|
|
795
985
|
if (targetIsBarrel) {
|
|
796
986
|
const nested = resolveBarrelMap(resolved, parsedByPath, memo, visiting);
|
|
@@ -817,12 +1007,12 @@ function buildAllBarrelMaps(srcDir, parsedByPath) {
|
|
|
817
1007
|
const barrels = /* @__PURE__ */ new Map();
|
|
818
1008
|
const memo = /* @__PURE__ */ new Map();
|
|
819
1009
|
for (const [absPath, parsed] of parsedByPath) {
|
|
820
|
-
const bn = (0,
|
|
1010
|
+
const bn = (0, import_node_path6.basename)(absPath);
|
|
821
1011
|
if (bn !== "index.ts" && bn !== "index.tsx") continue;
|
|
822
1012
|
if (parsed.reExports.length === 0) continue;
|
|
823
1013
|
const map = resolveBarrelMap(absPath, parsedByPath, memo, /* @__PURE__ */ new Set());
|
|
824
1014
|
if (map.size > 0) {
|
|
825
|
-
const barrelId = (0,
|
|
1015
|
+
const barrelId = (0, import_node_path6.relative)(srcDir, (0, import_node_path6.dirname)(absPath)).replace(/\\/g, "/");
|
|
826
1016
|
barrels.set(barrelId, map);
|
|
827
1017
|
}
|
|
828
1018
|
}
|
|
@@ -843,14 +1033,15 @@ function extractRoute(id) {
|
|
|
843
1033
|
return route || "/";
|
|
844
1034
|
}
|
|
845
1035
|
function nameFromFilename(absPath) {
|
|
846
|
-
return (0,
|
|
1036
|
+
return (0, import_node_path6.basename)(absPath, (0, import_node_path6.extname)(absPath)).replace(/[-_](\w)/g, (_, c) => c.toUpperCase()).replace(/^(\w)/, (_, c) => c.toUpperCase());
|
|
847
1037
|
}
|
|
848
|
-
function
|
|
849
|
-
let route = "/" + (0,
|
|
1038
|
+
function filePathToAppRoute(appDir, absPath) {
|
|
1039
|
+
let route = ("/" + (0, import_node_path6.relative)(appDir, absPath).replace(/\\/g, "/")).replace(/\/route\.tsx?$/, "");
|
|
1040
|
+
route = route.replace(/\/\([^)]+\)/g, "");
|
|
1041
|
+
route = route.replace(/\[\.\.\.([^\]]+)\]/g, "*$1");
|
|
850
1042
|
route = route.replace(/\[([^\]]+)\]/g, ":$1");
|
|
851
1043
|
route = route.replace(/\/+/g, "/");
|
|
852
|
-
|
|
853
|
-
return "/api" + route;
|
|
1044
|
+
return route === "" ? "/" : route;
|
|
854
1045
|
}
|
|
855
1046
|
function camelToPascal(s) {
|
|
856
1047
|
if (!s) return s;
|
|
@@ -935,7 +1126,7 @@ function matchRouteToPage(route, routeToNodeId) {
|
|
|
935
1126
|
if (routeToNodeId.has(normalized)) return routeToNodeId.get(normalized);
|
|
936
1127
|
return null;
|
|
937
1128
|
}
|
|
938
|
-
function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps, routeToNodeId) {
|
|
1129
|
+
function extractEdges(srcDir, rootDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps, routeToNodeId) {
|
|
939
1130
|
const edges = [];
|
|
940
1131
|
const flagged = [];
|
|
941
1132
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -963,7 +1154,7 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
|
|
|
963
1154
|
for (const name of names) {
|
|
964
1155
|
const targetAbs = barrelMap.get(name);
|
|
965
1156
|
if (targetAbs) {
|
|
966
|
-
const targetId = toNodeId(srcDir, targetAbs);
|
|
1157
|
+
const targetId = toNodeId(srcDir, rootDir, targetAbs);
|
|
967
1158
|
if (nodeIdSet.has(targetId)) {
|
|
968
1159
|
if (!byTarget.has(targetId)) byTarget.set(targetId, []);
|
|
969
1160
|
byTarget.get(targetId).push(name);
|
|
@@ -977,7 +1168,7 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
|
|
|
977
1168
|
} else {
|
|
978
1169
|
const resolved = resolveImport(srcDir, specifier);
|
|
979
1170
|
if (resolved) {
|
|
980
|
-
const targetId = toNodeId(srcDir, resolved);
|
|
1171
|
+
const targetId = toNodeId(srcDir, rootDir, resolved);
|
|
981
1172
|
if (nodeIdSet.has(targetId) && !targetId.endsWith("/index.ts") && !targetId.endsWith("/index.tsx")) {
|
|
982
1173
|
addEdge(targetId, edgeTypeFor(isTypeOnly, names));
|
|
983
1174
|
}
|
|
@@ -986,7 +1177,7 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
|
|
|
986
1177
|
} else if (specifier.startsWith(".")) {
|
|
987
1178
|
const resolved = resolveRelativeImport(absPath, specifier);
|
|
988
1179
|
if (resolved) {
|
|
989
|
-
const targetId = toNodeId(srcDir, resolved);
|
|
1180
|
+
const targetId = toNodeId(srcDir, rootDir, resolved);
|
|
990
1181
|
if (nodeIdSet.has(targetId) && !targetId.endsWith("/index.ts") && !targetId.endsWith("/index.tsx")) {
|
|
991
1182
|
addEdge(targetId, edgeTypeFor(isTypeOnly, names));
|
|
992
1183
|
}
|
|
@@ -1030,24 +1221,30 @@ function extractEdges(srcDir, absPath, sourceId, parsed, nodeIdSet, barrelMaps,
|
|
|
1030
1221
|
}
|
|
1031
1222
|
return { edges, flagged };
|
|
1032
1223
|
}
|
|
1033
|
-
function hasNextConfig(rootDir) {
|
|
1034
|
-
return (0, import_node_fs5.existsSync)((0, import_node_path5.join)(rootDir, "next.config.ts")) || (0, import_node_fs5.existsSync)((0, import_node_path5.join)(rootDir, "next.config.js")) || (0, import_node_fs5.existsSync)((0, import_node_path5.join)(rootDir, "next.config.mjs"));
|
|
1035
|
-
}
|
|
1036
1224
|
function detect(rootDir) {
|
|
1037
1225
|
const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
|
|
1038
|
-
return paths !== null
|
|
1226
|
+
return paths !== null;
|
|
1039
1227
|
}
|
|
1040
1228
|
function generate(rootDir) {
|
|
1041
1229
|
const config = loadConfig(rootDir);
|
|
1042
1230
|
const paths = resolveProjectPaths(rootDir, config);
|
|
1043
1231
|
const srcDir = paths.srcDir;
|
|
1044
|
-
const
|
|
1045
|
-
const
|
|
1046
|
-
const
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1232
|
+
const allDiscovered = [];
|
|
1233
|
+
const discoveredSet = /* @__PURE__ */ new Set();
|
|
1234
|
+
for (const root of paths.srcRoots) {
|
|
1235
|
+
for (const f of walk(root, [".tsx", ".ts"])) {
|
|
1236
|
+
if (!discoveredSet.has(f)) {
|
|
1237
|
+
discoveredSet.add(f);
|
|
1238
|
+
allDiscovered.push(f);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
for (const conv of paths.conventionFiles) {
|
|
1243
|
+
if (!discoveredSet.has(conv)) {
|
|
1244
|
+
discoveredSet.add(conv);
|
|
1245
|
+
allDiscovered.push(conv);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1051
1248
|
const parsedByPath = /* @__PURE__ */ new Map();
|
|
1052
1249
|
for (const absPath of allDiscovered) {
|
|
1053
1250
|
parsedByPath.set(absPath, parseFileTS(absPath));
|
|
@@ -1057,9 +1254,9 @@ function generate(rootDir) {
|
|
|
1057
1254
|
const apiNodes = [];
|
|
1058
1255
|
const nodeIdSet = /* @__PURE__ */ new Set();
|
|
1059
1256
|
const routeToNodeId = /* @__PURE__ */ new Map();
|
|
1060
|
-
const fileSet = allDiscovered.filter((f) => !(0,
|
|
1257
|
+
const fileSet = allDiscovered.filter((f) => !(0, import_node_path6.basename)(f).startsWith("index."));
|
|
1061
1258
|
for (const absPath of fileSet) {
|
|
1062
|
-
const id = toNodeId(srcDir, absPath);
|
|
1259
|
+
const id = toNodeId(srcDir, rootDir, absPath);
|
|
1063
1260
|
const type = classifyType(absPath, id);
|
|
1064
1261
|
if (type === "test" || type === "story") continue;
|
|
1065
1262
|
const parsed = parsedByPath.get(absPath);
|
|
@@ -1074,7 +1271,7 @@ function generate(rootDir) {
|
|
|
1074
1271
|
const dbCalls = extractDbCallsTS(absPath);
|
|
1075
1272
|
const authWrappers = extractAuthWrappersTS(absPath);
|
|
1076
1273
|
const deep = extractDeep(absPath);
|
|
1077
|
-
const routePath = (
|
|
1274
|
+
const routePath = filePathToAppRoute(paths.appDir, absPath);
|
|
1078
1275
|
const mutations = dbCalls.filter((c) => c.isMutation);
|
|
1079
1276
|
const mutates = mutations.length > 0;
|
|
1080
1277
|
const authStrategy = [...authWrappers];
|
|
@@ -1118,11 +1315,12 @@ function generate(rootDir) {
|
|
|
1118
1315
|
const uiEdges = [];
|
|
1119
1316
|
const uiFlagged = [];
|
|
1120
1317
|
for (const absPath of fileSet) {
|
|
1121
|
-
const id = toNodeId(srcDir, absPath);
|
|
1318
|
+
const id = toNodeId(srcDir, rootDir, absPath);
|
|
1122
1319
|
if (!nodeIdSet.has(id)) continue;
|
|
1123
1320
|
const parsed = parsedByPath.get(absPath);
|
|
1124
1321
|
const { edges, flagged } = extractEdges(
|
|
1125
1322
|
srcDir,
|
|
1323
|
+
rootDir,
|
|
1126
1324
|
absPath,
|
|
1127
1325
|
id,
|
|
1128
1326
|
parsed,
|
|
@@ -1135,7 +1333,7 @@ function generate(rootDir) {
|
|
|
1135
1333
|
}
|
|
1136
1334
|
const fetchCallEntries = [];
|
|
1137
1335
|
for (const absPath of fileSet) {
|
|
1138
|
-
const sourceId = toNodeId(srcDir, absPath);
|
|
1336
|
+
const sourceId = toNodeId(srcDir, rootDir, absPath);
|
|
1139
1337
|
if (!nodeIdSet.has(sourceId)) continue;
|
|
1140
1338
|
const parsed = parsedByPath.get(absPath);
|
|
1141
1339
|
if (parsed.fetchCalls.length === 0) continue;
|
|
@@ -1151,20 +1349,7 @@ function generate(rootDir) {
|
|
|
1151
1349
|
});
|
|
1152
1350
|
}
|
|
1153
1351
|
const externalScanned = new Set(allDiscovered.map((f) => f.replace(/\\/g, "/")));
|
|
1154
|
-
const
|
|
1155
|
-
"node_modules",
|
|
1156
|
-
".next",
|
|
1157
|
-
"dist",
|
|
1158
|
-
".launchsecure",
|
|
1159
|
-
".git",
|
|
1160
|
-
"src",
|
|
1161
|
-
"coverage",
|
|
1162
|
-
".turbo",
|
|
1163
|
-
"build",
|
|
1164
|
-
"out",
|
|
1165
|
-
".vercel"
|
|
1166
|
-
]);
|
|
1167
|
-
const externalCandidates = walkWithIgnore(rootDir, [".ts", ".tsx"], IGNORE_DIRS2);
|
|
1352
|
+
const externalCandidates = walkWithIgnore(rootDir, [".ts", ".tsx"], { extraIgnore: /* @__PURE__ */ new Set(["src"]) });
|
|
1168
1353
|
for (const absPath of externalCandidates) {
|
|
1169
1354
|
const normalized = absPath.replace(/\\/g, "/");
|
|
1170
1355
|
if (externalScanned.has(normalized)) continue;
|
|
@@ -1174,7 +1359,7 @@ function generate(rootDir) {
|
|
|
1174
1359
|
} catch {
|
|
1175
1360
|
continue;
|
|
1176
1361
|
}
|
|
1177
|
-
const externalId = (0,
|
|
1362
|
+
const externalId = (0, import_node_path6.relative)(rootDir, absPath).replace(/\\/g, "/");
|
|
1178
1363
|
const edgesFromThis = [];
|
|
1179
1364
|
const seen = /* @__PURE__ */ new Set();
|
|
1180
1365
|
for (const imp of parsed.imports) {
|
|
@@ -1187,7 +1372,7 @@ function generate(rootDir) {
|
|
|
1187
1372
|
for (const name of names) {
|
|
1188
1373
|
const targetAbs = barrelMap.get(name);
|
|
1189
1374
|
if (!targetAbs) continue;
|
|
1190
|
-
const targetId2 = toNodeId(srcDir, targetAbs);
|
|
1375
|
+
const targetId2 = toNodeId(srcDir, rootDir, targetAbs);
|
|
1191
1376
|
if (!nodeIdSet.has(targetId2)) continue;
|
|
1192
1377
|
const key2 = `${externalId}\u2192${targetId2}`;
|
|
1193
1378
|
if (seen.has(key2)) continue;
|
|
@@ -1201,7 +1386,7 @@ function generate(rootDir) {
|
|
|
1201
1386
|
resolved = resolveRelativeImport(absPath, specifier);
|
|
1202
1387
|
}
|
|
1203
1388
|
if (!resolved) continue;
|
|
1204
|
-
const targetId = toNodeId(srcDir, resolved);
|
|
1389
|
+
const targetId = toNodeId(srcDir, rootDir, resolved);
|
|
1205
1390
|
if (!nodeIdSet.has(targetId)) continue;
|
|
1206
1391
|
if (targetId.endsWith("/index.ts") || targetId.endsWith("/index.tsx")) continue;
|
|
1207
1392
|
const key = `${externalId}\u2192${targetId}`;
|
|
@@ -1370,14 +1555,15 @@ function generate(rootDir) {
|
|
|
1370
1555
|
}
|
|
1371
1556
|
return result;
|
|
1372
1557
|
}
|
|
1373
|
-
var
|
|
1558
|
+
var import_node_fs6, import_node_path6, HTTP_METHODS, CLASSIFICATION_TO_LAYER, typescriptProjectParser;
|
|
1374
1559
|
var init_typescript_project = __esm({
|
|
1375
1560
|
"src/server/graph/parsers/ts/typescript-project.ts"() {
|
|
1376
1561
|
"use strict";
|
|
1377
|
-
|
|
1378
|
-
|
|
1562
|
+
import_node_fs6 = require("node:fs");
|
|
1563
|
+
import_node_path6 = require("node:path");
|
|
1379
1564
|
init_config();
|
|
1380
1565
|
init_resolve_paths();
|
|
1566
|
+
init_walk();
|
|
1381
1567
|
init_ts_extractor();
|
|
1382
1568
|
HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]);
|
|
1383
1569
|
CLASSIFICATION_TO_LAYER = {
|
|
@@ -1493,11 +1679,25 @@ function parseEnums(content) {
|
|
|
1493
1679
|
return nodes;
|
|
1494
1680
|
}
|
|
1495
1681
|
function detect2(rootDir) {
|
|
1496
|
-
|
|
1682
|
+
const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
|
|
1683
|
+
return paths?.dbConfig.kind === "prisma" && (0, import_node_fs7.existsSync)(paths.dbConfig.schemaPath);
|
|
1497
1684
|
}
|
|
1498
1685
|
function generate2(rootDir) {
|
|
1499
|
-
const
|
|
1500
|
-
|
|
1686
|
+
const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
|
|
1687
|
+
if (paths.dbConfig.kind !== "prisma") {
|
|
1688
|
+
return {
|
|
1689
|
+
metadata: { generated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), layer: "db", source: "none" },
|
|
1690
|
+
nodes: [],
|
|
1691
|
+
edges: [],
|
|
1692
|
+
cross_refs: [],
|
|
1693
|
+
contradictions: [],
|
|
1694
|
+
warnings: [],
|
|
1695
|
+
flagged_edges: [],
|
|
1696
|
+
patterns: { total_tables: 0, total_enums: 0, total_relations: 0 }
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
const schemaPath = paths.dbConfig.schemaPath;
|
|
1700
|
+
const content = (0, import_node_fs7.readFileSync)(schemaPath, "utf-8");
|
|
1501
1701
|
const { nodes: modelNodes, relations } = parseModels(content);
|
|
1502
1702
|
const enumNodes = parseEnums(content);
|
|
1503
1703
|
const allNodes = [...modelNodes, ...enumNodes];
|
|
@@ -1517,7 +1717,7 @@ function generate2(rootDir) {
|
|
|
1517
1717
|
metadata: {
|
|
1518
1718
|
generated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
1519
1719
|
scope: "prisma-schema",
|
|
1520
|
-
source:
|
|
1720
|
+
source: schemaPath,
|
|
1521
1721
|
provider: "postgresql",
|
|
1522
1722
|
layer: "db",
|
|
1523
1723
|
total_models: modelNodes.length,
|
|
@@ -1546,12 +1746,13 @@ function generate2(rootDir) {
|
|
|
1546
1746
|
}
|
|
1547
1747
|
};
|
|
1548
1748
|
}
|
|
1549
|
-
var
|
|
1749
|
+
var import_node_fs7, prismaSchemaParser;
|
|
1550
1750
|
var init_prisma_schema = __esm({
|
|
1551
1751
|
"src/server/graph/parsers/db/prisma-schema.ts"() {
|
|
1552
1752
|
"use strict";
|
|
1553
|
-
|
|
1554
|
-
|
|
1753
|
+
import_node_fs7 = require("node:fs");
|
|
1754
|
+
init_config();
|
|
1755
|
+
init_resolve_paths();
|
|
1555
1756
|
prismaSchemaParser = {
|
|
1556
1757
|
id: "prisma-schema",
|
|
1557
1758
|
layer: "db",
|
|
@@ -1684,20 +1885,30 @@ function parseUniqueIndex(sql, state) {
|
|
|
1684
1885
|
state.uniqueIndexes.get(m[1]).add(m[2]);
|
|
1685
1886
|
}
|
|
1686
1887
|
}
|
|
1687
|
-
function
|
|
1688
|
-
|
|
1888
|
+
function discoverMigrationFiles(migrationsDir) {
|
|
1889
|
+
if (!(0, import_node_fs8.existsSync)(migrationsDir)) return [];
|
|
1890
|
+
const out = [];
|
|
1891
|
+
const entries = (0, import_node_fs8.readdirSync)(migrationsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1892
|
+
for (const entry of entries) {
|
|
1893
|
+
if (entry.isDirectory()) {
|
|
1894
|
+
const sqlPath = (0, import_node_path7.join)(migrationsDir, entry.name, "migration.sql");
|
|
1895
|
+
if ((0, import_node_fs8.existsSync)(sqlPath)) out.push(sqlPath);
|
|
1896
|
+
} else if (entry.isFile() && entry.name.endsWith(".sql")) {
|
|
1897
|
+
out.push((0, import_node_path7.join)(migrationsDir, entry.name));
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
return out;
|
|
1901
|
+
}
|
|
1902
|
+
function parseMigrations(migrationsDir) {
|
|
1689
1903
|
const state = {
|
|
1690
1904
|
tables: /* @__PURE__ */ new Map(),
|
|
1691
1905
|
enums: /* @__PURE__ */ new Map(),
|
|
1692
1906
|
fks: [],
|
|
1693
1907
|
uniqueIndexes: /* @__PURE__ */ new Map()
|
|
1694
1908
|
};
|
|
1695
|
-
if (!
|
|
1696
|
-
const
|
|
1697
|
-
|
|
1698
|
-
const sqlPath = (0, import_node_path7.join)(migrationsDir, dir, "migration.sql");
|
|
1699
|
-
if (!(0, import_node_fs7.existsSync)(sqlPath)) continue;
|
|
1700
|
-
const sql = (0, import_node_fs7.readFileSync)(sqlPath, "utf-8");
|
|
1909
|
+
if (!migrationsDir) return state;
|
|
1910
|
+
for (const sqlPath of discoverMigrationFiles(migrationsDir)) {
|
|
1911
|
+
const sql = (0, import_node_fs8.readFileSync)(sqlPath, "utf-8");
|
|
1701
1912
|
parseCreateEnum(sql, state);
|
|
1702
1913
|
parseCreateTable(sql, state);
|
|
1703
1914
|
parseAlterTable(sql, state);
|
|
@@ -1707,10 +1918,9 @@ function parseMigrations(rootDir) {
|
|
|
1707
1918
|
}
|
|
1708
1919
|
return state;
|
|
1709
1920
|
}
|
|
1710
|
-
function loadPrismaState(
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
const content = (0, import_node_fs7.readFileSync)(schemaPath, "utf-8");
|
|
1921
|
+
function loadPrismaState(schemaPath) {
|
|
1922
|
+
if (!schemaPath || !(0, import_node_fs8.existsSync)(schemaPath)) return null;
|
|
1923
|
+
const content = (0, import_node_fs8.readFileSync)(schemaPath, "utf-8");
|
|
1714
1924
|
const tables = /* @__PURE__ */ new Map();
|
|
1715
1925
|
const enums = /* @__PURE__ */ new Map();
|
|
1716
1926
|
const relations = [];
|
|
@@ -1874,14 +2084,28 @@ function verify(sqlState, prisma) {
|
|
|
1874
2084
|
}
|
|
1875
2085
|
return { contradictions, flaggedEdges };
|
|
1876
2086
|
}
|
|
2087
|
+
function migrationsDirFor(rootDir) {
|
|
2088
|
+
const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
|
|
2089
|
+
if (!paths) return null;
|
|
2090
|
+
if (paths.dbConfig.kind === "prisma" || paths.dbConfig.kind === "sql-migrations") {
|
|
2091
|
+
return paths.dbConfig.migrationsDir;
|
|
2092
|
+
}
|
|
2093
|
+
return null;
|
|
2094
|
+
}
|
|
2095
|
+
function schemaPathFor(rootDir) {
|
|
2096
|
+
const paths = resolveProjectPaths(rootDir, loadConfig(rootDir));
|
|
2097
|
+
if (!paths) return null;
|
|
2098
|
+
return paths.dbConfig.kind === "prisma" ? paths.dbConfig.schemaPath : null;
|
|
2099
|
+
}
|
|
1877
2100
|
function detect3(rootDir) {
|
|
1878
|
-
const
|
|
1879
|
-
if (!
|
|
1880
|
-
return (
|
|
2101
|
+
const dir = migrationsDirFor(rootDir);
|
|
2102
|
+
if (!dir) return false;
|
|
2103
|
+
return discoverMigrationFiles(dir).length > 0;
|
|
1881
2104
|
}
|
|
1882
2105
|
function generate3(rootDir) {
|
|
1883
|
-
const
|
|
1884
|
-
const
|
|
2106
|
+
const migrationsDir = migrationsDirFor(rootDir);
|
|
2107
|
+
const sqlState = parseMigrations(migrationsDir);
|
|
2108
|
+
const prisma = loadPrismaState(schemaPathFor(rootDir));
|
|
1885
2109
|
const prismaTableIds = prisma ? new Set(prisma.tables.keys()) : /* @__PURE__ */ new Set();
|
|
1886
2110
|
const prismaEnumIds = prisma ? new Set(prisma.enums.keys()) : /* @__PURE__ */ new Set();
|
|
1887
2111
|
const nodes = [];
|
|
@@ -1927,7 +2151,7 @@ function generate3(rootDir) {
|
|
|
1927
2151
|
metadata: {
|
|
1928
2152
|
generated: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
1929
2153
|
scope: "sql-migrations",
|
|
1930
|
-
source: "
|
|
2154
|
+
source: migrationsDir ?? "none",
|
|
1931
2155
|
layer: "db",
|
|
1932
2156
|
sql_tables: sqlState.tables.size,
|
|
1933
2157
|
sql_enums: sqlState.enums.size,
|
|
@@ -1944,12 +2168,14 @@ function generate3(rootDir) {
|
|
|
1944
2168
|
flagged_edges: flaggedEdges
|
|
1945
2169
|
};
|
|
1946
2170
|
}
|
|
1947
|
-
var
|
|
2171
|
+
var import_node_fs8, import_node_path7, PG_TO_PRISMA, sqlMigrationsParser;
|
|
1948
2172
|
var init_sql_migrations = __esm({
|
|
1949
2173
|
"src/server/graph/parsers/db/sql-migrations.ts"() {
|
|
1950
2174
|
"use strict";
|
|
1951
|
-
|
|
2175
|
+
import_node_fs8 = require("node:fs");
|
|
1952
2176
|
import_node_path7 = require("node:path");
|
|
2177
|
+
init_config();
|
|
2178
|
+
init_resolve_paths();
|
|
1953
2179
|
PG_TO_PRISMA = {
|
|
1954
2180
|
"TEXT": "String",
|
|
1955
2181
|
"VARCHAR": "String",
|
|
@@ -2212,9 +2438,9 @@ var init_fetch_resolver = __esm({
|
|
|
2212
2438
|
|
|
2213
2439
|
// src/server/graph/parsers/crosslayer/api-annotations.ts
|
|
2214
2440
|
function walk2(dir, exts) {
|
|
2215
|
-
if (!(0,
|
|
2441
|
+
if (!(0, import_node_fs9.existsSync)(dir)) return [];
|
|
2216
2442
|
const results = [];
|
|
2217
|
-
for (const entry of (0,
|
|
2443
|
+
for (const entry of (0, import_node_fs9.readdirSync)(dir, { withFileTypes: true })) {
|
|
2218
2444
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
2219
2445
|
const full = (0, import_node_path8.join)(dir, entry.name);
|
|
2220
2446
|
if (entry.isDirectory()) {
|
|
@@ -2228,11 +2454,11 @@ function walk2(dir, exts) {
|
|
|
2228
2454
|
function toNodeId2(srcDir, absPath) {
|
|
2229
2455
|
return (0, import_node_path8.relative)(srcDir, absPath).replace(/\\/g, "/");
|
|
2230
2456
|
}
|
|
2231
|
-
var
|
|
2457
|
+
var import_node_fs9, import_node_path8, API_ANNOTATION_RE, apiAnnotationsParser;
|
|
2232
2458
|
var init_api_annotations = __esm({
|
|
2233
2459
|
"src/server/graph/parsers/crosslayer/api-annotations.ts"() {
|
|
2234
2460
|
"use strict";
|
|
2235
|
-
|
|
2461
|
+
import_node_fs9 = require("node:fs");
|
|
2236
2462
|
import_node_path8 = require("node:path");
|
|
2237
2463
|
init_api_route_matching();
|
|
2238
2464
|
API_ANNOTATION_RE = /@api\s+(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+(\/\S+)/g;
|
|
@@ -2241,7 +2467,7 @@ var init_api_annotations = __esm({
|
|
|
2241
2467
|
layer: "crosslayer",
|
|
2242
2468
|
concern: "api-binding",
|
|
2243
2469
|
detect(rootDir) {
|
|
2244
|
-
return (0,
|
|
2470
|
+
return (0, import_node_fs9.existsSync)((0, import_node_path8.join)(rootDir, "src"));
|
|
2245
2471
|
},
|
|
2246
2472
|
generate(rootDir, layerOutputs) {
|
|
2247
2473
|
const apiOutput = layerOutputs.get("api");
|
|
@@ -2258,7 +2484,7 @@ var init_api_annotations = __esm({
|
|
|
2258
2484
|
const flaggedEdges = [];
|
|
2259
2485
|
const seen = /* @__PURE__ */ new Set();
|
|
2260
2486
|
for (const absPath of files) {
|
|
2261
|
-
const content = (0,
|
|
2487
|
+
const content = (0, import_node_fs9.readFileSync)(absPath, "utf-8");
|
|
2262
2488
|
const sourceId = toNodeId2(srcDir, absPath);
|
|
2263
2489
|
if (!uiNodeIds.has(sourceId)) continue;
|
|
2264
2490
|
let match;
|
|
@@ -2305,9 +2531,9 @@ var init_api_annotations = __esm({
|
|
|
2305
2531
|
|
|
2306
2532
|
// src/server/graph/parsers/crosslayer/url-literal-scanner.ts
|
|
2307
2533
|
function walk3(dir, exts) {
|
|
2308
|
-
if (!(0,
|
|
2534
|
+
if (!(0, import_node_fs10.existsSync)(dir)) return [];
|
|
2309
2535
|
const results = [];
|
|
2310
|
-
for (const entry of (0,
|
|
2536
|
+
for (const entry of (0, import_node_fs10.readdirSync)(dir, { withFileTypes: true })) {
|
|
2311
2537
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
2312
2538
|
const full = (0, import_node_path9.join)(dir, entry.name);
|
|
2313
2539
|
if (entry.isDirectory()) {
|
|
@@ -2321,11 +2547,11 @@ function walk3(dir, exts) {
|
|
|
2321
2547
|
function toNodeId3(srcDir, absPath) {
|
|
2322
2548
|
return (0, import_node_path9.relative)(srcDir, absPath).replace(/\\/g, "/");
|
|
2323
2549
|
}
|
|
2324
|
-
var
|
|
2550
|
+
var import_node_fs10, import_node_path9, URL_LITERAL_RE, urlLiteralScannerParser;
|
|
2325
2551
|
var init_url_literal_scanner = __esm({
|
|
2326
2552
|
"src/server/graph/parsers/crosslayer/url-literal-scanner.ts"() {
|
|
2327
2553
|
"use strict";
|
|
2328
|
-
|
|
2554
|
+
import_node_fs10 = require("node:fs");
|
|
2329
2555
|
import_node_path9 = require("node:path");
|
|
2330
2556
|
init_api_route_matching();
|
|
2331
2557
|
init_config();
|
|
@@ -2360,7 +2586,7 @@ var init_url_literal_scanner = __esm({
|
|
|
2360
2586
|
for (const absPath of files) {
|
|
2361
2587
|
const sourceId = toNodeId3(srcDir, absPath);
|
|
2362
2588
|
if (!uiNodeIds.has(sourceId)) continue;
|
|
2363
|
-
const content = (0,
|
|
2589
|
+
const content = (0, import_node_fs10.readFileSync)(absPath, "utf-8");
|
|
2364
2590
|
let match;
|
|
2365
2591
|
URL_LITERAL_RE.lastIndex = 0;
|
|
2366
2592
|
while ((match = URL_LITERAL_RE.exec(content)) !== null) {
|
|
@@ -2427,14 +2653,14 @@ function extractEnumValues(rootDir) {
|
|
|
2427
2653
|
];
|
|
2428
2654
|
let content = "";
|
|
2429
2655
|
for (const p of schemaPaths) {
|
|
2430
|
-
if ((0,
|
|
2656
|
+
if ((0, import_node_fs11.existsSync)(p)) {
|
|
2431
2657
|
try {
|
|
2432
|
-
const stat = (0,
|
|
2658
|
+
const stat = (0, import_node_fs11.statSync)(p);
|
|
2433
2659
|
if (stat.isFile()) {
|
|
2434
|
-
content = (0,
|
|
2660
|
+
content = (0, import_node_fs11.readFileSync)(p, "utf-8");
|
|
2435
2661
|
} else if (stat.isDirectory()) {
|
|
2436
|
-
const files = (0,
|
|
2437
|
-
content = files.map((f) => (0,
|
|
2662
|
+
const files = (0, import_node_fs11.readdirSync)(p).filter((f) => f.endsWith(".prisma"));
|
|
2663
|
+
content = files.map((f) => (0, import_node_fs11.readFileSync)((0, import_node_path10.join)(p, f), "utf-8")).join("\n");
|
|
2438
2664
|
}
|
|
2439
2665
|
} catch {
|
|
2440
2666
|
continue;
|
|
@@ -2508,7 +2734,7 @@ function extractStringArrayFromNode(node) {
|
|
|
2508
2734
|
return values;
|
|
2509
2735
|
}
|
|
2510
2736
|
function findArrayDecl(root, varName) {
|
|
2511
|
-
function
|
|
2737
|
+
function walk4(node) {
|
|
2512
2738
|
if (node.type === "variable_declarator") {
|
|
2513
2739
|
const nameNode = node.childForFieldName("name");
|
|
2514
2740
|
const valueNode = node.childForFieldName("value");
|
|
@@ -2521,12 +2747,12 @@ function findArrayDecl(root, varName) {
|
|
|
2521
2747
|
}
|
|
2522
2748
|
}
|
|
2523
2749
|
for (const child of node.namedChildren) {
|
|
2524
|
-
const found =
|
|
2750
|
+
const found = walk4(child);
|
|
2525
2751
|
if (found) return found;
|
|
2526
2752
|
}
|
|
2527
2753
|
return null;
|
|
2528
2754
|
}
|
|
2529
|
-
return
|
|
2755
|
+
return walk4(root);
|
|
2530
2756
|
}
|
|
2531
2757
|
function extractObjectPropsRegex(objStr) {
|
|
2532
2758
|
const props = {};
|
|
@@ -2593,10 +2819,10 @@ function extractSeedData(rootDir) {
|
|
|
2593
2819
|
(0, import_node_path10.join)(rootDir, "prisma", "seed.ts"),
|
|
2594
2820
|
(0, import_node_path10.join)(rootDir, "prisma", "seed.js"),
|
|
2595
2821
|
(0, import_node_path10.join)(rootDir, "src", "server", "lib", "system-tags.ts")
|
|
2596
|
-
].filter(
|
|
2822
|
+
].filter(import_node_fs11.existsSync);
|
|
2597
2823
|
const useTreeSitter = tryLoadTreeSitter();
|
|
2598
2824
|
for (const filePath of seedFiles) {
|
|
2599
|
-
const content = (0,
|
|
2825
|
+
const content = (0, import_node_fs11.readFileSync)(filePath, "utf-8");
|
|
2600
2826
|
const relPath = (0, import_node_path10.relative)(rootDir, filePath);
|
|
2601
2827
|
const seeded = detectSeededArrays(content, relPath);
|
|
2602
2828
|
let astRoot = null;
|
|
@@ -2691,9 +2917,9 @@ function extractSeedData(rootDir) {
|
|
|
2691
2917
|
return { nodes, edges };
|
|
2692
2918
|
}
|
|
2693
2919
|
function walkDir(dir, exts) {
|
|
2694
|
-
if (!(0,
|
|
2920
|
+
if (!(0, import_node_fs11.existsSync)(dir)) return [];
|
|
2695
2921
|
const results = [];
|
|
2696
|
-
for (const entry of (0,
|
|
2922
|
+
for (const entry of (0, import_node_fs11.readdirSync)(dir, { withFileTypes: true })) {
|
|
2697
2923
|
if (entry.name === "node_modules" || entry.name === ".next" || entry.name === "dist") continue;
|
|
2698
2924
|
const full = (0, import_node_path10.join)(dir, entry.name);
|
|
2699
2925
|
if (entry.isDirectory()) results.push(...walkDir(full, exts));
|
|
@@ -2704,9 +2930,9 @@ function walkDir(dir, exts) {
|
|
|
2704
2930
|
function extractConstants(rootDir) {
|
|
2705
2931
|
const nodes = [];
|
|
2706
2932
|
const srcDir = (0, import_node_path10.join)(rootDir, "src");
|
|
2707
|
-
if (!(0,
|
|
2933
|
+
if (!(0, import_node_fs11.existsSync)(srcDir)) return { nodes };
|
|
2708
2934
|
for (const filePath of walkDir(srcDir, [".ts", ".tsx"])) {
|
|
2709
|
-
const content = (0,
|
|
2935
|
+
const content = (0, import_node_fs11.readFileSync)(filePath, "utf-8");
|
|
2710
2936
|
const relPath = (0, import_node_path10.relative)(rootDir, filePath);
|
|
2711
2937
|
const constArrayRe = /export\s+const\s+([A-Z][A-Z_0-9]+)\s*(?::[^=]+)?\s*=\s*\[/g;
|
|
2712
2938
|
let cm;
|
|
@@ -2740,7 +2966,7 @@ function extractConstants(rootDir) {
|
|
|
2740
2966
|
return { nodes };
|
|
2741
2967
|
}
|
|
2742
2968
|
function detect4(rootDir) {
|
|
2743
|
-
return (0,
|
|
2969
|
+
return (0, import_node_fs11.existsSync)((0, import_node_path10.join)(rootDir, "prisma", "schema.prisma")) || (0, import_node_fs11.existsSync)((0, import_node_path10.join)(rootDir, "prisma", "seed.ts"));
|
|
2744
2970
|
}
|
|
2745
2971
|
function generate4(rootDir) {
|
|
2746
2972
|
const enumResult = extractEnumValues(rootDir);
|
|
@@ -2807,11 +3033,11 @@ function generate4(rootDir) {
|
|
|
2807
3033
|
}
|
|
2808
3034
|
};
|
|
2809
3035
|
}
|
|
2810
|
-
var
|
|
3036
|
+
var import_node_fs11, import_node_path10, parseCode, SHARED_MODELS, DB_MODELS, staticValuesParser;
|
|
2811
3037
|
var init_static_values = __esm({
|
|
2812
3038
|
"src/server/graph/parsers/static/static-values.ts"() {
|
|
2813
3039
|
"use strict";
|
|
2814
|
-
|
|
3040
|
+
import_node_fs11 = require("node:fs");
|
|
2815
3041
|
import_node_path10 = require("node:path");
|
|
2816
3042
|
parseCode = null;
|
|
2817
3043
|
SHARED_MODELS = /* @__PURE__ */ new Set(["permission", "role", "tag"]);
|
|
@@ -2826,23 +3052,6 @@ var init_static_values = __esm({
|
|
|
2826
3052
|
});
|
|
2827
3053
|
|
|
2828
3054
|
// src/server/graph/parsers/crosslayer/static-ref-scanner.ts
|
|
2829
|
-
function walk4(dir, exts) {
|
|
2830
|
-
if (!(0, import_node_fs11.existsSync)(dir)) return [];
|
|
2831
|
-
const results = [];
|
|
2832
|
-
function recurse(d) {
|
|
2833
|
-
for (const entry of (0, import_node_fs11.readdirSync)(d, { withFileTypes: true })) {
|
|
2834
|
-
const full = (0, import_node_path11.join)(d, entry.name);
|
|
2835
|
-
if (entry.isDirectory()) {
|
|
2836
|
-
if (entry.name === "node_modules" || entry.name === ".next" || entry.name === "dist") continue;
|
|
2837
|
-
recurse(full);
|
|
2838
|
-
} else if (exts.some((ext) => entry.name.endsWith(ext))) {
|
|
2839
|
-
results.push(full);
|
|
2840
|
-
}
|
|
2841
|
-
}
|
|
2842
|
-
}
|
|
2843
|
-
recurse(dir);
|
|
2844
|
-
return results;
|
|
2845
|
-
}
|
|
2846
3055
|
function isInCommentOrType(node) {
|
|
2847
3056
|
let current = node.parent;
|
|
2848
3057
|
while (current) {
|
|
@@ -2927,14 +3136,15 @@ function collectStaticRefsRegex(content, valueLookup, allValues) {
|
|
|
2927
3136
|
}
|
|
2928
3137
|
return refs;
|
|
2929
3138
|
}
|
|
2930
|
-
var
|
|
3139
|
+
var import_node_fs12, import_node_path11, MIN_VALUE_LENGTH, SKIP_VALUES, staticRefScannerParser;
|
|
2931
3140
|
var init_static_ref_scanner = __esm({
|
|
2932
3141
|
"src/server/graph/parsers/crosslayer/static-ref-scanner.ts"() {
|
|
2933
3142
|
"use strict";
|
|
2934
|
-
|
|
3143
|
+
import_node_fs12 = require("node:fs");
|
|
2935
3144
|
import_node_path11 = require("node:path");
|
|
2936
3145
|
init_config();
|
|
2937
3146
|
init_resolve_paths();
|
|
3147
|
+
init_walk();
|
|
2938
3148
|
MIN_VALUE_LENGTH = 4;
|
|
2939
3149
|
SKIP_VALUES = /* @__PURE__ */ new Set([
|
|
2940
3150
|
"true",
|
|
@@ -2993,11 +3203,11 @@ var init_static_ref_scanner = __esm({
|
|
|
2993
3203
|
if (!paths) return { cross_refs: [], flagged_edges: [], warnings: [] };
|
|
2994
3204
|
const srcDir = paths.srcDir;
|
|
2995
3205
|
const files = [
|
|
2996
|
-
...
|
|
2997
|
-
...
|
|
2998
|
-
...
|
|
2999
|
-
...
|
|
3000
|
-
...
|
|
3206
|
+
...walkWithIgnore((0, import_node_path11.join)(srcDir, "client"), [".ts", ".tsx"]),
|
|
3207
|
+
...walkWithIgnore(paths.appDir, [".ts", ".tsx"]),
|
|
3208
|
+
...walkWithIgnore((0, import_node_path11.join)(srcDir, "server"), [".ts", ".tsx"]),
|
|
3209
|
+
...walkWithIgnore((0, import_node_path11.join)(srcDir, "lib"), [".ts", ".tsx"]),
|
|
3210
|
+
...walkWithIgnore((0, import_node_path11.join)(srcDir, "config"), [".ts", ".tsx"])
|
|
3001
3211
|
];
|
|
3002
3212
|
const uiOutput = layerOutputs.get("ui");
|
|
3003
3213
|
const apiOutput = layerOutputs.get("api");
|
|
@@ -3018,7 +3228,7 @@ var init_static_ref_scanner = __esm({
|
|
|
3018
3228
|
const sourceId = (0, import_node_path11.relative)(srcDir, absPath).replace(/\\/g, "/");
|
|
3019
3229
|
const sourceLayer = uiNodeIds.has(sourceId) ? "ui" : apiNodeIds.has(sourceId) ? "api" : null;
|
|
3020
3230
|
if (!sourceLayer) continue;
|
|
3021
|
-
const content = (0,
|
|
3231
|
+
const content = (0, import_node_fs12.readFileSync)(absPath, "utf-8");
|
|
3022
3232
|
filesScanned++;
|
|
3023
3233
|
let fileRefs;
|
|
3024
3234
|
if (parseCode2) {
|
|
@@ -3287,9 +3497,9 @@ var init_merge = __esm({
|
|
|
3287
3497
|
// src/server/graph/core/graph-builder.ts
|
|
3288
3498
|
function readGraphFromDisk(rootDir, layer) {
|
|
3289
3499
|
const filePath = (0, import_node_path13.join)(rootDir, ".launchsecure", "graphs", `${layer}.json`);
|
|
3290
|
-
if (!(0,
|
|
3500
|
+
if (!(0, import_node_fs13.existsSync)(filePath)) return null;
|
|
3291
3501
|
try {
|
|
3292
|
-
return JSON.parse((0,
|
|
3502
|
+
return JSON.parse((0, import_node_fs13.readFileSync)(filePath, "utf-8"));
|
|
3293
3503
|
} catch {
|
|
3294
3504
|
return null;
|
|
3295
3505
|
}
|
|
@@ -3385,11 +3595,11 @@ function generateAll(rootDir) {
|
|
|
3385
3595
|
const extras = [...byLayer.keys()].filter((l) => !wellKnownOrder.includes(l)).sort();
|
|
3386
3596
|
return [...wellKnownOrder, ...extras].map((l) => byLayer.get(l)).filter((r) => !!r);
|
|
3387
3597
|
}
|
|
3388
|
-
var
|
|
3598
|
+
var import_node_fs13, import_node_path13;
|
|
3389
3599
|
var init_graph_builder = __esm({
|
|
3390
3600
|
"src/server/graph/core/graph-builder.ts"() {
|
|
3391
3601
|
"use strict";
|
|
3392
|
-
|
|
3602
|
+
import_node_fs13 = require("node:fs");
|
|
3393
3603
|
import_node_path13 = require("node:path");
|
|
3394
3604
|
init_config();
|
|
3395
3605
|
init_parser_registry();
|
|
@@ -3436,11 +3646,11 @@ function detectConventionDirs(rootDir, extraConventionDirs = []) {
|
|
|
3436
3646
|
for (const base of searchDirs) {
|
|
3437
3647
|
for (const convention of conventionDirs) {
|
|
3438
3648
|
const dir = (0, import_node_path14.join)(base, convention);
|
|
3439
|
-
if (!(0,
|
|
3649
|
+
if (!(0, import_node_fs14.existsSync)(dir)) continue;
|
|
3440
3650
|
try {
|
|
3441
|
-
const stat = (0,
|
|
3651
|
+
const stat = (0, import_node_fs14.statSync)(dir);
|
|
3442
3652
|
if (!stat.isDirectory()) continue;
|
|
3443
|
-
const entries = (0,
|
|
3653
|
+
const entries = (0, import_node_fs14.readdirSync)(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
|
|
3444
3654
|
if (entries.length > 0) {
|
|
3445
3655
|
const relPath = dir.replace(rootDir + "/", "").replace(rootDir + "\\", "");
|
|
3446
3656
|
result.set(relPath, entries);
|
|
@@ -3512,11 +3722,11 @@ function extractModuleFromPath(id, extraTrivial, extraSkipSegments) {
|
|
|
3512
3722
|
}
|
|
3513
3723
|
return "root";
|
|
3514
3724
|
}
|
|
3515
|
-
var
|
|
3725
|
+
var import_node_fs14, import_node_path14, CONVENTION_DIRS_BUILTIN, GENERIC_ROLE_NAMES_BUILTIN, SKIP_SEGMENTS_BUILTIN, TRIVIAL_GROUPS, cachedRootDir, cachedConventionDirs, moduleTagger;
|
|
3516
3726
|
var init_module_tagger = __esm({
|
|
3517
3727
|
"src/server/graph/taggers/module-tagger.ts"() {
|
|
3518
3728
|
"use strict";
|
|
3519
|
-
|
|
3729
|
+
import_node_fs14 = require("node:fs");
|
|
3520
3730
|
import_node_path14 = require("node:path");
|
|
3521
3731
|
CONVENTION_DIRS_BUILTIN = ["features", "modules", "domains", "areas"];
|
|
3522
3732
|
GENERIC_ROLE_NAMES_BUILTIN = /* @__PURE__ */ new Set([
|
|
@@ -3786,14 +3996,14 @@ function tagsFilePath(rootDir) {
|
|
|
3786
3996
|
}
|
|
3787
3997
|
function readTagStore(rootDir) {
|
|
3788
3998
|
const filePath = tagsFilePath(rootDir);
|
|
3789
|
-
if (!(0,
|
|
3790
|
-
const stat = (0,
|
|
3999
|
+
if (!(0, import_node_fs15.existsSync)(filePath)) return {};
|
|
4000
|
+
const stat = (0, import_node_fs15.statSync)(filePath);
|
|
3791
4001
|
const cached = tagCache.get(filePath);
|
|
3792
4002
|
if (cached && cached.mtimeMs === stat.mtimeMs) {
|
|
3793
4003
|
return cached.store;
|
|
3794
4004
|
}
|
|
3795
4005
|
try {
|
|
3796
|
-
const content = (0,
|
|
4006
|
+
const content = (0, import_node_fs15.readFileSync)(filePath, "utf-8");
|
|
3797
4007
|
const store = JSON.parse(content);
|
|
3798
4008
|
tagCache.set(filePath, { mtimeMs: stat.mtimeMs, store });
|
|
3799
4009
|
return store;
|
|
@@ -3804,14 +4014,14 @@ function readTagStore(rootDir) {
|
|
|
3804
4014
|
function writeTagStore(rootDir, store) {
|
|
3805
4015
|
const filePath = tagsFilePath(rootDir);
|
|
3806
4016
|
const dir = (0, import_node_path16.dirname)(filePath);
|
|
3807
|
-
(0,
|
|
4017
|
+
(0, import_node_fs15.mkdirSync)(dir, { recursive: true });
|
|
3808
4018
|
const cleaned = {};
|
|
3809
4019
|
for (const [nodeId, tags] of Object.entries(store)) {
|
|
3810
4020
|
if (Object.keys(tags).length > 0) {
|
|
3811
4021
|
cleaned[nodeId] = tags;
|
|
3812
4022
|
}
|
|
3813
4023
|
}
|
|
3814
|
-
(0,
|
|
4024
|
+
(0, import_node_fs15.writeFileSync)(filePath, JSON.stringify(cleaned, null, 2) + "\n", "utf-8");
|
|
3815
4025
|
tagCache.delete(filePath);
|
|
3816
4026
|
}
|
|
3817
4027
|
function setTag(rootDir, nodeId, key, value) {
|
|
@@ -3829,11 +4039,11 @@ function removeTag(rootDir, nodeId, key) {
|
|
|
3829
4039
|
}
|
|
3830
4040
|
writeTagStore(rootDir, store);
|
|
3831
4041
|
}
|
|
3832
|
-
var
|
|
4042
|
+
var import_node_fs15, import_node_path16, TAGS_FILENAME, GRAPHS_DIR, tagCache;
|
|
3833
4043
|
var init_tag_store = __esm({
|
|
3834
4044
|
"src/server/graph/core/tag-store.ts"() {
|
|
3835
4045
|
"use strict";
|
|
3836
|
-
|
|
4046
|
+
import_node_fs15 = require("node:fs");
|
|
3837
4047
|
import_node_path16 = require("node:path");
|
|
3838
4048
|
TAGS_FILENAME = "tags.json";
|
|
3839
4049
|
GRAPHS_DIR = ".launchsecure/graphs";
|
|
@@ -3844,8 +4054,8 @@ var init_tag_store = __esm({
|
|
|
3844
4054
|
// src/server/graph/index.ts
|
|
3845
4055
|
function getAvailableLayers(rootDir) {
|
|
3846
4056
|
const dir = (0, import_node_path17.join)(rootDir, GRAPHS_DIR2);
|
|
3847
|
-
if (!(0,
|
|
3848
|
-
return (0,
|
|
4057
|
+
if (!(0, import_node_fs16.existsSync)(dir)) return [];
|
|
4058
|
+
return (0, import_node_fs16.readdirSync)(dir).filter((f) => f.endsWith(".json") && f !== "tags.json").map((f) => f.replace(".json", ""));
|
|
3849
4059
|
}
|
|
3850
4060
|
function graphsDir(rootDir) {
|
|
3851
4061
|
return (0, import_node_path17.join)(rootDir, GRAPHS_DIR2);
|
|
@@ -3857,8 +4067,8 @@ function tagsFilePath2(rootDir) {
|
|
|
3857
4067
|
return (0, import_node_path17.join)(graphsDir(rootDir), "tags.json");
|
|
3858
4068
|
}
|
|
3859
4069
|
function getMtimeMs(filePath) {
|
|
3860
|
-
if (!(0,
|
|
3861
|
-
return (0,
|
|
4070
|
+
if (!(0, import_node_fs16.existsSync)(filePath)) return 0;
|
|
4071
|
+
return (0, import_node_fs16.statSync)(filePath).mtimeMs;
|
|
3862
4072
|
}
|
|
3863
4073
|
function invalidateCache(filePath) {
|
|
3864
4074
|
graphCache.delete(filePath);
|
|
@@ -3897,20 +4107,20 @@ function applyTags(graph, layer, rootDir) {
|
|
|
3897
4107
|
}
|
|
3898
4108
|
function readGraphRaw(rootDir, layer) {
|
|
3899
4109
|
const filePath = graphFilePath(rootDir, layer);
|
|
3900
|
-
if (!(0,
|
|
3901
|
-
const stat = (0,
|
|
4110
|
+
if (!(0, import_node_fs16.existsSync)(filePath)) return null;
|
|
4111
|
+
const stat = (0, import_node_fs16.statSync)(filePath);
|
|
3902
4112
|
const cached = graphCache.get(filePath);
|
|
3903
4113
|
if (cached && cached.mtimeMs === stat.mtimeMs) {
|
|
3904
4114
|
return cached.graph;
|
|
3905
4115
|
}
|
|
3906
|
-
const content = (0,
|
|
4116
|
+
const content = (0, import_node_fs16.readFileSync)(filePath, "utf-8");
|
|
3907
4117
|
const graph = JSON.parse(content);
|
|
3908
4118
|
graphCache.set(filePath, { mtimeMs: stat.mtimeMs, graph });
|
|
3909
4119
|
return graph;
|
|
3910
4120
|
}
|
|
3911
4121
|
function readGraph(rootDir, layer) {
|
|
3912
4122
|
const rawFilePath = graphFilePath(rootDir, layer);
|
|
3913
|
-
if (!(0,
|
|
4123
|
+
if (!(0, import_node_fs16.existsSync)(rawFilePath)) return null;
|
|
3914
4124
|
const rawMtime = getMtimeMs(rawFilePath);
|
|
3915
4125
|
const tagsMtime = getMtimeMs(tagsFilePath2(rootDir));
|
|
3916
4126
|
const cacheKey = `${rootDir}:${layer}`;
|
|
@@ -3940,21 +4150,21 @@ async function generateGraph(rootDir, layer) {
|
|
|
3940
4150
|
mutationMethods: config.parsers?.patterns?.mutationMethods
|
|
3941
4151
|
});
|
|
3942
4152
|
const dir = graphsDir(rootDir);
|
|
3943
|
-
(0,
|
|
4153
|
+
(0, import_node_fs16.mkdirSync)(dir, { recursive: true });
|
|
3944
4154
|
const results = layer ? [generateLayer(rootDir, layer)].filter((r) => r !== null) : generateAll(rootDir);
|
|
3945
4155
|
for (const result of results) {
|
|
3946
4156
|
const filePath = graphFilePath(rootDir, result.layer);
|
|
3947
|
-
(0,
|
|
4157
|
+
(0, import_node_fs16.writeFileSync)(filePath, JSON.stringify(result.output, null, 2) + "\n", "utf-8");
|
|
3948
4158
|
invalidateCache(filePath);
|
|
3949
4159
|
invalidateTaggedCache(rootDir, result.layer);
|
|
3950
4160
|
}
|
|
3951
4161
|
return results;
|
|
3952
4162
|
}
|
|
3953
|
-
var
|
|
4163
|
+
var import_node_fs16, import_node_path17, GRAPHS_DIR2, graphCache, taggedCache;
|
|
3954
4164
|
var init_graph = __esm({
|
|
3955
4165
|
"src/server/graph/index.ts"() {
|
|
3956
4166
|
"use strict";
|
|
3957
|
-
|
|
4167
|
+
import_node_fs16 = require("node:fs");
|
|
3958
4168
|
import_node_path17 = require("node:path");
|
|
3959
4169
|
init_graph_builder();
|
|
3960
4170
|
init_config();
|
|
@@ -3971,9 +4181,9 @@ var init_graph = __esm({
|
|
|
3971
4181
|
// src/server/graph/core/audit-core.ts
|
|
3972
4182
|
function readGraphFile(rootDir, layer) {
|
|
3973
4183
|
const filePath = (0, import_node_path18.join)(rootDir, ".launchsecure", "graphs", `${layer}.json`);
|
|
3974
|
-
if (!(0,
|
|
4184
|
+
if (!(0, import_node_fs17.existsSync)(filePath)) return null;
|
|
3975
4185
|
try {
|
|
3976
|
-
return JSON.parse((0,
|
|
4186
|
+
return JSON.parse((0, import_node_fs17.readFileSync)(filePath, "utf-8"));
|
|
3977
4187
|
} catch {
|
|
3978
4188
|
return null;
|
|
3979
4189
|
}
|
|
@@ -4019,8 +4229,8 @@ function checkUnprotectedRoutes(rootDir) {
|
|
|
4019
4229
|
if (!api) return buildReport("api", "unprotected_routes", findings);
|
|
4020
4230
|
const routePermsPath = (0, import_node_path18.join)(rootDir, "src", "config", "route-permissions.ts");
|
|
4021
4231
|
let routePermsContent = "";
|
|
4022
|
-
if ((0,
|
|
4023
|
-
routePermsContent = (0,
|
|
4232
|
+
if ((0, import_node_fs17.existsSync)(routePermsPath)) {
|
|
4233
|
+
routePermsContent = (0, import_node_fs17.readFileSync)(routePermsPath, "utf-8");
|
|
4024
4234
|
}
|
|
4025
4235
|
const registeredRoutes = /* @__PURE__ */ new Set();
|
|
4026
4236
|
const routeEntryRe = /path:\s*'([^']+)'/g;
|
|
@@ -4099,8 +4309,8 @@ function checkUnenforcedPermissions(rootDir) {
|
|
|
4099
4309
|
const permissions = staticGraph.nodes.filter((n) => n.type === "seed_permission").map((n) => ({ id: n.id, key: n.value, name: n.name }));
|
|
4100
4310
|
const routePermsPath = (0, import_node_path18.join)(rootDir, "src", "config", "route-permissions.ts");
|
|
4101
4311
|
let routePermsContent = "";
|
|
4102
|
-
if ((0,
|
|
4103
|
-
routePermsContent = (0,
|
|
4312
|
+
if ((0, import_node_fs17.existsSync)(routePermsPath)) {
|
|
4313
|
+
routePermsContent = (0, import_node_fs17.readFileSync)(routePermsPath, "utf-8");
|
|
4104
4314
|
}
|
|
4105
4315
|
for (const perm of permissions) {
|
|
4106
4316
|
const regex = new RegExp(`permission:\\s*['"]${perm.key}['"]`);
|
|
@@ -4131,8 +4341,8 @@ function checkHardcodedValues(rootDir) {
|
|
|
4131
4341
|
for (const node of api.nodes) {
|
|
4132
4342
|
if (node.type !== "endpoint") continue;
|
|
4133
4343
|
const filePath = (0, import_node_path18.join)(rootDir, "src", node.id);
|
|
4134
|
-
if (!(0,
|
|
4135
|
-
const content = (0,
|
|
4344
|
+
if (!(0, import_node_fs17.existsSync)(filePath)) continue;
|
|
4345
|
+
const content = (0, import_node_fs17.readFileSync)(filePath, "utf-8");
|
|
4136
4346
|
let m;
|
|
4137
4347
|
allCapsRe.lastIndex = 0;
|
|
4138
4348
|
while ((m = allCapsRe.exec(content)) !== null) {
|
|
@@ -4226,11 +4436,11 @@ function formatAsPrompt(reports) {
|
|
|
4226
4436
|
if (lines.length === 0) return "No audit findings.";
|
|
4227
4437
|
return lines.join("\n");
|
|
4228
4438
|
}
|
|
4229
|
-
var
|
|
4439
|
+
var import_node_fs17, import_node_path18, CHECKS;
|
|
4230
4440
|
var init_audit_core = __esm({
|
|
4231
4441
|
"src/server/graph/core/audit-core.ts"() {
|
|
4232
4442
|
"use strict";
|
|
4233
|
-
|
|
4443
|
+
import_node_fs17 = require("node:fs");
|
|
4234
4444
|
import_node_path18 = require("node:path");
|
|
4235
4445
|
CHECKS = {
|
|
4236
4446
|
db: {
|
|
@@ -4264,14 +4474,14 @@ function findProjectRoot(startDir) {
|
|
|
4264
4474
|
let dir = startDir;
|
|
4265
4475
|
for (let i = 0; i < 8; i++) {
|
|
4266
4476
|
const graphsDir2 = import_node_path19.default.join(dir, ".launchsecure", "graphs");
|
|
4267
|
-
if (
|
|
4477
|
+
if (import_node_fs18.default.existsSync(import_node_path19.default.join(graphsDir2, "ui.json")) || import_node_fs18.default.existsSync(import_node_path19.default.join(graphsDir2, "api.json")) || import_node_fs18.default.existsSync(import_node_path19.default.join(graphsDir2, "db.json"))) return dir;
|
|
4268
4478
|
const parent = import_node_path19.default.dirname(dir);
|
|
4269
4479
|
if (parent === dir) break;
|
|
4270
4480
|
dir = parent;
|
|
4271
4481
|
}
|
|
4272
4482
|
dir = startDir;
|
|
4273
4483
|
for (let i = 0; i < 8; i++) {
|
|
4274
|
-
if (
|
|
4484
|
+
if (import_node_fs18.default.existsSync(import_node_path19.default.join(dir, ".git"))) return dir;
|
|
4275
4485
|
const parent = import_node_path19.default.dirname(dir);
|
|
4276
4486
|
if (parent === dir) break;
|
|
4277
4487
|
dir = parent;
|
|
@@ -4332,16 +4542,16 @@ async function buildMergedGraph(root) {
|
|
|
4332
4542
|
};
|
|
4333
4543
|
}
|
|
4334
4544
|
function serveStatic(res, filePath) {
|
|
4335
|
-
if (!
|
|
4545
|
+
if (!import_node_fs18.default.existsSync(filePath) || !import_node_fs18.default.statSync(filePath).isFile()) return false;
|
|
4336
4546
|
const ext = import_node_path19.default.extname(filePath).toLowerCase();
|
|
4337
4547
|
const mime = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
4338
4548
|
res.writeHead(200, { "Content-Type": mime, "Cache-Control": "no-cache" });
|
|
4339
|
-
|
|
4549
|
+
import_node_fs18.default.createReadStream(filePath).pipe(res);
|
|
4340
4550
|
return true;
|
|
4341
4551
|
}
|
|
4342
4552
|
function serveIndex(res, clientDir) {
|
|
4343
4553
|
const indexPath = import_node_path19.default.join(clientDir, "index.html");
|
|
4344
|
-
if (!
|
|
4554
|
+
if (!import_node_fs18.default.existsSync(indexPath)) {
|
|
4345
4555
|
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
4346
4556
|
res.end(`LaunchChart client bundle not found at ${clientDir}. Run 'npm run build:chart-client'.`);
|
|
4347
4557
|
return;
|
|
@@ -4349,14 +4559,14 @@ function serveIndex(res, clientDir) {
|
|
|
4349
4559
|
serveStatic(res, indexPath);
|
|
4350
4560
|
}
|
|
4351
4561
|
function tryListen(server, port) {
|
|
4352
|
-
return new Promise((
|
|
4562
|
+
return new Promise((resolve4, reject) => {
|
|
4353
4563
|
const onError = (err2) => {
|
|
4354
4564
|
server.off("listening", onListening);
|
|
4355
4565
|
reject(err2);
|
|
4356
4566
|
};
|
|
4357
4567
|
const onListening = () => {
|
|
4358
4568
|
server.off("error", onError);
|
|
4359
|
-
|
|
4569
|
+
resolve4(port);
|
|
4360
4570
|
};
|
|
4361
4571
|
server.once("error", onError);
|
|
4362
4572
|
server.once("listening", onListening);
|
|
@@ -4410,10 +4620,10 @@ async function startChartServer(opts = {}) {
|
|
|
4410
4620
|
if (req.method === "GET" && url2.pathname === "/api/projects") {
|
|
4411
4621
|
const projectList = projects.length > 0 ? projects.map((p) => {
|
|
4412
4622
|
const absRoot = import_node_path19.default.resolve(projectRoot, p.root);
|
|
4413
|
-
const hasGraphs =
|
|
4414
|
-
const
|
|
4415
|
-
return { name: p.name, root: p.root, hasGraphs, hasNextConfig
|
|
4416
|
-
}) : [{ name: import_node_path19.default.basename(projectRoot), root: ".", hasGraphs:
|
|
4623
|
+
const hasGraphs = import_node_fs18.default.existsSync(import_node_path19.default.join(absRoot, ".launchsecure", "graphs"));
|
|
4624
|
+
const hasNextConfig = import_node_fs18.default.existsSync(import_node_path19.default.join(absRoot, "next.config.ts")) || import_node_fs18.default.existsSync(import_node_path19.default.join(absRoot, "next.config.js")) || import_node_fs18.default.existsSync(import_node_path19.default.join(absRoot, "next.config.mjs"));
|
|
4625
|
+
return { name: p.name, root: p.root, hasGraphs, hasNextConfig };
|
|
4626
|
+
}) : [{ name: import_node_path19.default.basename(projectRoot), root: ".", hasGraphs: import_node_fs18.default.existsSync(import_node_path19.default.join(projectRoot, ".launchsecure", "graphs")), hasNextConfig: true }];
|
|
4417
4627
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
4418
4628
|
res.end(JSON.stringify({ projects: projectList, monorepoRoot: projectRoot }));
|
|
4419
4629
|
return;
|
|
@@ -4465,14 +4675,14 @@ async function startChartServer(opts = {}) {
|
|
|
4465
4675
|
return;
|
|
4466
4676
|
}
|
|
4467
4677
|
const filePath = import_node_path19.default.join(reqRoot, relPath);
|
|
4468
|
-
if (!filePath.startsWith(reqRoot) || !
|
|
4678
|
+
if (!filePath.startsWith(reqRoot) || !import_node_fs18.default.existsSync(filePath) || !import_node_fs18.default.statSync(filePath).isFile()) {
|
|
4469
4679
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
4470
4680
|
res.end(JSON.stringify({ error: "File not found" }));
|
|
4471
4681
|
return;
|
|
4472
4682
|
}
|
|
4473
4683
|
const ext = import_node_path19.default.extname(filePath).toLowerCase();
|
|
4474
4684
|
const langMap = { ".ts": "typescript", ".tsx": "tsx", ".js": "javascript", ".jsx": "jsx", ".prisma": "prisma", ".json": "json", ".css": "css" };
|
|
4475
|
-
const content =
|
|
4685
|
+
const content = import_node_fs18.default.readFileSync(filePath, "utf-8");
|
|
4476
4686
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
4477
4687
|
res.end(JSON.stringify({ content, language: langMap[ext] ?? "text", path: relPath }));
|
|
4478
4688
|
return;
|
|
@@ -4515,7 +4725,7 @@ async function startChartServer(opts = {}) {
|
|
|
4515
4725
|
try {
|
|
4516
4726
|
const newConfig = JSON.parse(body);
|
|
4517
4727
|
const configPath = import_node_path19.default.join(reqRoot, ".launchchart.json");
|
|
4518
|
-
|
|
4728
|
+
import_node_fs18.default.writeFileSync(configPath, JSON.stringify(newConfig, null, 2) + "\n", "utf-8");
|
|
4519
4729
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
4520
4730
|
res.end(JSON.stringify({ ok: true }));
|
|
4521
4731
|
} catch (err2) {
|
|
@@ -4549,7 +4759,7 @@ async function startChartServer(opts = {}) {
|
|
|
4549
4759
|
const config2 = loadConfig(reqRoot);
|
|
4550
4760
|
config2.taggers = taggerConfig;
|
|
4551
4761
|
const configPath = import_node_path19.default.join(reqRoot, ".launchchart.json");
|
|
4552
|
-
|
|
4762
|
+
import_node_fs18.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
4553
4763
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
4554
4764
|
res.end(JSON.stringify({ ok: true }));
|
|
4555
4765
|
} catch (err2) {
|
|
@@ -4616,16 +4826,23 @@ async function startChartServer(opts = {}) {
|
|
|
4616
4826
|
const paths = resolveProjectPaths(reqRoot, config2);
|
|
4617
4827
|
const overrides = {
|
|
4618
4828
|
appDir: !!config2.paths?.appDir,
|
|
4619
|
-
dbDir: !!config2.paths?.dbDir
|
|
4829
|
+
dbDir: !!config2.paths?.dbDir,
|
|
4830
|
+
srcRoots: !!(config2.paths?.srcRoots && config2.paths.srcRoots.length > 0)
|
|
4620
4831
|
};
|
|
4832
|
+
const relFromRoot = (abs) => import_node_path19.default.relative(reqRoot, abs) || ".";
|
|
4621
4833
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
4622
4834
|
res.end(JSON.stringify({
|
|
4623
4835
|
projectRoot: reqRoot,
|
|
4624
4836
|
detected: paths ? {
|
|
4625
|
-
srcDir:
|
|
4626
|
-
appDir:
|
|
4627
|
-
apiDir:
|
|
4628
|
-
dbDir: paths.dbDir ?
|
|
4837
|
+
srcDir: relFromRoot(paths.srcDir),
|
|
4838
|
+
appDir: relFromRoot(paths.appDir),
|
|
4839
|
+
apiDir: relFromRoot(paths.apiDir),
|
|
4840
|
+
dbDir: paths.dbDir ? relFromRoot(paths.dbDir) : null,
|
|
4841
|
+
dbKind: paths.dbConfig.kind,
|
|
4842
|
+
dbSchemaPath: paths.dbConfig.schemaPath ? relFromRoot(paths.dbConfig.schemaPath) : null,
|
|
4843
|
+
dbMigrationsDir: paths.dbConfig.migrationsDir ? relFromRoot(paths.dbConfig.migrationsDir) : null,
|
|
4844
|
+
srcRoots: paths.srcRoots.map(relFromRoot),
|
|
4845
|
+
conventionFiles: paths.conventionFiles.map(relFromRoot)
|
|
4629
4846
|
} : null,
|
|
4630
4847
|
overrides,
|
|
4631
4848
|
isOverride: overrides.appDir
|
|
@@ -4642,7 +4859,7 @@ async function startChartServer(opts = {}) {
|
|
|
4642
4859
|
return;
|
|
4643
4860
|
}
|
|
4644
4861
|
try {
|
|
4645
|
-
const entries =
|
|
4862
|
+
const entries = import_node_fs18.default.readdirSync(abs, { withFileTypes: true });
|
|
4646
4863
|
const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules" && e.name !== "dist" && e.name !== ".next").map((e) => e.name).sort();
|
|
4647
4864
|
const parent = abs !== twoUp ? import_node_path19.default.dirname(abs) : null;
|
|
4648
4865
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -4673,7 +4890,7 @@ async function startChartServer(opts = {}) {
|
|
|
4673
4890
|
const config2 = loadConfig(projectRoot);
|
|
4674
4891
|
config2.projects = newProjects.length > 0 ? newProjects : void 0;
|
|
4675
4892
|
const configPath = import_node_path19.default.join(projectRoot, ".launchchart.json");
|
|
4676
|
-
|
|
4893
|
+
import_node_fs18.default.writeFileSync(configPath, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
4677
4894
|
projects.length = 0;
|
|
4678
4895
|
if (config2.projects) projects.push(...config2.projects);
|
|
4679
4896
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -4746,12 +4963,12 @@ function runServeCli(argv) {
|
|
|
4746
4963
|
process.exit(1);
|
|
4747
4964
|
});
|
|
4748
4965
|
}
|
|
4749
|
-
var import_node_http,
|
|
4966
|
+
var import_node_http, import_node_fs18, import_node_path19, MAX_PORT_SCAN, MIME_TYPES;
|
|
4750
4967
|
var init_chart_serve = __esm({
|
|
4751
4968
|
"src/server/chart-serve.ts"() {
|
|
4752
4969
|
"use strict";
|
|
4753
4970
|
import_node_http = __toESM(require("node:http"));
|
|
4754
|
-
|
|
4971
|
+
import_node_fs18 = __toESM(require("node:fs"));
|
|
4755
4972
|
import_node_path19 = __toESM(require("node:path"));
|
|
4756
4973
|
init_graph();
|
|
4757
4974
|
init_lockfile();
|
|
@@ -4774,214 +4991,6 @@ var init_chart_serve = __esm({
|
|
|
4774
4991
|
}
|
|
4775
4992
|
});
|
|
4776
4993
|
|
|
4777
|
-
// src/server/blast-radius-builder.ts
|
|
4778
|
-
function loadDefaults(rootDir) {
|
|
4779
|
-
const filePath = (0, import_node_path20.join)(rootDir, ".launchsecure", "blast-radius-defaults.json");
|
|
4780
|
-
try {
|
|
4781
|
-
if (import_node_fs18.default.existsSync(filePath)) {
|
|
4782
|
-
const raw = import_node_fs18.default.readFileSync(filePath, "utf-8");
|
|
4783
|
-
return JSON.parse(raw);
|
|
4784
|
-
}
|
|
4785
|
-
} catch {
|
|
4786
|
-
}
|
|
4787
|
-
return FALLBACK_DEFAULTS;
|
|
4788
|
-
}
|
|
4789
|
-
function generateAcceptance(node, inspect) {
|
|
4790
|
-
const criteria = [];
|
|
4791
|
-
const t = node.type?.toLowerCase() ?? "";
|
|
4792
|
-
if (t === "endpoint" || t === "mcp-tool") {
|
|
4793
|
-
const methods = inspect?.methods ?? [];
|
|
4794
|
-
const path3 = inspect?.path ?? node.id;
|
|
4795
|
-
if (methods.length > 0) {
|
|
4796
|
-
criteria.push(`${methods.join("/")} ${path3} still returns correct responses for authorized users`);
|
|
4797
|
-
} else {
|
|
4798
|
-
criteria.push(`${path3} still responds correctly`);
|
|
4799
|
-
}
|
|
4800
|
-
if (inspect?.auth && inspect.auth.includes("withAuth")) {
|
|
4801
|
-
criteria.push("Authentication and authorization still enforced");
|
|
4802
|
-
}
|
|
4803
|
-
if (inspect?.db_models && inspect.db_models.length > 0) {
|
|
4804
|
-
criteria.push(`DB operations on ${inspect.db_models.join(", ")} still work correctly`);
|
|
4805
|
-
}
|
|
4806
|
-
} else if (t === "page" || t === "component" || t === "layout") {
|
|
4807
|
-
criteria.push(`${node.name} renders without errors`);
|
|
4808
|
-
if (inspect?.stateVars && inspect.stateVars.length > 0) {
|
|
4809
|
-
criteria.push("State management still works correctly");
|
|
4810
|
-
}
|
|
4811
|
-
if (inspect?.elements && inspect.elements.length > 5) {
|
|
4812
|
-
criteria.push("All child components render correctly");
|
|
4813
|
-
}
|
|
4814
|
-
} else if (t === "table" || t === "enum") {
|
|
4815
|
-
criteria.push(`${node.name} schema unchanged or migration applies cleanly`);
|
|
4816
|
-
criteria.push("Existing queries against this table still work");
|
|
4817
|
-
} else if (t === "hook") {
|
|
4818
|
-
criteria.push(`${node.name} returns expected shape`);
|
|
4819
|
-
if (inspect?.stateVars && inspect.stateVars.length > 0) {
|
|
4820
|
-
criteria.push(`State variables [${inspect.stateVars.map((s) => s.name).join(", ")}] still returned`);
|
|
4821
|
-
}
|
|
4822
|
-
} else if (t === "context") {
|
|
4823
|
-
criteria.push(`${node.name} provides correct context to consumers`);
|
|
4824
|
-
} else if (t === "lib" || t === "config" || t === "types") {
|
|
4825
|
-
criteria.push(`${node.name} exports still conform to expected interface`);
|
|
4826
|
-
} else if (t === "seed" || t === "seed_role" || t === "seed_permission") {
|
|
4827
|
-
criteria.push("Seed runs without errors");
|
|
4828
|
-
criteria.push("Expected rows created in database");
|
|
4829
|
-
} else {
|
|
4830
|
-
criteria.push("Verify no regression");
|
|
4831
|
-
}
|
|
4832
|
-
return criteria;
|
|
4833
|
-
}
|
|
4834
|
-
function buildManifest(input) {
|
|
4835
|
-
const { mode, title, description, subtitle, blastResults, createNodes, inspectData, defaults } = input;
|
|
4836
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
4837
|
-
const centerNodeIds = /* @__PURE__ */ new Set();
|
|
4838
|
-
for (const result of blastResults) {
|
|
4839
|
-
centerNodeIds.add(result.center.id);
|
|
4840
|
-
for (const node of result.affected) {
|
|
4841
|
-
const existing = nodeMap.get(node.id);
|
|
4842
|
-
if (!existing || node.hop < existing.hop) {
|
|
4843
|
-
nodeMap.set(node.id, node);
|
|
4844
|
-
}
|
|
4845
|
-
}
|
|
4846
|
-
}
|
|
4847
|
-
for (const id of centerNodeIds) {
|
|
4848
|
-
nodeMap.delete(id);
|
|
4849
|
-
}
|
|
4850
|
-
const manifestNodes = [];
|
|
4851
|
-
for (const result of blastResults) {
|
|
4852
|
-
const c = result.center;
|
|
4853
|
-
if (manifestNodes.some((n) => n.id === c.id)) continue;
|
|
4854
|
-
const inspect = inspectData[c.id];
|
|
4855
|
-
manifestNodes.push({
|
|
4856
|
-
id: c.id,
|
|
4857
|
-
name: c.name,
|
|
4858
|
-
layer: c.layer,
|
|
4859
|
-
ring: "modify",
|
|
4860
|
-
type: c.type,
|
|
4861
|
-
reason: `Direct change target`,
|
|
4862
|
-
acceptance: generateAcceptance(
|
|
4863
|
-
{ id: c.id, name: c.name, type: c.type, layer: c.layer, hop: 0 },
|
|
4864
|
-
inspect
|
|
4865
|
-
)
|
|
4866
|
-
});
|
|
4867
|
-
}
|
|
4868
|
-
for (const [, node] of nodeMap) {
|
|
4869
|
-
const ring = node.hop <= 1 ? "modify" : "ripple";
|
|
4870
|
-
const inspect = inspectData[node.id];
|
|
4871
|
-
const reason = node.hop <= 1 ? `Directly depends on changed node` : `Indirect dependency (${node.hop} hops away)`;
|
|
4872
|
-
manifestNodes.push({
|
|
4873
|
-
id: node.id,
|
|
4874
|
-
name: node.name,
|
|
4875
|
-
layer: node.layer,
|
|
4876
|
-
ring,
|
|
4877
|
-
type: node.type,
|
|
4878
|
-
reason,
|
|
4879
|
-
acceptance: generateAcceptance(node, inspect)
|
|
4880
|
-
});
|
|
4881
|
-
}
|
|
4882
|
-
for (const cn of createNodes) {
|
|
4883
|
-
manifestNodes.push({
|
|
4884
|
-
id: cn.id,
|
|
4885
|
-
name: cn.name,
|
|
4886
|
-
layer: cn.layer,
|
|
4887
|
-
ring: "create",
|
|
4888
|
-
type: cn.type ?? "unknown",
|
|
4889
|
-
reason: cn.reason,
|
|
4890
|
-
acceptance: cn.acceptance ?? ["Verify implementation matches spec"]
|
|
4891
|
-
});
|
|
4892
|
-
}
|
|
4893
|
-
const layerIds = /* @__PURE__ */ new Set();
|
|
4894
|
-
for (const n of manifestNodes) {
|
|
4895
|
-
layerIds.add(n.layer);
|
|
4896
|
-
}
|
|
4897
|
-
const layers = [];
|
|
4898
|
-
for (const id of layerIds) {
|
|
4899
|
-
const def = defaults.layers[id];
|
|
4900
|
-
if (def) {
|
|
4901
|
-
layers.push({ id, name: def.name, icon: def.icon, color: def.color });
|
|
4902
|
-
} else {
|
|
4903
|
-
layers.push({ id, name: id, icon: "box", color: "#cbd5e1" });
|
|
4904
|
-
}
|
|
4905
|
-
}
|
|
4906
|
-
const edgeSet = /* @__PURE__ */ new Set();
|
|
4907
|
-
const edges = [];
|
|
4908
|
-
const allNodeIds = new Set(manifestNodes.map((n) => n.id));
|
|
4909
|
-
for (const cId of centerNodeIds) {
|
|
4910
|
-
for (const result of blastResults) {
|
|
4911
|
-
for (const affected of result.affected) {
|
|
4912
|
-
if (affected.hop === 1 && result.center.id === cId && allNodeIds.has(affected.id)) {
|
|
4913
|
-
const key = `${cId}->${affected.id}`;
|
|
4914
|
-
if (!edgeSet.has(key)) {
|
|
4915
|
-
edgeSet.add(key);
|
|
4916
|
-
edges.push({ source: cId, target: affected.id });
|
|
4917
|
-
}
|
|
4918
|
-
}
|
|
4919
|
-
}
|
|
4920
|
-
}
|
|
4921
|
-
}
|
|
4922
|
-
for (const result of blastResults) {
|
|
4923
|
-
if (result.edges) {
|
|
4924
|
-
for (const edge of result.edges) {
|
|
4925
|
-
if (allNodeIds.has(edge.source) && allNodeIds.has(edge.target)) {
|
|
4926
|
-
const key = `${edge.source}->${edge.target}`;
|
|
4927
|
-
if (!edgeSet.has(key)) {
|
|
4928
|
-
edgeSet.add(key);
|
|
4929
|
-
edges.push({ source: edge.source, target: edge.target });
|
|
4930
|
-
}
|
|
4931
|
-
}
|
|
4932
|
-
}
|
|
4933
|
-
}
|
|
4934
|
-
}
|
|
4935
|
-
for (const cn of createNodes) {
|
|
4936
|
-
edges.push({ source: "center", target: cn.id });
|
|
4937
|
-
if (cn.connects_to) {
|
|
4938
|
-
for (const targetId of cn.connects_to) {
|
|
4939
|
-
if (allNodeIds.has(targetId) || createNodes.some((c) => c.id === targetId)) {
|
|
4940
|
-
const key = `${cn.id}->${targetId}`;
|
|
4941
|
-
if (!edgeSet.has(key)) {
|
|
4942
|
-
edgeSet.add(key);
|
|
4943
|
-
edges.push({ source: cn.id, target: targetId });
|
|
4944
|
-
}
|
|
4945
|
-
}
|
|
4946
|
-
}
|
|
4947
|
-
}
|
|
4948
|
-
}
|
|
4949
|
-
return {
|
|
4950
|
-
mode,
|
|
4951
|
-
title,
|
|
4952
|
-
subtitle,
|
|
4953
|
-
layers,
|
|
4954
|
-
rings: defaults.rings,
|
|
4955
|
-
center: { name: title, description },
|
|
4956
|
-
nodes: manifestNodes,
|
|
4957
|
-
edges
|
|
4958
|
-
};
|
|
4959
|
-
}
|
|
4960
|
-
var import_node_fs18, import_node_path20, FALLBACK_DEFAULTS;
|
|
4961
|
-
var init_blast_radius_builder = __esm({
|
|
4962
|
-
"src/server/blast-radius-builder.ts"() {
|
|
4963
|
-
"use strict";
|
|
4964
|
-
import_node_fs18 = __toESM(require("node:fs"));
|
|
4965
|
-
import_node_path20 = require("node:path");
|
|
4966
|
-
FALLBACK_DEFAULTS = {
|
|
4967
|
-
rings: [
|
|
4968
|
-
{ id: "modify", name: "Modify", color: "#ff6b00" },
|
|
4969
|
-
{ id: "ripple", name: "Ripple (verify)", color: "#ffff00" },
|
|
4970
|
-
{ id: "create", name: "Create", color: "#00ff00" }
|
|
4971
|
-
],
|
|
4972
|
-
layers: {
|
|
4973
|
-
db: { name: "Database", icon: "database", color: "#cbd5e1" },
|
|
4974
|
-
api: { name: "API", icon: "server", color: "#cbd5e1" },
|
|
4975
|
-
middleware: { name: "Middleware", icon: "shield", color: "#cbd5e1" },
|
|
4976
|
-
ui: { name: "UI", icon: "layout-dashboard", color: "#cbd5e1" },
|
|
4977
|
-
config: { name: "Config / Seed", icon: "settings", color: "#cbd5e1" },
|
|
4978
|
-
shared: { name: "Shared Types", icon: "box", color: "#cbd5e1" }
|
|
4979
|
-
},
|
|
4980
|
-
center: { color: "#ff0000" }
|
|
4981
|
-
};
|
|
4982
|
-
}
|
|
4983
|
-
});
|
|
4984
|
-
|
|
4985
4994
|
// src/server/graph/core/language-detection.ts
|
|
4986
4995
|
function walkForExtensions(dir, extCounts, depth = 0) {
|
|
4987
4996
|
if (depth > 10) return;
|
|
@@ -4996,9 +5005,9 @@ function walkForExtensions(dir, extCounts, depth = 0) {
|
|
|
4996
5005
|
if (entry.name.startsWith(".") && entry.isDirectory()) continue;
|
|
4997
5006
|
if (entry.isDirectory()) {
|
|
4998
5007
|
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
4999
|
-
walkForExtensions((0,
|
|
5008
|
+
walkForExtensions((0, import_node_path20.join)(dir, entry.name), extCounts, depth + 1);
|
|
5000
5009
|
} else {
|
|
5001
|
-
const ext = (0,
|
|
5010
|
+
const ext = (0, import_node_path20.extname)(entry.name).toLowerCase();
|
|
5002
5011
|
if (ext && EXTENSION_TO_LANGUAGE[ext]) {
|
|
5003
5012
|
extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);
|
|
5004
5013
|
}
|
|
@@ -5037,12 +5046,12 @@ function detectLanguages(rootDir, supportedLanguages) {
|
|
|
5037
5046
|
});
|
|
5038
5047
|
return results;
|
|
5039
5048
|
}
|
|
5040
|
-
var import_node_fs19,
|
|
5049
|
+
var import_node_fs19, import_node_path20, EXTENSION_TO_LANGUAGE, IGNORE_DIRS, AUXILIARY_LANGUAGES;
|
|
5041
5050
|
var init_language_detection = __esm({
|
|
5042
5051
|
"src/server/graph/core/language-detection.ts"() {
|
|
5043
5052
|
"use strict";
|
|
5044
5053
|
import_node_fs19 = require("node:fs");
|
|
5045
|
-
|
|
5054
|
+
import_node_path20 = require("node:path");
|
|
5046
5055
|
EXTENSION_TO_LANGUAGE = {
|
|
5047
5056
|
// Web / Frontend
|
|
5048
5057
|
".ts": "typescript",
|
|
@@ -5375,144 +5384,6 @@ function handleBlastPoints(args) {
|
|
|
5375
5384
|
}
|
|
5376
5385
|
});
|
|
5377
5386
|
}
|
|
5378
|
-
function handleGenerateBlastRadius(args) {
|
|
5379
|
-
const rootDir = process.cwd();
|
|
5380
|
-
const mode = args.mode ?? "structural";
|
|
5381
|
-
const title = args.title;
|
|
5382
|
-
const description = args.description ?? title;
|
|
5383
|
-
const subtitle = args.subtitle;
|
|
5384
|
-
const hops = args.hops ?? 2;
|
|
5385
|
-
const defaults = loadDefaults(rootDir);
|
|
5386
|
-
let centerNodeIds = [];
|
|
5387
|
-
if (mode === "structural") {
|
|
5388
|
-
const nodeId = args.node_id;
|
|
5389
|
-
if (!nodeId) return err("structural mode requires node_id");
|
|
5390
|
-
centerNodeIds = [nodeId];
|
|
5391
|
-
} else {
|
|
5392
|
-
centerNodeIds = args.center_nodes ?? [];
|
|
5393
|
-
if (centerNodeIds.length === 0) return err("feature mode requires center_nodes[]");
|
|
5394
|
-
}
|
|
5395
|
-
const createNodes = args.create_nodes ?? [];
|
|
5396
|
-
const blastResults = [];
|
|
5397
|
-
for (const nodeId of centerNodeIds) {
|
|
5398
|
-
let targetLayer;
|
|
5399
|
-
const graphs = readAllGraphs(rootDir);
|
|
5400
|
-
for (const [layer, graph2] of Object.entries(graphs)) {
|
|
5401
|
-
if (graph2 && graph2.nodes.some((n) => n.id === nodeId)) {
|
|
5402
|
-
targetLayer = layer;
|
|
5403
|
-
break;
|
|
5404
|
-
}
|
|
5405
|
-
}
|
|
5406
|
-
if (!targetLayer) continue;
|
|
5407
|
-
const graph = readGraph(rootDir, targetLayer);
|
|
5408
|
-
if (!graph) continue;
|
|
5409
|
-
const center = graph.nodes.find((n) => n.id === nodeId);
|
|
5410
|
-
if (!center) continue;
|
|
5411
|
-
const result2 = reverseNeighborhood(graph, nodeId, hops, "reverse");
|
|
5412
|
-
const affected = [];
|
|
5413
|
-
for (const [id, { node, hop }] of result2.nodes) {
|
|
5414
|
-
if (id === nodeId) continue;
|
|
5415
|
-
const tags = node.tags;
|
|
5416
|
-
affected.push({ id: node.id, name: node.name, type: node.type, layer: targetLayer, hop, module: tags?.module });
|
|
5417
|
-
}
|
|
5418
|
-
const otherLayers = getAvailableLayers(rootDir).filter((l) => l !== targetLayer && l !== "static");
|
|
5419
|
-
for (const otherLayer of otherLayers) {
|
|
5420
|
-
const otherGraph = readGraph(rootDir, otherLayer);
|
|
5421
|
-
if (!otherGraph) continue;
|
|
5422
|
-
for (const edge of otherGraph.edges) {
|
|
5423
|
-
if (edge.target === nodeId || edge.source === nodeId) {
|
|
5424
|
-
const dependentId = edge.target === nodeId ? edge.source : edge.target;
|
|
5425
|
-
if (affected.some((a) => a.id === dependentId)) continue;
|
|
5426
|
-
const depNode = otherGraph.nodes.find((n) => n.id === dependentId);
|
|
5427
|
-
if (depNode) {
|
|
5428
|
-
const tags = depNode.tags;
|
|
5429
|
-
affected.push({ id: depNode.id, name: depNode.name, type: depNode.type, layer: otherLayer, hop: 1, module: tags?.module });
|
|
5430
|
-
}
|
|
5431
|
-
}
|
|
5432
|
-
}
|
|
5433
|
-
}
|
|
5434
|
-
const centerTags = center.tags;
|
|
5435
|
-
const edges = result2.edges.map((e) => ({ source: e.source, target: e.target }));
|
|
5436
|
-
blastResults.push({
|
|
5437
|
-
center: { id: center.id, name: center.name, type: center.type, layer: targetLayer, module: centerTags?.module },
|
|
5438
|
-
affected,
|
|
5439
|
-
edges
|
|
5440
|
-
});
|
|
5441
|
-
}
|
|
5442
|
-
if (blastResults.length === 0) {
|
|
5443
|
-
return err(`None of the center nodes were found in any graph layer: ${centerNodeIds.join(", ")}`);
|
|
5444
|
-
}
|
|
5445
|
-
const inspectData = {};
|
|
5446
|
-
const allAffectedIds = /* @__PURE__ */ new Set();
|
|
5447
|
-
for (const r of blastResults) {
|
|
5448
|
-
allAffectedIds.add(r.center.id);
|
|
5449
|
-
for (const a of r.affected) allAffectedIds.add(a.id);
|
|
5450
|
-
}
|
|
5451
|
-
const allGraphs = readAllGraphs(rootDir);
|
|
5452
|
-
for (const id of allAffectedIds) {
|
|
5453
|
-
for (const [, graph] of Object.entries(allGraphs)) {
|
|
5454
|
-
if (!graph) continue;
|
|
5455
|
-
const node = graph.nodes.find((n) => n.id === id);
|
|
5456
|
-
if (node) {
|
|
5457
|
-
inspectData[id] = {
|
|
5458
|
-
type: node.type,
|
|
5459
|
-
name: node.name,
|
|
5460
|
-
methods: node.methods,
|
|
5461
|
-
path: node.path ?? node.handler,
|
|
5462
|
-
auth: node.auth,
|
|
5463
|
-
db_models: node.db_models
|
|
5464
|
-
};
|
|
5465
|
-
break;
|
|
5466
|
-
}
|
|
5467
|
-
}
|
|
5468
|
-
}
|
|
5469
|
-
const manifest = buildManifest({
|
|
5470
|
-
mode,
|
|
5471
|
-
title,
|
|
5472
|
-
description,
|
|
5473
|
-
subtitle,
|
|
5474
|
-
blastResults,
|
|
5475
|
-
createNodes,
|
|
5476
|
-
inspectData,
|
|
5477
|
-
defaults
|
|
5478
|
-
});
|
|
5479
|
-
const pushToDeck = args.push_to_deck;
|
|
5480
|
-
const session = args.session;
|
|
5481
|
-
let deckResult;
|
|
5482
|
-
if (pushToDeck) {
|
|
5483
|
-
if (!session) return err("push_to_deck requires a session name");
|
|
5484
|
-
const deckLockPath = (0, import_node_path22.join)(rootDir, ".launchsecure", "launch-deck.lock");
|
|
5485
|
-
if (!(0, import_node_fs20.existsSync)(deckLockPath)) {
|
|
5486
|
-
deckResult = { pushed: false, reason: "Deck server not running (no lock file). Push manually via deck tool." };
|
|
5487
|
-
} else {
|
|
5488
|
-
try {
|
|
5489
|
-
const lock = JSON.parse((0, import_node_fs20.readFileSync)(deckLockPath, "utf-8"));
|
|
5490
|
-
const deckUrl = lock.url;
|
|
5491
|
-
const body = JSON.stringify({
|
|
5492
|
-
session,
|
|
5493
|
-
mode: "show",
|
|
5494
|
-
blocks: [{ type: "blast-radius", label: title, manifest }]
|
|
5495
|
-
});
|
|
5496
|
-
(0, import_node_child_process2.execFileSync)("curl", [
|
|
5497
|
-
"-s",
|
|
5498
|
-
"-X",
|
|
5499
|
-
"POST",
|
|
5500
|
-
deckUrl + "/api/deck",
|
|
5501
|
-
"-H",
|
|
5502
|
-
"Content-Type: application/json",
|
|
5503
|
-
"-d",
|
|
5504
|
-
body
|
|
5505
|
-
], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] });
|
|
5506
|
-
deckResult = { pushed: true, session, url: deckUrl };
|
|
5507
|
-
} catch (e) {
|
|
5508
|
-
deckResult = { pushed: false, reason: `Failed to push to deck: ${e}` };
|
|
5509
|
-
}
|
|
5510
|
-
}
|
|
5511
|
-
}
|
|
5512
|
-
const result = { ...manifest };
|
|
5513
|
-
if (deckResult) result._deck = deckResult;
|
|
5514
|
-
return okJson(result);
|
|
5515
|
-
}
|
|
5516
5387
|
function layerSummary(graph) {
|
|
5517
5388
|
const typeCounts = {};
|
|
5518
5389
|
const moduleCounts = {};
|
|
@@ -5741,11 +5612,11 @@ function handleReadGraph(args) {
|
|
|
5741
5612
|
return okJson(result);
|
|
5742
5613
|
}
|
|
5743
5614
|
function nodeToFilePath(rootDir, layer, nodeId) {
|
|
5744
|
-
if (layer === "ui" || layer === "api") return (0,
|
|
5745
|
-
if (layer === "db") return (0,
|
|
5746
|
-
const withSrc = (0,
|
|
5615
|
+
if (layer === "ui" || layer === "api") return (0, import_node_path21.join)(rootDir, "src", nodeId);
|
|
5616
|
+
if (layer === "db") return (0, import_node_path21.join)(rootDir, "prisma", "schema.prisma");
|
|
5617
|
+
const withSrc = (0, import_node_path21.join)(rootDir, "src", nodeId);
|
|
5747
5618
|
if ((0, import_node_fs20.existsSync)(withSrc)) return withSrc;
|
|
5748
|
-
const direct = (0,
|
|
5619
|
+
const direct = (0, import_node_path21.join)(rootDir, nodeId);
|
|
5749
5620
|
if ((0, import_node_fs20.existsSync)(direct)) return direct;
|
|
5750
5621
|
return null;
|
|
5751
5622
|
}
|
|
@@ -5958,9 +5829,9 @@ function handleStartChartServer(args) {
|
|
|
5958
5829
|
});
|
|
5959
5830
|
}
|
|
5960
5831
|
const entryPath = process.argv[1];
|
|
5961
|
-
const logDir = (0,
|
|
5832
|
+
const logDir = (0, import_node_path21.join)((0, import_node_os2.homedir)(), ".launchsecure");
|
|
5962
5833
|
(0, import_node_fs20.mkdirSync)(logDir, { recursive: true });
|
|
5963
|
-
const logPath = (0,
|
|
5834
|
+
const logPath = (0, import_node_path21.join)(logDir, "launch-chart.log");
|
|
5964
5835
|
const out = (0, import_node_fs20.openSync)(logPath, "a");
|
|
5965
5836
|
const err2 = (0, import_node_fs20.openSync)(logPath, "a");
|
|
5966
5837
|
const portArgs = args.port ? ["--port", String(args.port)] : [];
|
|
@@ -6082,18 +5953,18 @@ function handleDetectProjectStack() {
|
|
|
6082
5953
|
if (ref.type === "references_api") stats.references_api++;
|
|
6083
5954
|
}
|
|
6084
5955
|
}
|
|
6085
|
-
const srcDir = (0,
|
|
5956
|
+
const srcDir = (0, import_node_path21.join)(rootDir, "src");
|
|
6086
5957
|
if ((0, import_node_fs20.existsSync)(srcDir)) {
|
|
6087
5958
|
const scanDir = (dir) => {
|
|
6088
5959
|
if (!(0, import_node_fs20.existsSync)(dir)) return;
|
|
6089
5960
|
for (const entry of (0, import_node_fs20.readdirSync)(dir, { withFileTypes: true })) {
|
|
6090
5961
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
6091
|
-
const full = (0,
|
|
5962
|
+
const full = (0, import_node_path21.join)(dir, entry.name);
|
|
6092
5963
|
if (entry.isDirectory()) {
|
|
6093
5964
|
scanDir(full);
|
|
6094
5965
|
continue;
|
|
6095
5966
|
}
|
|
6096
|
-
if (![".ts", ".tsx"].includes((0,
|
|
5967
|
+
if (![".ts", ".tsx"].includes((0, import_node_path21.extname)(entry.name))) continue;
|
|
6097
5968
|
try {
|
|
6098
5969
|
const content = (0, import_node_fs20.readFileSync)(full, "utf-8");
|
|
6099
5970
|
const matches = content.match(/@api\s+(GET|POST|PUT|DELETE|PATCH)\s+\/\S+/g);
|
|
@@ -6214,10 +6085,6 @@ async function handleMessage(msg) {
|
|
|
6214
6085
|
respond(id ?? null, handleBlastPoints(args));
|
|
6215
6086
|
return;
|
|
6216
6087
|
}
|
|
6217
|
-
if (toolName === "generate_blast_radius") {
|
|
6218
|
-
respond(id ?? null, handleGenerateBlastRadius(args));
|
|
6219
|
-
return;
|
|
6220
|
-
}
|
|
6221
6088
|
respondError(id ?? null, -32601, `Unknown tool: ${toolName}`);
|
|
6222
6089
|
return;
|
|
6223
6090
|
}
|
|
@@ -6253,16 +6120,15 @@ function startGraphMcpServer() {
|
|
|
6253
6120
|
process.stderr.write(`[launchsecure-graph] MCP server started (cwd: ${process.cwd()})
|
|
6254
6121
|
`);
|
|
6255
6122
|
}
|
|
6256
|
-
var import_node_fs20,
|
|
6123
|
+
var import_node_fs20, import_node_path21, import_node_child_process2, import_node_os2, SERVER_INFO, TOOLS, COMPACT_SCHEMA, COMPACT_NODE_KNOWN_KEYS, DEEP_FIELDS, EST_CHARS_PER_NODE_FULL, EST_CHARS_PER_NODE_MIN, EST_CHARS_PER_EDGE, DEFAULT_EST_NODE_FULL, DEFAULT_EST_NODE_MIN, DEFAULT_EST_EDGE, NEIGHBORHOOD_BUDGET_CHARS, BATCH_BUDGET_CHARS;
|
|
6257
6124
|
var init_graph_mcp = __esm({
|
|
6258
6125
|
"src/server/graph-mcp.ts"() {
|
|
6259
6126
|
"use strict";
|
|
6260
6127
|
import_node_fs20 = require("node:fs");
|
|
6261
|
-
|
|
6128
|
+
import_node_path21 = require("node:path");
|
|
6262
6129
|
import_node_child_process2 = require("node:child_process");
|
|
6263
6130
|
import_node_os2 = require("node:os");
|
|
6264
6131
|
init_graph();
|
|
6265
|
-
init_blast_radius_builder();
|
|
6266
6132
|
init_lockfile();
|
|
6267
6133
|
init_config();
|
|
6268
6134
|
init_parser_registry();
|
|
@@ -6579,81 +6445,6 @@ Example: blast_points(node_id: "server/auth/middleware.ts", hops: 2) \u2192 retu
|
|
|
6579
6445
|
},
|
|
6580
6446
|
required: ["node_id"]
|
|
6581
6447
|
}
|
|
6582
|
-
},
|
|
6583
|
-
{
|
|
6584
|
-
name: "generate_blast_radius",
|
|
6585
|
-
description: `Generate a complete BlastRadiusManifest from graph data \u2014 ready to push to deck.
|
|
6586
|
-
|
|
6587
|
-
Two modes:
|
|
6588
|
-
- **Structural**: single node changed \u2192 auto-discover what's affected via reverse BFS
|
|
6589
|
-
Example: generate_blast_radius({ mode: "structural", node_id: "CommentChannel", title: "CommentChannel refactor" })
|
|
6590
|
-
- **Feature**: new feature \u2192 multiple starting nodes + new nodes to create
|
|
6591
|
-
Example: generate_blast_radius({ mode: "feature", title: "Client Role", description: "...", center_nodes: ["CommentChannel", "ProjectMember"], create_nodes: [{ id: "ChannelMember", name: "ChannelMember table", layer: "db", reason: "..." }] })
|
|
6592
|
-
|
|
6593
|
-
Output is a BlastRadiusManifest JSON that passes directly to the deck tool's blast-radius block.
|
|
6594
|
-
Reads ring/layer/center colors from .launchsecure/blast-radius-defaults.json.
|
|
6595
|
-
Auto-generates acceptance criteria per node using inspect_node AST data.`,
|
|
6596
|
-
inputSchema: {
|
|
6597
|
-
type: "object",
|
|
6598
|
-
properties: {
|
|
6599
|
-
mode: {
|
|
6600
|
-
type: "string",
|
|
6601
|
-
enum: ["structural", "feature"],
|
|
6602
|
-
description: '"structural" = single node changed. "feature" = new feature with multiple nodes.'
|
|
6603
|
-
},
|
|
6604
|
-
title: {
|
|
6605
|
-
type: "string",
|
|
6606
|
-
description: "Title for the blast radius (shown in center node and header)."
|
|
6607
|
-
},
|
|
6608
|
-
description: {
|
|
6609
|
-
type: "string",
|
|
6610
|
-
description: "Description of the change or feature."
|
|
6611
|
-
},
|
|
6612
|
-
subtitle: {
|
|
6613
|
-
type: "string",
|
|
6614
|
-
description: "Optional subtitle shown above title in the viz."
|
|
6615
|
-
},
|
|
6616
|
-
node_id: {
|
|
6617
|
-
type: "string",
|
|
6618
|
-
description: "Structural mode only: the node being changed."
|
|
6619
|
-
},
|
|
6620
|
-
center_nodes: {
|
|
6621
|
-
type: "array",
|
|
6622
|
-
items: { type: "string" },
|
|
6623
|
-
description: "Feature mode: existing graph node IDs that are the starting points for traversal."
|
|
6624
|
-
},
|
|
6625
|
-
create_nodes: {
|
|
6626
|
-
type: "array",
|
|
6627
|
-
items: {
|
|
6628
|
-
type: "object",
|
|
6629
|
-
properties: {
|
|
6630
|
-
id: { type: "string" },
|
|
6631
|
-
name: { type: "string" },
|
|
6632
|
-
layer: { type: "string" },
|
|
6633
|
-
type: { type: "string" },
|
|
6634
|
-
reason: { type: "string" },
|
|
6635
|
-
acceptance: { type: "array", items: { type: "string" } },
|
|
6636
|
-
connects_to: { type: "array", items: { type: "string" }, description: "IDs of existing nodes this new node has FK/relationship edges to." }
|
|
6637
|
-
},
|
|
6638
|
-
required: ["id", "name", "layer", "reason"]
|
|
6639
|
-
},
|
|
6640
|
-
description: "Feature mode: new nodes that need to be created (not in graph yet)."
|
|
6641
|
-
},
|
|
6642
|
-
hops: {
|
|
6643
|
-
type: "number",
|
|
6644
|
-
description: "Max hops for traversal. Default 2. Hop 1 = modify ring, hop 2+ = ripple ring."
|
|
6645
|
-
},
|
|
6646
|
-
push_to_deck: {
|
|
6647
|
-
type: "boolean",
|
|
6648
|
-
description: "If true, pushes the manifest directly to LaunchDeck browser (requires deck server running). Default false."
|
|
6649
|
-
},
|
|
6650
|
-
session: {
|
|
6651
|
-
type: "string",
|
|
6652
|
-
description: "Session name for the deck tab. Required when push_to_deck is true."
|
|
6653
|
-
}
|
|
6654
|
-
},
|
|
6655
|
-
required: ["title"]
|
|
6656
|
-
}
|
|
6657
6448
|
}
|
|
6658
6449
|
];
|
|
6659
6450
|
COMPACT_SCHEMA = {
|
|
@@ -6721,7 +6512,7 @@ Auto-generates acceptance criteria per node using inspect_node AST data.`,
|
|
|
6721
6512
|
// src/server/graph-mcp-entry.ts
|
|
6722
6513
|
var import_node_child_process3 = require("node:child_process");
|
|
6723
6514
|
var import_node_fs21 = require("node:fs");
|
|
6724
|
-
var
|
|
6515
|
+
var import_node_path22 = __toESM(require("node:path"));
|
|
6725
6516
|
var import_node_os3 = require("node:os");
|
|
6726
6517
|
var import_node_fs22 = require("node:fs");
|
|
6727
6518
|
init_lockfile();
|
|
@@ -6739,9 +6530,9 @@ function maybeAutoServe() {
|
|
|
6739
6530
|
return;
|
|
6740
6531
|
}
|
|
6741
6532
|
try {
|
|
6742
|
-
const logDir =
|
|
6533
|
+
const logDir = import_node_path22.default.join((0, import_node_os3.homedir)(), ".launchsecure");
|
|
6743
6534
|
(0, import_node_fs22.mkdirSync)(logDir, { recursive: true });
|
|
6744
|
-
const logPath =
|
|
6535
|
+
const logPath = import_node_path22.default.join(logDir, "launch-chart.log");
|
|
6745
6536
|
const out = (0, import_node_fs21.openSync)(logPath, "a");
|
|
6746
6537
|
const err2 = (0, import_node_fs21.openSync)(logPath, "a");
|
|
6747
6538
|
const entryPath = process.argv[1];
|