Class PropertyPathMap<V>

Stores values by structural PropertyPath paths using a property-key trie.

PropertyPathMap combines exact structural path storage with trie-aware object matching and bounded subtree traversal. In addition to normal map-style lookup, stored paths can be evaluated collectively against a candidate object, allowing the map to operate as a reusable index of properties, bindings, field definitions, validators, or other metadata associated with an object structure.

Unlike Map<readonly PropertyKey[], V>, lookup does not depend on property-key array identity. Equivalent paths resolve to the same entry even when a new property-key array is supplied:

const map = new PropertyPathMap<number>();

map.set(['actors', 0, 'id'], 42);
map.get(['actors', 0, 'id']); // 42

Dotted strings and equivalent string-key arrays share the same trie path:

map.set('settings.theme', 'dark');
map.get(['settings', 'theme']); // 'dark'

Exact array property-key paths remain necessary for symbols, numeric keys, empty-string keys, and string keys containing literal periods.

The collection provides three complementary iterator families:

  • entries, keys, and values iterate all stored entries in normal map insertion order.
  • matchingEntries, matchingKeys, and matchingValues evaluate stored paths against a candidate object.
  • subtreeEntries, subtreeKeys, and subtreeValues traverse a selected trie branch without inspecting an object.

Every iterator supports bounded operation through depth, result, and visit limits. Matching and subtree iterators additionally support absolute pathPrefix and stopAt bounds.

The matching iterators treat the stored trie as a reusable structural query over a candidate object:

const fields = new PropertyPathMap<string>();

fields.set('system.attributes.hp.value', 'hit-points');
fields.set('system.attributes.hp.max', 'maximum-hit-points');
fields.set('system.attributes.ac.value', 'armor-class');

const actor = {
system: {
attributes: {
hp: {
value: 12
}
}
}
};

[...fields.matchingEntries(actor)];
// [
// [['system', 'attributes', 'hp', 'value'], 'hit-points']
// ]

Matching traverses the property-key trie and candidate object together. Shared path prefixes are inspected only once for each matching operation. When a candidate prefix is missing or cannot be traversed, the complete stored subtree beneath that prefix is rejected without resolving each descendant path independently. This makes matching particularly useful when the map contains many paths with common prefixes.

matchingKeys yields only the available stored paths, while matchingValues yields only their mapped values. matchingEntries yields both:

for (const path of fields.matchingKeys(actor))
{
// path: readonly PropertyKey[]
}

for (const field of fields.matchingValues(actor))
{
// field: string
}

for (const [path, field] of fields.matchingEntries(actor))
{
// path: readonly PropertyKey[]
// field: string
}

By default, matching determines terminal property availability without reading the terminal value. This avoids invoking a terminal getter or proxy get trap merely to establish that a path exists.

Set includePropertyValue to include the resolved candidate value in the iterator result:

for (const [path, field, propertyValue] of fields.matchingEntries(actor, {
includePropertyValue: true
}))
{
// path: readonly PropertyKey[]
// field: string
// propertyValue: unknown
}

for (const [field, propertyValue] of fields.matchingValues(actor, {
includePropertyValue: true
}))
{
// field: string
// propertyValue: unknown
}

The overloads for matchingEntries and matchingValues reflect a literal includePropertyValue: true option in the returned iterator type.

Matching follows normal JavaScript property lookup by default. Set hasOwnOnly to require every matched segment to be an own property of the candidate value reached at that depth.

pathPrefix begins matching or subtree traversal directly at one absolute stored trie path. Unrelated branches are never visited:

fields.matchingEntries(actor, {
pathPrefix: 'system.attributes.hp'
});

Returned paths remain absolute. The prefix itself is included when it stores a mapped value and satisfies the iterator operation.

stopAt includes a selected path when it stores a value, but prunes every stored descendant beneath it:

fields.matchingEntries(actor, {
pathPrefix: 'system.attributes',
stopAt: 'system.attributes.hp'
});

When both options are supplied, stopAt must equal or descend from pathPrefix.

maxDepth is measured relative to pathPrefix, or relative to the trie root when no prefix is supplied.

Subtree iterators traverse stored entries without accessing a candidate object:

for (const [path, field] of fields.subtreeEntries({
pathPrefix: 'system.attributes.hp'
}))
{
// Every yielded entry belongs to the stored HP subtree.
}

These iterators are useful for inspecting, processing, or removing logical groups of stored paths without scanning unrelated branches. They share the same pathPrefix, stopAt, maxDepth, maxResults, and maxVisits controls as matching traversal.

Matching and subtree iterators use deterministic depth-first trie order rather than global insertion order.

Each path segment is stored in a native Map<PropertyKey, ...>:

  • Strings compare by value.
  • Numbers compare with Map / SameValueZero semantics.
  • Symbols compare by identity.
  • Numeric and string segments remain distinct (0 is not '0').

Stored canonical paths are copied and frozen once when first inserted. Overwriting an existing entry retains its original insertion position and canonical path. Deleting and reinserting a path moves it to the end, matching normal Map insertion-order behavior.

Every instance applies configurable limits to stored path depth, terminal entries, allocated trie nodes, yielded traversal results, and traversal visits. Storage limits are preflighted before mutation, so failed insertion cannot leave a partial trie branch. Per-call maxDepth, maxResults, and maxVisits options may reduce, but never exceed, the constructor traversal caps.

Reaching maxResults ends an iterator normally after the configured number of results. Exceeding maxVisits throws before another candidate property or trie node is processed during the iterative walk. Path normalization and fixed- depth trie scope lookup are bounded separately by maxPathDepth. These limits do not measure the retained size of mapped values or individual property keys.

Candidate-object matching may invoke getters and proxy traps when descendant traversal or an explicitly requested terminal property value requires a read. Exceptions from those operations are intentionally propagated.

get, has, and set are O(path length). delete is also O(path length) and prunes unused trie nodes. Normal map iteration is O(entry count) and follows insertion order through a linked list of terminal entries.

Trie-aware matching visits only reachable stored prefixes. An unavailable candidate prefix rejects every stored descendant beneath it with one candidate-property check. Matching entry and value iterators may optionally include the resolved candidate property value without performing a second path lookup.

pathPrefix begins traversal directly at a selected stored trie node, while stopAt prunes one descendant branch by node identity. Candidate-independent subtree iterators visit only terminal entries beneath the selected node.

Mutation of the map while an iterator is active is intentionally unspecified.

Type Parameters

  • V

    Stored value type.

Implements
Index
  • Creates a new property path map and optionally initializes it from PropertyPath / value pairs.

    Later duplicate paths overwrite earlier values without changing the original insertion position. Resource limits are validated before initial entries are inserted and apply to every subsequent operation.

    Type Parameters

    • V

      Stored value type.

    Parameters

    • Optionalentries: Iterable<readonly [PropertyPath, V], any, any>

      Optional initial PropertyPath / value entries.

    • Optionaloptions: Constructor

      Defensive storage and traversal limits.

      Constructor-level defensive limits applied to storage and every iterator.

      • OptionalmaxEntries?: number

        Maximum number of exact stored paths. Overwriting an existing path does not consume another entry.

        16384
        
      • OptionalmaxNodes?: number

        Maximum number of allocated non-root trie nodes. Shared path prefixes consume one node per unique segment.

        65536
        
      • OptionalmaxPathDepth?: number

        Maximum number of property-key segments in a stored or queried path.

        64
        
      • OptionalmaxTraversalResults?: number

        Maximum results produced by one iterator unless reduced per call. Reaching the limit truncates normally.

        16384
        
      • OptionalmaxTraversalVisits?: number

        Maximum properties or trie nodes processed during iterator traversal unless reduced per call. Exceeding the limit throws a RangeError.

        65536
        

    Returns PropertyPathMap<V>

    If a constructor option is not a non-negative safe integer.

    If an initial entry exceeds a configured storage limit.

  • get "[toStringTag]"(): string

    Provides the standard object tag used by Object.prototype.toString.

    Returns string

  • get nodeCount(): number

    Number of allocated non-root trie nodes.

    This value is maintained incrementally and may be used to monitor current trie resource consumption.

    Returns number

  • get size(): number

    Number of exact property paths currently stored.

    Returns number

  • Returns the default insertion-order iterator of [path, value] pairs.

    Constructor-level traversal result and visit caps apply. Use entries when per-call limits are required.

    Returns IterableIterator<[readonly PropertyKey[], V]>

  • Removes every stored entry and releases the complete trie.

    This operation is O(1) with respect to explicit traversal; the prior structure becomes available for garbage collection once no active iterator or external references remain.

    Returns void

  • Deletes the value stored at an exact property path.

    Descendant entries do not count as a match. Deleting ['settings'] does not remove ['settings', 'theme'], and deleting a parentless path does not affect siblings.

    After removal, unused nodes are pruned from the terminal node toward the root. Pruning stops at the first node that still stores a value or has another child.

    Parameters

    Returns boolean

    true when an entry existed and was removed; otherwise false.

    If path is not a valid PropertyPath.

    If the path exceeds the configured maxPathDepth.

  • Returns an insertion-order iterator of [path, value] pairs.

    Paths are canonical frozen arrays owned by this map. maxDepth is measured from the trie root, maxResults truncates the iterator normally, and maxVisits throws when exceeded. Yielded entries retain insertion order.

    Parameters

    • Optionaloptions: Iteration

      Optional insertion-order traversal limits.

    Returns IterableIterator<[readonly PropertyKey[], V]>

    Entry iterator.

    If a numeric traversal option is invalid.

    If options.maxVisits is exceeded.

  • Invokes a callback once for every entry in insertion order.

    The callback arguments follow Map.prototype.forEach: value, key, then map. The key is the canonical readonly property-key array associated with the stored entry. Unlike the explicit iterator methods, forEach always visits the complete map, which is already bounded by the configured maxEntries storage limit.

    Parameters

    • callback: (value: V, key: readonly PropertyKey[], map: PropertyPathMap<V>) => void

      Function invoked for each entry.

    • OptionalthisArg: unknown

      Optional callback this value.

    Returns void

  • Retrieves the value stored at an exact structural path.

    undefined may mean either that the path is absent or that undefined is the stored value. Use has when that distinction matters.

    Parameters

    Returns V

    Stored value or undefined when the exact path is absent.

    If path is not a valid PropertyPath.

    If the path exceeds the configured maxPathDepth.

  • Determines whether a value is stored at an exact structural path.

    Descendant paths do not cause a prefix to be reported as present.

    Parameters

    Returns boolean

    Whether the exact path stores a value.

    If path is not a valid PropertyPath.

    If the path exceeds the configured maxPathDepth.

  • Returns an insertion-order iterator of canonical property-key paths.

    maxDepth is measured from the trie root, maxResults truncates normally, and maxVisits throws when exceeded.

    Parameters

    • Optionaloptions: Iteration

      Optional insertion-order traversal limits.

    Returns IterableIterator<readonly PropertyKey[]>

    Key iterator.

    If a numeric traversal option is invalid.

    If options.maxVisits is exceeded.

  • Yields stored entries whose complete paths are available in a candidate value.

    Matching traverses the property-key trie directly instead of resolving every stored path independently. Once a candidate prefix is missing or cannot be traversed, every stored descendant below that prefix is rejected without additional property access. Shared prefixes are therefore checked and read at most once per matching operation.

    Set pathPrefix to begin matching at one absolute stored path and ignore every unrelated trie branch. The prefix itself is yielded when it stores an entry and exists in the candidate object. Set stopAt to match one absolute path normally while pruning every stored descendant beneath it. Returned paths always remain absolute.

    A terminal property is considered available when it exists, even when its value is undefined or null. By default, terminal-only properties are not read, avoiding unnecessary getter and proxy get trap invocation. Set includePropertyValue to true to append the resolved candidate property value to each yielded tuple.

    Array matching follows the same rules as the package property-path utilities: numeric indexes must be numbers in the ECMAScript array-index range, while symbol properties remain valid. String array indexes such as '0' are rejected. Ordinary objects retain normal JavaScript key coercion semantics.

    Circular candidate values are safe because traversal is bounded by the finite depth of the stored trie; no cycle tracking is required.

    Matching uses depth-first trie order, with sibling branches following the order in which their first trie nodes were created. This order is deterministic for an unchanged map but is not the map's global insertion order. maxDepth is relative to pathPrefix, or to the trie root when no prefix is supplied. maxResults truncates normally, while maxVisits throws before another candidate property or trie node is processed.

    Parameters

    • data: unknown

      Candidate object or function to inspect. Non-traversable values produce an empty iterator.

    • options: Match & { includePropertyValue: true }

      Matching options.

    Returns IterableIterator<[readonly PropertyKey[], V, unknown]>

    Iterator of canonical stored paths and their associated mapped values, optionally followed by the resolved candidate property value.

    If a boolean, numeric limit, or path option is invalid.

    If a path bound exceeds configured limits, options.stopAt is outside options.pathPrefix, or options.maxVisits is exceeded.

  • Yields stored entries whose complete paths are available in a candidate value.

    Matching traverses the property-key trie directly instead of resolving every stored path independently. Once a candidate prefix is missing or cannot be traversed, every stored descendant below that prefix is rejected without additional property access. Shared prefixes are therefore checked and read at most once per matching operation.

    Set pathPrefix to begin matching at one absolute stored path and ignore every unrelated trie branch. The prefix itself is yielded when it stores an entry and exists in the candidate object. Set stopAt to match one absolute path normally while pruning every stored descendant beneath it. Returned paths always remain absolute.

    A terminal property is considered available when it exists, even when its value is undefined or null. By default, terminal-only properties are not read, avoiding unnecessary getter and proxy get trap invocation. Set includePropertyValue to true to append the resolved candidate property value to each yielded tuple.

    Array matching follows the same rules as the package property-path utilities: numeric indexes must be numbers in the ECMAScript array-index range, while symbol properties remain valid. String array indexes such as '0' are rejected. Ordinary objects retain normal JavaScript key coercion semantics.

    Circular candidate values are safe because traversal is bounded by the finite depth of the stored trie; no cycle tracking is required.

    Matching uses depth-first trie order, with sibling branches following the order in which their first trie nodes were created. This order is deterministic for an unchanged map but is not the map's global insertion order. maxDepth is relative to pathPrefix, or to the trie root when no prefix is supplied. maxResults truncates normally, while maxVisits throws before another candidate property or trie node is processed.

    Parameters

    • data: unknown

      Candidate object or function to inspect. Non-traversable values produce an empty iterator.

    • Optionaloptions: Match & { includePropertyValue?: false }

      Matching options.

    Returns IterableIterator<[readonly PropertyKey[], V]>

    Iterator of canonical stored paths and their associated mapped values, optionally followed by the resolved candidate property value.

    If a boolean, numeric limit, or path option is invalid.

    If a path bound exceeds configured limits, options.stopAt is outside options.pathPrefix, or options.maxVisits is exceeded.

  • Yields stored entries whose complete paths are available in a candidate value.

    Matching traverses the property-key trie directly instead of resolving every stored path independently. Once a candidate prefix is missing or cannot be traversed, every stored descendant below that prefix is rejected without additional property access. Shared prefixes are therefore checked and read at most once per matching operation.

    Set pathPrefix to begin matching at one absolute stored path and ignore every unrelated trie branch. The prefix itself is yielded when it stores an entry and exists in the candidate object. Set stopAt to match one absolute path normally while pruning every stored descendant beneath it. Returned paths always remain absolute.

    A terminal property is considered available when it exists, even when its value is undefined or null. By default, terminal-only properties are not read, avoiding unnecessary getter and proxy get trap invocation. Set includePropertyValue to true to append the resolved candidate property value to each yielded tuple.

    Array matching follows the same rules as the package property-path utilities: numeric indexes must be numbers in the ECMAScript array-index range, while symbol properties remain valid. String array indexes such as '0' are rejected. Ordinary objects retain normal JavaScript key coercion semantics.

    Circular candidate values are safe because traversal is bounded by the finite depth of the stored trie; no cycle tracking is required.

    Matching uses depth-first trie order, with sibling branches following the order in which their first trie nodes were created. This order is deterministic for an unchanged map but is not the map's global insertion order. maxDepth is relative to pathPrefix, or to the trie root when no prefix is supplied. maxResults truncates normally, while maxVisits throws before another candidate property or trie node is processed.

    Parameters

    • data: unknown

      Candidate object or function to inspect. Non-traversable values produce an empty iterator.

    • Optionaloptions: Match

      Matching options.

    Returns IterableIterator<
        [readonly PropertyKey[], V]
        | [readonly PropertyKey[], V, unknown],
    >

    Iterator of canonical stored paths and their associated mapped values, optionally followed by the resolved candidate property value.

    If a boolean, numeric limit, or path option is invalid.

    If a path bound exceeds configured limits, options.stopAt is outside options.pathPrefix, or options.maxVisits is exceeded.

  • Yields canonical stored paths whose complete paths are available in a candidate value.

    This is a path-only projection of matchingEntries and uses the same trie-aware pruning, prefix / stop bounds, property semantics, and depth-first trie order. Candidate terminal values are never requested solely for this iterator; properties are read only when descendant traversal requires them.

    Parameters

    • data: unknown

      Candidate object or function to inspect.

    • Optionaloptions: MatchKeys

      Path-only matching options.

    Returns IterableIterator<readonly PropertyKey[]>

    Iterator of matching canonical property-key paths.

    If a boolean, numeric limit, or path option is invalid.

    If path bounds exceed configured limits or options.maxVisits is exceeded.

  • Yields mapped values whose stored path paths are available in a candidate value.

    By default, this returns only each value stored in the map. Set includePropertyValue to true to return [mappedValue, propertyValue] tuples, where propertyValue is resolved from the candidate data object at the matching property path. Prefix and stop bounds follow the semantics documented by matchingEntries.

    Parameters

    • data: unknown

      Candidate object or function to inspect.

    • options: Match & { includePropertyValue: true }

      Matching options.

    Returns IterableIterator<[V, unknown]>

    Iterator of mapped values or mapped-value / candidate-property-value tuples.

    If a boolean, numeric limit, or path option is invalid.

    If path bounds exceed configured limits or options.maxVisits is exceeded.

  • Yields mapped values whose stored path paths are available in a candidate value.

    By default, this returns only each value stored in the map. Set includePropertyValue to true to return [mappedValue, propertyValue] tuples, where propertyValue is resolved from the candidate data object at the matching property path. Prefix and stop bounds follow the semantics documented by matchingEntries.

    Parameters

    • data: unknown

      Candidate object or function to inspect.

    • Optionaloptions: Match & { includePropertyValue?: false }

      Matching options.

    Returns IterableIterator<V>

    Iterator of mapped values or mapped-value / candidate-property-value tuples.

    If a boolean, numeric limit, or path option is invalid.

    If path bounds exceed configured limits or options.maxVisits is exceeded.

  • Yields mapped values whose stored path paths are available in a candidate value.

    By default, this returns only each value stored in the map. Set includePropertyValue to true to return [mappedValue, propertyValue] tuples, where propertyValue is resolved from the candidate data object at the matching property path. Prefix and stop bounds follow the semantics documented by matchingEntries.

    Parameters

    • data: unknown

      Candidate object or function to inspect.

    • Optionaloptions: Match

      Matching options.

    Returns IterableIterator<V | [V, unknown]>

    Iterator of mapped values or mapped-value / candidate-property-value tuples.

    If a boolean, numeric limit, or path option is invalid.

    If path bounds exceed configured limits or options.maxVisits is exceeded.

  • Stores a value at an exact structural path.

    Existing trie nodes are inspected first so path depth, entry count, and node count limits can be validated before any mutation occurs. Overwriting an existing path updates only its value, preserving size and insertion order. A new entry copies and freezes its normalized path once for stable iteration.

    Parameters

    • path: PropertyPath

      Dotted or exact property-key path.

    • value: V

      Value to store. undefined is valid.

    Returns this

    This map.

    If path is not a valid PropertyPath.

    If the path depth, entry count, or trie node count limit would be exceeded.

  • Yields stored entries from one trie subtree without inspecting a candidate data object.

    pathPrefix selects the absolute trie node where traversal begins. The prefix entry is included when the exact path stores a value, even when it has no descendants. A missing stored prefix produces an empty iterator. stopAt includes its own entry when present and prunes all descendants beneath that node.

    Subtree traversal uses deterministic depth-first trie order rather than global insertion order. Returned canonical paths remain absolute and are reused from their stored entries. maxDepth is relative to pathPrefix, or to the trie root when no prefix is supplied. maxResults truncates normally, while maxVisits throws.

    Parameters

    • Optionaloptions: Subtree

      Subtree bounds.

    Returns IterableIterator<[readonly PropertyKey[], V]>

    Iterator of canonical stored paths and mapped values.

    If a numeric limit or path option is invalid.

    If path bounds exceed configured limits or options.maxVisits is exceeded.

  • Yields canonical stored paths from one trie subtree.

    This is a path-only projection of subtreeEntries. It performs no candidate object access and allocates no temporary entry tuples.

    Parameters

    • Optionaloptions: Subtree

      Subtree bounds.

    Returns IterableIterator<readonly PropertyKey[]>

    Iterator of canonical stored property-key paths.

    If a numeric limit or path option is invalid.

    If path bounds exceed configured limits or options.maxVisits is exceeded.

  • Yields mapped values from one trie subtree.

    This is a value-only projection of subtreeEntries. It performs no candidate object access and allocates no temporary entry tuples.

    Parameters

    • Optionaloptions: Subtree

      Subtree bounds.

    Returns IterableIterator<V>

    Iterator of mapped values.

    If a numeric limit or path option is invalid.

    If path bounds exceed configured limits or options.maxVisits is exceeded.

  • Returns an insertion-order iterator of stored values.

    maxDepth is measured from the trie root, maxResults truncates normally, and maxVisits throws when exceeded.

    Parameters

    • Optionaloptions: Iteration

      Optional insertion-order traversal limits.

    Returns IterableIterator<V>

    Value iterator.

    If a numeric traversal option is invalid.

    If options.maxVisits is exceeded.