Configuration Errors
Relevant source files
The following files were used as context for generating this wiki page:
Configuration Errors in the Keyfold library represent failures during the initialization phase of a merger. These errors are encapsulated by the KeyfoldConfigError class and occur when the MergeOptions provided to createMerger are malformed, contradictory, or describe unreachable logic.
The primary goal of this error-handling system is to validate path policies—such as keyBy and replace—before any state merging occurs. This ensures that policies are compiled once and that failures are caught early, preventing runtime overhead or inconsistent states during the actual merge process.
Sources: src/errors.ts:2-4, src/paths.ts:21-23, src/merge.ts:16-20
The KeyfoldConfigError Class
KeyfoldConfigError is a specialized error class that extends the native Error object. It is specifically thrown by the createMerger function when the provided configuration options violate the library's constraints.
export class KeyfoldConfigError extends Error {
override readonly name = "KeyfoldConfigError";
}
Sources: src/errors.ts:2-4
Configuration Validation Flow
Validation occurs within the compileOptions function. This function iterates through keyBy and replace policies to build a PolicyNode tree. If any validation check fails, a KeyfoldConfigError is thrown immediately, halting the creation of the merger.
The diagram shows the sequential validation steps performed during option compilation.
Sources: src/paths.ts:21-72, src/merge.ts:21-22
Common Configuration Violations
The library enforces several strict rules regarding path syntax and policy interaction. Violations of these rules result in a KeyfoldConfigError.
Invalid Path Syntax
Paths must be dot-separated properties with optional [] suffixes. Keyfold rejects paths that use empty segments, double markers, or unsafe property names (like constructor or __proto__).
| Violation | Example Path | Reason |
|---|---|---|
| Empty Segment | order..items | Consecutive dots are invalid. |
| Bare Marker | order.[] | Property name is missing before the marker. |
| Double Marker | items[][] | Multi-dimensional list markers are not supported. |
Trailing Marker in keyBy | items[] | keyBy must address the list, not the items. |
Sources: test/config.test.ts:5-13, src/paths.ts:40-46, src/paths.ts:98-106
Reserved Identity Fields
When configuring keyBy, the identity field (the primary key for list reconciliation) cannot be a reserved keyword.
- Reserved Fields:
$delete,__proto__,constructor,prototype. - Format: Must be a valid non-reserved property string.
Sources: test/config.test.ts:18-24, src/paths.ts:17, src/paths.ts:114-118
Policy Reachability and Conflicts
A major source of configuration errors is "shadowing," where one policy makes another unreachable.
- Broad Replacement: If
replace: ['a']is set, a more specific policy likekeyBy: { 'a.b': 'id' }is unreachable because thereplacepolicy ataprevents recursion intob. - Duplicate Paths: Defining the same path multiple times in the
replacearray is prohibited. - Missing KeyBy for Items: Addressing items with
[](e.g.,replace: ['items[]']) requires that the parent list (items) has a correspondingkeyBypolicy.
Sources: test/config.test.ts:31-61, src/paths.ts:54-72, src/paths.ts:121-137
Path Compilation Logic
Keyfold compiles string-based paths into a tree of PolicyNode objects. This tree is used during the merge to efficiently determine the policy for the current state branch.
PolicyNode Structure
export interface PolicyNode {
keyField?: string;
replace?: boolean;
children?: Map<string, PolicyNode>;
}
Sources: src/paths.ts:8-12
Reachability Validation
The validateReachability function ensures that every segment of a path is valid given the surrounding policy. For example, it enforces that the [] suffix is used when stepping through a keyed list.
function validateReachability(root: PolicyNode, path: string, segments: readonly string[]): void {
let node = root;
for (const segment of segments) {
if (segment === ITEM) {
if (node.keyField === undefined) {
throw new KeyfoldConfigError(`path '${path}' addresses items... but that list has no keyBy policy`);
}
} else if (node.keyField !== undefined) {
throw new KeyfoldConfigError(`path '${path}' steps through keyed list... without the '[]' suffix`);
}
node = node.children!.get(segment)!;
}
}
Sources: src/paths.ts:121-137
Summary
Configuration Errors are the first line of defense in Keyfold. By throwing a KeyfoldConfigError during the initialization of createMerger, the library guarantees that the resulting merger function is backed by a logically sound and valid policy tree. This architectural choice moves complex validation logic out of the hot path of the merge operation, ensuring that runtime performance is focused purely on data reconciliation.