@statelyai/graph 0.1.0
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 +191 -0
- package/dist/adjacency-list-CXpOCibq.mjs +49 -0
- package/dist/adjacency-list-DW-lAUe8.d.mts +10 -0
- package/dist/algorithms-R35X6ro4.mjs +1197 -0
- package/dist/algorithms.d.mts +82 -0
- package/dist/algorithms.mjs +3 -0
- package/dist/dot-BRtq3e3c.mjs +59 -0
- package/dist/dot-HmJeUMsj.d.mts +6 -0
- package/dist/edge-list-BRujEnnU.mjs +39 -0
- package/dist/edge-list-CJmfoNu2.d.mts +10 -0
- package/dist/formats/adjacency-list.d.mts +2 -0
- package/dist/formats/adjacency-list.mjs +3 -0
- package/dist/formats/dot.d.mts +2 -0
- package/dist/formats/dot.mjs +3 -0
- package/dist/formats/edge-list.d.mts +2 -0
- package/dist/formats/edge-list.mjs +3 -0
- package/dist/formats/graphml.d.mts +2 -0
- package/dist/formats/graphml.mjs +3 -0
- package/dist/graphml-CMjPzSfY.d.mts +7 -0
- package/dist/graphml-CUTNRXqd.mjs +240 -0
- package/dist/index.d.mts +142 -0
- package/dist/index.mjs +356 -0
- package/dist/indexing-BHg1VhqN.mjs +93 -0
- package/dist/queries.d.mts +37 -0
- package/dist/queries.mjs +221 -0
- package/dist/schemas.d.mts +46 -0
- package/dist/schemas.mjs +30 -0
- package/dist/types-XV3S5Jnh.d.mts +219 -0
- package/package.json +77 -0
- package/schemas/edge.schema.json +32 -0
- package/schemas/graph.schema.json +96 -0
- package/schemas/node.schema.json +35 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { o as invalidateIndex, t as getIndex } from "./indexing-BHg1VhqN.mjs";
|
|
2
|
+
import { A as createGraph, B as updateEntities, C as isAcyclic, D as addEdge, E as GraphInstance, F as getEdge, I as getNode, L as hasEdge, M as deleteEdge, N as deleteEntities, O as addEntities, P as deleteNode, R as hasNode, S as hasPath, T as isTree, V as updateNode, _ as getShortestPaths, a as genPreorders, b as getStronglyConnectedComponents, c as getAllPairsShortestPaths, d as getMinimumSpanningTree, f as getPostorder, g as getShortestPath, h as getPreorders, i as genPostorders, j as createVisualGraph, k as addNode, l as getConnectedComponents, m as getPreorder, n as dfs, o as genShortestPaths, p as getPostorders, r as genCycles, s as genSimplePaths, t as bfs, u as getCycles, v as getSimplePath, w as isConnected, x as getTopologicalSort, y as getSimplePaths, z as updateEdge } from "./algorithms-R35X6ro4.mjs";
|
|
3
|
+
import { EdgeSchema, GraphSchema, NodeSchema } from "./schemas.mjs";
|
|
4
|
+
import { getAncestors, getChildren, getDegree, getDepth, getDescendants, getEdgeBetween, getEdgesOf, getInDegree, getInEdges, getLCA, getNeighbors, getOutDegree, getOutEdges, getParent, getPredecessors, getRoots, getSiblings, getSinks, getSources, getSuccessors, isCompound, isLeaf } from "./queries.mjs";
|
|
5
|
+
import { n as toGraphML, t as fromGraphML } from "./graphml-CUTNRXqd.mjs";
|
|
6
|
+
import { t as toDOT } from "./dot-BRtq3e3c.mjs";
|
|
7
|
+
import { n as toAdjacencyList, t as fromAdjacencyList } from "./adjacency-list-CXpOCibq.mjs";
|
|
8
|
+
import { n as toEdgeList, t as fromEdgeList } from "./edge-list-BRujEnnU.mjs";
|
|
9
|
+
|
|
10
|
+
//#region src/diff.ts
|
|
11
|
+
function nodeToConfig(node) {
|
|
12
|
+
const config = { id: node.id };
|
|
13
|
+
if (node.parentId !== null) config.parentId = node.parentId;
|
|
14
|
+
if (node.initialNodeId !== null) config.initialNodeId = node.initialNodeId;
|
|
15
|
+
if (node.label !== "") config.label = node.label;
|
|
16
|
+
if (node.data !== void 0) config.data = node.data;
|
|
17
|
+
if (node.x !== void 0) config.x = node.x;
|
|
18
|
+
if (node.y !== void 0) config.y = node.y;
|
|
19
|
+
if (node.width !== void 0) config.width = node.width;
|
|
20
|
+
if (node.height !== void 0) config.height = node.height;
|
|
21
|
+
if (node.shape !== void 0) config.shape = node.shape;
|
|
22
|
+
if (node.color !== void 0) config.color = node.color;
|
|
23
|
+
if (node.style !== void 0) config.style = node.style;
|
|
24
|
+
return config;
|
|
25
|
+
}
|
|
26
|
+
function edgeToConfig(edge) {
|
|
27
|
+
const config = {
|
|
28
|
+
id: edge.id,
|
|
29
|
+
sourceId: edge.sourceId,
|
|
30
|
+
targetId: edge.targetId
|
|
31
|
+
};
|
|
32
|
+
if (edge.label !== "") config.label = edge.label;
|
|
33
|
+
if (edge.data !== void 0) config.data = edge.data;
|
|
34
|
+
if (edge.x !== void 0) config.x = edge.x;
|
|
35
|
+
if (edge.y !== void 0) config.y = edge.y;
|
|
36
|
+
if (edge.width !== void 0) config.width = edge.width;
|
|
37
|
+
if (edge.height !== void 0) config.height = edge.height;
|
|
38
|
+
if (edge.color !== void 0) config.color = edge.color;
|
|
39
|
+
if (edge.style !== void 0) config.style = edge.style;
|
|
40
|
+
return config;
|
|
41
|
+
}
|
|
42
|
+
/** Shallow-compare two values, returning true if they differ. */
|
|
43
|
+
function differs(a, b) {
|
|
44
|
+
if (a === b) return false;
|
|
45
|
+
if (a == null || b == null) return a !== b;
|
|
46
|
+
if (typeof a === "object" && typeof b === "object") return JSON.stringify(a) !== JSON.stringify(b);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
const NODE_COMPARE_KEYS = [
|
|
50
|
+
"parentId",
|
|
51
|
+
"initialNodeId",
|
|
52
|
+
"label",
|
|
53
|
+
"data",
|
|
54
|
+
"x",
|
|
55
|
+
"y",
|
|
56
|
+
"width",
|
|
57
|
+
"height",
|
|
58
|
+
"shape",
|
|
59
|
+
"color",
|
|
60
|
+
"style"
|
|
61
|
+
];
|
|
62
|
+
const EDGE_COMPARE_KEYS = [
|
|
63
|
+
"sourceId",
|
|
64
|
+
"targetId",
|
|
65
|
+
"label",
|
|
66
|
+
"data",
|
|
67
|
+
"x",
|
|
68
|
+
"y",
|
|
69
|
+
"width",
|
|
70
|
+
"height",
|
|
71
|
+
"color",
|
|
72
|
+
"style"
|
|
73
|
+
];
|
|
74
|
+
/** Compute a structured diff from graph `a` to graph `b` by matching IDs. */
|
|
75
|
+
function getDiff(a, b) {
|
|
76
|
+
const aNodeMap = new Map(a.nodes.map((n) => [n.id, n]));
|
|
77
|
+
const bNodeMap = new Map(b.nodes.map((n) => [n.id, n]));
|
|
78
|
+
const aEdgeMap = new Map(a.edges.map((e) => [e.id, e]));
|
|
79
|
+
const bEdgeMap = new Map(b.edges.map((e) => [e.id, e]));
|
|
80
|
+
const diff = {
|
|
81
|
+
nodes: {
|
|
82
|
+
added: [],
|
|
83
|
+
removed: [],
|
|
84
|
+
updated: []
|
|
85
|
+
},
|
|
86
|
+
edges: {
|
|
87
|
+
added: [],
|
|
88
|
+
removed: [],
|
|
89
|
+
updated: []
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
for (const [id, nodeB] of bNodeMap) {
|
|
93
|
+
const nodeA = aNodeMap.get(id);
|
|
94
|
+
if (!nodeA) diff.nodes.added.push(nodeToConfig(nodeB));
|
|
95
|
+
else {
|
|
96
|
+
const oldPartial = {};
|
|
97
|
+
const newPartial = {};
|
|
98
|
+
for (const key of NODE_COMPARE_KEYS) if (differs(nodeA[key], nodeB[key])) {
|
|
99
|
+
oldPartial[key] = nodeA[key];
|
|
100
|
+
newPartial[key] = nodeB[key];
|
|
101
|
+
}
|
|
102
|
+
if (Object.keys(oldPartial).length > 0) diff.nodes.updated.push({
|
|
103
|
+
id,
|
|
104
|
+
old: oldPartial,
|
|
105
|
+
new: newPartial
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const [id, nodeA] of aNodeMap) if (!bNodeMap.has(id)) diff.nodes.removed.push(nodeToConfig(nodeA));
|
|
110
|
+
for (const [id, edgeB] of bEdgeMap) {
|
|
111
|
+
const edgeA = aEdgeMap.get(id);
|
|
112
|
+
if (!edgeA) diff.edges.added.push(edgeToConfig(edgeB));
|
|
113
|
+
else {
|
|
114
|
+
const oldPartial = {};
|
|
115
|
+
const newPartial = {};
|
|
116
|
+
for (const key of EDGE_COMPARE_KEYS) if (differs(edgeA[key], edgeB[key])) {
|
|
117
|
+
oldPartial[key] = edgeA[key];
|
|
118
|
+
newPartial[key] = edgeB[key];
|
|
119
|
+
}
|
|
120
|
+
if (Object.keys(oldPartial).length > 0) diff.edges.updated.push({
|
|
121
|
+
id,
|
|
122
|
+
old: oldPartial,
|
|
123
|
+
new: newPartial
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
for (const [id, edgeA] of aEdgeMap) if (!bEdgeMap.has(id)) diff.edges.removed.push(edgeToConfig(edgeA));
|
|
128
|
+
return diff;
|
|
129
|
+
}
|
|
130
|
+
/** Check if a diff has zero changes. */
|
|
131
|
+
function isEmptyDiff(diff) {
|
|
132
|
+
return diff.nodes.added.length === 0 && diff.nodes.removed.length === 0 && diff.nodes.updated.length === 0 && diff.edges.added.length === 0 && diff.edges.removed.length === 0 && diff.edges.updated.length === 0;
|
|
133
|
+
}
|
|
134
|
+
/** Invert a diff: swap added↔removed, swap old↔new in updates. */
|
|
135
|
+
function invertDiff(diff) {
|
|
136
|
+
return {
|
|
137
|
+
nodes: {
|
|
138
|
+
added: diff.nodes.removed,
|
|
139
|
+
removed: diff.nodes.added,
|
|
140
|
+
updated: diff.nodes.updated.map((c) => ({
|
|
141
|
+
id: c.id,
|
|
142
|
+
old: c.new,
|
|
143
|
+
new: c.old
|
|
144
|
+
}))
|
|
145
|
+
},
|
|
146
|
+
edges: {
|
|
147
|
+
added: diff.edges.removed,
|
|
148
|
+
removed: diff.edges.added,
|
|
149
|
+
updated: diff.edges.updated.map((c) => ({
|
|
150
|
+
id: c.id,
|
|
151
|
+
old: c.new,
|
|
152
|
+
new: c.old
|
|
153
|
+
}))
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Compute an ordered patch list from graph `a` to graph `b`.
|
|
159
|
+
* Order: delete edges → delete nodes → add nodes → add edges → update nodes → update edges.
|
|
160
|
+
*/
|
|
161
|
+
function getPatches(a, b) {
|
|
162
|
+
return toPatches(getDiff(a, b));
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* **Mutable.** Apply patches to a graph in order.
|
|
166
|
+
* Delegates to addNode/deleteNode/updateNode/addEdge/deleteEdge/updateEdge.
|
|
167
|
+
*/
|
|
168
|
+
function applyPatches(graph, patches) {
|
|
169
|
+
for (const patch of patches) switch (patch.op) {
|
|
170
|
+
case "addNode":
|
|
171
|
+
addNode(graph, patch.node);
|
|
172
|
+
break;
|
|
173
|
+
case "deleteNode":
|
|
174
|
+
deleteNode(graph, patch.id);
|
|
175
|
+
break;
|
|
176
|
+
case "updateNode":
|
|
177
|
+
updateNode(graph, patch.id, patch.data);
|
|
178
|
+
break;
|
|
179
|
+
case "addEdge":
|
|
180
|
+
addEdge(graph, patch.edge);
|
|
181
|
+
break;
|
|
182
|
+
case "deleteEdge":
|
|
183
|
+
deleteEdge(graph, patch.id);
|
|
184
|
+
break;
|
|
185
|
+
case "updateEdge":
|
|
186
|
+
updateEdge(graph, patch.id, patch.data);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Flatten a structured diff into an ordered patch list.
|
|
192
|
+
* Order: add nodes → update edges → delete edges → delete nodes → add edges → update nodes.
|
|
193
|
+
* This avoids cascading deletes removing edges that are being updated,
|
|
194
|
+
* and ensures new nodes exist before edges reference them.
|
|
195
|
+
*/
|
|
196
|
+
function toPatches(diff) {
|
|
197
|
+
const patches = [];
|
|
198
|
+
for (const node of diff.nodes.added) patches.push({
|
|
199
|
+
op: "addNode",
|
|
200
|
+
node
|
|
201
|
+
});
|
|
202
|
+
for (const change of diff.edges.updated) {
|
|
203
|
+
const data = {};
|
|
204
|
+
for (const [key, value] of Object.entries(change.new)) data[key] = value;
|
|
205
|
+
patches.push({
|
|
206
|
+
op: "updateEdge",
|
|
207
|
+
id: change.id,
|
|
208
|
+
data
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
for (const edge of diff.edges.removed) patches.push({
|
|
212
|
+
op: "deleteEdge",
|
|
213
|
+
id: edge.id
|
|
214
|
+
});
|
|
215
|
+
for (const node of diff.nodes.removed) patches.push({
|
|
216
|
+
op: "deleteNode",
|
|
217
|
+
id: node.id
|
|
218
|
+
});
|
|
219
|
+
for (const edge of diff.edges.added) patches.push({
|
|
220
|
+
op: "addEdge",
|
|
221
|
+
edge
|
|
222
|
+
});
|
|
223
|
+
for (const change of diff.nodes.updated) {
|
|
224
|
+
const data = {};
|
|
225
|
+
for (const [key, value] of Object.entries(change.new)) data[key] = value;
|
|
226
|
+
patches.push({
|
|
227
|
+
op: "updateNode",
|
|
228
|
+
id: change.id,
|
|
229
|
+
data
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
return patches;
|
|
233
|
+
}
|
|
234
|
+
/** Group a patch list into a structured diff. */
|
|
235
|
+
function toDiff(patches) {
|
|
236
|
+
const diff = {
|
|
237
|
+
nodes: {
|
|
238
|
+
added: [],
|
|
239
|
+
removed: [],
|
|
240
|
+
updated: []
|
|
241
|
+
},
|
|
242
|
+
edges: {
|
|
243
|
+
added: [],
|
|
244
|
+
removed: [],
|
|
245
|
+
updated: []
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
for (const patch of patches) switch (patch.op) {
|
|
249
|
+
case "addNode":
|
|
250
|
+
diff.nodes.added.push(patch.node);
|
|
251
|
+
break;
|
|
252
|
+
case "deleteNode":
|
|
253
|
+
diff.nodes.removed.push({ id: patch.id });
|
|
254
|
+
break;
|
|
255
|
+
case "updateNode": {
|
|
256
|
+
const newPartial = {};
|
|
257
|
+
for (const [key, value] of Object.entries(patch.data)) newPartial[key] = value;
|
|
258
|
+
diff.nodes.updated.push({
|
|
259
|
+
id: patch.id,
|
|
260
|
+
old: {},
|
|
261
|
+
new: newPartial
|
|
262
|
+
});
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
case "addEdge":
|
|
266
|
+
diff.edges.added.push(patch.edge);
|
|
267
|
+
break;
|
|
268
|
+
case "deleteEdge":
|
|
269
|
+
diff.edges.removed.push({ id: patch.id });
|
|
270
|
+
break;
|
|
271
|
+
case "updateEdge": {
|
|
272
|
+
const newPartial = {};
|
|
273
|
+
for (const [key, value] of Object.entries(patch.data)) newPartial[key] = value;
|
|
274
|
+
diff.edges.updated.push({
|
|
275
|
+
id: patch.id,
|
|
276
|
+
old: {},
|
|
277
|
+
new: newPartial
|
|
278
|
+
});
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return diff;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
//#endregion
|
|
286
|
+
//#region src/transforms.ts
|
|
287
|
+
/**
|
|
288
|
+
* Flattens a hierarchical graph into a flat graph with only leaf nodes.
|
|
289
|
+
*
|
|
290
|
+
* - Edges targeting a compound node resolve to its initial child (recursively).
|
|
291
|
+
* - Edges originating from a compound node expand to all leaf descendants.
|
|
292
|
+
* - Only leaf nodes (nodes with no children) appear in the result.
|
|
293
|
+
* - Duplicate edges (same source + target) are deduplicated.
|
|
294
|
+
*/
|
|
295
|
+
function flatten(graph) {
|
|
296
|
+
const idx = getIndex(graph);
|
|
297
|
+
const leaves = /* @__PURE__ */ new Set();
|
|
298
|
+
for (const node of graph.nodes) if ((idx.childNodes.get(node.id) ?? []).length === 0) leaves.add(node.id);
|
|
299
|
+
function resolveInitial(nodeId) {
|
|
300
|
+
if (leaves.has(nodeId)) return nodeId;
|
|
301
|
+
const ni = idx.nodeById.get(nodeId);
|
|
302
|
+
if (ni === void 0) return null;
|
|
303
|
+
const node = graph.nodes[ni];
|
|
304
|
+
if (node.initialNodeId) return resolveInitial(node.initialNodeId);
|
|
305
|
+
const childIds = idx.childNodes.get(nodeId) ?? [];
|
|
306
|
+
if (childIds.length > 0) return resolveInitial(childIds[0]);
|
|
307
|
+
return nodeId;
|
|
308
|
+
}
|
|
309
|
+
function getLeafDescendants(nodeId) {
|
|
310
|
+
if (leaves.has(nodeId)) return [nodeId];
|
|
311
|
+
const result = [];
|
|
312
|
+
const collect = (id) => {
|
|
313
|
+
const childIds = idx.childNodes.get(id) ?? [];
|
|
314
|
+
for (const childId of childIds) if (leaves.has(childId)) result.push(childId);
|
|
315
|
+
else collect(childId);
|
|
316
|
+
};
|
|
317
|
+
collect(nodeId);
|
|
318
|
+
return result;
|
|
319
|
+
}
|
|
320
|
+
const edgeSeen = /* @__PURE__ */ new Set();
|
|
321
|
+
const flatEdges = [];
|
|
322
|
+
for (const edge of graph.edges) {
|
|
323
|
+
const sources = leaves.has(edge.sourceId) ? [edge.sourceId] : getLeafDescendants(edge.sourceId);
|
|
324
|
+
const target = leaves.has(edge.targetId) ? edge.targetId : resolveInitial(edge.targetId);
|
|
325
|
+
if (target === null) continue;
|
|
326
|
+
for (const source of sources) {
|
|
327
|
+
if (source === target) continue;
|
|
328
|
+
const key = `${source}->${target}`;
|
|
329
|
+
if (edgeSeen.has(key)) continue;
|
|
330
|
+
edgeSeen.add(key);
|
|
331
|
+
flatEdges.push({
|
|
332
|
+
type: "edge",
|
|
333
|
+
id: `${edge.id}:${source}->${target}`,
|
|
334
|
+
sourceId: source,
|
|
335
|
+
targetId: target,
|
|
336
|
+
label: edge.label,
|
|
337
|
+
data: edge.data
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
const leafNodes = graph.nodes.filter((n) => leaves.has(n.id)).map((n) => ({
|
|
342
|
+
id: n.id,
|
|
343
|
+
label: n.label,
|
|
344
|
+
data: n.data
|
|
345
|
+
}));
|
|
346
|
+
return createGraph({
|
|
347
|
+
id: graph.id,
|
|
348
|
+
type: graph.type,
|
|
349
|
+
nodes: leafNodes,
|
|
350
|
+
edges: flatEdges,
|
|
351
|
+
data: graph.data
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
//#endregion
|
|
356
|
+
export { EdgeSchema, GraphInstance, GraphSchema, NodeSchema, addEdge, addEntities, addNode, applyPatches, bfs, createGraph, createVisualGraph, deleteEdge, deleteEntities, deleteNode, dfs, flatten, fromAdjacencyList, fromEdgeList, fromGraphML, genCycles, genPostorders, genPreorders, genShortestPaths, genSimplePaths, getAllPairsShortestPaths, getAncestors, getChildren, getConnectedComponents, getCycles, getDegree, getDepth, getDescendants, getDiff, getEdge, getEdgeBetween, getEdgesOf, getInDegree, getInEdges, getLCA, getMinimumSpanningTree, getNeighbors, getNode, getOutDegree, getOutEdges, getParent, getPatches, getPostorder, getPostorders, getPredecessors, getPreorder, getPreorders, getRoots, getShortestPath, getShortestPaths, getSiblings, getSimplePath, getSimplePaths, getSinks, getSources, getStronglyConnectedComponents, getSuccessors, getTopologicalSort, hasEdge, hasNode, hasPath, invalidateIndex, invertDiff, isAcyclic, isCompound, isConnected, isEmptyDiff, isLeaf, isTree, toAdjacencyList, toDOT, toDiff, toEdgeList, toGraphML, toPatches, updateEdge, updateEntities, updateNode };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
//#region src/indexing.ts
|
|
2
|
+
const indexes = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
/** Get or lazily build the index for a graph. */
|
|
4
|
+
function getIndex(graph) {
|
|
5
|
+
let idx = indexes.get(graph);
|
|
6
|
+
if (!idx || idx.nodeCount !== graph.nodes.length || idx.edgeCount !== graph.edges.length) {
|
|
7
|
+
idx = buildIndex(graph);
|
|
8
|
+
indexes.set(graph, idx);
|
|
9
|
+
}
|
|
10
|
+
return idx;
|
|
11
|
+
}
|
|
12
|
+
/** Clear the cached index. Call this if you mutate graph.nodes/edges directly. */
|
|
13
|
+
function invalidateIndex(graph) {
|
|
14
|
+
indexes.delete(graph);
|
|
15
|
+
}
|
|
16
|
+
function buildIndex(graph) {
|
|
17
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
18
|
+
const edgeById = /* @__PURE__ */ new Map();
|
|
19
|
+
const outEdges = /* @__PURE__ */ new Map();
|
|
20
|
+
const inEdges = /* @__PURE__ */ new Map();
|
|
21
|
+
const childNodes = /* @__PURE__ */ new Map();
|
|
22
|
+
for (let i = 0; i < graph.nodes.length; i++) {
|
|
23
|
+
const n = graph.nodes[i];
|
|
24
|
+
nodeById.set(n.id, i);
|
|
25
|
+
outEdges.set(n.id, []);
|
|
26
|
+
inEdges.set(n.id, []);
|
|
27
|
+
const parent = n.parentId;
|
|
28
|
+
if (!childNodes.has(parent)) childNodes.set(parent, []);
|
|
29
|
+
childNodes.get(parent).push(n.id);
|
|
30
|
+
}
|
|
31
|
+
for (let i = 0; i < graph.edges.length; i++) {
|
|
32
|
+
const e = graph.edges[i];
|
|
33
|
+
edgeById.set(e.id, i);
|
|
34
|
+
outEdges.get(e.sourceId)?.push(e.id);
|
|
35
|
+
inEdges.get(e.targetId)?.push(e.id);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
nodeById,
|
|
39
|
+
edgeById,
|
|
40
|
+
outEdges,
|
|
41
|
+
inEdges,
|
|
42
|
+
childNodes,
|
|
43
|
+
nodeCount: graph.nodes.length,
|
|
44
|
+
edgeCount: graph.edges.length
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function indexAddNode(idx, node, arrayIndex) {
|
|
48
|
+
idx.nodeById.set(node.id, arrayIndex);
|
|
49
|
+
idx.outEdges.set(node.id, []);
|
|
50
|
+
idx.inEdges.set(node.id, []);
|
|
51
|
+
const parent = node.parentId;
|
|
52
|
+
if (!idx.childNodes.has(parent)) idx.childNodes.set(parent, []);
|
|
53
|
+
idx.childNodes.get(parent).push(node.id);
|
|
54
|
+
idx.nodeCount++;
|
|
55
|
+
}
|
|
56
|
+
function indexAddEdge(idx, edge, arrayIndex) {
|
|
57
|
+
idx.edgeById.set(edge.id, arrayIndex);
|
|
58
|
+
idx.outEdges.get(edge.sourceId)?.push(edge.id);
|
|
59
|
+
idx.inEdges.get(edge.targetId)?.push(edge.id);
|
|
60
|
+
idx.edgeCount++;
|
|
61
|
+
}
|
|
62
|
+
/** Update childNodes index when a node's parentId changes. */
|
|
63
|
+
function indexReparentNode(idx, nodeId, oldParentId, newParentId) {
|
|
64
|
+
const oldSiblings = idx.childNodes.get(oldParentId);
|
|
65
|
+
if (oldSiblings) {
|
|
66
|
+
const pos = oldSiblings.indexOf(nodeId);
|
|
67
|
+
if (pos !== -1) oldSiblings.splice(pos, 1);
|
|
68
|
+
}
|
|
69
|
+
if (!idx.childNodes.has(newParentId)) idx.childNodes.set(newParentId, []);
|
|
70
|
+
idx.childNodes.get(newParentId).push(nodeId);
|
|
71
|
+
}
|
|
72
|
+
/** Update adjacency lists when an edge's sourceId/targetId changes. */
|
|
73
|
+
function indexUpdateEdgeEndpoints(idx, edgeId, oldSourceId, oldTargetId, newSourceId, newTargetId) {
|
|
74
|
+
if (oldSourceId !== newSourceId) {
|
|
75
|
+
const oldOut = idx.outEdges.get(oldSourceId);
|
|
76
|
+
if (oldOut) {
|
|
77
|
+
const pos = oldOut.indexOf(edgeId);
|
|
78
|
+
if (pos !== -1) oldOut.splice(pos, 1);
|
|
79
|
+
}
|
|
80
|
+
idx.outEdges.get(newSourceId)?.push(edgeId);
|
|
81
|
+
}
|
|
82
|
+
if (oldTargetId !== newTargetId) {
|
|
83
|
+
const oldIn = idx.inEdges.get(oldTargetId);
|
|
84
|
+
if (oldIn) {
|
|
85
|
+
const pos = oldIn.indexOf(edgeId);
|
|
86
|
+
if (pos !== -1) oldIn.splice(pos, 1);
|
|
87
|
+
}
|
|
88
|
+
idx.inEdges.get(newTargetId)?.push(edgeId);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
export { indexUpdateEdgeEndpoints as a, indexReparentNode as i, indexAddEdge as n, invalidateIndex as o, indexAddNode as r, getIndex as t };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { c as Graph, d as GraphEdge, f as GraphNode } from "./types-XV3S5Jnh.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/queries.d.ts
|
|
4
|
+
declare function getEdgesOf<E>(graph: Graph<any, E>, nodeId: string): GraphEdge<E>[];
|
|
5
|
+
declare function getInEdges<E>(graph: Graph<any, E>, nodeId: string): GraphEdge<E>[];
|
|
6
|
+
declare function getOutEdges<E>(graph: Graph<any, E>, nodeId: string): GraphEdge<E>[];
|
|
7
|
+
declare function getEdgeBetween<E>(graph: Graph<any, E>, sourceId: string, targetId: string): GraphEdge<E> | undefined;
|
|
8
|
+
declare function getSuccessors<N>(graph: Graph<N>, nodeId: string): GraphNode<N>[];
|
|
9
|
+
declare function getPredecessors<N>(graph: Graph<N>, nodeId: string): GraphNode<N>[];
|
|
10
|
+
declare function getNeighbors<N>(graph: Graph<N>, nodeId: string): GraphNode<N>[];
|
|
11
|
+
declare function getDegree(graph: Graph, nodeId: string): number;
|
|
12
|
+
declare function getInDegree(graph: Graph, nodeId: string): number;
|
|
13
|
+
declare function getOutDegree(graph: Graph, nodeId: string): number;
|
|
14
|
+
declare function getChildren<N>(graph: Graph<N>, nodeId: string | null): GraphNode<N>[];
|
|
15
|
+
declare function getParent<N>(graph: Graph<N>, nodeId: string): GraphNode<N> | undefined;
|
|
16
|
+
declare function getAncestors<N>(graph: Graph<N>, nodeId: string): GraphNode<N>[];
|
|
17
|
+
declare function getDescendants<N>(graph: Graph<N>, nodeId: string): GraphNode<N>[];
|
|
18
|
+
declare function getRoots<N>(graph: Graph<N>): GraphNode<N>[];
|
|
19
|
+
/** Whether a node has children (is a compound/group node). */
|
|
20
|
+
declare function isCompound(graph: Graph, nodeId: string): boolean;
|
|
21
|
+
/** Whether a node has no children (is a leaf/atomic node). */
|
|
22
|
+
declare function isLeaf(graph: Graph, nodeId: string): boolean;
|
|
23
|
+
/** Depth of a node in the hierarchy (root = 0). */
|
|
24
|
+
declare function getDepth(graph: Graph, nodeId: string): number;
|
|
25
|
+
/** Sibling nodes (same parentId, excluding the node itself). */
|
|
26
|
+
declare function getSiblings<N>(graph: Graph<N>, nodeId: string): GraphNode<N>[];
|
|
27
|
+
/**
|
|
28
|
+
* Least Common Ancestor — deepest proper ancestor of all given nodes.
|
|
29
|
+
* A proper ancestor excludes the input nodes themselves.
|
|
30
|
+
*/
|
|
31
|
+
declare function getLCA<N>(graph: Graph<N>, ...nodeIds: string[]): GraphNode<N> | undefined;
|
|
32
|
+
/** Nodes with no incoming edges (inDegree 0). */
|
|
33
|
+
declare function getSources<N>(graph: Graph<N>): GraphNode<N>[];
|
|
34
|
+
/** Nodes with no outgoing edges (outDegree 0). */
|
|
35
|
+
declare function getSinks<N>(graph: Graph<N>): GraphNode<N>[];
|
|
36
|
+
//#endregion
|
|
37
|
+
export { getAncestors, getChildren, getDegree, getDepth, getDescendants, getEdgeBetween, getEdgesOf, getInDegree, getInEdges, getLCA, getNeighbors, getOutDegree, getOutEdges, getParent, getPredecessors, getRoots, getSiblings, getSinks, getSources, getSuccessors, isCompound, isLeaf };
|