Data Format

ArrowCrab reads a JSON object matching GraphData. The wire format contains flat-packed numeric records and one shared string table.

Top-level object

interface GraphData {
  subgraphs: number[];
  nodes:     number[];
  edges:     number[];
  strings:   string[];
}

Each packed array must have a length divisible by its record width. An empty strings[] is valid when no node records reference it; every node label and color otherwise requires a valid string-table index.

Node records

Every three consecutive values in nodes[] form one record.

OffsetFieldMeaning
i + 0idSafe-integer node ID
i + 1labelInteger index into strings[]
i + 2colorInteger index into strings[]; interpreted as a CSS color by the renderer

Edge records

Every four consecutive values in edges[] form one record.

OffsetFieldMeaning
i + 0sourceIdSource node ID
i + 1sourcePortNon-negative source output-port index below 4,294,967,295
i + 2targetIdTarget node ID
i + 3targetPortNon-negative target input-port index below 4,294,967,295

Subgraph records

Every three consecutive values in subgraphs[] form one record.

OffsetFieldMeaning
i + 0nodeIdCompound node that owns the scope
i + 1inIdScope entry boundary node
i + 2outIdScope exit boundary node

Validation rules

The validator applies these rules before constructing the resolved model:

The increasing-ID and edge-direction rules establish the topological order used by the viewer. The wire format carries scope boundaries rather than an explicit child list; scope membership is resolved from those intervals.

Scope semantics

A node belongs to the innermost scope whose open interval strictly contains its ID: inId < nodeId < outId. Entry and exit boundary nodes are excluded from a scope's nodeIds, and the compound owner is outside the closed interval. Boundary ports still contribute to the compound frame's port counts.

An edge belongs to the innermost scope for which both endpoints are inside, or one endpoint is inside and the other is that scope's boundary.

Port-count inference

Port counts are not stored in the wire format. For each node, the input count is the maximum referenced targetPort + 1, and the output count is the maximum referenced sourcePort + 1. The same inference is used for entry and exit boundary nodes when the renderer draws ports on a compound frame.