Skip to content

Releases: Open-Tech-Foundation/obj-diff

@opentf/obj-diff@0.17.0

Choose a tag to compare

Added

  • stringify(value) / parse(wire) — a general-purpose, type-safe JSON codec for any JavaScript value (the counterpart to serialize/deserialize, which are diff-specific). Built on the same wire codec, so it preserves every supported native type (Date, RegExp, Map, Set, all TypedArrays, ArrayBuffer, DataView, Error, URL, boxed primitives, BigInt, NaN/±Infinity/-0, undefined, Temporal) plus circular references and shared identity among plain objects and arrays, nested to any depth. Symbols, functions, and class instances throw.
  • serialize/deserialize now support circular references and shared identity for plain objects and arrays. Any plain container reachable by more than one edge within an op is hoisted into the $refs table as an obj/arr entry and referenced by token, so cycles (obj.self = obj, mutually linked nodes, self-referential arrays) and objects shared in two places rebuild with the same identity on the far side. Single-use containers still serialize inline as readable JSON, so output for the common case is unchanged. Cycles that run through a Map/Set/Error still throw.
  • serialize/deserialize now support URL values.

@opentf/obj-diff@0.16.0

Choose a tag to compare

Added

  • serialize(diff) / deserialize(wire) — a self-describing JSON wire format for diffs. diff() returns live objects by reference, so JSON.stringify mangles them (Date→string, Map/Set{}, TypedArray→numeric-keyed object, BigInt→throws). serialize keeps value/path readable but replaces each special value with a lightweight reference token "@n" and collects the real types in a per-op $refs table ({ "n": { _t, _v } }); deserialize resolves the tokens back into live typed values so a patch() on the far side reconstructs the correct objects. Covers Date, RegExp, Map, Set, all TypedArrays, ArrayBuffer, DataView, Error (+ standard subclasses), boxed primitives, BigInt, NaN/±Infinity/-0, undefined, and Temporal values, nested to any depth (incl. Map/Set/object keys in path). Real strings shaped like "@n" are escaped. Symbols, functions, class instances, and circular references throw. Deserializing a Temporal value needs a Temporal implementation on globalThis.
  • Native diffing support for all Temporal types (Temporal.Instant, PlainDate, PlainTime, PlainDateTime, PlainYearMonth, PlainMonthDay, ZonedDateTime, Duration). They are compared as immutable, atomic values — replaced wholesale via a single CHANGED op when they differ, and preserved by reference through patch(). Equality uses each type's own .equals(); Duration (which has no .equals()) is compared structurally by its canonical string, so two equal-length but differently-expressed durations (e.g. PT2H vs PT120M) are treated as a change and the exact value is reconstructed on patch. Detection is brand-based (Symbol.toStringTag), so it works with the native global or the @js-temporal/polyfill.

@opentf/obj-diff@0.15.0

Choose a tag to compare

Fixed

  • Fixed patch() losing null prototypes of nested objects; previously only the root object's null prototype was preserved.
  • Fixed two invalid Dates (NaN timestamps) being reported as changed; they now compare as equal, consistent with NaN === NaN handling for primitives.
  • Fixed Error objects with equal messages but different custom properties (or different error classes with the same message) comparing as equal. Errors now compare by prototype, name, message and own enumerable properties, and are replaced wholesale when they differ.
  • Fixed boxed primitives (new Number(), new String(), new Boolean()) ignoring custom properties: equal-valued boxes now diff their own enumerable properties; different values report a replacement.
  • Fixed class instances always comparing as equal: non-plain objects fell through to a string comparison where both sides stringify to [object Object]. Instances sharing the same prototype are now diffed by their own enumerable properties, and instances of different classes are reported as replaced.
  • Fixed patch() corrupting Maps that use object keys: cloning the map broke key reference identity, so patches missed the entry and inserted a duplicate key instead. Non-primitive patch keys are now matched against existing map keys by structural equality.
  • Fixed patch() mutating the target object it was diffed against: the sparse array cleanup walked the whole result, following values inserted by reference, and compacted sparse arrays belonging to the caller's objects.
  • Fixed patch() compacting sparse arrays that no patch touched, shifting indices of unrelated data. Array cleanup is now limited to arrays that actually received deletions, and only trailing holes are truncated — so patching to a sparse target (e.g. [1, 2, 3][1, , 3]) now round-trips correctly.
  • Fixed patch() producing a wrong Set when shrinking it by two or more elements. Set deletions are now emitted in descending index order so sequential removal during patching no longer shifts pending indices.
  • Fixed asymmetrical circular reference diffing to avoid false positives and stack overflows.
  • Improved comparison of unhandled object types (e.g. RegExp) by falling back to string value comparison.
  • Fixed deep patch() traversal failing when encountering Set collections.
  • Fixed sparse array cleanup after patching to also cover arrays nested within Map values and Set elements.

Changed

  • patch() now throws a descriptive TypeError naming the failing path when a patch references a path that does not exist in the object, instead of an opaque error from deep inside the traversal.
  • Objects that are the same reference now short-circuit as equal without a deep walk.
  • The DiffResult path type widened from Array<string | number> to Array<unknown>: object keys are strings and array/Set indexes are numbers as before, but Map entries use the map key itself, which can be a value of any type.

Added

  • Compact array diffs: two new array-only op types — INSERTED (type: 3, splice the value in at the index) and REMOVED (type: 4, splice the element out) — with application-time indexes. diff() now trims the common prefix and suffix of arrays and emits splice ops for insertion/removal runs, so inserting one element at the front of a 10k-element array produces 1 op instead of 10,001. Existing types 0/1/2 keep their exact semantics and previously serialized patches still apply. Sparse arrays and arrays with non-index properties keep the previous key-based diff.
  • Shortest edit scripts for arrays: after prefix/suffix trimming, the remaining middle window is resolved with a bounded Myers LCS diff, so interleaved edits (remove + insert elsewhere + change) produce one op per actual edit — a mixed edit on 10k elements dropped from 9,998 ops to 4. Adjacent delete+insert pairs are zipped into granular recursive replacements, and the algorithm falls back to positional alignment when the edit-distance/cost cap is exceeded, bounding worst-case time and memory.
  • Exported the diff type constants (DELETED, ADDED, CHANGED, INSERTED, REMOVED) and the DiffType type from the package root.
  • Native diffing support for ArrayBuffer and DataView: contents are compared byte-by-byte (including byteOffset/byteLength for views) instead of always comparing as equal via the string fallback.
  • Native diffing and patching support for all JavaScript TypedArrays (Uint8Array, Float32Array, BigInt64Array, etc.) allowing precise element-level diffs and preserving array types during patching.
  • Added comprehensive accuracy evaluation script (evaluate.js) covering ES6 collections and edge cases.
  • Added updated benchmark competitors (deep-diff-ts, @adobe/optimized-diff).
  • Updated README.md with detailed Accuracy vs Performance matrices.

@opentf/obj-diff@0.14.0

Choose a tag to compare

Minor Changes

  • e475557: - Added comprehensive JSDoc documentation to all public API symbols (diff, diffWith, patch, DiffResult) to improve JSR score and developer experience.
    • Fixed an issue where the JSR publish was failing due to uncommitted build artifacts by adding the --allow-dirty flag.
    • Updated GitHub Actions to opt-in to Node.js 24, future-proofing the CI/CD pipeline.

@opentf/obj-diff@0.13.1

Choose a tag to compare

Patch Changes

  • 5f0b4e6: Update pkg readme with updated docs.

@opentf/obj-diff@0.13.0

Choose a tag to compare

Minor Changes

  • 5d846d9: - Modernized the entire build and test pipeline using Bun and a custom ESBuild-based bundling script.
    • Significantly improved core robustness by fixing infinite recursion on circular references in patch().
    • Enhanced array diffing to correctly handle sparse array holes and non-numeric properties.
    • Added preservation for null prototypes during the patching process.
    • Refined diffWith() to support "Explicit Equality" by allowing the custom comparator to return false to bypass deep-diffing.
    • Consolidated GitHub Actions into streamlined CI and Release workflows with integrated JSR and NPM support.

@opentf/obj-diff@0.12.0

Choose a tag to compare

Minor Changes

  • e5b61db: Added value prop to the Set removed diff results.

@opentf/obj-diff@0.11.0

Choose a tag to compare

Minor Changes

  • 01f7696: Added deep diffing support for Map & Set objects.

@opentf/obj-diff@0.10.0

Choose a tag to compare

Minor Changes

@opentf/obj-diff@0.9.0

Choose a tag to compare

Minor Changes

  • 39521b0: Fixed other object null type checking.