Skip to content
@tde.io/plexis  ·  npm install @tde.io/plexis  ·  npm  ·  GitHub

Graph Introspection

domain.graph and pipeline.graph expose the static structure of your domain or pipeline as a GraphDescriptor object. Use it to inspect nodes and edges, run path queries, or feed a visualizer. This page covers reading the graph; rendering it as Mermaid or DOT is in State Graph Visualizer.

import { defineDomain, when, on, terminal } from '@tde.io/plexis';
const order = defineDomain('order', () => {
when('pending', () => {
on('submit', target('processing'));
});
when('processing', () => {
on('fulfill', target('fulfilled'));
});
when('fulfilled', terminal());
return { context: {}, initial: 'pending' };
});
const descriptor = order.graph.describe();
// {
// id: 'order',
// kind: 'domain',
// nodes: [...],
// edges: [...],
// entryNodes: [{ kind: 'domain-phase', nodeId: 'pending', ... }],
// terminalNodes: [{ kind: 'domain-phase', nodeId: 'fulfilled', ... }],
// attachments: []
// }
console.log(descriptor.nodes.map((n) => n.id));
// ['pending', 'processing', 'fulfilled']
console.log(descriptor.edges.map((e) => `${e.from.nodeId} --${e.event}--> ${e.to.nodeId}`));
// ['pending --submit--> processing', 'processing --fulfill--> fulfilled']

domain.graph.node(id) returns the GraphNode for a named phase or pipeline node.

const node = order.graph.node('processing');
// { ref: { kind: 'domain-phase', nodeId: 'processing' }, id: 'processing', terminal: false }

domain.graph.inbound(id) and domain.graph.outbound(id) return the edges that arrive at or depart from a node:

const inbound = order.graph.inbound('processing');
// edges where to.nodeId === 'processing' → [the 'submit' event edge]
const outbound = order.graph.outbound('processing');
// edges where from.nodeId === 'processing' → [the 'fulfill' event edge]

Path queries traverse the graph to find routes between two nodes.

// All paths from 'pending' to 'fulfilled'
const paths = order.graph.pathsFrom('pending');
paths.forEach((p) => {
console.log(p.nodes.map((r) => r.nodeId).join(''));
});
// 'pending → processing → fulfilled'
// Nodes reachable from 'pending'
const reachable = order.graph.reachableFrom('pending');
console.log(reachable.map((n) => n.id));
// ['pending', 'processing', 'fulfilled']

pathsTo(id) gives paths that end at the given node; pathsFrom(id) gives paths that start there. Both accept a PathQueryOptions object to control maxDepth, cycle handling, and direction.

Pipelines have the same graph API under pipeline.graph:

import { definePipeline, node, action, fork, target, terminal } from '@tde.io/plexis';
const process = definePipeline('process', () => {
node('validate', () => {
fork('accept', target('accept'), (ctx) => ctx.valid);
fork('reject', target('reject'), (ctx) => !ctx.valid);
});
node('accept', terminal());
node('reject', terminal());
return { initial: 'validate' };
});
const g = process.graph.describe();
console.log(g.terminalNodes.map((n) => n.nodeId));
// ['accept', 'reject']

domain.graph also exposes observedPathsTo(id) and observedPathsFrom(id), which return only paths the domain has actually traversed at runtime (based on the tracer). These are useful for coverage analysis and debugging.

await order.follow('submit');
const observed = order.graph.observedPathsFrom('pending');
// returns the actual path taken so far

For the full DomainGraph and PipelineGraph types, see the Domain Instance reference and Pipeline Instance reference. To render a GraphDescriptor as a diagram, see State Graph Visualizer.