@laioutr/valtio-json-patch
v0.1.1
Published
Convert valtio proxy operations to RFC 6902 JSON Patch
Readme
@laioutr/valtio-json-patch
Convert valtio state changes to RFC 6902 JSON Patch operations.
When you mutate a valtio proxy, valtio tracks exactly which properties changed. This package turns those changes into standard JSON Patch operations (add, remove, replace, move), so you can send them over the wire and feed them into any system that speaks JSON Patch.
Install
pnpm add @laioutr/valtio-json-patchUsage
import { proxy } from 'valtio/vanilla';
import { subscribeWithPatches } from '@laioutr/valtio-json-patch';
const state = proxy({
user: { name: 'Alice', role: 'admin' },
items: ['a', 'b'],
});
const unsubscribe = subscribeWithPatches(state, (patches) => {
console.log(patches);
});
state.user.name = 'Bob';
// [{ op: 'replace', path: '/user/name', value: 'Bob' }]
state.items.push('c');
// [{ op: 'add', path: '/items/2', value: 'c' }]
state.items.splice(0, 1);
// [{ op: 'remove', path: '/items/0' }]API
subscribeWithPatches(proxy, callback, options?)
Subscribe to a valtio proxy and receive JSON Patch arrays on every change. Returns an unsubscribe function.
const unsubscribe = subscribeWithPatches(
state,
(patches) => {
// patches is an Operation[] with one or more JSON Patch operations
},
{
notifyInSync: false, // default
}
);Options:
notifyInSync(default:false): By default, valtio batches rapid mutations and delivers them together in one callback (via microtask). Set totrueto receive each mutation immediately as it happens. See Batching andnotifyInSyncfor trade-offs.
opsToJsonPatch(ops)
Convert an array of valtio change tuples to JSON Patch operations. This is the core conversion function with zero dependencies.
Mapping rules:
| Change | Condition | JSON Patch |
| ---------------- | ---------------------------------------- | ---------- |
| Property set | Old value was undefined (new property) | add |
| Property set | Old value existed (update) | replace |
| Property deleted | | remove |
| Delete + set | Same object reference in both | move |
Array support: Array mutations (push, pop, shift, unshift, splice) are automatically coalesced from valtio's low-level per-index changes into the correct JSON Patch operations.
pathToPointer(path)
Convert a property path array to an RFC 6901 JSON Pointer string. Useful if you need to build pointers from valtio paths outside of opsToJsonPatch.
import { pathToPointer } from '@laioutr/valtio-json-patch';
pathToPointer(['pages', 'p1', 'title']);
// '/pages/p1/title'
pathToPointer(['keys/with/slashes', 'tildes~here']);
// '/keys~1with~1slashes/tildes~0here'Batching and notifyInSync
By default, valtio collects all synchronous mutations and delivers them as a single batch (via microtask). This is the recommended mode because:
- Move detection requires the
deleteandsetto arrive in the same batch. - Array coalescing requires all ops from a single array method (e.g.,
spliceemits multiple internal changes) to arrive together. - Fewer callbacks means less overhead when mutations happen in rapid succession.
Setting notifyInSync: true delivers each mutation immediately as a separate callback. This is useful when you need synchronous patch delivery, but patches will be more granular: moves become separate remove + add, and array methods may produce multiple patches instead of single operations.
Limitations
sort() and reverse() produce per-index replace ops. Technically we can't identify these from the raw valtio ops. The patches are correct but verbose.
add vs replace distinction relies on whether the old value was undefined. If a property is explicitly set to undefined and later reassigned, the package emits add instead of replace. In practice, this is fine, as most JSON Patch libraries treat add on an existing object key the same as replace.
License
MIT
