5.6 KiB
utils
(no submodules)
Purpose
utils.ts provides the low-level primitives that the entire @suaveplan/diff pipeline is built upon: deep clone, deep equality, FNV-1a hashing, and a family of path-navigation helpers for immutable and mutable document manipulation. Every operation is JSON-safe — it operates exclusively on the JsonValue universe (null | boolean | number | string | JsonValue[] | { [k: string]: JsonValue }).
The path-navigation helpers implement the RFC 6902 semantics for add, remove, replace, move, and copy at the document level, freeing the higher-level patch engine from the mechanics of array splice and object key management.
Features
deepClone— recursive JSON-safe clone withoutJSON.parse/stringifyoverheaddeepEqual— structural equality for arbitrarily nested JSON valueshashValue— FNV-1a 32-bit hash of JSON serialisation (for LCS bucketing)isObject/isPrimitive— type-guard predicatesgetValueAtPath— safe read at a path; returnsundefinedon missing keyssetValueAtPath/setValueAtPathMut— immutable and in-place writeinsertValueAtPath/insertValueAtPathMut— immutable and in-place insert (array splice)deleteValueAtPath/deleteValueAtPathMut— immutable and in-place delete
Usage: Basic Example
import { deepClone, deepEqual, getValueAtPath } from "@suaveplan/diff/utils";
import type { JsonValue } from "@suaveplan/diff/utils";
const doc: JsonValue = { users: [{ name: "Alice" }, { name: "Bob" }] };
const clone = deepClone(doc);
clone.users[0].name = "Carol";
console.log(doc.users[0].name); // "Alice" — original unchanged
deepEqual(doc, clone); // false — they now differ
const name = getValueAtPath(doc, ["users", 1, "name"]);
// "Bob"
Usage: Advanced Example
import {
setValueAtPath, insertValueAtPath, deleteValueAtPath,
setValueAtPathMut, hashValue,
} from "@suaveplan/diff/utils";
import type { JsonValue } from "@suaveplan/diff/utils";
const doc: JsonValue = { items: ["a", "b", "c"] };
// Immutable set — original unchanged
const updated = setValueAtPath(doc, ["items", 1], "B");
// doc still: { items: ["a", "b", "c"] }
// updated: { items: ["a", "B", "c"] }
// Array insert — existing elements shift right
const inserted = insertValueAtPath(doc, ["items", 1], "X");
// { items: ["a", "X", "b", "c"] }
// Append via "-" token
const appended = insertValueAtPath(doc, ["items", "-"], "Z");
// { items: ["a", "b", "c", "Z"] }
// Delete
const deleted = deleteValueAtPath(doc, ["items", 0]);
// { items: ["b", "c"] }
// In-place mutation (used internally by the patch engine)
const mutable = deepClone(doc) as { items: string[] };
setValueAtPathMut(mutable as JsonValue, ["items", 2], "C");
// Hash for LCS bucketing
hashValue({ x: 1 }); // e.g. "3a2b1c4d"
API Reference
JsonValue
type JsonValue = null | boolean | number | string | JsonValue[] | { [k: string]: JsonValue };
isObject(v: unknown): v is Record<string, unknown>
Returns true for non-null, non-array plain objects.
isPrimitive(v: unknown): v is null | boolean | number | string
Returns true for all primitive JSON value types.
deepClone<T>(value: T): T
Recursively clones arrays and objects; returns primitives as-is. Does not use JSON.parse/stringify.
deepEqual(a: unknown, b: unknown): boolean
Structural equality: handles null, same-type primitives, same-length arrays with equal elements, and objects with equal key sets and equal values. Returns false for type mismatches.
hashValue(value: unknown): string
FNV-1a 32-bit hash of JSON.stringify(value). Returns an 8-character lowercase hex string.
getValueAtPath(doc: JsonValue, path: Path): JsonValue | undefined
Traverses doc following path. Returns undefined if any step fails (missing key, index out of range, traversing through a primitive).
setValueAtPath(doc: JsonValue, path: Path, value: JsonValue): JsonValue
Returns a new deep clone of doc with value written at path. The original doc is not modified.
setValueAtPathMut(doc: JsonValue, path: Path, value: JsonValue): void
Sets value at path in doc in-place.
insertValueAtPath(doc: JsonValue, path: Path, value: JsonValue): JsonValue
Returns a new deep clone with value inserted. For arrays, elements at >= index shift right. The "-" segment appends to the array.
insertValueAtPathMut(doc: JsonValue, path: Path, value: JsonValue): void
Inserts value in-place using array splice or object key assignment.
deleteValueAtPath(doc: JsonValue, path: Path): JsonValue
Returns a new deep clone with the element at path removed. Array elements shift left.
deleteValueAtPathMut(doc: JsonValue, path: Path): void
Removes the element at path in-place.
Implementation Notes
deepCloneavoidsJSON.parse/stringifyto preserve type fidelity for values that would be mangled by JSON roundtrip (e.g.,undefinedin arrays becomesnull). This also avoids the overhead of two serialisation passes.resolveParentis a shared internal helper that walks to the parent of the target key, returning a{ parent, key }pair. This eliminates duplication between the six set/insert/delete variants.- The
"-"append token is handled in bothsetValueAtPathMutandinsertValueAtPathMutto match RFC 6902 §4.1 semantics for theaddoperation. hashValuedeliberately uses FNV-1a rather than a cryptographic hash for performance in the LCS hot path. Collision probability over realistic array sizes is negligible.