# 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 without `JSON.parse/stringify` overhead - **`deepEqual`** — structural equality for arbitrarily nested JSON values - **`hashValue`** — FNV-1a 32-bit hash of JSON serialisation (for LCS bucketing) - **`isObject` / `isPrimitive`** — type-guard predicates - **`getValueAtPath`** — safe read at a path; returns `undefined` on missing keys - **`setValueAtPath` / `setValueAtPathMut`** — immutable and in-place write - **`insertValueAtPath` / `insertValueAtPathMut`** — immutable and in-place insert (array splice) - **`deleteValueAtPath` / `deleteValueAtPathMut`** — immutable and in-place delete ## Usage: Basic Example ```ts 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 ```ts 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` ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [k: string]: JsonValue }; ``` ### `isObject(v: unknown): v is Record` 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(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 - `deepClone` avoids `JSON.parse/stringify` to preserve type fidelity for values that would be mangled by JSON roundtrip (e.g., `undefined` in arrays becomes `null`). This also avoids the overhead of two serialisation passes. - `resolveParent` is 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 both `setValueAtPathMut` and `insertValueAtPathMut` to match RFC 6902 §4.1 semantics for the `add` operation. - `hashValue` deliberately uses FNV-1a rather than a cryptographic hash for performance in the LCS hot path. Collision probability over realistic array sizes is negligible.