Package Exports
- @eeegl/tsgraph
- @eeegl/tsgraph/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@eeegl/tsgraph) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@eeegl/tsgraph
A TypeScript library for immutable directed graphs with functional operations.
Installation
npm install @eeegl/tsgraphQuick Start
import { createGraphV1, newNode, newEdge } from "@eeegl/tsgraph";
const graph = createGraphV1<string, string>();
const nodeA = newNode("A");
const nodeB = newNode("B");
const g = graph
.setNode(nodeA)
.setNode(nodeB)
.setEdge(newEdge({ fromId: nodeA.id, toId: nodeB.id, value: "connects" }));
console.log(g.nodeCount()); // 2
console.log(g.edgeCount()); // 1Features
- Fully immutable operations
- Type-safe with TypeScript generics
- Functional programming style (map, filter, reduce)
- Error handling with Result types
- JSON serialization/deserialization
API Reference
Creating a Graph
createGraphV1<N, E>()
Creates a new empty directed graph.
Type Parameters:
N- Type of node valuesE- Type of edge values (defaults tostring)
Returns: DiGraph<N, E>
Example:
const graph = createGraphV1<number, string>();Graph Properties
id(): string
Returns the unique identifier of the graph.
created(): IsoDatetimeUtcExtendedMs
Returns the creation timestamp of the graph in ISO format.
error(): Error | undefined
Returns the error state of the graph, or undefined if no error exists.
hasNodes(): boolean
Returns true if the graph contains any nodes.
hasEdges(): boolean
Returns true if the graph contains any edges.
nodeCount(predicate?: (node: Node<N>) => boolean): number
Returns the count of nodes. If predicate is provided, counts only matching nodes.
Example:
const total = graph.nodeCount();
const filtered = graph.nodeCount(node => node.value > 10);edgeCount(predicate?: (edge: Edge<E>) => boolean): number
Returns the count of edges. If predicate is provided, counts only matching edges.
Creating Nodes and Edges
newNode<N>(value: N): Node<N>
Creates a new node with the given value. The node has a unique ID and timestamp. This is a standalone function exported from the package.
Example:
import { newNode } from "@eeegl/tsgraph";
const node = newNode({ name: "Alice", age: 30 });newEdge<E>(params: { fromId: string; toId: string; value: E }): Edge<E>
Creates a new edge connecting two nodes. This is a standalone function exported from the package.
Example:
import { newEdge } from "@eeegl/tsgraph";
const edge = newEdge({
fromId: nodeA.id,
toId: nodeB.id,
value: "follows"
});Modifying the Graph
setNode(node: Node<N>): DiGraph<N, E>
Adds or updates a node in the graph. Returns a new graph instance.
Example:
import { newNode } from "@eeegl/tsgraph";
const node = newNode("value");
const newGraph = graph.setNode(node);setEdge(edge: Edge<E>): DiGraph<N, E>
Adds an edge to the graph. Both nodes must exist. Updates the edge lists of connected nodes. Returns a new graph instance or a graph with an error if nodes don't exist.
Example:
import { newEdge } from "@eeegl/tsgraph";
const edge = newEdge({ fromId: n1.id, toId: n2.id, value: "edge" });
const newGraph = graph.setEdge(edge);Querying the Graph
getNode(id: string): Node<N> | undefined
Retrieves a node by its ID.
Example:
const node = graph.getNode("node-id");
if (node) {
console.log(node.value);
}getEdge(id: string): Edge<E> | undefined
Retrieves an edge by its ID.
nodes(predicate?: (node: Node<N>) => boolean): Node<N>[]
Returns all nodes, optionally filtered by predicate.
Example:
const allNodes = graph.nodes();
const filtered = graph.nodes(node => node.value > 5);edges(predicate?: (edge: Edge<E>) => boolean): Edge<E>[]
Returns all edges, optionally filtered by predicate.
nodeValues(predicate?: (value: N) => boolean): N[]
Returns all node values, optionally filtered by predicate.
Example:
const values = graph.nodeValues();
const positives = graph.nodeValues(v => v > 0);edgeValues(predicate?: (value: E) => boolean): E[]
Returns all edge values, optionally filtered by predicate.
Filtering
filterNodes(predicate: (node: Node<N>) => boolean): DiGraph<N, E>
Returns a new graph containing only nodes that match the predicate.
Example:
const filtered = graph.filterNodes(node => node.value.active);filterEdges(predicate: (edge: Edge<E>) => boolean): DiGraph<N, E>
Returns a new graph containing only edges that match the predicate.
filterNodeValues(predicate: (value: N) => boolean): DiGraph<N, E>
Filters nodes by their values.
Example:
const filtered = graph.filterNodeValues(v => v > 10);filterEdgeValues(predicate: (value: E) => boolean): DiGraph<N, E>
Filters edges by their values.
Mapping
mapNodes<T>(fn: (node: Node<N>) => Node<T>): DiGraph<T, E>
Transforms all nodes and returns a new graph with different node type.
Example:
const transformed = graph.mapNodes(node => ({
...node,
value: node.value * 2
}));mapEdges<T>(fn: (edge: Edge<E>) => Edge<T>): DiGraph<N, T>
Transforms all edges and returns a new graph with different edge type.
mapNodeValues<T>(fn: (value: N) => T): DiGraph<T, E>
Transforms node values.
Example:
const stringGraph = graph.mapNodeValues(n => n.toString());mapEdgeValues<T>(fn: (value: E) => T): DiGraph<N, T>
Transforms edge values.
Reducing
reduceNodes<T>(fn: (acc: T, current: Node<N>, index: number) => T, start: T): T
Reduces all nodes to a single value.
Example:
const sum = graph.reduceNodes((acc, node) => acc + node.value, 0);reduceEdges<T>(fn: (acc: T, current: Edge<E>, index: number) => T, start: T): T
Reduces all edges to a single value.
reduceNodeValues<T>(fn: (acc: T, current: N, index: number) => T, start: T): T
Reduces node values.
Example:
const total = graph.reduceNodeValues((sum, val) => sum + val, 0);reduceEdgeValues<T>(fn: (acc: T, current: E, index: number) => T, start: T): T
Reduces edge values.
Iteration
forEachNode(fn: (node: Node<N>) => void): DiGraph<N, E>
Executes a function for each node. Returns the same graph for chaining.
Example:
graph.forEachNode(node => console.log(node.value));forEachEdge(fn: (edge: Edge<E>) => void): DiGraph<N, E>
Executes a function for each edge.
forEachNodeValue(fn: (value: N) => void): DiGraph<N, E>
Executes a function for each node value.
forEachEdgeValue(fn: (value: E) => void): DiGraph<N, E>
Executes a function for each edge value.
Error Handling
match<T, ErrT>(successFn: (graph: DiGraph<N, E>) => T, errorFn: (e: Error) => ErrT): Result<T, ErrT>
Pattern matching for error handling. Executes successFn if no error exists, otherwise executes errorFn.
Example:
const result = graph.match(
g => g.nodeCount(),
err => {
console.error(err.message);
return -1;
}
);
if (result.ok) {
console.log("Node count:", result.value);
} else {
console.log("Error result:", result.error);
}Serialization
toJson(params?: { pretty: boolean }): Result<string, Error>
Serializes the graph to JSON string. Returns a Result type.
Example:
const result = graph.toJson({ pretty: true });
if (result.ok) {
console.log(result.value);
}fromJson<N, E>(json: string): Result<DiGraph<N, E>, Error>
Deserializes a graph from JSON string. Returns a Result type.
Example:
const result = graph.fromJson(jsonString);
if (result.ok) {
const restoredGraph = result.value;
}Types
Node<T>
type Node<T> = {
id: string;
type: "node";
created: IsoDatetimeUtcExtendedMs;
value: T;
edgeIdsOut: string[]; // IDs of outgoing edges
edgeIdsIn: string[]; // IDs of incoming edges
};Edge<T>
type Edge<T> = {
id: string;
type: "edge";
created: IsoDatetimeUtcExtendedMs;
fromId: string; // Source node ID
toId: string; // Target node ID
value: T;
};Result<T, ErrT>
type Result<T, ErrT> =
| { ok: true; value: T }
| { ok: false; error: ErrT };Examples
Building a Graph
import { createGraphV1, newNode, newEdge } from "@eeegl/tsgraph";
const graph = createGraphV1<string, number>();
const nodes = ["A", "B", "C"].map(v => newNode(v));
let g = graph;
for (const node of nodes) {
g = g.setNode(node);
}
const edge1 = newEdge({ fromId: nodes[0].id, toId: nodes[1].id, value: 1 });
const edge2 = newEdge({ fromId: nodes[1].id, toId: nodes[2].id, value: 2 });
g = g.setEdge(edge1).setEdge(edge2);
console.log(g.nodeCount()); // 3
console.log(g.edgeCount()); // 2Filtering and Mapping
import { createGraphV1, newNode } from "@eeegl/tsgraph";
const graph = createGraphV1<number, string>();
const g = graph
.setNode(newNode(1))
.setNode(newNode(2))
.setNode(newNode(3))
.setNode(newNode(4));
const result = g
.filterNodeValues(v => v % 2 === 0)
.mapNodeValues(v => v * 10)
.reduceNodeValues((sum, v) => sum + v, 0);
console.log(result); // 60 (20 + 40)Error Handling
import { createGraphV1, newNode, newEdge } from "@eeegl/tsgraph";
const graph = createGraphV1<string, string>();
const node = newNode("A");
const g = graph.setNode(node);
const badEdge = newEdge({
fromId: node.id,
toId: "nonexistent",
value: "error"
});
const g2 = g.setEdge(badEdge);
const result = g2.match(
graph => `Success: ${graph.nodeCount()} nodes`,
err => `Failed: ${err.message}`
);
if (!result.ok) {
console.log(result.error); // "Failed: undefined edge: fromId=...; toId=nonexistent"
}JSON Round-Trip
import { createGraphV1, newNode } from "@eeegl/tsgraph";
const graph = createGraphV1<string, string>();
const original = graph
.setNode(newNode("A"))
.setNode(newNode("B"));
const jsonResult = original.toJson({ pretty: true });
if (jsonResult.ok) {
const restored = original.fromJson(jsonResult.value);
if (restored.ok) {
console.log(restored.value.nodeCount()); // 2
console.log(restored.value.id() === original.id()); // true
}
}Development
npm install
npm test
npm run test:watch