Skip to content
Effect Days 2026 Get your ticket

Graph

Models relationships between indexed nodes and edges.

This module provides immutable and scoped-mutable graph data structures. A graph can be directed or undirected, and it can store user-defined data on both nodes and edges. The module includes traversal, analysis, path-finding, transformation, and diagram export utilities.

137 exports Added in v3.18.0 Source

Algorithms

Lazily enumerates all simple paths tied for minimum total cost.

When to use

Use when every distinct route tied for the minimum total cost is required.

Details

Parallel edges produce distinct paths. Edge costs must be non-negative; Infinity behaves as unavailable.

Gotchas

The number of tied paths can still be large. Missing endpoints, invalid costs, arithmetic overflow, or an invalid limit throw a GraphError. Mutable graphs are snapshotted when iteration begins.

See

Signature

declare const allShortestPaths: {
<E>(config: AllShortestPathsConfig<E>): <N, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => PathWalker<E>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, config: AllShortestPathsConfig<E>): PathWalker<E>;
}

Returns the nodes whose removal increases the number of connected components.

When to use

Use when locating single-node failure points in an undirected network.

Details

Disconnected components, parallel edges, and self-loops are handled by an iterative, stack-safe low-link traversal in O(V + E) time. Results follow graph node order.

Gotchas

Throws a GraphError when used with a directed graph.

See

Signature

declare function articulationPoints<N, E>(graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">): Array<number>

Example

(Finding articulation points)

import { Graph } from "effect"
const graph = Graph.undirected<void, void>((mutable) => {
for (let i = 0; i < 3; i++) Graph.addNode(mutable, undefined)
Graph.addEdge(mutable, 0, 1, undefined)
Graph.addEdge(mutable, 1, 2, undefined)
})
Graph.articulationPoints(graph) // => [1]

astar

Added in v3.18.0 Source

Finds the shortest path from the configured source node to the target node using the A* pathfinding algorithm.

When to use

Use when a meaningful heuristic can reduce point-to-point search compared with Dijkstra's algorithm.

Details

The edge-cost function must return non-negative weights and not NaN. Infinity is allowed and behaves like an impassable edge. Returns Option.none() when the target is not reachable.

Gotchas

The heuristic must be consistent for the shortest-path guarantee and must return finite values. Missing endpoints, invalid edge costs, or non-finite heuristic values or arithmetic results throw a GraphError.

See

Signature

declare const astar: {
<E, N>(config: AstarConfig<E, N>): <T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Option<PathResult<E>>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, config: AstarConfig<E, N>): Option<PathResult<E>>;
}

Example

(Finding shortest paths with A*)

import { Graph, Option } from "effect"
const graph = Graph.directed<{ x: number; y: number }, number>((mutable) => {
const a = Graph.addNode(mutable, { x: 0, y: 0 })
const b = Graph.addNode(mutable, { x: 1, y: 0 })
const c = Graph.addNode(mutable, { x: 2, y: 0 })
Graph.addEdge(mutable, a, b, 1)
Graph.addEdge(mutable, b, c, 1)
})
// Manhattan distance heuristic
const heuristic = (
nodeData: { x: number; y: number },
targetData: { x: number; y: number }
) => Math.abs(nodeData.x - targetData.x) + Math.abs(nodeData.y - targetData.y)
const result = Graph.astar(graph, {
source: 0,
target: 2,
cost: (edgeData) => edgeData,
heuristic
})
Option.map(result, ({ distance, path }) => [distance, path] as const) // => Option.some([2, [0, 1, 2]])

bellmanFord

Added in v3.18.0 Source

Finds the shortest path from the configured source node to the target node using the Bellman-Ford algorithm.

When to use

Use when one source-to-target shortest-path query may traverse negative-cost edges.

Details

Negative edge weights are allowed, and Infinity behaves like an impassable edge. Returns Option.none() when the target is unreachable. A reachable negative cycle only causes failure when it can affect the target.

Gotchas

Missing endpoints, unsupported weights, finite-range overflow, or a relevant negative cycle throw a GraphError. In an undirected graph, any reachable negative edge forms a negative cycle because it can be traversed both ways.

See

Signature

declare const bellmanFord: {
<E>(config: BellmanFordConfig<E>): <N, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Option<PathResult<E>>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, config: BellmanFordConfig<E>): Option<PathResult<E>>;
}

Example

(Finding shortest paths with Bellman-Ford)

import { Graph, Option } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, -1) // Negative weight allowed
Graph.addEdge(mutable, b, c, 3)
Graph.addEdge(mutable, a, c, 5)
})
const result = Graph.bellmanFord(graph, {
source: 0,
target: 2,
cost: (edgeData) => edgeData
})
Option.map(result, ({ distance, path }) => [distance, path] as const) // => Option.some([2, [0, 1, 2]])

Returns the maximal biconnected node components of an undirected graph.

When to use

Use when decomposing an undirected graph into maximal regions that remain connected after removing any one node from the region.

Details

Articulation points can occur in more than one component. Isolated vertices are excluded, while a vertex with a self-loop forms a singleton component. Nodes within components and the components themselves follow graph order. Parallel edges are treated independently. The iterative low-link traversal is stack-safe and runs in O(V + E) time.

Gotchas

Throws a GraphError when used with a directed graph.

See

Signature

declare function biconnectedComponents<N, E>(graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">): Array<Array<number>>

Example

(Finding biconnected components)

import { Graph } from "effect"
const graph = Graph.undirected<void, void>((mutable) => {
for (let i = 0; i < 5; i++) Graph.addNode(mutable, undefined)
Graph.addEdge(mutable, 0, 1, undefined)
Graph.addEdge(mutable, 1, 2, undefined)
Graph.addEdge(mutable, 2, 0, undefined)
Graph.addEdge(mutable, 2, 3, undefined)
Graph.addEdge(mutable, 3, 4, undefined)
Graph.addEdge(mutable, 4, 2, undefined)
})
Graph.biconnectedComponents(graph) // => [[0, 1, 2], [2, 3, 4]]

bridges

Added in v4.0.0 Source

Returns the edges whose removal increases the number of connected components.

When to use

Use when locating single-edge failure points in an undirected network.

Details

Parent edges are tracked by edge index, so a parallel edge prevents either edge from being a bridge. Self-loops are never bridges. Results follow graph edge order. The iterative low-link traversal is stack-safe and runs in O(V + E) time.

Gotchas

Throws a GraphError when used with a directed graph.

See

Signature

declare function bridges<N, E>(graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">): Array<number>

Example

(Finding bridge edges)

import { Graph } from "effect"
const graph = Graph.undirected<void, void>((mutable) => {
for (let i = 0; i < 3; i++) Graph.addNode(mutable, undefined)
Graph.addEdge(mutable, 0, 1, undefined)
Graph.addEdge(mutable, 1, 2, undefined)
})
Graph.bridges(graph) // => [0, 1]

Returns the connected components of an undirected graph.

When to use

Use when partitioning an undirected graph into groups connected by paths.

Details

Each component is represented as an array of node indices. Isolated nodes form singleton components.

Gotchas

Throws a GraphError when used with a directed graph.

See

Signature

declare function connectedComponents<N, E>(graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">): Array<Array<number>>

Example

(Finding connected components)

import { Graph } from "effect"
const graph = Graph.undirected<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
const d = Graph.addNode(mutable, "D")
Graph.addEdge(mutable, a, b, "edge") // Component 1: A-B
Graph.addEdge(mutable, c, d, "edge") // Component 2: C-D
})
Graph.connectedComponents(graph) // => [[0, 1], [2, 3]]

dijkstra

Added in v3.18.0 Source

Finds the shortest path from the configured source node to the target node using Dijkstra's algorithm.

When to use

Use when you need one source-to-target shortest path and every edge cost is non-negative.

Details

Edge costs must be non-negative and not NaN. Infinity is allowed and behaves like an impassable edge. Returns Option.none() when the target is not reachable.

Gotchas

Throws a GraphError when either endpoint is missing or an edge cost is negative or NaN, or when a path distance exceeds the finite number range.

See

  • astar when a useful heuristic can guide the search
  • bellmanFord when edge costs may be negative
  • floydWarshall when shortest paths are needed for all pairs

Signature

declare const dijkstra: {
<E>(config: DijkstraConfig<E>): <N, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Option<PathResult<E>>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, config: DijkstraConfig<E>): Option<PathResult<E>>;
}

Example

(Finding shortest paths with Dijkstra)

import { Graph, Option } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, 5)
Graph.addEdge(mutable, a, c, 10)
Graph.addEdge(mutable, b, c, 2)
})
const result = Graph.dijkstra(graph, {
source: 0,
target: 2,
cost: (edgeData) => edgeData
})
Option.map(result, ({ distance, path }) => [distance, path] as const) // => Option.some([7, [0, 1, 2]])

findCycle

Added in v4.0.0 Source

Returns one cycle in a graph, if present.

When to use

Use when you need the nodes and edges of a concrete cycle for diagnostics or reporting.

Details

Directed cycles respect edge orientation. A self-loop is represented as a one-edge cycle, and two parallel undirected edges form a two-edge cycle.

See

  • isAcyclic when only a boolean cycle check is needed

Signature

declare function findCycle<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): Option<CycleResult>

floydWarshall

Added in v3.18.0 Source

Finds shortest paths between all pairs of nodes using the Floyd-Warshall algorithm.

When to use

Use when many or all node pairs will be queried and cubic computation plus quadratic result storage is acceptable.

Details

Computes distances, reconstructed node paths, and edge-data paths for every source and target pair in O(V^3) time. Negative edge weights are allowed, and Infinity behaves like an impassable edge.

Gotchas

A GraphError is thrown if any edge weight is NaN or -Infinity, or if finite arithmetic overflows or underflows, or if any negative cycle is detected.

See

  • dijkstra for one query with non-negative edge costs
  • bellmanFord for one query that may include negative edge costs

Signature

declare const floydWarshall: {
<E>(cost: (edgeData: E) => number): <N, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => AllPairsResult<E>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, cost: (edgeData: E) => number): AllPairsResult<E>;
}

Example

(Finding all-pairs shortest paths)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, 3)
Graph.addEdge(mutable, b, c, 2)
Graph.addEdge(mutable, a, c, 7)
})
const result = Graph.floydWarshall(graph, (edgeData) => edgeData)
const shortest = { distance: result.distances.get(0)?.get(2), path: result.paths.get(0)?.get(2) }
shortest // => { distance: 5, path: [0, 1, 2] }

isAcyclic

Added in v3.18.0 Source

Checks whether the graph is acyclic (contains no cycles).

When to use

Use when validating that a graph contains no cycle and a cycle witness is not needed.

Details

Directed cycles respect edge orientation. Self-loops are cycles, and two parallel edges form a cycle in an undirected graph.

See

  • findCycle for retrieving one cycle witness
  • topo for ordering a directed acyclic graph

Signature

declare function isAcyclic<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): boolean

Example

(Checking cycles)

import { Graph } from "effect"
// Acyclic directed graph (DAG)
const dag = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, "A->B")
Graph.addEdge(mutable, b, c, "B->C")
})
Graph.isAcyclic(dag) // => true
// Cyclic directed graph
const cyclic = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
Graph.addEdge(mutable, a, b, "A->B")
Graph.addEdge(mutable, b, a, "B->A") // Creates cycle
})
Graph.isAcyclic(cyclic) // => false

isBipartite

Added in v3.18.0 Source

Checks whether an undirected graph is bipartite.

When to use

Use when validating that nodes can be divided into two groups with every edge crossing between the groups.

Details

A bipartite graph is one whose vertices can be divided into two disjoint sets such that no two vertices within the same set are adjacent.

See

Signature

declare function isBipartite<N, E>(graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">): boolean

Example

(Checking bipartite graphs)

import { Graph } from "effect"
// Bipartite graph (alternating coloring possible)
const bipartite = Graph.undirected<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
const d = Graph.addNode(mutable, "D")
Graph.addEdge(mutable, a, b, "edge") // Set 1: {A, C}, Set 2: {B, D}
Graph.addEdge(mutable, b, c, "edge")
Graph.addEdge(mutable, c, d, "edge")
})
Graph.isBipartite(bipartite) // => true
// Non-bipartite graph (odd cycle)
const triangle = Graph.undirected<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, "edge")
Graph.addEdge(mutable, b, c, "edge")
Graph.addEdge(mutable, c, a, "edge") // Triangle (3-cycle)
})
Graph.isBipartite(triangle) // => false

Returns a maximum-cardinality matching of an undirected bipartite graph.

When to use

Use when assigning as many disjoint pairs as possible between the two sides of a bipartite graph, such as workers to jobs or users to resources.

Details

The bipartition is derived internally. Self-loops and odd cycles throw a GraphError. Isolated nodes are allowed. Parallel edges do not change the matching cardinality, and the first edge in graph order between each matched pair is reported. Results follow left-partition graph order. Hopcroft-Karp runs in O(E * sqrt(V)) time.

Gotchas

The graph must be undirected and bipartite. The derived left and right sides are not based on stored edge orientation.

See

  • isBipartite for validating the graph without computing a matching

Signature

declare function maximumBipartiteMatching<N, E>(graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">): Array<BipartiteMatch>

Example

(Matching a bipartite graph)

import { Graph } from "effect"
const graph = Graph.undirected<string, string>((mutable) => {
for (const node of ["A", "B", "X", "Y"]) Graph.addNode(mutable, node)
Graph.addEdge(mutable, 0, 2, "A-X")
Graph.addEdge(mutable, 0, 3, "A-Y")
Graph.addEdge(mutable, 1, 2, "B-X")
})
Graph.maximumBipartiteMatching(graph) // => [{ left: 0, right: 3, edge: 1 }, { left: 1, right: 2, edge: 2 }]

maximumFlow

Added in v4.0.0 Source

Returns a maximum flow and corresponding minimum cut for a directed graph.

When to use

Use when computing the greatest transferable capacity from one node to another and per-edge flow values are required.

Details

Parallel edges retain independent capacities, self-loops carry no source-to-target flow, and the flow map includes every original edge in graph order, including zero-flow edges. Edmonds-Karp runs in O(V * E^2) time.

Gotchas

The graph must be directed. Capacities must be finite and non-negative. Missing or equal endpoints, invalid capacities, and a total flow outside the finite number range throw a GraphError. Self-loops always carry zero flow.

See

  • minimumCut for the residual-reachability partition

Signature

declare const maximumFlow: {
<E>(config: MaximumFlowConfig<E>): <N>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">) => MaximumFlowResult;
<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, config: MaximumFlowConfig<E>): MaximumFlowResult;
}

Example

(Computing maximum flow)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
for (const node of ["source", "a", "target"]) Graph.addNode(mutable, node)
Graph.addEdge(mutable, 0, 1, 3)
Graph.addEdge(mutable, 1, 2, 2)
Graph.addEdge(mutable, 0, 2, 1)
})
Graph.maximumFlow(graph, { source: 0, target: 2, capacity: (edge) => edge }).value // => 3

minimumCut

Added in v4.0.0 Source

Returns a minimum cut and its node partitions for a directed graph.

When to use

Use when identifying the minimum-capacity edges that separate a source from a target, together with the resulting node partitions.

Details

The source partition contains nodes reachable from the source in the final residual network; the target partition contains its complement. Both follow graph node order. Cut edges follow graph edge order, and their total capacity equals the returned maximum-flow value. Validation, parallel-edge, self-loop, and O(V * E^2) complexity behavior match maximumFlow.

Gotchas

The graph must be directed. Invalid capacities, missing endpoints, or equal source and target nodes throw a GraphError.

See

Signature

declare const minimumCut: {
<E>(config: MaximumFlowConfig<E>): <N>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">) => MinimumCutResult;
<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, config: MaximumFlowConfig<E>): MinimumCutResult;
}

Example

(Partitioning a minimum cut)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
for (const node of ["source", "a", "target"]) Graph.addNode(mutable, node)
Graph.addEdge(mutable, 0, 1, 2)
Graph.addEdge(mutable, 1, 2, 1)
})
Graph.minimumCut(graph, { source: 0, target: 2, capacity: (edge) => edge }).source // => [0, 1]

Returns a minimum spanning forest of an undirected graph using Kruskal's algorithm.

When to use

Use when selecting a minimum-cost acyclic connector for every connected component of an undirected graph.

Details

All node indices and selected edge indices are preserved. Negative finite weights are allowed, Infinity marks an unavailable edge, and equal weights are resolved by original edge order. Disconnected inputs produce a forest, and isolated nodes remain present.

Gotchas

Throws a GraphError for a directed graph or when a weight is NaN or -Infinity. Edges weighted Infinity are omitted.

Signature

declare const minimumSpanningForest: {
<E>(cost: (edgeData: E) => number): <N>(graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">) => Graph<N, E, "undirected">;
<N, E>(graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">, cost: (edgeData: E) => number): Graph<N, E, "undirected">;
}

simplePaths

Added in v4.0.0 Source

Lazily enumerates simple source-to-target paths in depth-first edge order.

When to use

Use when you need possible loop-free routes rather than only an optimal route.

Details

Nodes are never repeated within a path, so enumeration is finite even for cyclic graphs. Path distance is the number of traversed edges.

Gotchas

The number of simple paths can be exponential. Missing endpoints or an invalid limit throw a GraphError. Mutable graphs are snapshotted when iteration begins.

See

Signature

declare const simplePaths: {
<E>(config: SimplePathsConfig): <N, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => PathWalker<E>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, config: SimplePathsConfig): PathWalker<E>;
}

Returns the strongly connected components of a directed graph.

When to use

Use when grouping nodes so every node in a component can reach every other node in that component.

Details

Each component is represented as an array of node indices and is computed with Kosaraju's algorithm.

Gotchas

Throws a GraphError when used with an undirected graph.

See

Signature

declare function stronglyConnectedComponents<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">): Array<Array<number>>

Example

(Finding strongly connected components)

import { Graph } from "effect"
const graph = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, "A->B")
Graph.addEdge(mutable, b, c, "B->C")
Graph.addEdge(mutable, c, a, "C->A") // Creates SCC: A-B-C
})
Graph.stronglyConnectedComponents(graph) // => [[0, 2, 1]]

Returns the transitive reduction of a directed acyclic graph.

When to use

Use when simplifying a dependency DAG while preserving which nodes can reach which other nodes.

Details

The result preserves reachability with the fewest structural source-target pairs. Node and retained edge indices are preserved.

Gotchas

This operation is structural and ignores edge costs. Parallel edges are coalesced by retaining the first edge for each required pair. Throws a GraphError for an undirected graph or cyclic input.

Signature

declare function transitiveReduction<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">): Graph<N, E, "directed">

Returns minimum unweighted distances from a source to every reachable node.

When to use

Use when every edge represents one step and you need hop counts from one source.

Details

Directed traversal is outgoing by default and can be changed with direction.

Gotchas

Throws a GraphError when the source does not exist.

See

  • hasPath when only a reachability boolean is needed
  • bfs for lazy traversal in increasing hop distance
  • dijkstra for weighted shortest paths

Signature

declare const unweightedDistances: {
(source: number, options?: ReachabilityConfig): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Map<number, number>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, source: number, options?: ReachabilityConfig): Map<number, number>;
}

Finds weakly connected components in a directed graph.

When to use

Use when grouping directed nodes by connectivity while ignoring edge orientation.

Details

Edge direction is ignored while partitioning nodes. Isolated nodes form singleton components.

Gotchas

Throws a GraphError when used with an undirected graph.

See

Signature

declare function weaklyConnectedComponents<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">): Array<Array<number>>

Configuration

AllShortestPathsConfig interface

Added in v4.0.0 Source

Configuration for enumerating all tied shortest paths.

When to use

Use when bounding enumeration of every route tied for minimum total cost.

Details

Edge costs must be non-negative. limit bounds the number of yielded paths and defaults to Infinity.

Gotchas

Invalid costs and limits throw a GraphError when evaluated.

Signature

interface AllShortestPathsConfig<E> extends DijkstraConfig<E> {
readonly limit?: number;
}

AstarConfig interface

Added in v3.18.0 Source

Configuration for finding a shortest path with the A* algorithm.

When to use

Use when configuring astar for point-to-point shortest-path searches where node data can provide a heuristic estimate toward the target.

Details

Specifies the source and target node indices, an edge-cost function that maps edge data to non-negative weights, and a heuristic that estimates the remaining cost from a node to the target.

Gotchas

Heuristic values must be finite and the heuristic must be consistent for A* to guarantee a shortest path.

Signature

interface AstarConfig<E, N> {
cost: (edgeData: E) => number;
heuristic: (sourceNodeData: N, targetNodeData: N) => number;
source: number;
target: number;
}

BellmanFordConfig interface

Added in v3.18.0 Source

Configuration for finding a shortest path with the Bellman-Ford algorithm.

When to use

Use when configuring bellmanFord to find a shortest path where edge weights may be negative.

Details

Specifies the source and target node indices, plus a cost function that maps each edge's data to a numeric weight.

Signature

interface BellmanFordConfig<E> {
cost: (edgeData: E) => number;
source: number;
target: number;
}

DijkstraConfig interface

Added in v3.18.0 Source

Configuration for finding a shortest path with Dijkstra's algorithm.

When to use

Use when configuring dijkstra to find a shortest path between two existing node indices with non-negative edge costs.

Details

Specifies the source and target node indices, plus a cost function that maps each edge's data to a non-negative numeric weight. Infinity is allowed and behaves like an impassable edge.

Gotchas

dijkstra throws a GraphError when either endpoint does not exist or when the cost function returns a negative weight or NaN.

Signature

interface DijkstraConfig<E> {
cost: (edgeData: E) => number;
source: number;
target: number;
}

ExternalsConfig interface

Added in v3.18.0 Source

Configuration for selecting external nodes.

When to use

Use to configure how externals identifies graph boundary nodes when you need sinks with no outgoing edges or sources with no incoming edges.

Details

direction chooses which missing edge direction makes a node external: "outgoing" selects nodes with no outgoing edges, and "incoming" selects nodes with no incoming edges. If omitted, direction defaults to "outgoing".

Signature

interface ExternalsConfig {
readonly direction?: Direction;
}

GraphVizOptions interface

Added in v3.18.0 Source

Configuration options for GraphViz DOT format generation from graphs.

When to use

Use when customizing labels or the graph name produced by toGraphViz.

Details

These options customize node labels, edge labels, and graph naming in DOT format compatible with GraphViz tools.

See

Signature

interface GraphVizOptions<N, E> {
readonly edgeLabel?: (data: E) => string;
readonly graphName?: string;
readonly nodeLabel?: (data: N) => string;
}

Example

(Configuring GraphViz labels)

import type { Graph } from "effect"
// Basic options with custom labels
const basicOptions: Graph.GraphVizOptions<string, number> = {
nodeLabel: (data) => `Node: ${data}`,
edgeLabel: (data) => `Weight: ${data}`
}
// Complete options with graph naming
const namedOptions: Graph.GraphVizOptions<string, string> = {
nodeLabel: (data) => data.toUpperCase(),
edgeLabel: (data) => data,
graphName: "MyDependencyGraph"
}
Array.of(basicOptions.nodeLabel?.("A"), namedOptions.graphName) // => ["Node: A", "MyDependencyGraph"]

IdentityOptions interface

Added in v4.0.0 Source

Configures node and edge identity for graph set operations.

When to use

Use when logical graph membership should be based on a stable key rather than the complete node or edge payload.

Details

Both functions default to using the complete node or edge data. Edge identity also includes the identities of its endpoint nodes and the graph kind. Projected identities use Effect equality and hashing semantics.

Gotchas

Edge identity defines set membership, not edge multiplicity. Parallel edges with the same endpoint identities and projected edge identity are treated as the same member by graph set operations.

Signature

interface IdentityOptions<N, E, NI = N, EI = E> {
readonly edgeIdentity?: (edge: E) => EI;
readonly nodeIdentity?: (node: N) => NI;
}

MaximumFlowConfig interface

Added in v4.0.0 Source

Configuration for source-to-target flow algorithms.

When to use

Use when defining endpoints and edge capacities for maximumFlow or minimumCut.

Details

capacity receives stored edge data and must return a finite, non-negative number.

Gotchas

The source and target must be distinct existing nodes in a directed graph.

Signature

interface MaximumFlowConfig<E> {
readonly capacity: (edge: E) => number;
readonly source: number;
readonly target: number;
}

MermaidOptions interface

Added in v3.18.0 Source

Configuration options for Mermaid diagram generation from graphs.

When to use

Use when customizing labels, layout, node shapes, or syntax emitted by toMermaid.

Details

These options customize node labels, edge labels, diagram type, layout direction and node shapes in Mermaid format.

See

Signature

interface MermaidOptions<N, E> {
readonly diagramType?: MermaidDiagramType;
readonly direction?: MermaidDirection;
readonly edgeLabel?: (data: E) => string;
readonly nodeLabel?: (data: N) => string;
readonly nodeShape?: (data: N) => MermaidNodeShape;
}

Example

(Configuring Mermaid output)

import type { Graph } from "effect"
// Basic options with custom labels
const basicOptions: Graph.MermaidOptions<string, number> = {
nodeLabel: (data) => `Node: ${data}`,
edgeLabel: (data) => `Weight: ${data}`
}
// Advanced options with all features
const advancedOptions: Graph.MermaidOptions<string, string> = {
nodeLabel: (data) => data.toUpperCase(),
edgeLabel: (data) => data,
diagramType: "flowchart",
direction: "LR",
nodeShape: (data) => data.includes("start") ? "circle" : "rectangle"
}
Array.of(basicOptions.nodeLabel?.("A"), advancedOptions.nodeShape?.("start")) // => ["Node: A", "circle"]

NeighborhoodConfig interface

Added in v4.0.0 Source

Configuration for selecting a graph neighborhood.

Details

radius limits the edge distance from the center node and defaults to 1. It accepts non-negative integers and Infinity. direction controls how directed edges are traversed and defaults to "outgoing".

Signature

interface NeighborhoodConfig {
readonly direction?: TraversalDirection;
readonly radius?: number;
}

ReachabilityConfig interface

Added in v4.0.0 Source

Configuration for unweighted reachability queries.

When to use

Use when controlling whether reachability follows outgoing edges, incoming edges, or either direction.

Details

direction defaults to "outgoing" and is ignored for undirected graphs.

Signature

interface ReachabilityConfig {
readonly direction?: TraversalDirection;
}

SearchConfig interface

Added in v3.18.0 Source

Configuration for DFS, BFS, and postorder graph traversals.

When to use

Use to configure the starting node indices and edge-following direction for lazy graph traversals.

Details

start supplies the node indices where traversal begins. If it is omitted, the iterator is empty. Distinct starts are prioritized in supplied order and duplicates are ignored. direction chooses whether traversal follows outgoing edges, incoming edges, or ignores edge direction. radius limits traversal by edge distance from the nearest start node and accepts non-negative integers or Infinity; omitting it means unbounded traversal.

Gotchas

Traversal creation validates and copies start, and throws a GraphError when a start node does not exist or radius is invalid. Each fresh iterator revalidates those starts against the graph snapshot it captures. Later mutations are not observed by an active iterator.

Signature

interface SearchConfig {
readonly direction?: TraversalDirection;
readonly radius?: number;
readonly start?: Array<number>;
}

SimplePathsConfig interface

Added in v4.0.0 Source

Configuration for lazy simple-path enumeration.

When to use

Use when bounding enumeration of loop-free routes between two nodes.

Details

limit bounds the number of yielded paths and defaults to Infinity.

Gotchas

limit must be a non-negative integer or Infinity.

Signature

interface SimplePathsConfig {
readonly limit?: number;
readonly source: number;
readonly target: number;
}

TopoConfig interface

Added in v3.18.0 Source

Configuration for the topological sort iterator.

When to use

Use to prioritize specific zero in-degree nodes in a topological sort.

Details

initials optionally supplies zero in-degree node indices used as prioritized initial queue entries. Topological sorting still includes the other zero in-degree nodes and produces a complete topological order.

Gotchas

Throws a GraphError when any initial node has incoming edges.

Signature

interface TopoConfig {
readonly initials?: Array<number>;
}

Constructors

directed

Added in v3.18.0 Source

Creates a directed graph, optionally with initial mutations.

When to use

Use when relationships have a source-to-target direction, such as dependencies, workflows, or routing links.

Gotchas

The mutable callback handle is finalized when the callback returns and must not be retained for later mutation.

Signature

declare const directed: <N, E>(mutate?: (mutable: MutableDirectedGraph<N, E>) => undefined) => DirectedGraph<N, E>

Example

(Creating a directed graph)

import { Graph } from "effect"
// Directed graph with initial nodes and edges
const graph = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, "A->B")
Graph.addEdge(mutable, b, c, "B->C")
})
Array.of(Graph.nodeCount(graph), Graph.edgeCount(graph)) // => [3, 2]

fromSnapshot

Added in v4.0.0 Source

Reconstructs an immutable graph from its indexed active structure.

When to use

Use when importing a snapshot or other externally indexed graph structure. Prefer directed or undirected when creating a new graph without existing identifiers.

Gotchas

The node and edge arrays must be ordered by strictly increasing, non-negative safe integer indexes, and every edge endpoint must reference a node in the snapshot. Invalid snapshots throw a GraphError. Historical removed identifiers after the greatest active index are not retained.

See

Signature

declare function fromSnapshot<N, E, T extends Kind>(snapshot: Snapshot<N, E, T>): Graph<N, E, T>

Example

(Preserving graph indexes)

import { Graph } from "effect"
const graph = Graph.fromSnapshot({
type: "directed",
nodes: [{ index: 2, data: "A" }, { index: 5, data: "B" }],
edges: [{ index: 3, source: 2, target: 5, data: 1 }]
})
Graph.toSnapshot(graph).edges[0].index // => 3

make

Added in v4.0.0 Source

Creates a graph constructor for the specified graph kind.

When to use

Use when the graph kind is selected dynamically. Prefer directed or undirected when the kind is known statically.

See

  • directed for constructing a directed graph directly
  • undirected for constructing an undirected graph directly

Signature

declare function make<T extends Kind>(type: T): <N, E>(mutate?: (mutable: MutableGraph<N, E, T>) => undefined) => Graph<N, E, T>

Example

(Constructing by kind)

import { Graph } from "effect"
const makeGraph = Graph.make("directed")
const graph = makeGraph<string, number>((mutable) => {
Graph.addNode(mutable, "A")
})
graph.type // => "directed"

undirected

Added in v3.18.0 Source

Creates an undirected graph, optionally with initial mutations.

When to use

Use when relationships connect both endpoints symmetrically, such as social connections or physical links.

Gotchas

The mutable callback handle is finalized when the callback returns and must not be retained for later mutation.

Signature

declare const undirected: <N, E>(mutate?: (mutable: MutableUndirectedGraph<N, E>) => undefined) => UndirectedGraph<N, E>

Example

(Creating an undirected graph)

import { Graph } from "effect"
// Undirected graph with initial nodes and edges
const graph = Graph.undirected<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, "A-B")
Graph.addEdge(mutable, b, c, "B-C")
})
Array.of(Graph.nodeCount(graph), Graph.edgeCount(graph)) // => [3, 2]

Converting

toGraphViz

Added in v3.18.0 Source

Exports a graph to GraphViz DOT format for visualization.

When to use

Use when sending graph structure to GraphViz-compatible visualization or documentation tools.

See

Signature

declare const toGraphViz: {
<N, E>(options?: GraphVizOptions<N, E>): <T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => string;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, options?: GraphVizOptions<N, E>): string;
}

Example

(Exporting GraphViz DOT)

import { Graph } from "effect"
const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
const nodeA = Graph.addNode(mutable, "Node A")
const nodeB = Graph.addNode(mutable, "Node B")
const nodeC = Graph.addNode(mutable, "Node C")
Graph.addEdge(mutable, nodeA, nodeB, 1)
Graph.addEdge(mutable, nodeB, nodeC, 2)
Graph.addEdge(mutable, nodeC, nodeA, 3)
})
Graph.toGraphViz(graph).split("\n") // => ['digraph "G" {', ' "0" [label="Node A"];', ' "1" [label="Node B"];', ' "2" [label="Node C"];', ' "0" -> "1" [label="1"];', ' "1" -> "2" [label="2"];', ' "2" -> "0" [label="3"];', "}"]

toMermaid

Added in v3.18.0 Source

Exports a graph to Mermaid diagram format for visualization.

When to use

Use when embedding graph diagrams in Markdown, documentation sites, or other Mermaid-compatible tools.

Details

Directed graphs default to flowchart with arrow edges, while undirected graphs default to graph with line edges. Labels and node shapes can be customized with MermaidOptions.

See

Signature

declare const toMermaid: {
<N, E>(options?: MermaidOptions<N, E>): <T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => string;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, options?: MermaidOptions<N, E>): string;
}

Example

(Exporting a Mermaid diagram)

import { Graph } from "effect"
const graph = Graph.directed<string, string>((mutable) => {
const app = Graph.addNode(mutable, "App")
const database = Graph.addNode(mutable, "Database")
Graph.addEdge(mutable, app, database, "queries")
})
Graph.toMermaid(graph).split("\n") // => ["flowchart TD", ' 0["App"]', ' 1["Database"]', ' 0 -->|"queries"| 1']

toSnapshot

Added in v4.0.0 Source

Returns the active indexed structure of a graph.

When to use

Use when serializing a graph or passing its active structure across a boundary where node and edge identifiers must be preserved.

Details

Nodes and edges are returned in graph order with their current indexes. Undirected edges retain their stored endpoint orientation, and each returned node and edge record is newly allocated. The operation runs in O(V + E).

Gotchas

Node and edge payloads are not cloned. The snapshot also omits allocator history for identifiers that are no longer active.

See

Signature

declare function toSnapshot<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): Snapshot<N, E, T>

Example

(Round-tripping a graph snapshot)

import { Equal, Graph } from "effect"
const graph = Graph.fromSnapshot({
type: "undirected",
nodes: [{ index: 2, data: "A" }, { index: 5, data: "B" }],
edges: [{ index: 3, source: 5, target: 2, data: "A-B" }]
})
Equal.equals(Graph.fromSnapshot(Graph.toSnapshot(graph)), graph) // => true

Errors

GraphError

Added in v3.18.0 Source

Error thrown by graph operations when the requested graph structure is invalid, such as referencing a missing node or using unsupported edge weights.

When to use

Use when handling failures thrown by graph operations that reject invalid graph structure or unsupported algorithm inputs.

Signature

declare class GraphError extends YieldableError<this> & {
readonly _tag: "GraphError";
} & Readonly<{
readonly message: string;
}> {
constructor(args: {
readonly message: string;
});
}

Filtering

filterEdges

Added in v3.18.0 Source

Filters edges by removing those that don't match the predicate. This function modifies the mutable graph in place. Nodes are retained even when removing edges leaves them isolated.

Gotchas

The predicate may query the graph, but cannot mutate or finalize the same graph while it runs.

Signature

declare function filterEdges<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>, predicate: (data: E) => boolean): void

Example

(Filtering edges)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, 5)
Graph.addEdge(mutable, b, c, 15)
Graph.addEdge(mutable, c, a, 25)
// Keep only edges with weight >= 10
Graph.filterEdges(mutable, (data) => data >= 10)
})
Graph.edgeCount(graph) // => 2

filterMapEdges

Added in v3.18.0 Source

Filters and optionally transforms edges in a mutable graph using a predicate function. Edges that return Option.none are removed from the graph.

Gotchas

The function may query the graph, but cannot mutate or finalize the same graph while it runs. Retained payloads must remain the same edge type.

Signature

declare function filterMapEdges<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>, f: (data: E) => Option<E>): void

Example

(Filtering and mapping edges)

import { Graph, Option } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, 5)
Graph.addEdge(mutable, b, c, 15)
Graph.addEdge(mutable, c, a, 25)
// Keep only edges with weight >= 10 and double their weight
Graph.filterMapEdges(
mutable,
(data) => data >= 10 ? Option.some(data * 2) : Option.none()
)
})
Graph.edgeCount(graph) // => 2

filterMapNodes

Added in v3.18.0 Source

Filters and optionally transforms nodes in a mutable graph using a predicate function. Nodes that return Option.none are removed along with all their connected edges.

Gotchas

The function may query the graph, but cannot mutate or finalize the same graph while it runs. Retained payloads must remain the same node type.

Signature

declare function filterMapNodes<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>, f: (data: N) => Option<N>): void

Example

(Filtering and mapping nodes)

import { Graph, Option } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "active")
const b = Graph.addNode(mutable, "inactive")
const c = Graph.addNode(mutable, "active")
Graph.addEdge(mutable, a, b, 1)
Graph.addEdge(mutable, b, c, 2)
// Keep only "active" nodes and transform to uppercase
Graph.filterMapNodes(
mutable,
(data) =>
data === "active" ? Option.some(data.toUpperCase()) : Option.none()
)
})
Graph.nodeCount(graph) // => 2

filterNodes

Added in v3.18.0 Source

Filters nodes by removing those that don't match the predicate. This function modifies the mutable graph in place. Removed nodes also remove all incident edges; retained node identifiers are preserved.

Gotchas

The predicate may query the graph, but cannot mutate or finalize the same graph while it runs.

Signature

declare function filterNodes<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>, predicate: (data: N) => boolean): void

Example

(Filtering nodes)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
Graph.addNode(mutable, "active")
Graph.addNode(mutable, "inactive")
Graph.addNode(mutable, "pending")
Graph.addNode(mutable, "active")
// Keep only "active" nodes
Graph.filterNodes(mutable, (data) => data === "active")
})
Graph.nodeCount(graph) // => 2

Getters

degree

Added in v4.0.0 Source

Returns the degree of a node in an undirected graph.

Parallel edges count separately and a self-loop contributes two. Throws a GraphError for a directed graph or missing node.

Signature

declare const degree: {
(nodeIndex: number): <N, E>(graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">) => number;
<N, E>(graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">, nodeIndex: number): number;
}

edgeCount

Added in v3.18.0 Source

Returns the number of edges in the graph.

Signature

declare function edgeCount<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): number

Example

(Counting edges)

import { Graph } from "effect"
const emptyGraph = Graph.directed<string, number>()
Graph.edgeCount(emptyGraph) // => 0
const graphWithEdges = Graph.mutate(emptyGraph, (mutable) => {
const nodeA = Graph.addNode(mutable, "Node A")
const nodeB = Graph.addNode(mutable, "Node B")
const nodeC = Graph.addNode(mutable, "Node C")
Graph.addEdge(mutable, nodeA, nodeB, 1)
Graph.addEdge(mutable, nodeB, nodeC, 2)
Graph.addEdge(mutable, nodeC, nodeA, 3)
})
Graph.edgeCount(graphWithEdges) // => 3

edgesBetween

Added in v4.0.0 Source

Returns all edge indices connecting the supplied nodes.

Directed graphs only include edges from source to target; undirected graphs include either stored orientation. Parallel edges are retained. Throws a GraphError when either node does not exist.

Signature

declare const edgesBetween: {
(source: number, target: number): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Array<number>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, source: number, target: number): Array<number>;
}

findEdge

Added in v3.18.0 Source

Finds the first edge that matches the given predicate.

Signature

declare const findEdge: {
<E>(predicate: (data: E, source: number, target: number) => boolean): <N, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Option<number>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, predicate: (data: E, source: number, target: number) => boolean): Option<number>;
}

Example

(Finding the first matching edge)

import { Graph, Option } from "effect"
const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
const nodeA = Graph.addNode(mutable, "Node A")
const nodeB = Graph.addNode(mutable, "Node B")
const nodeC = Graph.addNode(mutable, "Node C")
Graph.addEdge(mutable, nodeA, nodeB, 10)
Graph.addEdge(mutable, nodeB, nodeC, 20)
})
Graph.findEdge(graph, (data) => data > 15) // => Option.some(1)
Graph.findEdge(graph, (data) => data > 100) // => Option.none()

findEdges

Added in v3.18.0 Source

Finds all edges that match the given predicate.

Signature

declare const findEdges: {
<E>(predicate: (data: E, source: number, target: number) => boolean): <N, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Array<number>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, predicate: (data: E, source: number, target: number) => boolean): Array<number>;
}

Example

(Finding matching edges)

import { Graph } from "effect"
const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
const nodeA = Graph.addNode(mutable, "Node A")
const nodeB = Graph.addNode(mutable, "Node B")
const nodeC = Graph.addNode(mutable, "Node C")
Graph.addEdge(mutable, nodeA, nodeB, 10)
Graph.addEdge(mutable, nodeB, nodeC, 20)
Graph.addEdge(mutable, nodeC, nodeA, 30)
})
Graph.findEdges(graph, (data) => data >= 20) // => [1, 2]
Graph.findEdges(graph, (data) => data > 100) // => []

findNode

Added in v3.18.0 Source

Finds the first node that matches the given predicate.

Signature

declare const findNode: {
<N>(predicate: (data: N) => boolean): <E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Option<number>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, predicate: (data: N) => boolean): Option<number>;
}

Example

(Finding the first matching node)

import { Graph, Option } from "effect"
const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
Graph.addNode(mutable, "Node A")
Graph.addNode(mutable, "Node B")
Graph.addNode(mutable, "Node C")
})
Graph.findNode(graph, (data) => data.startsWith("Node B")) // => Option.some(1)
Graph.findNode(graph, (data) => data === "Node D") // => Option.none()

findNodes

Added in v3.18.0 Source

Finds all nodes that match the given predicate.

Signature

declare const findNodes: {
<N>(predicate: (data: N) => boolean): <E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Array<number>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, predicate: (data: N) => boolean): Array<number>;
}

Example

(Finding matching nodes)

import { Graph } from "effect"
const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
Graph.addNode(mutable, "Start A")
Graph.addNode(mutable, "Node B")
Graph.addNode(mutable, "Start C")
})
Graph.findNodes(graph, (data) => data.startsWith("Start")) // => [0, 2]
Graph.findNodes(graph, (data) => data === "Not Found") // => []

getEdge

Added in v3.18.0 Source

Gets the edge data associated with an edge index safely, if it exists.

Signature

declare const getEdge: {
(edgeIndex: number): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Option<Edge<E>>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, edgeIndex: number): Option<Edge<E>>;
}

Example

(Getting edge data)

import { Graph, Option } from "effect"
const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
const nodeA = Graph.addNode(mutable, "Node A")
const nodeB = Graph.addNode(mutable, "Node B")
Graph.addEdge(mutable, nodeA, nodeB, 42)
})
Graph.getEdge(graph, 0) // => Option.some({ source: 0, target: 1, data: 42 })

getNode

Added in v3.18.0 Source

Gets the data associated with a node index safely, if it exists.

Signature

declare const getNode: {
(nodeIndex: number): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Option<N>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, nodeIndex: number): Option<N>;
}

Example

(Getting node data)

import { Graph, Option } from "effect"
const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
Graph.addNode(mutable, "Node A")
})
Graph.getNode(graph, 0) // => Option.some("Node A")

Returns the indices of all edges incident to a node.

Each edge is returned once in graph edge order, including self-loops. Throws a GraphError when the node does not exist.

Signature

declare const incidentEdges: {
(nodeIndex: number): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Array<number>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, nodeIndex: number): Array<number>;
}

Returns the indices of incoming edges for a node in a directed graph.

Parallel edges and self-loops are returned separately in reverse-adjacency order. Throws a GraphError for an undirected graph or missing node.

Signature

declare const incomingEdges: {
(nodeIndex: number): <N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">) => Array<number>;
<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, nodeIndex: number): Array<number>;
}

inDegree

Added in v4.0.0 Source

Returns the in-degree of a node in a directed graph.

Parallel edges count separately and a self-loop contributes one. Throws a GraphError for an undirected graph or missing node.

Signature

declare const inDegree: {
(nodeIndex: number): <N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">) => number;
<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, nodeIndex: number): number;
}

neighbors

Added in v3.18.0 Source

Returns the neighboring node indices for a node.

Details

For directed graphs, neighbors are the targets of outgoing edges. For undirected graphs, neighbors are the other endpoints of incident edges. Each neighbor appears once in first edge occurrence order, including the queried node when it has a self-loop.

Gotchas

Returns an empty array when the node does not exist. For directed graphs, use predecessors when incoming neighbors are required.

Signature

declare const neighbors: {
(nodeIndex: number): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => Array<number>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, nodeIndex: number): Array<number>;
}

Example

(Getting outgoing neighbors)

import { Graph } from "effect"
const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
const nodeA = Graph.addNode(mutable, "Node A")
const nodeB = Graph.addNode(mutable, "Node B")
const nodeC = Graph.addNode(mutable, "Node C")
Graph.addEdge(mutable, nodeA, nodeB, 1)
Graph.addEdge(mutable, nodeA, nodeC, 2)
})
Graph.neighbors(graph, 0) // => [1, 2]
Graph.neighbors(graph, 1) // => []

Gets directed neighbors of a node in a specific direction.

When to use

Use when maintaining existing code that already passes an explicit traversal direction. New code should prefer successors or predecessors. Results contain each node once in first edge occurrence order, and a self-loop contributes the queried node once.

Gotchas

Throws a GraphError when used with an undirected graph.

See

  • successors for outgoing neighbors in a directed graph
  • predecessors for incoming neighbors in a directed graph

Signature

declare const neighborsDirected: {
(nodeIndex: number, direction: Direction): <N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">) => Array<number>;
<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, nodeIndex: number, direction: Direction): Array<number>;
}

Example

(Traversing directed neighbors)

import { Graph } from "effect"
const graph = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
Graph.addEdge(mutable, a, b, "A->B")
})
const nodeA = 0
const nodeB = 1
// Get outgoing neighbors (nodes that nodeA points to)
const outgoing = Graph.neighborsDirected(graph, nodeA, "outgoing")
// Get incoming neighbors (nodes that point to nodeB)
const incoming = Graph.neighborsDirected(graph, nodeB, "incoming")
Array.of(outgoing, incoming) // => [[1], [0]]

nodeCount

Added in v3.18.0 Source

Returns the number of nodes in the graph.

Signature

declare function nodeCount<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): number

Example

(Counting nodes)

import { Graph } from "effect"
const emptyGraph = Graph.directed<string, number>()
Graph.nodeCount(emptyGraph) // => 0
const graphWithNodes = Graph.mutate(emptyGraph, (mutable) => {
Graph.addNode(mutable, "Node A")
Graph.addNode(mutable, "Node B")
Graph.addNode(mutable, "Node C")
})
Graph.nodeCount(graphWithNodes) // => 3

outDegree

Added in v4.0.0 Source

Returns the out-degree of a node in a directed graph.

Parallel edges count separately and a self-loop contributes one. Throws a GraphError for an undirected graph or missing node.

Signature

declare const outDegree: {
(nodeIndex: number): <N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">) => number;
<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, nodeIndex: number): number;
}

Returns the indices of outgoing edges for a node in a directed graph.

Parallel edges and self-loops are returned separately in adjacency order. Throws a GraphError for an undirected graph or missing node.

Signature

declare const outgoingEdges: {
(nodeIndex: number): <N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">) => Array<number>;
<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, nodeIndex: number): Array<number>;
}

predecessors

Added in v4.0.0 Source

Returns the incoming neighbor node indices for a node in a directed graph.

When to use

Use when you need the nodes that reach a node by following incoming edges in a directed graph.

Each node appears once in first incoming edge occurrence order. A self-loop contributes the queried node once.

Gotchas

Throws a GraphError when used with an undirected graph. A missing node returns an empty array.

See

  • successors for outgoing neighbors in a directed graph
  • neighbors for generic neighbor lookup across graph kinds

Signature

declare const predecessors: {
(nodeIndex: number): <N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">) => Array<number>;
<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, nodeIndex: number): Array<number>;
}

successors

Added in v4.0.0 Source

Returns the outgoing neighbor node indices for a node in a directed graph.

When to use

Use when you need the nodes reached by following outgoing edges from a node in a directed graph.

Each node appears once in first outgoing edge occurrence order. A self-loop contributes the queried node once.

Gotchas

Throws a GraphError when used with an undirected graph. A missing node returns an empty array.

See

  • predecessors for incoming neighbors in a directed graph
  • neighbors for generic neighbor lookup across graph kinds

Signature

declare const successors: {
(nodeIndex: number): <N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">) => Array<number>;
<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, nodeIndex: number): Array<number>;
}

Guards

isGraph

Added in v4.0.0 Source

Returns true if a value has the graph runtime type identifier, narrowing it to an immutable or mutable graph.

When to use

Use to narrow an unknown value before treating it as a graph value.

Gotchas

This guard checks the shared graph runtime type identifier and does not distinguish immutable graphs from mutable graphs or directed graphs from undirected graphs.

Signature

declare function isGraph<N = unknown, E = unknown, T extends Kind = Kind, U = never>(u: U | Graph<N, E, T> | MutableGraph<N, E, T>): u is Graph<N, E, T> | MutableGraph<N, E, T>

Iterators

bfs

Added in v3.18.0 Source

Creates a lazy breadth-first traversal iterator from the configured start nodes.

When to use

Use when visiting nodes in increasing unweighted distance from the start nodes.

Details

If no start nodes are supplied, the iterator is empty. The direction option chooses whether to follow outgoing or incoming edges. The radius option limits traversal by edge distance from the start nodes. It accepts non-negative integers and Infinity; omitting it means unbounded traversal.

Gotchas

An invalid radius or missing start node throws a GraphError. Traversing a mutable graph captures a snapshot when iteration begins; later mutations are not observed by that iterator.

See

Signature

declare const bfs: {
(config?: SearchConfig): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => NodeWalker<N>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, config?: SearchConfig): NodeWalker<N>;
}

Example

(Traversing breadth-first)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, 1)
Graph.addEdge(mutable, b, c, 1)
})
// Start from a specific node
Array.from(Graph.indices(Graph.bfs(graph, { start: [0] }))) // => [0, 1, 2]
Array.from(Graph.indices(Graph.bfs(graph))) // => []

dfs

Added in v3.18.0 Source

Creates a lazy depth-first traversal iterator from the configured start nodes.

When to use

Use when exploring one branch deeply before visiting sibling branches.

Details

If no start nodes are supplied, the iterator is empty. The direction option chooses whether to follow outgoing or incoming edges. The radius option limits traversal by edge distance from the start nodes. It accepts non-negative integers and Infinity; omitting it means unbounded traversal.

Gotchas

An invalid radius or missing start node throws a GraphError. Traversing a mutable graph captures a snapshot when iteration begins; later mutations are not observed by that iterator.

See

  • bfs for traversal in increasing hop distance
  • dfsPostOrder for emitting descendants before ancestors

Signature

declare const dfs: {
(config?: SearchConfig): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => NodeWalker<N>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, config?: SearchConfig): NodeWalker<N>;
}

Example

(Traversing depth-first)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, 1)
Graph.addEdge(mutable, b, c, 1)
})
// Start from a specific node
Array.from(Graph.indices(Graph.dfs(graph, { start: [0] }))) // => [0, 1, 2]
Array.from(Graph.indices(Graph.dfs(graph))) // => []

dfsPostOrder

Added in v3.18.0 Source

Creates a lazy depth-first postorder traversal iterator from the configured start nodes.

When to use

Use when reachable descendants must be emitted before the nodes that lead to them.

Details

Nodes are emitted after their reachable descendants have been processed. If no start nodes are supplied, the iterator is empty. The direction option chooses whether to follow outgoing or incoming edges. The radius option limits traversal by edge distance from the start nodes. It accepts non-negative integers and Infinity; omitting it means unbounded traversal. With a finite radius, a bounded breadth-first pass first determines shortest-distance membership before nodes are emitted in postorder.

Gotchas

Invalid radii and missing start nodes throw a GraphError. Traversing a mutable graph captures a snapshot when iteration begins; later mutations are not observed by that iterator.

See

  • dfs for emitting nodes when first visited

Signature

declare const dfsPostOrder: {
(config?: SearchConfig): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => NodeWalker<N>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, config?: SearchConfig): NodeWalker<N>;
}

Example

(Traversing in postorder)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const root = Graph.addNode(mutable, "root")
const child1 = Graph.addNode(mutable, "child1")
const child2 = Graph.addNode(mutable, "child2")
Graph.addEdge(mutable, root, child1, 1)
Graph.addEdge(mutable, root, child2, 1)
})
// Postorder: children before parents
Array.from(Graph.indices(Graph.dfsPostOrder(graph, { start: [0] }))) // => [1, 2, 0]

edges

Added in v3.18.0 Source

Creates a walker over all edge index and edge entries in the graph.

Details

Entries follow graph edge order and include all edges regardless of connectivity. Use indices or values to project one side of each entry.

Gotchas

Mutable graphs are not snapshotted; mutations may affect the remaining iteration.

Signature

declare function edges<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): EdgeWalker<E>

Example

(Iterating all edges)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, 1)
Graph.addEdge(mutable, b, c, 2)
})
Array.from(Graph.indices(Graph.edges(graph))) // => [0, 1]

entries

Added in v3.18.0 Source

Returns an iterator over [index, data] entries in the walker.

Signature

declare function entries<T, N>(walker: Walker<T, N>): Iterable<[T, N]>

Example

(Iterating walker entries)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
Graph.addEdge(mutable, a, b, 1)
})
const dfs = Graph.dfs(graph, { start: [0] })
Array.from(Graph.entries(dfs)) // => [[0, "A"], [1, "B"]]

externals

Added in v3.18.0 Source

Creates an iterator over external nodes (nodes without edges in the specified direction).

When to use

Use when locating sources, sinks, or isolated boundary nodes.

Details

External nodes have no outgoing edges (direction: "outgoing") or no incoming edges (direction: "incoming").

Gotchas

For undirected graphs, incoming and outgoing adjacency are equivalent, so only isolated nodes are external. Mutable graphs are not snapshotted; mutations may affect the remaining iteration.

Signature

declare const externals: {
(config?: ExternalsConfig): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => NodeWalker<N>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, config?: ExternalsConfig): NodeWalker<N>;
}

Example

(Iterating external nodes)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const source = Graph.addNode(mutable, "source") // 0 - no incoming
const middle = Graph.addNode(mutable, "middle") // 1 - has both
const sink = Graph.addNode(mutable, "sink") // 2 - no outgoing
const isolated = Graph.addNode(mutable, "isolated") // 3 - no edges
Graph.addEdge(mutable, source, middle, 1)
Graph.addEdge(mutable, middle, sink, 2)
})
// Nodes with no outgoing edges (sinks + isolated)
Array.from(Graph.indices(Graph.externals(graph, { direction: "outgoing" }))) // => [2, 3]
// Nodes with no incoming edges (sources + isolated)
Array.from(Graph.indices(Graph.externals(graph, { direction: "incoming" }))) // => [0, 3]

indices

Added in v3.18.0 Source

Returns an iterator over the indices in the walker.

Signature

declare function indices<T, N>(walker: Walker<T, N>): Iterable<T>

Example

(Iterating walker indices)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
Graph.addEdge(mutable, a, b, 1)
})
const dfs = Graph.dfs(graph, { start: [0] })
Array.from(Graph.indices(dfs)) // => [0, 1]

nodes

Added in v3.18.0 Source

Creates a walker over all node index and payload entries in the graph.

Details

Entries follow graph node order and include all nodes regardless of connectivity. Use indices or values to project one side of each entry.

Gotchas

Mutable graphs are not snapshotted; mutations may affect the remaining iteration.

Signature

declare function nodes<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): NodeWalker<N>

Example

(Iterating all nodes)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, 1)
})
Array.from(Graph.indices(Graph.nodes(graph))) // => [0, 1, 2]

topo

Added in v3.18.0 Source

Creates a lazy topological-order iterator for a directed acyclic graph.

When to use

Use when processing dependencies so every predecessor is emitted before the nodes that depend on it.

Details

The iterator uses Kahn's algorithm. Multiple valid orders may exist; initials prioritizes eligible zero in-degree nodes without excluding other nodes.

Gotchas

Undirected or cyclic graphs, missing initial nodes, and initial nodes with incoming edges throw a GraphError. Traversing a mutable graph captures a snapshot when iteration begins; later mutations are not observed.

See

  • isAcyclic for checking the required graph property

Signature

declare const topo: {
(config?: TopoConfig): <N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">) => NodeWalker<N>;
<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">, config?: TopoConfig): NodeWalker<N>;
}

Example

(Sorting topologically)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, 1)
Graph.addEdge(mutable, b, c, 1)
})
Array.from(Graph.indices(Graph.topo(graph))) // => [0, 1, 2]

values

Added in v3.18.0 Source

Returns an iterator over the values (data) in the walker.

Signature

declare function values<T, N>(walker: Walker<T, N>): Iterable<N>

Example

(Iterating walker values)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
Graph.addEdge(mutable, a, b, 1)
})
const dfs = Graph.dfs(graph, { start: [0] })
Array.from(Graph.values(dfs)) // => ["A", "B"]

Mapping

mapEdges

Added in v3.18.0 Source

Transforms every edge payload in a mutable graph in place.

When to use

Use when updating every edge payload without changing graph structure.

Details

Edge identifiers and endpoints are preserved.

Gotchas

This function mutates in place, returns void, and cannot change the edge payload type. The mapping function may query the graph, but cannot mutate or finalize the same graph while it runs.

See

Signature

declare function mapEdges<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>, f: (data: E) => E): void

Example

(Mapping edge data)

import { Graph, Option } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, 10)
Graph.addEdge(mutable, b, c, 20)
Graph.mapEdges(mutable, (data) => data * 2)
})
Option.map(Graph.getEdge(graph, 0), (edge) => edge.data) // => Option.some(20)

mapNodes

Added in v3.18.0 Source

Transforms every node's data in a mutable graph in place using the provided mapping function.

When to use

Use when updating every node payload without changing graph structure.

Details

Node indices and edges are preserved; only the stored node data is replaced.

Gotchas

This function mutates in place, returns void, and cannot change the node payload type. The mapping function may query the graph, but cannot mutate or finalize the same graph while it runs.

See

Signature

declare function mapNodes<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>, f: (data: N) => N): void

Example

(Mapping node data)

import { Graph, Option } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
Graph.addNode(mutable, "node a")
Graph.addNode(mutable, "node b")
Graph.addNode(mutable, "node c")
Graph.mapNodes(mutable, (data) => data.toUpperCase())
})
Graph.getNode(graph, 0) // => Option.some("NODE A")

Models

AllPairsResult interface

Added in v3.18.0 Source

Result of an all-pairs shortest path computation.

When to use

Use when storing or passing around the complete output of floydWarshall so callers can look up shortest distances, node and edge paths, and edge data for any source and target node pair.

Details

Contains distance, node-path, edge-index-path, and edge-data maps keyed by source and target node indices. Unreachable pairs have distance Infinity, path null, and empty edge and cost arrays.

Signature

interface AllPairsResult<E> {
readonly costs: Map<number, Map<number, Array<E>>>;
readonly distances: Map<number, Map<number, number>>;
readonly edges: Map<number, Map<number, Array<number>>>;
readonly paths: Map<number, Map<number, Array<number> | null>>;
}

BipartiteMatch interface

Added in v4.0.0 Source

A pair of matched nodes and the edge that realizes the match.

Details

left and right refer to the bipartition derived by maximumBipartiteMatching, not to the stored edge orientation.

Signature

interface BipartiteMatch {
readonly edge: number;
readonly left: number;
readonly right: number;
}

CycleResult interface

Added in v4.0.0 Source

A cycle witness containing a closed node path and its traversed edges.

Details

path repeats its first node at the end, so edges.length is always path.length - 1.

Signature

interface CycleResult {
readonly edges: Array<number>;
readonly path: Array<number>;
}

DirectedGraph type

Added in v3.18.0 Source

Immutable graph type for source-to-target relationships.

When to use

Use as the immutable graph type when edge direction is part of the model and traversal or neighbor queries should follow source-to-target edges.

Details

DirectedGraph<N, E> is a Graph<N, E, "directed"> with node data of type N and edge data of type E.

See

Signature

type DirectedGraph<N, E> = Graph<N, E, "directed">

Direction type

Added in v3.18.0 Source

Direction of directed edges relative to a node.

Details

"outgoing" selects edges whose source is the node, while "incoming" selects edges whose target is the node.

Signature

type Direction = "outgoing" | "incoming"

Edge interface

Added in v3.18.0 Source

Represents edge data containing source, target, and user data.

When to use

Use as the graph edge value that carries source node, target node, and stored edge data together.

Signature

interface Edge<out E> {
readonly data: E;
readonly source: number;
readonly target: number;
}

EdgeIndex type

Added in v3.18.0 Source

Edge index for edge identification using plain numbers.

When to use

Use when you need to keep the identifier for a graph edge so you can later read, update, remove, or compare that edge.

Gotchas

An EdgeIndex is an identifier, not an array offset. Removed edge identifiers are not reused.

See

  • NodeIndex for node identifiers instead of edge identifiers

Signature

type EdgeIndex = number

EdgeWalker type

Added in v3.18.0 Source

Type alias for edge iteration using Walker. EdgeWalker is represented as Walker<EdgeIndex, Edge>.

When to use

Use to type helpers or parameters that consume edge iterators returned by Graph APIs, where each item is keyed by an EdgeIndex and carries the full Edge.

See

  • Walker for the generic lazy iterator wrapper
  • NodeWalker for node iterators
  • edges for creating edge walkers

Signature

type EdgeWalker<E> = Walker<EdgeIndex, Edge<E>>

Graph

Added in v4.0.0 Source

Companion namespace containing type-level metadata for immutable graphs.

Graph interface

Added in v3.18.0 Source

Immutable graph interface.

When to use

Use as the immutable graph model for code that queries, traverses, transforms, or analyzes graph structure without mutating it.

Gotchas

After a graph is hashed, its transitively contained node and edge payloads used by hashing must remain immutable, as with other Effect values.

See

  • MutableGraph for the mutable counterpart used inside mutation scopes

Signature

interface Graph<out N, out E, T extends Kind = "directed"> extends Proto<N, E> {
readonly mutable: false;
readonly type: T;
}

IndexedEdge interface

Added in v4.0.0 Source

An edge and its stable index in a graph snapshot.

Signature

interface IndexedEdge<out E> extends Edge<E> {
readonly index: number;
}

IndexedNode interface

Added in v4.0.0 Source

A node and its stable index in a graph snapshot.

Signature

interface IndexedNode<out N> {
readonly data: N;
readonly index: number;
}

Kind type

Added in v3.18.0 Source

Graph type for distinguishing directed and undirected graphs.

When to use

Use when writing graph-polymorphic types or helpers that need to preserve whether a graph is directed or undirected.

Signature

type Kind = "directed" | "undirected"

MaximumFlowResult interface

Added in v4.0.0 Source

Maximum flow value, per-edge flows, and a corresponding minimum cut.

Details

flows contains every original edge, including zero-flow edges. cut contains the crossing edge identifiers of the corresponding minimum cut.

Signature

interface MaximumFlowResult {
readonly cut: Array<number>;
readonly flows: Map<number, number>;
readonly value: number;
}

MermaidDiagramType type

Added in v3.18.0 Source

Mermaid diagram types for different visualization formats.

Details

Specifies the Mermaid diagram syntax to use:

  • flowchart: For directed graphs with arrows (A --> B)
  • graph: For undirected graphs with lines (A --- B)

When not specified, automatically selects based on graph type: directed graphs use "flowchart", undirected graphs use "graph".

Signature

type MermaidDiagramType = "flowchart" | "graph"

Example

(Selecting Mermaid diagram types)

import type { Graph } from "effect"
// Force flowchart format (even for undirected graphs)
const flowchartOptions: Graph.MermaidOptions<string, string> = {
diagramType: "flowchart"
}
// Force graph format (shows undirected connections)
const graphOptions: Graph.MermaidOptions<string, string> = {
diagramType: "graph"
}
// Auto-detection (recommended, default behavior)
const autoOptions: Graph.MermaidOptions<string, string> = {}
Array.of(flowchartOptions.diagramType, graphOptions.diagramType, autoOptions.diagramType) // => ["flowchart", "graph", undefined]

MermaidDirection type

Added in v3.18.0 Source

Mermaid diagram direction types for controlling layout orientation.

Details

Determines the flow direction of nodes and edges in the diagram:

  • TB/TD: Top to Bottom (vertical layout, default)
  • BT: Bottom to Top (reverse vertical)
  • LR: Left to Right (horizontal layout)
  • RL: Right to Left (reverse horizontal)

Signature

type MermaidDirection = "TB" | "TD" | "BT" | "RL" | "LR"

Example

(Configuring Mermaid directions)

import type { Graph } from "effect"
// Horizontal workflow diagram
const horizontalOptions: Graph.MermaidOptions<string, string> = {
direction: "LR"
}
// Vertical hierarchy (default)
const verticalOptions: Graph.MermaidOptions<string, string> = {
direction: "TB"
}
// Bottom-up flow
const bottomUpOptions: Graph.MermaidOptions<string, string> = {
direction: "BT"
}
Array.of(horizontalOptions.direction, verticalOptions.direction, bottomUpOptions.direction) // => ["LR", "TB", "BT"]

MermaidNodeShape type

Added in v3.18.0 Source

Mermaid node shape types for diagram visualization.

Details

Each shape produces different visual representations in Mermaid diagrams:

  • rectangle: Standard rectangular nodes A["label"]
  • rounded: Rounded rectangular nodes A("label")
  • circle: Circular nodes A(("label"))
  • diamond: Diamond-shaped nodes A{"label"}
  • hexagon: Hexagonal nodes A{{"label"}}
  • stadium: Stadium-shaped nodes A(["label"])
  • subroutine: Subroutine-style nodes A[["label"]]
  • cylindrical: Cylindrical database-style nodes A[("label")]

Signature

type MermaidNodeShape = "rectangle" | "rounded" | "circle" | "diamond" | "hexagon" | "stadium" | "subroutine" | "cylindrical"

Example

(Selecting Mermaid node shapes)

import type { Graph } from "effect"
// Shape selector function for different node types
const shapeSelector = (nodeData: string): Graph.MermaidNodeShape => {
if (nodeData.includes("start") || nodeData.includes("end")) return "circle"
if (nodeData.includes("decision")) return "diamond"
if (nodeData.includes("process")) return "rectangle"
if (nodeData.includes("data")) return "cylindrical"
return "rounded"
}
const options: Graph.MermaidOptions<string, string> = {
nodeShape: shapeSelector
}
options.nodeShape?.("decision") // => "diamond"

MinimumCutResult interface

Added in v4.0.0 Source

Minimum cut value, crossing edges, and residual-reachability partitions.

Details

source contains nodes residual-reachable from the configured source and target contains the remaining nodes.

Signature

interface MinimumCutResult {
readonly edges: Array<number>;
readonly source: Array<number>;
readonly target: Array<number>;
readonly value: number;
}

MutableDirectedGraph type

Added in v3.18.0 Source

Mutable directed graph type alias.

When to use

Use when annotating a temporary graph value that can be changed in place and whose edges have source-to-target direction.

See

Signature

type MutableDirectedGraph<N, E> = MutableGraph<N, E, "directed">

MutableGraph

Added in v4.0.0 Source

Companion namespace containing type-level metadata for scoped mutable graphs.

MutableGraph interface

Added in v3.18.0 Source

Mutable graph interface.

When to use

Use when adding, removing, or updating nodes and edges inside a graph mutation scope.

Gotchas

A callback invoked by another graph operation may query the same mutable graph, but cannot mutate or finalize it. Mutation is allowed in callbacks passed to graph constructors and mutate, where mutation is the purpose.

See

  • Graph for the immutable graph interface
  • mutate for scoped mutation of an immutable graph

Signature

interface MutableGraph<in out N, in out E, T extends Kind = "directed"> extends Iterable<readonly [NodeIndex, N]>, Equal, Pipeable, Inspectable {
readonly "~effect/collections/Graph": Variance<N, E>;
readonly mutable: true;
readonly type: T;
}

Mutable undirected graph type alias.

When to use

Use when annotating a temporary graph value that can be changed in place and whose edges connect both endpoints without direction.

See

Signature

type MutableUndirectedGraph<N, E> = MutableGraph<N, E, "undirected">

NodeIndex type

Added in v3.18.0 Source

Node index for node identification using plain numbers.

When to use

Use when storing or passing the stable identifier of a graph node between Graph operations.

Details

addNode allocates node identifiers from the graph's next node index.

Gotchas

A NodeIndex is an identifier, not an array offset. Removed node identifiers are not reused.

See

  • EdgeIndex for edge identifiers instead of node identifiers

Signature

type NodeIndex = number

NodeWalker type

Added in v3.18.0 Source

Type alias for node iteration using Walker. NodeWalker is represented as Walker<NodeIndex, N>.

When to use

Use as the shared node walker type returned by graph traversal and node listing APIs.

See

Signature

type NodeWalker<N> = Walker<NodeIndex, N>

PathResult interface

Added in v3.18.0 Source

Result of a shortest path computation.

When to use

Use to read the successful source-to-target shortest path returned by path-finding algorithms, including the ordered node and edge indices, total distance, and traversed edge data.

Details

Contains the node-index path, the traversed edge indices, the total numeric distance, and the edge data encountered along the path.

Gotchas

costs contains original edge data, not the numeric output of the cost function unless the edge data is numeric.

Signature

interface PathResult<E> {
readonly costs: Array<E>;
readonly distance: number;
readonly edges: Array<number>;
readonly path: Array<number>;
}

PathWalker interface

Added in v4.0.0 Source

A repeatable lazy iterable of edge-aware graph paths.

When to use

Use as the lazy result of graph path-enumeration functions.

Details

Each fresh iterator repeats the path enumeration.

Signature

interface PathWalker<E> extends Iterable<PathResult<E>> {}

Snapshot interface

Added in v4.0.0 Source

Active indexed structure used to reconstruct an immutable graph.

When to use

Use when serializing or importing graph structure while preserving active node and edge identifiers.

Details

Node and edge indexes must be non-negative safe integers in strictly increasing order. Every edge endpoint must reference an indexed node.

Gotchas

A snapshot records only active identifiers, not allocator history. After reconstruction, new identifiers continue after the greatest active index.

See

Signature

interface Snapshot<out N, out E, out T extends Kind> {
readonly edges: readonly Array<IndexedEdge<E>>;
readonly nodes: readonly Array<IndexedNode<N>>;
readonly type: T;
}

TraversalDirection type

Added in v4.0.0 Source

Controls how traversal follows directed edges.

Details

"outgoing" follows edges from source to target, "incoming" follows them from target to source, and "undirected" allows traversal in either direction.

Signature

type TraversalDirection = Direction | "undirected"

Example

(Traversing by direction)

import { Graph } from "effect"
const graph = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, "A-B")
Graph.addEdge(mutable, a, c, "A-C")
})
Array.from(Graph.indices(Graph.bfs(graph, { start: [0], direction: "outgoing" }))) // => [0, 1, 2]
Array.from(Graph.indices(Graph.bfs(graph, { start: [1], direction: "incoming" }))) // => [1, 0]
Array.from(Graph.indices(Graph.bfs(graph, { start: [1], direction: "undirected" }))) // => [1, 0, 2]

UndirectedGraph type

Added in v3.18.0 Source

Immutable graph type for relationships without source-to-target direction.

When to use

Use when modeling relationships where each edge connects both endpoints without a source-to-target direction.

Details

UndirectedGraph<N, E> is a Graph<N, E, "undirected">.

See

  • undirected for constructing undirected graphs
  • DirectedGraph for graphs whose edges have source-to-target direction

Signature

type UndirectedGraph<N, E> = Graph<N, E, "undirected">

Walker

Added in v3.18.0 Source

Represents an iterable wrapper used by graph traversal and listing APIs.

Details

A Walker yields [index, data] pairs lazily and can be viewed as just the indices, just the values, or mapped entries with indices, values, entries, and visit.

Signature

declare class Walker<T, N> implements Iterable<[T, N]> {
constructor<T, N>(visit: <U>(f: (index: T, data: N) => U) => Iterable<U>);
readonly [iterator]: () => Iterator<[T, N]>;
readonly visit: <U>(f: (index: T, data: N) => U) => Iterable<U>;
}

Example

(Working with node walkers)

import { Graph } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
Graph.addEdge(mutable, a, b, 1)
})
// Both traversal and element iterators return NodeWalker
const dfsNodes: Graph.NodeWalker<string> = Graph.dfs(graph, { start: [0] })
const allNodes: Graph.NodeWalker<string> = Graph.nodes(graph)
// Common interface for working with node iterables
function processNodes<N>(nodeIterable: Graph.NodeWalker<N>): Array<number> {
return Array.from(Graph.indices(nodeIterable))
}
// Access node data using values() or entries()
Array.from(Graph.values(dfsNodes)) // => ["A", "B"]
Array.from(Graph.entries(allNodes)) // => [[0, "A"], [1, "B"]]

Mutations

addEdge

Added in v3.18.0 Source

Adds a new edge to a mutable graph and returns its index.

When to use

Use to connect two existing nodes in a mutable graph while storing edge data and receiving the new edge identifier.

Details

Self-loops and parallel edges are allowed. Undirected graphs retain the supplied source and target orientation in the stored Edge, while traversal and neighbor queries treat the connection as bidirectional.

Gotchas

The source and target nodes must already exist in the mutable graph; missing endpoints throw a GraphError.

See

  • mutate for obtaining a mutable graph from an immutable graph
  • addNode for creating node indexes before connecting them

Signature

declare function addEdge<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>, source: number, target: number, data: E): number

Example

(Adding edges)

import { Graph } from "effect"
Graph.mutate(Graph.directed<string, number>(), (mutable) => {
const nodeA = Graph.addNode(mutable, "Node A")
const nodeB = Graph.addNode(mutable, "Node B")
Graph.addEdge(mutable, nodeA, nodeB, 42) // => 0
})

addNode

Added in v3.18.0 Source

Adds a new node to a mutable graph and returns its index.

When to use

Use to allocate a new node in a mutable graph before storing edges or querying it by index.

Details

The returned index is allocated from the graph's next node index. The mutable graph stores the node data and initializes empty incoming and outgoing edge indexes for the new node.

Gotchas

NodeIndex values are identifiers and are not reused after removals.

See

  • mutate for obtaining a mutable graph from an immutable graph
  • addEdge for connecting existing nodes

Signature

declare function addNode<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>, data: N): number

Example

(Adding nodes)

import { Graph } from "effect"
Graph.mutate(Graph.directed<string, number>(), (mutable) => {
Graph.addNode(mutable, "Node A") // => 0
Graph.addNode(mutable, "Node B") // => 1
})

beginMutation

Added in v3.18.0 Source

Creates a mutable copy of an immutable graph for a manual mutation scope.

When to use

Use when a mutation scope must span code that cannot be expressed as one mutate callback.

Gotchas

The graph structure is copied, but node and edge payload objects remain shared by reference. Always finish the scope with endMutation; prefer mutate when a callback is sufficient.

See

  • endMutation for finalizing the mutable graph
  • mutate for automatically scoped mutation

Signature

declare function beginMutation<N, E, T extends Kind = "directed">(graph: Graph<N, E, T>): MutableGraph<N, E, T>

Example

(Beginning a mutation scope)

import { Graph } from "effect"
const graph = Graph.directed<string, number>()
const mutable = Graph.beginMutation(graph)
// Now mutable can be safely modified without affecting original graph
Array.of(Graph.nodeCount(mutable), Graph.nodeCount(graph)) // => [0, 0]

endMutation

Added in v3.18.0 Source

Converts a mutable graph back to an immutable graph, ending the mutation scope.

When to use

Use to finish a mutation scope opened with beginMutation.

Gotchas

Finalization is terminal. Later public mutation operations on the same mutable handle fail with a GraphError.

See

Signature

declare function endMutation<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>): Graph<N, E, T>

Example

(Ending a mutation scope)

import { Graph } from "effect"
const graph = Graph.directed<string, number>()
const mutable = Graph.beginMutation(graph)
// ... perform mutations on mutable ...
Graph.nodeCount(Graph.endMutation(mutable)) // => 0

mutate

Added in v3.18.0 Source

Returns an immutable graph after applying scoped mutations to a structural copy.

When to use

Use for the usual immutable update workflow when several node or edge mutations should be applied together.

Details

The original graph remains structurally unchanged. The mutable callback handle is finalized whether the callback returns or throws.

Gotchas

Payload objects are shared unless the callback replaces them. A callback failure is rethrown after the mutable handle is finalized, and the handle must not escape for later mutation.

See

Signature

declare const mutate: {
<N, E, T extends Kind = "directed">(f: (mutable: MutableGraph<N, E, T>) => undefined): (graph: Graph<N, E, T>) => Graph<N, E, T>;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T>, f: (mutable: MutableGraph<N, E, T>) => undefined): Graph<N, E, T>;
}

Example

(Applying scoped mutations)

import { Graph } from "effect"
const graph = Graph.directed<string, number>()
const newGraph = Graph.mutate(graph, (mutable) => {
const nodeA = Graph.addNode(mutable, "A")
const nodeB = Graph.addNode(mutable, "B")
Graph.addEdge(mutable, nodeA, nodeB, 1)
})
Graph.nodeCount(newGraph) // => 2
Graph.edgeCount(newGraph) // => 1

removeEdge

Added in v3.18.0 Source

Removes an edge from a mutable graph.

Gotchas

A missing edge index is ignored.

Signature

declare function removeEdge<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>, edgeIndex: number): void

Example

(Removing an edge)

import { Graph } from "effect"
const result = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
const nodeA = Graph.addNode(mutable, "Node A")
const nodeB = Graph.addNode(mutable, "Node B")
const edge = Graph.addEdge(mutable, nodeA, nodeB, 42)
// Remove the edge
Graph.removeEdge(mutable, edge)
})
Array.of(Graph.nodeCount(result), Graph.edgeCount(result)) // => [2, 0]

removeEdges

Added in v4.0.0 Source

Removes multiple edges from a mutable graph.

When to use

Use when deleting a collection of edges in one mutation pass.

Details

The input is collected before mutation, so it may be backed by an iterator over the same graph.

Gotchas

Missing and duplicate edge indices are ignored. Nodes are never removed.

See

Signature

declare function removeEdges<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>, edgeIndices: Iterable<number>): void

removeNode

Added in v3.18.0 Source

Removes a node and all its incident edges from a mutable graph.

Gotchas

A missing node index is ignored.

Signature

declare function removeNode<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>, nodeIndex: number): void

Example

(Removing a node)

import { Graph } from "effect"
const result = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
const nodeA = Graph.addNode(mutable, "Node A")
const nodeB = Graph.addNode(mutable, "Node B")
Graph.addEdge(mutable, nodeA, nodeB, 42)
// Remove nodeA and all edges connected to it
Graph.removeNode(mutable, nodeA)
})
Array.of(Graph.nodeCount(result), Graph.edgeCount(result)) // => [1, 0]

removeNodes

Added in v4.0.0 Source

Removes multiple nodes and all their incident edges from a mutable graph.

When to use

Use when deleting a collection of nodes in one mutation pass.

Details

The input is collected before mutation, so it may be backed by an iterator over the same graph.

Gotchas

Missing and duplicate node indices are ignored. Removing a node also removes all of its incident edges.

See

Signature

declare function removeNodes<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>, nodeIndices: Iterable<number>): void

Predicates

hasEdge

Added in v3.18.0 Source

Checks whether an edge exists between two nodes in the graph.

Details

Directed graphs test only source to target; undirected graphs accept either stored orientation. Parallel edges still produce one boolean result.

Gotchas

Returns false when either node does not exist.

See

Signature

declare const hasEdge: {
(source: number, target: number): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => boolean;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, source: number, target: number): boolean;
}

Example

(Checking edge existence)

import { Graph } from "effect"
const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
const nodeA = Graph.addNode(mutable, "Node A")
const nodeB = Graph.addNode(mutable, "Node B")
const nodeC = Graph.addNode(mutable, "Node C")
Graph.addEdge(mutable, nodeA, nodeB, 42)
})
Graph.hasEdge(graph, 0, 1) // => true
Graph.hasEdge(graph, 0, 2) // => false

hasNode

Added in v3.18.0 Source

Checks whether a node with the given index exists in the graph.

Signature

declare const hasNode: {
(nodeIndex: number): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => boolean;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, nodeIndex: number): boolean;
}

Example

(Checking node existence)

import { Graph } from "effect"
const graph = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
Graph.addNode(mutable, "Node A")
})
Graph.hasNode(graph, 0) // => true
Graph.hasNode(graph, 999) // => false

hasPath

Added in v4.0.0 Source

Tests whether a target is reachable from a source.

When to use

Use when you only need a reachability boolean rather than distances or a reconstructed path.

Details

Directed traversal is outgoing by default and can be changed with direction. A node is reachable from itself.

Gotchas

Throws a GraphError when either endpoint does not exist.

See

Signature

declare const hasPath: {
(source: number, target: number, options?: ReachabilityConfig): <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>) => boolean;
<N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>, source: number, target: number, options?: ReachabilityConfig): boolean;
}

isConnected

Added in v4.0.0 Source

Tests whether an undirected graph has at most one connected component.

When to use

Use when checking undirected connectivity without allocating the component partition.

Gotchas

The empty graph is considered connected. Throws a GraphError when used with a directed graph.

See

Signature

declare function isConnected<N, E>(graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">): boolean

Tests whether a directed graph has at most one strongly connected component.

When to use

Use when checking that every node in a directed graph can reach every other node.

Gotchas

The empty graph is considered strongly connected. Throws a GraphError when used with an undirected graph.

See

Signature

declare function isStronglyConnected<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">): boolean

isTree

Added in v4.0.0 Source

Tests whether a non-empty undirected graph is a tree.

When to use

Use when validating that an undirected graph is connected and has no cycle.

Gotchas

The empty graph is not a tree. Parallel edges and self-loops prevent a graph from being a tree. Throws a GraphError when used with a directed graph.

Signature

declare function isTree<N, E>(graph: Graph<N, E, "undirected"> | MutableGraph<N, E, "undirected">): boolean

Tests whether a directed graph has at most one weakly connected component.

When to use

Use when checking whether a directed graph is connected after ignoring edge orientation.

Gotchas

The empty graph is considered weakly connected. Throws a GraphError when used with an undirected graph.

See

Signature

declare function isWeaklyConnected<N, E>(graph: Graph<N, E, "directed"> | MutableGraph<N, E, "directed">): boolean

Protocols

Proto interface

Added in v3.18.0 Source

Common public protocol for graph values.

Details

Contains only the runtime marker and shared protocols. Graph storage is kept internal; use module functions such as nodes, edges, getNode, and getEdge to inspect graph contents.

Signature

interface Proto<out N, out E> extends Iterable<readonly [NodeIndex, N]>, Equal, Pipeable, Inspectable {
readonly "~effect/collections/Graph": Variance<N, E>;
}

Set Operations

complement

Added in v4.0.0 Source

Returns the complement over the existing node set.

When to use

Use when materializing every relationship that is currently absent between distinct nodes.

Details

Directed graphs add each missing ordered pair. Undirected graphs add each missing unordered pair once. The createEdge function receives the source and target node data for each added edge. The result has the same graph kind as self.

G' = {V, ((V x V) without self-pairs) \ E}

Gotchas

Self-loops are never created. If any edge already connects a pair, parallel complement edges are not added. The result allocates new identifiers.

See

  • hasEdge for testing one relationship

Signature

declare const complement: {
<N, E>(createEdge: (source: N, target: N) => E): <T extends Kind = "directed">(self: Graph<N, E, T>) => Graph<N, E, T>;
<N, E, T extends Kind = "directed">(self: Graph<N, E, T>, createEdge: (source: N, target: N) => E): Graph<N, E, T>;
}

Example

(Finding missing relationships)

import { Graph } from "effect"
const graph = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
Graph.addEdge(mutable, a, b, "A-B")
})
const result = Graph.complement(graph, (source, target) => `${source}-${target}`)
Graph.edgeCount(result) // => 1

compose

Added in v4.0.0 Source

Composes two graphs, merging nodes by identity.

When to use

Use when combining graphs that describe overlapping logical entities and should merge those entities by payload or a projected identity.

Details

Nodes and edges present in both graphs use data from that. The result has the same graph kind as self. Throws a GraphError when the graph kinds do not match. nodeIdentity and edgeIdentity default to the complete node and edge data. Edge identity also includes the endpoint identities.

G1 ∪ G2 = {V1 ∪ V2, E1 ∪ E2}

Gotchas

Nodes with equal identities in one input graph are coalesced. The last node supplies the data, and redirected edges can collapse or become self-loops. Parallel edges with equal identities are also coalesced, with the last edge supplying the data. The result allocates new node and edge identifiers.

See

  • sum for combining graphs without merging equal nodes

Signature

declare const compose: {
<N, E, T extends Kind = "directed", NI = N, EI = E>(that: Graph<N, E, T>, options?: IdentityOptions<N, E, NI, EI>): (self: Graph<N, E, NoInfer<T>>) => Graph<N, E, T>;
<N, E, T extends Kind = "directed", NI = N, EI = E>(self: Graph<N, E, T>, that: Graph<N, E, NoInfer<T>>, options?: IdentityOptions<N, E, NI, EI>): Graph<N, E, T>;
}

Example

(Combining graphs)

import { Graph } from "effect"
const left = Graph.directed<{ id: string }, string>((mutable) => {
const a = Graph.addNode(mutable, { id: "A" })
const b = Graph.addNode(mutable, { id: "B" })
Graph.addEdge(mutable, a, b, "A-B")
})
const right = Graph.directed<{ id: string }, string>((mutable) => {
const b = Graph.addNode(mutable, { id: "B" })
const c = Graph.addNode(mutable, { id: "C" })
Graph.addEdge(mutable, b, c, "B-C")
})
const result = Graph.compose(left, right, {
nodeIdentity: (node) => node.id
})
Graph.nodeCount(result) // => 3
Graph.edgeCount(result) // => 2

difference

Added in v4.0.0 Source

Returns self without edges also present in that.

When to use

Use when retaining all logical nodes from one graph while removing edge relationships also represented by another graph.

Details

All nodes from self are preserved. Edges are matched by endpoint and edge identities. The result has the same graph kind as self. Throws a GraphError when the graph kinds do not match. nodeIdentity and edgeIdentity default to the complete node and edge data.

G1 \ G2 = {V1, E1 \ E2}

Gotchas

Nodes with equal identities in one input graph are coalesced. The last node supplies the data, and redirected edges can collapse or become self-loops. If that contains an edge identity, every parallel edge with that identity is removed from self. The result allocates new node and edge identifiers.

See

Signature

declare const difference: {
<N, E, T extends Kind = "directed", NI = N, EI = E>(that: Graph<N, E, T>, options?: IdentityOptions<N, E, NI, EI>): (self: Graph<N, E, NoInfer<T>>) => Graph<N, E, T>;
<N, E, T extends Kind = "directed", NI = N, EI = E>(self: Graph<N, E, T>, that: Graph<N, E, NoInfer<T>>, options?: IdentityOptions<N, E, NI, EI>): Graph<N, E, T>;
}

Example

(Removing shared edges)

import { Graph } from "effect"
const left = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, "A-B")
Graph.addEdge(mutable, b, c, "B-C")
})
const right = Graph.directed<string, string>((mutable) => {
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, b, c, "B-C")
})
const result = Graph.difference(left, right)
Graph.nodeCount(result) // => 3
Graph.edgeCount(result) // => 1

Returns the subgraph induced by a collection of node indices.

When to use

Use when selecting an exact node set while preserving its active node and edge identifiers.

Details

Node and edge indices are preserved. Duplicate input indices are ignored, output ordering follows the original graph, and every edge whose endpoints are both selected is retained.

Gotchas

Throws a GraphError when a selected node does not exist.

See

Signature

declare const inducedSubgraph: {
(nodeIndices: Iterable<number>): <N, E, T extends Kind = "directed">(self: Graph<N, E, T>) => Graph<N, E, T>;
<N, E, T extends Kind = "directed">(self: Graph<N, E, T>, nodeIndices: Iterable<number>): Graph<N, E, T>;
}

intersection

Added in v4.0.0 Source

Returns the intersection of two graphs, matching nodes by identity.

When to use

Use when extracting nodes and edges that represent the same logical structure in both graphs.

Details

Node data comes from self, and edge data comes from that. The result has the same graph kind as self. Throws a GraphError when the graph kinds do not match. nodeIdentity and edgeIdentity default to the complete node and edge data. Edge identity also includes the endpoint identities.

G1 ∩ G2 = {V1 ∩ V2, E1 ∩ E2}

Gotchas

Nodes with equal identities in one input graph are coalesced. The last node supplies the data, and redirected edges can collapse or become self-loops. The result contains at most one edge for each shared edge identity and allocates new node and edge identifiers.

See

  • compose for identity-based graph union

Signature

declare const intersection: {
<N, E, T extends Kind = "directed", NI = N, EI = E>(that: Graph<N, E, T>, options?: IdentityOptions<N, E, NI, EI>): (self: Graph<N, E, NoInfer<T>>) => Graph<N, E, T>;
<N, E, T extends Kind = "directed", NI = N, EI = E>(self: Graph<N, E, T>, that: Graph<N, E, NoInfer<T>>, options?: IdentityOptions<N, E, NI, EI>): Graph<N, E, T>;
}

Example

(Finding shared structure)

import { Graph } from "effect"
const left = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
Graph.addEdge(mutable, a, b, "shared")
})
const right = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
Graph.addEdge(mutable, a, b, "shared")
})
const result = Graph.intersection(left, right)
Graph.nodeCount(result) // => 2
Graph.edgeCount(result) // => 1

neighborhood

Added in v4.0.0 Source

Returns the induced subgraph containing nodes within a radius of a node.

When to use

Use when extracting a local reachable region around one node.

Details

The radius option is the maximum edge distance from nodeIndex, accepts non-negative integers and Infinity, and defaults to 1. Invalid radii throw a GraphError. The direction option controls directed graph traversal and defaults to "outgoing". The result has the same graph kind as self and keeps all original edges whose endpoints are both reached. "undirected" ignores edge direction while finding reachable nodes.

Gotchas

Traversal chooses the nodes, then all original edges between reached nodes are retained. The result is not merely a traversal tree, and it allocates new node and edge identifiers.

See

Signature

declare const neighborhood: {
(nodeIndex: number, options?: NeighborhoodConfig): <N, E, T extends Kind = "directed">(self: Graph<N, E, T>) => Graph<N, E, T>;
<N, E, T extends Kind = "directed">(self: Graph<N, E, T>, nodeIndex: number, options?: NeighborhoodConfig): Graph<N, E, T>;
}

Example

(Getting a local neighborhood)

import { Graph } from "effect"
const graph = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, "A-B")
Graph.addEdge(mutable, b, c, "B-C")
})
const result = Graph.neighborhood(graph, 1, { radius: 1 })
Graph.nodeCount(result) // => 2

sum

Added in v4.0.0 Source

Returns the disjoint union of two graphs.

When to use

Use when combining graphs while keeping every node distinct, even when node payloads are equal.

Details

Copies all nodes and edges from both graphs without merging equal node data. The result has the same graph kind as self. Throws a GraphError when the graph kinds do not match.

G1 + G2 = {disjoint V1 + V2, disjoint E1 + E2}

Gotchas

All node and edge identifiers are newly allocated.

See

  • compose for merging overlapping logical nodes by identity

Signature

declare const sum: {
<N, E, T extends Kind>(that: Graph<N, E, T>): (self: Graph<N, E, NoInfer<T>>) => Graph<N, E, T>;
<N, E, T extends Kind>(self: Graph<N, E, T>, that: Graph<N, E, NoInfer<T>>): Graph<N, E, T>;
}

Returns edges present in exactly one of two graphs.

When to use

Use when comparing graphs and retaining relationships unique to either one.

Details

Keeps nodes from both graphs. Overlapping nodes use data from that. The result has the same graph kind as self. Throws a GraphError when the graph kinds do not match. nodeIdentity and edgeIdentity default to the complete node and edge data. Edge identity also includes the endpoint identities.

G1 Δ G2 = {V1 ∪ V2, (E1 ∪ E2) \ (E1 ∩ E2)}

Gotchas

Edges with different projected identities are distinct. Nodes with equal identities in one input graph are coalesced. The last node supplies the data, and redirected edges can collapse or become self-loops. Parallel edges with equal identities are coalesced before the graphs are compared. The result allocates new node and edge identifiers.

See

  • difference for removing only the edges found in another graph

Signature

declare const symmetricDifference: {
<N, E, T extends Kind = "directed", NI = N, EI = E>(that: Graph<N, E, T>, options?: IdentityOptions<N, E, NI, EI>): (self: Graph<N, E, NoInfer<T>>) => Graph<N, E, T>;
<N, E, T extends Kind = "directed", NI = N, EI = E>(self: Graph<N, E, T>, that: Graph<N, E, NoInfer<T>>, options?: IdentityOptions<N, E, NI, EI>): Graph<N, E, T>;
}

Example

(Finding differing edges)

import { Graph } from "effect"
const left = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, "A-B")
Graph.addEdge(mutable, b, c, "B-C")
})
const right = Graph.directed<string, string>((mutable) => {
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
const d = Graph.addNode(mutable, "D")
Graph.addEdge(mutable, b, c, "B-C")
Graph.addEdge(mutable, c, d, "C-D")
})
const result = Graph.symmetricDifference(left, right)
Graph.nodeCount(result) // => 4
Graph.edgeCount(result) // => 2

Transforming

reverse

Added in v3.18.0 Source

Swaps source and target nodes for every edge in a mutable graph.

When to use

Use when reversing every relationship in a directed graph, such as creating a dependency transpose.

Details

Edge identifiers and payloads are preserved.

Gotchas

This operation is a no-op for undirected graphs.

Signature

declare function reverse<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>): void

Example

(Reversing edge directions)

import { Graph, Option } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, 1) // A -> B
Graph.addEdge(mutable, b, c, 2) // B -> C
Graph.reverse(mutable) // Now B -> A, C -> B
})
Option.map(Graph.getEdge(graph, 0), (edge) => edge.source) // => Option.some(1)

updateEdge

Added in v3.18.0 Source

Updates a single edge's data by applying a transformation function.

When to use

Use when replacing one edge payload while preserving its identifier and endpoints.

Gotchas

A missing edge index is ignored. The transformation may query the graph, but cannot mutate or finalize the same graph while it runs.

Signature

declare function updateEdge<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>, edgeIndex: number, f: (data: E) => E): void

Example

(Updating edge data)

import { Graph, Option } from "effect"
const result = Graph.mutate(Graph.directed<string, number>(), (mutable) => {
const nodeA = Graph.addNode(mutable, "Node A")
const nodeB = Graph.addNode(mutable, "Node B")
const edgeIndex = Graph.addEdge(mutable, nodeA, nodeB, 10)
Graph.updateEdge(mutable, edgeIndex, (data) => data * 2)
})
Option.map(Graph.getEdge(result, 0), (edge) => edge.data) // => Option.some(20)

updateNode

Added in v3.18.0 Source

Updates a single node's data by applying a transformation function.

When to use

Use when replacing one node payload while preserving its identifier and incident edges.

Gotchas

A missing node index is ignored. The transformation may query the graph, but cannot mutate or finalize the same graph while it runs.

Signature

declare function updateNode<N, E, T extends Kind = "directed">(mutable: MutableGraph<N, E, T>, index: number, f: (data: N) => N): void

Example

(Updating node data)

import { Graph, Option } from "effect"
const graph = Graph.directed<string, number>((mutable) => {
Graph.addNode(mutable, "Node A")
Graph.addNode(mutable, "Node B")
Graph.updateNode(mutable, 0, (data) => data.toUpperCase())
})
Graph.getNode(graph, 0) // => Option.some("NODE A")