npm package: @neuralsea/tracegraph
Description: Generic trace graph builder with pluggable semantics profiles and enrichers.
Latest version: 0.1.0

# 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`).

- **Dependency Inversion**:
  - The core does not import React Flow or elkjs.
  - Visualisation adapters are plain data shapers; you inject ELK.

## Install

```bash
npm i @neuralsea/tracegraph
```

## Quick start (build a graph)

```ts
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.

```ts
new TotalOrderProfile({ edgeLabel: "next", annotateIndexAs: "globalIndex" })
```

### Partial order

Adds:
- per-actor `po` edges
- `hb:comm` edges for Send(corrId) → Receive(corrId)
- optional vector clocks stored under `vc`

```ts
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`)

```ts
new ProvenanceProfile({
  addDerivedFromEdges: true,
  getInputs: (e) => (e.payload?.inputs as string[]) ?? [],
  getOutputs: (e) => (e.payload?.outputs as string[]) ?? []
})
```

## Enrichers

- `TopologicalSortEnricher` annotates nodes with `topoIndex`
- `TransitiveReductionEnricher` removes redundant edges (on a DAG)
- `CriticalPathEnricher` annotates critical nodes and stores `criticalPath` in `graph.meta`
- `VerificationEnricher` runs monitors and annotates nodes with `violations`

## 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

```ts
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):

```ts
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

```bash
npm install
npm run demo:node
npm run demo:node:json
npm run demo:node:dot
```

### React Flow + ELK demo

```bash
cd demos/reactflow
npm install
npm run dev
```

### VS Code extension guidance

See: `demos/vscode-extension/README.md`.

The recommended approach is:

1. Build the trace graph in the **extension host** (Node).
2. Send graph JSON to a **webview**.
3. Render with React Flow in the webview.
4. 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.kind` as 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.id` via a custom `IdFactory`.