
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
A differ/patcher for arbitrary values that presents delta in a terse WSON-like format.
WSON can be used to stringify structured data, transmit that string to some receiver, where it can be parsed to reconstruct that original data. Now both ends posses that identical data. If now that data happens to change a little, why should we retransmit that whole redundant information? This is where wson-diff comes in:
diff with your old value have and the current wish.patch to apply that delta to the value have in being.diff uses mdiff to find minimal changes of arrays and strings. For arrays this changes are further boiled down to deletes, moves, inserts and replaces.patch can use notifiers to forward changes to some dependent (DOM?)-structure.$ npm install wson-diff
import wdiffFactory from 'wson-diff'
const wdiff = wdiffFactory()
var have = {
name: 'otto',
size: 177.3,
completed: ['forth', 'javascript', 'c++', 'haskell'],
active: true,
message: 'My hovercraft is full of eels.'
}
var wish = {
name: 'rudi',
size: 177.4,
completed: ['forth', 'coffeescript', 'haskell', 'c++', 'lisp'],
active: false,
message: 'My hovercraft is full of eels!'
}
var delta = wdiff.diff(have, wish)
console.log('delta=\'%s\'', delta)
// delta='|active:#f|completed[m3@2][i4:lisp][r1:coffeescript]|message[s29=!]|name:rudi|size:#177.4'
var result = wdiff.patch(have, delta)
console.log('result=\'%j\'', result)
// Now result (and have) is deep equal to wish.
This is an informal description of the Delta-Syntax. There is also an EBNF-file in the doc-directory and a syntax-diagram created from it.
If have is deep equal to wish, diff returns null.
Examples:
have | wish |
|---|---|
23 | 23 |
{a: 3, b: 4} | {a: 3, b: 4} |
[3, 4] | [3, 4] |
'hovercraft' | 'hovercraft' |
Any WSON-value is a plain-delta. Its semantics is: Ignore have, just use the WSON-stringified delta as the result.
A plain-delta will be produced for scalars (everything but string, array, or object – except Date, which are WSON-scalars) and if have and wish have different types.
Examples:
have | wish | delta |
|---|---|---|
23 | 42 | #42 |
false | true | #t |
undefined | null | #n |
[3, 4] | {a: 3, b: 4} | {a:3|b:4} |
{a: 3, b: 4} | [3, 4] | [3|4] |
A real-delta starts with a '|' followed by zero ore more modifiers followed by zero ore more assignments. The assignments are separated from each other by '|'. There must be at least one modifier or assignments. Since the only WSON-value that starts with a '|' is a backref, which can't occur at top-level, there is no ambiguity.
A path consists of one ore more keys separated by '|'. Each key is a WSON-string.
Examples:
| path | keys |
|---|---|
| a | 'a' |
| foo|a|members | 'foo', 'a', 'members' |
| foo|`a`e|#|42 | 'foo', '[]', '', '42' (Special characters '[' and ']' are WSON-escaped; '#' is the empty string.) |
An assignment consists of a path followed by a ':', followed by a WSON-value. Its semantics is: Use path to dive into the object (All keys but the last must resolve to objects). Use the last key to set or replace that property by value.
Examples:
have | wish | delta |
|---|---|---|
{a: 3, b: 4} | {a: 3, b: 42} | |b:#42 |
{foo: {a: 3, b: 4}} | {foo: {a: 3, b: 42}} | |foo|b:#42 |
{foo: {a: 3, b: 4}} | {foo: {a: 3, b: 4, c: 5}} | |foo|c:#5 |
A Modifier consists of a '[', followed by a kind character, followed by one or more '|'-separated kind-specific items, closed by a ']'.
A path-delta is an assignments or a path followed by one ore more modifiers.
There are two modifiers that operate on an object: unset, and assign. Deltas of objects are created by deep comparing all own properties of these objects. See also: Custom Objects
Examples:
have | wish | delta |
|---|---|---|
{a: 3, b: 4, c: 5} | {b: 4} | [-a|c] |
Examples:
have | wish | delta |
|---|---|---|
{foo: {a: 3, b: 4, c: 5}} | {foo: {a: 4, b: 3, c: 5}} | |foo[=a:#4|b:#3] |
There are four modifiers that operate on an array: delete, move insert, and replace. Since arrays are assumed to be mutable, these modifiers work cumulatively. I.e. the indexes refer to the array after all previous modifications applied):
Note: diff will create a plain-delta:
arrayLimit entry changes).Examples:
have | wish | delta | Explanation |
|---|---|---|---|
[2, 3, 5, 7, 11, 13] | [2, 5, 7, 11, 13] | [d1] | Delete one entry at index 1 |
[2, 3, 5, 7, 11, 13] | [3, 5, 13] | [d3+1|0] | Delete 2 entries (one + 1) at index 3. From the resulting array delete one entry at index 0 |
Examples:
have | wish | delta | Explanation |
|---|---|---|---|
[2, 3, 5, 7, 11, 13] | [2, 3, 7, 11, 5, 13] | [m2@4] | Cut one entry at index 2 and reinsert it at index 4 |
[2, 3, 5, 7, 11, 13] | [2, 11, 13, 3, 5, 7] | [m4+1@1] | Cut 2 entries (one + 1) at index 4 and reinsert them at index 1 |
[2, 3, 5, 7, 11, 13] | [2, 13, 11, 3, 5, 7] | [m4-1@1] | Cut 2 entries (one + 1) at index 4 and reinsert them swapped at index 1 |
Examples:
have | wish | delta | Explanation |
|---|---|---|---|
[2, 5, 7, 11, 13] | [2, 3, 5, 7, 11, 13] | [i1:#3] | Insert value 3 at index 1. |
[3, 5, 13] | [2, 3, 5, 7, 11, 13] | [i2:#7:#11|0:#2] | Insert values 7, 11 at index 11. Into the resulting array insert value 2 at index 0. |
Examples:
have | wish | delta | Explanation |
|---|---|---|---|
[2, 3, 5, 7, 11, 13] | [2, 3, 15, 7, 11, 13] | [r2:#15] | Replace the entry at index 2 with 15. |
[2, 3, 5, 7, 11, 13] | [2, 23, 15, 7, 1, 13] | [r1:#23:#15|4:#1] | Replace the entries at index 1 with 23, 15. Then replace the entry at index 4 with 1. |
[{a: 3}, {b: 4}] | [{a: 4}, {b: 3}] | [r0|a:#4|1|b:#3] | Replace 'a' at index 0, then 'b' at index 1. |
diff will produce array-modifiers in the order delete, move, insert, replace
Examples:
have | wish | delta | Explanation |
|---|---|---|---|
[2, 3, 5, 7, 11, 13] | [5, 11, 13, 7, 42] | [d0+1][m1@3][i4:#42] | Delete 2, 3, move 7, insert 42. |
[2, 3, 5, 7, 11, 13] | [13, 11, 2, 3, 51, 7] | [m4-1@0][r4:#51] | Move reversed 11, 13, replace 5 by 51. |
There in one modifier that operates on a string: substitute. Since strings are assumed to be immutable, these modifiers work simultaneously (in contrast to array-modifiers:
Note: diff will create a plain-delta:
stringEdge)stringLimit character changes).Examples (with stringEdge: 0):
have | wish | delta | Explanation |
|---|---|---|---|
'hovercraft' | 'Hovercraft' | [s0=H] | simple replacement |
'my hovercraft' | 'thine hovercraft' | [s0+3=thine] | grow replacement |
'hovercraft is missing' | 'hovercraft is away' | [s14-3=away] | shrink replacement |
'full of my eels' | 'full of eels' | [s8-3] | pure deletion |
'my hovercraft' | 'hover my craft' | [s0-3|8+4= my ] | delete and insert |
The underlying WSON-processor may be created with connectors to support custom objects. These conectectors can be augmented for wson-diff with these extra properties:
diffKeys (array of strings): Instead of looking at all own properties diff just compares the properties referred by these keys. Thus you can hide private properties from delta.postpatch (function): This function - if present - will be called (bound to the current object) after all patches have been applied. Thus you can update private properties thereafter.have | wish | delta | Explanation |
|---|---|---|---|
| {foo: {a: [1, 2]}} | {foo: {a: [1, 2, 3]}} | |foo|a[i2:#3] | |
| {foo: {a: [1, 2]}} | {bar: {a: [1, 2]}} | |[-foo]bar:{a:[#1|#2]} | sorry, no renaming |
| {foo: {a: [1, 2]}} | {foo: {a: 1, b: 1}} | |foo[=a:#1|b:#1] | assign modifier |
| [{a: 'alice'}, {b: 'bob'}] | [{a: 'eve'}, {b: 'bob'}] | |[r0|a:eve] |
Be that you are not only interested in in the result of patching some value by a delta, but want to update some related structure - say a DOM-tree - accordingly. This task can be accomplished by passing patchone or notifiers, that should provide the following interface:
interface Notifier {
checkedBudge: (up: number, key: Key, current: any) => boolean
unset: (key: string, curent: any) => void
assign: (key: string | null, value: any, current?: any) => void
delete: (idx: number, len: number, current?: any) => void
move: (srcIdx: number, dstIdx: number, len: number, reverse: boolean, current?: any) => void
insert: (idx: number, values: any[], current?: any) => void
replace: (idx: number, values: any[], current?: any) => void
substitute: (patches: Patch[], current?: any) => void
}
A notifier is assumed to to hold some cursor that could be manipulated by calling checkedBudge: up is a number >= 0 that says how many levels we want to go up. If key is not null it is a numeric array-index or a string object-key. Then the cursor should be moved into the item that is indicated by key. If a key is provided (or for a first extra call with up=0, key=null), checkedBudge may return false to signal a cut. Inititally the cursor refers to the root-value (The have that is passed to patch).
The other methods just resemble the parsed modifiers. For convenience the current value (that one the cursor refers to, before apllying the modification) is passed as an additional argument current.
assign may be called with or without key. If key is null, the item under the cursor is expected to be replaced. Otherwise the item referred by this key is expected to be set or replaced.
To finer tailor the amount of notification checkedBudge may return false. Then all modifications for the value reached by key will be collected and notified by a single call of assign (without a current argument). E.g. if checkedBudge returns false whenever current happens to be a string, a substitute-delta will never have substitute be called but results in the same call of assign as if there had been a plain-delta for this string.
Creates a new diff/patch processor.
Recognized options:
WSON: a WSON-processor.wsonOptions: If no WSON is provided, create one with this options.Other options are handed to diff.
Returns null if have and wish are deep equal. Otherwise returns the string delta.
Recognized options:
arrayLimit (integer or function, default: null): If the number of differences between some have-array and some wish-array exceeds this limit, a plain-delta will be created instead of a list of modifiers. A move will count as twice it's length. This is to limit the amount of time used to find the minimal set of changes by the underlying mdiff. If arrayLimit is a function, it will applied to (have-array, wish-array) to return the limit dynamically.
stringLimit (integer or function, default: null): If the number of differences between some have-string and some wish-string exceeds this limit, a plain-delta will be created instead of a substitute-modifier. Every deleted or inserted character count as one change. If stringLimit is a function, it will applied to (have-string, wish-string) to return the limit dynamically.
stringEdge (integer, default: 16): If some wish-string is shorter than this limit, a plain-delta will be created instead of a substitute-modifier.
Returns the result of applying delta to have. If possible, have will be changed in place.
Recognized options:
notifiers: an array of notifiers, each of which receives all applied changes. A single notifier is accepted, too.FAQs
Object diff and patch
The npm package wson-diff receives a total of 5 weekly downloads. As such, wson-diff popularity was classified as not popular.
We found that wson-diff demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.