@kage-core/kage-graph-mcp 1.1.20 → 1.1.21
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/README.md +41 -13
- package/dist/cli.js +25 -1
- package/dist/daemon.js +5 -2
- package/dist/index.js +17 -0
- package/dist/kernel.js +1123 -177
- package/dist/structural-worker.js +30 -0
- package/package.json +1 -1
- package/viewer/app.js +540 -31
- package/viewer/index.html +2 -2
package/viewer/app.js
CHANGED
|
@@ -111,8 +111,13 @@
|
|
|
111
111
|
proofList: document.getElementById("proofList")
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
-
var MEMORY_CODE_RELATIONS = new Set(["explains_symbol", "informs_symbol", "fixes_symbol", "applies_to_route", "verified_by_test", "
|
|
114
|
+
var MEMORY_CODE_RELATIONS = new Set(["explains_symbol", "informs_symbol", "fixes_symbol", "applies_to_route", "verified_by_test", "affects_code_path"]);
|
|
115
115
|
var INSPECTOR_CONNECTION_LIMIT = 8;
|
|
116
|
+
var PATH_BRIDGE_EDGE_LIMIT_PER_PATH = 8;
|
|
117
|
+
var PATH_BRIDGE_EDGE_LIMIT_TOTAL = 160;
|
|
118
|
+
var VISIBLE_EDGE_MULTIPLIER = 4;
|
|
119
|
+
var VISIBLE_EDGE_MIN = 160;
|
|
120
|
+
var VISIBLE_EDGE_MAX = 560;
|
|
116
121
|
|
|
117
122
|
els.graphFile.addEventListener("change", handleFile);
|
|
118
123
|
els.searchInput.addEventListener("input", scheduleRender);
|
|
@@ -221,12 +226,13 @@
|
|
|
221
226
|
|
|
222
227
|
function loadFromUrlParams() {
|
|
223
228
|
var params = new URLSearchParams(window.location.search);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
229
|
+
applyRequestedView(params.get("view") || params.get("mode"));
|
|
230
|
+
var memoryGraphPaths = splitParamValues(params.getAll("graph"));
|
|
231
|
+
var codeGraphPaths = splitParamValues(params.getAll("code"));
|
|
232
|
+
var graphPaths = memoryGraphPaths.concat(codeGraphPaths);
|
|
233
|
+
var graphLabels = new Map();
|
|
234
|
+
memoryGraphPaths.forEach(function (path) { graphLabels.set(path, "memory knowledge graph"); });
|
|
235
|
+
codeGraphPaths.forEach(function (path) { graphLabels.set(path, "structural code graph"); });
|
|
230
236
|
var metricsPath = params.get("metrics");
|
|
231
237
|
var inboxPath = params.get("inbox");
|
|
232
238
|
var reviewPath = params.get("review");
|
|
@@ -246,10 +252,7 @@
|
|
|
246
252
|
}
|
|
247
253
|
setAutoLoad("loading project graph", false);
|
|
248
254
|
Promise.all(graphPaths.map(function (path) {
|
|
249
|
-
return
|
|
250
|
-
if (!response.ok) throw new Error(response.status + " " + path);
|
|
251
|
-
return response.json().then(function (graph) { return { fileName: path.split("/").pop() || path, graph: graph }; });
|
|
252
|
-
});
|
|
255
|
+
return loadGraphPath(path).then(function (graph) { return { fileName: graphLabels.get(path) || path.split("/").pop() || path, graph: graph }; });
|
|
253
256
|
}).concat(jobs)).then(function (items) {
|
|
254
257
|
var graphItems = items.filter(Boolean);
|
|
255
258
|
if (!graphItems.length) {
|
|
@@ -270,7 +273,7 @@
|
|
|
270
273
|
setAutoLoad("loading hosted repo graph", false);
|
|
271
274
|
Promise.all([
|
|
272
275
|
fetchJson("./data/kage/graph.json"),
|
|
273
|
-
|
|
276
|
+
loadGraphPath("./data/kage/code_graph/graph.json"),
|
|
274
277
|
fetchJson("./data/kage/metrics.json").catch(function () { return null; }),
|
|
275
278
|
fetchJson("./data/kage/inbox.json").catch(function () { return null; })
|
|
276
279
|
]).then(function (items) {
|
|
@@ -313,6 +316,58 @@
|
|
|
313
316
|
});
|
|
314
317
|
}
|
|
315
318
|
|
|
319
|
+
function loadGraphPath(path) {
|
|
320
|
+
return fetchJson(path).then(function (graph) {
|
|
321
|
+
return hydrateCompactCodeGraph(graph, path);
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function resolveGraphRef(basePath, ref) {
|
|
326
|
+
if (!ref) return ref;
|
|
327
|
+
try {
|
|
328
|
+
return new URL(ref, new URL(basePath, window.location.href)).href;
|
|
329
|
+
} catch (_error) {
|
|
330
|
+
var base = String(basePath || "");
|
|
331
|
+
return base.replace(/\/[^/]*$/, "/" + ref);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function hydrateCompactCodeGraph(graph, basePath) {
|
|
336
|
+
if (!graph || graph.compact !== true || !graph.refs) return Promise.resolve(graph);
|
|
337
|
+
if (graph.refs.entities && graph.refs.edges) {
|
|
338
|
+
return Promise.all([
|
|
339
|
+
fetchJson(resolveGraphRef(basePath, graph.refs.entities)),
|
|
340
|
+
fetchJson(resolveGraphRef(basePath, graph.refs.edges)),
|
|
341
|
+
graph.refs.episodes ? fetchJson(resolveGraphRef(basePath, graph.refs.episodes)) : Promise.resolve([])
|
|
342
|
+
]).then(function (items) {
|
|
343
|
+
return Object.assign({}, graph, {
|
|
344
|
+
compact: false,
|
|
345
|
+
entities: items[0],
|
|
346
|
+
edges: items[1],
|
|
347
|
+
episodes: items[2]
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
return Promise.all([
|
|
352
|
+
fetchJson(resolveGraphRef(basePath, graph.refs.files)),
|
|
353
|
+
fetchJson(resolveGraphRef(basePath, graph.refs.symbols)),
|
|
354
|
+
fetchJson(resolveGraphRef(basePath, graph.refs.imports))
|
|
355
|
+
]).then(function (items) {
|
|
356
|
+
var fileOverrides = new Map(graph.file_parser_overrides || []);
|
|
357
|
+
var symbolOverrides = new Map(graph.symbol_parser_overrides || []);
|
|
358
|
+
return Object.assign({}, graph, {
|
|
359
|
+
compact: false,
|
|
360
|
+
files: (items[0] || []).map(function (file) {
|
|
361
|
+
return fileOverrides.has(file.path) ? Object.assign({}, file, { parser: fileOverrides.get(file.path) }) : file;
|
|
362
|
+
}),
|
|
363
|
+
symbols: (items[1] || []).map(function (symbol) {
|
|
364
|
+
return symbolOverrides.has(symbol.id) ? Object.assign({}, symbol, { parser: symbolOverrides.get(symbol.id) }) : symbol;
|
|
365
|
+
}).concat(graph.extra_symbols || []),
|
|
366
|
+
imports: (items[2] || []).concat(graph.extra_imports || [])
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
316
371
|
function fetchText(path) {
|
|
317
372
|
return fetch(path).then(function (response) {
|
|
318
373
|
if (!response.ok) throw new Error(response.status + " " + path);
|
|
@@ -379,7 +434,8 @@
|
|
|
379
434
|
graph.edges.forEach(function (edge) {
|
|
380
435
|
var from = remappedIds.get(edge.from) || edge.from;
|
|
381
436
|
var to = remappedIds.get(edge.to) || edge.to;
|
|
382
|
-
var
|
|
437
|
+
var crossesBoundary = crossesMemoryCodeBoundary(entities.get(from), entities.get(to));
|
|
438
|
+
var memoryCodeLink = Boolean(crossesBoundary && (edge.memory_code_link || isMemoryCodeRelation(edge.relation) || edge.relation === "affects_path"));
|
|
383
439
|
var id = edge.id + ":" + from + ":" + to;
|
|
384
440
|
edges.set(id, Object.assign({}, edge, {
|
|
385
441
|
id: id,
|
|
@@ -390,18 +446,131 @@
|
|
|
390
446
|
});
|
|
391
447
|
graph.episodes.forEach(function (episode) { episodes.set(episode.id, episode); });
|
|
392
448
|
});
|
|
449
|
+
addPathPrefixBridgeEdges(entities, edges);
|
|
393
450
|
return { entities: Array.from(entities.values()), edges: Array.from(edges.values()), episodes: Array.from(episodes.values()) };
|
|
394
451
|
}
|
|
395
452
|
|
|
453
|
+
function crossesMemoryCodeBoundary(from, to) {
|
|
454
|
+
if (!from || !to) return false;
|
|
455
|
+
return (from.graph_kind === "memory" && to.graph_kind === "code") ||
|
|
456
|
+
(from.graph_kind === "code" && to.graph_kind === "memory");
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function addPathPrefixBridgeEdges(entities, edges) {
|
|
460
|
+
var codeFiles = Array.from(entities.values()).filter(function (entity) {
|
|
461
|
+
return entity.graph_kind === "code" && entity.type === "file" && entity.path;
|
|
462
|
+
});
|
|
463
|
+
if (!codeFiles.length) return;
|
|
464
|
+
|
|
465
|
+
var codeDegree = new Map();
|
|
466
|
+
edges.forEach(function (edge) {
|
|
467
|
+
var from = entities.get(edge.from);
|
|
468
|
+
var to = entities.get(edge.to);
|
|
469
|
+
if (from && from.graph_kind === "code") codeDegree.set(from.id, (codeDegree.get(from.id) || 0) + 1);
|
|
470
|
+
if (to && to.graph_kind === "code") codeDegree.set(to.id, (codeDegree.get(to.id) || 0) + 1);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
var total = 0;
|
|
474
|
+
Array.from(edges.values()).filter(function (edge) {
|
|
475
|
+
return edge.relation === "affects_path";
|
|
476
|
+
}).forEach(function (edge) {
|
|
477
|
+
if (total >= PATH_BRIDGE_EDGE_LIMIT_TOTAL) return;
|
|
478
|
+
var memoryEntity = entities.get(edge.from);
|
|
479
|
+
var pathEntity = entities.get(edge.to);
|
|
480
|
+
if (!memoryEntity || !pathEntity || memoryEntity.graph_kind !== "memory") return;
|
|
481
|
+
if (pathEntity.graph_kind === "code") return;
|
|
482
|
+
|
|
483
|
+
var memoryPath = entityPathValue(pathEntity);
|
|
484
|
+
var matches = codeFiles.filter(function (file) {
|
|
485
|
+
return fileMatchesMemoryPath(file.path, memoryPath);
|
|
486
|
+
}).sort(function (a, b) {
|
|
487
|
+
return pathBridgeFileScore(b, memoryPath, codeDegree) - pathBridgeFileScore(a, memoryPath, codeDegree) ||
|
|
488
|
+
String(a.path || "").localeCompare(String(b.path || ""));
|
|
489
|
+
}).slice(0, PATH_BRIDGE_EDGE_LIMIT_PER_PATH);
|
|
490
|
+
|
|
491
|
+
matches.forEach(function (file) {
|
|
492
|
+
if (total >= PATH_BRIDGE_EDGE_LIMIT_TOTAL) return;
|
|
493
|
+
var id = "path_bridge:" + edge.id + ":" + file.id;
|
|
494
|
+
if (edges.has(id)) return;
|
|
495
|
+
edges.set(id, {
|
|
496
|
+
id: id,
|
|
497
|
+
from: edge.from,
|
|
498
|
+
to: file.id,
|
|
499
|
+
relation: "affects_code_path",
|
|
500
|
+
fact: (memoryEntity.name || "Memory") + " applies to code under " + (memoryPath || "repo root") + ": " + file.path + ".",
|
|
501
|
+
confidence: Math.min(Number(edge.confidence || 0.7), 0.75),
|
|
502
|
+
evidence: edge.evidence || [],
|
|
503
|
+
commit: edge.commit,
|
|
504
|
+
source: "viewer_path_bridge",
|
|
505
|
+
graph_kind: "memory",
|
|
506
|
+
memory_code_link: true
|
|
507
|
+
});
|
|
508
|
+
total += 1;
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function entityPathValue(entity) {
|
|
514
|
+
var candidates = [entity.path, entity.name].concat(entity.aliases || [], entity.id || []);
|
|
515
|
+
for (var index = 0; index < candidates.length; index += 1) {
|
|
516
|
+
var normalized = normalizeRepoPath(candidates[index]);
|
|
517
|
+
if (normalized || normalized === "") return normalized;
|
|
518
|
+
}
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function normalizeRepoPath(value) {
|
|
523
|
+
if (value === null || value === undefined) return null;
|
|
524
|
+
var text = String(value).trim();
|
|
525
|
+
if (!text) return null;
|
|
526
|
+
text = text.replace(/^path:/, "").replace(/^file:/, "").replace(/\\/g, "/");
|
|
527
|
+
text = text.replace(/^\.\//, "").replace(/\/+/g, "/").replace(/\/$/, "");
|
|
528
|
+
if ([".", "/", "root"].indexOf(text.toLowerCase()) !== -1) return "";
|
|
529
|
+
return text;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function fileMatchesMemoryPath(filePath, memoryPath) {
|
|
533
|
+
var file = normalizeRepoPath(filePath);
|
|
534
|
+
if (file === null || memoryPath === null) return false;
|
|
535
|
+
if (memoryPath === "") return true;
|
|
536
|
+
return file === memoryPath || file.indexOf(memoryPath + "/") === 0;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function pathBridgeFileScore(file, memoryPath, codeDegree) {
|
|
540
|
+
var path = String(file.path || "");
|
|
541
|
+
var lower = path.toLowerCase();
|
|
542
|
+
var score = (codeDegree.get(file.id) || 0) * 6;
|
|
543
|
+
if (normalizeRepoPath(path) === memoryPath) score += 90;
|
|
544
|
+
if (/^(readme|package|agents|claude)\./i.test(path.split("/").pop() || "")) score += 28;
|
|
545
|
+
if (["source", "test"].indexOf(file.kind) !== -1) score += 14;
|
|
546
|
+
if (lower.indexOf(".agent_memory/") !== -1 || lower.indexOf("node_modules/") !== -1 || lower.indexOf("dist/") !== -1 || lower.indexOf("build/") !== -1) score -= 80;
|
|
547
|
+
score -= path.split("/").length * 2;
|
|
548
|
+
score -= Math.min(20, path.length / 60);
|
|
549
|
+
return score;
|
|
550
|
+
}
|
|
551
|
+
|
|
396
552
|
function canonicalEntityId(entity, aliasToCodeId) {
|
|
397
553
|
if (!entity) return "";
|
|
398
554
|
if (entity.graph_kind === "memory" && ["symbol", "test", "route", "file", "path"].indexOf(entity.type) !== -1) {
|
|
399
|
-
var
|
|
555
|
+
var candidates = [entity.id, entity.name].concat(entity.aliases || []).filter(function (value) {
|
|
556
|
+
return canonicalAliasMatchesEntityType(value, entity.type);
|
|
557
|
+
});
|
|
558
|
+
var alias = candidates.find(function (value) { return aliasToCodeId.has(value); });
|
|
400
559
|
if (alias) return aliasToCodeId.get(alias);
|
|
401
560
|
}
|
|
402
561
|
return aliasToCodeId.get(entity.id) || entity.id;
|
|
403
562
|
}
|
|
404
563
|
|
|
564
|
+
function canonicalAliasMatchesEntityType(value, type) {
|
|
565
|
+
if (!value) return false;
|
|
566
|
+
var text = String(value);
|
|
567
|
+
if (type === "file" || type === "path") return true;
|
|
568
|
+
if (type === "route") return text.indexOf("route:") === 0;
|
|
569
|
+
if (type === "test") return text.indexOf("symbol:") === 0 || text.indexOf("test:") === 0;
|
|
570
|
+
if (type === "symbol") return text.indexOf("symbol:") === 0;
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
|
|
405
574
|
function mergeViewerEntity(existing, next) {
|
|
406
575
|
if (!existing) return next;
|
|
407
576
|
var graphKinds = unique([existing.graph_kind, next.graph_kind].concat(existing.graph_kinds || []).concat(next.graph_kinds || []));
|
|
@@ -463,6 +632,13 @@
|
|
|
463
632
|
name: file.path,
|
|
464
633
|
summary: file.kind + " file, " + file.language + ", " + file.line_count + " lines",
|
|
465
634
|
aliases: [file.hash, file.path],
|
|
635
|
+
path: file.path,
|
|
636
|
+
language: file.language,
|
|
637
|
+
parser: file.parser,
|
|
638
|
+
kind: file.kind,
|
|
639
|
+
size_bytes: file.size_bytes,
|
|
640
|
+
line_count: file.line_count,
|
|
641
|
+
hash: file.hash,
|
|
466
642
|
evidence: []
|
|
467
643
|
});
|
|
468
644
|
});
|
|
@@ -474,7 +650,15 @@
|
|
|
474
650
|
graph_kind: "code",
|
|
475
651
|
name: symbol.name,
|
|
476
652
|
summary: symbol.kind + " in " + symbol.path + ":" + symbol.line + (symbol.signature ? "\n" + symbol.signature : ""),
|
|
477
|
-
aliases: [
|
|
653
|
+
aliases: [],
|
|
654
|
+
path: symbol.path,
|
|
655
|
+
language: symbol.language,
|
|
656
|
+
parser: symbol.parser,
|
|
657
|
+
kind: symbol.kind,
|
|
658
|
+
exported: Boolean(symbol.export),
|
|
659
|
+
line: symbol.line,
|
|
660
|
+
end_line: symbol.end_line,
|
|
661
|
+
signature: symbol.signature,
|
|
478
662
|
evidence: []
|
|
479
663
|
});
|
|
480
664
|
addEdge("file:" + symbol.path, symbol.id, "defines_symbol", symbol.path + " defines " + symbol.kind + " " + symbol.name + ".", "symbols");
|
|
@@ -500,7 +684,13 @@
|
|
|
500
684
|
graph_kind: "code",
|
|
501
685
|
name: route.method + " " + route.path,
|
|
502
686
|
summary: route.framework + " route in " + route.file_path + ":" + route.line,
|
|
503
|
-
aliases: [
|
|
687
|
+
aliases: [],
|
|
688
|
+
path: route.file_path,
|
|
689
|
+
method: route.method,
|
|
690
|
+
route_path: route.path,
|
|
691
|
+
framework: route.framework,
|
|
692
|
+
handler_symbol: route.handler_symbol,
|
|
693
|
+
line: route.line,
|
|
504
694
|
evidence: []
|
|
505
695
|
});
|
|
506
696
|
addEdge("file:" + route.file_path, route.id, "defines_route", route.file_path + " defines " + route.method + " " + route.path + ".", "routes");
|
|
@@ -538,7 +728,7 @@
|
|
|
538
728
|
return edge.relation || "related";
|
|
539
729
|
})));
|
|
540
730
|
if (state.edges.some(function (edge) { return edge.memory_code_link; })) {
|
|
541
|
-
els.relationFilter.appendChild(new Option("Memory
|
|
731
|
+
els.relationFilter.appendChild(new Option("Memory <-> Code only", "__memory_code__"));
|
|
542
732
|
}
|
|
543
733
|
}
|
|
544
734
|
|
|
@@ -680,8 +870,8 @@
|
|
|
680
870
|
displayName(state.entityById.get(a)).localeCompare(displayName(state.entityById.get(b)));
|
|
681
871
|
});
|
|
682
872
|
entities = options.mode === "combined"
|
|
683
|
-
? balancedSignalEntities(ranked, options.maxNodes)
|
|
684
|
-
:
|
|
873
|
+
? balancedSignalEntities(ranked, edges, options.maxNodes)
|
|
874
|
+
: connectedSignalEntities(ranked, edges, options.maxNodes);
|
|
685
875
|
if (state.selected && state.selected.kind === "entity") entities.add(state.selected.id);
|
|
686
876
|
edges = edgesWithVisibleEndpoints(edges, entities);
|
|
687
877
|
}
|
|
@@ -694,10 +884,78 @@
|
|
|
694
884
|
}
|
|
695
885
|
}
|
|
696
886
|
|
|
697
|
-
return { entities: entities, edges: edgesWithVisibleEndpoints(edges, entities) };
|
|
887
|
+
return { entities: entities, edges: capVisibleEdges(edgesWithVisibleEndpoints(edges, entities), entities, options) };
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
function capVisibleEdges(edgeIds, entityIds, options) {
|
|
891
|
+
var maxEdges = clamp(Math.round((options.maxNodes || 90) * VISIBLE_EDGE_MULTIPLIER), VISIBLE_EDGE_MIN, VISIBLE_EDGE_MAX);
|
|
892
|
+
if (edgeIds.size <= maxEdges) return edgeIds;
|
|
893
|
+
|
|
894
|
+
var sorted = Array.from(edgeIds).sort(function (a, b) {
|
|
895
|
+
return edgeDisplayImportance(state.edgeById.get(b), entityIds) - edgeDisplayImportance(state.edgeById.get(a), entityIds) ||
|
|
896
|
+
String(a).localeCompare(String(b));
|
|
897
|
+
});
|
|
898
|
+
if (options.mode !== "combined" || options.relation || options.query.active) {
|
|
899
|
+
return new Set(sorted.slice(0, maxEdges));
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
var result = new Set();
|
|
903
|
+
var codeEdges = [];
|
|
904
|
+
var memoryCodeEdges = [];
|
|
905
|
+
var memoryEdges = [];
|
|
906
|
+
var otherEdges = [];
|
|
907
|
+
sorted.forEach(function (id) {
|
|
908
|
+
var edge = state.edgeById.get(id);
|
|
909
|
+
var from = edge && state.entityById.get(edge.from);
|
|
910
|
+
var to = edge && state.entityById.get(edge.to);
|
|
911
|
+
if (!edge || !from || !to) return;
|
|
912
|
+
if (isMemoryCodeEdge(edge)) memoryCodeEdges.push(id);
|
|
913
|
+
else if (from.graph_kind === "code" && to.graph_kind === "code") codeEdges.push(id);
|
|
914
|
+
else if (from.graph_kind === "memory" && to.graph_kind === "memory") memoryEdges.push(id);
|
|
915
|
+
else otherEdges.push(id);
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
var codeBudget = Math.round(maxEdges * 0.30);
|
|
919
|
+
var memoryCodeBudget = Math.round(maxEdges * 0.50);
|
|
920
|
+
var memoryBudget = Math.round(maxEdges * 0.14);
|
|
921
|
+
takeEdges(result, codeEdges, codeBudget);
|
|
922
|
+
takeEdges(result, memoryCodeEdges, memoryCodeBudget);
|
|
923
|
+
takeEdges(result, memoryEdges, memoryBudget);
|
|
924
|
+
takeEdges(result, otherEdges, maxEdges - result.size);
|
|
925
|
+
takeEdges(result, sorted, maxEdges - result.size);
|
|
926
|
+
return result;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function takeEdges(result, ids, count) {
|
|
930
|
+
var target = result.size + Math.max(0, count);
|
|
931
|
+
ids.forEach(function (id) {
|
|
932
|
+
if (result.size < target) result.add(id);
|
|
933
|
+
});
|
|
698
934
|
}
|
|
699
935
|
|
|
700
|
-
function
|
|
936
|
+
function edgeDisplayImportance(edge, entityIds) {
|
|
937
|
+
if (!edge) return -1000;
|
|
938
|
+
var from = state.entityById.get(edge.from);
|
|
939
|
+
var to = state.entityById.get(edge.to);
|
|
940
|
+
var score = entityImportance(from) + entityImportance(to);
|
|
941
|
+
var relation = String(edge.relation || "");
|
|
942
|
+
if (state.selected && state.selected.kind === "edge" && state.selected.id === edge.id) score += 10000;
|
|
943
|
+
if (state.selected && state.selected.kind === "entity" && (state.selected.id === edge.from || state.selected.id === edge.to)) score += 1600;
|
|
944
|
+
if (isMemoryCodeEdge(edge)) {
|
|
945
|
+
if (relation === "affects_code_path") score += 520;
|
|
946
|
+
else if (["fixes_symbol", "verified_by_test"].indexOf(relation) !== -1) score += 420;
|
|
947
|
+
else if (["explains_symbol", "informs_symbol", "applies_to_route"].indexOf(relation) !== -1) score += 300;
|
|
948
|
+
else score += 120;
|
|
949
|
+
} else if (["defines_symbol", "calls", "covers", "imports", "defines_route", "handled_by"].indexOf(relation) !== -1) {
|
|
950
|
+
score += 360;
|
|
951
|
+
} else if (["contains_memory", "has_type", "mentions_tag", "verified_by"].indexOf(relation) !== -1) {
|
|
952
|
+
score += 160;
|
|
953
|
+
}
|
|
954
|
+
if (entityIds && entityIds.has(edge.from) && entityIds.has(edge.to)) score += 30;
|
|
955
|
+
return score;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
function balancedSignalEntities(rankedIds, edgeIds, maxNodes) {
|
|
701
959
|
var result = new Set();
|
|
702
960
|
var memoryIds = rankedIds.filter(function (id) {
|
|
703
961
|
var entity = state.entityById.get(id);
|
|
@@ -712,10 +970,21 @@
|
|
|
712
970
|
return entity && entity.graph_kind === "memory" && !memoryIds.includes(id);
|
|
713
971
|
});
|
|
714
972
|
|
|
715
|
-
var memoryBudget = clamp(Math.round(maxNodes * 0.
|
|
973
|
+
var memoryBudget = clamp(Math.round(maxNodes * 0.30), 14, Math.min(36, maxNodes));
|
|
716
974
|
memoryIds.slice(0, memoryBudget).forEach(function (id) { result.add(id); });
|
|
717
975
|
otherMemoryIds.slice(0, Math.max(0, Math.round(memoryBudget * 0.35))).forEach(function (id) { result.add(id); });
|
|
718
|
-
|
|
976
|
+
|
|
977
|
+
var bridgeBudget = Math.max(0, Math.round(maxNodes * 0.22));
|
|
978
|
+
memoryCodeConnectionsForIds(result).forEach(function (id) {
|
|
979
|
+
if (result.size < maxNodes && bridgeBudget > 0 && result.size < memoryBudget + bridgeBudget) result.add(id);
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
var codeBudget = Math.max(0, maxNodes - result.size);
|
|
983
|
+
connectedSignalEntities(codeIds, edgeIds, codeBudget).forEach(function (id) {
|
|
984
|
+
if (result.size < maxNodes) result.add(id);
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
memoryCodeConnectionsForIds(result).forEach(function (id) {
|
|
719
988
|
if (result.size < maxNodes) result.add(id);
|
|
720
989
|
});
|
|
721
990
|
rankedIds.forEach(function (id) {
|
|
@@ -724,6 +993,65 @@
|
|
|
724
993
|
return result;
|
|
725
994
|
}
|
|
726
995
|
|
|
996
|
+
function memoryCodeConnectionsForIds(entityIds) {
|
|
997
|
+
var connected = new Map();
|
|
998
|
+
state.edges.forEach(function (edge) {
|
|
999
|
+
if (!isMemoryCodeEdge(edge)) return;
|
|
1000
|
+
if (entityIds.has(edge.from) && !entityIds.has(edge.to)) {
|
|
1001
|
+
connected.set(edge.to, Math.max(connected.get(edge.to) || 0, memoryCodePeerImportance(edge, edge.to)));
|
|
1002
|
+
}
|
|
1003
|
+
if (entityIds.has(edge.to) && !entityIds.has(edge.from)) {
|
|
1004
|
+
connected.set(edge.from, Math.max(connected.get(edge.from) || 0, memoryCodePeerImportance(edge, edge.from)));
|
|
1005
|
+
}
|
|
1006
|
+
});
|
|
1007
|
+
return Array.from(connected.keys()).sort(function (a, b) {
|
|
1008
|
+
return (connected.get(b) || 0) - (connected.get(a) || 0) ||
|
|
1009
|
+
displayName(state.entityById.get(a)).localeCompare(displayName(state.entityById.get(b)));
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
function memoryCodePeerImportance(edge, peerId) {
|
|
1014
|
+
var peer = state.entityById.get(peerId);
|
|
1015
|
+
var score = entityImportance(peer);
|
|
1016
|
+
var relation = String(edge.relation || "");
|
|
1017
|
+
if (relation === "affects_code_path") score += 900;
|
|
1018
|
+
if (["fixes_symbol", "verified_by_test"].indexOf(relation) !== -1) score += 520;
|
|
1019
|
+
if (["explains_symbol", "informs_symbol", "applies_to_route"].indexOf(relation) !== -1) score += 360;
|
|
1020
|
+
if (peer && peer.type === "file") score += 180;
|
|
1021
|
+
return score;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
function connectedSignalEntities(rankedIds, edgeIds, maxNodes) {
|
|
1025
|
+
var result = new Set();
|
|
1026
|
+
var candidates = new Set(rankedIds);
|
|
1027
|
+
var neighbors = new Map();
|
|
1028
|
+
edgeIds.forEach(function (edgeId) {
|
|
1029
|
+
var edge = state.edgeById.get(edgeId);
|
|
1030
|
+
if (!edge || !candidates.has(edge.from) || !candidates.has(edge.to)) return;
|
|
1031
|
+
if (!neighbors.has(edge.from)) neighbors.set(edge.from, []);
|
|
1032
|
+
if (!neighbors.has(edge.to)) neighbors.set(edge.to, []);
|
|
1033
|
+
neighbors.get(edge.from).push(edge.to);
|
|
1034
|
+
neighbors.get(edge.to).push(edge.from);
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
rankedIds.forEach(function (id) {
|
|
1038
|
+
if (result.size >= maxNodes) return;
|
|
1039
|
+
result.add(id);
|
|
1040
|
+
(neighbors.get(id) || [])
|
|
1041
|
+
.sort(function (a, b) {
|
|
1042
|
+
return entityImportance(state.entityById.get(b)) - entityImportance(state.entityById.get(a)) ||
|
|
1043
|
+
displayName(state.entityById.get(a)).localeCompare(displayName(state.entityById.get(b)));
|
|
1044
|
+
})
|
|
1045
|
+
.forEach(function (peer) {
|
|
1046
|
+
if (result.size < maxNodes) result.add(peer);
|
|
1047
|
+
});
|
|
1048
|
+
});
|
|
1049
|
+
rankedIds.forEach(function (id) {
|
|
1050
|
+
if (result.size < maxNodes) result.add(id);
|
|
1051
|
+
});
|
|
1052
|
+
return result;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
727
1055
|
function focusSelection(entityIds, edgeIds, selection) {
|
|
728
1056
|
var entities = new Set();
|
|
729
1057
|
var edges = new Set();
|
|
@@ -1352,7 +1680,7 @@
|
|
|
1352
1680
|
els.selectionStatus.textContent = state.selected.kind === "entity" ? "Node" : reviewStatus(item);
|
|
1353
1681
|
|
|
1354
1682
|
if (state.selected.kind === "entity") {
|
|
1355
|
-
|
|
1683
|
+
entityDetailRows(item).forEach(function (row) { rows.appendChild(row); });
|
|
1356
1684
|
rows.appendChild(detailRow("Graph", item.graph_kind || ""));
|
|
1357
1685
|
rows.appendChild(detailRow("Aliases", Array.isArray(item.aliases) ? item.aliases.join(", ") : ""));
|
|
1358
1686
|
rows.appendChild(detailRow("Evidence", formatEvidence(item.evidence)));
|
|
@@ -1377,6 +1705,49 @@
|
|
|
1377
1705
|
renderInspectorConnections(item);
|
|
1378
1706
|
}
|
|
1379
1707
|
|
|
1708
|
+
function entityDetailRows(entity) {
|
|
1709
|
+
var rows = [detailRow("Summary", entitySummary(entity))];
|
|
1710
|
+
if (entity.graph_kind === "code") {
|
|
1711
|
+
if (entity.path) rows.push(detailRow("Path", entity.path));
|
|
1712
|
+
if (entity.language) rows.push(detailRow("Language", entity.language));
|
|
1713
|
+
if (entity.parser) rows.push(detailRow("Parser", entity.parser));
|
|
1714
|
+
if (entity.kind) rows.push(detailRow("Kind", entity.kind));
|
|
1715
|
+
if (entity.line) rows.push(detailRow("Line", String(entity.line)));
|
|
1716
|
+
if (entity.end_line) rows.push(detailRow("End line", String(entity.end_line)));
|
|
1717
|
+
if (entity.exported != null) rows.push(detailRow("Exported", entity.exported ? "yes" : "no"));
|
|
1718
|
+
if (entity.signature) rows.push(detailRow("Signature", entity.signature));
|
|
1719
|
+
if (entity.line_count) rows.push(detailRow("Lines", String(entity.line_count)));
|
|
1720
|
+
if (entity.size_bytes != null) rows.push(detailRow("Size", formatBytes(entity.size_bytes)));
|
|
1721
|
+
if (entity.hash) rows.push(detailRow("Hash", entity.hash));
|
|
1722
|
+
if (entity.method) rows.push(detailRow("Method", entity.method));
|
|
1723
|
+
if (entity.route_path) rows.push(detailRow("Route", entity.route_path));
|
|
1724
|
+
if (entity.framework) rows.push(detailRow("Framework", entity.framework));
|
|
1725
|
+
if (entity.handler_symbol) rows.push(detailRow("Handler", entity.handler_symbol));
|
|
1726
|
+
}
|
|
1727
|
+
return rows;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
function entitySummary(entity) {
|
|
1731
|
+
if (entity.summary) return entity.summary;
|
|
1732
|
+
if (entity.graph_kind === "code" && entity.type === "symbol") {
|
|
1733
|
+
return [entity.kind || "symbol", entity.path && "in " + entity.path, entity.line && "line " + entity.line].filter(Boolean).join(" ");
|
|
1734
|
+
}
|
|
1735
|
+
if (entity.graph_kind === "code" && entity.type === "file") {
|
|
1736
|
+
return [entity.kind || "file", entity.language, entity.line_count && entity.line_count + " lines"].filter(Boolean).join(", ");
|
|
1737
|
+
}
|
|
1738
|
+
if (entity.graph_kind === "code" && entity.type === "test") {
|
|
1739
|
+
return ["test", entity.path && "in " + entity.path, entity.line && "line " + entity.line].filter(Boolean).join(" ");
|
|
1740
|
+
}
|
|
1741
|
+
return displayName(entity);
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
function formatBytes(value) {
|
|
1745
|
+
var bytes = Number(value || 0);
|
|
1746
|
+
if (bytes < 1024) return bytes + " B";
|
|
1747
|
+
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
|
|
1748
|
+
return (bytes / 1024 / 1024).toFixed(1) + " MB";
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1380
1751
|
function renderInspectorConnections(item) {
|
|
1381
1752
|
if (state.selected.kind !== "entity") {
|
|
1382
1753
|
if (item && isMemoryCodeEdge(item)) {
|
|
@@ -1387,6 +1758,14 @@
|
|
|
1387
1758
|
return;
|
|
1388
1759
|
}
|
|
1389
1760
|
|
|
1761
|
+
var direct = directConnections(item.id);
|
|
1762
|
+
if (direct.length) {
|
|
1763
|
+
var directRows = direct.slice(0, INSPECTOR_CONNECTION_LIMIT).map(function (link) {
|
|
1764
|
+
return connectionText(link.edge, item, link.other);
|
|
1765
|
+
});
|
|
1766
|
+
els.selectionDetails.appendChild(detailSection("Connected Relations", directRows, direct.length - directRows.length));
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1390
1769
|
var links = memoryCodeConnections(item.id);
|
|
1391
1770
|
if (!links.length) return;
|
|
1392
1771
|
var rows = links.slice(0, INSPECTOR_CONNECTION_LIMIT).map(function (link) {
|
|
@@ -1409,6 +1788,35 @@
|
|
|
1409
1788
|
});
|
|
1410
1789
|
}
|
|
1411
1790
|
|
|
1791
|
+
function directConnections(entityId) {
|
|
1792
|
+
return state.edges
|
|
1793
|
+
.filter(function (edge) { return edge.from === entityId || edge.to === entityId; })
|
|
1794
|
+
.map(function (edge) {
|
|
1795
|
+
var otherId = edge.from === entityId ? edge.to : edge.from;
|
|
1796
|
+
return { edge: edge, other: state.entityById.get(otherId) };
|
|
1797
|
+
})
|
|
1798
|
+
.filter(function (link) { return Boolean(link.other); })
|
|
1799
|
+
.sort(function (a, b) {
|
|
1800
|
+
return connectionImportance(b) - connectionImportance(a) ||
|
|
1801
|
+
displayName(a.other).localeCompare(displayName(b.other));
|
|
1802
|
+
});
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
function splitParamValues(values) {
|
|
1806
|
+
return []
|
|
1807
|
+
.concat(values || [])
|
|
1808
|
+
.flatMap(function (value) { return String(value || "").split(","); })
|
|
1809
|
+
.map(function (value) { return value.trim(); })
|
|
1810
|
+
.filter(Boolean);
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
function applyRequestedView(view) {
|
|
1814
|
+
if (!view) return;
|
|
1815
|
+
var normalized = String(view).toLowerCase();
|
|
1816
|
+
if (["combined", "memory", "code"].indexOf(normalized) === -1) return;
|
|
1817
|
+
els.viewMode.value = normalized;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1412
1820
|
function isMemoryCodeEdge(edge) {
|
|
1413
1821
|
return Boolean(edge && (edge.memory_code_link || isMemoryCodeRelation(edge.relation)));
|
|
1414
1822
|
}
|
|
@@ -1513,11 +1921,11 @@
|
|
|
1513
1921
|
var official = state.metrics;
|
|
1514
1922
|
var metrics = official ? [
|
|
1515
1923
|
["Readiness", official.harness.readiness_score + "/100"],
|
|
1516
|
-
["Tokens Saved", official.savings ? official.savings.estimated_tokens_saved_per_recall : "n/a"],
|
|
1517
1924
|
["Code Files", official.code_graph.files],
|
|
1518
|
-
["
|
|
1519
|
-
["
|
|
1520
|
-
["
|
|
1925
|
+
["Structural Symbols", official.structural_index ? official.structural_index.symbols : official.code_graph.symbols],
|
|
1926
|
+
["Parser Coverage", official.code_graph.indexer_coverage_percent + "%"],
|
|
1927
|
+
["Cache Hits", official.structural_index ? official.structural_index.cache_hits : official.code_graph.cache_hits],
|
|
1928
|
+
["Tokens Saved", official.savings ? official.savings.estimated_tokens_saved_per_recall : "n/a"]
|
|
1521
1929
|
] : [
|
|
1522
1930
|
["Nodes", visibleEntities.length + "/" + state.entities.length],
|
|
1523
1931
|
["Relations", visibleEdges.length + "/" + state.edges.length],
|
|
@@ -1655,9 +2063,10 @@
|
|
|
1655
2063
|
var pills = official ? [
|
|
1656
2064
|
["Readiness", official.harness.readiness_score + "/100", ""],
|
|
1657
2065
|
["Pending", official.memory_graph ? String(official.memory_graph.pending_packets) : "n/a", official.memory_graph && official.memory_graph.pending_packets ? "warn" : ""],
|
|
1658
|
-
["
|
|
1659
|
-
["
|
|
1660
|
-
["Parser coverage", official.code_graph.indexer_coverage_percent + "%", "code"]
|
|
2066
|
+
["Structural", official.structural_index ? official.structural_index.files + " files" : official.code_graph.files + " files", "code"],
|
|
2067
|
+
["Code symbols", String(official.code_graph.symbols), "code"],
|
|
2068
|
+
["Parser coverage", official.code_graph.indexer_coverage_percent + "%", "code"],
|
|
2069
|
+
["Memory packets", official.memory_graph ? String(official.memory_graph.approved_packets) : "n/a", "memory"]
|
|
1661
2070
|
] : [
|
|
1662
2071
|
["Memory", String(memoryCount), "memory"],
|
|
1663
2072
|
["Code", String(codeCount), "code"],
|
|
@@ -2172,6 +2581,101 @@
|
|
|
2172
2581
|
return String(value || "unknown").toLowerCase().replace(/[^a-z0-9_-]+/g, "-");
|
|
2173
2582
|
}
|
|
2174
2583
|
|
|
2584
|
+
function testSignalVisibilityForGraph(graph, maxNodes) {
|
|
2585
|
+
var normalized = normalizeGraph(graph);
|
|
2586
|
+
return testVisibilityForNormalized(normalized, "code", maxNodes || 90);
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2589
|
+
function testCombinedVisibilityForGraphs(graphs, maxNodes) {
|
|
2590
|
+
var normalized = mergeNormalizedGraphs(graphs.map(function (graph) { return normalizeGraph(graph); }));
|
|
2591
|
+
return testVisibilityForNormalized(normalized, "combined", maxNodes || 90);
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
function testRelationVisibilityForGraphs(graphs, relation, maxNodes) {
|
|
2595
|
+
var normalized = mergeNormalizedGraphs(graphs.map(function (graph) { return normalizeGraph(graph); }));
|
|
2596
|
+
state.entities = normalized.entities;
|
|
2597
|
+
state.edges = normalized.edges;
|
|
2598
|
+
state.entityById = new Map(state.entities.map(function (entity) { return [entity.id, entity]; }));
|
|
2599
|
+
state.edgeById = new Map(state.edges.map(function (edge) { return [edge.id, edge]; }));
|
|
2600
|
+
state.degreeById = buildDegreeMap(state.edges);
|
|
2601
|
+
var edgeIds = new Set(state.edges.filter(function (edge) {
|
|
2602
|
+
return relation === "__memory_code__" ? isMemoryCodeEdge(edge) : edge.relation === relation;
|
|
2603
|
+
}).map(function (edge) { return edge.id; }));
|
|
2604
|
+
var entityIds = new Set();
|
|
2605
|
+
edgeIds.forEach(function (id) {
|
|
2606
|
+
var edge = state.edgeById.get(id);
|
|
2607
|
+
if (!edge) return;
|
|
2608
|
+
entityIds.add(edge.from);
|
|
2609
|
+
entityIds.add(edge.to);
|
|
2610
|
+
});
|
|
2611
|
+
var ranked = Array.from(entityIds).sort(function (a, b) {
|
|
2612
|
+
return entityImportance(state.entityById.get(b)) - entityImportance(state.entityById.get(a)) ||
|
|
2613
|
+
displayName(state.entityById.get(a)).localeCompare(displayName(state.entityById.get(b)));
|
|
2614
|
+
});
|
|
2615
|
+
var entities = connectedSignalEntities(ranked, edgeIds, maxNodes || 90);
|
|
2616
|
+
var edges = capVisibleEdges(edgesWithVisibleEndpoints(edgeIds, entities), entities, {
|
|
2617
|
+
maxNodes: maxNodes || 90,
|
|
2618
|
+
mode: "combined",
|
|
2619
|
+
relation: relation,
|
|
2620
|
+
query: { active: false }
|
|
2621
|
+
});
|
|
2622
|
+
return visibilityStats(entities, edges);
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
function testVisibilityForNormalized(normalized, mode, maxNodes) {
|
|
2626
|
+
state.entities = normalized.entities;
|
|
2627
|
+
state.edges = normalized.edges;
|
|
2628
|
+
state.entityById = new Map(state.entities.map(function (entity) { return [entity.id, entity]; }));
|
|
2629
|
+
state.edgeById = new Map(state.edges.map(function (edge) { return [edge.id, edge]; }));
|
|
2630
|
+
state.degreeById = buildDegreeMap(state.edges);
|
|
2631
|
+
var ranked = state.entities
|
|
2632
|
+
.filter(function (entity) { return mode === "combined" || entity.graph_kind === mode; })
|
|
2633
|
+
.map(function (entity) { return entity.id; })
|
|
2634
|
+
.sort(function (a, b) {
|
|
2635
|
+
return entityImportance(state.entityById.get(b)) - entityImportance(state.entityById.get(a)) ||
|
|
2636
|
+
displayName(state.entityById.get(a)).localeCompare(displayName(state.entityById.get(b)));
|
|
2637
|
+
});
|
|
2638
|
+
var edgeIds = new Set(state.edges.filter(function (edge) { return mode === "combined" || edge.graph_kind === mode; }).map(function (edge) { return edge.id; }));
|
|
2639
|
+
var entities = mode === "combined"
|
|
2640
|
+
? balancedSignalEntities(ranked, edgeIds, maxNodes)
|
|
2641
|
+
: connectedSignalEntities(ranked, edgeIds, maxNodes);
|
|
2642
|
+
var edges = capVisibleEdges(edgesWithVisibleEndpoints(edgeIds, entities), entities, {
|
|
2643
|
+
maxNodes: maxNodes,
|
|
2644
|
+
mode: mode,
|
|
2645
|
+
relation: "",
|
|
2646
|
+
query: { active: false }
|
|
2647
|
+
});
|
|
2648
|
+
return visibilityStats(entities, edges);
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
function visibilityStats(entities, edges) {
|
|
2652
|
+
return {
|
|
2653
|
+
entities: entities.size,
|
|
2654
|
+
edges: edges.size,
|
|
2655
|
+
memoryCodeEdges: Array.from(edges).filter(function (id) {
|
|
2656
|
+
return isMemoryCodeEdge(state.edgeById.get(id));
|
|
2657
|
+
}).length,
|
|
2658
|
+
pathBridgeEdges: Array.from(edges).filter(function (id) {
|
|
2659
|
+
var edge = state.edgeById.get(id);
|
|
2660
|
+
return edge && edge.relation === "affects_code_path";
|
|
2661
|
+
}).length,
|
|
2662
|
+
codeEdges: Array.from(edges).filter(function (id) {
|
|
2663
|
+
var edge = state.edgeById.get(id);
|
|
2664
|
+
var from = edge && state.entityById.get(edge.from);
|
|
2665
|
+
var to = edge && state.entityById.get(edge.to);
|
|
2666
|
+
return from && to && from.graph_kind === "code" && to.graph_kind === "code";
|
|
2667
|
+
}).length,
|
|
2668
|
+
memory: Array.from(entities).filter(function (id) {
|
|
2669
|
+
var entity = state.entityById.get(id);
|
|
2670
|
+
return entity && entity.graph_kind === "memory";
|
|
2671
|
+
}).length,
|
|
2672
|
+
code: Array.from(entities).filter(function (id) {
|
|
2673
|
+
var entity = state.entityById.get(id);
|
|
2674
|
+
return entity && entity.graph_kind === "code";
|
|
2675
|
+
}).length
|
|
2676
|
+
};
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2175
2679
|
function svgEl(name, attrs) {
|
|
2176
2680
|
var element = document.createElementNS("http://www.w3.org/2000/svg", name);
|
|
2177
2681
|
Object.keys(attrs || {}).forEach(function (key) {
|
|
@@ -2184,6 +2688,11 @@
|
|
|
2184
2688
|
globalThis.__KAGE_VIEWER_TEST__ = {
|
|
2185
2689
|
normalizeGraph: normalizeGraph,
|
|
2186
2690
|
normalizeCodeGraph: normalizeCodeGraph,
|
|
2691
|
+
hydrateCompactCodeGraph: hydrateCompactCodeGraph,
|
|
2692
|
+
connectedSignalEntities: connectedSignalEntities,
|
|
2693
|
+
testSignalVisibilityForGraph: testSignalVisibilityForGraph,
|
|
2694
|
+
testCombinedVisibilityForGraphs: testCombinedVisibilityForGraphs,
|
|
2695
|
+
testRelationVisibilityForGraphs: testRelationVisibilityForGraphs,
|
|
2187
2696
|
mergeNormalizedGraphs: mergeNormalizedGraphs,
|
|
2188
2697
|
isMemoryCodeRelation: isMemoryCodeRelation
|
|
2189
2698
|
};
|