fix(graph): filter dangling edges before passing to cytoscape-dagre
Cytoscape-dagre crashes with "Cannot set properties of undefined (setting 'order')" when an edge's source or target is not present in the cytoscape node set. Both backends can produce such edges in edge cases (ON_PAGE edges referencing a page_no absent from the pages list, DERIVED_FROM edges crossing document boundaries in Neo4j). When cytoscape-dagre adds these edges to its internal dagre/graphlib graph, graphlib auto-creates a skeleton node entry with undefined data; dagre's ordering phase then tries to set `.order` on that undefined object and throws. The fix builds a nodeIds Set from the returned nodes before constructing the elements array, then filters every edge so only those with a valid source AND target are forwarded to cytoscape. Dangling edges have no visual meaning anyway, so this is purely defensive. https://claude.ai/code/session_01PjZeeTBqERYDPs59RhXqCz
This commit is contained in:
parent
2807cc3aea
commit
cb2040a227
1 changed files with 18 additions and 8 deletions
|
|
@ -238,6 +238,14 @@ async function renderGraph(): Promise<void> {
|
|||
)
|
||||
parentMap.value = computedParentMap
|
||||
|
||||
// Guard against dangling edges (source/target not in the node set). Both
|
||||
// backends can emit them in edge cases — e.g. ON_PAGE edges whose page_no
|
||||
// doesn't match any returned Page node, or DERIVED_FROM edges crossing
|
||||
// document boundaries in Neo4j. Cytoscape-dagre auto-creates a skeleton
|
||||
// graphlib entry for the missing endpoint with undefined data, then crashes
|
||||
// in its ordering phase when it tries to set `.order` on that entry.
|
||||
const nodeIds = new Set(payload.value.nodes.map((n) => n.id))
|
||||
|
||||
const elements = [
|
||||
...payload.value.nodes.map((n) => ({
|
||||
data: {
|
||||
|
|
@ -256,7 +264,9 @@ async function renderGraph(): Promise<void> {
|
|||
raw: n,
|
||||
},
|
||||
})),
|
||||
...payload.value.edges.map((e) => ({
|
||||
...payload.value.edges
|
||||
.filter((e) => nodeIds.has(e.source) && nodeIds.has(e.target))
|
||||
.map((e) => ({
|
||||
data: {
|
||||
id: e.id,
|
||||
source: e.source,
|
||||
|
|
|
|||
Loading…
Reference in a new issue