Repository: League-of-Foundry-Developers/foundry-vtt-types
Pull request #2651 (2024-07-19 02:43:22)

V12/common.utils.bitmask-wordtree-stringtree
Implements 
* common/utils/bitmask.d.mts
* common/utils/word-tree.d.mts
* common/utils/string-tree.d.mts

Addresses #2645, #2646, #2647


Patch:
diff --git a/src/foundry/client-esm/applications/dice/roll-resolver.d.mts b/src/foundry/client-esm/applications/dice/roll-resolver.d.mts
index d9e40cc59..fe8c7df37 100644
--- a/src/foundry/client-esm/applications/dice/roll-resolver.d.mts
+++ b/src/foundry/client-esm/applications/dice/roll-resolver.d.mts
@@ -1,4 +1,95 @@
+import type { DeepPartial, InexactPartial } from "../../../../types/utils.d.mts";
+import type { ApplicationConfiguration } from "../../../common/config.d.mts";
 import type ApplicationV2 from "../api/application.d.mts";
 import type HandlebarsApplicationMixin from "../api/handlebars-application.mjs";
 
-export default class RollResolver extends HandlebarsApplicationMixin(ApplicationV2) {}
+/**
+ * An application responsible for handling unfulfilled dice terms in a roll.
+ */
+
+declare class RollResolver<Configuration extends ApplicationConfiguration> extends HandlebarsApplicationMixin(
+  ApplicationV2,
+) {
+  constructor(roll: Roll, options?: DeepPartial<Configuration>);
+
+  // a placeholder private method to help subclassing
+  #rollResolver: true;
+
+  /**
+   * A collection of fulfillable dice terms.
+   */
+  get fulfillable(): Map<string, RollResolver.DiceTermFulfillmentDescriptor>;
+
+  /**
+   * The roll being resolved.
+   */
+  get roll(): Roll;
+  #roll: Roll;
+
+  /**
+   * Identify any terms in this Roll that should be fulfilled externally, and prompt the user to do so.
+   * @returns Returns a Promise that resolves when the first pass of fulfillment is complete.
+   */
+  awaitFulfillment(): Promise<void>;
+
+  /**
+   * Register a fulfilled die roll.
+   * @param method        - The method used for fulfillment.
+   * @param denomination  - The denomination of the fulfilled die.
+   * @param result        - The rolled number.
+   * @returns               Whether the result was consumed.
+   */
+  registerResult(method: string, denomination: string, result: number): boolean;
+
+  /**
+   * Handle prompting for a single extra result from a term.
+   * @param term                - The term.
+   * @param method              - The method used to obtain the result.
+   * @param  options            - Optional configuration options
+   * @param  options.reroll     - Defaults to false
+   * @param  options.explode    - Defaults to false
+   * @returns
+   */
+  resolveResult(
+    term: DiceTerm,
+    method: string,
+    options?: InexactPartial<{ reroll: boolean; explode: boolean }>,
+  ): Promise<number | void>;
+
+  /**
+   * Update the Roll instance with the fulfilled results.
+   * @param event     - The originating form submission event.
+   * @param form      - The form element that was submitted.
+   * @param formData  - Processed data for the submitted form.
+   */
+  static _fulfillRoll(event: SubmitEvent, form: HTMLFormElement, formData: FormDataExtended): Promise<void>;
+
+  /**
+   * Add a new term to the resolver.
+   * @param term        - The term.
+   * @returns  A Promise that resolves when the term's results have been externally fulfilled.
+   */
+  addTerm(term: DiceTerm): Promise<void>;
+
+  /**
+   * Check if all rolls have been fulfilled.
+   */
+  _checkDone(): void;
+
+  /**
+   * Toggle the state of the submit button.
+   * @param enabled  - Whether the button is enabled.
+   */
+  _toggleSubmission(enabled: boolean): void;
+}
+
+declare namespace RollResolver {
+  interface DiceTermFulfillmentDescriptor {
+    id: string;
+    term: DiceTerm;
+    method: string;
+    isNew?: boolean | undefined;
+  }
+}
+
+export default RollResolver;
diff --git a/src/foundry/common/data/module.d.mts b/src/foundry/common/data/module.d.mts
index 793b29a40..6d9e67cba 100644
--- a/src/foundry/common/data/module.d.mts
+++ b/src/foundry/common/data/module.d.mts
@@ -1,4 +1,9 @@
-export type * as validators from "./validators.d.mts";
-export type * as validation from "./validation-failure.d.mts";
-export type * as fields from "./fields.d.mts";
-export type * from "./data.d.mts";
+// In Foundry itself this file contains re-exports of these other modules.
+// Therefore it has a runtime effect and uses `.mjs` instead of `.d.mts`.
+// While `.mts` could work, to avoid `import/no-unresolved from erroring `.mjs` is used.
+/* eslint-disable import/extensions */
+
+export * as validators from "./validators.mjs";
+export * as validation from "./validation-failure.mjs";
+export * as fields from "./fields.mjs";
+export * from "./data.mjs";
diff --git a/src/foundry/common/documents/_module.d.mts b/src/foundry/common/documents/_module.d.mts
index e634973d3..e5bf17ffc 100644
--- a/src/foundry/common/documents/_module.d.mts
+++ b/src/foundry/common/documents/_module.d.mts
@@ -1,30 +1,35 @@
-export type { default as BaseActiveEffect } from "./active-effect.d.mts";
-export type { default as BaseActorDelta } from "./actor-delta.d.mts";
-export type { default as BaseActor } from "./actor.d.mts";
-export type { default as BaseAdventure } from "./adventure.d.mts";
-export type { default as BaseAmbientLight } from "./ambient-light.d.mts";
-export type { default as BaseAmbientSound } from "./ambient-sound.d.mts";
-export type { default as BaseCard } from "./card.d.mts";
-export type { default as BaseCards } from "./cards.d.mts";
-export type { default as BaseChatMessage } from "./chat-message.d.mts";
-export type { default as BaseCombat } from "./combat.d.mts";
-export type { default as BaseCombatant } from "./combatant.d.mts";
-export type { default as BaseDrawing } from "./drawing.d.mts";
-export type { default as BaseFogExploration } from "./fog-exploration.d.mts";
-export type { default as BaseFolder } from "./folder.d.mts";
-export type { default as BaseItem } from "./item.d.mts";
-export type { default as BaseJournalEntry } from "./journal-entry.d.mts";
-export type { default as BaseJournalEntryPage } from "./journal-entry-page.d.mts";
-export type { default as BaseMacro } from "./macro.d.mts";
-export type { default as BaseMeasuredTemplate } from "./measured-template.d.mts";
-export type { default as BaseNote } from "./note.d.mts";
-export type { default as BasePlaylist } from "./playlist.d.mts";
-export type { default as BasePlaylistSound } from "./playlist-sound.d.mts";
-export type { default as BaseRollTable } from "./roll-table.d.mts";
-export type { default as BaseScene } from "./scene.d.mts";
-export type { default as BaseSetting } from "./setting.d.mts";
-export type { default as BaseTableResult } from "./table-result.d.mts";
-export type { default as BaseTile } from "./tile.d.mts";
-export type { default as BaseToken } from "./token.d.mts";
-export type { default as BaseUser } from "./user.d.mts";
-export type { default as BaseWall } from "./wall.d.mts";
+// In Foundry itself this file contains re-exports of these other modules.
+// Therefore it has a runtime effect and uses `.mjs` instead of `.d.mts`.
+// While `.mts` could work, to avoid `import/no-unresolved from erroring `.mjs` is used.
+/* eslint-disable import/extensions */
+
+export { default as BaseActiveEffect } from "./active-effect.mjs";
+export { default as BaseActorDelta } from "./actor-delta.mjs";
+export { default as BaseActor } from "./actor.mjs";
+export { default as BaseAdventure } from "./adventure.mjs";
+export { default as BaseAmbientLight } from "./ambient-light.mjs";
+export { default as BaseAmbientSound } from "./ambient-sound.mjs";
+export { default as BaseCard } from "./card.mjs";
+export { default as BaseCards } from "./cards.mjs";
+export { default as BaseChatMessage } from "./chat-message.mjs";
+export { default as BaseCombat } from "./combat.mjs";
+export { default as BaseCombatant } from "./combatant.mjs";
+export { default as BaseDrawing } from "./drawing.mjs";
+export { default as BaseFogExploration } from "./fog-exploration.mjs";
+export { default as BaseFolder } from "./folder.mjs";
+export { default as BaseItem } from "./item.mjs";
+export { default as BaseJournalEntry } from "./journal-entry.mjs";
+export { default as BaseJournalEntryPage } from "./journal-entry-page.mjs";
+export { default as BaseMacro } from "./macro.mjs";
+export { default as BaseMeasuredTemplate } from "./measured-template.mjs";
+export { default as BaseNote } from "./note.mjs";
+export { default as BasePlaylist } from "./playlist.mjs";
+export { default as BasePlaylistSound } from "./playlist-sound.mjs";
+export { default as BaseRollTable } from "./roll-table.mjs";
+export { default as BaseScene } from "./scene.mjs";
+export { default as BaseSetting } from "./setting.mjs";
+export { default as BaseTableResult } from "./table-result.mjs";
+export { default as BaseTile } from "./tile.mjs";
+export { default as BaseToken } from "./token.mjs";
+export { default as BaseUser } from "./user.mjs";
+export { default as BaseWall } from "./wall.mjs";
diff --git a/src/foundry/common/packages/module.d.mts b/src/foundry/common/packages/module.d.mts
index b88416655..8ebb344b0 100644
--- a/src/foundry/common/packages/module.d.mts
+++ b/src/foundry/common/packages/module.d.mts
@@ -1,8 +1,13 @@
-export type { default as BasePackage } from "./base-package.d.mts";
-export type { default as BaseWorld } from "./base-world.d.mts";
-export type { default as BaseSystem } from "./base-system.d.mts";
-export type { default as BaseModule } from "./base-module.d.mts";
-export type { PackageCompatibility, RelatedPackage } from "./base-package.d.mts";
+// In Foundry itself this file contains re-exports of these other modules.
+// Therefore it has a runtime effect and uses `.mjs` instead of `.d.mts`.
+// While `.mts` could work, to avoid `import/no-unresolved from erroring `.mjs` is used.
+/* eslint-disable import/extensions */
+
+export { default as BasePackage } from "./base-package.mjs";
+export { default as BaseWorld } from "./base-world.mjs";
+export { default as BaseSystem } from "./base-system.mjs";
+export { default as BaseModule } from "./base-module.mjs";
+export { PackageCompatibility, RelatedPackage } from "./base-package.mjs";
 
 declare global {
   type PackageAuthorData =
diff --git a/src/foundry/common/utils/bitmask.d.mts b/src/foundry/common/utils/bitmask.d.mts
new file mode 100644
index 000000000..04d20eb75
--- /dev/null
+++ b/src/foundry/common/utils/bitmask.d.mts
@@ -0,0 +1,110 @@
+export default class BitMask extends Number {
+  /**
+   * Create a new BitMask instance.
+   * @param states    - An object containing valid states and their corresponding initial boolean values (default is null).
+   */
+  constructor(states?: Record<string, boolean> | null);
+
+  // a placeholder private method to help subclassing
+  #bitmask: true;
+
+  /**
+   * The enum associated with this structure.
+   * @readonly
+   */
+  states: Record<string, string>;
+
+  /**
+   * True if this bitmask is empty (no active states).
+   */
+  get isEmpty(): boolean;
+
+  /**
+   * Check if a specific state is active.
+   * @param state   - The state to check.
+   * @returns True if the state is active, false otherwise.
+   */
+  hasState(state: string): boolean;
+
+  /**
+   * Add a state to the bitmask.
+   * @param state - The state to add. Throws an error if the provided state is not valid.
+   */
+  addState(state: string): void;
+
+  /**
+   * Remove a state from the bitmask.
+   * @param state   - The state to remove. Throws an error if the provided state is not valid.
+   */
+  removeState(state: string): void;
+
+  /**
+   * Toggle the state of a specific state in the bitmask.
+   * @param state The state to toggle. Throws an error if the provided state is not valid.
+   * @param enabled   - Toggle on (true) or off (false)? If undefined, the state is switched automatically.
+   */
+  toggleState(state: string, enabled: boolean): void;
+
+  /**
+   * Toggle the state of a specific state in the bitmask.
+   * @param state The state to toggle.
+   * @returns  The updated bitmask. Throws an error if the provided state is not valid.
+   */
+  toggleState(state: string): number;
+
+  /**
+   * Clear the bitmask, setting all states to inactive.
+   */
+  clear(): void;
+
+  /**
+   * Get the current value of the bitmask.
+   * @returns The current value of the bitmask.
+   */
+  valueOf(): number;
+
+  /**
+   * Get a string representation of the bitmask in binary format.
+   * @returns The string representation of the bitmask.
+   */
+  toString(): string;
+
+  /**
+   * Checks if two bitmasks structures are compatible (the same valid states).
+   * @param otherBitMask  - The bitmask structure to compare with.
+   * @returns True if the two bitmasks have the same structure, false otherwise.
+   */
+  isCompatible(otherBitMask: BitMask): boolean;
+
+  /**
+   * Serializes the bitmask to a JSON string.
+   * @returns The JSON string representing the bitmask.
+   */
+  toJSON(): string;
+
+  /**
+   * Creates a new BitMask instance from a JSON string.
+   * @param jsonString    - The JSON string representing the bitmask.
+   * @returns A new BitMask instance created from the JSON string.
+   */
+  static fromJSON(jsonString: string): BitMask;
+
+  /**
+   * Convert value of this BitMask to object representation according to structure.
+   * @returns The data represented by the bitmask.
+   */
+  toObject(): Record<string, boolean>;
+
+  /**
+   * Creates a clone of this BitMask instance.
+   * @returns {BitMask} A new BitMask instance with the same value and valid states as this instance.
+   */
+  clone(): BitMask;
+
+  /**
+   * Generates shader constants based on the provided states.
+   * @param states    - An array containing valid states.
+   * @returns Shader bit mask constants generated from the states.
+   */
+  static generateShaderBitMaskConstants(states: string[]): string;
+}
diff --git a/src/foundry/common/utils/module.d.mts b/src/foundry/common/utils/module.d.mts
index c005ff62b..58e01b45e 100644
--- a/src/foundry/common/utils/module.d.mts
+++ b/src/foundry/common/utils/module.d.mts
@@ -8,3 +8,6 @@ export { default as IterableWeakSet } from "./iterable-weak-set.mts";
 export { default as IterableWeakMap } from "./iterable-weak-map.mts";
 export { default as Color } from "./color.mts";
 export { default as Semaphore } from "./semaphore.mts";
+export { default as StringTree } from "./string-tree.mts";
+export { default as WordTree } from "./word-tree.mts";
+export { default as BitMask } from "./bitmask.mts";
diff --git a/src/foundry/common/utils/string-tree.d.mts b/src/foundry/common/utils/string-tree.d.mts
new file mode 100644
index 000000000..85b5f745a
--- /dev/null
+++ b/src/foundry/common/utils/string-tree.d.mts
@@ -0,0 +1,75 @@
+import type { InexactPartial } from "../../../types/utils.d.mts";
+
+/**
+ * A data structure representing a tree of string nodes with arbitrary object leaves.
+ */
+declare class StringTree<EntryType> {
+  /**
+   * The key symbol that stores the leaves of any given node.
+   * @type {symbol}
+   */
+  // replaced the getter definition with a static property so that
+  //    it can be referenced as part of the StringTreeNode interface
+  // static get leaves(): symbol;
+  static readonly leaves: unique symbol;
+
+  static readonly #leaves: unique symbol;
+
+  /**
+   * Insert an entry into the tree.
+   * @param strings  - The string parents for the entry.
+   * @param entry         - The entry to store.
+   * @returns   The node the entry was added to.
+   */
+  addLeaf(strings: string[], entry: EntryType): StringTree.StringTreeNode;
+
+  /**
+   * Traverse the tree along the given string path and return any entries reachable from the node.
+   * @param strings         - The string path to the desired node.
+   * @param options         - Additional options to configure behaviour.
+   * @param options.limit   - The maximum number of items to retrieve.
+   * @returns    The reachable entries
+   */
+  lookup(strings: string[], options?: InexactPartial<{ limit: number }>): StringTree.StringTreeNode[];
+
+  /**
+   * Returns the node at the given path through the tree.
+   * @param strings                     - The string path to the desired node.
+   * @param options                     - Additional options to configure behaviour.
+   * @param options.hasLeaves           - Only return the most recently visited node that has
+   *                                      leaves, otherwise return the exact node at the prefix,
+   *                                      if it exists. Defaults to false.
+   * @returns The node at the path, if found
+   */
+  nodeAtPrefix(strings: string[], options?: InexactPartial<{ hasLeaves: boolean }>): StringTree.StringTreeNode | void;
+
+  /**
+   * Perform a breadth-first search starting from the given node and retrieving any entries reachable from that node,
+   * until we reach the limit.
+   * @param node            - The starting node.
+   * @param entries         - The accumulated entries.
+   * @param queue           - The working queue of nodes to search.
+   * @param options         - Additional options to configure behaviour.
+   * @param options.limit   - The maximum number of entries to retrieve before stopping.
+   * @protected
+   */
+  _breadthFirstSearch(
+    node: StringTree.StringTreeNode,
+    entries: EntryType[],
+    queue: StringTree.StringTreeNode[],
+    options?: InexactPartial<{ limit: number }>,
+  ): void;
+}
+
+/**
+ * A string tree node consists of zero-or-more string keys, and a leaves property that contains any objects that
+ * terminate at the current node.
+ */
+declare namespace StringTree {
+  interface StringTreeNode {
+    [StringTree.leaves]: Record<string, unknown>[];
+    [key: string]: StringTreeNode;
+  }
+}
+
+export default StringTree;
diff --git a/src/foundry/common/utils/word-tree.d.mts b/src/foundry/common/utils/word-tree.d.mts
new file mode 100644
index 000000000..93a1d1533
--- /dev/null
+++ b/src/foundry/common/utils/word-tree.d.mts
@@ -0,0 +1,56 @@
+import type { InexactPartial } from "../../../types/utils.d.mts";
+import type StringTree from "./string-tree.d.mts";
+
+/**
+ * A data structure for quickly retrieving objects by a string prefix.
+ * Note that this works well for languages with alphabets (latin, cyrillic, korean, etc.), but may need more nuanced
+ * handling for languages that compose characters and letters.
+ */
+declare class WordTree extends StringTree<WordTree.WordTreeEntry> {
+  /**
+   * Insert an entry into the tree.
+   * @param string    - The string key for the entry.
+   * @param entry     - The entry to store.
+   * @returns         The node the entry was added to.
+   */
+  addLeaf(string: string, entry: WordTree.WordTreeEntry): StringTree.StringTreeNode;
+  addLeaf(strings: string[], entry: any): StringTree.StringTreeNode;
+
+  /**
+   * Return entries that match the given string prefix.
+   * @param prefix              - The prefix.
+   * @param options             - Additional options to configure behaviour.
+   * @param options.limit       - The maximum number of items to retrieve. Defaults to 10. It is important
+   *                              to set this value as very short prefixes will naturally match large numbers
+   *                              of entries.
+   * @returns                   A number of entries that have the given prefix.
+   */
+  lookup(prefix: string, options?: InexactPartial<{ limit: number }>): WordTree.WordTreeEntry[];
+  lookup(strings: string[], options?: InexactPartial<{ limit: number }>): StringTree.StringTreeNode[];
+
+  /**
+   * Returns the node at the given prefix.
+   * @param prefix        - The prefix.
+   * @returns The node
+   */
+  nodeAtPrefix(strings: string[], options?: InexactPartial<{ hasLeaves: boolean }>): StringTree.StringTreeNode | void;
+  nodeAtPrefix(prefix: string): WordTree.WordTreeEntry;
+}
+
+declare namespace WordTree {
+  /**
+   * A leaf entry in the tree.
+   * @property  entry          - An object that this entry represents.
+   * @property  documentName   - The document type.
+   * @property  uuid           - The document's UUID.
+   * @property  pack           - The (optional) pack ID.
+   */
+  interface WordTreeEntry {
+    entry: Document;
+    documentName: string;
+    uuid: string;
+    pack?: string;
+  }
+}
+
+export default WordTree;


Test patch:
diff --git a/tests/foundry/client-esm/applications/api/roll-resolver.test-d.ts b/tests/foundry/client-esm/applications/api/roll-resolver.test-d.ts
new file mode 100644
index 000000000..60f4bf8aa
--- /dev/null
+++ b/tests/foundry/client-esm/applications/api/roll-resolver.test-d.ts
@@ -0,0 +1,22 @@
+import { expectTypeOf } from "vitest";
+
+const r = new foundry.applications.dice.RollResolver(new Roll("3d6"));
+
+const t = new Die();
+
+expectTypeOf(r.awaitFulfillment()).toEqualTypeOf<Promise<void>>();
+expectTypeOf(r.registerResult("a", "a", 5)).toEqualTypeOf<boolean>();
+expectTypeOf(await r.resolveResult(t, "a")).toEqualTypeOf<number | void>();
+expectTypeOf(await r.resolveResult(t, "a", { reroll: true, explode: true })).toEqualTypeOf<number | void>();
+
+expectTypeOf(
+  await foundry.applications.dice.RollResolver._fulfillRoll(
+    new SubmitEvent("a"),
+    new HTMLFormElement(),
+    new FormDataExtended(new HTMLFormElement()),
+  ),
+).toEqualTypeOf<void>();
+
+expectTypeOf(await r.addTerm(t)).toEqualTypeOf<void>();
+expectTypeOf(r._checkDone()).toEqualTypeOf<void>();
+expectTypeOf(r._toggleSubmission(true)).toEqualTypeOf<void>();
diff --git a/tests/foundry/common/utils/bitmask.mjs.test-d.ts b/tests/foundry/common/utils/bitmask.mjs.test-d.ts
new file mode 100644
index 000000000..53a317619
--- /dev/null
+++ b/tests/foundry/common/utils/bitmask.mjs.test-d.ts
@@ -0,0 +1,25 @@
+import { expectTypeOf } from "vitest";
+
+const b = new foundry.utils.BitMask();
+
+expectTypeOf(b.hasState("a")).toEqualTypeOf<boolean>();
+expectTypeOf(b.addState("a")).toEqualTypeOf<void>();
+expectTypeOf(b.removeState("a")).toEqualTypeOf<void>();
+
+expectTypeOf(b.toggleState("a", true)).toEqualTypeOf<void>();
+expectTypeOf(b.toggleState("a")).toEqualTypeOf<number>();
+
+expectTypeOf(b.clear()).toEqualTypeOf<void>();
+expectTypeOf(b.valueOf()).toEqualTypeOf<number>();
+expectTypeOf(b.toString()).toEqualTypeOf<string>();
+
+const b2 = new foundry.utils.BitMask({ a: true });
+expectTypeOf(b.isCompatible(b2)).toEqualTypeOf<boolean>();
+
+expectTypeOf(b.toJSON()).toEqualTypeOf<string>();
+
+expectTypeOf(foundry.utils.BitMask.fromJSON("{a: true}")).toEqualTypeOf<foundry.utils.BitMask>();
+expectTypeOf(b.toObject()).toEqualTypeOf<Record<string, boolean>>();
+expectTypeOf(b.clone()).toEqualTypeOf<foundry.utils.BitMask>();
+
+expectTypeOf(foundry.utils.BitMask.generateShaderBitMaskConstants(["a"])).toEqualTypeOf<string>();
diff --git a/tests/foundry/common/utils/string-tree.mjs.test-d.ts b/tests/foundry/common/utils/string-tree.mjs.test-d.ts
new file mode 100644
index 000000000..ce5be50aa
--- /dev/null
+++ b/tests/foundry/common/utils/string-tree.mjs.test-d.ts
@@ -0,0 +1,8 @@
+import { expectTypeOf } from "vitest";
+
+const s = new foundry.utils.StringTree();
+declare const node: foundry.utils.StringTree.StringTreeNode;
+
+expectTypeOf(s.addLeaf(["a"], node)).toEqualTypeOf<foundry.utils.StringTree.StringTreeNode>();
+expectTypeOf(s.lookup(["a"], { limit: 4 })).toEqualTypeOf<foundry.utils.StringTree.StringTreeNode[]>();
+expectTypeOf(s.nodeAtPrefix(["a"])).toEqualTypeOf<foundry.utils.StringTree.StringTreeNode | void>();
diff --git a/tests/foundry/common/utils/word-tree.mjs.test-d.ts b/tests/foundry/common/utils/word-tree.mjs.test-d.ts
new file mode 100644
index 000000000..580d546a6
--- /dev/null
+++ b/tests/foundry/common/utils/word-tree.mjs.test-d.ts
@@ -0,0 +1,8 @@
+import { expectTypeOf } from "vitest";
+
+const w = new foundry.utils.WordTree();
+declare const entry: foundry.utils.WordTree.WordTreeEntry;
+
+expectTypeOf(w.addLeaf("a", entry)).toEqualTypeOf<foundry.utils.WordTree.WordTreeEntry>();
+expectTypeOf(w.lookup("a", { limit: 4 })).toEqualTypeOf<foundry.utils.WordTree.WordTreeEntry[]>();
+expectTypeOf(w.nodeAtPrefix("a")).toEqualTypeOf<foundry.utils.WordTree.WordTreeEntry>();