first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,138 @@
# utils
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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<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
- `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.