@statelyai/graph 0.7.0 → 0.8.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.
@@ -2,6 +2,37 @@ import { a as indexUpdateEdgeEndpoints, i as indexReparentNode, n as indexAddEdg
2
2
 
3
3
  //#region src/graph.ts
4
4
  /**
5
+ * Create a resolved graph port from a config. Fills in defaults.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const port = createGraphPort({ name: 'output', direction: 'out' });
10
+ * // { name: 'output', direction: 'out', data: undefined }
11
+ * ```
12
+ */
13
+ function createGraphPort(config) {
14
+ if (!config.name) throw new Error("Port name must be a non-empty string");
15
+ const port = {
16
+ name: config.name,
17
+ direction: config.direction ?? "inout",
18
+ data: config.data
19
+ };
20
+ if (config.label !== void 0) port.label = config.label;
21
+ if (config.x !== void 0) port.x = config.x;
22
+ if (config.y !== void 0) port.y = config.y;
23
+ if (config.width !== void 0) port.width = config.width;
24
+ if (config.height !== void 0) port.height = config.height;
25
+ if (config.style !== void 0) port.style = config.style;
26
+ return port;
27
+ }
28
+ function validatePortNames(ports) {
29
+ const seen = /* @__PURE__ */ new Set();
30
+ for (const port of ports) {
31
+ if (seen.has(port.name)) throw new Error(`Duplicate port name "${port.name}" on node`);
32
+ seen.add(port.name);
33
+ }
34
+ }
35
+ /**
5
36
  * Create a resolved graph node from a config. Fills in defaults.
6
37
  *
7
38
  * @example
@@ -21,6 +52,10 @@ function createGraphNode(config) {
21
52
  label: config.label ?? "",
22
53
  data: config.data
23
54
  };
55
+ if (config.ports !== void 0 && config.ports.length > 0) {
56
+ validatePortNames(config.ports);
57
+ node.ports = config.ports.map(createGraphPort);
58
+ }
24
59
  if (config.x !== void 0) node.x = config.x;
25
60
  if (config.y !== void 0) node.y = config.y;
26
61
  if (config.width !== void 0) node.width = config.width;
@@ -51,6 +86,8 @@ function createGraphEdge(config) {
51
86
  label: config.label ?? null,
52
87
  data: config.data
53
88
  };
89
+ if (config.sourcePort !== void 0) edge.sourcePort = config.sourcePort;
90
+ if (config.targetPort !== void 0) edge.targetPort = config.targetPort;
54
91
  if (config.weight !== void 0) edge.weight = config.weight;
55
92
  if (config.x !== void 0) edge.x = config.x;
56
93
  if (config.y !== void 0) edge.y = config.y;
@@ -101,14 +138,24 @@ function createVisualGraph(config) {
101
138
  return {
102
139
  ...base,
103
140
  direction: config?.direction ?? "down",
104
- nodes: base.nodes.map((n) => ({
105
- ...n,
106
- x: n.x ?? 0,
107
- y: n.y ?? 0,
108
- width: n.width ?? 0,
109
- height: n.height ?? 0,
110
- ...n.shape !== void 0 && { shape: n.shape }
111
- })),
141
+ nodes: base.nodes.map((n) => {
142
+ const { ports, ...rest } = n;
143
+ return {
144
+ ...rest,
145
+ x: n.x ?? 0,
146
+ y: n.y ?? 0,
147
+ width: n.width ?? 0,
148
+ height: n.height ?? 0,
149
+ ...n.shape !== void 0 && { shape: n.shape },
150
+ ...ports !== void 0 && { ports: ports.map((p) => ({
151
+ ...p,
152
+ x: p.x ?? 0,
153
+ y: p.y ?? 0,
154
+ width: p.width ?? 0,
155
+ height: p.height ?? 0
156
+ })) }
157
+ };
158
+ }),
112
159
  edges: base.edges.map((e) => ({
113
160
  ...e,
114
161
  x: e.x ?? 0,
@@ -299,6 +346,12 @@ function addEdge(graph, config) {
299
346
  if (idx.edgeById.has(config.id)) throw new Error(`Edge "${config.id}" already exists`);
300
347
  if (!idx.nodeById.has(config.sourceId)) throw new Error(`Source node "${config.sourceId}" does not exist`);
301
348
  if (!idx.nodeById.has(config.targetId)) throw new Error(`Target node "${config.targetId}" does not exist`);
349
+ if (config.sourcePort !== void 0) {
350
+ if (!graph.nodes[idx.nodeById.get(config.sourceId)].ports?.some((p) => p.name === config.sourcePort)) throw new Error(`Port "${config.sourcePort}" does not exist on source node "${config.sourceId}"`);
351
+ }
352
+ if (config.targetPort !== void 0) {
353
+ if (!graph.nodes[idx.nodeById.get(config.targetId)].ports?.some((p) => p.name === config.targetPort)) throw new Error(`Port "${config.targetPort}" does not exist on target node "${config.targetId}"`);
354
+ }
302
355
  indexAddEdge(idx, edge, graph.edges.push(edge) - 1);
303
356
  return edge;
304
357
  }
@@ -369,6 +422,7 @@ function updateNode(graph, id, update) {
369
422
  if (update.parentId !== void 0 && update.parentId !== null) {
370
423
  if (!idx.nodeById.has(update.parentId)) throw new Error(`Parent node "${update.parentId}" does not exist`);
371
424
  }
425
+ if (update.ports !== void 0 && update.ports.length > 0) validatePortNames(update.ports);
372
426
  const node = graph.nodes[arrayIdx];
373
427
  const oldParentId = node.parentId;
374
428
  const updated = {
@@ -376,7 +430,8 @@ function updateNode(graph, id, update) {
376
430
  ...update.parentId !== void 0 && { parentId: update.parentId ?? null },
377
431
  ...update.initialNodeId !== void 0 && { initialNodeId: update.initialNodeId ?? null },
378
432
  ...update.label !== void 0 && { label: update.label },
379
- ...update.data !== void 0 && { data: update.data }
433
+ ...update.data !== void 0 && { data: update.data },
434
+ ...update.ports !== void 0 && { ports: update.ports.map(createGraphPort) }
380
435
  };
381
436
  graph.nodes[arrayIdx] = updated;
382
437
  if (update.parentId !== void 0 && updated.parentId !== oldParentId) indexReparentNode(idx, id, oldParentId, updated.parentId);
@@ -405,12 +460,22 @@ function updateEdge(graph, id, update) {
405
460
  const edge = graph.edges[arrayIdx];
406
461
  const oldSourceId = edge.sourceId;
407
462
  const oldTargetId = edge.targetId;
463
+ const effectiveSourceId = update.sourceId ?? edge.sourceId;
464
+ const effectiveTargetId = update.targetId ?? edge.targetId;
465
+ if (update.sourcePort !== void 0) {
466
+ if (!graph.nodes[idx.nodeById.get(effectiveSourceId)].ports?.some((p) => p.name === update.sourcePort)) throw new Error(`Port "${update.sourcePort}" does not exist on source node "${effectiveSourceId}"`);
467
+ }
468
+ if (update.targetPort !== void 0) {
469
+ if (!graph.nodes[idx.nodeById.get(effectiveTargetId)].ports?.some((p) => p.name === update.targetPort)) throw new Error(`Port "${update.targetPort}" does not exist on target node "${effectiveTargetId}"`);
470
+ }
408
471
  const updated = {
409
472
  ...edge,
410
473
  ...update.sourceId !== void 0 && { sourceId: update.sourceId },
411
474
  ...update.targetId !== void 0 && { targetId: update.targetId },
412
475
  ...update.label !== void 0 && { label: update.label },
413
- ...update.data !== void 0 && { data: update.data }
476
+ ...update.data !== void 0 && { data: update.data },
477
+ ...update.sourcePort !== void 0 && { sourcePort: update.sourcePort },
478
+ ...update.targetPort !== void 0 && { targetPort: update.targetPort }
414
479
  };
415
480
  graph.edges[arrayIdx] = updated;
416
481
  if (updated.sourceId !== oldSourceId || updated.targetId !== oldTargetId) indexUpdateEdgeEndpoints(idx, id, oldSourceId, oldTargetId, updated.sourceId, updated.targetId);
@@ -2139,4 +2204,4 @@ function joinPaths(headPath, tailPath) {
2139
2204
  }
2140
2205
 
2141
2206
  //#endregion
2142
- export { addEntities as A, getEdge as B, hasPath as C, joinPaths as D, isTree as E, createGraphNode as F, updateEntities as G, hasEdge as H, createVisualGraph as I, updateNode as K, deleteEdge as L, createGraph as M, createGraphEdge as N, GraphInstance as O, createGraphFromTransition as P, deleteEntities as R, getTopologicalSort as S, isConnected as T, hasNode as U, getNode as V, updateEdge as W, getShortestPath as _, genPreorders as a, getSimplePaths as b, getAStarPath as c, getCycles as d, getMinimumSpanningTree as f, getPreorders as g, getPreorder as h, genPostorders as i, addNode as j, addEdge as k, getAllPairsShortestPaths as l, getPostorders as m, dfs as n, genShortestPaths as o, getPostorder as p, genCycles as r, genSimplePaths as s, bfs as t, getConnectedComponents as u, getShortestPaths as v, isAcyclic as w, getStronglyConnectedComponents as x, getSimplePath as y, deleteNode as z };
2207
+ export { addEntities as A, deleteNode as B, hasPath as C, joinPaths as D, isTree as E, createGraphNode as F, updateEdge as G, getNode as H, createGraphPort as I, updateEntities as K, createVisualGraph as L, createGraph as M, createGraphEdge as N, GraphInstance as O, createGraphFromTransition as P, deleteEdge as R, getTopologicalSort as S, isConnected as T, hasEdge as U, getEdge as V, hasNode as W, getShortestPath as _, genPreorders as a, getSimplePaths as b, getAStarPath as c, getCycles as d, getMinimumSpanningTree as f, getPreorders as g, getPreorder as h, genPostorders as i, addNode as j, addEdge as k, getAllPairsShortestPaths as l, getPostorders as m, dfs as n, genShortestPaths as o, getPostorder as p, updateNode as q, genCycles as r, genSimplePaths as s, bfs as t, getConnectedComponents as u, getShortestPaths as v, isAcyclic as w, getStronglyConnectedComponents as x, getSimplePath as y, deleteEntities as z };
@@ -1,4 +1,4 @@
1
- import { C as SinglePathOptions, S as PathOptions, T as TraversalOptions, _ as GraphPath, h as GraphNode, n as AllPairsShortestPathsOptions, t as AStarOptions, u as Graph, y as MSTOptions } from "./types-DkKjaQW3.mjs";
1
+ import { A as TraversalOptions, O as SinglePathOptions, S as MSTOptions, T as PathOptions, _ as GraphNode, n as AllPairsShortestPathsOptions, t as AStarOptions, u as Graph, y as GraphPath } from "./types-BzckPChi.mjs";
2
2
 
3
3
  //#region src/algorithms.d.ts
4
4
 
@@ -1,3 +1,3 @@
1
- import { C as hasPath, D as joinPaths, E as isTree, S as getTopologicalSort, T as isConnected, _ 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, 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 } from "./algorithms-Dw5jEwDK.mjs";
1
+ import { C as hasPath, D as joinPaths, E as isTree, S as getTopologicalSort, T as isConnected, _ 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, 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 } from "./algorithms-Pbj9dJB1.mjs";
2
2
 
3
3
  export { 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 };
@@ -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/adjacency-list/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/converter/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/cytoscape/index.d.ts
4
4
  interface CytoscapeNode {
@@ -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/d3/index.d.ts
4
4
  interface D3Node {
@@ -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/dot/index.d.ts
4
4
 
@@ -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";
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
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";
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
@@ -567,4 +577,4 @@ declare function getCoverage<N, E>(graph: Graph<N, E>, steps: GraphStep<N, E>[],
567
577
  from?: string;
568
578
  }): CoverageStats;
569
579
  //#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 };
580
+ 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 GraphEntity, type GraphEntityConfig, type GraphFormatConverter, GraphInstance, type GraphNode, type GraphPatch, type GraphPath, type GraphPort, type GraphStep, 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, bfs, createFormatConverter, createGraph, createGraphEdge, createGraphFromTransition, createGraphNode, createGraphPort, 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, getEdgesByPort, getEdgesOf, getInDegree, getInEdges, getLCA, getMinimumSpanningTree, getNeighbors, getNode, getOutDegree, getOutEdges, 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, isLeaf, isTree, joinPaths, reverseGraph, takeSteps, takeUntilEdge, takeUntilEdgeCoverage, takeUntilNode, takeUntilNodeCoverage, toDiff, toPatches, updateEdge, updateEntities, updateNode };
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
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 { A as addEntities, B as deleteNode, C as hasPath, D as joinPaths, E as isTree, F as createGraphNode, G as updateEdge, H as getNode, I as createGraphPort, K as updateEntities, L as createVisualGraph, M as createGraph, N as createGraphEdge, O as GraphInstance, P as createGraphFromTransition, R as deleteEdge, S as getTopologicalSort, T as isConnected, U as hasEdge, V as getEdge, W as hasNode, _ 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, q as updateNode, 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 deleteEntities } from "./algorithms-Pbj9dJB1.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
6
  //#region src/diff.ts
@@ -813,4 +813,4 @@ function getCoverage(graph, steps, options) {
813
813
  }
814
814
 
815
815
  //#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 };
816
+ export { GraphInstance, addEdge, addEntities, addNode, applyPatches, bfs, createFormatConverter, createGraph, createGraphEdge, createGraphFromTransition, createGraphNode, createGraphPort, 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, getEdgesByPort, getEdgesOf, getInDegree, getInEdges, getLCA, getMinimumSpanningTree, getNeighbors, getNode, getOutDegree, getOutEdges, 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, isLeaf, 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 };
@@ -5,31 +5,59 @@ interface EntityRect {
5
5
  width: number;
6
6
  height: number;
7
7
  }
8
- interface GraphConfig<TNodeData = any, TEdgeData = any, TGraphData = any> {
8
+ /** Config-level base shared optional visual/style props for nodes, edges, ports. */
9
+ interface GraphEntityConfig {
10
+ x?: number;
11
+ y?: number;
12
+ width?: number;
13
+ height?: number;
14
+ style?: Record<string, string | number>;
15
+ }
16
+ /** Resolved entity base — optional visual props (non-visual graphs may omit). */
17
+ interface GraphEntity extends GraphEntityConfig {}
18
+ /** Visual entity base — required position/size. */
19
+ interface VisualGraphEntity {
20
+ x: number;
21
+ y: number;
22
+ width: number;
23
+ height: number;
24
+ style?: Record<string, string | number>;
25
+ }
26
+ type PortDirection = 'in' | 'out' | 'inout';
27
+ interface PortConfig<TPortData = any> extends GraphEntityConfig {
28
+ name: string;
29
+ direction?: PortDirection;
30
+ label?: string;
31
+ data?: TPortData;
32
+ }
33
+ interface GraphPort<TPortData = any> extends GraphEntity {
34
+ name: string;
35
+ direction: PortDirection;
36
+ label?: string;
37
+ data: TPortData;
38
+ }
39
+ interface VisualPort<TPortData = any> extends Omit<GraphPort<TPortData>, keyof EntityRect>, VisualGraphEntity {}
40
+ interface GraphConfig<TNodeData = any, TEdgeData = any, TGraphData = any, TPortData = any> {
9
41
  id?: string;
10
42
  type?: 'directed' | 'undirected';
11
43
  initialNodeId?: string;
12
- nodes?: NodeConfig<TNodeData>[];
44
+ nodes?: NodeConfig<TNodeData, TPortData>[];
13
45
  edges?: EdgeConfig<TEdgeData>[];
14
46
  data?: TGraphData;
15
47
  direction?: 'up' | 'down' | 'left' | 'right';
16
48
  style?: Record<string, string | number>;
17
49
  }
18
- interface NodeConfig<TNodeData = any> {
50
+ interface NodeConfig<TNodeData = any, TPortData = any> extends GraphEntityConfig {
19
51
  id: string;
20
52
  parentId?: string | null;
21
53
  initialNodeId?: string;
22
54
  label?: string;
23
55
  data?: TNodeData;
24
- x?: number;
25
- y?: number;
26
- width?: number;
27
- height?: number;
56
+ ports?: PortConfig<TPortData>[];
28
57
  shape?: string;
29
58
  color?: string;
30
- style?: Record<string, string | number>;
31
59
  }
32
- interface EdgeConfig<TEdgeData = any> {
60
+ interface EdgeConfig<TEdgeData = any> extends GraphEntityConfig {
33
61
  /**
34
62
  * The id of the edge.
35
63
  */
@@ -52,40 +80,35 @@ interface EdgeConfig<TEdgeData = any> {
52
80
  * When `getWeight` is not provided, algorithms default to `edge.weight ?? 1`.
53
81
  */
54
82
  weight?: number;
83
+ /** Port name on the source node this edge connects from. */
84
+ sourcePort?: string;
85
+ /** Port name on the target node this edge connects to. */
86
+ targetPort?: string;
55
87
  data?: TEdgeData;
56
- x?: number;
57
- y?: number;
58
- width?: number;
59
- height?: number;
60
88
  color?: string;
61
- style?: Record<string, string | number>;
62
89
  }
63
- interface Graph<TNodeData = any, TEdgeData = any, TGraphData = any> {
90
+ interface Graph<TNodeData = any, TEdgeData = any, TGraphData = any, TPortData = any> {
64
91
  id: string;
65
92
  type: 'directed' | 'undirected';
66
93
  initialNodeId?: string | null;
67
- nodes: GraphNode<TNodeData>[];
94
+ nodes: GraphNode<TNodeData, TPortData>[];
68
95
  edges: GraphEdge<TEdgeData>[];
69
96
  data: TGraphData;
70
97
  direction?: 'up' | 'down' | 'left' | 'right';
71
98
  style?: Record<string, string | number>;
72
99
  }
73
- interface GraphNode<TNodeData = any> {
100
+ interface GraphNode<TNodeData = any, TPortData = any> extends GraphEntity {
74
101
  type: 'node';
75
102
  id: string;
76
103
  parentId?: string | null;
77
104
  initialNodeId?: string | null;
78
105
  label?: string;
79
106
  data: TNodeData;
80
- x?: number;
81
- y?: number;
82
- width?: number;
83
- height?: number;
107
+ ports?: GraphPort<TPortData>[];
84
108
  shape?: string;
85
109
  color?: string;
86
- style?: Record<string, string | number>;
87
110
  }
88
- interface GraphEdge<TEdgeData = any> {
111
+ interface GraphEdge<TEdgeData = any> extends GraphEntity {
89
112
  type: 'edge';
90
113
  id: string;
91
114
  sourceId: string;
@@ -97,35 +120,35 @@ interface GraphEdge<TEdgeData = any> {
97
120
  * When `getWeight` is not provided, algorithms default to `edge.weight ?? 1`.
98
121
  */
99
122
  weight?: number;
123
+ /** Port name on the source node this edge connects from. */
124
+ sourcePort?: string;
125
+ /** Port name on the target node this edge connects to. */
126
+ targetPort?: string;
100
127
  data: TEdgeData;
101
- x?: number;
102
- y?: number;
103
- width?: number;
104
- height?: number;
105
128
  color?: string;
106
- style?: Record<string, string | number>;
107
129
  }
108
- interface VisualNode<TNodeData = any> extends Omit<GraphNode<TNodeData>, keyof EntityRect>, EntityRect {
130
+ interface VisualNode<TNodeData = any, TPortData = any> extends Omit<GraphNode<TNodeData, TPortData>, keyof EntityRect>, VisualGraphEntity {
109
131
  shape?: string;
132
+ ports?: VisualPort<TPortData>[];
110
133
  }
111
- interface VisualEdge<TEdgeData = any> extends Omit<GraphEdge<TEdgeData>, keyof EntityRect>, EntityRect {}
112
- interface VisualGraph<TNodeData = any, TEdgeData = any, TGraphData = any> extends Omit<Graph<TNodeData, TEdgeData, TGraphData>, 'nodes' | 'edges'> {
113
- nodes: VisualNode<TNodeData>[];
134
+ interface VisualEdge<TEdgeData = any> extends Omit<GraphEdge<TEdgeData>, keyof EntityRect>, VisualGraphEntity {}
135
+ interface VisualGraph<TNodeData = any, TEdgeData = any, TGraphData = any, TPortData = any> extends Omit<Graph<TNodeData, TEdgeData, TGraphData, TPortData>, 'nodes' | 'edges'> {
136
+ nodes: VisualNode<TNodeData, TPortData>[];
114
137
  edges: VisualEdge<TEdgeData>[];
115
138
  direction: 'up' | 'down' | 'left' | 'right';
116
139
  }
117
- interface VisualGraphConfig<TNodeData = any, TEdgeData = any, TGraphData = any> extends GraphConfig<TNodeData, TEdgeData, TGraphData> {
140
+ interface VisualGraphConfig<TNodeData = any, TEdgeData = any, TGraphData = any, TPortData = any> extends GraphConfig<TNodeData, TEdgeData, TGraphData, TPortData> {
118
141
  direction?: 'up' | 'down' | 'left' | 'right';
119
142
  }
120
143
  interface DeleteNodeOptions {
121
144
  reparent?: boolean;
122
145
  }
123
- interface EntitiesConfig<TNodeData = any, TEdgeData = any> {
124
- nodes?: NodeConfig<TNodeData>[];
146
+ interface EntitiesConfig<TNodeData = any, TEdgeData = any, TPortData = any> {
147
+ nodes?: NodeConfig<TNodeData, TPortData>[];
125
148
  edges?: EdgeConfig<TEdgeData>[];
126
149
  }
127
- interface EntitiesUpdate<TNodeData = any, TEdgeData = any> {
128
- nodes?: (Partial<Omit<NodeConfig<TNodeData>, 'id'>> & {
150
+ interface EntitiesUpdate<TNodeData = any, TEdgeData = any, TPortData = any> {
151
+ nodes?: (Partial<Omit<NodeConfig<TNodeData, TPortData>, 'id'>> & {
129
152
  id: string;
130
153
  })[];
131
154
  edges?: (Partial<Omit<EdgeConfig<TEdgeData>, 'id'>> & {
@@ -314,4 +337,4 @@ interface TransitionOptions<TState, TEvent> {
314
337
  id?: string;
315
338
  }
316
339
  //#endregion
317
- export { VisualNode as A, SinglePathOptions as C, VisualGraph as D, VisualEdge as E, WalkOptions as M, WeightedWalkOptions as N, VisualGraphConfig as O, PathOptions as S, TraversalOptions as T, GraphPath as _, EdgeChange as a, NodeChange as b, EntitiesUpdate as c, GraphConfig as d, GraphDiff as f, GraphPatch as g, GraphNode as h, DeleteNodeOptions as i, WalkContext as j, VisualGraphFormatConverter as k, EntityRect as l, GraphFormatConverter as m, AllPairsShortestPathsOptions as n, EdgeConfig as o, GraphEdge as p, CoverageStats as r, EntitiesConfig as s, AStarOptions as t, Graph as u, GraphStep as v, TransitionOptions as w, NodeConfig as x, MSTOptions as y };
340
+ export { TraversalOptions as A, WeightedWalkOptions as B, NodeChange as C, PortDirection as D, PortConfig as E, VisualGraphFormatConverter as F, VisualNode as I, VisualPort as L, VisualGraph as M, VisualGraphConfig as N, SinglePathOptions as O, VisualGraphEntity as P, WalkContext as R, MSTOptions as S, PathOptions as T, GraphNode as _, EdgeChange as a, GraphPort as b, EntitiesUpdate as c, GraphConfig as d, GraphDiff as f, GraphFormatConverter as g, GraphEntityConfig as h, DeleteNodeOptions as i, VisualEdge as j, TransitionOptions as k, EntityRect as l, GraphEntity as m, AllPairsShortestPathsOptions as n, EdgeConfig as o, GraphEdge as p, CoverageStats as r, EntitiesConfig as s, AStarOptions as t, Graph as u, GraphPatch as v, NodeConfig as w, GraphStep as x, GraphPath as y, WalkOptions as z };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@statelyai/graph",
3
3
  "type": "module",
4
- "version": "0.7.0",
4
+ "version": "0.8.0",
5
5
  "description": "A TypeScript-first graph library with plain JSON-serializable objects",
6
6
  "author": "David Khourshid <david@stately.ai>",
7
7
  "license": "MIT",