@stopcock/diff
v2.1.0
Published
Structural diffing, patching, composition, and rebasing for objects and arrays.
Readme
@stopcock/diff
Structural diffing and patching. Compute patches between objects, apply them, compose them, invert them, rebase concurrent edits.
bun add @stopcock/diffimport { pipe } from '@stopcock/fp'
import { diff, applyUnsafe, invert } from '@stopcock/diff'
const before = { name: 'Tom', scores: [10, 20] }
const after = { name: 'Tom', scores: [10, 20, 30] }
const p = diff(before, after)
applyUnsafe(before, p) // { name: 'Tom', scores: [10, 20, 30] }
const undo = invert(p)
pipe(after, applyUnsafe(undo)) // back to beforeWhat's in the box
- diff / diffWith: compute a
Patchbetween two values, with optional move/rename detection and custom equality - apply / applyUnsafe: apply a patch to a value (safe returns
Result, unsafe throws) - invert: reverse a patch for undo
- compose: merge sequential patches into one, with simplification
- rebase: transform a patch over concurrent edits, with conflict detection
- toJsonPatch / fromJsonPatch: RFC 6902 JSON Patch interop
- toLens / fromLens / fromTraversal: bridge between patches and
@stopcock/fpoptics
Every data-taking operation with two or more arguments is dual-form. Call it
data-first (apply(target, patch)) or use the same name data-last
(pipe(target, apply(patch))).
Optics bridge
The bridge uses the functional optics API from @stopcock/fp/optic:
import { toLens } from '@stopcock/diff'
import { set, view } from '@stopcock/fp/optic'
const source = { user: { name: 'Tom' } }
const name = toLens({
op: 'replace',
path: ['user', 'name'],
oldValue: 'Tom',
newValue: 'Ada',
})
if (name) {
view(name)(source) // 'Tom'
set(name, 'Ada')(source) // { user: { name: 'Ada' } }
}