Skip to content

@graphty/graphty-element / index / DataManager

Class: DataManager ​

Defined in: graphty-element/src/managers/DataManager.ts:308

Manages all data operations for nodes and edges Handles CRUD operations, caching, and data source loading

THE AUTHORITATIVE COPY OF THE GRAPH IS THE STORE, NOT THE MESHES. nodes and edges hold the render objects -- a Node builds a Babylon mesh in its constructor -- and they remain how the scene is drawn. Alongside them this manager keeps ONE GraphStore for the life of the graph: every record that arrives is pushed into its builder, and getSnapshot() hands out an immutable graph-format snapshot of it. Node.index and Edge.index are that snapshot's dense row numbers, so an id maps to a row without walking a map of meshes.

The two copies are not always in step, and the direction of the discrepancy is deliberate:

  • An edge whose endpoints have not arrived is in the STORE already (the builder is addMissingNodes: true, so it materialises both endpoints) while its render object waits in pendingEdges for the Node objects it reads in its constructor. The snapshot is therefore the more complete of the two mid-load.
  • A NODE whose configured id path yields something graph-format will not accept -- null from a record with no id key, most often -- gets a render object with index === INVALID_INDEX and no row in the store. The element has always drawn such a record; refusing it would be a new failure in the middle of a data load.

An EDGE is the exception, and deliberately: one whose endpoint ids the store will not take is REJECTED, counted in the import report, and never becomes a render object at all. An edge with no store row could not be filtered -- the per-frame mask forces an unplaced edge visible -- so it was permanently on screen with nothing able to hide it.

Implements ​

Constructors ​

Constructor ​

new DataManager(eventManager, styles): DataManager

Defined in: graphty-element/src/managers/DataManager.ts:482

Creates an instance of DataManager

Parameters ​

eventManager ​

EventManager

Event manager for emitting data events

styles ​

Styles

Styles instance for applying styles to data

Returns ​

DataManager

Properties ​

edgeVersion ​

edgeVersion: number = 0

Defined in: graphty-element/src/managers/DataManager.ts:354

Goes up on every edge added or removed, so a cache over the edge set knows it is stale.


meshCache ​

meshCache: MeshCache

Defined in: graphty-element/src/managers/DataManager.ts:417


nodeCache ​

nodeCache: Map<NodeIdType, Node>

Defined in: graphty-element/src/managers/DataManager.ts:355

Accessors ​

[WRITABLE_LANE] ​

Get Signature ​

get [WRITABLE_LANE](): ElementPositions

Defined in: graphty-element/src/managers/DataManager.ts:546

The writable lane, for the element's own engines and nodes. See writableLane.

Returns ​

ElementPositions

the live position array


directionSettledBy ​

Get Signature ​

get directionSettledBy(): DirectionProvenance

Defined in: graphty-element/src/managers/DataManager.ts:565

How the direction of the loaded graph was settled, and by what text.

Returns ​

DirectionProvenance

the provenance; "unsettled" until a file or the configuration says


edges ​

Get Signature ​

get edges(): ReadonlyMap<string, Edge>

Defined in: graphty-element/src/managers/DataManager.ts:346

Every edge the graph holds, keyed by Edge.id.

The key type is string and not string | number, because Edge.id is the element's own edge counter printed as a string and nothing else. While the key was widened, getEdge(0) compiled, answered undefined for the edge whose id is "0", and said nothing about it.

Returns ​

ReadonlyMap<string, Edge>

The edges.


edgesByIndex ​

Get Signature ​

get edgesByIndex(): readonly (Edge | undefined)[]

Defined in: graphty-element/src/managers/DataManager.ts:363

Render objects by their store edge index, so a freeze report's edgeRemap -- and a removal, which hands back the incident edge indices and nothing else -- can find them in O(1). Sparse: an index with no render object yet, or whose edge was removed, reads undefined.

Returns ​

readonly (Edge | undefined)[]

The edges by row, read-only.


graphResults ​

Get Signature ​

get graphResults(): AdHocData | undefined

Defined in: graphty-element/src/managers/DataManager.ts:389

Graph-level results a plugin algorithm without a descriptor wrote, kept as the graphResults value of the graph. Read-only, except to a plugin while algo.legacy runs it: what it writes then is part of that command's step.

Returns ​

AdHocData | undefined

The value, or undefined when none was written.

Set Signature ​

set graphResults(value): void

Defined in: graphty-element/src/managers/DataManager.ts:401

Write graph-level results; only a plugin can, while algo.legacy runs it.

Throws ​

A GraphtyError with E_UNSUPPORTED outside a plugin run.

Parameters ​
value ​

AdHocData | undefined

The results.

Returns ​

void


isLoading ​

Get Signature ​

get isLoading(): boolean

Defined in: graphty-element/src/managers/DataManager.ts:1752

Whether a load from a data source is still streaming records in.

A static layout reads it to tell a chunk of a load, after which the whole graph is arranged again, from a reader's add to a finished graph, after which existing nodes stay put.

Returns ​

boolean

true between a load's first chunk and its end


lastImport ​

Get Signature ​

get lastImport(): LoadReport | null

Defined in: graphty-element/src/managers/DataManager.ts:577

What the last load did: which endpoint spelling answered, how many repeats were seen and what the policy did with them, and how many edges the graph actually holds.

A door rather than only an event, because the two load events are fire-and-forget and a consumer that subscribed after the load has no way to ask otherwise.

Returns ​

LoadReport | null

the report, or null when nothing has been loaded into this graph


nodes ​

Get Signature ​

get nodes(): ReadonlyMap<string | number, Node>

Defined in: graphty-element/src/managers/DataManager.ts:331

Every node the graph holds, keyed by id. Read-only: nodes arrive and leave through the data doors, which are undoable steps.

Returns ​

ReadonlyMap<string | number, Node>

The nodes.


positions ​

Get Signature ​

get positions(): ReadonlyElementPositions

Defined in: graphty-element/src/managers/DataManager.ts:535

The element-owned node coordinates: a stride-3 Float32Array indexed by Node.index.

The element owns these, not the snapshot -- every freeze lends the array to the new snapshot as its role: "position" column BY REFERENCE -- so a layout, a drag or a GPU readback writes in one place and nothing is lost when the graph is frozen again. A row that no layout has placed reads as NaN, never as the origin: zero is a real coordinate and "not placed yet" is not.

Read-only here: a write would move nodes with no step. The element's own engines reach the writable lane through writableLane; a consumer places nodes through session.positions.set.

Returns ​

ReadonlyElementPositions

the coordinates, read-only


seededNodeCount ​

Get Signature ​

get seededNodeCount(): number

Defined in: graphty-element/src/managers/DataManager.ts:557

How many nodes the DATA arrived carrying a coordinate for.

Distinct from positions.placedCount, which counts what anything has placed -- including a layout that has run for one frame. See GraphStore.seededNodeCount.

Returns ​

number

the count


snapshotStale ​

Get Signature ​

get snapshotStale(): boolean

Defined in: graphty-element/src/managers/DataManager.ts:508

Whether the next getSnapshot would freeze a new snapshot.

Returns ​

boolean

True when the store is not settled.

Methods ​

addDataFromSource() ​

addDataFromSource(type, opts?, load?): Promise<void>

Defined in: graphty-element/src/managers/DataManager.ts:1834

Loads data from a registered data source, as one step: a data.import on its turn in the queue. A REPLACING load empties the graph in the same step, and a load that fails rolls the whole step back, so a malformed or empty file leaves the graph it would have replaced exactly as it was.

A load that reads no node records and no edge records at all fails with E_EMPTY_LOAD rather than completing with zero counts. A file of edges alone is not empty: its endpoints become nodes.

The load that STARTED last wins. Once a replacing load has started, every load started before it -- replacing or additive -- is superseded: it adds nothing more and rejects with E_SUPERSEDED, whichever order the sources finish in. A superseded load emits no data-loading-error, because nothing went wrong with its source.

Parameters ​

type ​

string

Data source type identifier

opts? ​

object = {}

Options to pass to the data source

load? ​

Which load this is, for every event it emits, and whether it replaces the graph

coalesce? ​

string

The key imports coalesce under while the first waits its turn

generation? ​

number

The place beginLoad reserved for this load when the caller's call was made; left unset, the load takes its place now

loadId? ​

number

The id every event about this load carries

replace? ​

boolean

Swap the graph for what the source holds, once it has all parsed

setup? ​

boolean

Declared at construction: it becomes the baseline

Returns ​

Promise<void>


addEdge() ​

addEdge(edge, options?): void

Defined in: graphty-element/src/managers/DataManager.ts:1582

Adds a single edge to the graph

Parameters ​

edge ​

AdHocData

Edge data object

options? ​

AddEdgesOptions

the endpoint expressions and the repeat policy for this call

Returns ​

void


addEdges() ​

addEdges(edges, options?): void

Defined in: graphty-element/src/managers/DataManager.ts:1608

Add edge records to the graph, resolving their endpoints once for the whole batch.

THREE THINGS HAPPEN HERE THAT USED TO HAPPEN ELSEWHERE OR NOT AT ALL.

The endpoint spelling is decided once per batch and reported, rather than read from two configured paths whose defaults disagreed with every guide the element ships. A batch whose records answer none of the accepted spellings throws instead of quietly producing a graph with nodes and no edges.

A record naming an ordered pair the graph already holds is handed to the repeat policy, which by default KEEPS it as a second edge. It used to be dropped before the store could see it, which is why statistics().repeatedEdgeCount has always been zero.

A record whose endpoint ids graph-format will not store is REJECTED and counted, rather than becoming a render object with no store row -- which is how an edge ended up permanently visible and unfilterable.

Parameters ​

edges ​

Record<string | number, unknown>[]

Array of edge data objects

options? ​

AddEdgesOptions

the endpoint expressions and the repeat policy for this call

Returns ​

void

Throws ​

A GraphtyError with E_EDGE_ENDPOINTS_UNRESOLVED when no spelling answers, and with E_DUPLICATE_EDGE under the "error" repeat policy.


addNode() ​

addNode(node, idPath?): void

Defined in: graphty-element/src/managers/DataManager.ts:1322

Adds a single node to the graph

Parameters ​

node ​

AdHocData

Node data object

idPath? ​

string

JMESPath expression to extract node ID from data

Returns ​

void


addNodes() ​

addNodes(nodes, idPath?): void

Defined in: graphty-element/src/managers/DataManager.ts:1353

Adds multiple nodes to the graph

Parameters ​

nodes ​

Record<string | number, unknown>[]

Array of node data objects

idPath? ​

string

JMESPath expression to extract node ID from data

Returns ​

void


beginLoad() ​

beginLoad(replace): number

Defined in: graphty-element/src/managers/DataManager.ts:1899

Reserve a load's place in line, at the moment the caller asked for it.

A caller that reads a file or sniffs a URL before it loads calls this FIRST, so a load that was asked for later still wins however long the earlier one spends reading. A replacing load supersedes every load reserved before it.

Parameters ​

replace ​

boolean

Whether the load will replace the graph

Returns ​

number

The generation to hand to addDataFromSource and throwIfSuperseded


bindSession() ​

bindSession(dispatcher, hooks): void

Defined in: graphty-element/src/managers/DataManager.ts:592

Write through the session from here on: the data doors dispatch data.apply and data.import, and the session carries both out through this manager's ingest, over this manager's store.

Parameters ​

dispatcher ​

Dispatcher

The session's dispatcher.

hooks ​

What the graph does around a write.

loading ​

Called with true when an import starts reading and false when it stops.

removing ​

Called with the nodes and edges a removal names, before it writes.

rowsAdded ​

Called by each command that adds rows, after it wrote them, with how that command starts work as its deferred members.

Returns ​

void


clear() ​

clear(): void

Defined in: graphty-element/src/managers/DataManager.ts:1935

Remove every node, edge, record and graph-level value, as one undoable step.

Returns ​

void


dispose() ​

dispose(): void

Defined in: graphty-element/src/managers/DataManager.ts:1297

Disposes of the data manager and cleans up all resources

Returns ​

void

Implementation of ​

Manager.dispose


getEdge() ​

getEdge(edgeId): Edge | undefined

Defined in: graphty-element/src/managers/DataManager.ts:1704

Gets an edge by its ID

Parameters ​

edgeId ​

string

Edge identifier

Returns ​

Edge | undefined

Edge instance or undefined if not found


getEdgesBetween() ​

getEdgesBetween(srcNodeId, dstNodeId): readonly Edge[]

Defined in: graphty-element/src/managers/DataManager.ts:1722

Every edge running from one node to another.

Plural because "the edge between a and b" stopped being a single thing the moment parallel edges became representable.

Answered by the store: the builder's incidence lists name the live edges between the two rows, and edgesByIndex turns each into its render object. An edge whose render object is still pending is left out, and so, in an undirected graph, is an edge recorded the other way round -- the pair is ORDERED, the same question for either direction.

Parameters ​

srcNodeId ​

NodeIdType

Source node identifier

dstNodeId ​

NodeIdType

Destination node identifier

Returns ​

readonly Edge[]

the edges, oldest first; empty when there are none


getNode() ​

getNode(nodeId): Node | undefined

Defined in: graphty-element/src/managers/DataManager.ts:1498

Gets a node by its ID, in either of the two types an integer id can be written as.

The map is keyed on the id the source file carried, untouched: GML parses a bare integer with parseInt, so the shipped Karate Club and Football samples hold NUMBER keys, while every id that has been through a URL, a DOM attribute, a JSON document or a consumer's own UI is a string by the time it comes back. Map.get("34") misses the key 34 in silence -- no throw, no event -- so pin, selectNode and zoomToNodes were no-ops on exactly those samples, and the one consumer carried its own retry for the two verbs whose return value made the miss detectable at all.

The exact key always wins, so a graph holding both 34 and "34" is unaffected. The retry is integers only: a float or a hexadecimal id would round-trip through Number into a different value than the file carried, and finding an id the file never had is worse than a lookup that missed.

Parameters ​

nodeId ​

NodeIdType

Node identifier

Returns ​

Node | undefined

Node instance or undefined if not found


getSnapshot() ​

getSnapshot(): GraphSnapshot

Defined in: graphty-element/src/managers/DataManager.ts:500

The current graph snapshot, freezing first when records have arrived since the last one.

The same object is returned until the graph changes again, so a burst of records -- through the operation queue, through skipQueue, or through addDataFromSource, which bypasses the queue entirely -- costs exactly one freeze however many readers ask afterwards. The snapshot is immutable; the node coordinates it carries are not, because the element lends it positions by reference.

Returns ​

GraphSnapshot

the snapshot


getStats() ​

getStats(): object

Defined in: graphty-element/src/managers/DataManager.ts:1954

Get statistics about the data

Returns ​

object

the node and edge counts the graph holds -- the same numbers statistics() and the stats panel give -- and the cached mesh count

cachedMeshes ​

cachedMeshes: number

edgeCount ​

edgeCount: number

nodeCount ​

nodeCount: number


heldCounts() ​

heldCounts(): object

Defined in: graphty-element/src/managers/DataManager.ts:1762

What the graph HOLDS right now: the node and edge counts of the store, the same numbers statistics() and the import report give. An edge endpoint no record declared as a node is counted, and so is an edge still waiting for an endpoint.

Returns ​

object

the node and edge counts the graph holds

edges ​

edges: number

nodes ​

nodes: number


init() ​

init(): Promise<void>

Defined in: graphty-element/src/managers/DataManager.ts:1259

Initializes the data manager

Returns ​

Promise<void>

Promise that resolves when initialization is complete

Implementation of ​

Manager.init


reconcile() ​

reconcile(slice, dirty, cause): object

Defined in: graphty-element/src/managers/DataManager.ts:713

Bring the render objects in line with the graph slice: what the derivation lane's graph hook runs, forward and on undo, redo and rollback alike. A node or edge the slice holds and nothing draws is built; one drawn that the slice no longer holds is torn down; one whose record changed is handed the new record. Forward adds were drawn as they were ingested, so for them this finds nothing to build.

Parameters ​

slice ​

GraphSlice

The slice to draw.

dirty ​

ReadonlySet<string>

The slice's keys changed since the last pass.

cause ​

HistoryCause

What moved the state, for the events.

Returns ​

object

How many rows were built and torn down.

added ​

added: number

removed ​

removed: number


removeEdge() ​

removeEdge(edgeId): boolean

Defined in: graphty-element/src/managers/DataManager.ts:1799

Removes an edge from the graph

Parameters ​

edgeId ​

string

Edge identifier to remove

Returns ​

boolean

True if the edge was removed, false if not found


removeNodeAndIncidentEdges() ​

removeNodeAndIncidentEdges(nodeId): readonly string[] | null

Defined in: graphty-element/src/managers/DataManager.ts:1523

Remove a node AND every edge attached to it, as one undoable step.

The cascade is what the name says, and it used to be missing: the store side already tombstoned the incident edges, but their render objects survived with their meshes, their place in the layout engine's own lists and a hard reference to the disposed Node. Three things followed from that, all of them visible to a reader. The edge kept drawing, because the frame loop walks the engine's list rather than this manager's. The edge became permanently visible and unfilterable, because the mask application forces an edge with no store row visible. And the removed Node stayed reachable through Edge.srcNode, so disposing it freed the Babylon resources and not the JavaScript retention -- which on a large graph is the removal leak that matters.

Parameters ​

nodeId ​

NodeIdType

Node identifier to remove

Returns ​

readonly string[] | null

the ids of the edges that went with it, or null when there was no such node


setEdges() ​

setEdges(edges, options?): void

Defined in: graphty-element/src/managers/DataManager.ts:1779

Replace every built edge with a new set, or leave the graph exactly as it was.

The ceiling is decided BEFORE anything is removed. Removing first and letting addEdges refuse would leave a host that assigned too many edges with its old edges gone and none of the new ones held, which is neither the graph it had nor the one it asked for. The new batch is counted against an emptied graph, since the old edges are what it replaces; a pending edge, whose endpoints have not arrived, survives the replace as it always has.

Parameters ​

edges ​

Record<string | number, unknown>[]

the edges the graph should hold afterwards

options? ​

AddEdgesOptions

the endpoint expressions and the repeat policy for this call

Returns ​

void

Throws ​

A GraphtyError with E_TOO_LARGE when the new set is past the ceiling, and whatever addEdges throws.


setGraphContext() ​

setGraphContext(context): void

Defined in: graphty-element/src/managers/DataManager.ts:1238

Set the GraphContext for creating nodes and edges

Parameters ​

context ​

GraphContext

GraphContext instance to use for node/edge creation

Returns ​

void


setLayoutEngine() ​

setLayoutEngine(engine): void

Defined in: graphty-element/src/managers/DataManager.ts:1251

Set the layout engine reference for managing node and edge positions

Parameters ​

engine ​

LayoutEngine | undefined

Layout engine instance or undefined to clear

Returns ​

void


setNodes() ​

setNodes(nodes, idPath?): void

Defined in: graphty-element/src/managers/DataManager.ts:1333

Replace every node with these, as one step: a node the records name again keeps its row and its edges, and one they no longer name goes, with its edges. A set past the render ceiling is refused with E_TOO_LARGE and the step rolls back, so the graph keeps the nodes it had.

Parameters ​

nodes ​

Record<string | number, unknown>[]

the nodes the graph should hold afterwards

idPath? ​

string

JMESPath expression to extract the id; the configured node id path when unset

Returns ​

void


sliceProblems() ​

sliceProblems(slice): string[]

Defined in: graphty-element/src/managers/DataManager.ts:672

Strict state: the drawn maps keyed exactly like the graph slice. An edge the slice holds may instead be waiting for an endpoint that has not arrived.

Parameters ​

slice ​

GraphSlice

The slice the last pass derived.

Returns ​

string[]

One sentence per problem; empty when there is none.


startLabelAnimations() ​

startLabelAnimations(): void

Defined in: graphty-element/src/managers/DataManager.ts:1943

Start label animations for all nodes Called when layout has settled

Returns ​

void


supersedeLoads() ​

supersedeLoads(): void

Defined in: graphty-element/src/managers/DataManager.ts:1912

Abandon every load in flight: each rejects with E_SUPERSEDED, its step rolled back, and adds nothing. The element's clearData calls this, so a load finishing after the graph was closed does not bring its data back.

Returns ​

void


throwIfSuperseded() ​

throwIfSuperseded(generation, type): void

Defined in: graphty-element/src/managers/DataManager.ts:1924

Throw E_SUPERSEDED when a load reserved at generation has been overtaken.

Parameters ​

generation ​

number

What beginLoad returned for the load

type ​

string

The load's format, for the message

Returns ​

void


undirected() ​

undirected(snapshot): DerivedGraph

Defined in: graphty-element/src/managers/DataManager.ts:517

The undirected view of a snapshot, built once per snapshot and cached.

Parameters ​

snapshot ​

GraphSnapshot

a snapshot this manager produced

Returns ​

DerivedGraph

the derived graph, including the edge remap an edge-result adapter needs


updateStyles() ​

updateStyles(styles): void

Defined in: graphty-element/src/managers/DataManager.ts:1230

Update the configuration document reference when it changes

Parameters ​

styles ​

Styles

New configuration document to read from

Returns ​

void