- #36
e1ef7bcThanks @davidkpiano! - Add unweighted-distance and explicit connectivity APIs. Harden weighted arithmetic, low-link traversal, transform callbacks, and bulk deletion.
-
#34
5a8eef6Thanks @davidkpiano! - Large performance overhaul of the algorithm hot paths:getStronglyConnectedComponentsis now an iterative typed-array Tarjan over the CSR (stack-safe, ~15x faster).getTopologicalSortis a CSR Kahn's pass with cached in-degrees (~4x faster, no more O(n²) queue).isBipartite/getMaximumBipartiteMatching2-color directly over the cached CSR with no per-call adjacency rebuild (~60x faster on repeated queries).- Bellman-Ford (
algorithm: 'bellman-ford') relaxes cached compact arc arrays; single-pair queries skip tie-predecessor bookkeeping entirely (~15x faster). - Floyd-Warshall all-pairs uses a flat distance matrix with copy-on-write tie-predecessor lists and O(length) path materialization (~4x faster).
genBFS/genDFS/genPostorderare hand-rolled chunked iterators (identical order and laziness semantics, no generator resume machinery; ~1.5-2x faster full traversals), and traversal snapshots now reuse the CSR's node snapshot instead of copyinggraph.nodesper call.- Dijkstra / A* / bidirectional search read default edge weights from a cached per-arc
Float64Arrayinstead of loading edge objects in the inner loop. getDegreeserves from a per-version degree map (one hashed lookup per call), and repeated indexed queries against the same graph skip the WeakMap via a one-entry memo.- All-targets shortest-path reconstruction (
genShortestPaths) materializes each path once via a shared backtracking buffer instead of per-level array spreads.
-
#32
3baa78cThanks @davidkpiano! - Add lazy, multi-source, directional, radius-limited postorder traversal. Keep active traversal structure stable across graph mutations and reject non-finite A-star heuristic values. -
e4a800eThanks @davidkpiano! - Add immutable counterparts for graph CRUD, patch application, and layout geometry updates. These helpers return updated graph copies while leaving their input graphs untouched. -
7b5ac38Thanks @davidkpiano! - Add graph-generic path-set and coverage utilities: path inspection and containment, coverage targets and coverage-preserving reduction, edge-covering path planning, ordered shortest simple paths, Eulerian paths/circuits, and line graph construction. -
#31
0bd1016Thanks @davidkpiano! - Add multi-source, directional, radius-limited BFS and DFS; induced neighborhood subgraphs; and graph union, intersection, difference, symmetric difference, disjoint union, and complement operations. -
97622b1Thanks @davidkpiano! - New algorithms, public kernel, cancellation, and format fidelity:- New algorithms:
isPlanar(left-right planarity test),getTSPTour(nearest-neighbor + 2-opt),getSteinerTree(metric-closure 2-approximation),getGraphColoring/isValidColoring(Welsh–Powell and DSatur),genAllPairsShortestPaths(lazy gen twin ofgetAllPairsShortestPaths). - New generators:
createWattsStrogatzGraph,createBarabasiAlbertGraph. - New
@statelyai/graph/kernelsubpath:getIndex,getCSR,invalidateIndex, andmemoizeByGraph— the fast-path primitives for large graphs and third-party algorithm plugins. - Cancellation: expensive algorithms (centrality, community detection,
max-flow, all-pairs paths, isomorphism, dominators) accept
options.signal: AbortSignal. - Round-trip fidelity: DOT preserves graph attributes, node/edge
defaults,
rank=same, HTML labels, and compass points; Mermaid preserves%%{init}%%directives, click handlers, linkStyle (now index-stable), state notes, mindmap::icon(), and block arrow tokens. - Fix:
getAllPairsShortestPathsno longer overflows the call stack on graphs with a few hundred nodes. - Benchmarks: reproducible via
pnpm bench:compare(--quickvariant, JSON results, generated docs tables, fairness notes).
- New algorithms:
-
#33
66b3828Thanks @davidkpiano! - AddgetMappedGraph()andgetFilteredGraph()structural transforms.getMappedGraph()returns a new graph with node/edgedatatransformed by mapping functions while preserving all structure;getFilteredGraph()returns a new graph keeping only nodes and edges that pass the given predicates, dropping incident edges of removed nodes.getSubgraph(),getReversedGraph(), and the new transforms now also preserve graph-leveldirectionandstyle.
-
#28
0498d52Thanks @davidkpiano! - Layout suite round two: transitions, geometry utilities, portable constraints, and four more engines.genLayoutTransition(from, to, options?)(@statelyai/graph/layout, zero-dep) — tween between two layouts of the same graph: yields interpolatedLayoutFrames (drive withapplyLayoutFrame, one per animation frame) and returns the target layout. Lay out with one engine, re-lay out with another, morph live. Options:steps(default 30),ease(default smoothstep).- Geometry utilities (
@statelyai/graph/layout) —translateGraph(graph, dx, dy)andcenterGraph(graph, rect)(mutable, in place): shift/center node positions, edge routepoints, and edge label rects. Hierarchy-aware — parent-relative children and container-relative edge routes are left alone. LayoutOptions.constraints— portable, advisory layout constraints. First constraint:layer(node)assigns nodes to ordered layers along the flow axis. ELK maps it to partitions (elk.partitioning.partition); the Graphvizdotengine maps it to{ rank=same; … }groups; engines without a layer concept ignore it.@statelyai/graph/layout/forceatlas2—getForceAtlas2Layout(sync; optional peersgraphology+graphology-layout-forceatlas2): seeded determinism, native pinning viaisFixed, edgeweightinfluence.@statelyai/graph/layout/d3-hierarchy—getTidyTreeLayout(sync; optional peerd3-hierarchy): Reingold–Tilford tidy tree. Root fromrootId→initialNodeId→ unique source; forests supported; non-tree extra edges preserved (spanning-tree layout).@statelyai/graph/layout/webcola—getColaLayout(sync; optional peerwebcola): constraint-based layout with overlap avoidance, seeded determinism,isFixedpinning, DAG flow viadirection.@statelyai/graph/layout/cytoscape—getCytoscapeLayout(async; optional peercytoscape, headless): bridges cytoscape's layout ecosystem (grid,circle,concentric,breadthfirst,cose, plus caller-registered extensions via the injectablecyoption). Compound nodes map to cytoscape parents.
The package smoke test exercises all nine layout entry points against the packed tarball.
-
#28
0498d52Thanks @davidkpiano! - Analytical coverage tail: cores, Katz, bipartite matching, min-cut, seeded label propagation, and graph generators.- k-core —
getCoreNumbers(graph)(Batagelj–Zaveršnik, O(m)) andgetKCore(graph, k); degrees are undirected per the standard definition. - Katz centrality —
getKatzCentrality(graph, { alpha, beta, getWeight, ... }); throws a descriptive error whenalphaexceeds the spectral bound and iteration diverges. - Eigenvector centrality hardened —
(A+I)-shifted power iteration (no more bipartite oscillation),getWeightsupport, descriptive non-convergence error. Differentially tested against graphology. - Bipartite —
isBipartite(graph)andgetMaximumBipartiteMatching(graph)(Hopcroft–Karp, O(m√n)); the non-bipartite error names the edge that closes the odd cycle. - Min-cut —
getMinCut(graph, { source, sink, getCapacity? })→{ value, cutEdges, partition }, sharing the max-flow solver (valuealways equalsgetMaxFlow(...)by construction). - Seeded label propagation —
getLabelPropagationCommunitiesgainsseed: asynchronous LPA with seeded shuffling/tie-breaking, deterministic per seed. - Generators —
createCompleteGraph(n),createGridGraph(rows, cols),createRandomGraph(n, p, { seed })(G(n,p), deterministic per seed) in the root export.
- k-core —
-
#25
e1e2107Thanks @davidkpiano! - Pluggable layout: a renderer-agnostic layout contract with adapters for ELK, Graphviz, dagre, and d3-force — no layout algorithms of our own, just typed plug-and-play over the plain-JSON graph.- Model: edges gain
points?: {x,y}[](route waypoints incl. endpoints, tail→head) androuting?: 'polyline' | 'orthogonal' | 'splines'(splines= Graphviz 3n+1 bezier control-point convention). Both round-trip through every full-fidelity format, diff/patch, andLAYOUT_KEYS. Edgex/y/width/heightare now canonically the edge-label rect (top-left + size) — engines readwidth/heightas label dimensions and write computed label positions back; this matches dagre's own convention and was previously undefined. @statelyai/graph/layout(zero-dep):LayoutFn/IterativeLayoutFn/LayoutFrame/LayoutOptions(direction, spacing,measurefor renderer-owned text measurement,isFixedpinning,seed), plusapplyLayoutFrame(per-animation-frame position writes, safe under the index contract),getLayoutBounds,getNodeSize.@statelyai/graph/layout/elk—getElkLayout(async; optional peerelkjs): hierarchy + ports first-class, orthogonal edge routes captured intopoints, computed edge label rects, all ELK algorithms viaalgorithm/layoutOptions, injectable ELK instance for web workers. (fromELKnow also captures routed sections and label geometry for anyone running ELK manually.)@statelyai/graph/layout/dagre—getDagreLayout(sync; optional peer@dagrejs/dagre): polyline routes, label rects, multigraph parallel edges, compound support.@statelyai/graph/layout/d3-force—genForceLayoutgenerator (one simulation tick pernext(), caller owns pacing/cancellation; yieldsLayoutFrames, returns the settledVisualGraph) +getForceLayout; seeded determinism (same seed ⇒ same layout),isFixedpinning; optional peerd3-force.@statelyai/graph/layout/graphviz—getGraphvizLayout(async WASM; optional peer@hpcc-js/wasm-graphviz): all eight Graphviz engines (dot, neato, fdp, sfdp, circo, twopi, osage, patchwork), spline control points intopoints/routing: 'splines', label positions, y-flip/center→top-left conversion handled.
The package smoke test exercises every adapter against the packed tarball.
- Model: edges gain
-
#28
0e5982aThanks @davidkpiano! - xyflow: labels now land where the renderers actually read them.toXYFlowemits edge labels as the top-leveledge.label(the prop React Flow / Svelte Flow render — previously the label went toedge.data.label, which built-in edges ignore) and node labels asdata.label(what React Flow's default node renders).fromXYFlowreads both spots back for external React Flow input, and full-fidelity round-tripping via the__statelyaimetadata is unchanged. If you relied onedge.data.labelintoXYFlowoutput, readedge.labelinstead.
-
#28
0e5982aThanks @davidkpiano! -getDegreeis now O(1) per call:|out| + |in|corrected by a cached per-node count of non-directed self-loops (revalidated by index version + graph mode, like the CSR snapshot). A full degree sweep over a 100k-node/300k-edge graph drops from ~148 ms to ~10 ms — at parity with ngraph and graphology, which was the one benchmark cell this library lost across the board. -
a9d5a4bThanks @davidkpiano! - Allow nullableinitialNodeIdconfig inputs in TypeScript, mark the package as side-effect free for bundlers, and add repo-wide type/convention checks to the verification gate. -
#28
0498d52Thanks @davidkpiano! - Pathfinding internals: lazy path materialization and a typed-array heap.genShortestPathsnow reconstructs a path only when it is actually yielded (abandoning the generator early skips the work), and the Dijkstra/A*/bidirectional hot loops use a Float64Array/Int32Array binary heap instead of object nodes. Same API, same results — measured −70% on first-path-then-stop, −41% on all-targets, −71% on single-target early exit (10k-node graph). -
af77e3fThanks @davidkpiano! - Validate nodeinitialNodeIdreferences inaddNode,updateNode, and batch node additions.Add prefixed canonical exports for traversal, transforms, diff patching, path joining, and walk stop helpers while preserving the old names as deprecated aliases.
-
#22
6bead2cThanks @davidkpiano! - Correctness, performance, and API-honesty overhaul.Migration notes (the two changes most likely to require action):
- In-place field mutation is no longer auto-detected.
edge.sourceId = 'x'/node.parentId = 'y'now requireinvalidateIndex(graph)afterwards (or useupdateEdge/updateNode, or immutable-style array replacement — both auto-detected). Code relying on the old per-read deep scan gets stale query results. This trade bought O(1) reads: a 10k-node query sweep dropped from 17.3 s to 14 ms. - Errors instead of silently wrong results: Dijkstra/A* throw on negative weights (use
{ algorithm: 'bellman-ford' }); Floyd-Warshall throws on negative cycles; GraphML/GEXF/GML importers throw on non-numeric numeric fields;updateNode/updateEdgereject orphaned port references and hierarchy-cycle-creating reparents.
Full changes:
updateNode/updateEdgenow apply every declared field. Previouslyx/y/width/height/shape/color/style(and edgemode/weight) were silently dropped. NewNodeUpdate/EdgeUpdatetypes; optional fields acceptnullto unset (JSON-safe), making diff → patch → apply converge.- Mode-aware queries.
getSuccessors,getPredecessors,getDegree,getInDegree,getOutDegree,getSources,getSinksnow honor effective edge directedness (graphmode+ per-edge overrides).getInEdges/getOutEdgesremain structural (authored direction) and are documented as such. - Indexing is now O(1) per read (was O(nodes+edges) on every query — a 10k-node
getSuccessorssweep dropped from 17.3 s to 14 ms). The index auto-rebuilds whengraph.nodes/graph.edgesare replaced or change length; in-place field mutations now requireinvalidateIndex()(previously auto-detected at the cost above). Also fixes stale-index results after immutable-style array replacement. - Algorithm fixes: zero-weight-cycle stack overflow in shortest paths/
hasPath(now BFS-based); Dijkstra/A* throw on negative weights instead of silently returning wrong paths;isTreeedge-count check; undirected cycle dedup no longer drops distinct cycles; biconnected components split correctly at DFS-root articulation points; SCC honors undirected/bidirectional edges;getTopologicalSortreturnsnullfor non-directed edges;isIsomorphiccompares self-loop edges; Prim returns a spanning forest on disconnected graphs (matching Kruskal); undirected self-loops are reported bygetCycles. - Mutation safety:
updateEdgevalidates port references when endpoints change;updateNoderejects port removals that would orphan edge port refs and parent changes that would create hierarchy cycles. - Diff:
getDiffnow coversports,weight,mode,sourcePort,targetPort;invertDiffno longer aliases its input. - Transforms:
reverseGraphswapssourcePort/targetPortand preserves edgemodeand node ports;getSubgraphpreserves ports/per-edge mode and strips danglinginitialNodeId;flattenpreserves authored leaf self-loops, edgeweight/mode, node fields, and resolves the graphinitialNodeId. - Walks: mode-aware traversal (undirected edges walk both ways);
genQuickRandomWalkdetours honorfilterand no longer depend on shortest-path reconstruction;takeUntil*Coverageyield nothing when the target is already met. - Formats:
toD2no longer crashes on graphs not produced byfromD2; xyflow round-tripsdata: undefinedwithout leaking metadata; per-edgemoderound-trips in cytoscape/d3/jgf/gml/elk/xyflow; GraphML no longer mutates numeric-looking labels or trims whitespace and synthesizes collision-safe edge ids; GEXF preserves empty labels; DOT escapes newlines and quotes reserved keywords; mermaid escapes|in labels;fromAdjacencyListmaterializes referenced nodes; the format support matrix now matches actual converter behavior.
- In-place field mutation is no longer auto-detected.
-
#22
e48bbdaThanks @davidkpiano! - CSR pathfinding, polynomial mixed-graph acyclicity, and malformed-input hardening:- Pathfinding on the CSR core. Dijkstra/BFS shortest paths and A* now run on the compressed-sparse-row snapshot. Measured on 50k nodes / 200k edges: single-target
getShortestPath329 → 58 ms (5.7×),getAStarPath27 → 7 ms (3.8×), all-targetsgetShortestPaths514 → 342 ms (reconstruction-bound). Results unchanged (validated by the differential Dijkstra oracle). isAcyclicon mixed graphs is now polynomial in practice: cycles among directed edges alone, cycles among non-directed edges alone (union-find), and the all-singleton-SCC case resolve without enumeration; only ambiguous multi-node SCCs fall back to exact simple-cycle search, restricted to that SCC. A 30-diamond acyclic mixed graph (2^30 simple paths) that previously hung now resolves instantly.- New
getGraphIssues(graph)(core export, zod-free): structural invariant checking — duplicate ids, dangling edge endpoints, missing parents, parent cycles (reported once per cycle), missing initial nodes, duplicate port names, invalid port references — with entity-naming messages and machine-readable codes. The recommended gate for untrusted/imported graphs;@statelyai/graph/schemas'validateGraphnow delegates its invariant portion to it. - Hierarchy queries terminate on malformed parent cycles:
getAncestors,getDescendants,getDepth, andgetLCApreviously hung forever on authoredparentIdcycles; each now stops at the first repeated node (documented convention). - mermaid/state: user nodes whose ids merely contain
_region_(e.g.foo_region_bar) are no longer mistaken for parallel-region markers and dropped — region detection now requires the exact structural pattern under a parallel parent.
- Pathfinding on the CSR core. Dijkstra/BFS shortest paths and A* now run on the compressed-sparse-row snapshot. Measured on 50k nodes / 200k edges: single-target
-
#22
6bead2cThanks @davidkpiano! - Mode unification, standard-GraphML import, and follow-up fixes:- Per-edge
modeoverrides now work everywhere.isAcyclic/getCyclesdispatch on effective edge modes — genuinely mixed graphs (directed + non-directed edges) use an exact simple-cycle search (correct, may be expensive on large dense mixed graphs); centrality (degree/in/out, closeness, PageRank, HITS, eigenvector), Prim MST, andisIsomorphicall honor per-edge modes.isIsomorphicno longer requires equal graph-levelmode(effective edge modes are what's structural). Two parallel undirected edges are now correctly reported as a 2-cycle bygetCycles(consistent withisAcyclic). - MST output preserves entity fields (node ports/shape/visual props; edge
mode/ports/color) instead of stripping them. - Floyd-Warshall detects negative cycles and throws a descriptive error instead of crashing during path reconstruction.
- Standard-GraphML import: nested
<graph>elements →parentIdhierarchy, native<port>elements → ports,sourceport/targetportattributes → edge port refs; multi-graph documents import the first graph. The format-support matrix is legitimately back to full hierarchy/ports for GraphML. Numeric<data>values that aren't numbers now throw a descriptive error (also in GEXF/GML) instead of silently poisoning the graph with NaN. - ELK port ids are document-unique (
nodeId__portName) as ELK requires; original port names round-trip via metadata; external ELK input with ports resolves to correct endpoints. - mermaid/state emit: isolated plain states are emitted; node labels emit via
state "label" as id(and parse back intolabel);graph.initialNodeIdround-trips as a top-level[*] -->transition. - xyflow: parents are ordered before children in
toXYFlowoutput, as React Flow requires. - Fixes from re-auditing the previous release:
updateNodeno longer hangs when reparenting onto a graph with a pre-existing authored parent cycle;invertDiffdeep-copies nested values (ports/style/data) instead of sharing them with the input. - New perf regression test guards the O(1) index read path.
- Per-edge
-
#22
c482fcdThanks @davidkpiano! - Performance core, new algorithms, and a differential-testing correctness moat:- CSR algorithm core. Hot algorithm loops now run on an internal compressed-sparse-row snapshot (
Int32Arrayarcs, integer node indices — no string hashing in inner loops), cached per index and invalidated by the same transparent contract as the index (API mutations, array replacement, length changes;invalidateIndex()for in-place field mutation). Measured on a 2k-node/6k-edge graph: closeness 2,575 → 73 ms (35×), betweenness 4,597 → 169 ms (27×), HITS 202 → 4 ms (50×), PageRank 26 → 2 ms (13×); connected components on 100k nodes/100k edges 379 → 7 ms (54×). Head-to-head on identical graphs this is now faster than graphology for betweenness (1.7×), PageRank (1.3×), and components (2×). Public API and results are unchanged (validated by the new differential suite); a thresholded perf regression test guards the CSR path in CI. - New algorithms:
getLouvainCommunities(deterministic Louvain modularity optimization),getMaxFlow(Edmonds–Karp max-flow with min-cut edges, capacities fromweight),getDominatorTree(Cooper–Harvey–Kennedy immediate dominators — for statecharts: which states every path from the initial state must pass through),getTransitiveReduction(minimal equivalent DAG; throws descriptively on cycles or non-directed edges). All mode-aware where applicable, with known-answer tests (CLRS flow network, dominator-paper examples). - Differential test suite (
tests/differential/): seeded random graphs run through both this library and graphology as an oracle — connected components, Dijkstra distances, PageRank, degrees, and betweenness must agree (158 tests; zero discrepancies found). Plus randomized self-properties: override-equivalence,reverseGraphinvolution, diff→patch convergence under random mutations,hasPath⇔getShortestPath, Prim ≡ Kruskal. - Structural fix: one shared, compile-time-guarded
toNodeConfig/toEdgeConfig(adding a field toGraphNode/GraphEdgenow fails compilation until every config producer handles it) adopted by diff, transforms, and MST output — closing the recurring silent-field-drop bug class. MST output no longer shares port objects with the source graph.
- CSR algorithm core. Hot algorithm loops now run on an internal compressed-sparse-row snapshot (
-
#24
9eca989Thanks @davidkpiano! - Bidirectional Dijkstra for single-pair queries + airtight negative-weight enforcement:getShortestPathnow runs bidirectional Dijkstra (forward on traversable arcs, backward on reverse arcs, Pohl termination). On a 50k-node/200k-edge random graph a point query dropped 58 ms → 0.8 ms; in the cross-library harness this is now 2.2× faster than graphology's bidirectional implementation (previously 60× slower). Same results — one shortest path, ties broken arbitrarily as before;{ algorithm: 'bellman-ford' }keeps the full search for negative weights.- Negative-weight detection is now up-front for sublinear searches (single-pair, early-exit, A*): O(1) via a flag cached on the CSR build for default weights, one O(edges) sweep for custom
getWeight. Previously a sublinear search could terminate without scanning a reachable negative edge and silently return a wrong path. Corner-case behavior change: these queries now throw even when the negative edge is *unreachable* from the source — deterministic failure instead of result-dependent behavior.
-
#24
634f618Thanks @davidkpiano! - Benchmark-driven pathfinding/traversal performance:bfs/dfsgenerators run on the CSR snapshot — a full BFS sweep over a 100k-node/300k-edge graph dropped from 623 ms to 4.4 ms, now the fastest of the five libraries measured (graphology, ngraph, graphlib, cytoscape) instead of the slowest.- Single-target
getShortestPath/getShortestPaths({ to })early-exit the Dijkstra/BFS search once everything at the target's distance is settled (all equal-cost tie paths, including through zero-weight edges, are preserved — tested). Random-graph single-pair queries dropped ~2× on top of the earlier CSR gains.
Also adds
pnpm bench:compare— a reproducible cross-library benchmark harness (seeded identical graphs across 4 shapes × 3 sizes, idiomatic public APIs, median-of-runs, markdown/JSON reports inbench/compare/results/).
-
5acd7c3Thanks @davidkpiano! - Add graph and edgemodedirectedness, replacing graphtype, and add D2 format support.Graphs now use
mode: 'directed' | 'undirected' | 'bidirectional'as the graph-level default, and edges may override it with their ownmode. Traversal, path, and query logic resolves effective edge mode so mixed directedness works consistently.Adds
@statelyai/graph/d2with parsing and emitting for D2 syntax, including hierarchy, ports, styles, comments, classes, imports, and connector directedness.
-
aeedcc0Thanks @davidkpiano! - Add semantic graph validation viavalidateGraph(), covering shape plus graph invariants such as duplicate ids, dangling edges, missing parents, invalid initial nodes, duplicate ports, invalid port references, and parent cycles.Default missing graph, node, edge, and port
datavalues tonullwhen creating resolved graph objects.Refresh format fidelity claims and conformance tests for ELK, xyflow, and Mermaid state round-tripping, and expand algorithm benchmarks across sparse, dense, compound, multi-edge, and port-heavy graphs.
450d7daThanks @davidkpiano! - Preserve graph, node, and edge metadata more fully across D3, Cytoscape, GML, GEXF, and JGF adapters, and verify shipped schema JSON files in package smoke tests.
09fe970Thanks @davidkpiano! - Add runtime schema validation helpers, improve port round-tripping across structured format adapters, extend package smoke coverage to type-check public subpath imports, and document format support and validation usage in the README.
b4b2195Thanks @davidkpiano! - Preserve graph metadata in GEXF, round-trip DOT edge port references, tighten structured format parity tests, and derive package smoke coverage from the published export map.
-
1480565Thanks @davidkpiano! - Add Mermaid Ishikawa conversion and improve Mermaid v11.13 sequence and ER parsing. -
18588bdThanks @davidkpiano! - Preserve ports and edge port references in GraphML round-trips, allow nullable node labels inGraphSchema
88d0dbdThanks @davidkpiano! - Add format support metadata as a published subpath, tighten schema drift checks, and modularize the algorithms entrypoint. This also adds benchmark coverage and CI checks for generated schema artifacts across multiple Node versions.
-
bfb5f0bThanks @davidkpiano! - Clean up and simplify type definitions:- Remove
GraphEntityConfig; merged intoGraphEntity - Export
VisualGraphFormatConverterfrom main entry - Rename
Positionedexport toEntityRect - Simplify
VisualNode,VisualEdge,VisualPortto use property narrowing instead ofOmit - Fix
Graph<any, E>→Graph<N, E>on exported APIs for better generic propagation - Normalize
labeltostring | nullon bothGraphNodeandGraphEdge(node label default changed from''tonull)
- Remove
160167bThanks @davidkpiano! - RemovegetEdgeBetweenin favor ofgetEdgesBetween.
-
0fb9433Thanks @davidkpiano! - Add entity equivalence functions:areEntitiesEqual,isLayoutEqual,isNonLayoutEqual, andLAYOUT_KEYS. -
6d2465dThanks @davidkpiano! - Add centrality, community detection, connectivity, and isomorphism algorithms.
-
8b97c9bThanks @davidkpiano! - Add port support for nodes and edges, enabling dataflow/node-editor graphs (Node-RED, Unreal Blueprints, ComfyUI).- Add
GraphEntityConfig,GraphEntity,VisualGraphEntitybase interfaces (DRY shared props for nodes, edges, ports) - Add
PortConfig<P>,GraphPort<P>,VisualPort<P>types with generic data parameter - Add
P(port data) as 4th generic toGraph<N, E, G, P>and all related types - Add
ports?: PortConfig[]onNodeConfig,ports?: GraphPort[]onGraphNode - Add
sourcePort?/targetPort?(port name strings) onEdgeConfigandGraphEdge - Add
createGraphPort()factory - Add port validation: duplicate port names rejected,
addEdge/updateEdgevalidate port existence - Add port queries:
getPort(),getPorts(),getEdgesByPort() - ELK adapter: round-trip ports (name ↔ ELK port id, direction ↔
org.eclipse.elk.port.side) - xyflow adapter:
sourcePort↔sourceHandle,targetPort↔targetHandle
- Add
976a7e6Thanks @davidkpiano! - - MakeGraph.initialNodeId,GraphNode.label, andGraphEdge.labeloptional on resolved types for easier consumer usage- Add
createGraphNode()andcreateGraphEdge()helpers that resolve defaults from config
- Add
-
3115609Thanks @davidkpiano! - Makeedge.labelnullable -
54023f4Thanks @davidkpiano! - Add edge weights, A* pathfinding, subgraph extraction, and graph reversal.weight?: numberon edges; algorithms default to(e) => e.weight ?? 1with BFS fast path when unweightedgetAStarPath(graph, { from, to, heuristic })for heuristic-guided shortest pathsgetSubgraph(graph, nodeIds)returns induced subgraph with internal edgesreverseGraph(graph, filterEdge?)flips edge directions- Remove stale TODO for Mermaid sequence blocks (already implemented)
-
8f9912dThanks @davidkpiano! - Add walk generators and coverage utilities for model-based testing.genRandomWalk(),genWeightedRandomWalk(),genQuickRandomWalk(),genPredefinedWalk()— step-by-step graph traversal generators that yieldGraphStep, with optionalseedfor deterministic replay- Composable stop conditions:
takeSteps(),takeUntilNode(),takeUntilEdge(),takeUntilNodeCoverage(),takeUntilEdgeCoverage() getCoverage()computes node/edge coverage stats from a walkfilteroption for edge guards,onStepcallback for actions — keeps graph JSON-serializable
-
22f77a5Thanks @davidkpiano! - Fix schema and GraphML serialization drift, optimize weighted graph algorithms, and makegenSimplePaths()truly lazy.- add
weighttoEdgeSchemaand tighten schema drift tests against the runtime graph types - preserve graph, node, and edge metadata in GraphML round-trips, including
initialNodeId,direction,style, geometry, and edgeweight - use a heap-backed priority queue for weighted shortest paths, A*, and Prim MST
- refactor
genSimplePaths()to yield incrementally instead of collecting all paths before returning
- add
-
371133cThanks @davidkpiano! - Add ELK formatter -
9e596d6Thanks @davidkpiano! - Generate schemas
-
166b695Thanks @davidkpiano! - Fix Mermaid types -
49ffd94Thanks @davidkpiano! - Improve Mermaid parity
-
e268990Thanks @davidkpiano! - MakeparentId,initialNodeId, andshapeoptional onGraphNode. These fields are no longer defaulted tonull/'rectangle'bycreateGraph/createVisualGraph, they are simply omitted when not provided.Add empty string validation for node/edge IDs,
parentId,sourceId, andtargetId. -
55462e6Thanks @davidkpiano! - Add xyflow (React Flow / Svelte Flow) format converter withtoXYFlow()andfromXYFlow()for converting betweenVisualGraphand xyflow node/edge structures. Uses@xyflow/systemas an optional peer dependency for types. Also addsVisualGraphFormatConvertertype for visual-first format converters.
-
#6
c186c2aThanks @davidkpiano! - Add JSDoc with usage examples to all exported functions -
83b3c66Thanks @davidkpiano! -VisualNode['shape']is now optional -
#8
27f3f7fThanks @davidkpiano! - Mermaid: reconstruct sequence diagram blocks
-
#5
25ab36aThanks @davidkpiano! - - Add DOT format (@statelyai/graph/dot)- Add Mermaid formatters: flowchart, sequence, state, class diagram, ER, mindmap, block
- Add
getRelativeDistanceMapandgetRelativeDistance(queries) - Restructure formats into per-format subpackages with READMEs
-
#3
bcf8c48Thanks @davidkpiano! - Add format converters for JGF, Cytoscape.js JSON, D3 force JSON, GEXF, GML, and TGF- New
GraphFormatConverter<TSerial>interface andcreateFormatConverter()factory - 6 new format modules with bidirectional
to*/from*functions:toJGF/fromJGF,toCytoscapeJSON/fromCytoscapeJSON,toD3Graph/fromD3Graph,toGEXF/fromGEXF,toGML/fromGML,toTGF/fromTGF - Input validation with descriptive error messages on all
from*functions - End-to-end integration tests with real Cytoscape.js and D3 force libraries
cytoscapeandd3-forceadded as optional peer dependencies
- New
-
4f02507Thanks @davidkpiano! - RemovetoGraphML,fromGraphML,GraphSchema,NodeSchema, andEdgeSchemafrom the main barrel export to avoid pulling in optional peer deps (fast-xml-parser,zod) during SSR.Use subpath imports instead:
@statelyai/graph/formats/graphml@statelyai/graph/schemas