Compare two JS objects, find what changed, patch back, humanize the diff.
npm install spotdiffimport { spotdiff, patch, humanize } from 'spotdiff'
const v1 = { user: { name: 'Juan', age: 30, tags: ['a', 'b'] } }
const v2 = { user: { name: 'Juan', age: 31, tags: ['a', 'c', 'd'] } }
const changes = spotdiff(v1, v2)
// [
// { path: 'user.age', op: 'changed', from: 30, to: 31 },
// { path: 'user.tags[1]', op: 'changed', from: 'b', to: 'c' },
// { path: 'user.tags[2]', op: 'added', from: undefined, to: 'd' },
// ]
patch(v1, changes) // → v2
patch(v2, changes, true) // → v1
humanize(changes)
// [
// "user.age changed from 30 to 31",
// "user.tags[1] changed from b to c",
// "user.tags[2] was added with value d"
// ]
humanize(changes, { lang: 'es' })
// [
// "user.age cambió de 30 a 31",
// "user.tags[1] cambió de b a c",
// "user.tags[2] fue agregado con valor d"
// ]| Option | Type | Default | Description |
|---|---|---|---|
maxDepth |
number |
Infinity |
Stop recursion at this depth |
ignoreKeys |
string[] |
[] |
Skip these keys everywhere |
arrays |
'index' | 'smart' |
'index' |
Array comparison strategy |
Apply changes forward or backward (reverse=true).
| Option | Type | Default |
|---|---|---|
lang |
'en' | 'es' |
'en' |
interface Change {
path: string // "user.address.city" or "tags[2]"
op: 'added' | 'removed' | 'changed'
from: unknown // undefined if op is 'added'
to: unknown // undefined if op is 'removed'
}- Zero production dependencies
- Dual package ESM + CJS
- TypeScript strict mode
- Never mutates inputs
- Handles
null,undefined,Date,Map,Set - Throws on circular references with a descriptive message