Use the engine in code
The CLI and the viewer are built from packages you can use directly:
| Package | What it does |
|---|---|
@kgdiff/core | The diff engine: normalize, match, classify, hunks, invertible deltas. No dependencies. |
@kgdiff/formats | Parsers from RDF, OBO and JSON graph formats into kgdiff graphs. |
@kgdiff/layout | A deterministic, label-aware layout that both versions share. |
@kgdiff/scene | Turns a diff into visual primitives: design tokens, encodings, replay timeline. |
@kgdiff/renderer | A Canvas 2D renderer: camera, level of detail, labels, picking, minimap. |
@kgdiff/viewer | The <kgdiff-viewer> web component. |
@kgdiff/g6 | Draws kgdiff scenes in AntV G6 v5 graphs. |
@kgdiff/cli | The command-line tool, kgdiff. |
All of them are ESM only and run in Node.js 22 or later and in evergreen browsers. Core and formats are synchronous and do no I/O: you pass text and objects in and get objects out.
Diff two graphs
npm install @kgdiff/core @kgdiff/formatsimport { readFile } from 'node:fs/promises';
import { diff, formatHunk } from '@kgdiff/core';
import { parseGraph } from '@kgdiff/formats';
const read = async (file: string) => parseGraph(await readFile(file, 'utf8'), { filename: file });
const before = await read('before.obo');
const after = await read('after.obo');
const result = diff(before.graph, after.graph, { match: { strategy: 'id' } });
console.log(result.stats.nodes); // { added: 5, removed: 0, modified: 23, unchanged: 156 }
for (const hunk of result.hunks) {
console.log(formatHunk(hunk, result).title);
}
// Merged “juvenile diabetes” and “insulin-dependent diabetes mellitus” into “type 1 diabetes mellitus”
// Merged “heart attack” into “myocardial infarction”
// …parseGraph(text, { filename })detects the format and returns thegraphtogether with theformatit read andwarningsabout recoverable oddities (merged named graphs, ignored fields). Passformatto skip detection.diff(before, after, options)takes parsed graphs or plain{ nodes, edges }objects in kgdiff JSON.formatHunk(hunk, result, locale)words a hunk as the CLI does, as atitleand asubtitle, inen(default) orzh-CN.
Options
diff(before.graph, after.graph, {
match: { strategy: 'auto', threshold: 0.82 }, // 'id' | 'auto' | 'similarity'
hierarchyPredicates: ['is_a'], // replaces DEFAULT_HIERARCHY_PREDICATES
ignoreAttrs: ['creation_date'],
detectMerges: true,
detectSplits: true,
edgeIdentity: 'structure', // or 'id'
});They are the CLI's --match, --threshold, --hierarchy, --ignore-attr, --no-merges, --no-splits and --edge-identity.
The result
| Field | Contents |
|---|---|
stats | Nodes and edges added, removed, modified and unchanged; hunks per kind; per type; identical |
hunks | The review units in review order, each with a kind: add, remove, modify, merge, split or relations |
nodes, edges | The union of both versions: every element with its status and what changed about it |
matches, suggestions | Node pairs found by replacement hints or similarity, and near misses |
delta | The invertible patch from before to after |
before, after | The two graphs, normalized |
Apply, invert and verify deltas
import { applyDelta, hashGraph, invertDelta, normalize } from '@kgdiff/core';
const patched = applyDelta(result.before, result.delta);
hashGraph(patched) === hashGraph(result.after); // true
const restored = applyDelta(patched, invertDelta(result.delta));
hashGraph(restored) === hashGraph(result.before); // trueapplyDelta works on normalized graphs: result.before, or a graph you parse again and pass through normalize(). It checks the content hash of its input first and throws a KgdiffError with code delta-base-mismatch when the graph is not the delta's base:
const delta = JSON.parse(await readFile('patch.json', 'utf8'));
const graph = normalize((await read('before.obo')).graph);
const updated = applyDelta(graph, delta);With { strict: false, onSkip } it applies every operation whose precondition holds and reports the others to onSkip. Deltas covers the same from the command line.
Save and load
diffToJSON(result)anddiffFromJSON(json): the complete diff as JSON, as the CLI's--jsonwrites it.graphToJSON(graph): a graph in kgdiff JSON, whichparseGraphreads back losslessly.result.deltais plain JSON already; Delta format specifies it.
Each of the three names its format (kgdiff.diff, kgdiff.graph, kgdiff.delta) and version, and has a JSON Schema in @kgdiff/core/schema/ (Formats). Readers reject a version they do not support with KgdiffError('unsupported-version').
Lay out and draw
The viewer does all of this for you. To build your own interface:
layoutDiff(result)from@kgdiff/layoutplaces the union of both graphs, deterministically.buildScene(result, layout)from@kgdiff/sceneturns the diff into visual primitives in the kgdiff visual language.@kgdiff/rendererdraws a scene on a canvas;@kgdiff/g6draws it in an AntV G6 graph.
The package READMEs show each step: layout, scene, renderer and g6.
Errors
Every API throws KgdiffError for bad input, with a code (invalid-input, delta-base-mismatch, delta-conflict, unsupported-version) and details.