CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-jsondiffpatch

JSON diff & patch library with support for objects, arrays, text diffs, and multiple output formats

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

options-configuration.mddocs/

Options and Configuration

Comprehensive configuration system for customizing diff behavior including array handling, text diffing, property filtering, and output formatting.

Capabilities

Options Interface

Complete configuration interface for controlling diff, patch, and clone behavior.

interface Options {
  /** Custom hash function for object matching in arrays */
  objectHash?: (item: object, index?: number) => string | undefined;
  
  /** Match array items by position instead of content */
  matchByPosition?: boolean;
  
  /** Array-specific diffing options */
  arrays?: {
    /** Detect and represent item moves within arrays */
    detectMove?: boolean;
    /** Include moved item value in delta (not just index) */
    includeValueOnMove?: boolean;
  };
  
  /** Text diffing configuration */
  textDiff?: {
    /** diff-match-patch instance for text diffing */
    diffMatchPatch: typeof diff_match_patch;
    /** Minimum string length to trigger text diffing */
    minLength?: number;
  };
  
  /** Filter function to exclude properties from diffing */
  propertyFilter?: (name: string, context: DiffContext) => boolean;
  
  /** Control value cloning during diff operations */
  cloneDiffValues?: boolean | ((value: unknown) => unknown);
  
  /** Omit old values from deletion deltas */
  omitRemovedValues?: boolean;
}

Configuration Options

Object Hash Function

Controls how objects are matched within arrays for optimal diff results.

/**
 * Custom hash function for object matching in arrays
 * @param item - Object to generate hash for
 * @param index - Position in array (optional)
 * @returns Unique identifier string or undefined for position-based matching
 */
objectHash?: (item: object, index?: number) => string | undefined;

Usage Examples:

import { create } from "jsondiffpatch";

// Match by ID field
const patcher = create({
  objectHash: (obj) => obj.id ? String(obj.id) : undefined
});

const delta = patcher.diff(
  [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }],
  [{ id: 2, name: "Bob", role: "admin" }, { id: 1, name: "Alice" }]
);
// Detects move of Alice and modification of Bob

// Multi-field hash
const complexPatcher = create({
  objectHash: (obj, index) => {
    if (obj.type === 'user' && obj.email) {
      return `user:${obj.email}`;
    }
    if (obj.type === 'product' && obj.sku) {
      return `product:${obj.sku}`;
    }
    return undefined; // Fall back to position matching
  }
});

// Conditional hash based on context
const contextPatcher = create({
  objectHash: (obj, index) => {
    // Only use ID for large arrays to improve performance
    if (index !== undefined && index > 100 && obj.id) {
      return String(obj.id);
    }
    return undefined;
  }
});

Array Configuration

Controls array diffing behavior including move detection and value inclusion.

arrays?: {
  /** Detect and represent item moves within arrays */
  detectMove?: boolean;
  /** Include moved item value in delta (not just index) */
  includeValueOnMove?: boolean;
};

Usage Examples:

import { create } from "jsondiffpatch";

// Disable move detection for performance
const noMovePatcher = create({
  arrays: {
    detectMove: false
  }
});

const delta1 = noMovePatcher.diff([1, 2, 3], [2, 3, 1]);
// Treats as delete + add instead of move

// Include values in move operations
const valueMovePatcher = create({
  arrays: {
    detectMove: true,
    includeValueOnMove: true
  }
});

const delta2 = valueMovePatcher.diff(
  [{ name: "Alice" }, { name: "Bob" }],
  [{ name: "Bob" }, { name: "Alice" }]
);
// Move delta includes the moved object value

// Position-based matching for simple arrays
const positionPatcher = create({
  matchByPosition: true,
  arrays: {
    detectMove: false
  }
});

const delta3 = positionPatcher.diff(["a", "b", "c"], ["x", "b", "c"]);
// Result: { 0: ["a", "x"] } - replaces first item

Text Diff Configuration

Enables automatic text diffing for long strings using diff-match-patch algorithm.

textDiff?: {
  /** diff-match-patch instance for text diffing */
  diffMatchPatch: typeof diff_match_patch;
  /** Minimum string length to trigger text diffing */
  minLength?: number;
};

Usage Examples:

import { create } from "jsondiffpatch";
import DiffMatchPatch from "diff-match-patch";

// Manual text diff configuration
const textPatcher = create({
  textDiff: {
    diffMatchPatch: DiffMatchPatch,
    minLength: 50 // Only diff strings over 50 characters
  }
});

const longText1 = "The quick brown fox jumps over the lazy dog. This is a longer text that will trigger text diffing.";
const longText2 = "The quick brown fox jumped over the lazy cat. This is a longer text that will trigger text diffing.";

const textDelta = textPatcher.diff({ content: longText1 }, { content: longText2 });
// Result includes text diff format instead of full string replacement

// Use with-text-diffs module for automatic setup
import { create as createWithText } from "jsondiffpatch/with-text-diffs";

const autoTextPatcher = createWithText({
  textDiff: {
    minLength: 30 // Override default minimum length
  }
});

Property Filtering

Excludes specific properties from diff operations based on custom logic.

/**
 * Filter function to exclude properties from diffing
 * @param name - Property name being considered
 * @param context - Diff context with left/right values and metadata
 * @returns true to include property, false to exclude
 */
propertyFilter?: (name: string, context: DiffContext) => boolean;

Usage Examples:

import { create } from "jsondiffpatch";

// Exclude private properties
const privatePatcher = create({
  propertyFilter: (name) => !name.startsWith('_')
});

const delta1 = privatePatcher.diff(
  { name: "Alice", _internal: "secret1" },
  { name: "Bob", _internal: "secret2" }
);
// Result: { name: ["Alice", "Bob"] } - _internal ignored

// Contextual filtering
const contextPatcher = create({
  propertyFilter: (name, context) => {
    // Skip metadata for temporary objects
    if (name === 'metadata' && context.left?.type === 'temp') {
      return false;
    }
    
    // Skip computed properties that depend on other changes
    if (name === 'computed' && (context.left?.dirty || context.right?.dirty)) {
      return false;
    }
    
    // Skip large arrays in certain contexts
    if (name === 'items' && Array.isArray(context.left?.[name]) && 
        context.left[name].length > 1000) {
      return false;
    }
    
    return true;
  }
});

// Performance-focused filtering
const perfPatcher = create({
  propertyFilter: (name, context) => {
    // Skip deep object diffing for performance
    const leftValue = context.left?.[name];
    const rightValue = context.right?.[name];
    
    if (typeof leftValue === 'object' && typeof rightValue === 'object' &&
        leftValue && rightValue && Object.keys(leftValue).length > 50) {
      return false;
    }
    
    return true;
  }
});

Clone Configuration

Controls how values are cloned during diff and patch operations.

/**
 * Control value cloning during diff operations
 * Can be boolean or custom clone function
 */
cloneDiffValues?: boolean | ((value: unknown) => unknown);

Usage Examples:

import { create } from "jsondiffpatch";

// Disable cloning for performance (modifies original objects)
const noClonePatcher = create({
  cloneDiffValues: false
});

// This will modify the original objects during patching
const original = { name: "Alice", tags: ["user"] };
const delta = { tags: { _t: "a", 1: ["admin"] } };
noClonePatcher.patch(original, delta);
console.log(original.tags); // ["user", "admin"] - original was modified

// Custom clone function
const customClonePatcher = create({
  cloneDiffValues: (value) => {
    // Custom cloning logic
    if (value instanceof Date) {
      return new Date(value.getTime());
    }
    if (Array.isArray(value)) {
      return value.map(item => customClonePatcher.clone(item));
    }
    if (value && typeof value === 'object') {
      const cloned = {};
      for (const [key, val] of Object.entries(value)) {
        if (key !== '_computed') { // Skip computed properties
          cloned[key] = customClonePatcher.clone(val);
        }
      }
      return cloned;
    }
    return value;
  }
});

Remove Value Configuration

Controls whether deleted values are included in deletion deltas.

/**
 * Omit old values from deletion deltas
 * Reduces delta size at cost of losing delete value information
 */
omitRemovedValues?: boolean;

Usage Examples:

import { create } from "jsondiffpatch";

// Standard behavior (includes removed values)
const standardPatcher = create();
const delta1 = standardPatcher.diff(
  { name: "Alice", age: 25, role: "user" },
  { name: "Alice", age: 26 }
);
// Result: { age: [25, 26], role: ["user", 0, 0] }

// Omit removed values for smaller deltas
const omitPatcher = create({
  omitRemovedValues: true
});
const delta2 = omitPatcher.diff(
  { name: "Alice", age: 25, role: "user" },
  { name: "Alice", age: 26 }
);
// Result: { age: [25, 26], role: [0, 0] } - removed value not stored

// Useful for large objects where removed values are not needed
const largePatcher = create({
  omitRemovedValues: true,
  propertyFilter: (name) => !name.startsWith('temp_')
});

Configuration Examples

Performance-Optimized Configuration

const perfPatcher = create({
  arrays: {
    detectMove: false // Disable expensive move detection
  },
  cloneDiffValues: false, // Disable cloning for speed
  omitRemovedValues: true, // Reduce delta size
  propertyFilter: (name) => {
    // Skip expensive-to-diff properties
    return !['computed', 'cache', 'metadata'].includes(name);
  }
});

Comprehensive Diff Configuration

const comprehensivePatcher = create({
  objectHash: (obj) => obj.id || obj.key || obj.name,
  arrays: {
    detectMove: true,
    includeValueOnMove: true
  },
  textDiff: {
    diffMatchPatch: DiffMatchPatch,
    minLength: 40
  },
  cloneDiffValues: true,
  propertyFilter: (name, context) => {
    // Include all properties except truly private ones
    return !name.startsWith('__');
  }
});

Minimal Delta Configuration

const minimalPatcher = create({
  omitRemovedValues: true,
  arrays: {
    detectMove: true,
    includeValueOnMove: false // Omit values from moves
  },
  propertyFilter: (name, context) => {
    // Only include changed properties that matter
    const essential = ['id', 'name', 'status', 'value'];
    return essential.includes(name) || !name.startsWith('meta');
  }
});

docs

core-operations.md

diffpatcher-class.md

formatters.md

index.md

options-configuration.md

tile.json