Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@broofa/jsondiff

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@broofa/jsondiff - npm Package Compare versions

Comparing version 1.3.0 to 1.3.1

58

index.js

@@ -5,2 +5,8 @@ // Reserved values

/**
* Normalize a patch value by converting DROP values to undefined. This is
* useful for doing code such as `if (jsondiff.value(patch.someValue)) ...`
*
* @param {any} value
*/
function value(val) {

@@ -10,2 +16,10 @@ return val === DROP ? undefined : val;

/**
* Generate a patch object that describes the difference between two states
*
* @param {any} before
* @param {any} after
*
* @returns {any} Patch object as described in the README
*/
function diff(before, after) {

@@ -61,11 +75,19 @@ if (after === undefined) return DROP;

function patch(before, _diff) {
if (_diff === DROP) return undefined;
if (_diff === KEEP) _diff = before;
if (_diff == null) return _diff;
/**
* Apply a patch object to some 'before' state and return the 'after' state
*
* @param {any} before
* @param {any} _patch
*
* @returns {any} The mutated state
*/
function patch(before, _patch) {
if (_patch === DROP) return undefined;
if (_patch === KEEP) _patch = before;
if (_patch == null) return _patch;
if (before === _diff) return before;
if (before === _patch) return before;
const beforeType = before == null ? 'null' : before.constructor.name;
const type = _diff.constructor.name;
const type = _patch.constructor.name;

@@ -76,3 +98,3 @@ if (beforeType !== type) {

case 'Array': before = []; break;
default: return _diff;
default: return _patch;
}

@@ -88,3 +110,3 @@ }

case 'Date': // Not strictly JSON but useful
if (before.getTime() == _diff.getTime()) _diff = before;
if (before.getTime() == _patch.getTime()) _patch = before;
break;

@@ -95,4 +117,4 @@

const values = {...before};
for (const k in _diff) {
if (value(_diff[k]) === undefined) {
for (const k in _patch) {
if (value(_patch[k]) === undefined) {
if (k in values) {

@@ -103,3 +125,3 @@ delete values[k];

} else {
const val = patch(before[k], _diff[k]);
const val = patch(before[k], _patch[k]);
if (val !== before[k]) {

@@ -112,3 +134,3 @@ values[k] = val;

_diff = isEqual ? before : values;
_patch = isEqual ? before : values;
break;

@@ -118,6 +140,6 @@ }

case 'Array': {
const values = new Array(_diff.length);
let isEqual = before.length === _diff.length;
for (let i = 0, l = _diff.length; i < l; i++) {
const val = patch(before[i], _diff[i]);
const values = new Array(_patch.length);
let isEqual = before.length === _patch.length;
for (let i = 0, l = _patch.length; i < l; i++) {
const val = patch(before[i], _patch[i]);

@@ -127,3 +149,3 @@ if (val !== before[i]) isEqual = false;

}
_diff = isEqual ? before : values;
_patch = isEqual ? before : values;
break;

@@ -135,3 +157,3 @@ }

}
return before === _diff ? before : _diff;
return before === _patch ? before : _patch;
};

@@ -138,0 +160,0 @@

{
"name": "@broofa/jsondiff",
"version": "1.3.0",
"version": "1.3.1",
"description": "Pragmatic, intuitive diffing and patching of JSON objects",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -7,61 +7,116 @@ <!--

Pragmatic, intuitive diffing and patching of JSON objects
Pragmatic and intuitive diff and patch functions for JSON data
## Summary of related modules
## Installation
There are variety of modules available that can diff and patch JSON data
structures. Here's a quick run down:
`npm install @broofa/jsondiff`
| Module | Size | Patch format | Notes |
|---|---|---|---|
| **@broofa/jsondiff** | 0.7K | Overlay | Readable patches |
| deep-diff | 3.5K | RFC9602-like | Most popular module |
| rfc9602 | 2K | RFC9602 | See below |
| fast-json-patch | 4K | RFC9602 | See below |
## Usage
The main difference between these modules is in the patch object structure. Most
(all?) of the other modules use a structure based on or similar to RFC9602,
which is composed of a series of operations that describe how to transform the
target object. In contrast, the patches in this module act as an "overlay"
that is copied onto the target object. There are tradeoffs to this, some
good, some bad:
Require it:
1. **Readability** - A structured patch is easier to read because it "looks"
like the object it's modifying.
2. **Reordering** - Data is not "moved" in a structured patch. It is simply
deleted from the old location and inserted at the new location.
2. **Size** - Operation-based patches are more verbose (lots of duplicate keys
and values), except in the case where values move locations. This may have a
significant impact on network bandwidth, especially for uncompressed data
streams.
3. **Fault tolerance** - Operation-based patches may fail if operations are
applied out of order or if the target object does not have the expected
structure.
4. **DROP/KEEP hack** - See comments about `DROP` and `KEEP` values in "Patch
Objects", below. This may be off-putting to some readers.
```javascript
const jsondiff = require('@broofa/jsondiff');
## Installation
// ... or ES6 module style:
// import jsondiff from '@broofa/jsondiff';
`npm install @broofa/jsondiff`
```
## Usage
Start with some `before` and `after` state:
```javascript
console.log(before);
⇒ { name: 'my object',
⇒ description: 'it\'s an object!',
⇒ details: { it: 'has', an: 'array', with: [ 'a', 'few', 'elements' ] } }
```
```javascript
const jsondiff = require('@broofa/jsondiff');
console.log(after);
const before = {a: 'Hello', b: 'you', c: ['big', 'bad'], d: 'beast'};
const after = {a: 'Hi', c: ['big', 'bad', 'bold'], d: 'beast'};
⇒ { name: 'updated object',
⇒ title: 'it\'s an object!',
⇒ details:
⇒ { it: 'has',
⇒ an: 'array',
⇒ with: [ 'a', 'few', 'more', 'elements', { than: 'before' } ] } }
```
// Create a patch
// Note the use of DROP (-) and KEEP(+) values
const patch = jsondiff.diff(before, after); // ⇨ { a: 'Hi', b: '-', c: [ '+', '+', 'bold' ] }
Create a patch that descibes the difference between the two:
```javascript
const patch = jsondiff.diff(before, after);
console.log(patch);
// Apply it to the original
const patched = jsondiff.patch(before, patch); // ⇨ { a: 'Hi', c: [ 'big', 'bad', 'bold' ], d: 'beast' }
⇒ { name: 'updated object',
⇒ description: '-',
⇒ details:
⇒ { with: [ '+', '+', 'more', 'elements', { than: 'before' } ] },
⇒ title: 'it\'s an object!' }
```
*(Note the special DROP and KEEP values ("-" and "+")! These are explained in **Patch Objects**, below.)*
// Get the expected result
assert.deepEqual(after, patched); // Passes!
Apply `patch` to the before state to reproduce the `after` state:
```javascript
const patched = jsondiff.patch(before, patch);
console.log(patched);
⇒ { name: 'updated object',
⇒ details:
⇒ { it: 'has',
⇒ an: 'array',
⇒ with: [ 'a', 'few', 'more', 'elements', { than: 'before' } ] },
⇒ title: 'it\'s an object!' }
```
## Why yet-another diff module?
There are already several modules in this space - `deep-diff`, `rfc6902`, or `fast-json-patch`, to name a few. `deep-diff` is the most popular, however `rfc6902` is (to my mind) the most compelling because it will interoperate with other libraries that support [RFC6902 standard](https://tools.ietf.org/html/rfc6902).
However ... the patch formats used by these modules tends to be cryptic and overly verbose -
a list of the mutations needed to transform between the two states. In the case
of `deep-diff` you end up with this patch:
```javascript
console.log(deepPatch);
⇒ [ { kind: 'E',
⇒ path: [ 'name' ],
⇒ lhs: 'my object',
⇒ rhs: 'updated object' },
⇒ { kind: 'D', path: [ 'description' ], lhs: 'it\'s an object!' },
⇒ { kind: 'A',
⇒ path: [ 'details', 'with' ],
⇒ index: 4,
⇒ item: { kind: 'N', rhs: [ [Function: Object] ] } },
⇒ { kind: 'A',
⇒ path: [ 'details', 'with' ],
⇒ index: 3,
⇒ item: { kind: 'N', rhs: 'elements' } },
⇒ { kind: 'E',
⇒ path: [ 'details', 'with', 2 ],
⇒ lhs: 'elements',
⇒ rhs: 'more' },
⇒ { kind: 'N', path: [ 'title' ], rhs: 'it\'s an object!' } ]
```
And for `rfc6902`:
```javascript
console.log(rfcPatch);
⇒ [ { op: 'remove', path: '/description' },
⇒ { op: 'add', path: '/title', value: 'it\'s an object!' },
⇒ { op: 'replace', path: '/name', value: 'updated object' },
⇒ { op: 'add', path: '/details/with/2', value: 'more' },
⇒ { op: 'add', path: '/details/with/-', value: { than: 'before' } } ]
```
The advantage(?) of this module is that the patch structure mirrors the
structure of the target data. As such, it terse, readable, and resilient.
That said, this module may not be for everyone. In particular, readers may find
the DROP and KEEP values (described below) to be... "interesting".
## API

@@ -82,7 +137,18 @@

### jsondiff.merge(before, after)
### jsondiff.value(val)
Shorthand for `jsondiff.patch(before, jsondiff.diff(before, after))`. Useful
for mutating an object only where values have actually changed.
Normalize patch values. Currently this just converts `DROP` values to
`undefined`, otherwise returns the value. This is useful in determining if a
patch has a meaningful value. E.g.
```javascript
const newPatch = {foo: jsondiff.DROP, bar: 123};
newPatch.foo; // ⇨ '-'
jsondiff.value(newPatch.foo); // ⇨ undefined
jsondiff.value(newPatch.bar); // ⇨ 123
jsondiff.value(newPatch.whups); // ⇨ undefined
```
## Patch Objects

@@ -89,0 +155,0 @@

```javascript --hide --run usage
runmd.onRequire = path => path.replace(/^@broofa\/\w+/, '..');
const assert = require('assert');
```
# @broofa/jsondiff
const before = {
name: 'my object',
description: 'it\'s an object!',
details: {
it: 'has',
an: 'array',
with: ['a', 'few', 'elements']
}
};
Pragmatic, intuitive diffing and patching of JSON objects
const after = {
name: 'updated object',
title: 'it\'s an object!',
details: {
it: 'has',
an: 'array',
with: ['a', 'few', 'more', 'elements', { than: 'before' }]
}
};
## Summary of related modules
const deepPatch = [
{kind: 'E', path: ['name'], lhs: 'my object', rhs: 'updated object'},
{kind: 'D', path: ['description'], lhs: 'it\'s an object!'},
{kind: 'A', path: ['details', 'with'], index: 4, item: {kind: 'N', rhs: [Object]}},
{kind: 'A', path: ['details', 'with'], index: 3, item: {kind: 'N', rhs: 'elements'}},
{kind: 'E', path: ['details', 'with', 2], lhs: 'elements', rhs: 'more' },
{kind: 'N', path: ['title'], rhs: 'it\'s an object!'}
];
There are variety of modules available that can diff and patch JSON data
structures. Here's a quick run down:
const rfcPatch = [
{op: 'remove', path: '/description'},
{op: 'add', path: '/title', value: 'it\'s an object!'},
{op: 'replace', path: '/name', value: 'updated object'},
{op: 'add', path: '/details/with/2', value: 'more'},
{op: 'add', path: '/details/with/-', value: {than: 'before'}}
];
| Module | Size | Patch format | Notes |
|---|---|---|---|
| **@broofa/jsondiff** | 0.7K | Overlay | Readable patches |
| deep-diff | 3.5K | RFC9602-like | Most popular module |
| rfc9602 | 2K | RFC9602 | See below |
| fast-json-patch | 4K | RFC9602 | See below |
```
The main difference between these modules is in the patch object structure. Most
(all?) of the other modules use a structure based on or similar to RFC9602,
which is composed of a series of operations that describe how to transform the
target object. In contrast, the patches in this module act as an "overlay"
that is copied onto the target object. There are tradeoffs to this, some
good, some bad:
# @broofa/jsondiff
1. **Readability** - A structured patch is easier to read because it "looks"
like the object it's modifying.
2. **Reordering** - Data is not "moved" in a structured patch. It is simply
deleted from the old location and inserted at the new location.
2. **Size** - Operation-based patches are more verbose (lots of duplicate keys
and values), except in the case where values move locations. This may have a
significant impact on network bandwidth, especially for uncompressed data
streams.
3. **Fault tolerance** - Operation-based patches may fail if operations are
applied out of order or if the target object does not have the expected
structure.
4. **DROP/KEEP hack** - See comments about `DROP` and `KEEP` values in "Patch
Objects", below. This may be off-putting to some readers.
Pragmatic and intuitive diff and patch functions for JSON data

@@ -49,19 +54,58 @@ ## Installation

Require it:
```javascript --run usage
const jsondiff = require('@broofa/jsondiff');
const before = {a: 'Hello', b: 'you', c: ['big', 'bad'], d: 'beast'};
const after = {a: 'Hi', c: ['big', 'bad', 'bold'], d: 'beast'};
// ... or ES6 module style:
// import jsondiff from '@broofa/jsondiff';
```
// Create a patch
// Note the use of DROP (-) and KEEP(+) values
const patch = jsondiff.diff(before, after); // RESULT
Start with some `before` and `after` state:
```javascript --run usage
console.log(before);
```
// Apply it to the original
const patched = jsondiff.patch(before, patch); // RESULT
```javascript --run usage
console.log(after);
```
// Get the expected result
assert.deepEqual(after, patched); // Passes!
Create a patch that descibes the difference between the two:
```javascript --run usage
const patch = jsondiff.diff(before, after);
console.log(patch);
```
*(Note the special DROP and KEEP values ("-" and "+")! These are explained in **Patch Objects**, below.)*
Apply `patch` to the before state to reproduce the `after` state:
```javascript --run usage
const patched = jsondiff.patch(before, patch);
console.log(patched);
```
## Why yet-another diff module?
There are already several modules in this space - `deep-diff`, `rfc6902`, or `fast-json-patch`, to name a few. `deep-diff` is the most popular, however `rfc6902` is (to my mind) the most compelling because it will interoperate with other libraries that support [RFC6902 standard](https://tools.ietf.org/html/rfc6902).
However ... the patch formats used by these modules tends to be cryptic and overly verbose -
a list of the mutations needed to transform between the two states. In the case
of `deep-diff` you end up with this patch:
```javascript --run usage
console.log(deepPatch);
```
And for `rfc6902`:
```javascript --run usage
console.log(rfcPatch);
```
The advantage(?) of this module is that the patch structure mirrors the
structure of the target data. As such, it terse, readable, and resilient.
That said, this module may not be for everyone. In particular, readers may find
the DROP and KEEP values (described below) to be... "interesting".
## API

@@ -82,7 +126,17 @@

### jsondiff.merge(before, after)
### jsondiff.value(val)
Shorthand for `jsondiff.patch(before, jsondiff.diff(before, after))`. Useful
for mutating an object only where values have actually changed.
Normalize patch values. Currently this just converts `DROP` values to
`undefined`, otherwise returns the value. This is useful in determining if a
patch has a meaningful value. E.g.
```javascript --run usage
const newPatch = {foo: jsondiff.DROP, bar: 123};
newPatch.foo; // RESULT
jsondiff.value(newPatch.foo); // RESULT
jsondiff.value(newPatch.bar); // RESULT
jsondiff.value(newPatch.whups); // RESULT
```
## Patch Objects

@@ -89,0 +143,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc