Skip to content
D
Documentation

Serialization

reference
7 min readUpdated

Import these from @grafloria/engine.

Functions

beginIncrementalCapture

Convenience: start watching a diagram for incremental commits.

ts
function beginIncrementalCapture(diagram: DiagramModel): IncrementalCapture

canonicalStringify

Deterministic JSON: object keys sorted recursively so the same logical document always hashes identically, regardless of property insertion order. (Arrays keep their order — element order IS meaning in nodes/links.)

ts
function canonicalStringify(value: unknown): string

checksumOf

Integrity checksum of a document's canonical JSON.

ts
function checksumOf(document: SerializedDiagram): string

deserializeSubgraphInto

Install a subgraph into a live diagram through the unified restore path.

This is a USER MUTATION (unlike document load): entities are installed via restore* so the normal node

/link
events fire for undo and rendering, and port registries are reconciled at the end.

ts
function deserializeSubgraphInto(
  diagram: DiagramModel,
  subgraph: SerializedSubgraph,
  options: DeserializeSubgraphOptions = {}
): DeserializedSubgraph

exportDiagramText

Export a diagram as Mermaid-compatible text. With lossless (default) the exact document travels in a %%grafloria:document comment, so importing this text reproduces the diagram byte-for-byte (serialize-equality), while the body stays renderable by any Mermaid consumer.

ts
function exportDiagramText(
  diagram: DiagramModel,
  options: ExportTextOptions = {}
): string

getDiagramMigrations

Registered migrations, in order (primarily for tests/diagnostics).

ts
function getDiagramMigrations(): readonly DiagramMigration[]

importDiagramText

Import diagram text. Sidecar-carrying text loads losslessly through the unified JSON path; pure Mermaid text parses through the DSL (best effort — the documented lossy boundary). See ImportTextOptions.prefer for who wins when the body was hand-edited after export.

ts
function importDiagramText(
  text: string,
  options: ImportTextOptions = {}
): ImportTextResult

isDiagramDocumentEnvelope

ts
function isDiagramDocumentEnvelope(value: unknown): value is DiagramDocumentEnvelope

registerDiagramMigration

Register a document migration. Apps embedding the engine can register their own steps when they extend the document shape.

ts
function registerDiagramMigration(migration: DiagramMigration): void

runDiagramMigrations

Upgrade a serialized document to DIAGRAM_SCHEMA_VERSION.

  • A document with no schemaVersion is treated as v1.
  • A document NEWER than the runtime throws (loading it would silently drop or mangle data written by a newer app — the caller must upgrade instead).
  • A missing step in the chain throws (a partial upgrade is worse than none).
ts
function runDiagramMigrations(
  data: SerializedDiagram,
  migrations: readonly DiagramMigration[] = registry
): SerializedDiagram

Parameters

  • migrations: override the global registry (tests / embedders).

serializeSubgraph

ts
function serializeSubgraph(
  diagram: DiagramModel,
  selection: SubgraphSelection
): SerializedSubgraph

stripGrafloriaSidecar

The body without any %%grafloria sidecar lines (what a human reads/edits).

ts
function stripGrafloriaSidecar(text: string): string

unwrapDiagramDocument

Accepts an enveloped document OR a legacy flat SerializedDiagram and returns the inner document. When the envelope carries a checksum it is verified (throws DiagramChecksumError on mismatch — an integrity failure must never load silently).

ts
function unwrapDiagramDocument(
  input: SerializedDiagram | DiagramDocumentEnvelope
): UnwrapResult

validateSerializedDiagram

ts
function validateSerializedDiagram(data: SerializedDiagram): DiagramValidationReport

wrapDiagramDocument

ts
function wrapDiagramDocument(
  document: SerializedDiagram,
  options: WrapOptions = {}
): DiagramDocumentEnvelope

Classes

DiagramChecksumError

ts
class DiagramChecksumError extends Error

Methods

  • constructor( public readonly expected: string, public readonly actual: string )

DiagramSerializer

ts
class DiagramSerializer

Methods

  • serialize(diagram: DiagramModel): SerializedDiagram — Serialize diagram to plain object
  • serializeEnvelope(diagram: DiagramModel, options?: WrapOptions): DiagramDocumentEnvelope — Serialize wrapped in the portable document envelope (generator identity, createdAt, integrity checksum). The envelope is the recommended shape for NEW persistence; deserialize() accepts both it and the legacy flat form.
  • deserialize( data: SerializedDiagram | DiagramDocumentEnvelope, options?: DiagramLoadOptions ): DiagramModel — Deserialize diagram from plain object — accepts the enveloped document, the legacy Serializer flat form, or a raw DiagramModel.serialize payload. Envelope checksums are verified (mismatch throws — corruption must never load silently).

DiagramValidationError

Thrown by strict-mode loads when the document has integrity errors.

ts
class DiagramValidationError extends Error

Methods

  • constructor(public readonly report: DiagramValidationReport)

IncrementalCapture

Watches a live diagram and produces a DiagramIncremental per commit(). Continues capturing after commit (autosave-loop friendly) until stop().

ts
class IncrementalCapture

Methods

  • constructor(private readonly diagram: DiagramModel)
  • hasChanges(): boolean — Anything captured since the window began?
  • commit(): DiagramIncremental | null — Drain the window into a patch (null when nothing changed) and keep capturing. Added/modified entities are serialized LIVE at commit time, so intra-window churn costs nothing.
  • stop(): void — Unsubscribe everything. The capture cannot be reused afterwards.

Constants

DIAGRAM_ENVELOPE_FORMAT

ts
const DIAGRAM_ENVELOPE_FORMAT: "grafloria-diagram"

DIAGRAM_ENVELOPE_VERSION

ts
const DIAGRAM_ENVELOPE_VERSION: 1

DIAGRAM_SCHEMA_VERSION

The schema version this engine writes.

History:

  • 1: implicit — documents written before schemaVersion existed (fromJSON bypassed the wired restore path; groups could carry a runtime 'diagram' metadata key).
  • 2: documents written by the unified load/save path. Structurally identical to v1 except: schemaVersion is stamped, groups is always present, and runtime-only metadata is never serialized.
ts
const DIAGRAM_SCHEMA_VERSION: 3

GRAFLORIA_DOC_PREFIX

ts
const GRAFLORIA_DOC_PREFIX: "%%grafloria:document "

GRAFLORIA_HASH_PREFIX

ts
const GRAFLORIA_HASH_PREFIX: "%%grafloria:body-hash "

INCREMENTAL_FORMAT

ts
const INCREMENTAL_FORMAT: "grafloria-incremental"

SUBGRAPH_FORMAT

ts
const SUBGRAPH_FORMAT: "grafloria-subgraph"

Interfaces

DeserializedSubgraph

ts
interface DeserializedSubgraph

Properties

NameTypeDefaultDescription
nodesNodeModel[]
linksLinkModel[]
groupsGroupModel[]
idMapMap<string, string>oldId -> newId for nodes and groups (identity map when remapIds=false).
portIdMapMap<string, string>oldPortId -> newPortId (identity when remapIds=false).

DeserializeSubgraphOptions

ts
interface DeserializeSubgraphOptions

Properties

NameTypeDefaultDescription
remapIds?booleanMint fresh ids/uuids for every entity (default true). Turn off only when importing into a diagram KNOWN not to contain the ids (e.g. template instantiation into an empty document where stable ids are wanted).
offset?PointTranslate all node/group positions by this delta (paste-at-cursor).

DiagramDocumentEnvelope

ts
interface DiagramDocumentEnvelope

Properties

NameTypeDefaultDescription
formattypeof DIAGRAM_ENVELOPE_FORMAT
envelopeVersionnumber
generatorstringWriter identity, e.g. '@grafloria/engine'.
generatorVersionstringWriter version — the document schemaVersion the writer targets.
createdAtstringISO-8601 creation timestamp.
checksum?stringFNV-1a hash of the canonical JSON of document (see canonicalStringify).
documentSerializedDiagram

DiagramIncremental

ts
interface DiagramIncremental

Properties

NameTypeDefaultDescription
formattypeof INCREMENTAL_FORMAT
schemaVersionnumber
baseVersionnumberdiagram.version at the moment the window began (ordering hint).
targetVersionnumberdiagram.version at commit — apply converges the replica's counter to it.
added{ nodes: SerializedNode[]; links: SerializedLink[]; groups: SerializedGroup[]; strokes?: SerializedSt...
removed{ nodes: string[]; links: string[]; groups: string[]; strokes?: string[] }
modified{ nodes: SerializedNode[]; links: SerializedLink[]; groups: SerializedGroup[]; strokes?: SerializedSt...
diagram?{ name?: string; viewport?: { x: number; y: number; width: number; height: number; zoom: number }; metada...

DiagramMigration

ts
interface DiagramMigration

Properties

NameTypeDefaultDescription
fromnumberSource schema version this migration upgrades FROM.
tonumberTarget schema version (must be from + 1 — migrations run stepwise).
descriptionstringHuman-readable summary shown in errors/logs.

Members

  • migrate(data: SerializedDiagram): SerializedDiagram — Pure upgrade: receives the document, returns the upgraded document.

DiagramValidationFinding

ts
interface DiagramValidationFinding

Properties

NameTypeDefaultDescription
severity'error' | 'warning'
codestringStable machine-readable code (e.g. 'duplicate-id', 'dangling-link-endpoint').
messagestring
entityId?stringId of the entity the finding is about, when applicable.

DiagramValidationReport

ts
interface DiagramValidationReport

Properties

NameTypeDefaultDescription
okboolean
errorsDiagramValidationFinding[]
warningsDiagramValidationFinding[]

ExportTextOptions

ts
interface ExportTextOptions

Properties

NameTypeDefaultDescription
lossless?booleanAppend the lossless sidecar (default true). Without it the text is pure Mermaid and imports are best-effort DSL parses (the lossy boundary).
positions?booleanWrite every node's and zone's exact position and size as %%grafloria:at id x,y WxH (default false). The sidecar already carries them; this is for a READABLE body that redraws the same picture on its own — the way an AI-drawn diagram is written for Grafloria.

ImportTextOptions

ts
interface ImportTextOptions extends DiagramLoadOptions

Properties

NameTypeDefaultDescription
prefer?'auto' | 'sidecar' | 'text'Which source wins when both exist: - 'auto' (default): the sidecar wins UNLESS the body hash shows the body was hand-edited after export — then the edited body wins. - 'sidecar': always load the sidecar document (ignore body edits). - 'text': always parse the body text (ignore the sidecar).

ImportTextResult

ts
interface ImportTextResult

Properties

NameTypeDefaultDescription
diagramDiagramModel
source'sidecar' | 'text'Which source actually produced the model.
bodyEditedbooleanTrue when a sidecar existed but the body had been hand-edited.
sidecarMerged?booleanTrue when a hand-edited body was applied ON TOP of the sidecar document — the edit took, and everything the grammar cannot express (positions, styles, ports, groups, viewport) survived from the sidecar.
sidecarInvalid?booleanTrue when a sidecar line existed but its JSON would not parse.
unsupported?stringSet to the diagram-type name when the body is a Mermaid type we recognise but do not yet parse (sequenceDiagram, gantt, pie, …). The diagram is empty rather than a garbage flowchart.

SerializedDiagramData

ts
interface SerializedDiagram extends Omit<DiagramSerializedData, 'version'>

Properties

NameTypeDefaultDescription
versionstring
diagramVersion?number
mode?DiagramMode

SerializedSubgraph

ts
interface SerializedSubgraph

Properties

NameTypeDefaultDescription
formattypeof SUBGRAPH_FORMAT
schemaVersionnumber
sourceDiagramId?string
nodesSerializedNode[]
linksSerializedLink[]
groupsSerializedGroup[]
boundaryLinksArray<{ linkId: string; insideEnd: 'source' | 'target' }>Links that crossed the selection boundary and were therefore excluded.

SubgraphSelection

ts
interface SubgraphSelection

Properties

NameTypeDefaultDescription
nodeIdsIterable<string>
groupIds?Iterable<string>

UnwrapResult

ts
interface UnwrapResult

Properties

NameTypeDefaultDescription
documentSerializedDiagram
envelope?DiagramDocumentEnvelopePresent when the input was enveloped.

WrapOptions

ts
interface WrapOptions

Properties

NameTypeDefaultDescription
generator?string
generatorVersion?string
checksum?booleanInclude an integrity checksum (default true).
createdAt?stringTimestamp override (tests / deterministic exports).

Was this page helpful?