Skip to content

v6.0.0

Latest

Choose a tag to compare

@github-actions github-actions released this 13 Jun 20:07

6.0.0 (2025-06-13)

⚠ BREAKING CHANGES

  • API Rename and Visibility:**
    • The diffItem function is no longer exported. Its functionality is now primarily internal.
    • A new function diffValue(source: unknown, target: unknown, basePath?: Path): SanityPatchOperations[] is introduced and exported. This function generates an array of SanityPatchOperations (which are plain objects like {set: {...}}, {unset: [...]}) based on the differences between source and target values. It does not wrap these operations in the SanityPatchMutation structure.
    • The diffPatch function (which diffs documents and returns SanityPatchMutation[]) now internally calls diffItem and then uses the refactored serializePatches to construct the final mutations. The logic for adding id and ifRevisionID to the patch mutations now resides within diffPatch.
  • Patch Type Refinements:**
    • Removed older, more generic patch types like SetPatch, InsertAfterPatch, SanitySetPatch, SanityUnsetPatch, SanityInsertPatch, and SanityDiffMatchPatch from the public API (some were previously exported from patches.ts).
    • Introduced new, more specific types for patch operations:
      • SanitySetPatchOperation ({ set: Record<string, unknown> })
      • SanityUnsetPatchOperation ({ unset: string[] })
      • SanityInsertPatchOperation ({ insert: { before/after/replace: string, items: unknown[] } })
      • SanityDiffMatchPatchOperation ({ diffMatchPatch: Record<string, string> })
    • The SanityPatchOperations type is now a Partial union of these new operation types, reflecting that a single patch object from diffValue will contain one or more of these operations.
    • The SanityPatch type (used within SanityPatchMutation) now extends SanityPatchOperations and includes id and optional ifRevisionID.
    • The internal Patch type (used by diffItem) remains but is now an internal detail.
  • Refactored serializePatches Function:
    • The serializePatches function now takes an array of internal Patch objects and returns an array of SanityPatchOperation[] (the raw operation objects like {set: {...}}).
    • It no longer handles adding id or ifRevisionID; this responsibility is moved to the diffPatch function.
    • The logic for grouping set, unset, insert, and diffMatchPatch operations into distinct objects in the output array has been improved for clarity.
  • Refactored diffPatch Function:
    • Now calls the internal diffItem to get the raw patch list.
    • Calls the refactored serializePatches to get SanityPatchOperations[].
    • Maps over these operations to create SanityPatchMutation[], adding the id to each and ifRevisionID only to the first patch mutation in the array.
  • JSDoc Updates:
    • Updated JSDoc for diffValue to clearly explain its purpose, parameters, and return type.
    • Updated JSDoc for diffPatch and internal types to reflect the changes.

Rationale:

  • Clearer Public API: diffValue provides a more intuitive name for diffing arbitrary JavaScript values and returning the raw operations, distinct from diffPatch which is document-centric.
  • Improved Type Safety & Granularity: The new Sanity...Operation types are more precise and make it easier to work with the different kinds of patch operations programmatically.
  • Correct ifRevisionID Handling: Ensuring ifRevisionID is only on the first patch of a transaction is crucial for correct optimistic locking in Sanity.
  • Better Separation of Concerns: diffItem focuses on generating a flat list of diffs, serializePatches (as used by diffValue) groups them into operations, and diffPatch handles the document-specific concerns like _id and ifRevisionID.

This refactor provides a cleaner and more robust API for generating patches, both for full documents and for arbitrary values.

  • remove undefined-to-null conversion warnings and simplify internal APIs (#38)

*   Removed the `diffMatchPatch` options (`enabled`, `lengthThresholdAbsolute`, `lengthThresholdRelative`) from `PatchOptions`.
*   Removed the `DiffMatchPatchOptions` and `DiffOptions` (which included `diffMatchPatch`) interfaces from the public API.
*   Removed the internal `mergeOptions` function and the DMP-specific parts of `defaultOptions`.
  • New Performance-Based Heuristics for DMP:
    • Introduced a new exported utility function shouldUseDiffMatchPatch(source: string, target: string): boolean. This function encapsulates the new logic for deciding whether to use DMP.
    • The decision is now based on:
      • Document Size Limit: Documents larger than 1MB (DMP_MAX_DOCUMENT_SIZE) will use set operations.
      • Change Ratio Threshold: If more than 40% (DMP_MAX_CHANGE_RATIO) of the text changes, set is used (indicates replacement vs. editing).
      • Small Document Optimization: Documents smaller than 10KB (DMP_MIN_SIZE_FOR_RATIO_CHECK) always use DMP, as performance is consistently high for these.
      • System Key Protection: Properties starting with _ (system keys) continue to use set operations.
    • Added extensive JSDoc to shouldUseDiffMatchPatch detailing the heuristic rationale, performance characteristics (based on testing @sanity/diff-match-patch on an M2 MacBook Pro), algorithm details, and test methodology.
  • Internal Simplification:
    • The internal getDiffMatchPatch function now uses shouldUseDiffMatchPatch to make its decision and no longer accepts DMP-related options.
    • Simplified the call to the underlying @sanity/diff-match-patch library within getDiffMatchPatch to use makePatches(source, target) directly. This is more concise and leverages the internal optimizations of that library, with performance validated to be equivalent to the previous multi-step approach.
  • Constants: Introduced SYSTEM_KEYS, DMP_MAX_DOCUMENT_SIZE, DMP_MAX_CHANGE_RATIO, and DMP_MIN_SIZE_FOR_RATIO_CHECK to define these thresholds.
  • Test Updates: Snapshots have been updated to reflect the new DMP behavior based on these heuristics.

Rationale for Change:

The previous configurable thresholds for DMP were somewhat arbitrary and could lead to suboptimal performance or overly verbose patches in certain scenarios. This change is based on empirical performance testing of the @sanity/diff-match-patch library itself. The new heuristics are designed to:

  • Optimize for common editing patterns: Ensure fast performance for keystrokes and small pastes, which are the most frequent operations.
  • Prevent performance degradation: Avoid triggering complex and potentially slow DMP algorithm paths when users perform large text replacements (e.g., pasting entirely new content).
  • Simplify the API: Remove the burden of configuration from the user, providing sensible defaults.
  • Maintain conflict-resistance: Continue to leverage DMP's strengths for collaborative editing where appropriate.

By hardcoding these well-tested heuristics, we aim for a more robust and performant string diffing strategy by default.

Features

  • add key-based reordering support for keyed object arrays (#41) (27dcdc2)
  • remove undefined-to-null conversion warnings and simplify internal APIs (#38) (86cff6e)
  • replace diffItem with diffValue (#39) (b8ad36a)
  • replace configurable DMP with perf-based heuristics (#36) (9577019)

Bug Fixes


This release is also available on: