@neuralsea/tracegraph
v0.1.0
Published
Generic trace graph builder with pluggable semantics profiles and enrichers.
Readme
TraceGraph Core (TypeScript 5+, Node >= 18)
A small, generic trace graph toolkit:
- TraceEvent → TraceGraph via a pluggable semantics profile (Strategy pattern)
- Optional enrichers (Chain of Responsibility) for derived structure:
- topological order
- transitive reduction
- critical path
- verification monitors (safety/liveness-ish)
Includes adapters for React Flow and ELK (elkjs) so you can render and lay out graphs.
Why this design
Single Responsibility:
- Builder orchestrates ingestion only.
- Semantics profile decides meaning (nodes/edges).
- Enrichers derive extra structure (and can be turned on/off).
Open/Closed:
- Add new profiles/enrichers without changing the builder.
Liskov + Interface Segregation:
- Small interfaces (
ISemanticsProfile,IEnricher,IVerificationMonitor).
- Small interfaces (
Dependency Inversion:
- The core does not import React Flow or elkjs.
- Visualisation adapters are plain data shapers; you inject ELK.
Install
npm i @neuralsea/tracegraphQuick start (build a graph)
import {
TraceGraphBuilder,
CompositeSemanticsProfile,
PartialOrderProfile,
ProvenanceProfile,
EnricherPipeline,
TopologicalSortEnricher,
VerificationEnricher,
AcyclicOrderMonitor,
type TraceEvent
} from "@neuralsea/tracegraph";
type MyPayload = {
inputs?: string[];
outputs?: string[];
durationMs?: number;
};
type MyEvent = TraceEvent<MyPayload>;
const profile = new CompositeSemanticsProfile([
new PartialOrderProfile({ captureVectorClocks: true }),
new ProvenanceProfile({ addDerivedFromEdges: true })
]);
const enrichers = new EnricherPipeline([
new TopologicalSortEnricher(),
new VerificationEnricher([ new AcyclicOrderMonitor() ])
]);
const builder = new TraceGraphBuilder<MyEvent>({ profile, enrichers });
builder.ingestAll(myEvents);
const graph = builder.build();
console.log(graph.toJSON());Semantics profiles
Total order
Adds next edges in ingestion order.
new TotalOrderProfile({ edgeLabel: "next", annotateIndexAs: "globalIndex" })Partial order
Adds:
- per-actor
poedges hb:commedges for Send(corrId) → Receive(corrId)- optional vector clocks stored under
vc
new PartialOrderProfile({
captureVectorClocks: true,
isSend: (e) => e.kind === "Send",
isReceive: (e) => e.kind === "Receive"
})Provenance
Creates value/entity nodes and adds PROV-style edges:
- Activity → Entity (
prov:used,prov:generated) - optional Value → Value (
prov:derivedFrom)
new ProvenanceProfile({
addDerivedFromEdges: true,
getInputs: (e) => (e.payload?.inputs as string[]) ?? [],
getOutputs: (e) => (e.payload?.outputs as string[]) ?? []
})Enrichers
TopologicalSortEnricherannotates nodes withtopoIndexTransitiveReductionEnricherremoves redundant edges (on a DAG)CriticalPathEnricherannotates critical nodes and storescriticalPathingraph.metaVerificationEnricherruns monitors and annotates nodes withviolations
Visualisation with React Flow + elkjs
Yes: React Flow and elkjs are a good pairing for trace graphs:
- React Flow provides interaction, custom nodes/edges, animations.
- ELK handles readable layouts, especially DAG-like traces.
Convert graph → React Flow elements
import { toReactFlowElements } from "@neuralsea/tracegraph";
const rf = toReactFlowElements(graph, {
nodeFilter: (n) => (n.labels ?? []).includes("Event"),
edgeFilter: (e) => e.label === "po" || e.label.startsWith("hb"),
edgeAnimated: (e) => e.label.startsWith("hb")
});Apply ELK layout
Inject an elk instance (Node or browser):
import ELK from "elkjs/lib/elk.bundled.js";
import { applyElkLayout } from "@neuralsea/tracegraph";
const elk = new ELK();
const laidOut = await applyElkLayout(elk, rf, {
layoutOptions: { "elk.algorithm": "layered", "elk.direction": "RIGHT" }
});Animate edges AND arrowheads
- Edge path animation: React Flow supports
edge.animated = true. - Arrowhead animation: usually requires a custom edge type (because markers don’t inherit the path animation reliably).
This repo includes a demo custom edge (AnimatedArrowEdge) that animates both:
- edge path via a dashed stroke animation
- arrowhead via a pulse animation
See: demos/reactflow/src/AnimatedArrowEdge.tsx.
Demos
Node CLI
npm install
npm run demo:node
npm run demo:node:json
npm run demo:node:dotReact Flow + ELK demo
cd demos/reactflow
npm install
npm run devVS Code extension guidance
See: demos/vscode-extension/README.md.
The recommended approach is:
- Build the trace graph in the extension host (Node).
- Send graph JSON to a webview.
- Render with React Flow in the webview.
- Layout with ELK either:
- in the extension host (best for big graphs), or
- in the webview (fine for small/medium graphs, ideally in a Worker).
Notes
- The core library intentionally keeps
TraceEvent.kindas a string: map your grammar/runtime to the canonical set you want. - If you need parallel edges between the same nodes/label, supply unique
edge.idvia a customIdFactory.
