@statelyai/graph 0.7.0 → 0.9.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.
@@ -1,4 +1,4 @@
1
- import { u as Graph } from "../../types-DkKjaQW3.mjs";
1
+ import { u as Graph } from "../../types-BzckPChi.mjs";
2
2
 
3
3
  //#region src/formats/edge-list/index.d.ts
4
4
 
@@ -1,4 +1,4 @@
1
- import { D as VisualGraph, k as VisualGraphFormatConverter } from "../../types-DkKjaQW3.mjs";
1
+ import { F as VisualGraphFormatConverter, M as VisualGraph } from "../../types-BzckPChi.mjs";
2
2
  import { ElkEdge, ElkEdgeSection, ElkExtendedEdge, ElkGraphElement, ElkLabel, ElkNode, ElkNode as ElkNode$1, ElkPoint, ElkPort, ElkPrimitiveEdge, ElkShape, LayoutOptions } from "elkjs/lib/elk-api";
3
3
 
4
4
  //#region src/formats/elk/index.d.ts
@@ -16,12 +16,24 @@ const ELK_TO_DIRECTION = {
16
16
  function convertEdge(edge) {
17
17
  const elkEdge = {
18
18
  id: edge.id,
19
- sources: [edge.sourceId],
20
- targets: [edge.targetId]
19
+ sources: [edge.sourcePort ?? edge.sourceId],
20
+ targets: [edge.targetPort ?? edge.targetId]
21
21
  };
22
22
  if (edge.label) elkEdge.labels = [{ text: edge.label }];
23
23
  return elkEdge;
24
24
  }
25
+ function convertPort(port) {
26
+ const elkPort = {
27
+ id: port.name,
28
+ x: port.x,
29
+ y: port.y,
30
+ width: port.width,
31
+ height: port.height
32
+ };
33
+ if (port.label) elkPort.labels = [{ text: port.label }];
34
+ if (port.direction !== "inout") elkPort.layoutOptions = { "org.eclipse.elk.port.side": port.direction === "in" ? "WEST" : "EAST" };
35
+ return elkPort;
36
+ }
25
37
  function convertNode(graph, node) {
26
38
  const elkNode = {
27
39
  id: node.id,
@@ -31,6 +43,7 @@ function convertNode(graph, node) {
31
43
  height: node.height
32
44
  };
33
45
  if (node.label) elkNode.labels = [{ text: node.label }];
46
+ if (node.ports && node.ports.length > 0) elkNode.ports = node.ports.map(convertPort);
34
47
  const children = getChildren(graph, node.id);
35
48
  if (children.length > 0) {
36
49
  elkNode.children = children.map((child) => convertNode(graph, child));
@@ -84,7 +97,7 @@ function toELK(graph) {
84
97
  if (rootEdges.length > 0) root.edges = rootEdges.map(convertEdge);
85
98
  return root;
86
99
  }
87
- function flattenElkNodes(elkNode, parentId, nodes, edges, edgeIdx) {
100
+ function flattenElkNodes(elkNode, parentId, nodes, edges, edgeIdx, portOwner) {
88
101
  if (elkNode.children) for (const child of elkNode.children) {
89
102
  const label = child.labels?.[0]?.text ?? "";
90
103
  const node = {
@@ -99,15 +112,34 @@ function flattenElkNodes(elkNode, parentId, nodes, edges, edgeIdx) {
99
112
  width: child.width ?? 0,
100
113
  height: child.height ?? 0
101
114
  };
115
+ if (child.ports && child.ports.length > 0) node.ports = child.ports.map((elkPort) => {
116
+ portOwner.set(elkPort.id, child.id);
117
+ const sideOpt = elkPort.layoutOptions?.["org.eclipse.elk.port.side"];
118
+ let direction = "inout";
119
+ if (sideOpt === "WEST") direction = "in";
120
+ else if (sideOpt === "EAST") direction = "out";
121
+ return {
122
+ name: elkPort.id,
123
+ direction,
124
+ label: elkPort.labels?.[0]?.text,
125
+ data: void 0,
126
+ x: elkPort.x ?? 0,
127
+ y: elkPort.y ?? 0,
128
+ width: elkPort.width ?? 0,
129
+ height: elkPort.height ?? 0
130
+ };
131
+ });
102
132
  nodes.push(node);
103
- flattenElkNodes(child, child.id, nodes, edges, edgeIdx);
133
+ flattenElkNodes(child, child.id, nodes, edges, edgeIdx, portOwner);
104
134
  }
105
135
  if (elkNode.edges) for (const elkEdge of elkNode.edges) for (const source of elkEdge.sources) for (const target of elkEdge.targets) {
136
+ const sourceNodeId = portOwner.get(source);
137
+ const targetNodeId = portOwner.get(target);
106
138
  const edge = {
107
139
  type: "edge",
108
140
  id: elkEdge.id ?? `e${edgeIdx.value++}`,
109
- sourceId: source,
110
- targetId: target,
141
+ sourceId: sourceNodeId ?? source,
142
+ targetId: targetNodeId ?? target,
111
143
  label: elkEdge.labels?.[0]?.text ?? "",
112
144
  data: void 0,
113
145
  x: 0,
@@ -115,6 +147,8 @@ function flattenElkNodes(elkNode, parentId, nodes, edges, edgeIdx) {
115
147
  width: 0,
116
148
  height: 0
117
149
  };
150
+ if (sourceNodeId) edge.sourcePort = source;
151
+ if (targetNodeId) edge.targetPort = target;
118
152
  edges.push(edge);
119
153
  }
120
154
  }
@@ -141,7 +175,7 @@ function flattenElkNodes(elkNode, parentId, nodes, edges, edgeIdx) {
141
175
  function fromELK(elkRoot) {
142
176
  const nodes = [];
143
177
  const edges = [];
144
- flattenElkNodes(elkRoot, null, nodes, edges, { value: 0 });
178
+ flattenElkNodes(elkRoot, null, nodes, edges, { value: 0 }, /* @__PURE__ */ new Map());
145
179
  const seenEdges = /* @__PURE__ */ new Map();
146
180
  for (const edge of edges) if (!seenEdges.has(edge.id)) seenEdges.set(edge.id, edge);
147
181
  const elkDir = elkRoot.layoutOptions?.["elk.direction"];
@@ -1,4 +1,4 @@
1
- import { m as GraphFormatConverter, u as Graph } from "../../types-DkKjaQW3.mjs";
1
+ import { g as GraphFormatConverter, u as Graph } from "../../types-BzckPChi.mjs";
2
2
 
3
3
  //#region src/formats/gexf/index.d.ts
4
4
  declare function toGEXF(graph: Graph): string;
@@ -1,4 +1,4 @@
1
- import { m as GraphFormatConverter, u as Graph } from "../../types-DkKjaQW3.mjs";
1
+ import { g as GraphFormatConverter, u as Graph } from "../../types-BzckPChi.mjs";
2
2
 
3
3
  //#region src/formats/gml/index.d.ts
4
4
 
@@ -1,4 +1,4 @@
1
- import { m as GraphFormatConverter, u as Graph } from "../../types-DkKjaQW3.mjs";
1
+ import { g as GraphFormatConverter, u as Graph } from "../../types-BzckPChi.mjs";
2
2
 
3
3
  //#region src/formats/graphml/index.d.ts
4
4
  declare function toGraphML(graph: Graph): string;
@@ -1,4 +1,4 @@
1
- import { m as GraphFormatConverter, u as Graph } from "../../types-DkKjaQW3.mjs";
1
+ import { g as GraphFormatConverter, u as Graph } from "../../types-BzckPChi.mjs";
2
2
 
3
3
  //#region src/formats/jgf/index.d.ts
4
4
  interface JGFNode {
@@ -1,4 +1,4 @@
1
- import { m as GraphFormatConverter, u as Graph } from "../../types-DkKjaQW3.mjs";
1
+ import { g as GraphFormatConverter, u as Graph } from "../../types-BzckPChi.mjs";
2
2
 
3
3
  //#region src/formats/mermaid/sequence.d.ts
4
4
  interface SequenceNodeData {
@@ -1,4 +1,4 @@
1
- import { m as GraphFormatConverter, u as Graph } from "../../types-DkKjaQW3.mjs";
1
+ import { g as GraphFormatConverter, u as Graph } from "../../types-BzckPChi.mjs";
2
2
 
3
3
  //#region src/formats/tgf/index.d.ts
4
4
 
@@ -1,4 +1,4 @@
1
- import { D as VisualGraph, k as VisualGraphFormatConverter } from "../../types-DkKjaQW3.mjs";
1
+ import { F as VisualGraphFormatConverter, M as VisualGraph } from "../../types-BzckPChi.mjs";
2
2
  import { EdgeBase, NodeBase } from "@xyflow/system";
3
3
 
4
4
  //#region src/formats/xyflow/index.d.ts
@@ -42,6 +42,8 @@ function toXYFlow(graph) {
42
42
  source: e.sourceId,
43
43
  target: e.targetId
44
44
  };
45
+ if (e.sourcePort) edge.sourceHandle = e.sourcePort;
46
+ if (e.targetPort) edge.targetHandle = e.targetPort;
45
47
  if (e.data !== void 0) edge.data = e.data;
46
48
  if (e.label) edge.data = {
47
49
  ...edge.data,
@@ -96,6 +98,8 @@ function fromXYFlow(flow) {
96
98
  sourceId: e.source,
97
99
  targetId: e.target,
98
100
  label: e.data?.label?.toString() ?? "",
101
+ ...e.sourceHandle && { sourcePort: e.sourceHandle },
102
+ ...e.targetHandle && { targetPort: e.targetHandle },
99
103
  data: e.data,
100
104
  x: 0,
101
105
  y: 0,
package/dist/index.d.mts CHANGED
@@ -1,10 +1,20 @@
1
- import { A as VisualNode, C as SinglePathOptions, D as VisualGraph, E as VisualEdge, M as WalkOptions, N as WeightedWalkOptions, O as VisualGraphConfig, S as PathOptions, T as TraversalOptions, _ as GraphPath, a as EdgeChange, b as NodeChange, c as EntitiesUpdate, d as GraphConfig, f as GraphDiff, g as GraphPatch, h as GraphNode, i as DeleteNodeOptions, j as WalkContext, l as EntityRect, m as GraphFormatConverter, n as AllPairsShortestPathsOptions, o as EdgeConfig, p as GraphEdge, r as CoverageStats, s as EntitiesConfig, t as AStarOptions, u as Graph, v as GraphStep, w as TransitionOptions, x as NodeConfig, y as MSTOptions } from "./types-DkKjaQW3.mjs";
2
- import { bfs, dfs, genCycles, genPostorders, genPreorders, genShortestPaths, genSimplePaths, getAStarPath, getAllPairsShortestPaths, getConnectedComponents, getCycles, getMinimumSpanningTree, getPostorder, getPostorders, getPreorder, getPreorders, getShortestPath, getShortestPaths, getSimplePath, getSimplePaths, getStronglyConnectedComponents, getTopologicalSort, hasPath, isAcyclic, isConnected, isTree, joinPaths } from "./algorithms.mjs";
1
+ import { A as TraversalOptions, B as WeightedWalkOptions, C as NodeChange, D as PortDirection, E as PortConfig, I as VisualNode, L as VisualPort, M as VisualGraph, N as VisualGraphConfig, O as SinglePathOptions, P as VisualGraphEntity, R as WalkContext, S as MSTOptions, T as PathOptions, _ as GraphNode, a as EdgeChange, b as GraphPort, c as EntitiesUpdate, d as GraphConfig, f as GraphDiff, g as GraphFormatConverter, h as GraphEntityConfig, i as DeleteNodeOptions, j as VisualEdge, k as TransitionOptions, l as EntityRect, m as GraphEntity, n as AllPairsShortestPathsOptions, o as EdgeConfig, p as GraphEdge, r as CoverageStats, s as EntitiesConfig, t as AStarOptions, u as Graph, v as GraphPatch, w as NodeConfig, x as GraphStep, y as GraphPath, z as WalkOptions } from "./types-BzckPChi.mjs";
2
+ import { A as getArticulationPoints, B as HITSResult, C as hasPath, D as joinPaths, E as isTree, F as genGirvanNewmanCommunities, G as getEigenvectorCentrality, H as getBetweennessCentrality, I as getGirvanNewmanCommunities, J as getOutDegreeCentrality, K as getHITS, L as getGreedyModularityCommunities, M as getBridges, N as GirvanNewmanOptions, O as IsomorphismOptions, P as LabelPropagationOptions, R as getLabelPropagationCommunities, S as getTopologicalSort, T as isConnected, U as getClosenessCentrality, V as IterativeCentralityOptions, W as getDegreeCentrality, Y as getPageRank, _ as getShortestPath, a as genPreorders, b as getSimplePaths, c as getAStarPath, d as getCycles, f as getMinimumSpanningTree, g as getPreorders, h as getPreorder, i as genPostorders, j as getBiconnectedComponents, k as isIsomorphic, l as getAllPairsShortestPaths, m as getPostorders, n as dfs, o as genShortestPaths, p as getPostorder, q as getInDegreeCentrality, r as genCycles, s as genSimplePaths, t as bfs, u as getConnectedComponents, v as getShortestPaths, w as isAcyclic, x as getStronglyConnectedComponents, y as getSimplePath, z as getModularity } from "./algorithms-g2uWmPrb.mjs";
3
3
  import { createFormatConverter } from "./formats/converter/index.mjs";
4
- import { getAncestors, getChildren, getDegree, getDepth, getDescendants, getEdgeBetween, getEdgesOf, getInDegree, getInEdges, getLCA, getNeighbors, getOutDegree, getOutEdges, getParent, getPredecessors, getRelativeDistance, getRelativeDistanceMap, getRoots, getSiblings, getSinks, getSources, getSuccessors, isCompound, isLeaf } from "./queries.mjs";
4
+ import { getAncestors, getChildren, getDegree, getDepth, getDescendants, getEdgeBetween, getEdgesByPort, getEdgesOf, getInDegree, getInEdges, getLCA, getNeighbors, getOutDegree, getOutEdges, getParent, getPort, getPorts, getPredecessors, getRelativeDistance, getRelativeDistanceMap, getRoots, getSiblings, getSinks, getSources, getSuccessors, isCompound, isLeaf } from "./queries.mjs";
5
5
 
6
6
  //#region src/graph.d.ts
7
7
 
8
+ /**
9
+ * Create a resolved graph port from a config. Fills in defaults.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const port = createGraphPort({ name: 'output', direction: 'out' });
14
+ * // { name: 'output', direction: 'out', data: undefined }
15
+ * ```
16
+ */
17
+ declare function createGraphPort<P = any>(config: PortConfig<P>): GraphPort<P>;
8
18
  /**
9
19
  * Create a resolved graph node from a config. Fills in defaults.
10
20
  *
@@ -14,7 +24,7 @@ import { getAncestors, getChildren, getDegree, getDepth, getDescendants, getEdge
14
24
  * // { type: 'node', id: 'a', label: '', data: { label: 'hi' } }
15
25
  * ```
16
26
  */
17
- declare function createGraphNode<T = any>(config: NodeConfig<T>): GraphNode<T>;
27
+ declare function createGraphNode<N = any, P = any>(config: NodeConfig<N, P>): GraphNode<N, P>;
18
28
  /**
19
29
  * Create a resolved graph edge from a config. Fills in defaults.
20
30
  *
@@ -36,7 +46,7 @@ declare function createGraphEdge<T = any>(config: EdgeConfig<T>): GraphEdge<T>;
36
46
  * });
37
47
  * ```
38
48
  */
39
- declare function createGraph<N = any, E = any, G = any>(config?: GraphConfig<N, E, G>): Graph<N, E, G>;
49
+ declare function createGraph<N = any, E = any, G = any, P = any>(config?: GraphConfig<N, E, G, P>): Graph<N, E, G, P>;
40
50
  /**
41
51
  * Create a visual graph with required position/size on all nodes and edges.
42
52
  *
@@ -49,7 +59,7 @@ declare function createGraph<N = any, E = any, G = any>(config?: GraphConfig<N,
49
59
  * // graph.nodes[0].x === 0
50
60
  * ```
51
61
  */
52
- declare function createVisualGraph<N = any, E = any, G = any>(config?: VisualGraphConfig<N, E, G>): VisualGraph<N, E, G>;
62
+ declare function createVisualGraph<N = any, E = any, G = any, P = any>(config?: VisualGraphConfig<N, E, G, P>): VisualGraph<N, E, G, P>;
53
63
  /**
54
64
  * Create a graph by BFS exploration of a transition function.
55
65
  * Each unique state becomes a node; each (state, event) -> nextState becomes an edge.
@@ -139,7 +149,7 @@ declare function hasEdge(graph: Graph, id: string): boolean;
139
149
  * // graph.nodes.length === 1
140
150
  * ```
141
151
  */
142
- declare function addNode<N>(graph: Graph<N>, config: NodeConfig<N>): GraphNode<N>;
152
+ declare function addNode<N, P = any>(graph: Graph<N, any, any, P>, config: NodeConfig<N, P>): GraphNode<N, P>;
143
153
  /**
144
154
  * **Mutable.** Add an edge to the graph. Mutates `graph.edges` in place.
145
155
  * @returns The resolved edge that was added.
@@ -195,7 +205,7 @@ declare function deleteEdge(graph: Graph, id: string): void;
195
205
  * // updated.label === 'new'
196
206
  * ```
197
207
  */
198
- declare function updateNode<N>(graph: Graph<N>, id: string, update: Partial<Omit<NodeConfig<N>, 'id'>>): GraphNode<N>;
208
+ declare function updateNode<N, P = any>(graph: Graph<N, any, any, P>, id: string, update: Partial<Omit<NodeConfig<N, P>, 'id'>>): GraphNode<N, P>;
199
209
  /**
200
210
  * **Mutable.** Update an edge in place.
201
211
  * @returns The updated edge.
@@ -273,9 +283,9 @@ declare function updateEntities<N, E>(graph: Graph<N, E>, updates: EntitiesUpdat
273
283
  * instance.toJSON(); // plain Graph object
274
284
  * ```
275
285
  */
276
- declare class GraphInstance<N = any, E = any, G = any> {
277
- graph: Graph<N, E, G>;
278
- constructor(config?: GraphConfig<N, E, G>);
286
+ declare class GraphInstance<N = any, E = any, G = any, P = any> {
287
+ graph: Graph<N, E, G, P>;
288
+ constructor(config?: GraphConfig<N, E, G, P>);
279
289
  /**
280
290
  * Wrap an existing plain graph object.
281
291
  *
@@ -286,26 +296,26 @@ declare class GraphInstance<N = any, E = any, G = any> {
286
296
  * instance.hasNode('a'); // true
287
297
  * ```
288
298
  */
289
- static from<N = any, E = any, G = any>(graph: Graph<N, E, G>): GraphInstance<N, E, G>;
299
+ static from<N = any, E = any, G = any, P = any>(graph: Graph<N, E, G, P>): GraphInstance<N, E, G, P>;
290
300
  get id(): string;
291
301
  get type(): "directed" | "undirected";
292
- get nodes(): GraphNode<N>[];
302
+ get nodes(): GraphNode<N, P>[];
293
303
  get edges(): GraphEdge<E>[];
294
304
  get data(): G;
295
- getNode(id: string): GraphNode<N> | undefined;
305
+ getNode(id: string): GraphNode<N, any> | undefined;
296
306
  getEdge(id: string): GraphEdge<E> | undefined;
297
307
  hasNode(id: string): boolean;
298
308
  hasEdge(id: string): boolean;
299
- addNode(config: NodeConfig<N>): GraphNode<N>;
309
+ addNode(config: NodeConfig<N, P>): GraphNode<N, P>;
300
310
  addEdge(config: EdgeConfig<E>): GraphEdge<E>;
301
311
  deleteNode(id: string, opts?: DeleteNodeOptions): void;
302
312
  deleteEdge(id: string): void;
303
- updateNode(id: string, update: Partial<Omit<NodeConfig<N>, 'id'>>): GraphNode<N>;
313
+ updateNode(id: string, update: Partial<Omit<NodeConfig<N, P>, 'id'>>): GraphNode<N, P>;
304
314
  updateEdge(id: string, update: Partial<Omit<EdgeConfig<E>, 'id'>>): GraphEdge<E>;
305
- addEntities(entities: EntitiesConfig<N, E>): void;
315
+ addEntities(entities: EntitiesConfig<N, E, P>): void;
306
316
  deleteEntities(ids: string | string[], opts?: DeleteNodeOptions): void;
307
- updateEntities(updates: EntitiesUpdate<N, E>): void;
308
- toJSON(): Graph<N, E, G>;
317
+ updateEntities(updates: EntitiesUpdate<N, E, P>): void;
318
+ toJSON(): Graph<N, E, G, P>;
309
319
  }
310
320
  //#endregion
311
321
  //#region src/indexing.d.ts
@@ -324,6 +334,57 @@ declare class GraphInstance<N = any, E = any, G = any> {
324
334
  */
325
335
  declare function invalidateIndex(graph: Graph): void;
326
336
  //#endregion
337
+ //#region src/equivalence.d.ts
338
+ declare const LAYOUT_KEYS: {
339
+ node: readonly ["x", "y", "width", "height", "style", "color", "shape"];
340
+ edge: readonly ["x", "y", "width", "height", "style", "color"];
341
+ };
342
+ /**
343
+ * Compare two entities on a given set of keys.
344
+ * If `keys` is omitted or empty, compares all own keys of `a`.
345
+ *
346
+ * @example
347
+ * ```ts
348
+ * import { createGraphNode, areEntitiesEqual, LAYOUT_KEYS } from '@statelyai/graph';
349
+ *
350
+ * const a = createGraphNode({ id: 'n1', label: 'hello', x: 0 });
351
+ * const b = createGraphNode({ id: 'n1', label: 'hello', x: 100 });
352
+ *
353
+ * areEntitiesEqual(a, b, LAYOUT_KEYS.node); // false (x differs)
354
+ * areEntitiesEqual(a, b, NON_LAYOUT_KEYS.node); // true
355
+ * areEntitiesEqual(a, b); // false (compares all keys)
356
+ * ```
357
+ */
358
+ declare function areEntitiesEqual<T extends GraphNode | GraphEdge>(a: T, b: T, keys?: readonly (keyof T)[]): boolean;
359
+ /**
360
+ * Compare two entities on layout keys only (position, size, style, color, shape).
361
+ *
362
+ * @example
363
+ * ```ts
364
+ * import { createGraphNode, isLayoutEqual } from '@statelyai/graph';
365
+ *
366
+ * const a = createGraphNode({ id: 'n1', x: 0, y: 0 });
367
+ * const b = createGraphNode({ id: 'n1', x: 100, y: 200 });
368
+ *
369
+ * isLayoutEqual(a, b); // false
370
+ * ```
371
+ */
372
+ declare function isLayoutEqual<T extends GraphNode | GraphEdge>(a: T, b: T): boolean;
373
+ /**
374
+ * Compare two entities on non-layout keys only (id, data, connections, labels, etc.).
375
+ *
376
+ * @example
377
+ * ```ts
378
+ * import { createGraphNode, isNonLayoutEqual } from '@statelyai/graph';
379
+ *
380
+ * const a = createGraphNode({ id: 'n1', label: 'hello', x: 0 });
381
+ * const b = createGraphNode({ id: 'n1', label: 'hello', x: 100 });
382
+ *
383
+ * isNonLayoutEqual(a, b); // true (layout changed, but non-layout didn't)
384
+ * ```
385
+ */
386
+ declare function isNonLayoutEqual<T extends GraphNode | GraphEdge>(a: T, b: T): boolean;
387
+ //#endregion
327
388
  //#region src/diff.d.ts
328
389
  /**
329
390
  * Compute a structured diff from graph `a` to graph `b` by matching IDs.
@@ -567,4 +628,4 @@ declare function getCoverage<N, E>(graph: Graph<N, E>, steps: GraphStep<N, E>[],
567
628
  from?: string;
568
629
  }): CoverageStats;
569
630
  //#endregion
570
- export { type AStarOptions, type AllPairsShortestPathsOptions, type CoverageStats, type DeleteNodeOptions, type EdgeChange, type EdgeConfig, type EntitiesConfig, type EntitiesUpdate, type Graph, type GraphConfig, type GraphDiff, type GraphEdge, type GraphFormatConverter, GraphInstance, type GraphNode, type GraphPatch, type GraphPath, type GraphStep, type MSTOptions, type NodeChange, type NodeConfig, type PathOptions, type EntityRect as Positioned, type SinglePathOptions, type TransitionOptions, type TraversalOptions, type VisualEdge, type VisualGraph, type VisualGraphConfig, type VisualNode, type WalkContext, type WalkOptions, type WeightedWalkOptions, addEdge, addEntities, addNode, applyPatches, bfs, createFormatConverter, createGraph, createGraphEdge, createGraphFromTransition, createGraphNode, createVisualGraph, deleteEdge, deleteEntities, deleteNode, dfs, flatten, genCycles, genPostorders, genPredefinedWalk, genPreorders, genQuickRandomWalk, genRandomWalk, genShortestPaths, genSimplePaths, genWeightedRandomWalk, getAStarPath, getAllPairsShortestPaths, getAncestors, getChildren, getConnectedComponents, getCoverage, getCycles, getDegree, getDepth, getDescendants, getDiff, getEdge, getEdgeBetween, getEdgesOf, getInDegree, getInEdges, getLCA, getMinimumSpanningTree, getNeighbors, getNode, getOutDegree, getOutEdges, getParent, getPatches, getPostorder, getPostorders, getPredecessors, getPreorder, getPreorders, getRelativeDistance, getRelativeDistanceMap, getRoots, getShortestPath, getShortestPaths, getSiblings, getSimplePath, getSimplePaths, getSinks, getSources, getStronglyConnectedComponents, getSubgraph, getSuccessors, getTopologicalSort, hasEdge, hasNode, hasPath, invalidateIndex, invertDiff, isAcyclic, isCompound, isConnected, isEmptyDiff, isLeaf, isTree, joinPaths, reverseGraph, takeSteps, takeUntilEdge, takeUntilEdgeCoverage, takeUntilNode, takeUntilNodeCoverage, toDiff, toPatches, updateEdge, updateEntities, updateNode };
631
+ export { type AStarOptions, type AllPairsShortestPathsOptions, type CoverageStats, type DeleteNodeOptions, type EdgeChange, type EdgeConfig, type EntitiesConfig, type EntitiesUpdate, type GirvanNewmanOptions, type Graph, type GraphConfig, type GraphDiff, type GraphEdge, type GraphEntity, type GraphEntityConfig, type GraphFormatConverter, GraphInstance, type GraphNode, type GraphPatch, type GraphPath, type GraphPort, type GraphStep, type HITSResult, type IsomorphismOptions, type IterativeCentralityOptions, LAYOUT_KEYS, type LabelPropagationOptions, type MSTOptions, type NodeChange, type NodeConfig, type PathOptions, type PortConfig, type PortDirection, type EntityRect as Positioned, type SinglePathOptions, type TransitionOptions, type TraversalOptions, type VisualEdge, type VisualGraph, type VisualGraphConfig, type VisualGraphEntity, type VisualNode, type VisualPort, type WalkContext, type WalkOptions, type WeightedWalkOptions, addEdge, addEntities, addNode, applyPatches, areEntitiesEqual, bfs, createFormatConverter, createGraph, createGraphEdge, createGraphFromTransition, createGraphNode, createGraphPort, createVisualGraph, deleteEdge, deleteEntities, deleteNode, dfs, flatten, genCycles, genGirvanNewmanCommunities, genPostorders, genPredefinedWalk, genPreorders, genQuickRandomWalk, genRandomWalk, genShortestPaths, genSimplePaths, genWeightedRandomWalk, getAStarPath, getAllPairsShortestPaths, getAncestors, getArticulationPoints, getBetweennessCentrality, getBiconnectedComponents, getBridges, getChildren, getClosenessCentrality, getConnectedComponents, getCoverage, getCycles, getDegree, getDegreeCentrality, getDepth, getDescendants, getDiff, getEdge, getEdgeBetween, getEdgesByPort, getEdgesOf, getEigenvectorCentrality, getGirvanNewmanCommunities, getGreedyModularityCommunities, getHITS, getInDegree, getInDegreeCentrality, getInEdges, getLCA, getLabelPropagationCommunities, getMinimumSpanningTree, getModularity, getNeighbors, getNode, getOutDegree, getOutDegreeCentrality, getOutEdges, getPageRank, getParent, getPatches, getPort, getPorts, getPostorder, getPostorders, getPredecessors, getPreorder, getPreorders, getRelativeDistance, getRelativeDistanceMap, getRoots, getShortestPath, getShortestPaths, getSiblings, getSimplePath, getSimplePaths, getSinks, getSources, getStronglyConnectedComponents, getSubgraph, getSuccessors, getTopologicalSort, hasEdge, hasNode, hasPath, invalidateIndex, invertDiff, isAcyclic, isCompound, isConnected, isEmptyDiff, isIsomorphic, isLayoutEqual, isLeaf, isNonLayoutEqual, isTree, joinPaths, reverseGraph, takeSteps, takeUntilEdge, takeUntilEdgeCoverage, takeUntilNode, takeUntilNodeCoverage, toDiff, toPatches, updateEdge, updateEntities, updateNode };
package/dist/index.mjs CHANGED
@@ -1,8 +1,100 @@
1
1
  import { o as invalidateIndex, t as getIndex } from "./indexing-DyfgLuzw.mjs";
2
- import { A as addEntities, B as getEdge, C as hasPath, D as joinPaths, E as isTree, F as createGraphNode, G as updateEntities, H as hasEdge, I as createVisualGraph, K as updateNode, L as deleteEdge, M as createGraph, N as createGraphEdge, O as GraphInstance, P as createGraphFromTransition, R as deleteEntities, S as getTopologicalSort, T as isConnected, U as hasNode, V as getNode, W as updateEdge, _ as getShortestPath, a as genPreorders, b as getSimplePaths, c as getAStarPath, d as getCycles, f as getMinimumSpanningTree, g as getPreorders, h as getPreorder, i as genPostorders, j as addNode, k as addEdge, l as getAllPairsShortestPaths, m as getPostorders, n as dfs, o as genShortestPaths, p as getPostorder, r as genCycles, s as genSimplePaths, t as bfs, u as getConnectedComponents, v as getShortestPaths, w as isAcyclic, x as getStronglyConnectedComponents, y as getSimplePath, z as deleteNode } from "./algorithms-Dw5jEwDK.mjs";
3
- import { getAncestors, getChildren, getDegree, getDepth, getDescendants, getEdgeBetween, getEdgesOf, getInDegree, getInEdges, getLCA, getNeighbors, getOutDegree, getOutEdges, getParent, getPredecessors, getRelativeDistance, getRelativeDistanceMap, getRoots, getSiblings, getSinks, getSources, getSuccessors, isCompound, isLeaf } from "./queries.mjs";
2
+ import { $ as createGraphPort, A as getBiconnectedComponents, B as getEigenvectorCentrality, C as hasPath, D as joinPaths, E as isTree, F as getLabelPropagationCommunities, G as GraphInstance, H as getInDegreeCentrality, I as getModularity, J as addNode, K as addEdge, L as getBetweennessCentrality, M as genGirvanNewmanCommunities, N as getGirvanNewmanCommunities, O as isIsomorphic, P as getGreedyModularityCommunities, Q as createGraphNode, R as getClosenessCentrality, S as getTopologicalSort, T as isConnected, U as getOutDegreeCentrality, V as getHITS, W as getPageRank, X as createGraphEdge, Y as createGraph, Z as createGraphFromTransition, _ as getShortestPath, a as genPreorders, at as getNode, b as getSimplePaths, c as getAStarPath, ct as updateEdge, d as getCycles, et as createVisualGraph, f as getMinimumSpanningTree, g as getPreorders, h as getPreorder, i as genPostorders, it as getEdge, j as getBridges, k as getArticulationPoints, l as getAllPairsShortestPaths, lt as updateEntities, m as getPostorders, n as dfs, nt as deleteEntities, o as genShortestPaths, ot as hasEdge, p as getPostorder, q as addEntities, r as genCycles, rt as deleteNode, s as genSimplePaths, st as hasNode, t as bfs, tt as deleteEdge, u as getConnectedComponents, ut as updateNode, v as getShortestPaths, w as isAcyclic, x as getStronglyConnectedComponents, y as getSimplePath, z as getDegreeCentrality } from "./algorithms-3xvCxHzo.mjs";
3
+ import { getAncestors, getChildren, getDegree, getDepth, getDescendants, getEdgeBetween, getEdgesByPort, getEdgesOf, getInDegree, getInEdges, getLCA, getNeighbors, getOutDegree, getOutEdges, getParent, getPort, getPorts, getPredecessors, getRelativeDistance, getRelativeDistanceMap, getRoots, getSiblings, getSinks, getSources, getSuccessors, isCompound, isLeaf } from "./queries.mjs";
4
4
  import { n as createFormatConverter } from "./converter-B5CUD0r9.mjs";
5
5
 
6
+ //#region src/equivalence.ts
7
+ /** Shallow-compare two values, returning true if they differ. */
8
+ function differs$1(a, b) {
9
+ if (a === b) return false;
10
+ if (a == null || b == null) return a !== b;
11
+ if (typeof a === "object" && typeof b === "object") return JSON.stringify(a) !== JSON.stringify(b);
12
+ return true;
13
+ }
14
+ const LAYOUT_KEYS = {
15
+ node: [
16
+ "x",
17
+ "y",
18
+ "width",
19
+ "height",
20
+ "style",
21
+ "color",
22
+ "shape"
23
+ ],
24
+ edge: [
25
+ "x",
26
+ "y",
27
+ "width",
28
+ "height",
29
+ "style",
30
+ "color"
31
+ ]
32
+ };
33
+ const LAYOUT_KEY_SET = {
34
+ node: new Set(LAYOUT_KEYS.node),
35
+ edge: new Set(LAYOUT_KEYS.edge)
36
+ };
37
+ /**
38
+ * Compare two entities on a given set of keys.
39
+ * If `keys` is omitted or empty, compares all own keys of `a`.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * import { createGraphNode, areEntitiesEqual, LAYOUT_KEYS } from '@statelyai/graph';
44
+ *
45
+ * const a = createGraphNode({ id: 'n1', label: 'hello', x: 0 });
46
+ * const b = createGraphNode({ id: 'n1', label: 'hello', x: 100 });
47
+ *
48
+ * areEntitiesEqual(a, b, LAYOUT_KEYS.node); // false (x differs)
49
+ * areEntitiesEqual(a, b, NON_LAYOUT_KEYS.node); // true
50
+ * areEntitiesEqual(a, b); // false (compares all keys)
51
+ * ```
52
+ */
53
+ function areEntitiesEqual(a, b, keys) {
54
+ const compareKeys = keys && keys.length > 0 ? keys : Object.keys(a);
55
+ for (const key of compareKeys) if (differs$1(a[key], b[key])) return false;
56
+ return true;
57
+ }
58
+ /**
59
+ * Compare two entities on layout keys only (position, size, style, color, shape).
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * import { createGraphNode, isLayoutEqual } from '@statelyai/graph';
64
+ *
65
+ * const a = createGraphNode({ id: 'n1', x: 0, y: 0 });
66
+ * const b = createGraphNode({ id: 'n1', x: 100, y: 200 });
67
+ *
68
+ * isLayoutEqual(a, b); // false
69
+ * ```
70
+ */
71
+ function isLayoutEqual(a, b) {
72
+ return areEntitiesEqual(a, b, LAYOUT_KEYS[a.type]);
73
+ }
74
+ /**
75
+ * Compare two entities on non-layout keys only (id, data, connections, labels, etc.).
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * import { createGraphNode, isNonLayoutEqual } from '@statelyai/graph';
80
+ *
81
+ * const a = createGraphNode({ id: 'n1', label: 'hello', x: 0 });
82
+ * const b = createGraphNode({ id: 'n1', label: 'hello', x: 100 });
83
+ *
84
+ * isNonLayoutEqual(a, b); // true (layout changed, but non-layout didn't)
85
+ * ```
86
+ */
87
+ function isNonLayoutEqual(a, b) {
88
+ const skip = LAYOUT_KEY_SET[a.type];
89
+ const keys = Object.keys(a);
90
+ for (let i = 0; i < keys.length; i++) {
91
+ if (skip.has(keys[i])) continue;
92
+ if (differs$1(a[keys[i]], b[keys[i]])) return false;
93
+ }
94
+ return true;
95
+ }
96
+
97
+ //#endregion
6
98
  //#region src/diff.ts
7
99
  function nodeToConfig$1(node) {
8
100
  const config = { id: node.id };
@@ -813,4 +905,4 @@ function getCoverage(graph, steps, options) {
813
905
  }
814
906
 
815
907
  //#endregion
816
- export { GraphInstance, addEdge, addEntities, addNode, applyPatches, bfs, createFormatConverter, createGraph, createGraphEdge, createGraphFromTransition, createGraphNode, createVisualGraph, deleteEdge, deleteEntities, deleteNode, dfs, flatten, genCycles, genPostorders, genPredefinedWalk, genPreorders, genQuickRandomWalk, genRandomWalk, genShortestPaths, genSimplePaths, genWeightedRandomWalk, getAStarPath, getAllPairsShortestPaths, getAncestors, getChildren, getConnectedComponents, getCoverage, getCycles, getDegree, getDepth, getDescendants, getDiff, getEdge, getEdgeBetween, getEdgesOf, getInDegree, getInEdges, getLCA, getMinimumSpanningTree, getNeighbors, getNode, getOutDegree, getOutEdges, getParent, getPatches, getPostorder, getPostorders, getPredecessors, getPreorder, getPreorders, getRelativeDistance, getRelativeDistanceMap, getRoots, getShortestPath, getShortestPaths, getSiblings, getSimplePath, getSimplePaths, getSinks, getSources, getStronglyConnectedComponents, getSubgraph, getSuccessors, getTopologicalSort, hasEdge, hasNode, hasPath, invalidateIndex, invertDiff, isAcyclic, isCompound, isConnected, isEmptyDiff, isLeaf, isTree, joinPaths, reverseGraph, takeSteps, takeUntilEdge, takeUntilEdgeCoverage, takeUntilNode, takeUntilNodeCoverage, toDiff, toPatches, updateEdge, updateEntities, updateNode };
908
+ export { GraphInstance, LAYOUT_KEYS, addEdge, addEntities, addNode, applyPatches, areEntitiesEqual, bfs, createFormatConverter, createGraph, createGraphEdge, createGraphFromTransition, createGraphNode, createGraphPort, createVisualGraph, deleteEdge, deleteEntities, deleteNode, dfs, flatten, genCycles, genGirvanNewmanCommunities, genPostorders, genPredefinedWalk, genPreorders, genQuickRandomWalk, genRandomWalk, genShortestPaths, genSimplePaths, genWeightedRandomWalk, getAStarPath, getAllPairsShortestPaths, getAncestors, getArticulationPoints, getBetweennessCentrality, getBiconnectedComponents, getBridges, getChildren, getClosenessCentrality, getConnectedComponents, getCoverage, getCycles, getDegree, getDegreeCentrality, getDepth, getDescendants, getDiff, getEdge, getEdgeBetween, getEdgesByPort, getEdgesOf, getEigenvectorCentrality, getGirvanNewmanCommunities, getGreedyModularityCommunities, getHITS, getInDegree, getInDegreeCentrality, getInEdges, getLCA, getLabelPropagationCommunities, getMinimumSpanningTree, getModularity, getNeighbors, getNode, getOutDegree, getOutDegreeCentrality, getOutEdges, getPageRank, getParent, getPatches, getPort, getPorts, getPostorder, getPostorders, getPredecessors, getPreorder, getPreorders, getRelativeDistance, getRelativeDistanceMap, getRoots, getShortestPath, getShortestPaths, getSiblings, getSimplePath, getSimplePaths, getSinks, getSources, getStronglyConnectedComponents, getSubgraph, getSuccessors, getTopologicalSort, hasEdge, hasNode, hasPath, invalidateIndex, invertDiff, isAcyclic, isCompound, isConnected, isEmptyDiff, isIsomorphic, isLayoutEqual, isLeaf, isNonLayoutEqual, isTree, joinPaths, reverseGraph, takeSteps, takeUntilEdge, takeUntilEdgeCoverage, takeUntilNode, takeUntilNodeCoverage, toDiff, toPatches, updateEdge, updateEntities, updateNode };
@@ -1,4 +1,4 @@
1
- import { h as GraphNode, p as GraphEdge, u as Graph } from "./types-DkKjaQW3.mjs";
1
+ import { _ as GraphNode, b as GraphPort, p as GraphEdge, u as Graph } from "./types-BzckPChi.mjs";
2
2
 
3
3
  //#region src/queries.d.ts
4
4
 
@@ -454,5 +454,62 @@ declare function getSources<N>(graph: Graph<N>): GraphNode<N>[];
454
454
  * ```
455
455
  */
456
456
  declare function getSinks<N>(graph: Graph<N>): GraphNode<N>[];
457
+ /**
458
+ * Get a port by name on a node, or `undefined` if not found.
459
+ *
460
+ * @example
461
+ * ```ts
462
+ * const graph = createGraph({
463
+ * nodes: [{
464
+ * id: 'a',
465
+ * ports: [{ name: 'out', direction: 'out' }],
466
+ * }],
467
+ * });
468
+ * getPort(graph, 'a', 'out'); // => { name: 'out', direction: 'out', ... }
469
+ * getPort(graph, 'a', 'missing'); // => undefined
470
+ * ```
471
+ */
472
+ declare function getPort<P = any>(graph: Graph<any, any, any, P>, nodeId: string, portName: string): GraphPort<P> | undefined;
473
+ /**
474
+ * Get all ports on a node. Returns `[]` if the node has no ports or doesn't exist.
475
+ *
476
+ * @example
477
+ * ```ts
478
+ * const graph = createGraph({
479
+ * nodes: [{
480
+ * id: 'a',
481
+ * ports: [
482
+ * { name: 'in', direction: 'in' },
483
+ * { name: 'out', direction: 'out' },
484
+ * ],
485
+ * }],
486
+ * });
487
+ * getPorts(graph, 'a'); // => [port in, port out]
488
+ * ```
489
+ */
490
+ declare function getPorts<P = any>(graph: Graph<any, any, any, P>, nodeId: string): GraphPort<P>[];
491
+ /**
492
+ * Get all edges connected to a specific port on a node.
493
+ *
494
+ * Returns edges where:
495
+ * - `sourceId === nodeId && sourcePort === portName`, or
496
+ * - `targetId === nodeId && targetPort === portName`
497
+ *
498
+ * @example
499
+ * ```ts
500
+ * const graph = createGraph({
501
+ * nodes: [
502
+ * { id: 'a', ports: [{ name: 'out', direction: 'out' }] },
503
+ * { id: 'b', ports: [{ name: 'in', direction: 'in' }] },
504
+ * ],
505
+ * edges: [{
506
+ * id: 'e1', sourceId: 'a', targetId: 'b',
507
+ * sourcePort: 'out', targetPort: 'in',
508
+ * }],
509
+ * });
510
+ * getEdgesByPort(graph, 'a', 'out'); // => [edge e1]
511
+ * ```
512
+ */
513
+ declare function getEdgesByPort<E = any>(graph: Graph<any, E>, nodeId: string, portName: string): GraphEdge<E>[];
457
514
  //#endregion
458
- export { getAncestors, getChildren, getDegree, getDepth, getDescendants, getEdgeBetween, getEdgesOf, getInDegree, getInEdges, getLCA, getNeighbors, getOutDegree, getOutEdges, getParent, getPredecessors, getRelativeDistance, getRelativeDistanceMap, getRoots, getSiblings, getSinks, getSources, getSuccessors, isCompound, isLeaf };
515
+ export { getAncestors, getChildren, getDegree, getDepth, getDescendants, getEdgeBetween, getEdgesByPort, getEdgesOf, getInDegree, getInEdges, getLCA, getNeighbors, getOutDegree, getOutEdges, getParent, getPort, getPorts, getPredecessors, getRelativeDistance, getRelativeDistanceMap, getRoots, getSiblings, getSinks, getSources, getSuccessors, isCompound, isLeaf };
package/dist/queries.mjs CHANGED
@@ -679,6 +679,83 @@ function getSinks(graph) {
679
679
  const idx = getIndex(graph);
680
680
  return graph.nodes.filter((n) => (idx.outEdges.get(n.id)?.length ?? 0) === 0);
681
681
  }
682
+ /**
683
+ * Get a port by name on a node, or `undefined` if not found.
684
+ *
685
+ * @example
686
+ * ```ts
687
+ * const graph = createGraph({
688
+ * nodes: [{
689
+ * id: 'a',
690
+ * ports: [{ name: 'out', direction: 'out' }],
691
+ * }],
692
+ * });
693
+ * getPort(graph, 'a', 'out'); // => { name: 'out', direction: 'out', ... }
694
+ * getPort(graph, 'a', 'missing'); // => undefined
695
+ * ```
696
+ */
697
+ function getPort(graph, nodeId, portName) {
698
+ const ni = getIndex(graph).nodeById.get(nodeId);
699
+ if (ni === void 0) return void 0;
700
+ return graph.nodes[ni].ports?.find((p) => p.name === portName);
701
+ }
702
+ /**
703
+ * Get all ports on a node. Returns `[]` if the node has no ports or doesn't exist.
704
+ *
705
+ * @example
706
+ * ```ts
707
+ * const graph = createGraph({
708
+ * nodes: [{
709
+ * id: 'a',
710
+ * ports: [
711
+ * { name: 'in', direction: 'in' },
712
+ * { name: 'out', direction: 'out' },
713
+ * ],
714
+ * }],
715
+ * });
716
+ * getPorts(graph, 'a'); // => [port in, port out]
717
+ * ```
718
+ */
719
+ function getPorts(graph, nodeId) {
720
+ const ni = getIndex(graph).nodeById.get(nodeId);
721
+ if (ni === void 0) return [];
722
+ return graph.nodes[ni].ports ?? [];
723
+ }
724
+ /**
725
+ * Get all edges connected to a specific port on a node.
726
+ *
727
+ * Returns edges where:
728
+ * - `sourceId === nodeId && sourcePort === portName`, or
729
+ * - `targetId === nodeId && targetPort === portName`
730
+ *
731
+ * @example
732
+ * ```ts
733
+ * const graph = createGraph({
734
+ * nodes: [
735
+ * { id: 'a', ports: [{ name: 'out', direction: 'out' }] },
736
+ * { id: 'b', ports: [{ name: 'in', direction: 'in' }] },
737
+ * ],
738
+ * edges: [{
739
+ * id: 'e1', sourceId: 'a', targetId: 'b',
740
+ * sourcePort: 'out', targetPort: 'in',
741
+ * }],
742
+ * });
743
+ * getEdgesByPort(graph, 'a', 'out'); // => [edge e1]
744
+ * ```
745
+ */
746
+ function getEdgesByPort(graph, nodeId, portName) {
747
+ const idx = getIndex(graph);
748
+ const result = [];
749
+ for (const eid of idx.outEdges.get(nodeId) ?? []) {
750
+ const ai = idx.edgeById.get(eid);
751
+ if (ai !== void 0 && graph.edges[ai].sourcePort === portName) result.push(graph.edges[ai]);
752
+ }
753
+ for (const eid of idx.inEdges.get(nodeId) ?? []) {
754
+ const ai = idx.edgeById.get(eid);
755
+ if (ai !== void 0 && graph.edges[ai].targetPort === portName) result.push(graph.edges[ai]);
756
+ }
757
+ return result;
758
+ }
682
759
 
683
760
  //#endregion
684
- export { getAncestors, getChildren, getDegree, getDepth, getDescendants, getEdgeBetween, getEdgesOf, getInDegree, getInEdges, getLCA, getNeighbors, getOutDegree, getOutEdges, getParent, getPredecessors, getRelativeDistance, getRelativeDistanceMap, getRoots, getSiblings, getSinks, getSources, getSuccessors, isCompound, isLeaf };
761
+ export { getAncestors, getChildren, getDegree, getDepth, getDescendants, getEdgeBetween, getEdgesByPort, getEdgesOf, getInDegree, getInEdges, getLCA, getNeighbors, getOutDegree, getOutEdges, getParent, getPort, getPorts, getPredecessors, getRelativeDistance, getRelativeDistanceMap, getRoots, getSiblings, getSinks, getSources, getSuccessors, isCompound, isLeaf };