fix: apply remote patches addressing marks and markDefs elements as data patches#2974
Open
ryanbonial wants to merge 1 commit into
Open
fix: apply remote patches addressing marks and markDefs elements as data patches#2974ryanbonial wants to merge 1 commit into
ryanbonial wants to merge 1 commit into
Conversation
🦋 Changeset detectedLatest commit: 57f64bf The changes in this PR will be included in the next version bump. This PR includes changesets to release 14 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
@ryanbonial is attempting to deploy a commit to the Sanity Team on Vercel. A member of the Team first needs to authorize it. |
This was referenced Jul 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Remote patches produced by diffing tools (e.g.
@sanity/diff-patchin@portabletext/plugin-sdk-value) can address elements of sidecar arrays:span.marks[0],span.marks[-1],block.markDefs[_key=="..."]. These paths end in a keyed or numeric segment just like structural node paths, soapplyOperationrouted them through structural child insertion/removal against the owning node'schildren.The consequences in a two-editor session, whenever a collaborator toggles a decorator or annotation:
unset marks[0]/unset markDefs[_key=="..."]threwCannot apply an "unset" (node removal) operation ... because the node was not found., killing the consumer's sync actor. The editor kept accepting keystrokes but stopped receiving remote changes until reload.insert after marks[-1]silently wrote a boguschildren: ['strong']array onto the span. The corrupted span was then pushed back to the datastore and persisted.set marks[0]anddiffMatchPatchonmarks[0]were silently ignored, so the editors drifted apart.The fix teaches the engine to detect that such a path does not target the owning node's structural child array (
targetsStructuralChildren) and to apply the operation as a plain data patch on the root block (applyOnRootBlock, viaapplyAllfrom@portabletext/patches), matching what the datastore computed. This is the same fallback pattern thesetand property-unsethandlers already use for markDefs property paths. An alternative of rewriting these patches inplugin-sdk-valuewas rejected: it would only cover SDK apps and leave the engine bug reachable from every other remote patch source.What to review
packages/editor/src/engine/core/apply-operation.ts: the routing check and the data-patch fallback, including theset-based inverse computed for local sidecar unsets (remote patches skip inverses).packages/editor/src/internal-utils/applyPatch.ts: thediffMatchPatchfallback for non-textstring targets.getChildFieldNameis the right structural-field oracle for the check.Testing
packages/editor/tests/event.patches.sidecar-arrays.test.tsx: 6 regression tests using the exact patch shapesdiffValueemits for decorator/annotation toggles. All 6 fail onmain(three throw, three silently corrupt or no-op).packages/plugin-sdk-value/src/plugin.value-sync.browser.test.tsx: 2 end-to-end tests drivingValueSyncPluginthrough remote decorator and annotation toggles, covering the span split/merge +markDefsflow.