Skip to content

Use the engine in code ​

The CLI and the viewer are built from packages you can use directly:

PackageWhat it does
@kgdiff/coreThe diff engine: normalize, match, classify, hunks, invertible deltas. No dependencies.
@kgdiff/formatsParsers from RDF, OBO and JSON graph formats into kgdiff graphs.
@kgdiff/layoutA deterministic, label-aware layout that both versions share.
@kgdiff/sceneTurns a diff into visual primitives: design tokens, encodings, replay timeline.
@kgdiff/rendererA Canvas 2D renderer: camera, level of detail, labels, picking, minimap.
@kgdiff/viewerThe <kgdiff-viewer> web component.
@kgdiff/g6Draws kgdiff scenes in AntV G6 v5 graphs.
@kgdiff/cliThe 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 ​

sh
npm install @kgdiff/core @kgdiff/formats
ts
import { 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 the graph together with the format it read and warnings about recoverable oddities (merged named graphs, ignored fields). Pass format to 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 a title and a subtitle, in en (default) or zh-CN.

Options ​

ts
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 ​

FieldContents
statsNodes and edges added, removed, modified and unchanged; hunks per kind; per type; identical
hunksThe review units in review order, each with a kind: add, remove, modify, merge, split or relations
nodes, edgesThe union of both versions: every element with its status and what changed about it
matches, suggestionsNode pairs found by replacement hints or similarity, and near misses
deltaThe invertible patch from before to after
before, afterThe two graphs, normalized

Apply, invert and verify deltas ​

ts
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); // true

applyDelta 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:

ts
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) and diffFromJSON(json): the complete diff as JSON, as the CLI's --json writes it.
  • graphToJSON(graph): a graph in kgdiff JSON, which parseGraph reads back losslessly.
  • result.delta is 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/layout places the union of both graphs, deterministically.
  • buildScene(result, layout) from @kgdiff/scene turns the diff into visual primitives in the kgdiff visual language.
  • @kgdiff/renderer draws a scene on a canvas; @kgdiff/g6 draws 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.

Released under the MIT License. Example data CC0-1.0.