@statelyai/graph 0.13.0 → 2.0.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.
Files changed (57) hide show
  1. package/README.md +57 -26
  2. package/dist/{adjacency-list-Ca0VjKIf.mjs → adjacency-list-GeL1Cu-L.mjs} +5 -3
  3. package/dist/{algorithms-BlM-qoJb.d.mts → algorithms-CsGNehct.d.mts} +137 -2
  4. package/dist/{algorithms-BNDQcHU3.mjs → algorithms-DF1pSQGv.mjs} +1494 -357
  5. package/dist/algorithms.d.mts +2 -2
  6. package/dist/algorithms.mjs +2 -2
  7. package/dist/{converter-Dspillnn.mjs → converter-DyCJJfTe.mjs} +2 -2
  8. package/dist/{edge-list-gKe8-iRa.mjs → edge-list-BcZ0h6zz.mjs} +1 -1
  9. package/dist/format-support.mjs +67 -11
  10. package/dist/formats/adjacency-list/index.d.mts +1 -1
  11. package/dist/formats/adjacency-list/index.mjs +1 -1
  12. package/dist/formats/converter/index.d.mts +1 -60
  13. package/dist/formats/converter/index.mjs +1 -1
  14. package/dist/formats/cytoscape/index.d.mts +1 -1
  15. package/dist/formats/cytoscape/index.mjs +5 -3
  16. package/dist/formats/d2/index.d.mts +109 -0
  17. package/dist/formats/d2/index.mjs +1100 -0
  18. package/dist/formats/d3/index.d.mts +2 -2
  19. package/dist/formats/d3/index.mjs +5 -3
  20. package/dist/formats/dot/index.d.mts +1 -1
  21. package/dist/formats/dot/index.mjs +24 -8
  22. package/dist/formats/edge-list/index.d.mts +1 -1
  23. package/dist/formats/edge-list/index.mjs +1 -1
  24. package/dist/formats/elk/index.d.mts +1 -1
  25. package/dist/formats/elk/index.mjs +23 -16
  26. package/dist/formats/gexf/index.d.mts +1 -1
  27. package/dist/formats/gexf/index.mjs +30 -17
  28. package/dist/formats/gml/index.d.mts +1 -1
  29. package/dist/formats/gml/index.mjs +22 -13
  30. package/dist/formats/graphml/index.d.mts +1 -1
  31. package/dist/formats/graphml/index.mjs +83 -25
  32. package/dist/formats/jgf/index.d.mts +1 -1
  33. package/dist/formats/jgf/index.mjs +6 -3
  34. package/dist/formats/mermaid/index.d.mts +1 -1
  35. package/dist/formats/mermaid/index.mjs +57 -20
  36. package/dist/formats/tgf/index.d.mts +1 -1
  37. package/dist/formats/tgf/index.mjs +2 -2
  38. package/dist/formats/xyflow/index.d.mts +1 -1
  39. package/dist/formats/xyflow/index.mjs +33 -6
  40. package/dist/index-D51lJnt2.d.mts +61 -0
  41. package/dist/index-DWmo1mIp.d.mts +697 -0
  42. package/dist/index.d.mts +6 -631
  43. package/dist/index.mjs +144 -295
  44. package/dist/mode-D8OnHFBk.mjs +15 -0
  45. package/dist/queries-BfXeTXRf.d.mts +547 -0
  46. package/dist/queries-KirMDR7e.mjs +980 -0
  47. package/dist/queries.d.mts +1 -514
  48. package/dist/queries.mjs +1 -766
  49. package/dist/schemas.d.mts +21 -10
  50. package/dist/schemas.mjs +35 -86
  51. package/dist/{types-CnZ01raw.d.mts → types-DNYdIU21.d.mts} +83 -11
  52. package/dist/validate-TtH-x3JV.mjs +190 -0
  53. package/package.json +14 -3
  54. package/schemas/edge.schema.json +11 -0
  55. package/schemas/graph.schema.json +24 -3
  56. package/schemas/node.schema.json +6 -0
  57. package/dist/indexing-DUl3kTqm.mjs +0 -137
package/dist/queries.mjs CHANGED
@@ -1,768 +1,3 @@
1
- import { t as getIndex } from "./indexing-DUl3kTqm.mjs";
1
+ import { C as getSinks, D as isLeaf, E as isCompound, S as getSiblings, T as getSuccessors, _ as getPorts, a as getDescendants, b as getRelativeDistanceMap, c as getEdgesOf, d as getLCA, f as getNeighbors, g as getPort, h as getParent, i as getDepth, l as getInDegree, m as getOutEdges, n as getChildren, o as getEdgesBetween, p as getOutDegree, r as getDegree, s as getEdgesByPort, t as getAncestors, u as getInEdges, v as getPredecessors, w as getSources, x as getRoots, y as getRelativeDistance } from "./queries-KirMDR7e.mjs";
2
2
 
3
- //#region src/queries.ts
4
- /**
5
- * Returns all edges (incoming + outgoing) connected to a node.
6
- *
7
- * @example
8
- * ```ts
9
- * const graph = createGraph({
10
- * nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
11
- * edges: [
12
- * { id: 'e1', sourceId: 'a', targetId: 'b' },
13
- * { id: 'e2', sourceId: 'c', targetId: 'b' },
14
- * ],
15
- * });
16
- * getEdgesOf(graph, 'b');
17
- * // => [edge e1, edge e2]
18
- * ```
19
- */
20
- function getEdgesOf(graph, nodeId) {
21
- const idx = getIndex(graph);
22
- const outIds = idx.outEdges.get(nodeId) ?? [];
23
- const inIds = idx.inEdges.get(nodeId) ?? [];
24
- const seen = /* @__PURE__ */ new Set();
25
- const result = [];
26
- for (const eid of outIds) {
27
- seen.add(eid);
28
- const ai = idx.edgeById.get(eid);
29
- if (ai !== void 0) result.push(graph.edges[ai]);
30
- }
31
- for (const eid of inIds) if (!seen.has(eid)) {
32
- const ai = idx.edgeById.get(eid);
33
- if (ai !== void 0) result.push(graph.edges[ai]);
34
- }
35
- return result;
36
- }
37
- /**
38
- * Returns incoming edges to a node.
39
- *
40
- * @example
41
- * ```ts
42
- * const graph = createGraph({
43
- * nodes: [{ id: 'a' }, { id: 'b' }],
44
- * edges: [{ id: 'e1', sourceId: 'a', targetId: 'b' }],
45
- * });
46
- * getInEdges(graph, 'b');
47
- * // => [edge e1]
48
- * getInEdges(graph, 'a');
49
- * // => []
50
- * ```
51
- */
52
- function getInEdges(graph, nodeId) {
53
- const idx = getIndex(graph);
54
- return (idx.inEdges.get(nodeId) ?? []).map((eid) => graph.edges[idx.edgeById.get(eid)]);
55
- }
56
- /**
57
- * Returns outgoing edges from a node.
58
- *
59
- * @example
60
- * ```ts
61
- * const graph = createGraph({
62
- * nodes: [{ id: 'a' }, { id: 'b' }],
63
- * edges: [{ id: 'e1', sourceId: 'a', targetId: 'b' }],
64
- * });
65
- * getOutEdges(graph, 'a');
66
- * // => [edge e1]
67
- * getOutEdges(graph, 'b');
68
- * // => []
69
- * ```
70
- */
71
- function getOutEdges(graph, nodeId) {
72
- const idx = getIndex(graph);
73
- return (idx.outEdges.get(nodeId) ?? []).map((eid) => graph.edges[idx.edgeById.get(eid)]);
74
- }
75
- /**
76
- * Returns all edges from `sourceId` to `targetId`.
77
- * For undirected graphs, checks both directions.
78
- *
79
- * @example
80
- * ```ts
81
- * const graph = createGraph({
82
- * nodes: [{ id: 'a' }, { id: 'b' }],
83
- * edges: [{ id: 'e1', sourceId: 'a', targetId: 'b' }],
84
- * });
85
- * getEdgesBetween(graph, 'a', 'b');
86
- * // => [edge e1]
87
- * getEdgesBetween(graph, 'b', 'a');
88
- * // => [] (directed graph)
89
- * ```
90
- */
91
- function getEdgesBetween(graph, sourceId, targetId) {
92
- const idx = getIndex(graph);
93
- const result = [];
94
- const seen = /* @__PURE__ */ new Set();
95
- const outIds = idx.outEdges.get(sourceId) ?? [];
96
- for (const eid of outIds) {
97
- const ai = idx.edgeById.get(eid);
98
- const e = graph.edges[ai];
99
- if (e.targetId === targetId) {
100
- seen.add(eid);
101
- result.push(e);
102
- }
103
- }
104
- if (graph.type === "undirected") {
105
- const outIds2 = idx.outEdges.get(targetId) ?? [];
106
- for (const eid of outIds2) {
107
- if (seen.has(eid)) continue;
108
- const ai = idx.edgeById.get(eid);
109
- const e = graph.edges[ai];
110
- if (e.targetId === sourceId) result.push(e);
111
- }
112
- }
113
- return result;
114
- }
115
- /**
116
- * Returns direct successor nodes (targets of outgoing edges).
117
- *
118
- * @example
119
- * ```ts
120
- * const graph = createGraph({
121
- * nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
122
- * edges: [
123
- * { id: 'e1', sourceId: 'a', targetId: 'b' },
124
- * { id: 'e2', sourceId: 'a', targetId: 'c' },
125
- * ],
126
- * });
127
- * getSuccessors(graph, 'a');
128
- * // => [node b, node c]
129
- * ```
130
- */
131
- function getSuccessors(graph, nodeId) {
132
- const idx = getIndex(graph);
133
- const edgeIds = idx.outEdges.get(nodeId) ?? [];
134
- const seen = /* @__PURE__ */ new Set();
135
- const result = [];
136
- for (const eid of edgeIds) {
137
- const e = graph.edges[idx.edgeById.get(eid)];
138
- if (!seen.has(e.targetId)) {
139
- seen.add(e.targetId);
140
- const ni = idx.nodeById.get(e.targetId);
141
- if (ni !== void 0) result.push(graph.nodes[ni]);
142
- }
143
- }
144
- return result;
145
- }
146
- /**
147
- * Returns direct predecessor nodes (sources of incoming edges).
148
- *
149
- * @example
150
- * ```ts
151
- * const graph = createGraph({
152
- * nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
153
- * edges: [
154
- * { id: 'e1', sourceId: 'a', targetId: 'c' },
155
- * { id: 'e2', sourceId: 'b', targetId: 'c' },
156
- * ],
157
- * });
158
- * getPredecessors(graph, 'c');
159
- * // => [node a, node b]
160
- * ```
161
- */
162
- function getPredecessors(graph, nodeId) {
163
- const idx = getIndex(graph);
164
- const edgeIds = idx.inEdges.get(nodeId) ?? [];
165
- const seen = /* @__PURE__ */ new Set();
166
- const result = [];
167
- for (const eid of edgeIds) {
168
- const e = graph.edges[idx.edgeById.get(eid)];
169
- if (!seen.has(e.sourceId)) {
170
- seen.add(e.sourceId);
171
- const ni = idx.nodeById.get(e.sourceId);
172
- if (ni !== void 0) result.push(graph.nodes[ni]);
173
- }
174
- }
175
- return result;
176
- }
177
- /**
178
- * Returns all neighbor nodes (successors + predecessors).
179
- *
180
- * @example
181
- * ```ts
182
- * const graph = createGraph({
183
- * nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
184
- * edges: [
185
- * { id: 'e1', sourceId: 'a', targetId: 'b' },
186
- * { id: 'e2', sourceId: 'c', targetId: 'b' },
187
- * ],
188
- * });
189
- * getNeighbors(graph, 'b');
190
- * // => [node a, node c]
191
- * ```
192
- */
193
- function getNeighbors(graph, nodeId) {
194
- const idx = getIndex(graph);
195
- const ids = /* @__PURE__ */ new Set();
196
- for (const eid of idx.outEdges.get(nodeId) ?? []) ids.add(graph.edges[idx.edgeById.get(eid)].targetId);
197
- for (const eid of idx.inEdges.get(nodeId) ?? []) ids.add(graph.edges[idx.edgeById.get(eid)].sourceId);
198
- return [...ids].map((id) => graph.nodes[idx.nodeById.get(id)]).filter(Boolean);
199
- }
200
- /**
201
- * Returns the total degree of a node (inDegree + outDegree).
202
- * For undirected graphs, each edge is counted once.
203
- *
204
- * @example
205
- * ```ts
206
- * const graph = createGraph({
207
- * nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
208
- * edges: [
209
- * { id: 'e1', sourceId: 'a', targetId: 'b' },
210
- * { id: 'e2', sourceId: 'c', targetId: 'b' },
211
- * ],
212
- * });
213
- * getDegree(graph, 'b'); // => 2
214
- * getDegree(graph, 'a'); // => 1
215
- * ```
216
- */
217
- function getDegree(graph, nodeId) {
218
- const idx = getIndex(graph);
219
- if (graph.type === "undirected") {
220
- const out = idx.outEdges.get(nodeId) ?? [];
221
- const inE = idx.inEdges.get(nodeId) ?? [];
222
- return new Set([...out, ...inE]).size;
223
- }
224
- return (idx.inEdges.get(nodeId)?.length ?? 0) + (idx.outEdges.get(nodeId)?.length ?? 0);
225
- }
226
- /**
227
- * Returns the in-degree of a node (number of incoming edges).
228
- *
229
- * @example
230
- * ```ts
231
- * const graph = createGraph({
232
- * nodes: [{ id: 'a' }, { id: 'b' }],
233
- * edges: [{ id: 'e1', sourceId: 'a', targetId: 'b' }],
234
- * });
235
- * getInDegree(graph, 'b'); // => 1
236
- * getInDegree(graph, 'a'); // => 0
237
- * ```
238
- */
239
- function getInDegree(graph, nodeId) {
240
- return getIndex(graph).inEdges.get(nodeId)?.length ?? 0;
241
- }
242
- /**
243
- * Returns the out-degree of a node (number of outgoing edges).
244
- *
245
- * @example
246
- * ```ts
247
- * const graph = createGraph({
248
- * nodes: [{ id: 'a' }, { id: 'b' }],
249
- * edges: [{ id: 'e1', sourceId: 'a', targetId: 'b' }],
250
- * });
251
- * getOutDegree(graph, 'a'); // => 1
252
- * getOutDegree(graph, 'b'); // => 0
253
- * ```
254
- */
255
- function getOutDegree(graph, nodeId) {
256
- return getIndex(graph).outEdges.get(nodeId)?.length ?? 0;
257
- }
258
- /**
259
- * Returns direct children of a node in the hierarchy.
260
- * Pass `null` to get root-level nodes.
261
- *
262
- * @example
263
- * ```ts
264
- * const graph = createGraph({
265
- * nodes: [
266
- * { id: 'parent' },
267
- * { id: 'child1', parentId: 'parent' },
268
- * { id: 'child2', parentId: 'parent' },
269
- * ],
270
- * });
271
- * getChildren(graph, 'parent');
272
- * // => [node child1, node child2]
273
- * getChildren(graph, null);
274
- * // => [node parent]
275
- * ```
276
- */
277
- function getChildren(graph, nodeId) {
278
- const idx = getIndex(graph);
279
- return (idx.childNodes.get(nodeId) ?? []).map((id) => graph.nodes[idx.nodeById.get(id)]).filter(Boolean);
280
- }
281
- /**
282
- * Returns the parent node in the hierarchy, or `undefined` if root-level.
283
- *
284
- * @example
285
- * ```ts
286
- * const graph = createGraph({
287
- * nodes: [
288
- * { id: 'parent' },
289
- * { id: 'child', parentId: 'parent' },
290
- * ],
291
- * });
292
- * getParent(graph, 'child');
293
- * // => node parent
294
- * getParent(graph, 'parent');
295
- * // => undefined
296
- * ```
297
- */
298
- function getParent(graph, nodeId) {
299
- const idx = getIndex(graph);
300
- const ni = idx.nodeById.get(nodeId);
301
- if (ni === void 0) return void 0;
302
- const node = graph.nodes[ni];
303
- if (!node.parentId) return void 0;
304
- const pi = idx.nodeById.get(node.parentId);
305
- return pi !== void 0 ? graph.nodes[pi] : void 0;
306
- }
307
- /**
308
- * Returns all ancestors from the node up to the root (nearest parent first).
309
- *
310
- * @example
311
- * ```ts
312
- * const graph = createGraph({
313
- * nodes: [
314
- * { id: 'root' },
315
- * { id: 'mid', parentId: 'root' },
316
- * { id: 'leaf', parentId: 'mid' },
317
- * ],
318
- * });
319
- * getAncestors(graph, 'leaf');
320
- * // => [node mid, node root]
321
- * ```
322
- */
323
- function getAncestors(graph, nodeId) {
324
- const idx = getIndex(graph);
325
- const result = [];
326
- let ni = idx.nodeById.get(nodeId);
327
- if (ni === void 0) return result;
328
- let current = graph.nodes[ni];
329
- while (current && current.parentId) {
330
- const pi = idx.nodeById.get(current.parentId);
331
- if (pi === void 0) break;
332
- const p = graph.nodes[pi];
333
- result.push(p);
334
- current = p;
335
- }
336
- return result;
337
- }
338
- /**
339
- * Returns all descendants recursively (depth-first).
340
- *
341
- * @example
342
- * ```ts
343
- * const graph = createGraph({
344
- * nodes: [
345
- * { id: 'root' },
346
- * { id: 'child', parentId: 'root' },
347
- * { id: 'grandchild', parentId: 'child' },
348
- * ],
349
- * });
350
- * getDescendants(graph, 'root');
351
- * // => [node child, node grandchild]
352
- * ```
353
- */
354
- function getDescendants(graph, nodeId) {
355
- const idx = getIndex(graph);
356
- const result = [];
357
- const collect = (id) => {
358
- const childIds = idx.childNodes.get(id) ?? [];
359
- for (const childId of childIds) {
360
- const ci = idx.nodeById.get(childId);
361
- if (ci !== void 0) {
362
- result.push(graph.nodes[ci]);
363
- collect(childId);
364
- }
365
- }
366
- };
367
- collect(nodeId);
368
- return result;
369
- }
370
- /**
371
- * Returns all root nodes (nodes with no parent).
372
- *
373
- * @example
374
- * ```ts
375
- * const graph = createGraph({
376
- * nodes: [
377
- * { id: 'root1' },
378
- * { id: 'root2' },
379
- * { id: 'child', parentId: 'root1' },
380
- * ],
381
- * });
382
- * getRoots(graph);
383
- * // => [node root1, node root2]
384
- * ```
385
- */
386
- function getRoots(graph) {
387
- const idx = getIndex(graph);
388
- return idx.childNodes.get(null)?.map((id) => graph.nodes[idx.nodeById.get(id)]).filter(Boolean) ?? [];
389
- }
390
- /**
391
- * Whether a node has children (is a compound/group node).
392
- *
393
- * @example
394
- * ```ts
395
- * const graph = createGraph({
396
- * nodes: [
397
- * { id: 'parent' },
398
- * { id: 'child', parentId: 'parent' },
399
- * ],
400
- * });
401
- * isCompound(graph, 'parent'); // => true
402
- * isCompound(graph, 'child'); // => false
403
- * ```
404
- */
405
- function isCompound(graph, nodeId) {
406
- return (getIndex(graph).childNodes.get(nodeId) ?? []).length > 0;
407
- }
408
- /**
409
- * Whether a node has no children (is a leaf/atomic node).
410
- *
411
- * @example
412
- * ```ts
413
- * const graph = createGraph({
414
- * nodes: [
415
- * { id: 'parent' },
416
- * { id: 'child', parentId: 'parent' },
417
- * ],
418
- * });
419
- * isLeaf(graph, 'child'); // => true
420
- * isLeaf(graph, 'parent'); // => false
421
- * ```
422
- */
423
- function isLeaf(graph, nodeId) {
424
- return !isCompound(graph, nodeId);
425
- }
426
- /**
427
- * Depth of a node in the hierarchy (root = 0).
428
- * Returns -1 if the node is not found.
429
- *
430
- * @example
431
- * ```ts
432
- * const graph = createGraph({
433
- * nodes: [
434
- * { id: 'root' },
435
- * { id: 'child', parentId: 'root' },
436
- * { id: 'grandchild', parentId: 'child' },
437
- * ],
438
- * });
439
- * getDepth(graph, 'root'); // => 0
440
- * getDepth(graph, 'child'); // => 1
441
- * getDepth(graph, 'grandchild'); // => 2
442
- * ```
443
- */
444
- function getDepth(graph, nodeId) {
445
- const idx = getIndex(graph);
446
- let d = 0;
447
- let ni = idx.nodeById.get(nodeId);
448
- if (ni === void 0) return -1;
449
- let current = graph.nodes[ni];
450
- while (current.parentId) {
451
- d++;
452
- const pi = idx.nodeById.get(current.parentId);
453
- if (pi === void 0) break;
454
- current = graph.nodes[pi];
455
- }
456
- return d;
457
- }
458
- /**
459
- * Sibling nodes (same parentId, excluding the node itself).
460
- *
461
- * @example
462
- * ```ts
463
- * const graph = createGraph({
464
- * nodes: [
465
- * { id: 'parent' },
466
- * { id: 'a', parentId: 'parent' },
467
- * { id: 'b', parentId: 'parent' },
468
- * { id: 'c', parentId: 'parent' },
469
- * ],
470
- * });
471
- * getSiblings(graph, 'a');
472
- * // => [node b, node c]
473
- * ```
474
- */
475
- function getSiblings(graph, nodeId) {
476
- const idx = getIndex(graph);
477
- const ni = idx.nodeById.get(nodeId);
478
- if (ni === void 0) return [];
479
- const node = graph.nodes[ni];
480
- return (idx.childNodes.get(node.parentId ?? null) ?? []).filter((id) => id !== nodeId).map((id) => graph.nodes[idx.nodeById.get(id)]).filter(Boolean);
481
- }
482
- /**
483
- * Least Common Ancestor -- deepest proper ancestor of all given nodes.
484
- * A proper ancestor excludes the input nodes themselves.
485
- *
486
- * @example
487
- * ```ts
488
- * const graph = createGraph({
489
- * nodes: [
490
- * { id: 'root' },
491
- * { id: 'a', parentId: 'root' },
492
- * { id: 'b', parentId: 'root' },
493
- * { id: 'a1', parentId: 'a' },
494
- * ],
495
- * });
496
- * getLCA(graph, 'a1', 'b');
497
- * // => node root
498
- * getLCA(graph, 'a', 'b');
499
- * // => node root
500
- * ```
501
- */
502
- function getLCA(graph, ...nodeIds) {
503
- if (nodeIds.length === 0) return void 0;
504
- const idx = getIndex(graph);
505
- const getAncestorChain = (id) => {
506
- const result = [id];
507
- let ni$1 = idx.nodeById.get(id);
508
- if (ni$1 === void 0) return result;
509
- let current = graph.nodes[ni$1];
510
- while (current.parentId) {
511
- result.push(current.parentId);
512
- const pi = idx.nodeById.get(current.parentId);
513
- if (pi === void 0) break;
514
- current = graph.nodes[pi];
515
- }
516
- return result;
517
- };
518
- let common = getAncestorChain(nodeIds[0]);
519
- for (let i = 1; i < nodeIds.length; i++) {
520
- const set = new Set(getAncestorChain(nodeIds[i]));
521
- common = common.filter((id) => set.has(id));
522
- }
523
- const inputSet = new Set(nodeIds);
524
- common = common.filter((id) => !inputSet.has(id));
525
- if (common.length === 0) return void 0;
526
- const lcaId = common[0];
527
- const ni = idx.nodeById.get(lcaId);
528
- return ni !== void 0 ? graph.nodes[ni] : void 0;
529
- }
530
- /**
531
- * Returns a map of nodeId → shortest-path distance for all sibling nodes
532
- * (same parentId). Distance is measured from the parent's `initialNodeId`
533
- * (or `graph.initialNodeId` for root-level nodes).
534
- *
535
- * Only follows edges between siblings. Unreachable siblings are omitted.
536
- *
537
- * @example Root-level nodes (uses `graph.initialNodeId`):
538
- * ```ts
539
- * const graph = createGraph({
540
- * initialNodeId: 'a',
541
- * nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
542
- * edges: [
543
- * { id: 'e1', sourceId: 'a', targetId: 'b' },
544
- * { id: 'e2', sourceId: 'b', targetId: 'c' },
545
- * ],
546
- * });
547
- * getRelativeDistanceMap(graph, null);
548
- * // => { a: 0, b: 1, c: 2 }
549
- * ```
550
- *
551
- * @example Nested nodes (uses parent's `initialNodeId`):
552
- * ```ts
553
- * const graph = createGraph({
554
- * nodes: [
555
- * { id: 'parent', initialNodeId: 's1' },
556
- * { id: 's1', parentId: 'parent' },
557
- * { id: 's2', parentId: 'parent' },
558
- * { id: 's3', parentId: 'parent' },
559
- * ],
560
- * edges: [
561
- * { id: 'e1', sourceId: 's1', targetId: 's2' },
562
- * { id: 'e2', sourceId: 's2', targetId: 's3' },
563
- * ],
564
- * });
565
- * getRelativeDistanceMap(graph, 'parent');
566
- * // => { s1: 0, s2: 1, s3: 2 }
567
- * ```
568
- */
569
- function getRelativeDistanceMap(graph, parentId) {
570
- const idx = getIndex(graph);
571
- let sourceId = null;
572
- if (parentId !== null) {
573
- const pi = idx.nodeById.get(parentId);
574
- if (pi !== void 0) sourceId = graph.nodes[pi].initialNodeId ?? null;
575
- } else sourceId = graph.initialNodeId ?? null;
576
- if (!sourceId) return {};
577
- const siblingSet = new Set(idx.childNodes.get(parentId) ?? []);
578
- if (!siblingSet.has(sourceId)) return {};
579
- const dist = /* @__PURE__ */ new Map();
580
- dist.set(sourceId, 0);
581
- const queue = [sourceId];
582
- while (queue.length > 0) {
583
- const id = queue.shift();
584
- const d = dist.get(id);
585
- for (const eid of idx.outEdges.get(id) ?? []) {
586
- const ai = idx.edgeById.get(eid);
587
- if (ai === void 0) continue;
588
- const neighborId = graph.edges[ai].targetId;
589
- if (siblingSet.has(neighborId) && !dist.has(neighborId)) {
590
- dist.set(neighborId, d + 1);
591
- queue.push(neighborId);
592
- }
593
- }
594
- if (graph.type === "undirected") for (const eid of idx.inEdges.get(id) ?? []) {
595
- const ai = idx.edgeById.get(eid);
596
- if (ai === void 0) continue;
597
- const neighborId = graph.edges[ai].sourceId;
598
- if (siblingSet.has(neighborId) && !dist.has(neighborId)) {
599
- dist.set(neighborId, d + 1);
600
- queue.push(neighborId);
601
- }
602
- }
603
- }
604
- const result = {};
605
- for (const [id, d] of dist) result[id] = d;
606
- return result;
607
- }
608
- /**
609
- * Returns the shortest-path distance of a node from its parent's initial node.
610
- * Automatically scopes to the node's sibling group (same `parentId`).
611
- *
612
- * Returns `undefined` if the node is not found or unreachable.
613
- *
614
- * @example
615
- * ```ts
616
- * const graph = createGraph({
617
- * initialNodeId: 'a',
618
- * nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
619
- * edges: [
620
- * { id: 'e1', sourceId: 'a', targetId: 'b' },
621
- * { id: 'e2', sourceId: 'b', targetId: 'c' },
622
- * ],
623
- * });
624
- * getRelativeDistance(graph, 'a'); // => 0
625
- * getRelativeDistance(graph, 'b'); // => 1
626
- * getRelativeDistance(graph, 'c'); // => 2
627
- * ```
628
- *
629
- * @example Nested nodes:
630
- * ```ts
631
- * const graph = createGraph({
632
- * nodes: [
633
- * { id: 'parent', initialNodeId: 's1' },
634
- * { id: 's1', parentId: 'parent' },
635
- * { id: 's2', parentId: 'parent' },
636
- * ],
637
- * edges: [{ id: 'e1', sourceId: 's1', targetId: 's2' }],
638
- * });
639
- * getRelativeDistance(graph, 's1'); // => 0
640
- * getRelativeDistance(graph, 's2'); // => 1
641
- * ```
642
- */
643
- function getRelativeDistance(graph, nodeId) {
644
- const ni = getIndex(graph).nodeById.get(nodeId);
645
- if (ni === void 0) return void 0;
646
- const node = graph.nodes[ni];
647
- return getRelativeDistanceMap(graph, node.parentId ?? null)[nodeId];
648
- }
649
- /**
650
- * Nodes with no incoming edges (inDegree 0).
651
- *
652
- * @example
653
- * ```ts
654
- * const graph = createGraph({
655
- * nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
656
- * edges: [
657
- * { id: 'e1', sourceId: 'a', targetId: 'b' },
658
- * { id: 'e2', sourceId: 'b', targetId: 'c' },
659
- * ],
660
- * });
661
- * getSources(graph);
662
- * // => [node a]
663
- * ```
664
- */
665
- function getSources(graph) {
666
- const idx = getIndex(graph);
667
- return graph.nodes.filter((n) => (idx.inEdges.get(n.id)?.length ?? 0) === 0);
668
- }
669
- /**
670
- * Nodes with no outgoing edges (outDegree 0).
671
- *
672
- * @example
673
- * ```ts
674
- * const graph = createGraph({
675
- * nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
676
- * edges: [
677
- * { id: 'e1', sourceId: 'a', targetId: 'b' },
678
- * { id: 'e2', sourceId: 'b', targetId: 'c' },
679
- * ],
680
- * });
681
- * getSinks(graph);
682
- * // => [node c]
683
- * ```
684
- */
685
- function getSinks(graph) {
686
- const idx = getIndex(graph);
687
- return graph.nodes.filter((n) => (idx.outEdges.get(n.id)?.length ?? 0) === 0);
688
- }
689
- /**
690
- * Get a port by name on a node, or `undefined` if not found.
691
- *
692
- * @example
693
- * ```ts
694
- * const graph = createGraph({
695
- * nodes: [{
696
- * id: 'a',
697
- * ports: [{ name: 'out', direction: 'out' }],
698
- * }],
699
- * });
700
- * getPort(graph, 'a', 'out'); // => { name: 'out', direction: 'out', ... }
701
- * getPort(graph, 'a', 'missing'); // => undefined
702
- * ```
703
- */
704
- function getPort(graph, nodeId, portName) {
705
- const ni = getIndex(graph).nodeById.get(nodeId);
706
- if (ni === void 0) return void 0;
707
- return graph.nodes[ni].ports?.find((p) => p.name === portName);
708
- }
709
- /**
710
- * Get all ports on a node. Returns `[]` if the node has no ports or doesn't exist.
711
- *
712
- * @example
713
- * ```ts
714
- * const graph = createGraph({
715
- * nodes: [{
716
- * id: 'a',
717
- * ports: [
718
- * { name: 'in', direction: 'in' },
719
- * { name: 'out', direction: 'out' },
720
- * ],
721
- * }],
722
- * });
723
- * getPorts(graph, 'a'); // => [port in, port out]
724
- * ```
725
- */
726
- function getPorts(graph, nodeId) {
727
- const ni = getIndex(graph).nodeById.get(nodeId);
728
- if (ni === void 0) return [];
729
- return graph.nodes[ni].ports ?? [];
730
- }
731
- /**
732
- * Get all edges connected to a specific port on a node.
733
- *
734
- * Returns edges where:
735
- * - `sourceId === nodeId && sourcePort === portName`, or
736
- * - `targetId === nodeId && targetPort === portName`
737
- *
738
- * @example
739
- * ```ts
740
- * const graph = createGraph({
741
- * nodes: [
742
- * { id: 'a', ports: [{ name: 'out', direction: 'out' }] },
743
- * { id: 'b', ports: [{ name: 'in', direction: 'in' }] },
744
- * ],
745
- * edges: [{
746
- * id: 'e1', sourceId: 'a', targetId: 'b',
747
- * sourcePort: 'out', targetPort: 'in',
748
- * }],
749
- * });
750
- * getEdgesByPort(graph, 'a', 'out'); // => [edge e1]
751
- * ```
752
- */
753
- function getEdgesByPort(graph, nodeId, portName) {
754
- const idx = getIndex(graph);
755
- const result = [];
756
- for (const eid of idx.outEdges.get(nodeId) ?? []) {
757
- const ai = idx.edgeById.get(eid);
758
- if (ai !== void 0 && graph.edges[ai].sourcePort === portName) result.push(graph.edges[ai]);
759
- }
760
- for (const eid of idx.inEdges.get(nodeId) ?? []) {
761
- const ai = idx.edgeById.get(eid);
762
- if (ai !== void 0 && graph.edges[ai].targetPort === portName) result.push(graph.edges[ai]);
763
- }
764
- return result;
765
- }
766
-
767
- //#endregion
768
3
  export { getAncestors, getChildren, getDegree, getDepth, getDescendants, getEdgesBetween, getEdgesByPort, getEdgesOf, getInDegree, getInEdges, getLCA, getNeighbors, getOutDegree, getOutEdges, getParent, getPort, getPorts, getPredecessors, getRelativeDistance, getRelativeDistanceMap, getRoots, getSiblings, getSinks, getSources, getSuccessors, isCompound, isLeaf };