
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.
@sanity/diff-patch
Advanced tools
Generates a set of Sanity patches needed to change an item (usually a document) from one shape to another
Generate Sanity patch mutations by comparing two documents or values. This library creates conflict-resistant patches designed for collaborative editing environments where multiple users may be editing the same document simultaneously.
Used internally by the Sanity App SDK for its collaborative editing system.
npm install @sanity/diff-patch
diffPatch(source, target, options?)Generate patch mutations to transform a source document into a target document.
Parameters:
source: DocumentStub - The original documenttarget: DocumentStub - The desired document stateoptions?: PatchOptions - Configuration optionsReturns: SanityPatchMutation[] - Array of patch mutations
Options:
interface PatchOptions {
id?: string // Document ID (extracted from _id if not provided)
basePath?: Path // Base path for patches (default: [])
ifRevisionID?: string | true // Revision lock for optimistic updates
}
Example:
import {diffPatch} from '@sanity/diff-patch'
const source = {
_id: 'movie-123',
_type: 'movie',
_rev: 'abc',
title: 'The Matrix',
year: 1999,
}
const target = {
_id: 'movie-123',
_type: 'movie',
title: 'The Matrix Reloaded',
year: 2003,
director: 'The Wachowskis',
}
const mutations = diffPatch(source, target, {ifRevisionID: true})
// [
// {
// patch: {
// id: 'movie-123',
// ifRevisionID: 'abc',
// set: {
// title: 'The Matrix Reloaded',
// year: 2003,
// director: 'The Wachowskis'
// }
// }
// }
// ]
diffValue(source, target, basePath?)Generate patch operations for values without document wrapper.
Parameters:
source: unknown - The original valuetarget: unknown - The desired value statebasePath?: Path - Base path to prefix operations (default: [])Returns: SanityPatchOperations[] - Array of patch operations
Example:
import {diffValue} from '@sanity/diff-patch'
const source = {
name: 'John',
tags: ['developer'],
}
const target = {
name: 'John Doe',
tags: ['developer', 'typescript'],
active: true,
}
const operations = diffValue(source, target)
// [
// {
// set: {
// name: 'John Doe',
// 'tags[1]': 'typescript',
// active: true
// }
// }
// ]
// With base path
const operations = diffValue(source, target, ['user', 'profile'])
// [
// {
// set: {
// 'user.profile.name': 'John Doe',
// 'user.profile.tags[1]': 'typescript',
// 'user.profile.active': true
// }
// }
// ]
The library generates patches that preserve user intent and minimize conflicts in collaborative scenarios:
// Starting document
const originalDoc = {
_id: 'blog-post-123',
_type: 'blogPost',
title: 'Getting Started with Sanity',
paragraphs: [
{
_key: 'intro',
_type: 'paragraph',
text: 'Sanity is a complete content operating system for modern applications.',
},
{
_key: 'benefits',
_type: 'paragraph',
text: 'It offers real-time collaboration and gives developers controll over the entire stack.',
},
{
_key: 'conclusion',
_type: 'paragraph',
text: 'Learning Sanity will help you take control of your content workflow.',
},
],
}
// User A reorders paragraphs AND fixes a typo
const userAChanges = {
...originalDoc,
paragraphs: [
{
_key: 'intro',
_type: 'paragraph',
text: 'Sanity is a complete content operating system for modern applications.',
},
{
_key: 'conclusion', // Moved conclusion before benefits
_type: 'paragraph',
text: 'Learning Sanity will help you take control of your content workflow.',
},
{
_key: 'benefits',
_type: 'paragraph',
text: 'It offers real-time collaboration and gives developers control over the entire stack.', // Fixed typo: "controll" → "control"
},
],
}
// User B simultaneously improves the intro text
const userBChanges = {
...originalDoc,
paragraphs: [
{
_key: 'intro',
_type: 'paragraph',
text: 'Sanity is a complete content operating system that gives developers control over the entire stack.', // Added more specific language about developer control
},
{
_key: 'benefits',
_type: 'paragraph',
text: 'It offers real-time collaboration and gives developers control over the entire stack.',
},
{
_key: 'conclusion',
_type: 'paragraph',
text: 'Learning Sanity will help you take control of your content workflow.',
},
],
}
// Generate patches that capture each user's intent
const patchA = diffPatch(originalDoc, userAChanges)
const patchB = diffPatch(originalDoc, userBChanges)
// Apply both patches - they merge successfully because they target different aspects
// User A's reordering and typo fix + User B's content improvement both apply
const finalMergedResult = {
_id: 'blog-post-123',
_type: 'blogPost',
title: 'Getting Started with Sanity',
paragraphs: [
{
_key: 'intro',
_type: 'paragraph',
text: 'Sanity is a complete content operating system that gives developers control over the entire stack.', // ✅ User B's improvement
},
{
_key: 'conclusion', // ✅ User A's reordering
_type: 'paragraph',
text: 'Learning Sanity will help you take control of your content workflow.',
},
{
_key: 'benefits',
_type: 'paragraph',
text: 'It offers real-time collaboration and gives developers control over the entire stack.', // ✅ User A's typo fix
},
],
}
When comparing strings, the library attempts to use diff-match-patch to generate granular text patches instead of simple replacements. This preserves editing intent and enables better conflict resolution.
Automatic selection criteria:
set operationsset (indicates replacement vs. editing)_ (e.g. _type, _key) always use set operations as these are not typically edited by usersPerformance rationale:
These thresholds are based on performance testing of the underlying @sanity/diff-match-patch library on an M2 MacBook Pro:
The 40% change ratio threshold catches problematic replacement scenarios while allowing the algorithm to excel at insertions, deletions, and small edits.
Migration from v5:
Version 5 allowed configuring diff-match-patch behavior with lengthThresholdAbsolute and lengthThresholdRelative options. Version 6 removes these options in favor of tested defaults that provide consistent performance across real-world editing patterns. This allows us to change the behavior of this over time to better meet performance needs.
Keyed arrays: Arrays containing objects with _key properties are diffed by key rather than index, producing more stable patches for collaborative editing.
Index-based arrays: Arrays without keys are diffed by index position.
Undefined values: When undefined values are encountered in arrays, they are converted to null. This follows the same behavior as JSON.stringify() and ensures consistent serialization. To remove undefined values before diffing:
const cleanArray = array.filter((item) => typeof item !== 'undefined')
The following keys are ignored at the root of the document when diffing a document as they are managed by Sanity:
_id_type_createdAt_updatedAt_rev_id differs between documents and no explicit id option provided_type at document rootDiffErrorifRevisionID: true but no _rev in source documentMIT © Sanity.io
FAQs
Generates a set of Sanity patches needed to change an item (usually a document) from one shape to another
The npm package @sanity/diff-patch receives a total of 525,111 weekly downloads. As such, @sanity/diff-patch popularity was classified as popular.
We found that @sanity/diff-patch demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 90 open source maintainers 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.