Skip to content

A zero dependency, high-performance, security-conscious JavaScript diffing library

License

Notifications You must be signed in to change notification settings

vmcruz/sandboxed-diff

Repository files navigation

@sandboxed/diff

A zero dependency, high-performance, security-conscious JavaScript diffing library for comparing complex data structures with ease.

Features

  • ⚡️ Zero dependencies – lightweight and no external libraries required
  • 📝 Detects additions, deletions, and modifications
  • 💡 Supports Primitives, Objects, Arrays, Maps, and Sets
  • 🔄 Handles circular references safely
  • 🛠️ Highly configurable to fit different use cases
  • 🚨 Built with security in mind to prevent prototype pollution and other risks
  • 💻 Works in both Node.js and browser environments

Installation

npm install @sandboxed/diff

yarn add @sandboxed/diff

Supports esm and cjs

Works with both ESM (import) and CJS (require). Use the syntax that matches your environment:

// ESM
import diff, { ChangeType } from '@sandboxed/diff';

// CJS option 1
const diff = require('@sandboxed/diff').default;
const { ChangeType } = require('@sandboxed/diff');

// CJS option 2
const { default: diff, ChangeType } = require('@sandboxed/diff');

Usage

diff(lhs: any, rhs: any, config?: DiffConfig): Diff

import diff, { ChangeType } from '@sandboxed/diff';

const a = { name: "Alice", age: 25 };
const b = { name: "Alice", age: 26, city: "New York" };

const result = diff(a, b);

console.log(result);
console.log(result.toDiffString());
console.log(result.equal); // false

Output:

[
  { type: 'noop', str: '{', depth: 0, path: [] },
  {
    type: 'noop',
    str: '"name": "Alice",',
    depth: 1,
    path: [ 'name', { deleted: false, value: 'Alice' } ]
  },
  {
    type: 'remove',
    str: '"age": 25,',
    depth: 1,
    path: [ 'age', { deleted: true, value: 25 } ]
  },
  {
    type: 'update',
    str: '"age": 26,',
    depth: 1,
    path: [ 'age', { deleted: false, value: 26 } ]
  },
  {
    type: 'add',
    str: '"city": "New York",',
    depth: 1,
    path: [ 'city', { deleted: false, value: 'New York' } ]
  },
  { type: 'noop', str: '}', depth: 0, path: [] }
]

// ---

{
   "name": "Alice",
-  "age": 25,
!  "age": 26,
+  "city": "New York",
}

Config

option Description
config.include Include only these change types from the diff result. Can be combined with exclude.
config.exclude Excludes the change types from the diff result. Can be combined with include.
config.strict Performs loose type check if disabled.
config.showUpdatedOnly @sandboxed/diff creates a ChangeType.REMOVE entry for every ChangeType.UPDATE. This flags prevents this behavior.
config.pathHints Hashmap of map and set path hints. These strings will be used in the path array to provide a hit about the object's type.
config.redactKeys List of keys that should be redacted from the output. Works with string based keys and serialized Symbol.
config.maxDepth Max depth that the diffing function can traverse.
config.maxKeys Max keys the diffing function can traverse.
config.timeout Milliseconds before throwing a timeout error.

Utils

util Description
toDiffString Generates the diff string representation of the diff result.
equal Determines whether the inputs are structurally equal based on the diff result.

Motivation

Many diffing libraries are optimized for either structured output or human-readable text, but rarely both. @sandboxed/diff is designed to provide a structured diff result along with a utility to generate a string representation, making it easy to use in both programmatic logic and UI rendering.

Trade-off: It may be slower than other libraries, but if you prioritize structured diffs with a built-in string representation, @sandboxed/diff is a great fit.

LICENSE

MIT