JSPM

@eeegl/tsgraph

1.0.1
    • ESM via JSPM
    • ES Module Entrypoint
    • Export Map
    • Keywords
    • License
    • Repository URL
    • TypeScript Types
    • README
    • Created
    • Published
    • Downloads 3
    • Score
      100M100P100Q43526F
    • License MIT

    A TypeScript library for immutable directed graphs with functional operations

    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/tsgraph

    Quick 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()); // 1

    Features

    • 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 values
    • E - Type of edge values (defaults to string)

    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()); // 2

    Filtering 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