Skip to content

Migrating to 0.11 ​

kgdiff 0.11 runs every computation in the browser and in Node on kgdiff's Rust engine compiled to WebAssembly, @kgdiff/compute: the same engine, and the same bytes, as the kgdiff CLI. Compiling WebAssembly is asynchronous, so the functions that compute return promises. 0.11 is the last release with breaking changes; 1.0 is 0.11 after a stable period (RFC 0002).

The functions that became asynchronous ​

Package0.100.11
@kgdiff/corediff(before, after, options?): DiffResultdiff(before, after, options?): Promise<DiffResult>
@kgdiff/corematchNodes(before, after, options?): MatchResultmatchNodes(before, after, options?): Promise<MatchResult>
@kgdiff/coretextSimilarity(a, b): numbertextSimilarity(a, b): Promise<number>
@kgdiff/formatsparseGraph(text, options?): ParseResultparseGraph(text, options?): Promise<ParseResult> (deprecated)
@kgdiff/formatsdetectFormat(text, filename?): FormatIddetectFormat(text, filename?): Promise<FormatId> (deprecated)
@kgdiff/formatscompactIri(iri, prefixes?): stringcompactIri(iri, prefixes?): Promise<string> (deprecated)
@kgdiff/layoutlayoutDiff(diff, options?): LayoutResultlayoutDiff(diff, options?): Promise<LayoutResult>
@kgdiff/layoutextendLayout(diff, layout, nodes, options?): LayoutResultextendLayout(diff, layout, nodes, options?): Promise<LayoutResult>

New in @kgdiff/layout, asynchronous too: layoutGraph(graph, options?), layoutDiffOn(diff, previous, options?) and layoutAfter(diff, layout, options?), the layouts that keep one map across versions (Embed kgdiff in a Rust application explains them; the TypeScript functions are the same).

log2 of @kgdiff/core is removed, with the TypeScript engine that used it; use Math.log2. @kgdiff/formats keeps FORMAT_EXTENSIONS (synchronous) and its types; its TypeScript parsers are removed, and its three functions run kgdiff's parsers (above).

Two ways to migrate ​

Await the call. Put await in front of each function of the table, in an async function or at the top level of a module:

ts
import { diff } from '@kgdiff/core';
import { layoutDiff } from '@kgdiff/layout';

const result = await diff(before, after, options); // was: diff(before, after, options)
const layout = await layoutDiff(result);

The first call loads and compiles the module it needs (compute.wasm for diff, layout.wasm for the layouts, formats.wasm for the parsers); later calls only run it.

Initialize once, then call synchronously. For a loop over many pairs, or code that must stay synchronous, await init() of @kgdiff/compute once and use its *Sync functions, which return the same objects:

ts
import { init, diffSync, layoutDiffSync, parseGraphSync } from '@kgdiff/compute';

await init(); // all three modules; init({ modules: ['compute'] }) for some only
const result = diffSync(before, after, options);
const layout = layoutDiffSync(result);

A search and replace that finds the calls: \b(diff|matchNodes|textSimilarity|parseGraph|detectFormat|compactIri|layoutDiff|extendLayout)\(; TypeScript then reports every place where a Promise is used as the value it resolves to.

Workers ​

Initialize in the worker that computes: each worker compiles its own modules (await init(), or the first asynchronous call). Pass the kgdiff.diff document between threads, as JSON text (json.diff of @kgdiff/compute, or JSON.stringify(diffToJSON(result))), not DiffResult objects, which hold maps and closures; read it on the other side with diffFromJSON, which is synchronous.

Bundlers and serving the .wasm files ​

@kgdiff/compute locates its modules with new URL('../wasm/<module>.wasm', import.meta.url). Vite, webpack 5, Rollup and Node 22 or later resolve it and copy the files as assets; esbuild needs the file loader for .wasm. Serve them as application/wasm. To serve them from elsewhere, pass their URLs or bytes: init({ sources: { compute: url } }). They are exported as @kgdiff/compute/compute.wasm, /formats.wasm and /layout.wasm.

A bundle that only reads diffs (diffFromJSON, layoutFromJSON, the viewer showing a report) contains no WebAssembly: the packages import @kgdiff/compute with a dynamic import() only when a computing function is called.

Content Security Policy ​

Compiling WebAssembly needs 'wasm-unsafe-eval' in script-src, for example Content-Security-Policy: script-src 'self' 'wasm-unsafe-eval'. A page showing a report with its layout does not compile anything; expanding a collapsed group of a report laid out around its changes does.

Layouts ​

Positions are computed by kgdiff's Rust layout engine, the one the CLI has used since 0.7. For the same input and options they are the positions 0.10's TypeScript engine computed: through 0.10 the conformance corpus checked on every case that the two engines agreed, and RFC 0002 records the same on synthetic graphs of 1,000 to 10,000 nodes, so a stored layout needs no recomputing. What changes is the document: layouts written since 0.11 are layout documents of version 2, with a generator; layoutFromJSON reads versions 1 and 2. Positions stored by 0.10 stay valid pins (pinned, layoutDiffOn). Graphs whose nodes carry a position are still laid out anchored on them: diff keeps the positions of its graphs on the nodes of the result.

@kgdiff/formats is deprecated ​

Its functions are asynchronous wrappers over the parsers of @kgdiff/compute now: use parseGraph, detectFormat and compactIri of @kgdiff/compute (or their *Sync forms after init()). Its types stay in @kgdiff/formats until the package is removed in 2.0. @kgdiff/cli is no longer a JavaScript library since 0.10: use @kgdiff/compute for the computations, the kgdiff CLI or the kgdiff-report crate for reports.

The viewer ​

<kgdiff-viewer>'s API is unchanged. load() parses, diffs and lays out on @kgdiff/compute. The standalone add-on kgdiff-viewer-compute.js carries the layout-only module inlined, so a single-file report expands collapsed groups offline; load() with graphs in the standalone viewer also needs @kgdiff/compute in the page's import map, as text sources need @kgdiff/formats.

What does not change ​

  • Every JSON document (graphs, diffs, packed diffs, deltas, layouts), every hash, hunk title, summary and delta is byte-identical to the CLI's, and to 0.10's except that layouts are written as version 2 documents (their positions are unchanged).
  • Error codes and messages: failures are KgdiffErrors with the same code.
  • The read path stays synchronous and needs no WebAssembly: normalize, isKGraph, graphToJSON, hashGraph, diffFromJSON, diffToJSON, packDiffJSON, unpackDiffJSON, applyDelta, invertDelta, formatHunk, facetLabel, predicateLabel, resolveOptions, layoutFromJSON, layoutToJSON, nodeRadius and estimateLabelWidth.
  • The scene, renderer, viewer and G6 packages.

Support ​

0.x releases before 0.11 receive security fixes for six months after 0.11 is published.

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