---
name: Graph Explorer
type: MOL
section: graphs
why: A worker draws the far field into one canvas and decides which few hundred nodes get DOM, so a million-node graph stays a bounded page; every node, label and panel you read is plain, themed, keyboard-reachable HTML.
when: Exploring large connected data - service maps, data lineage, knowledge graphs, dependency graphs - by semantic zoom, search and drill-down. For a schematic you draw by hand, diagram; for a small text-defined diagram, mermaid; for a tree of records, data-tree.
where: dist/components/graphs.css + dist/components/graphs.js (the extra bundle - load it after all.css / all.js; per component: dist/components/graph-explorer/graph-explorer.css + graph-explorer.js)
supportedStates: default, selected, focused
---

# Pattern: Graph Explorer

## Native basis

A `<div class="graph-explorer">` becomes one focusable `role="application"`
viewport. A Web Worker started from the component's own source owns the graph:
the hierarchy of clusters, the layouts, the cycles and every dot and bulk edge,
drawn into an `OffscreenCanvas` and handed over as an `ImageBitmap`. The main
thread keeps a bounded pool of DOM slots - chips and cards for the nodes worth
reading, labels along the edges - and morphs them only when their markup
changes. The rest of an explorer's interface (breadcrumb, search, outline,
details, relations, a layer switch) are **outlets**: elements the page places
where it likes - typically Panels of a Border Layout - that the runtime fills.

## Native Web APIs

- [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) + [`Blob` URLs](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static) - the graph lives off the main thread; a classic Blob worker also starts inside an opaque-origin sandbox
- [`OffscreenCanvas`](https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas) + [`ImageBitmapRenderingContext`](https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmapRenderingContext) - the worker draws the far field; the bitmap moves to the page without a copy
- [Transferable objects](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects) - bitmaps and tier arrays cross threads for free
- [`requestAnimationFrame()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame) - one clock for the bitmap and the DOM writes
- [Pointer Events](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events) + [`setPointerCapture()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture) - pan, pinch, press and hold
- [`wheel`](https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event) - zoom anchored at the cursor
- [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API) - the keyboard map
- [`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) + [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) - the viewport refits; attributes written from outside apply at once
- [`translate`](https://developer.mozilla.org/en-US/docs/Web/CSS/translate) + [`contain`](https://developer.mozilla.org/en-US/docs/Web/CSS/contain) - slots move on the compositor, the viewport's layout stays its own
- [CSS masks](https://developer.mozilla.org/en-US/docs/Web/CSS/mask) - the icons: one empty element each, painted in the current color
- [Relative colors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_colors/Relative_colors) + [`color-mix()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix) - type colors and tints from the theme's chart tokens
- [`@starting-style`](https://developer.mozilla.org/en-US/docs/Web/CSS/@starting-style) - nodes fade in as they get DOM
- [WAI-ARIA combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/) - the search outlet; the outline is a [tree](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/)
- [`prefers-reduced-motion`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion) / [`prefers-contrast`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-contrast) / [`forced-colors`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/forced-colors) - jumps instead of flights, stronger strokes, system colors (also on the canvas)

---

## Structure

```html
<div class="graph-explorer" id="kg" data-nodes="100000" data-seed="7" data-frame aria-label="Service graph"></div>
```

- `data-nodes` - the size of the synthetic graph, 2,000 to 4,000,000 (default
  `1000000`); `data-seed` - its seed (default `7`): the same seed builds the same
  graph, so ids are stable across loads.
- Ids name nodes: `c<number>` a cluster (`c0` is the top), `n<number>` an entity.
- `data-selected="c4 c9"` - the selected nodes; `data-selected-edge="c4>c9"` -
  the selected link (`<source id>><target id>`, two nodes of the view; nodes and
  a link are never selected together); `data-focus="c12"` - the cluster the view
  is in; `data-layers="nodes relations health"` - the layers shown (absent: the
  base layers `nodes relations`; empty: none); `data-compact` - every node a
  one-line chip; `data-expanded` - the selection's full page in the details
  outlet. These are the state.
- `data-hud` - a performance overlay (nodes, DOM, canvas, worker timing).
- `data-frame` - a border and radius when it stands alone (in a Panel the panel
  is the frame).
- Its height: `100%` of its container, at least `--graph-explorer-min-height`
  (default `20rem`); a Panel body that holds it gives it all its room.

### Outlets and outputs

Anything the page binds with `data-graph-explorer-for="<id>"` - the element
itself or one inside it:

| Attribute | The runtime puts in it |
| --- | --- |
| `data-graph-explorer-outlet="breadcrumb"` | a Breadcrumb of the path to the view; a crumb moves up |
| `data-graph-explorer-outlet="search"` | a search combobox over every node (the scan runs in the worker); a result flies there |
| `data-graph-explorer-outlet="outline"` | a filterable tree of the view's children (the biggest first) |
| `data-graph-explorer-outlet="details"` | the detail renderer's view of the first selected node or of the selected link; expanded, its full page |
| `data-graph-explorer-outlet="relations"` | a Table of its relations, each link drawn by the edge label renderers (a click on one inspects it); for a link, its single links |
| `data-graph-explorer-outlet="layers"` | the layer switch: a Toggle Group of the layers (Nodes, Relations, Labels, Health, the plugins'), each with its colour as an Indicator dot, and Compact after a Separator |
| `data-graph-explorer-outlet="updates"` | Updates: every change of the shown layers, newest first, one compact row each in a Virtual List (a plain list of the newest 200 without `df$.shadcn.virtualList`); a row selects its node or link |
| `data-graph-explorer-output="details-title"` | the details title (the authored text while nothing is selected) |
| `data-graph-explorer-output="relations-title"` | the selected node's name and its relation count ("Identity · 5"), for a link the names of both ends and its count (the authored text while nothing is selected); the label beside it is the page's |
| `data-graph-explorer-output="view"` | the view's name |
| `data-graph-explorer-output="status"` | the announcements (the explorer's own live region when there is none) |
| `data-graph-explorer-action="up"` / `fit` / `live` / `help` / `collapse` | move up, fit, Live mode on / off (`aria-pressed`), the keyboard map, back from an expanded page |

Inside the explorer, renderers write `data-graph-explorer-action="enter"` /
`bring` / `expand` / `collapse` / `details` (acting on their node or the
selected link), `data-graph-explorer-goto="<id>"` (fly there - on an expanded
page: that node's page, so a page drills down into the next) and
`data-graph-explorer-edge="<source id>><target id>"` (select that link).

A click on a link - its label or its line - selects it: the rest of the graph
steps back and the details show its traffic both ways, the relations it carries,
the circuit breakers it bundles, who sends and who receives most; `E` or a
double click expands it, `Esc` lets it go.

Expand maximizes the **Panel** that holds the details outlet (and restores it
on the way back); a Panel holding the relations outlet renders it when it
unfolds. The whole application - toolbar north, outline west, the graph in the
center, details east, relations south - is a Border Layout of Panels on the
page:

```html
<div class="border-layout" data-frame="none" style="block-size: 40rem">
  <header class="border-layout-north flex items-center gap-3 px-3 py-2">
    <div class="flex-1 min-w-0" data-graph-explorer-for="kg" data-graph-explorer-outlet="breadcrumb"></div>
    <div class="w-full max-w-80" data-graph-explorer-for="kg" data-graph-explorer-outlet="search"></div>
    <button class="btn" data-variant="outline" data-size="sm" type="button" data-graph-explorer-for="kg" data-graph-explorer-action="fit">Fit</button>
  </header>
  <div class="resizer border-layout-west" data-min="160" data-max="420">
    <section class="card panel" style="width: 15rem" aria-labelledby="kg-outline-t">
      <header class="panel-header"><h3 class="panel-title" id="kg-outline-t">Outline</h3></header>
      <div class="panel-body" data-flush data-graph-explorer-for="kg" data-graph-explorer-outlet="outline"></div>
    </section>
  </div>
  <section class="card panel border-layout-center" aria-labelledby="kg-graph-t">
    <header class="panel-header"><h3 class="panel-title" id="kg-graph-t">Graph</h3>
      <div class="panel-tools" data-graph-explorer-for="kg" data-graph-explorer-outlet="layers"></div></header>
    <div class="panel-body" data-flush><div class="graph-explorer" id="kg" data-nodes="1000000" aria-label="Service graph"></div></div>
  </section>
  <div class="resizer border-layout-east" data-min="240" data-max="640">
    <section class="card panel" style="width: 23rem" aria-labelledby="kg-details-t">
      <header class="panel-header"><h3 class="panel-title" id="kg-details-t" data-graph-explorer-for="kg" data-graph-explorer-output="details-title">Details</h3>
        <div class="panel-tools"><label class="swap panel-maximize">...</label></div></header>
      <div class="panel-body" data-graph-explorer-for="kg" data-graph-explorer-outlet="details"></div>
    </section>
  </div>
</div>
```

### Plugins

`df$.shadcn.graphExplorer.register(scope, plugin)` - `scope` is `'global'` or one
explorer (its plugins win). A renderer is a pure function of its input that
returns `ctx.html` markup (every interpolation escaped; `on*` attributes, URLs
that run code and active elements are stripped):

- **node** - `match(node)` returns a score or `false`, `sizes: { chip, card }`
  (or a function of the node), `render(node, ctx)` per tier (`ctx.tier` is
  `chip` or `card`). Without `layer` it draws the node itself (the nodes
  layer) and puts `ctx.parts` - the other layers' parts - where they belong (the
  runtime appends them when it leaves them out). With `layer` it draws that
  layer's **part** of a node: a chip part adds its width, a card part its height;
  `layerLabel`, `layerIcon` and `layerColor` name a layer of its own in the
  switch.
- **edge** - `layer` (default: `relations` for its rules, `labels` for its
  label); `rules`: plain data the worker evaluates for every link it draws
  (`{ when: [{ field: 'breaker.state', equals: 'open' }], style: { tone: 'danger', width: 2, gap: true } }`);
  `label`: DOM labels along links in three tiers (`icon`, `label`, `full`)
  picked by the link's length on screen, budgeted, never overlapping;
  `detail`: `{ match(item), title(item), render(item, ctx) }` - the details of a
  selected link (`item.source`, `item.target`, `item.edge`; its numbers from
  `ctx.fetchEdge(item.id)`).
- **detail** - `render(node, ctx)` for the details outlet; `ctx.expanded` asks
  for the node's full page. Anything async goes through `ctx.request(key, load)`,
  which renders the node again when the value arrives. `page(node)` returning
  false says a node has no page; `ctx.expandable` is true only where a page can
  show (a details outlet, and a renderer that offers one) - an Expand button
  belongs only there (an edge `detail` takes `page(item)` the same way).
- **update** - `update: { match(update), render(update, ctx) }`: one row of the
  Updates outlet (`ctx.html`, `ctx.icon`, `ctx.color(layer)`, `ctx.time(ms)`);
  the explorer wraps it in the button that selects the update's node or link.
  Keep it to one line - the rows are 2rem high.

The details are markup the runtime morphs on every change. A library component
with a script inside them - a Chart, a Geo Map, an Avatar - goes in
`ctx.island(key, markup)`: an island keeps its element, and what its script
made, while its key and its markup stay the same. `ctx.island(key, markup, mount)`
also runs `mount(el)` once when the island's element is made - bind a Data Grid's
records or a chart's data there - and the function it returns when the island
goes. `ctx.fetchRows(id)` answers every row behind a node or a link, built by
the worker: the entities below a cluster (id, label, type, system, domain,
owner, out, in, rows, freshness) or a link's single links both ways (from, to,
relation, direction, the systems).

```html
<!-- a detail renderer's markup: CSS-only components inline, a Chart in an island -->
<dl class="graph-explorer-stats"><div class="graph-explorer-stat"><dt>p95</dt><dd>63 ms</dd></div></dl>
<div class="graph-explorer-island" data-graph-explorer-island="latency:c26">
  <div class="chart" data-size="sm" role="img" aria-label="p95 latency" data-chart='{"xAxis":{"type":"category","data":["0h","1h","2h"]},"yAxis":{"type":"value"},"series":[{"type":"line","data":[61,63,58]}]}'></div>
</div>
```

The built-in plugins draw clusters and entities and their parts in four layers,
relation labels, circuit breakers (one per link
between entities, health by share on links between clusters) and the details of
a node and of a link - side and expanded - from the library's Badge, Button,
Card, Statistic, Table and Data Grid and the explorer's own parts. An expanded
cluster page holds every entity below it in a Data Grid, an expanded link page
every single link both ways; beside the grid, dataview aggregations of the
grid's own source show how the rows its filters match split up (by type, owner,
the largest systems; by relation, way, the busiest senders) and follow every
filter; a name in the grid opens its page. Without `df$.shadcn.dataGrid` loaded
the pages keep sample tables. The explorer's parts (`graph-explorer-stats`,
`-section`, `-rows` / `-row`, `-bar`, `-shares`, `-trail`, `-page`,
`-kpis`, `-page-grid`, `-records`), which plugins use the same way.
The icons are `ctx.icon(name)` - a Lucide subset as CSS masks;
`registerIcon(name, shapes)` adds one.

### Layers

What the graph shows is layers of information, each a filter of its own, each
drawn in its colour - the theme's chart colours in order (chart-1 turned around
the hue circle when the theme's five are one hue, as the default theme's blues):

| Layer | Shows | Default |
| --- | --- | --- |
| `nodes` | the chips and cards with each node's type, name and Expand (a cluster card zooms in), the names beside small nodes | shown |
| `relations` | the lines between nodes and their arrows, the signposts to nodes off screen; a part with a node's links and the small cycle it is in | shown |
| `labels` | the link labels (counts, relation names); a part with what a node is and how big | hidden |
| `health` | the circuit breakers: their line colours and badges; a part with a cluster's links by breaker state, an entity's freshness | hidden |
| a plugin's | its parts and its link rules and labels | hidden |

With none shown the clusters' circles and the entities' dots remain. A part is
bordered and tinted in its layer's colour (a band in a card, a pill in a chip),
an edge label in its plugin's layer's colour, a calm line in the relations
colour, a chip or a card in the nodes colour. `1`-`9` show or hide a layer.

### Live mode and updates

An update is one change: a node (`target` its id) or a link (`target`
`'<from>><to>'`, `view` the cluster whose children it joins), with the layer
it belongs to, a `type` word, a line of `text`, the target's `label`, a
`tone`. For eight seconds a changed node carries a glowing dot at its top right
and a changed link lights up in its layer's colour and fades - while that layer
is shown (a link of the health layer lights up without the relations layer).
Every update joins the `updates` outlet, newest first (the last 10,000); the
list shows the shown layers' updates only, and a reader scrolled down keeps the
rows they read.

```html
<button class="btn" data-variant="outline" data-size="sm" aria-pressed="false"
        data-graph-explorer-for="kg" data-graph-explorer-action="live">Live mode</button>
<section class="card panel" aria-labelledby="t-updates">
  <header class="panel-header"><h3 class="panel-title" id="t-updates">Updates</h3></header>
  <div class="panel-body" data-flush data-graph-explorer-for="kg" data-graph-explorer-outlet="updates"></div>
</section>
```

The `live` action (or `simulate(target, on)`) starts Live mode, the
explorer's simulation: breakers trip, probe and close (health), nodes deploy
(nodes), a link's traffic moves (relations), counts change (labels). An app
adds its own changes with `df$.shadcn.graphExplorer.update(target, update)`;
each fires `graph-explorer-update`.

## Variants

| Attribute | Values | Behavior |
| --- | --- | --- |
| `data-layers` | the layers' names, space separated (absent: `nodes relations`; empty: none) | which layers show; the layers outlet switches, `1`-`9` too |
| `data-compact` | *(present)* | every node as its one-line chip, never a card |
| `data-frame` | *(present)* | a border and a radius around the viewport |
| `data-hud` | *(present)* | the performance overlay |

## Sizes

| Attribute | Values | Behavior |
| --- | --- | --- |
| `data-size` on `.graph-explorer-icon` | `xs` (0.75rem), `sm` (0.875rem), `lg` (1.5rem), `xl` (2rem) | an icon's size; without it 1rem (`ctx.icon(name, { size })`) |

## ARIA

| Attribute | Where | Why |
| --- | --- | --- |
| `role="application"` + `aria-roledescription="graph"` | the `.graph-explorer` (generated) | one tab stop that takes the keys: arrows move the focus to the nearest node in that direction, Enter enters, Space selects, `?` opens the map |
| `aria-describedby` | the `.graph-explorer` (generated) | the keyboard map |
| `aria-label` | the `.graph-explorer` | names the graph |
| `aria-label` | a details row's link button (generated) | "Inspect the link to *name*": the keyboard way to a link |
| `aria-busy="true"` | the `.graph-explorer`, while the worker builds the graph | not final yet |
| `role="combobox"` + `aria-activedescendant` | the search outlet's input (generated) | the APG combobox: arrows walk the results, Enter flies to one |
| `role="tree"` / `treeitem` | the outline outlet (generated) | the path and the view's children; arrows walk, Enter acts |
| live region | the status output, else the explorer's own `<output>` | the view, the keyboard focus, the neighbour ring (Jump to a neighbour) |

## Notes

- The page never holds more than a bounded set of nodes: at most 120 cards, 300
  chips and 200 edge labels, however large the graph.
- The data is a synthetic service graph the worker builds from the seed -
  domains, systems (API, Worker, Store, Stream) and entities (Dataset, Endpoint,
  Document, Person, Job, Topic), linked by calls, reads and owns, with cycles and
  circuit breakers. The renderers, the outlets and the State API do not depend on
  it.
- Zooming into a cluster until it fills most of the viewport enters it; zooming
  out, or panning off its disk, moves up. Search, the breadcrumb, the outline and
  the relations fly there with a smooth zoom-pan (a jump under reduced motion).
- The names the worker draws beside small nodes are measured, and the edge
  labels keep clear of them.
- Expand shows only where a page can show: an explorer without a details outlet
  draws its cards without the Expand button, and E or `expanded: true` only
  select (the state lands unexpanded).
- The canvas, the slots, the labels and every outlet's content are the runtime's
  (`data-ce-chrome`): render() and the HTML Preview Editor see only the
  element's attributes and the outlets as authored.
- Theme changes re-read the colors for the canvas; forced colors draw it in
  system colors.

## States

| State | Meaning |
| --- | --- |
| `default` | The top level, nothing selected |
| `selected` | `config.selected` nodes - or the link `config.edge` - picked at the top level (an id deeper down flies there - the state lands in `focused`) |
| `focused` | Inside the cluster `config.focus`, with or without a selection there |

```js
const graph = document.querySelector('#kg');
await graph.api.settled();                                   // the worker built the graph
await graph.api.setState('focused', { focus: 'c3' });        // enter cluster c3
await graph.api.setState('focused', { focus: 'c3', selected: ['c41'], expanded: true }); // its child's full page
await graph.api.setState('selected', { edge: 'c3>c7' });     // the link between two domains
graph.api.setState('default', { layers: ['nodes', 'relations', 'health'] }); // back to the top, health shown
graph.api.getState(); // { name: 'default', config: { focus: null, selected: [], layers: ['nodes', 'relations', 'health'], compact: false, expanded: false, edge: null } }
```

The registry global is `df$.shadcn.graphExplorerApi` /
`df$.shadcn.graphExplorerStates`; the explorer API is `df$.shadcn.graphExplorer`.

## API

<!-- generated from the source by `bun run api-docs` - edit the JSDoc and the types in the .ts, not this section -->

### States

<code>type GraphExplorerState = 'default' | 'selected' | 'focused'</code> - `setState(name, config)` takes the config of the state it names (`GraphExplorerStateConfigs[name]`).

| State | Description |
|---|---|
| `default` | The top level, nothing selected - the focus and the selection are cleared. <b>config</b> <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>focus?</code></td><td><code>string \| null</code></td><td>the cluster the view is in (null = the top); 'focused' needs one</td></tr><tr><td><code>selected?</code></td><td><code>string \| string[]</code></td><td>the selected node ids, space separated or as a list</td></tr><tr><td><code>layers?</code></td><td><code>string \| string[]</code></td><td>the layers shown, space separated or as a list: nodes, relations, labels, health or a plugin's (none: only the clusters' circles and dots) - data-layers, absent for the base layers nodes and relations</td></tr><tr><td><code>compact?</code></td><td><code>boolean</code></td><td>every node as a one-line chip (data-compact)</td></tr><tr><td><code>expanded?</code></td><td><code>boolean</code></td><td>the details outlet expanded to the selection's page (the first selected node's, or the link's)</td></tr><tr><td><code>edge?</code></td><td><code>string \| null</code></td><td>the selected link: '&lt;source id&gt;&gt;&lt;target id&gt;' of two nodes in the view; a node selection clears it</td></tr></table> |
| `selected` | Nodes (`config.selected`) or a link (`config.edge`) picked at the top level; an id deeper down flies there and lands in focused. <b>config</b> <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>focus?</code></td><td><code>string \| null</code></td><td>the cluster the view is in (null = the top); 'focused' needs one</td></tr><tr><td><code>selected?</code></td><td><code>string \| string[]</code></td><td>the selected node ids, space separated or as a list</td></tr><tr><td><code>layers?</code></td><td><code>string \| string[]</code></td><td>the layers shown, space separated or as a list: nodes, relations, labels, health or a plugin's (none: only the clusters' circles and dots) - data-layers, absent for the base layers nodes and relations</td></tr><tr><td><code>compact?</code></td><td><code>boolean</code></td><td>every node as a one-line chip (data-compact)</td></tr><tr><td><code>expanded?</code></td><td><code>boolean</code></td><td>the details outlet expanded to the selection's page (the first selected node's, or the link's)</td></tr><tr><td><code>edge?</code></td><td><code>string \| null</code></td><td>the selected link: '&lt;source id&gt;&gt;&lt;target id&gt;' of two nodes in the view; a node selection clears it</td></tr></table> |
| `focused` | Inside the cluster `config.focus` (the view dives there), with or without a selection. <b>config</b> <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>focus?</code></td><td><code>string \| null</code></td><td>the cluster the view is in (null = the top); 'focused' needs one</td></tr><tr><td><code>selected?</code></td><td><code>string \| string[]</code></td><td>the selected node ids, space separated or as a list</td></tr><tr><td><code>layers?</code></td><td><code>string \| string[]</code></td><td>the layers shown, space separated or as a list: nodes, relations, labels, health or a plugin's (none: only the clusters' circles and dots) - data-layers, absent for the base layers nodes and relations</td></tr><tr><td><code>compact?</code></td><td><code>boolean</code></td><td>every node as a one-line chip (data-compact)</td></tr><tr><td><code>expanded?</code></td><td><code>boolean</code></td><td>the details outlet expanded to the selection's page (the first selected node's, or the link's)</td></tr><tr><td><code>edge?</code></td><td><code>string \| null</code></td><td>the selected link: '&lt;source id&gt;&gt;&lt;target id&gt;' of two nodes in the view; a node selection clears it</td></tr></table> |

### Every element

| Member | Description |
|---|---|
| <code>el.api.setState&lt;S extends GraphExplorerState&gt;(name: S, config?: GraphExplorerStateConfigs[S]): unknown</code> | Enter a state: the DOM work runs (also when it is the current state), the store records it. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>name</code></td><td><code>S</code></td><td>a declared state (an unknown name throws)</td></tr><tr><td><code>config?</code></td><td><code>GraphExplorerStateConfigs[S]</code></td><td>that state's config (merged into the stored one when the component merges)</td></tr></table> <b>Returns</b> <code>unknown</code> - what the state's DOM work returned - a Promise for an async state (or await settled()) |
| <code>el.api.getState(): { name: GraphExplorerState; config: GraphExplorerStateConfigs[GraphExplorerState]; model?: ElementModel }</code> | The state the element shows now - read back from the DOM, so it includes what the user changed. <b>Returns</b> <code>{ name: GraphExplorerState; config: GraphExplorerStateConfigs[GraphExplorerState]; model?: ElementModel }</code> - the state's name, its config and the authored markup model render() starts from |
| <code>el.api.render(state?: { name: GraphExplorerState; config: GraphExplorerStateConfigs[GraphExplorerState]; model?: ElementModel }): string</code> | The element's markup in a state - the authored markup with that state applied; a pure function of the state. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>state?</code></td><td><code>{ name: GraphExplorerState; config: GraphExplorerStateConfigs[GraphExplorerState]; model?: ElementModel }</code></td><td>a state as getState() returns it (default: the current one)</td></tr></table> <b>Returns</b> <code>string</code> - the element's outer HTML in that state |
| <code>el.api.settled(): Promise&lt;void&gt;</code> | Wait for the last state's DOM work (async states: a diagram rendering, a chart mounting). <b>Returns</b> <code>Promise&lt;void&gt;</code> - resolves when nothing is pending |
| <code>el.store: Store&lt;{ name: GraphExplorerState; config: GraphExplorerStateConfigs[GraphExplorerState] }&gt;</code> | A defuss-store store of the element's state - subscribe to follow every change (also the user's), set it to drive the component. |

### Registry

| Member | Description |
|---|---|
| <code>df$.shadcn.graphExplorerApi.setState&lt;S extends GraphExplorerState&gt;(el: HTMLElement, name: S, config?: GraphExplorerStateConfigs[S]): unknown</code> | Enter a state: the DOM work runs (also when it is the current state), the store records it. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>el</code></td><td><code>HTMLElement</code></td><td>the component's element</td></tr><tr><td><code>name</code></td><td><code>S</code></td><td>a declared state (an unknown name throws)</td></tr><tr><td><code>config?</code></td><td><code>GraphExplorerStateConfigs[S]</code></td><td>that state's config (merged into the stored one when the component merges)</td></tr></table> <b>Returns</b> <code>unknown</code> - what the state's DOM work returned - a Promise for an async state (await it, or el.api.settled()) |
| <code>df$.shadcn.graphExplorerApi.getState(el: HTMLElement): { name: GraphExplorerState; config: GraphExplorerStateConfigs[GraphExplorerState]; model?: ElementModel }</code> | The state the element shows now - read back from the DOM, so it includes what the user changed. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>el</code></td><td><code>HTMLElement</code></td><td>the component's element</td></tr></table> <b>Returns</b> <code>{ name: GraphExplorerState; config: GraphExplorerStateConfigs[GraphExplorerState]; model?: ElementModel }</code> - the state's name, its config and the authored markup model render() starts from |
| <code>df$.shadcn.graphExplorerApi.render(state: { name: GraphExplorerState; config: GraphExplorerStateConfigs[GraphExplorerState]; model?: ElementModel }): string</code> | The element's markup in a state - the authored markup with that state applied; a pure function of the state. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>state</code></td><td><code>{ name: GraphExplorerState; config: GraphExplorerStateConfigs[GraphExplorerState]; model?: ElementModel }</code></td><td>a state as getState() returns it (with its model)</td></tr></table> <b>Returns</b> <code>string</code> - the element's outer HTML in that state |
| <code>df$.shadcn.graphExplorerApi.store(el: HTMLElement): Store&lt;{ name: GraphExplorerState; config: GraphExplorerStateConfigs[GraphExplorerState] }&gt;</code> | The element's store (bindComponent made it). <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>el</code></td><td><code>HTMLElement</code></td><td>the component's element</td></tr></table> <b>Returns</b> <code>Store&lt;{ name: GraphExplorerState; config: GraphExplorerStateConfigs[GraphExplorerState] }&gt;</code> - a defuss-store store of { name, config } - subscribe to follow every change, set it to drive the component |
| <code>df$.shadcn.graphExplorerApi.commit&lt;S extends GraphExplorerState&gt;(el: HTMLElement, name: S, config?: GraphExplorerStateConfigs[S]): void</code> | Record a state the element reached on its own (no DOM work) - for a component's own handlers. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>el</code></td><td><code>HTMLElement</code></td><td>the component's element</td></tr><tr><td><code>name</code></td><td><code>S</code></td><td>the state it is in</td></tr><tr><td><code>config?</code></td><td><code>GraphExplorerStateConfigs[S]</code></td><td>its config</td></tr></table> |
| <code>df$.shadcn.graphExplorerStates: GraphExplorerState[]</code> | The declared states, 'default' first: <code>default</code>, <code>selected</code>, <code>focused</code>. |

### `df$.shadcn.graphExplorer`

| Member | Description |
|---|---|
| <code>html(strings: TemplateStringsArray, ...values: unknown[]): SafeHtml</code> | The escaping template tag renderers write markup with: every interpolation is escaped; nested html`` values and arrays of them pass through as markup. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>strings</code></td><td><code>TemplateStringsArray</code></td><td>the template's literal parts</td></tr><tr><td><code>...values</code></td><td><code>unknown[]</code></td><td>the interpolations</td></tr></table> <b>Returns</b> <code>SafeHtml</code> - the markup as SafeHtml |
| <code>register(scope: 'global' \| HTMLElement \| string, plugin: GraphPlugin): ()</code> | Registers a node, edge, detail or update plugin - for every explorer ('global') or one element (its plugins win). Explorers on the page render again with it. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>scope</code></td><td><code>'global' \| HTMLElement \| string</code></td><td>'global', or the .graph-explorer element (or its selector)</td></tr><tr><td><code>plugin</code></td><td><code>GraphPlugin</code></td><td>the plugin</td></tr></table> <b>Returns</b> <code>()</code> - a function that removes the plugin again |
| <code>icon(name: string, opts: GraphIconOptions = {}): SafeHtml</code> | An icon of the explorer's set (a Lucide subset drawn as a CSS mask in the current color) - decorative unless it has a label. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>name</code></td><td><code>string</code></td><td>the icon's name (database, plug-zap, user, ...); an unknown name draws a circle</td></tr><tr><td><code>opts</code></td><td><code>GraphIconOptions</code> = <code>{}</code></td><td>its label, tone, size, type color and classes</td></tr></table> <b>Returns</b> <code>SafeHtml</code> - the icon's markup |
| <code>registerIcon(name: string, svg: string): void</code> | Adds an icon to the set: the inner markup of a 24x24 stroke icon (Lucide's conventions), drawn as a mask. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>name</code></td><td><code>string</code></td><td>its name (lowercase words joined by "-")</td></tr><tr><td><code>svg</code></td><td><code>string</code></td><td>the shapes inside the &lt;svg&gt; (paths, circles, lines - nothing that loads or runs)</td></tr></table> |
| <code>navigate(target: string \| HTMLElement, id: string): Promise&lt;void&gt;</code> | Flies to a node anywhere in the graph - out of the view and into the clusters on its path - and selects it. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the node's id (c&lt;number&gt; or n&lt;number&gt;)</td></tr></table> <b>Returns</b> <code>Promise&lt;void&gt;</code> - resolves when the flight has landed |
| <code>dive(target: string \| HTMLElement, id: string): Promise&lt;void&gt;</code> | Enters a cluster from anywhere: flies there and makes it the view (the focus). <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the cluster's id</td></tr></table> <b>Returns</b> <code>Promise&lt;void&gt;</code> - resolves when the view is inside it |
| <code>up(target: string \| HTMLElement): Promise&lt;boolean&gt;</code> | Moves up one level. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr></table> <b>Returns</b> <code>Promise&lt;boolean&gt;</code> - true when it moved (false at the top) |
| <code>fit(target: string \| HTMLElement): Promise&lt;string&gt;</code> | Fits the view's whole cluster into the viewport. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr></table> <b>Returns</b> <code>Promise&lt;string&gt;</code> - 'done', or 'interrupted' when another flight took over |
| <code>flyTo(target: string \| HTMLElement, camera: GraphCamera): Promise&lt;string&gt;</code> | Flies the camera to a point of the view's unit disk. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>camera</code></td><td><code>GraphCamera</code></td><td>the camera to land on</td></tr></table> <b>Returns</b> <code>Promise&lt;string&gt;</code> - 'done', or 'interrupted' when another flight took over |
| <code>path(target: string \| HTMLElement, id: string): Promise&lt;GraphPathStep[]&gt;</code> | The way from the top of the graph to a node: every cluster above it and the node itself - the worker answers. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the node's id (c&lt;number&gt; or n&lt;number&gt;)</td></tr></table> <b>Returns</b> <code>Promise&lt;GraphPathStep[]&gt;</code> - id and name of every step, the top (c0) first, the node last |
| <code>subtree(target: string \| HTMLElement, id: string, levels: number = 3): Promise&lt;GraphTreeRow[]&gt;</code> | A node and its descendants down to a number of levels, as flat records linked by parent id - what a tree grid takes (data-parent-field="parent"). The worker answers: a domain of a million-node graph with two levels below it is its systems and all their entities. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the subtree's root (c0 is the whole graph)</td></tr><tr><td><code>levels</code></td><td><code>number</code> = <code>3</code></td><td>how many levels to return, the root's included (default 3)</td></tr></table> <b>Returns</b> <code>Promise&lt;GraphTreeRow[]&gt;</code> - the rows, depth first, the root first |
| <code>find(target: string \| HTMLElement, query: string, onPartial?: (results: GraphSearchResult[], scanned: number) =&gt; void): Promise&lt;GraphSearchResult[]&gt;</code> | Searches the whole graph for names that hold the query (or #&lt;number&gt; for an entity) and answers with the hits - the clusters first, then up to twelve entities; the worker scans the entities in chunks. A newer search ends this one with what it found so far. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>query</code></td><td><code>string</code></td><td>the text to find in names</td></tr><tr><td><code>onPartial?</code></td><td><code>(results: GraphSearchResult[], scanned: number) =&gt; void</code></td><td>called with the hits so far and the entities scanned while the scan runs</td></tr></table> <b>Returns</b> <code>Promise&lt;GraphSearchResult[]&gt;</code> - the hits |
| <code>search(target: string \| HTMLElement, query: string): void</code> | Searches the graph like the search outlet does (the results show in it). <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>query</code></td><td><code>string</code></td><td>the text to find in names (or #&lt;number&gt; for an entity)</td></tr></table> |
| <code>select(target: string \| HTMLElement, ids: string[]): void</code> | Selects nodes of the view (an empty list clears the selection). <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>ids</code></td><td><code>string[]</code></td><td>the node ids</td></tr></table> |
| <code>selectEdge(target: string \| HTMLElement, id: string \| null): void</code> | Selects a link of the view (null clears it): its details show and the rest of the graph steps back. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>id</code></td><td><code>string \| null</code></td><td>the link: '&lt;source id&gt;&gt;&lt;target id&gt;' of two nodes in the view, or null</td></tr></table> |
| <code>setLayers(target: string \| HTMLElement, layers: string[]): void</code> | Shows exactly these layers: nodes, relations, labels, health or a plugin's (an empty list leaves the circles). <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>layers</code></td><td><code>string[]</code></td><td>the layers' names</td></tr></table> |
| <code>layers(target: string \| HTMLElement): GraphLayer[]</code> | The layers the switch offers - the built-in ones and those plugins add - with their colours and whether they show. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr></table> <b>Returns</b> <code>GraphLayer[]</code> - name, label, icon, colour, base and shown of each |
| <code>setCompact(target: string \| HTMLElement, on: boolean): void</code> | Draws every node as a one-line chip (on) or by its size (off). <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>on</code></td><td><code>boolean</code></td><td>true for one-line chips</td></tr></table> |
| <code>expand(target: string \| HTMLElement, id: string): Promise&lt;void&gt;</code> | Expands a node's full page: its detail renderer with ctx.expanded, the details outlet's Panel maximized (navigates there first when the node is not in view). <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the node's id, or a link's ('&lt;source id&gt;&gt;&lt;target id&gt;')</td></tr></table> <b>Returns</b> <code>Promise&lt;void&gt;</code> - resolves when the page shows |
| <code>collapse(target: string \| HTMLElement): void</code> | Collapses the expanded page back to the details. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr></table> |
| <code>simulate(target: string \| HTMLElement, on: boolean): void</code> | Starts or stops Live mode: a simulation in which breakers trip, probe and close, nodes deploy, traffic moves and counts change - each change an update (a dot on the node, a flash on the link, a row in the Updates outlet). <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>on</code></td><td><code>boolean</code></td><td>true to start, false to stop</td></tr></table> |
| <code>update(target: string \| HTMLElement, updates: Partial&lt;GraphUpdate&gt; \| Array&lt;Partial&lt;GraphUpdate&gt;&gt;): GraphUpdate[]</code> | Adds changes from the app: each marks its node or link for a while and joins the Updates outlet, newest first. A link's target is '&lt;from&gt;&gt;&lt;to&gt;' in the view its `view` names (default: the view shown); the id, the time, the layer (nodes for a node, relations for a link), the type and the label are filled in when left out. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>updates</code></td><td><code>Partial&lt;GraphUpdate&gt; \| Array&lt;Partial&lt;GraphUpdate&gt;&gt;</code></td><td>one change or several</td></tr></table> <b>Returns</b> <code>GraphUpdate[]</code> - the updates as recorded |
| <code>updates(target: string \| HTMLElement): GraphUpdate[]</code> | Every update so far, newest first (the last 10,000). <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr></table> <b>Returns</b> <code>GraphUpdate[]</code> - a copy of the list |
| <code>inspect(target: string \| HTMLElement, opts: { children?: boolean } = {}): GraphInspection</code> | Diagnostics for tests and the HUD: the camera, the view, the budget's use, the worker's timing. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr><tr><td><code>opts</code></td><td><code>{ children?: boolean }</code> = <code>{}</code></td><td>{ children: true } adds the view's children with their positions</td></tr></table> <b>Returns</b> <code>GraphInspection</code> - the inspection |
| <code>destroy(target: string \| HTMLElement): void</code> | Stops the explorer: the worker ends, the chrome and the outlets' content go, the State API is unbound. <table><tr><th>Argument</th><th>Type</th><th>Description</th></tr><tr><td><code>target</code></td><td><code>string \| HTMLElement</code></td><td>the .graph-explorer element or its selector</td></tr></table> |

### Events

| Event | Description |
|---|---|
| `graph-explorer-action` | an action button was used <code>detail</code>: <code>GraphExplorerActionDetail</code> <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>action</code></td><td><code>string</code></td><td>the action: enter, bring, expand, collapse, details, up, fit, live, help</td></tr><tr><td><code>id</code></td><td><code>string \| null</code></td><td>the node it acted on, null for the explorer's own actions</td></tr></table> |
| `graph-explorer-error` | the worker or a renderer failed <code>detail</code>: <code>GraphExplorerErrorDetail</code> <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>message</code></td><td><code>string</code></td><td>what failed</td></tr></table> |
| `graph-explorer-focus` | the view moved to another cluster (null = the top) <code>detail</code>: <code>GraphExplorerFocusDetail</code> <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>focus</code></td><td><code>string \| null</code></td><td>the cluster's id, null at the top</td></tr><tr><td><code>path</code></td><td><code>GraphPathStep[]</code></td><td>the path from the top to it</td></tr></table> |
| `graph-explorer-layers` | a layer was shown or hidden, or Compact switched <code>detail</code>: <code>GraphExplorerLayersDetail</code> <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>layers</code></td><td><code>string[]</code></td><td>the layers shown now</td></tr><tr><td><code>compact</code></td><td><code>boolean</code></td><td>true when every node is a one-line chip</td></tr></table> |
| `graph-explorer-select` | the selection changed - by a click, a key, a search result or setState <code>detail</code>: <code>GraphExplorerSelectDetail</code> <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>selected</code></td><td><code>string[]</code></td><td>the selected node ids</td></tr><tr><td><code>edge</code></td><td><code>string \| null</code></td><td>the selected link ('&lt;source id&gt;&gt;&lt;target id&gt;'), null when nodes or nothing is selected</td></tr></table> |
| `graph-explorer-update` | a node or a link changed - Live mode's simulation or update() <code>detail</code>: <code>GraphExplorerUpdateDetail</code> <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>update</code></td><td><code>GraphUpdate</code></td><td>the change, as the Updates outlet lists it</td></tr></table> |
| `graph-explorer-view` | the camera came to rest <code>detail</code>: <code>GraphExplorerViewDetail</code> <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>focus</code></td><td><code>string \| null</code></td><td>the cluster the view is in, null at the top</td></tr><tr><td><code>camera</code></td><td><code>GraphCamera</code></td><td>the camera</td></tr></table> |

### Types

| Type | Description |
|---|---|
| `GraphBreaker` | The breaker facts of a link: one circuit breaker, or the worst of the many an aggregated link bundles. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>state</code></td><td><code>'closed' \| 'half-open' \| 'open' \| 'cut'</code></td><td>closed (healthy), half-open (probing), open (tripped) or cut (by hand)</td></tr><tr><td><code>trips</code></td><td><code>number</code></td><td>trips counted</td></tr><tr><td><code>since</code></td><td><code>number</code></td><td>minutes since the breaker left closed</td></tr><tr><td><code>probes</code></td><td><code>number</code></td><td>probes a half-open breaker sends</td></tr><tr><td><code>probesOk</code></td><td><code>number</code></td><td>probes that succeeded</td></tr><tr><td><code>by</code></td><td><code>string</code></td><td>who cut it</td></tr><tr><td><code>open</code></td><td><code>number</code></td><td>member breakers open</td></tr><tr><td><code>half</code></td><td><code>number</code></td><td>member breakers half-open</td></tr><tr><td><code>cut</code></td><td><code>number</code></td><td>member breakers cut</td></tr><tr><td><code>members</code></td><td><code>number</code></td><td>breakers the link bundles</td></tr><tr><td><code>aggregate</code></td><td><code>boolean</code></td><td>true when the link bundles several breakers (an edge between clusters)</td></tr><tr><td><code>openShare</code></td><td><code>number</code></td><td>the share of open members, 0 to 1</td></tr><tr><td><code>badShare</code></td><td><code>number</code></td><td>the share of members not closed, 0 to 1</td></tr></table> |
| `GraphBreakerCounts` | A cluster's breakers by state, summed over its links at this level. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>open</code></td><td><code>number</code></td><td>open breakers</td></tr><tr><td><code>half</code></td><td><code>number</code></td><td>half-open breakers</td></tr><tr><td><code>cut</code></td><td><code>number</code></td><td>cut breakers</td></tr><tr><td><code>total</code></td><td><code>number</code></td><td>breakers in all</td></tr></table> |
| `GraphCamera` | The camera: where the view's unit disk is shown. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>cx</code></td><td><code>number</code></td><td>the center, in the view's unit disk</td></tr><tr><td><code>cy</code></td><td><code>number</code></td><td>the center, in the view's unit disk</td></tr><tr><td><code>z</code></td><td><code>number</code></td><td>screen px per unit</td></tr></table> |
| `GraphCondition` | One condition of an edge rule; every condition of a rule must hold. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>field</code></td><td><code>string</code></td><td>the edge field, dotted for nested ones: 'kind', 'type', 'breaker.state', 'breaker.openShare'</td></tr><tr><td><code>equals?</code></td><td><code>string \| number \| boolean</code></td><td>the value must equal this</td></tr><tr><td><code>in?</code></td><td><code>(string \| number \| boolean)[]</code></td><td>the value must be one of these</td></tr><tr><td><code>exists?</code></td><td><code>boolean</code></td><td>the value must exist (true) or be missing (false)</td></tr><tr><td><code>gte?</code></td><td><code>number</code></td><td>the value must be at least this</td></tr><tr><td><code>lte?</code></td><td><code>number</code></td><td>the value must be at most this</td></tr></table> |
| `GraphDetails` | What the worker knows about a node beyond its view: path, relations, counts. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the node's id</td></tr><tr><td><code>path</code></td><td><code>GraphPathStep[]</code></td><td>the path from the top to the node</td></tr><tr><td><code>relations?</code></td><td><code>GraphRelation[]</code></td><td>a cluster's relations at its level, the busiest first (at most 60)</td></tr><tr><td><code>members?</code></td><td><code>number</code></td><td>a cluster's entities</td></tr><tr><td><code>children?</code></td><td><code>number</code></td><td>a cluster's direct children</td></tr><tr><td><code>level?</code></td><td><code>number</code></td><td>a cluster's hierarchy level</td></tr><tr><td><code>out?</code></td><td><code>GraphLink[]</code></td><td>an entity's outgoing links (at most 40)</td></tr><tr><td><code>in?</code></td><td><code>GraphLink[]</code></td><td>an entity's incoming links (the sources; at most 40)</td></tr><tr><td><code>outDeg?</code></td><td><code>number</code></td><td>an entity's outgoing links in all</td></tr><tr><td><code>inDeg?</code></td><td><code>number</code></td><td>an entity's incoming links in all</td></tr></table> |
| `GraphEdge` | A link between two nodes of the view. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>k</code></td><td><code>number</code></td><td>its index in the view</td></tr><tr><td><code>a</code></td><td><code>number</code></td><td>the index of its source node in the view</td></tr><tr><td><code>b</code></td><td><code>number</code></td><td>the index of its target node in the view</td></tr><tr><td><code>count</code></td><td><code>number</code></td><td>links from a to b (an aggregate counts its members)</td></tr><tr><td><code>count2</code></td><td><code>number</code></td><td>links from b to a</td></tr><tr><td><code>bidir</code></td><td><code>boolean</code></td><td>true when it runs both ways</td></tr><tr><td><code>back</code></td><td><code>boolean</code></td><td>true when it is the back-edge of a cycle</td></tr><tr><td><code>kind</code></td><td><code>'leaf' \| 'agg'</code></td><td>'leaf' between entities, 'agg' between clusters (it bundles the links below)</td></tr><tr><td><code>type</code></td><td><code>string</code></td><td>the relation: calls, reads or owns</td></tr><tr><td><code>breaker</code></td><td><code>GraphBreaker \| null</code></td><td>its breaker facts (null when it has none)</td></tr><tr><td><code>traffic</code></td><td><code>boolean</code></td><td>true while traffic flows (the live simulation)</td></tr><tr><td><code>style</code></td><td><code>GraphEdgeStyle</code></td><td>how the rules draw it</td></tr></table> |
| `GraphEdgeContext` | What an edge label renderer gets besides the edge. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>tier</code></td><td><code>'icon' \| 'label' \| 'full'</code></td><td>icon, label or full - picked by the edge's length on screen, the budgets and the free space</td></tr><tr><td><code>depth</code></td><td><code>number</code></td><td>the hierarchy level of the view (0 = the top)</td></tr><tr><td><code>zoom</code></td><td><code>number</code></td><td>the camera's zoom</td></tr><tr><td><code>view</code></td><td><code>string \| null</code></td><td>the view's id</td></tr><tr><td><code>html</code></td><td><code>(strings: TemplateStringsArray, ...values: unknown[]) =&gt; SafeHtml</code></td><td>the escaping template tag</td></tr><tr><td><code>icon</code></td><td><code>(name: string, options?: GraphIconOptions) =&gt; SafeHtml</code></td><td>an icon</td></tr><tr><td><code>source?</code></td><td><code>GraphNode</code></td><td>the link's source node (labels in the graph)</td></tr><tr><td><code>target?</code></td><td><code>GraphNode</code></td><td>the link's target node (labels in the graph)</td></tr><tr><td><code>selected?</code></td><td><code>boolean</code></td><td>true when an end of the link is selected</td></tr></table> |
| `GraphEdgeDetail` | An edge detail renderer: what the details outlet shows for a selected link (an edge plugin's `detail`). <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>match?</code></td><td><code>(item: GraphEdgeItem) =&gt; number \| false</code></td><td>a score for a link, or false when it does not describe it; the highest wins (default 0)</td></tr><tr><td><code>title?</code></td><td><code>(item: GraphEdgeItem) =&gt; string</code></td><td>the details title for the link</td></tr><tr><td><code>render</code></td><td><code>(item: GraphEdgeItem, ctx: GraphRenderContext) =&gt; SafeHtml \| Promise&lt;SafeHtml&gt;</code></td><td>the markup (or a Promise of it); ctx.expanded asks for the link's full page</td></tr><tr><td><code>page?</code></td><td><code>(item: GraphEdgeItem) =&gt; boolean</code></td><td>whether the link has a page (default: every link) - without one no Expand button shows and E does nothing</td></tr></table> |
| `GraphEdgeDetails` | What the worker knows about an edge: its links both ways, by relation, the bundled breakers and samples. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the edge's id: '&lt;source id&gt;&gt;&lt;target id&gt;'</td></tr><tr><td><code>kind</code></td><td><code>'leaf' \| 'agg'</code></td><td>'leaf' between entities, 'agg' between clusters</td></tr><tr><td><code>path</code></td><td><code>GraphPathStep[]</code></td><td>the path from the top to the view the edge is in</td></tr><tr><td><code>source</code></td><td><code>GraphEdgeEnd</code></td><td>the source end</td></tr><tr><td><code>target</code></td><td><code>GraphEdgeEnd</code></td><td>the target end</td></tr><tr><td><code>forward</code></td><td><code>number</code></td><td>links from the source to the target</td></tr><tr><td><code>backward</code></td><td><code>number</code></td><td>links from the target back to the source</td></tr><tr><td><code>byType</code></td><td><code>Record&lt;string, number&gt;</code></td><td>links per relation (calls, reads, owns), both ways</td></tr><tr><td><code>pairs</code></td><td><code>GraphEdgePair[]</code></td><td>the system pairs it bundles with their breakers, the worst first (at most 40)</td></tr><tr><td><code>sources</code></td><td><code>GraphEdgeShare[]</code></td><td>the source's members that send the most links (at most 8)</td></tr><tr><td><code>targets</code></td><td><code>GraphEdgeShare[]</code></td><td>the target's members that receive the most links (at most 8)</td></tr><tr><td><code>samples</code></td><td><code>GraphEdgeSample[]</code></td><td>single links between entities, both ways (at most 40)</td></tr></table> |
| `GraphEdgeEnd` | One end of a link in an edge's details. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the node's id</td></tr><tr><td><code>label</code></td><td><code>string</code></td><td>the node's name</td></tr><tr><td><code>type</code></td><td><code>string</code></td><td>its type: an entity type (Dataset, Endpoint, ...) or a cluster kind (API system, Domain, ...)</td></tr><tr><td><code>system?</code></td><td><code>string</code></td><td>the system it belongs to (entities)</td></tr></table> |
| `GraphEdgeItem` | A selected link as its detail renderer gets it: the two ends and the view's edge. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>kind</code></td><td><code>'edge'</code></td><td>always 'edge'</td></tr><tr><td><code>id</code></td><td><code>string</code></td><td>'&lt;source id&gt;&gt;&lt;target id&gt;' - the value of data-selected-edge</td></tr><tr><td><code>source</code></td><td><code>GraphNode</code></td><td>the source node</td></tr><tr><td><code>target</code></td><td><code>GraphNode</code></td><td>the target node</td></tr><tr><td><code>edge</code></td><td><code>GraphEdge</code></td><td>the edge in the view: counts, relation, breaker, style</td></tr><tr><td><code>depth</code></td><td><code>number</code></td><td>the hierarchy level of the view (0 = the top)</td></tr></table> |
| `GraphEdgeLabel` | An edge label renderer: DOM labels and icons along links, chosen per zoom level. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>levels?</code></td><td><code>{ icon?: number; label?: number; full?: number }</code></td><td>the shortest on-screen length (px) for each tier</td></tr><tr><td><code>sizes?</code></td><td><code>{ icon?: [number, number]; label?: [number, number]; full?: [number, number] }</code></td><td>the box [width, height] of each tier, for collision</td></tr><tr><td><code>depths?</code></td><td><code>number[] \| null</code></td><td>the hierarchy levels it labels (null = all)</td></tr><tr><td><code>budget?</code></td><td><code>{ icon?: number; label?: number; full?: number } \| null</code></td><td>its own cap per tier</td></tr><tr><td><code>match</code></td><td><code>(edge: GraphEdge, ctx: GraphEdgeContext) =&gt; number \| false</code></td><td>a score for an edge, or false when it does not label it</td></tr><tr><td><code>priority?</code></td><td><code>(edge: GraphEdge, ctx: GraphEdgeContext) =&gt; number</code></td><td>the rank of an edge's label (the busiest or worst first)</td></tr><tr><td><code>render</code></td><td><code>(edge: GraphEdge, ctx: GraphEdgeContext) =&gt; SafeHtml</code></td><td>the label's markup</td></tr></table> |
| `GraphEdgePair` | A bundled pair of systems inside an edge between clusters, with its circuit breaker. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>from</code></td><td><code>GraphEdgeEnd</code></td><td>the calling system</td></tr><tr><td><code>to</code></td><td><code>GraphEdgeEnd</code></td><td>the called system</td></tr><tr><td><code>count</code></td><td><code>number</code></td><td>the links between them</td></tr><tr><td><code>state</code></td><td><code>string</code></td><td>the breaker: closed, half-open, open or cut</td></tr><tr><td><code>trips</code></td><td><code>number</code></td><td>how often it tripped</td></tr><tr><td><code>since</code></td><td><code>number</code></td><td>how long it has been in its state (minutes)</td></tr><tr><td><code>by</code></td><td><code>string</code></td><td>who changed it last</td></tr></table> |
| `GraphEdgeSample` | One link between two entities, as a sample of an edge. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>from</code></td><td><code>GraphEdgeEnd</code></td><td>the source entity</td></tr><tr><td><code>to</code></td><td><code>GraphEdgeEnd</code></td><td>the target entity</td></tr><tr><td><code>type</code></td><td><code>string</code></td><td>the relation: calls, reads or owns</td></tr><tr><td><code>back</code></td><td><code>boolean</code></td><td>true when it runs from the edge's target back to its source</td></tr></table> |
| `GraphEdgeShare` | A node that sends or receives a share of an edge's links. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>id</code></td><td><code>string</code></td><td>its id</td></tr><tr><td><code>label</code></td><td><code>string</code></td><td>its name</td></tr><tr><td><code>type</code></td><td><code>string</code></td><td>its type</td></tr><tr><td><code>count</code></td><td><code>number</code></td><td>its links on this edge</td></tr></table> |
| `GraphEdgeStyle` | How a link is drawn - what the edge rules decide. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>tone?</code></td><td><code>GraphTone</code></td><td>its color role</td></tr><tr><td><code>width?</code></td><td><code>number</code></td><td>its stroke width in px</td></tr><tr><td><code>arrow?</code></td><td><code>'end' \| 'none'</code></td><td>an arrowhead at the target, or none</td></tr><tr><td><code>dash?</code></td><td><code>number[]</code></td><td>a dash pattern in px</td></tr><tr><td><code>flow?</code></td><td><code>number</code></td><td>animated dashes that move along a link with traffic (1 = on)</td></tr><tr><td><code>pulse?</code></td><td><code>boolean</code></td><td>a slow pulse</td></tr><tr><td><code>gap?</code></td><td><code>boolean</code></td><td>a gap in the middle: a broken link</td></tr><tr><td><code>route?</code></td><td><code>'straight' \| 'around'</code></td><td>'around': drawn as a curve (a cycle's back-edge)</td></tr></table> |
| `GraphEntityAttrs` | The attributes of an entity (a leaf node). <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>owner</code></td><td><code>string</code></td><td>its owner</td></tr><tr><td><code>rows</code></td><td><code>number</code></td><td>rows, in thousands</td></tr><tr><td><code>freshness</code></td><td><code>number</code></td><td>hours since its last update</td></tr><tr><td><code>out</code></td><td><code>number</code></td><td>outgoing links</td></tr><tr><td><code>in</code></td><td><code>number</code></td><td>incoming links</td></tr></table> |
| `GraphExplorerActionDetail` | graph-explorer-action: an action button was used (in a card, the details or bound to the explorer). <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>action</code></td><td><code>string</code></td><td>the action: enter, bring, expand, collapse, details, up, fit, live, help</td></tr><tr><td><code>id</code></td><td><code>string \| null</code></td><td>the node it acted on, null for the explorer's own actions</td></tr></table> |
| `GraphExplorerConfig` | setState() config: what the markup says - the focus, the selection, the layers, the expanded details. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>focus?</code></td><td><code>string \| null</code></td><td>the cluster the view is in (null = the top); 'focused' needs one</td></tr><tr><td><code>selected?</code></td><td><code>string \| string[]</code></td><td>the selected node ids, space separated or as a list</td></tr><tr><td><code>layers?</code></td><td><code>string \| string[]</code></td><td>the layers shown, space separated or as a list: nodes, relations, labels, health or a plugin's (none: only the clusters' circles and dots) - data-layers, absent for the base layers nodes and relations</td></tr><tr><td><code>compact?</code></td><td><code>boolean</code></td><td>every node as a one-line chip (data-compact)</td></tr><tr><td><code>expanded?</code></td><td><code>boolean</code></td><td>the details outlet expanded to the selection's page (the first selected node's, or the link's)</td></tr><tr><td><code>edge?</code></td><td><code>string \| null</code></td><td>the selected link: '&lt;source id&gt;&gt;&lt;target id&gt;' of two nodes in the view; a node selection clears it</td></tr></table> |
| `GraphExplorerErrorDetail` | graph-explorer-error: the worker or a renderer failed. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>message</code></td><td><code>string</code></td><td>what failed</td></tr></table> |
| `GraphExplorerFocusDetail` | graph-explorer-focus: the view moved to another cluster. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>focus</code></td><td><code>string \| null</code></td><td>the cluster's id, null at the top</td></tr><tr><td><code>path</code></td><td><code>GraphPathStep[]</code></td><td>the path from the top to it</td></tr></table> |
| `GraphExplorerLayersDetail` | graph-explorer-layers: a layer was shown or hidden, or Compact switched. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>layers</code></td><td><code>string[]</code></td><td>the layers shown now</td></tr><tr><td><code>compact</code></td><td><code>boolean</code></td><td>true when every node is a one-line chip</td></tr></table> |
| `GraphExplorerSelectDetail` | graph-explorer-select: the selection changed. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>selected</code></td><td><code>string[]</code></td><td>the selected node ids</td></tr><tr><td><code>edge</code></td><td><code>string \| null</code></td><td>the selected link ('&lt;source id&gt;&gt;&lt;target id&gt;'), null when nodes or nothing is selected</td></tr></table> |
| `GraphExplorerUpdateDetail` | graph-explorer-update: a node or a link changed. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>update</code></td><td><code>GraphUpdate</code></td><td>the change, as the Updates outlet lists it</td></tr></table> |
| `GraphExplorerViewDetail` | graph-explorer-view: the camera came to rest. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>focus</code></td><td><code>string \| null</code></td><td>the cluster the view is in, null at the top</td></tr><tr><td><code>camera</code></td><td><code>GraphCamera</code></td><td>the camera</td></tr></table> |
| `GraphIconOptions` | Options of an icon. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>label?</code></td><td><code>string</code></td><td>an accessible name; without one the icon is decorative (aria-hidden)</td></tr><tr><td><code>tone?</code></td><td><code>GraphTone</code></td><td>its color role</td></tr><tr><td><code>size?</code></td><td><code>'xs' \| 'sm' \| 'lg' \| 'xl'</code></td><td>xs, sm, lg or xl (default 1rem)</td></tr><tr><td><code>type?</code></td><td><code>number</code></td><td>a type number: the icon takes that type's color (with the graph-explorer-type-icon class)</td></tr><tr><td><code>cls?</code></td><td><code>string</code></td><td>extra classes</td></tr></table> |
| `GraphInspection` | What inspect() reports: the camera, the view, the budget's use and the worker's timing. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>view</code></td><td><code>string \| null</code></td><td>the view's id</td></tr><tr><td><code>cam</code></td><td><code>GraphCamera</code></td><td>the camera</td></tr><tr><td><code>slots</code></td><td><code>number</code></td><td>DOM slots in use</td></tr><tr><td><code>flight</code></td><td><code>boolean</code></td><td>true while a flight runs</td></tr><tr><td><code>navigating</code></td><td><code>boolean</code></td><td>true while a navigation runs</td></tr><tr><td><code>lag</code></td><td><code>number</code></td><td>frames behind the camera</td></tr><tr><td><code>elements</code></td><td><code>number</code></td><td>the elements inside the explorer</td></tr><tr><td><code>worker</code></td><td><code>Record&lt;string, number&gt;</code></td><td>the worker's last frame: cards, chips, dots, edges, labels, ms</td></tr><tr><td><code>graph</code></td><td><code>Record&lt;string, number&gt; \| null</code></td><td>the generated graph: nodes, edges, ms</td></tr><tr><td><code>children?</code></td><td><code>{ id: string; label: string; x: number; y: number; r: number }[]</code></td><td>the view's children with their positions (with { children: true })</td></tr><tr><td><code>edges?</code></td><td><code>{ id: string; count: number; count2: number; kind: string }[]</code></td><td>the view's edges by id, with their link counts (with { children: true })</td></tr><tr><td><code>dotLabels?</code></td><td><code>number[][]</code></td><td>the boxes [x, y, width, height] of the labels the worker drew into the canvas (with { children: true })</td></tr></table> |
| `GraphIslandMount` | Runs once when an island's element is made - bind a component to it (a grid's records, a chart's data); the function it returns runs when the island goes. = <code>(el: HTMLElement) =&gt; void \| (() =&gt; void)</code> |
| `GraphLayer` | A layer of information the layer switch shows or hides. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>name</code></td><td><code>string</code></td><td>its name (a word of data-layers)</td></tr><tr><td><code>label</code></td><td><code>string</code></td><td>its label</td></tr><tr><td><code>icon</code></td><td><code>string</code></td><td>its icon</td></tr><tr><td><code>color</code></td><td><code>string</code></td><td>its colour: the CSS colour its switch, its parts and its labels are drawn in</td></tr><tr><td><code>base</code></td><td><code>boolean</code></td><td>true for the base layers (nodes, relations) - shown while data-layers is absent</td></tr><tr><td><code>shown</code></td><td><code>boolean</code></td><td>true while it is shown</td></tr></table> |
| `GraphLink` | An outgoing link of an entity (details of an entity). <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the target's id</td></tr><tr><td><code>label</code></td><td><code>string</code></td><td>the target's name</td></tr><tr><td><code>type</code></td><td><code>string</code></td><td>the target's type</td></tr><tr><td><code>system</code></td><td><code>string</code></td><td>the target's system</td></tr><tr><td><code>domain</code></td><td><code>string</code></td><td>the target's domain</td></tr><tr><td><code>edge</code></td><td><code>string</code></td><td>the relation: calls, reads or owns</td></tr><tr><td><code>same</code></td><td><code>boolean</code></td><td>true when the target is in the same system</td></tr></table> |
| `GraphNode` | A node as renderers see it: a cluster (a domain or a system) or an entity. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the stable id: c&lt;number&gt; for a cluster, n&lt;number&gt; for an entity</td></tr><tr><td><code>i</code></td><td><code>number</code></td><td>its index in the current view</td></tr><tr><td><code>kind</code></td><td><code>'cluster' \| 'entity'</code></td><td>a cluster has children; an entity is a leaf</td></tr><tr><td><code>label</code></td><td><code>string</code></td><td>its name</td></tr><tr><td><code>type</code></td><td><code>number</code></td><td>its type number (0-7), the key of its color</td></tr><tr><td><code>typeName</code></td><td><code>string</code></td><td>its type: Domain, API, Worker, Store, Stream - or an entity's Dataset, Endpoint, Document, Person, Job, Topic</td></tr><tr><td><code>members</code></td><td><code>number</code></td><td>the entities below a cluster (1 for an entity)</td></tr><tr><td><code>children</code></td><td><code>number</code></td><td>a cluster's direct children (0 for an entity)</td></tr><tr><td><code>degree</code></td><td><code>number</code></td><td>its links at this level</td></tr><tr><td><code>scc</code></td><td><code>number</code></td><td>the size of its strongly connected component - more than 1 = in a cycle</td></tr><tr><td><code>x</code></td><td><code>number</code></td><td>its center, in the view's unit disk</td></tr><tr><td><code>y</code></td><td><code>number</code></td><td>its center, in the view's unit disk</td></tr><tr><td><code>r</code></td><td><code>number</code></td><td>its radius, in the view's unit disk</td></tr><tr><td><code>depth</code></td><td><code>number</code></td><td>the hierarchy level of the view it is in (0 = the top)</td></tr><tr><td><code>breakers?</code></td><td><code>GraphBreakerCounts \| null</code></td><td>a cluster's breakers, summed over its links at this level (null in an entity view)</td></tr><tr><td><code>attrs?</code></td><td><code>GraphEntityAttrs</code></td><td>an entity's attributes</td></tr></table> |
| `GraphPathStep` | One step of a path from the top: a cluster or the node itself. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the step's id</td></tr><tr><td><code>label</code></td><td><code>string</code></td><td>the step's name</td></tr></table> |
| `GraphPlugin` | A plugin: a node renderer (kind 'node'), edge rules and labels (kind 'edge') or a detail renderer (kind 'detail'). <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>kind</code></td><td><code>'node' \| 'edge' \| 'detail' \| 'update'</code></td><td>node, edge, detail or update</td></tr><tr><td><code>name</code></td><td><code>string</code></td><td>its name (error messages, the HUD)</td></tr><tr><td><code>match?</code></td><td><code>(node: GraphNode) =&gt; number \| false</code></td><td>node / detail: a score for a node, or false; the highest wins, the later of equal ones</td></tr><tr><td><code>sizes?</code></td><td><code>GraphSizes \| ((node: GraphNode) =&gt; GraphSizes)</code></td><td>node: the box of each tier, [width, height] in px - a part's: a chip part adds its width to the chip, a card part its height to the card (its width is the least the card needs)</td></tr><tr><td><code>render?</code></td><td><code>(node: GraphNode, ctx: GraphRenderContext) =&gt; SafeHtml \| Promise&lt;SafeHtml&gt;</code></td><td>node / detail: the markup (a detail renderer may return a Promise of it)</td></tr><tr><td><code>key?</code></td><td><code>(node: GraphNode, ctx: GraphRenderContext) =&gt; string</code></td><td>node: a key of what the markup depends on beyond the node (a slot morphs only when it changes)</td></tr><tr><td><code>layer?</code></td><td><code>string</code></td><td>node: the layer this renderer draws a part of (a lowercase word) - without one it draws the node itself (the nodes layer); edge: the layer its rules and labels belong to (default: relations for rules, labels for labels)</td></tr><tr><td><code>layerLabel?</code></td><td><code>string</code></td><td>node / edge: the layer's name in the layer switch (a plugin's own layer)</td></tr><tr><td><code>layerIcon?</code></td><td><code>string</code></td><td>node / edge: the layer's icon in the layer switch</td></tr><tr><td><code>layerColor?</code></td><td><code>string</code></td><td>node / edge: the layer's colour, any CSS colour (default: the next chart colour)</td></tr><tr><td><code>title?</code></td><td><code>(node: GraphNode) =&gt; string</code></td><td>detail: the details title for a node</td></tr><tr><td><code>page?</code></td><td><code>(node: GraphNode) =&gt; boolean</code></td><td>detail: whether a node has a page (default: every node) - without one no Expand button shows and E does nothing</td></tr><tr><td><code>rules?</code></td><td><code>GraphRule[]</code></td><td>edge: the style rules (plain data)</td></tr><tr><td><code>label?</code></td><td><code>GraphEdgeLabel</code></td><td>edge: the label renderer</td></tr><tr><td><code>detail?</code></td><td><code>GraphEdgeDetail</code></td><td>edge: the details of a selected link (the details outlet, its full page when expanded)</td></tr><tr><td><code>update?</code></td><td><code>GraphUpdateRenderer</code></td><td>update: the row renderer of the Updates outlet</td></tr></table> |
| `GraphRelation` | A relation of a cluster at its level (details of a cluster). <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the other cluster's id</td></tr><tr><td><code>label</code></td><td><code>string</code></td><td>the other cluster's name</td></tr><tr><td><code>out</code></td><td><code>number</code></td><td>links from the cluster to it</td></tr><tr><td><code>in</code></td><td><code>number</code></td><td>links from it to the cluster</td></tr><tr><td><code>breaker</code></td><td><code>GraphBreaker \| null</code></td><td>the breaker facts of the link</td></tr><tr><td><code>back</code></td><td><code>boolean</code></td><td>true when the link is the back-edge of a cycle</td></tr></table> |
| `GraphRenderContext` | What a node or detail renderer gets besides the node. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>tier?</code></td><td><code>'chip' \| 'card' \| 'detail' \| 'relations'</code></td><td>chip or card for a node in the graph; detail for the details outlet; relations for the relations outlet</td></tr><tr><td><code>selected</code></td><td><code>boolean</code></td><td>true when the node is selected</td></tr><tr><td><code>highlighted</code></td><td><code>boolean</code></td><td>true while the pointer is over the node</td></tr><tr><td><code>focused</code></td><td><code>boolean</code></td><td>true when the node has the keyboard focus</td></tr><tr><td><code>depth</code></td><td><code>number</code></td><td>the hierarchy level of the view (0 = the top)</td></tr><tr><td><code>zoom</code></td><td><code>number</code></td><td>the camera's zoom: screen px per unit of the view's disk</td></tr><tr><td><code>expanded</code></td><td><code>boolean</code></td><td>true when the details outlet is expanded (its Panel maximized): render the node's full page</td></tr><tr><td><code>layers</code></td><td><code>string[]</code></td><td>the layers shown (data-layers): nodes, relations, labels, health and the plugins'</td></tr><tr><td><code>compact</code></td><td><code>boolean</code></td><td>true when every node is drawn as a one-line chip (data-compact)</td></tr><tr><td><code>parts</code></td><td><code>SafeHtml</code></td><td>a node renderer: the parts the shown layers add to this node at this tier - put them where they belong (the explorer appends them after the markup of a renderer that leaves them out)</td></tr><tr><td><code>html</code></td><td><code>(strings: TemplateStringsArray, ...values: unknown[]) =&gt; SafeHtml</code></td><td>the escaping template tag</td></tr><tr><td><code>icon</code></td><td><code>(name: string, options?: GraphIconOptions) =&gt; SafeHtml</code></td><td>an icon</td></tr><tr><td><code>action</code></td><td><code>(name: string) =&gt; string</code></td><td>the value of a data-graph-explorer-action attribute: enter, bring, expand, collapse, details</td></tr><tr><td><code>edgeLabel</code></td><td><code>(edge: Partial&lt;GraphEdge&gt;, tier?: 'icon' \| 'label' \| 'full') =&gt; SafeHtml</code></td><td>a link drawn by the edge label renderers, at a tier</td></tr><tr><td><code>fetchView</code></td><td><code>(id: string) =&gt; Promise&lt;GraphView&gt;</code></td><td>a cluster's view, loaded by the worker</td></tr><tr><td><code>fetchDetails</code></td><td><code>(id: string) =&gt; Promise&lt;GraphDetails&gt;</code></td><td>a node's details, loaded by the worker</td></tr><tr><td><code>fetchEdge</code></td><td><code>(id: string) =&gt; Promise&lt;GraphEdgeDetails&gt;</code></td><td>an edge's details, loaded by the worker: its links both ways, by relation, its breakers and samples</td></tr><tr><td><code>island</code></td><td><code>(key: string, content: SafeHtml, mount?: GraphIslandMount) =&gt; SafeHtml</code></td><td>markup the details keep as it is while its key and its markup stay the same: a library component with a script (a Chart, a Geo Map, an Avatar) keeps what its script made when the details render again; `mount` runs once when the island's element is made (bind a grid's records, a chart's data) and what it returns when it goes</td></tr><tr><td><code>expandable</code></td><td><code>boolean</code></td><td>true when the node (or the link) has a page to expand to: a details outlet shows it and its detail renderer offers one - an Expand button belongs only there</td></tr><tr><td><code>fetchRows</code></td><td><code>(id: string) =&gt; Promise&lt;GraphRows&gt;</code></td><td>every row behind a node or a link, built by the worker: the entities below a cluster, a link's single links</td></tr><tr><td><code>request</code></td><td><code>&lt;T&gt;(key: string, load: () =&gt; T \| Promise&lt;T&gt;) =&gt; T \| undefined</code></td><td>a value loaded once per key: undefined until it first arrives, then the node renders again with it; when the data moves on (a Live mode update) the key loads again and the last answer stays meanwhile</td></tr></table> |
| `GraphRow` | One row of GraphRows: plain values. = <code>Record&lt;string, string \| number&gt;</code> |
| `GraphRows` | Every row behind a node or a link (ctx.fetchRows) - the records a Data Grid takes. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the node's or the link's id</td></tr><tr><td><code>kind</code></td><td><code>'entities' \| 'links'</code></td><td>entities: every entity below a cluster (or the entity itself); links: a link's single links, both ways</td></tr><tr><td><code>rows</code></td><td><code>GraphRow[]</code></td><td>an entity: id, label, type, system, domain, owner, out, in, rows (thousands), freshness (hours); a link: id, from, fromLabel, relation, to, toLabel, direction (forward, back), fromSystem, toSystem</td></tr></table> |
| `GraphRule` | An edge rule: plain data the worker evaluates for every link it draws; the highest priority that matches wins. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>when?</code></td><td><code>GraphCondition[]</code></td><td>the conditions, all of which must hold</td></tr><tr><td><code>style</code></td><td><code>GraphEdgeStyle</code></td><td>the style it gives</td></tr><tr><td><code>priority?</code></td><td><code>number</code></td><td>its rank among the rules that match (default 0; a plugin registered on one element adds 1000)</td></tr></table> |
| `GraphSearchResult` | One hit of find(): a cluster or an entity whose name holds the query. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the node's id</td></tr><tr><td><code>label</code></td><td><code>string</code></td><td>the node's name</td></tr><tr><td><code>kind</code></td><td><code>'cluster' \| 'entity'</code></td><td>cluster or entity</td></tr><tr><td><code>type</code></td><td><code>string</code></td><td>Domain, a system kind or an entity type</td></tr><tr><td><code>path</code></td><td><code>string</code></td><td>the clusters above it, joined by ' › '</td></tr><tr><td><code>parents</code></td><td><code>{ id: string; label: string }[]</code></td><td>the clusters above it, the top first - id and name each</td></tr></table> |
| `GraphSizes` | A node renderer's boxes: [width, height] in px per tier (a part: the width a chip part adds, the height a card part adds). <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>chip?</code></td><td><code>[number, number]</code></td><td>the chip's box</td></tr><tr><td><code>card?</code></td><td><code>[number, number]</code></td><td>the card's box</td></tr></table> |
| `GraphTone` | A tone: a color role of the theme (muted text, ink, an accent, a link, healthy, warning, danger). = <code>'muted' \| 'ink' \| 'accent' \| 'link' \| 'ok' \| 'warn' \| 'danger'</code> |
| `GraphTreeRow` | One node of a subtree (subtree()) - a flat record a tree grid takes (data-parent-field="parent"). <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the node's id (c&lt;number&gt; a cluster, n&lt;number&gt; an entity)</td></tr><tr><td><code>parent</code></td><td><code>string</code></td><td>the parent's id; '' for the subtree's root</td></tr><tr><td><code>label</code></td><td><code>string</code></td><td>the node's name</td></tr><tr><td><code>kind</code></td><td><code>'cluster' \| 'entity'</code></td><td>cluster or entity</td></tr><tr><td><code>type</code></td><td><code>string</code></td><td>what it is: Graph, Domain, a system kind (API, Worker, Store, Stream) or an entity type (Dataset, Endpoint ...)</td></tr><tr><td><code>level</code></td><td><code>number</code></td><td>the hierarchy level in the whole graph (0 = the top)</td></tr><tr><td><code>count</code></td><td><code>number</code></td><td>a cluster's members (every entity below it), an entity's links (in + out)</td></tr><tr><td><code>children</code></td><td><code>number</code></td><td>the children it has in the graph - also when the subtree stops above them</td></tr></table> |
| `GraphUpdate` | A change in the graph: a node or a link that changed, as the Updates outlet lists it and the graph marks it (a glowing dot on a node, a link that flashes in its layer's colour). Live mode's simulation makes them; an app adds its own with update(). <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>id</code></td><td><code>string</code></td><td>a unique id (made when an update leaves it out)</td></tr><tr><td><code>time</code></td><td><code>number</code></td><td>when it happened, in ms since the epoch</td></tr><tr><td><code>kind</code></td><td><code>'node' \| 'edge'</code></td><td>what changed: a node, or a link between two nodes</td></tr><tr><td><code>target</code></td><td><code>string</code></td><td>the node's id, or the link's '&lt;from&gt;&gt;&lt;to&gt;'</td></tr><tr><td><code>view?</code></td><td><code>string</code></td><td>a link's view: the cluster whose children it joins ('c0' the top level) - a click goes there first</td></tr><tr><td><code>layer</code></td><td><code>string</code></td><td>the layer it belongs to: it shows while that layer is shown (nodes, relations, labels, health or a plugin's)</td></tr><tr><td><code>type</code></td><td><code>string</code></td><td>a short word for the kind of change: deploy, breaker, traffic, count ...</td></tr><tr><td><code>text</code></td><td><code>string</code></td><td>what happened, one line</td></tr><tr><td><code>label</code></td><td><code>string</code></td><td>the target's name (the explorer fills it in for a node or link of the view it shows)</td></tr><tr><td><code>tone?</code></td><td><code>GraphTone</code></td><td>its tone: ok, warn, danger, accent ... (the row's dot; default: the layer's colour)</td></tr></table> |
| `GraphUpdateContext` | What an update renderer gets beside the update. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>html</code></td><td><code>(strings: TemplateStringsArray, ...values: unknown[]) =&gt; SafeHtml</code></td><td>the escaping template tag every renderer returns its markup through</td></tr><tr><td><code>icon</code></td><td><code>(name: string, opts?: GraphIconOptions) =&gt; SafeHtml</code></td><td>an icon of the explorer's set</td></tr><tr><td><code>color</code></td><td><code>(layer: string) =&gt; string</code></td><td>a layer's colour (any CSS colour)</td></tr><tr><td><code>time</code></td><td><code>(ms: number) =&gt; string</code></td><td>a time, formatted for the row (hours, minutes, seconds)</td></tr></table> |
| `GraphUpdateRenderer` | An update renderer: one compact row of the Updates outlet (the explorer wraps it in the button that selects the target). <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>match?</code></td><td><code>(update: GraphUpdate) =&gt; number \| false</code></td><td>a score for an update, or false; the highest wins, the later of equal ones (default 0)</td></tr><tr><td><code>render</code></td><td><code>(update: GraphUpdate, ctx: GraphUpdateContext) =&gt; SafeHtml</code></td><td>the row's content</td></tr></table> |
| `GraphView` | A cluster's view: its children laid out in its unit disk, and the links between them. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>id</code></td><td><code>string</code></td><td>the cluster's id (c0 = the top)</td></tr><tr><td><code>path</code></td><td><code>GraphPathStep[]</code></td><td>the path from the top to the cluster</td></tr><tr><td><code>children</code></td><td><code>GraphNode[]</code></td><td>the cluster's children</td></tr><tr><td><code>edges</code></td><td><code>GraphEdge[]</code></td><td>the links between them</td></tr><tr><td><code>leafView</code></td><td><code>boolean</code></td><td>true when the children are entities</td></tr></table> |
| `SafeHtml` | Markup made by html`` - every interpolation escaped; the only value a renderer may return. <table><tr><th>Field</th><th>Type</th><th>Description</th></tr><tr><td><code>html</code></td><td><code>string</code></td><td>the markup, its interpolations escaped</td></tr></table> |
