Introduction to keyfold
Relevant source files
The following files were used as context for generating this wiki page:
keyfold is a library designed to immutably fold partial deltas into plain state trees. Its primary differentiator from standard JSON Merge Patch (RFC 7396) is its ability to perform identity-keyed list reconciliation. While standard merge tools often treat arrays as opaque or match by index—which can lead to data corruption in lists—keyfold matches configured list items by a unique identity field, merging them in place, appending new items, or deleting them via tombstones.
Sources: README.md:1-20, AGENTS.md:3-5
The library is built as a zero-dependency, tree-shakeable ESM package. It prioritizes technical invariants such as structural sharing (returning the original base reference if no changes occur) and failure safety (ensuring that a throw never leaves a partially mutated tree). It is intended for use in environments where partial updates arrive from a single authoritative source, such as websocket reducers, optimistic UI state, or form autosave mechanisms.
Sources: README.md:22-30, AGENTS.md:13-20
Core Architecture and Data Flow
The architecture of keyfold is split into a configuration/compilation phase and a runtime execution phase. Users define a merger by providing path policies that dictate how specific branches of the state tree should be handled.
High-Level Flow
The following diagram illustrates how keyfold processes a base state and a delta using a pre-compiled merger function.
A brief overview of the flow: Configuration is compiled once into a policy tree, which the merger then traverses alongside the base and delta objects to produce the new state.
Sources: src/merge.ts:16-28, src/paths.ts:25-30
Configuration and Path Policies
keyfold uses a path-based configuration system to determine which parts of the state tree should be deep-merged, replaced wholesale, or reconciled as keyed lists.
Policy Configuration Options
| Option | Type | Description |
|---|---|---|
keyBy | Record<string, string> | Maps a dot-separated path to the field used as a unique identifier for list reconciliation. |
replace | string[] | Specifies paths that should be swapped wholesale without recursing or merging. |
wireDeletes | boolean | If true, enables the interpretation of @@keyfold/delete as a field deletion token (useful for JSON). |
Sources: src/types.ts:5-23, README.md:58-75
Path Syntax
- Dot Notation: Paths are property names separated by dots (e.g.,
order.customer). - Item Marker
[]: Represents "inside each keyed item of the preceding list". For example,items[].tagsaddresses thetagsfield within every item of theitemslist. - Validation: Policies are validated at creation. Replaced paths cannot have child policies, and keyed item markers cannot follow unkeyed lists.
Sources: src/paths.ts:89-110, README.md:144-155
Keyed-List Reconciliation
Reconciliation is the core logic that differentiates keyfold from other merge tools. When a path is marked with keyBy, the library builds a map of the delta items by their identity before processing the base list.
Reconciliation Logic
- Index Delta: Creates a Map of identity values to "patches" (the delta items).
- Scan Base: Iterates through the base list. If a base item's identity exists in the delta map:
- If the delta is a tombstone (
$delete: true), the item is removed. - Otherwise, the base item and delta item are merged.
- If the delta is a tombstone (
- Append New: Any items remaining in the delta map (not found in the base) are appended to the end of the list.
- Order Preservation: Survivors retain their relative order from the base list.
Sources: src/reconcile.ts:15-56, README.md:12-20
Reconciliation Sequence
The diagram shows the standard reconciliation loop where base items are matched against the indexed delta map.
Sources: src/reconcile.ts:25-54
Immutability and Structural Sharing
keyfold adheres to strict immutability. It never mutates the base or delta inputs. To optimize performance and memory, it employs structural sharing:
- Reference Preservation: If a sub-branch of the state tree is not mentioned in the delta, the resulting tree retains the exact reference to the original sub-branch.
- Identity Result: If the final result is semantically identical to the
base(e.g., a no-op delta), the library returns the originalbasereference itself. - Lazy Copying: Objects are only shallow-copied at the moment a change is detected within them.
Sources: src/merge.ts:98-112, AGENTS.md:15-18, README.md:157-170
Safety Constraints
To ensure reliability and security, the library enforces several runtime and static constraints:
- Unsafe Keys: Recursive folds automatically ignore
__proto__,constructor, andprototypekeys in deltas to prevent prototype pollution. - Atomic Types: Built-ins like
Date,Map,Set, andRegExpare treated as atomic. They are replaced wholesale rather than merged. Custom classes can be marked asAtomicin the type signature to enforce similar behavior. - Identity Integrity: Identity values must be unique, non-
NaNstrings or numbers. Duplicate identities in either the base or delta will trigger aKeyfoldMergeError.
Sources: src/objects.ts:2-15, src/types.ts:63-75, AGENTS.md:28-35, src/reconcile.ts:98-125
Conclusion
keyfold provides a specialized solution for managing complex state updates by introducing identity-aware reconciliation to the immutable fold pattern. By allowing developers to explicitly define reconciliation keys and replacement boundaries, it offers a deterministic and safe way to handle partial deltas in nested data structures without the overhead of a full state management framework.
Sources: README.md:1-10, AGENTS.md:43-50