Repository navigation
Releases
v0.7.0
Minor changes
-
cca58f9: JSON input with a lone surrogate escape is rejected.
JSON.parseaccepts"\ud800", half of a UTF-16 surrogate pair escaped without its other half, and kgdiff used to carry it through ids and labels into its diffs and deltas; such text is not Unicode, and the Rust implementation (RFC 0002) cannot hold it.parseGraphnow throwsinvalid-inputfor one in JSON and JSON-LD, located at the escape (Invalid JSON on line 6, column 35: the escape \ud83d is a lone surrogate (half of a UTF-16 pair), which kgdiff does not accept.), and so does a JSON-LD context text adocumentLoaderreturns;kgdiff applyrefuses a delta that has one. Escaped surrogate pairs ("😀") read as before.@kgdiff/cli·@kgdiff/formats -
d01c4e0: Matching gives the same result in every JavaScript engine. Its neighbour and key weights (
1 / log₂(2 + degree),1 / log₂(1 + postings)) now use alog₂built from exactly rounded operations only, the one the layout already used, instead ofMath.log2, which engines may approximate differently: V8 ports FreeBSD'se_log2.c, while an engine that calls the platform's C library gets that library's rounding (Apple's differs from V8 on 439 of the integers up to 2,000,000). A diff computed in a browser, as the playground does, now equals the CLI's whatever the engine. The newlog₂differs from V8's by one unit in the last place on about 2% of integers; no match, suggestion or score changes in the conformance corpus, the example datasets or 144 synthetic diffs, but a score that sits exactly on a rounding boundary or a tie can move. The function is exported aslog2, and@kgdiff/layoutuses it from core.@kgdiff/core -
7586808: Reports open the same way whatever Unicode version the browser has.
diffFromJSONtakes the labels, aliases and types of a document's snapshots as written instead of composing them to NFC again with the runtime's ICU, and the content hashes in the delta check that text: a report made with Node 24 (Unicode 17) no longer fails to open in a browser with older tables when a label holds a character whose composition changed in between, and a snapshot whose text was edited or is not in NFC is still rejected. To restore a label it left out without Unicode tables either, kgdiff JSON (diffs, graphs and deltas) now leaves an edge label out only when it equalspredicateLabel(predicate)before its NFC step; a predicate whose readable part is written decomposed keeps its label in the document. A diff written by an earlier version for such a predicate, with that label left out, no longer opens: write it again.normalizecomposes API input to NFC as before.@kgdiff/core·@kgdiff/viewer -
cca58f9: Numbers beyond the range of a double in JSON graphs are kept instead of lost. JSON may write
1e400, whichJSON.parsereads asInfinity; the JSON reader used to drop such a number without a word (an attribute ormetamember disappeared, an array item becamenull), so a diff could report nothing for a value that changed, or a change that did not happen. It now keeps the value as the string"Infinity"or"-Infinity", asnormalizekeeps a non-finite number an API caller passes, and reports how many with ajson-non-finitewarning (ParseWarningCode), in every JSON dialect.@kgdiff/formats -
9ebb278:
layoutFromJSONreads version 2 of the layout document, which kgdiff's Rust layout engine will write. Version 2 is version 1's members plusgenerator, a non-empty string such as"kgdiff-layout/1"that names the engine generation that computed the positions. It becomes the new optionalLayoutResult.generator. Positions read from either version are valid pins for anchored layouts andextendLayout. A version 2 document without ageneratoris rejected (invalid-input), and versions other than 1 and 2 are rejected as before (unsupported-version).layoutToJSONstill writes version 1, so a layout read from version 2 is written back as version 1 without itsgenerator. Types:LayoutJSONis now the union of the documentslayoutFromJSONreads,LayoutJSONV1 | LayoutJSONV2, andlayoutToJSONreturnsLayoutJSONV1. Code that relied onLayoutJSON['version']being1should useLayoutJSONV1. ThroughReportPayload.layout(aLayoutJSON), the viewer opens reports whose layout is version 2.@kgdiff/layout -
e510258: Report payloads may leave their layout out.
ReportPayload.layoutis optional: a payload without one (the member left out) is laid out in the browser when the viewer reads it, withlayoutDiff's default options, so an application that computed a diff itself can show it by passing itsdiff.v1JSON as a payload. The ESM build imports the compute module on demand for it; the standalone viewer takes it from the@kgdiff/viewer/standalone/computeadd-on (globalThis.KgdiffCompute), and without the add-on shows an error that says what is missing instead of failing to read the layout. The diff engine never runs for a payload, and payloads with a layout open exactly as before, without loading the compute module. Alayoutthat is present but not an object (null, for instance) is rejected. The CLI keeps writing the layout into its reports.@kgdiff/viewer
Every package is at 0.7.0: @kgdiff/cli · @kgdiff/core · @kgdiff/formats · @kgdiff/layout · @kgdiff/scene · @kgdiff/renderer · @kgdiff/viewer · @kgdiff/g6.
GitHub Action: uses: iiwish/kgdiff@v0.7.0.
v0.6.0
Minor changes
-
325afea: The viewer's layout can be arranged, and its parts placed one by one. Every region of
<kgdiff-viewer>(the change list, the detail panel, the search, the filters, the view dock, the replay transport, the legend, the zoom buttons, the minimap, the title, the actions) is open, collapsed or hidden through one attribute,panels="detail:collapsed minimap:hidden", or the propertyviewer.panels = { detail: 'collapsed' }(reading it gives the page's declaration back; the new read-onlyviewer.layouthas every region's state and the sizes);sidebar="closed",header="compact"andchrome="none"keep working as presets of it. The detail panel can now be collapsed to a bar of its own, the sidebar's width and the split between the change list and the details can be dragged (or moved with the arrow keys), andkgdiff-layoutreports every change. The viewer remembers what the reader changes (panels, sizes, theme, palette, language) inlocalStoragefor the next visit, unless the page sets them orpersist="none";persist-keygives an app on a shared origin a store of its own.shortcuts="scoped|global|none"says where keyboard shortcuts apply (global-shortcutsstill works) andkeymap="x:none r:review"rebinds single keys (a letter in both cases, so Shift+J / K now step through the changes too); thekgdiff-*events are typed on every element, the document and the window (KgdiffEventMap;KgdiffViewerEventMapstays as a deprecated alias);refreshTheme()redraws the canvas after the page changed--kgdiff-*tokens, which are now documented, public CSS custom properties. Four of them are renamed to whole words before they become public:--kgdiff-bg→--kgdiff-background,--kgdiff-mono→--kgdiff-font-mono,--kgdiff-text-2→--kgdiff-text-secondaryand--kgdiff-text-3→--kgdiff-text-muted; a page that set the old names sets the new ones instead. New, experimental elements compose a layout of your own:<kgdiff-root>holds the session, and<kgdiff-stage>(with five slots for your content over the canvas, which the graph is framed clear of, andpanelsfor its own chrome: dock, transport, legend, zoom, minimap),<kgdiff-change-list>,<kgdiff-change-detail>,<kgdiff-search>and<kgdiff-filters>show its parts, inside it or anywhere on the page withfor="<root id>".@kgdiff/viewer/elements/<name>defines a single element, and@kgdiff/viewer/panelsis the panels syntax (no DOM). The CLI's--panelssets a report's initial panel states; reports now carry their theme and language in the payload only, so a reader's earlier choice wins over them.@kgdiff/cli·@kgdiff/viewer -
8b0538c: Several views of one diff, and the canvas chrome as elements (experimental). A
<kgdiff-root>now draws every<kgdiff-stage>bound to it, each with its own canvas, camera, zoom buttons and minimap, all sharing the selection, the hover, the filters, the search and the replay;version="before"orversion="after"pins a stage to one version, for before and after side by side. The view dock, the replay transport, the legend, the zoom buttons, the minimap, the title and the actions are elements of their own (each says which root it shows withrootElement):<kgdiff-view-dock>,<kgdiff-transport>,<kgdiff-legend>(variant="full|mini",open),<kgdiff-zoom>and<kgdiff-minimap>(bound to the nearest stage or to the onestage="<id>"names:stage,stageElement,rootElement; the minimap is in the custom state:state(overview)while the whole graph is in view),<kgdiff-title>and<kgdiff-actions>, each with a subpath@kgdiff/viewer/elements/<name>. Slotted into a stage or a<kgdiff-viewer>, one of them replaces the built-in region of its kind (in the viewer the title and the actions too) whilepanelskeeps applying to the rest; placed anywhere else it is no obstacle to fits and flights unless registered withregisterObstacle.<kgdiff-viewer>gets the stage's five slots: your content inslot="top-end"(andtop-start,bottom-start,bottom-center,bottom-end) goes over the canvas and the graph is framed clear of it. The shortcuts dialog lists the keys bound afterkeymap, and?in a composed root opens the dialog of its<kgdiff-actions>. Accessibility: the zoom toolbar, the appearance menu and the search's results have names, a<kgdiff-stage>is a named region rather than a main landmark, the end of a replay is announced once, and the page's content in the top corners comes first in the tab order.@kgdiff/viewer/elements/rootno longer carries the renderer (64 kB instead of 88 kB, min + gzip): each stage brings its own.@kgdiff/viewer -
69a0975: The viewer describes its elements for tools. The package ships a Custom Elements Manifest (
custom-elements.json, named by the newcustomElementsfield), generated from the sources: every element's attributes and the properties they reflect, its methods, the roots' events with theirdetailtypes, the slots, the--kgdiff-*CSS custom properties and<kgdiff-minimap>'s:state(overview), with each experimental part marked. Generated from it:vscode.html-custom-data.json, which VS Code reads throughhtml.customDatato complete and describe the tags and their attributes; and@kgdiff/viewer/react(experimental), React 19's JSX types for the elements, their properties and theironkgdiff-*events, enabled withimport type {} from '@kgdiff/viewer/react'(@types/reactis an optional peer dependency). The elements stay experimental in 0.6 and become stable in 0.7. The docs gain a reference of every element generated from the manifest, and tested recipes for React 19, Vue 3 and Svelte 5.@kgdiff/viewer
Every package is at 0.6.0: @kgdiff/cli · @kgdiff/core · @kgdiff/formats · @kgdiff/layout · @kgdiff/scene · @kgdiff/renderer · @kgdiff/viewer · @kgdiff/g6.
GitHub Action: uses: iiwish/kgdiff@v0.6.0.
v0.5.0
Minor changes
-
764535e: The terminal summary and the Markdown summary (so the GitHub Action's pull request comment too) list the possible matches: pairings kgdiff considered but did not apply, between a node listed as removed and one listed as added. Until now only the viewer's detail panel showed them, so "removed “Saudi Aramco”, added “Aramco”" gave no hint that the two may be one entity. Each line names both nodes (with their ids when the names are equal), the score, why it was not applied (below
--threshold, or too close to another candidate) and the label and neighbourhood evidence, and a closing line says how to pair one: a synonym, a replacement link or a lower threshold. At most 10 in the terminal (all with--limit 0) and 20 in Markdown;--jsonkeeps every suggestion.@kgdiff/cli -
3f6e7d8: Changes to definitions are flagged ⚑ for review first. A reworded definition can change what existing data means while the diff looks small, and it used to be folded into
~1 attributeamong the other edits. Now a line under the diffstat bar counts the hunks that change one (⚑ Review first: definition changed in 9 hunks, also in--stat), each of their rows says so before its summary (content · ⚑ definition · ~2 attributes), and the Markdown summary, so the GitHub Action's pull request comment too, lists them in a ⚑ Review first block before the phase groups. By default kgdiff watchesdef(OBO),definition(kgdiff JSON),IAO:0000115andskos:definition; the new--watch-attr <key>(repeatable) replaces that list, e.g.--watch-attr rdfs:commentfor schema.org-style vocabularies.TextContextandMarkdownOptionstake the same list aswatch, andDEFAULT_WATCH_ATTRSandwatchedChangesare exported.@kgdiff/cli -
6ed2b9c: HTML reports are smaller: they embed the diff packed (
packDiffJSON, formatkgdiff.diff.packed, schemaschema/diff-packed.v1.json), with its unchanged elements, which are most of a large diff, stored as tables of indices into one string table. For 100k nodes / 300k edges the embedded diff goes from 68.7 MB to 19.3 MB, 6.8 MB to 3.2 MB gzipped, and the report opens as fast.unpackDiffJSONgives theDiffJSONback byte for byte; the viewer reads packed and unpacked payloads (ReportPayload.diffis either), so older reports still open. The standalone viewer bundle budget goes from 100 kB to 105 kB (min + gzip) for the unpacking and the consistency checks ofdiffFromJSON.@kgdiff/cli·@kgdiff/core·@kgdiff/viewer -
f957a49:
diffFromJSONchecks that a diff document is consistent before it returns, and throwsinvalid-inputnaming the first contradiction: an element whose versions do not fit its status (an added node with a before id, a removed edge with an after snapshot), an unchanged element with facets or a hunk, a changed one outside every hunk, a repeated id, an edge whose endpoints are not union nodes or disagree with its snapshots, a hunk that lists elements that are not its own, delta ops owned by two hunks or out of range, hunks out of review order, a summary or stats that do not match the elements, or a delta whose base or target is another graph (by content hash). Until now such a document was read as it was, and a viewer could show wrong counts or export a delta for other graphs. Everything kgdiff writes passes.diffFromJSONis also about 10% faster on large diffs: edges are found by their key instead of by a canonical JSON fingerprint.schema/diff.v1.jsonstates the same rules per element (versions by status; facets and hunk by status).@kgdiff/core -
212c48c:
UnionNodeandUnionEdgeare now unions bystatus, so the types say which versions an element is in: anaddednode hasafterIdandafterand no before version, aremovedone onlybeforeIdandbefore, anunchangedormodifiedone both; the same for edges, where only edges in both versions can havesourceBefore,targetBeforeorpredicateBefore. Narrow onstatusto readbefore/afterwithout?.or casts (AddedUnionNode,RemovedUnionNode,MatchedUnionNodeand the edge counterparts name the cases). The objects themselves are unchanged; code that builds union elements by hand (test fixtures, mocks) now has to give each status the versions it implies.@kgdiff/core
Patch changes
-
86f4583: The bug report link of an unexpected error takes the platform and the Node.js version from
CliIO.runtimeinstead of readingprocessinmain, somainkeeps taking every side effect fromioand runs in hosts other than Node.js.nodeIO()fills it in, so the link is unchanged for thekgdiffcommand; a customiowithoutruntimegets a link that names only the CLI version.@kgdiff/cli -
088671c: Link unexpected CLI errors to a bug-report form prefilled with public version and runtime
metadata, without including error messages or input paths in the URL.@kgdiff/cli -
f02bc72: Similarity matching no longer pairs two same-named nodes on their label alone when their types conflict. With
--match similaritynothing is matched in the first round, so the neighbour evidence was missing for every pair and an equal label outweighed disjoint types: “Mercury” the planet (orbiting the Sun) and “Mercury” the element (with symbol Hg) were reported as one node whose type changed. Such a pair (both nodes typed, no type and no attribute in common, neighbours not matched yet) now waits for the next round, when propagation brings in the neighbours; the two Mercurys share none, so they are reported as removed and added with a possible match below the threshold, as--match autoalready did. A type change that the neighbours confirm is still matched, and nodes without edges are matched as before.@kgdiff/cli·@kgdiff/core -
638c617:
diffFromJSONis about 30% faster on large diffs, so HTML reports open about 20% sooner: a report of 100k nodes / 300k edges is ready in 1.0 s instead of 1.24 s in headless Chrome. Rebuilding the two graphs was most ofdiffFromJSON. The snapshotsdiffToJSONwrites are already canonical, so they are now read straight into graphs, and each union edge keeps the edge it was read into instead of looking it up again. A documentnormalizewould change (parallel edges, a repeated id, a dangling end, a hand-edited snapshot) is normalized as before, as fast as before. The result is the same, field for field and in the same order:diffToJSON(diffFromJSON(j))is stillj, byte for byte, the graph hashes are unchanged, and every consistency check still runs.@kgdiff/core·@kgdiff/viewer -
ed1c3c9: Normalized graphs take about 30% less memory: a graph of 100k nodes and 300k edges now keeps about 111 MB of heap instead of 157 MB. Structural edge keys were built by concatenation, which V8 stores as a rope of many small strings that stays allocated behind every key; they are now built as flat strings, which are also quicker to hash. Keys, diffs, deltas and their JSON are unchanged, byte for byte.
@kgdiff/core -
6d34966: The standalone viewer bundle, which every HTML report inlines, no longer carries the facet names
offacetLabel: they are kept apart from the hunk phrasebooks thatformatHunkreads, which
saves 0.2 kB (min + gzip). Behaviour is unchanged. The bundle's size ceiling goes from 105 kB to
130 kB, so a feature no longer has to raise it, and the HTML report overhead budget (without data
or compute add-on) from 120 kB to 135 kB gzip, so it still covers the bundle.@kgdiff/core·@kgdiff/viewer -
5286e02: The viewer's CSS custom properties are renamed from
--kg-*to--kgdiff-*. They are not documented API yet; a page that set one of the old names on<kgdiff-viewer>sets the new name instead, and the canvas now follows it too, not only the panels. Styles are one shared constructable stylesheet, parsed once for every viewer on the page, instead of<style>elements in each. Inside,<kgdiff-viewer>is now built from a headless session and regions bound to it (RFC 0001, phase 1); its attributes, properties, methods, events and HTML reports are unchanged.@kgdiff/viewer
Every package is at 0.5.0: @kgdiff/cli · @kgdiff/core · @kgdiff/formats · @kgdiff/layout · @kgdiff/scene · @kgdiff/renderer · @kgdiff/viewer · @kgdiff/g6.
GitHub Action: uses: iiwish/kgdiff@v0.5.0.
@kgdiff/viewer@0.4.0
Minor Changes
-
86fe0da: Changes only is now a scope that combines with any mode instead of a fourth mode:
modeisoverlay | swipe | replayand the newscope(all | changes) shows only the changes, in overlay, swipe or replay alike.mode="changes"still works (overlay +scope="changes"), as do old#kgdiff=…;mode:changeslinks;kgdiff-modereports{ mode, scope }. The CLI gains--scope(--mode changeskeeps working).The canvas loses its header bar: the title floats top left, the actions top right, and the modes, the Changes only toggle with its context hops, and replay's transport live in one dock at the bottom. The diffstat pills (the sidebar's filter chips show the same counts) and the second fit button are gone, the chips wear the legend's swatches, the legend starts collapsed and the minimap appears only while part of the graph is out of view. Unchanged node labels use the muted text color, so the changes stand out.
Patch Changes
- Updated dependencies [86fe0da]
- @kgdiff/scene@0.4.0
- @kgdiff/renderer@0.4.0
- @kgdiff/core@0.4.0
- @kgdiff/formats@0.4.0
- @kgdiff/layout@0.4.0
@kgdiff/scene@0.4.0
Patch Changes
-
86fe0da: Changes only is now a scope that combines with any mode instead of a fourth mode:
modeisoverlay | swipe | replayand the newscope(all | changes) shows only the changes, in overlay, swipe or replay alike.mode="changes"still works (overlay +scope="changes"), as do old#kgdiff=…;mode:changeslinks;kgdiff-modereports{ mode, scope }. The CLI gains--scope(--mode changeskeeps working).The canvas loses its header bar: the title floats top left, the actions top right, and the modes, the Changes only toggle with its context hops, and replay's transport live in one dock at the bottom. The diffstat pills (the sidebar's filter chips show the same counts) and the second fit button are gone, the chips wear the legend's swatches, the legend starts collapsed and the minimap appears only while part of the graph is out of view. Unchanged node labels use the muted text color, so the changes stand out.
-
@kgdiff/core@0.4.0
- @kgdiff/layout@0.4.0
@kgdiff/renderer@0.4.0
Patch Changes
- Updated dependencies [86fe0da]
- @kgdiff/scene@0.4.0
- @kgdiff/core@0.4.0
@kgdiff/layout@0.4.0
Patch Changes
- @kgdiff/core@0.4.0
@kgdiff/g6@0.4.0
Patch Changes
- Updated dependencies [86fe0da]
- @kgdiff/scene@0.4.0
- @kgdiff/core@0.4.0
@kgdiff/formats@0.4.0
Patch Changes
- @kgdiff/core@0.4.0
@kgdiff/core@0.4.0
No changes in this release.