Gathering detailed insights and metrics for @sanity/diff-patch
Gathering detailed insights and metrics for @sanity/diff-patch
Gathering detailed insights and metrics for @sanity/diff-patch
Gathering detailed insights and metrics for @sanity/diff-patch
sanity-diff-patch
Generates a set of Sanity patches needed to change an item (usually a document) from one shape to another
@sanity/diff-match-patch
Robust diff, match and patch algorithms to perform operations required for synchronizing plain text
@rexxars/sanity-diff-patch
Generates a set of Sanity patches by comparing two JSON structures
Generates a set of Sanity patches by comparing two JSON structures
npm install @sanity/diff-patch
Typescript
Module System
Min. Node Version
Node Version
NPM Version
TypeScript (100%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
29 Stars
94 Commits
1 Forks
1 Watchers
16 Branches
35 Contributors
Updated on Jun 14, 2025
Latest Version
6.0.0
Package Id
@sanity/diff-patch@6.0.0
Unpacked Size
150.80 kB
Size
37.55 kB
File Count
16
NPM Version
10.9.2
Node Version
22.16.0
Published on
Jun 13, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
1
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.
1npm 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:
1interface PatchOptions { 2 id?: string // Document ID (extracted from _id if not provided) 3 basePath?: Path // Base path for patches (default: []) 4 ifRevisionID?: string | true // Revision lock for optimistic updates 5}
Example:
1import {diffPatch} from '@sanity/diff-patch' 2 3const source = { 4 _id: 'movie-123', 5 _type: 'movie', 6 _rev: 'abc', 7 title: 'The Matrix', 8 year: 1999, 9} 10 11const target = { 12 _id: 'movie-123', 13 _type: 'movie', 14 title: 'The Matrix Reloaded', 15 year: 2003, 16 director: 'The Wachowskis', 17} 18 19const mutations = diffPatch(source, target, {ifRevisionID: true}) 20// [ 21// { 22// patch: { 23// id: 'movie-123', 24// ifRevisionID: 'abc', 25// set: { 26// title: 'The Matrix Reloaded', 27// year: 2003, 28// director: 'The Wachowskis' 29// } 30// } 31// } 32// ]
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:
1import {diffValue} from '@sanity/diff-patch' 2 3const source = { 4 name: 'John', 5 tags: ['developer'], 6} 7 8const target = { 9 name: 'John Doe', 10 tags: ['developer', 'typescript'], 11 active: true, 12} 13 14const operations = diffValue(source, target) 15// [ 16// { 17// set: { 18// name: 'John Doe', 19// 'tags[1]': 'typescript', 20// active: true 21// } 22// } 23// ] 24 25// With base path 26const operations = diffValue(source, target, ['user', 'profile']) 27// [ 28// { 29// set: { 30// 'user.profile.name': 'John Doe', 31// 'user.profile.tags[1]': 'typescript', 32// 'user.profile.active': true 33// } 34// } 35// ]
The library generates patches that preserve user intent and minimize conflicts in collaborative scenarios:
1// Starting document 2const originalDoc = { 3 _id: 'blog-post-123', 4 _type: 'blogPost', 5 title: 'Getting Started with Sanity', 6 paragraphs: [ 7 { 8 _key: 'intro', 9 _type: 'paragraph', 10 text: 'Sanity is a complete content operating system for modern applications.', 11 }, 12 { 13 _key: 'benefits', 14 _type: 'paragraph', 15 text: 'It offers real-time collaboration and gives developers controll over the entire stack.', 16 }, 17 { 18 _key: 'conclusion', 19 _type: 'paragraph', 20 text: 'Learning Sanity will help you take control of your content workflow.', 21 }, 22 ], 23} 24 25// User A reorders paragraphs AND fixes a typo 26const userAChanges = { 27 ...originalDoc, 28 paragraphs: [ 29 { 30 _key: 'intro', 31 _type: 'paragraph', 32 text: 'Sanity is a complete content operating system for modern applications.', 33 }, 34 { 35 _key: 'conclusion', // Moved conclusion before benefits 36 _type: 'paragraph', 37 text: 'Learning Sanity will help you take control of your content workflow.', 38 }, 39 { 40 _key: 'benefits', 41 _type: 'paragraph', 42 text: 'It offers real-time collaboration and gives developers control over the entire stack.', // Fixed typo: "controll" → "control" 43 }, 44 ], 45} 46 47// User B simultaneously improves the intro text 48const userBChanges = { 49 ...originalDoc, 50 paragraphs: [ 51 { 52 _key: 'intro', 53 _type: 'paragraph', 54 text: 'Sanity is a complete content operating system that gives developers control over the entire stack.', // Added more specific language about developer control 55 }, 56 { 57 _key: 'benefits', 58 _type: 'paragraph', 59 text: 'It offers real-time collaboration and gives developers control over the entire stack.', 60 }, 61 { 62 _key: 'conclusion', 63 _type: 'paragraph', 64 text: 'Learning Sanity will help you take control of your content workflow.', 65 }, 66 ], 67} 68 69// Generate patches that capture each user's intent 70const patchA = diffPatch(originalDoc, userAChanges) 71const patchB = diffPatch(originalDoc, userBChanges) 72 73// Apply both patches - they merge successfully because they target different aspects 74// User A's reordering and typo fix + User B's content improvement both apply 75const finalMergedResult = { 76 _id: 'blog-post-123', 77 _type: 'blogPost', 78 title: 'Getting Started with Sanity', 79 paragraphs: [ 80 { 81 _key: 'intro', 82 _type: 'paragraph', 83 text: 'Sanity is a complete content operating system that gives developers control over the entire stack.', // ✅ User B's improvement 84 }, 85 { 86 _key: 'conclusion', // ✅ User A's reordering 87 _type: 'paragraph', 88 text: 'Learning Sanity will help you take control of your content workflow.', 89 }, 90 { 91 _key: 'benefits', 92 _type: 'paragraph', 93 text: 'It offers real-time collaboration and gives developers control over the entire stack.', // ✅ User A's typo fix 94 }, 95 ], 96}
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:
1const 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 rootDiffError
ifRevisionID: true
but no _rev
in source documentMIT © Sanity.io
No vulnerabilities found.
No security vulnerabilities found.