v2.1.0
Minor Changes
-
#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.
Patch Changes
-
#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.