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,71 @@
# `StateReconciler`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Resolves the five reconciliation strategies (`latest-wins`, `remote-wins`, `local-wins`, `deep-merge`, `custom`) against a single `ConflictInput` tuple and returns a typed `ConflictResolution`. The path strategy registry and sync engine consult the reconciler — the logic is pure so consumers can also call it directly for tests and audits.
## Features
- Closed `ReconciliationStrategy` switch with `winner` reported on every resolution (`local` / `remote` / `merged`).
- `latest-wins` ties go to the remote so the receiver does not stall when timestamps coincide.
- `deep-merge` recursively combines plain objects; scalar / array conflicts deterministically pick the remote so the merge is reproducible.
- `custom` strategy delegates to a `CustomMergeFn`; absent merge falls back to the local value (caller bug — registry rejects this at registration time so this is defensive only).
## Usage: Basic Example
```ts
import {
StateReconciler,
type ConflictInput,
} from "@suaveplan/network-plugin-state";
const r = new StateReconciler();
const input: ConflictInput = {
path: "score",
localValue: 1,
localTimestamp: 1,
remoteValue: 2,
remoteTimestamp: 2,
};
const out = r.resolve("latest-wins", input);
// out.resolvedValue === 2, out.winner === "remote"
```
## Usage: Advanced Example
```ts
import {
StateReconciler,
deepMerge,
type ConflictInput,
} from "@suaveplan/network-plugin-state";
const r = new StateReconciler();
const input: ConflictInput = {
path: "inventory",
localValue: { coins: 10, gems: 1 },
localTimestamp: 100,
remoteValue: { coins: 5, scrolls: 2 },
remoteTimestamp: 99,
};
const dm = r.resolve("deep-merge", input);
// dm.resolvedValue === { coins: 5, gems: 1, scrolls: 2 } — remote wins on conflict, locals merged in
const merged = deepMerge(input.localValue, input.remoteValue);
```
## API Reference
- `class StateReconciler`
- `resolve(strategy, input, merge?): ConflictResolution`.
- `function deepMerge(local, remote): StateValue | undefined` — exported pure helper for consumers that want the merge result without the wrapper.
## Implementation Notes
- The reconciler is stateless — every `resolve` call is pure given its inputs. Subscribers can run resolutions in parallel without coordination.
- `latest-wins`'s tie-break (remote on equal timestamps) avoids ping-pong cycles where two peers keep "winning" the same conflict at the same time.
- `deep-merge`'s scalar-tie remote-wins rule means the merge is associative — `deepMerge(a, b) === deepMerge(deepMerge(a, b), b)` for all inputs — which is essential for consumers that re-apply the merge under retries.
- `custom` strategy without a `merge` is defensive: the registry's `addStrategy` raises `STRATEGY_MISSING_MERGE` at registration, so the in-resolve fallback only activates if a caller bypasses the registry.