{"version":3,"file":"SearchableMap.mjs","sources":["../src/SearchableMap/TreeIterator.ts","../src/SearchableMap/fuzzySearch.ts","../src/SearchableMap/SearchableMap.ts"],"sourcesContent":["import type { Entry, LeafType, RadixTree } from \"./typings.js\";\n\nexport const ENTRIES = \"ENTRIES\";\n\nexport const KEYS = \"KEYS\";\n\nexport const VALUES = \"VALUES\";\n\nexport const LEAF = \"\" as LeafType;\n\ninterface Iterators<T> {\n  ENTRIES: Entry<T>;\n  KEYS: string;\n  VALUES: T;\n}\n\ntype Kind<T> = keyof Iterators<T>;\ntype Result<T, K extends keyof Iterators<T>> = Iterators<T>[K];\n\ntype IteratorPath<T> = {\n  node: RadixTree<T>;\n  keys: string[];\n}[];\n\nexport interface IterableSet<T> {\n  _tree: RadixTree<T>;\n  _prefix: string;\n}\n\n/**\n * @private\n */\nexport class TreeIterator<T, K extends Kind<T>>\n  implements Iterator<Result<T, K>>\n{\n  set: IterableSet<T>;\n  _type: K;\n  _path: IteratorPath<T>;\n\n  constructor(set: IterableSet<T>, type: K) {\n    const node = set._tree;\n    const keys = Array.from(node.keys());\n\n    this.set = set;\n    this._type = type;\n    this._path = keys.length > 0 ? [{ node, keys }] : [];\n  }\n\n  next(): IteratorResult<Result<T, K>> {\n    const value = this.dive();\n\n    this.backtrack();\n\n    return value;\n  }\n\n  dive(): IteratorResult<Result<T, K>> {\n    if (this._path.length === 0) return { done: true, value: undefined };\n\n    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n    const { node, keys } = last(this._path)!;\n\n    if (last(keys) === LEAF) return { done: false, value: this.result() };\n\n    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n    const child = node.get(last(keys)!)!;\n\n    this._path.push({ node: child, keys: Array.from(child.keys()) });\n\n    return this.dive();\n  }\n\n  backtrack(): void {\n    if (this._path.length === 0) return;\n\n    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n    const keys = last(this._path)!.keys;\n\n    keys.pop();\n    if (keys.length > 0) return;\n\n    this._path.pop();\n    this.backtrack();\n  }\n\n  key(): string {\n    return (\n      this.set._prefix +\n      this._path\n        .map(({ keys }) => last(keys))\n        .filter((key) => key !== LEAF)\n        .join(\"\")\n    );\n  }\n\n  value(): T {\n    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n    return last(this._path)!.node.get(LEAF)!;\n  }\n\n  result(): Result<T, K> {\n    switch (this._type) {\n      case VALUES:\n        return this.value() as Result<T, K>;\n      case KEYS:\n        return this.key() as Result<T, K>;\n      default:\n        return [this.key(), this.value()] as Result<T, K>;\n    }\n  }\n\n  [Symbol.iterator](): this {\n    return this;\n  }\n}\n\nconst last = <T>(array: T[]): T | undefined => {\n  return array[array.length - 1];\n};\n","import { LEAF } from \"./TreeIterator.js\";\nimport type { FuzzyResults, RadixTree } from \"./typings.js\";\n\nexport const fuzzySearch = <Value = any>(\n  node: RadixTree<Value>,\n  query: string,\n  maxDistance: number,\n): FuzzyResults<Value> => {\n  const results: FuzzyResults<Value> = new Map();\n\n  if (typeof query !== \"string\") return results;\n\n  // Number of columns in the Levenshtein matrix.\n  const n = query.length + 1;\n\n  // Matching terms can never be longer than N + maxDistance.\n  const m = n + maxDistance;\n\n  // Fill first matrix row and column with numbers: 0 1 2 3 ...\n  const matrix = new Uint8Array(m * n).fill(maxDistance + 1);\n\n  for (let j = 0; j < n; ++j) matrix[j] = j;\n  for (let i = 1; i < m; ++i) matrix[i * n] = i;\n\n  recurse(node, query, maxDistance, results, matrix, 1, n, \"\");\n\n  return results;\n};\n\n// Modified version of http://stevehanov.ca/blog/?id=114\n\n// This builds a Levenshtein matrix for a given query and continuously updates\n// it for nodes in the radix tree that fall within the given maximum edit\n// distance. Keeping the same matrix around is beneficial especially for larger\n// edit distances.\n//\n//           k   a   t   e   <-- query\n//       0   1   2   3   4\n//   c   1   1   2   3   4\n//   a   2   2   1   2   3\n//   t   3   3   2   1  [2]  <-- edit distance\n//   ^\n//   ^ term in radix tree, rows are added and removed as needed\n\nconst recurse = <Value = any>(\n  node: RadixTree<Value>,\n  query: string,\n  maxDistance: number,\n  results: FuzzyResults<Value>,\n  matrix: Uint8Array,\n  m: number,\n  n: number,\n  prefix: string,\n): void => {\n  const offset = m * n;\n\n  key: for (const key of node.keys())\n    if (key === LEAF) {\n      // We've reached a leaf node. Check if the edit distance acceptable and\n      // store the result if it is.\n      const distance = matrix[offset - 1];\n\n      if (distance <= maxDistance)\n        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n        results.set(prefix, [node.get(key)!, distance]);\n    } else {\n      // Iterate over all characters in the key. Update the Levenshtein matrix\n      // and check if the minimum distance in the last row is still within the\n      // maximum edit distance. If it is, we can recurse over all child nodes.\n      let i = m;\n\n      for (let pos = 0; pos < key.length; ++pos, ++i) {\n        const char = key[pos];\n        const thisRowOffset = n * i;\n        const prevRowOffset = thisRowOffset - n;\n\n        // Set the first column based on the previous row, and initialize the\n        // minimum distance in the current row.\n        let minDistance = matrix[thisRowOffset];\n\n        const jmin = Math.max(0, i - maxDistance - 1);\n        const jmax = Math.min(n - 1, i + maxDistance);\n\n        // Iterate over remaining columns (characters in the query).\n        for (let j = jmin; j < jmax; ++j) {\n          const different = char !== query[j];\n\n          // It might make sense to only read the matrix positions used for\n          // deletion/insertion if the characters are different. But we want to\n          // avoid conditional reads for performance reasons.\n          const rpl = matrix[prevRowOffset + j] + +different;\n          const del = matrix[prevRowOffset + j + 1] + 1;\n          const ins = matrix[thisRowOffset + j] + 1;\n\n          const dist = (matrix[thisRowOffset + j + 1] = Math.min(\n            rpl,\n            del,\n            ins,\n          ));\n\n          if (dist < minDistance) minDistance = dist;\n        }\n\n        // Because distance will never decrease, we can stop. There will be no\n        // matching child nodes.\n        if (minDistance > maxDistance) continue key;\n      }\n\n      recurse(\n        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n        node.get(key)!,\n        query,\n        maxDistance,\n        results,\n        matrix,\n        i,\n        n,\n        prefix + key,\n      );\n    }\n};\n","import { ENTRIES, KEYS, LEAF, TreeIterator, VALUES } from \"./TreeIterator.js\";\nimport { fuzzySearch } from \"./fuzzySearch.js\";\nimport type { Entry, FuzzyResults, Path, RadixTree } from \"./typings.js\";\n\n/**\n * A class implementing the same interface as a standard JavaScript\n * [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)\n * with string keys, but adding support for efficiently searching entries with\n * prefix or fuzzy search. This class is used internally by {@link SearchIndex} as\n * the inverted index data structure. The implementation is a radix tree\n * (compressed prefix tree).\n *\n * Since this class can be of general utility beyond _SlimSearch_, it is\n * exported by the `slimsearch` package and can be imported (or required) as\n * `slimsearch/SearchableMap`.\n *\n * @typeParam Value The type of the values stored in the map.\n */\nexport class SearchableMap<Value = any> {\n  /**\n   * @ignore\n   */\n  _tree: RadixTree<Value>;\n\n  /**\n   * @ignore\n   */\n  _prefix: string;\n\n  private _size: number | undefined = undefined;\n\n  /**\n   * The constructor is normally called without arguments, creating an empty\n   * map. In order to create a {@link SearchableMap} from an iterable or from an\n   * object, check {@link SearchableMap.from} and {@link SearchableMap.fromObject}.\n   *\n   * The constructor arguments are for internal use, when creating derived\n   * mutable views of a map at a prefix.\n   */\n  constructor(tree: RadixTree<Value> = new Map(), prefix = \"\") {\n    this._tree = tree;\n    this._prefix = prefix;\n  }\n\n  /**\n   * Creates and returns a mutable view of this {@link SearchableMap}, containing only\n   * entries that share the given prefix.\n   *\n   * ### Usage:\n   *\n   * ```js\n   * const map = new SearchableMap()\n   * map.set(\"unicorn\", 1)\n   * map.set(\"universe\", 2)\n   * map.set(\"university\", 3)\n   * map.set(\"unique\", 4)\n   * map.set(\"hello\", 5)\n   *\n   * const uni = map.atPrefix(\"uni\")\n   * uni.get(\"unique\") // => 4\n   * uni.get(\"unicorn\") // => 1\n   * uni.get(\"hello\") // => undefined\n   *\n   * const univer = map.atPrefix(\"univer\")\n   * univer.get(\"unique\") // => undefined\n   * univer.get(\"universe\") // => 2\n   * univer.get(\"university\") // => 3\n   * ```\n   *\n   * @param prefix  The prefix\n   * @return A {@link SearchableMap} representing a mutable view of the original Map at the given prefix\n   */\n  atPrefix(prefix: string): SearchableMap<Value> {\n    if (!prefix.startsWith(this._prefix)) throw new Error(\"Mismatched prefix\");\n\n    const [node, path] = trackDown(\n      this._tree,\n      prefix.slice(this._prefix.length),\n    );\n\n    if (node === undefined) {\n      const [parentNode, key] = last(path);\n\n      for (const k of parentNode.keys())\n        if (k !== LEAF && k.startsWith(key)) {\n          const node = new Map();\n\n          // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n          node.set(k.slice(key.length), parentNode.get(k)!);\n\n          return new SearchableMap<Value>(node, prefix);\n        }\n    }\n\n    return new SearchableMap<Value>(node, prefix);\n  }\n\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear\n   */\n  clear(): void {\n    this._size = undefined;\n    this._tree.clear();\n  }\n\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete\n   * @param key  Key to delete\n   */\n  delete(key: string): void {\n    this._size = undefined;\n\n    return remove(this._tree, key);\n  }\n\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries\n   * @return An iterator iterating through `[key, value]` entries.\n   */\n  entries(): TreeIterator<Value, \"ENTRIES\"> {\n    return new TreeIterator(this, ENTRIES);\n  }\n\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach\n   * @param fn  Iteration function\n   */\n  forEach(fn: (key: string, value: Value, map: SearchableMap) => void): void {\n    for (const [key, value] of this) fn(key, value, this);\n  }\n\n  /**\n   * Returns a Map of all the entries that have a key within the given edit\n   * distance from the search key. The keys of the returned Map are the matching\n   * keys, while the values are two-element arrays where the first element is\n   * the value associated to the key, and the second is the edit distance of the\n   * key to the search key.\n   *\n   * ### Usage:\n   *\n   * ```js\n   * const map = new SearchableMap()\n   * map.set('hello', 'world')\n   * map.set('hell', 'yeah')\n   * map.set('ciao', 'mondo')\n   *\n   * // Get all entries that match the key 'hallo' with a maximum edit distance of 2\n   * map.fuzzyGet('hallo', 2)\n   * // => Map(2) { 'hello' => ['world', 1], 'hell' => ['yeah', 2] }\n   *\n   * // In the example, the \"hello\" key has value \"world\" and edit distance of 1\n   * // (change \"e\" to \"a\"), the key \"hell\" has value \"yeah\" and edit distance of 2\n   * // (change \"e\" to \"a\", delete \"o\")\n   * ```\n   *\n   * @param key  The search key\n   * @param maxEditDistance  The maximum edit distance (Levenshtein)\n   * @return A Map of the matching keys to their value and edit distance\n   */\n  fuzzyGet(key: string, maxEditDistance: number): FuzzyResults<Value> {\n    return fuzzySearch<Value>(this._tree, key, maxEditDistance);\n  }\n\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get\n   * @param key  Key to get\n   * @return Value associated to the key, or `undefined` if the key is not\n   * found.\n   */\n  get(key: string): Value | undefined {\n    const node = lookup<Value>(this._tree, key);\n\n    return node !== undefined ? node.get(LEAF) : undefined;\n  }\n\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has\n   * @param key  Key\n   * @return True if the key is in the map, false otherwise\n   */\n  has(key: string): boolean {\n    const node = lookup(this._tree, key);\n\n    return node?.has(LEAF) ?? false;\n  }\n\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys\n   * @return An `Iterable` iterating through keys\n   */\n  keys(): TreeIterator<Value, \"KEYS\"> {\n    return new TreeIterator(this, KEYS);\n  }\n\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set\n   * @param key  Key to set\n   * @param value  Value to associate to the key\n   * @return The {@link SearchableMap} itself, to allow chaining\n   */\n  set(key: string, value: Value): this {\n    if (typeof key !== \"string\") throw new Error(\"key must be a string\");\n\n    this._size = undefined;\n    const node = createPath(this._tree, key);\n\n    node.set(LEAF, value);\n\n    return this;\n  }\n\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size\n   */\n  get size(): number {\n    if (this._size) return this._size;\n\n    /** @ignore */\n    this._size = 0;\n\n    const iter = this.entries();\n\n    while (!iter.next().done) this._size += 1;\n\n    return this._size;\n  }\n\n  /**\n   * Updates the value at the given key using the provided function. The function\n   * is called with the current value at the key, and its return value is used as\n   * the new value to be set.\n   *\n   * ### Example:\n   *\n   * ```js\n   * // Increment the current value by one\n   * searchableMap.update('somekey', (currentValue) => currentValue == null ? 0 : currentValue + 1)\n   * ```\n   *\n   * If the value at the given key is or will be an object, it might not require\n   * re-assignment. In that case it is better to use `fetch()`, because it is\n   * faster.\n   *\n   * @param key  The key to update\n   * @param fn  The function used to compute the new value from the current one\n   * @return The {@link SearchableMap} itself, to allow chaining\n   */\n  update(key: string, fn: (value: Value | undefined) => Value): this {\n    if (typeof key !== \"string\") throw new Error(\"key must be a string\");\n\n    this._size = undefined;\n    const node = createPath(this._tree, key);\n\n    node.set(LEAF, fn(node.get(LEAF)));\n\n    return this;\n  }\n\n  /**\n   * Fetches the value of the given key. If the value does not exist, calls the\n   * given function to create a new value, which is inserted at the given key\n   * and subsequently returned.\n   *\n   * ### Example:\n   *\n   * ```js\n   * const map = searchableMap.fetch('somekey', () => new Map())\n   * map.set('foo', 'bar')\n   * ```\n   *\n   * @param key  The key to update\n   * @param initial  A function that creates a new value if the key does not exist\n   * @return The existing or new value at the given key\n   */\n  fetch(key: string, initial: () => Value): Value {\n    if (typeof key !== \"string\") throw new Error(\"key must be a string\");\n\n    this._size = undefined;\n    const node = createPath(this._tree, key);\n\n    let value = node.get(LEAF);\n\n    if (value === undefined) node.set(LEAF, (value = initial()));\n\n    return value;\n  }\n\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values\n   * @return An `Iterable` iterating through values.\n   */\n  values(): TreeIterator<Value, \"VALUES\"> {\n    return new TreeIterator(this, VALUES);\n  }\n\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/@@iterator\n   */\n  [Symbol.iterator](): TreeIterator<Value, \"ENTRIES\"> {\n    return this.entries();\n  }\n\n  /**\n   * Creates a {@link SearchableMap} from an `Iterable` of entries\n   *\n   * @param entries  Entries to be inserted in the {@link SearchableMap}\n   * @return A new {@link SearchableMap} with the given entries\n   */\n  static from<T = any>(\n    entries: Iterable<Entry<T>> | Entry<T>[],\n  ): SearchableMap<T> {\n    const tree = new SearchableMap<T>();\n\n    for (const [key, value] of entries) tree.set(key, value);\n\n    return tree;\n  }\n\n  /**\n   * Creates a {@link SearchableMap} from the iterable properties of a JavaScript object\n   *\n   * @param object  Object of entries for the {@link SearchableMap}\n   * @return A new {@link SearchableMap} with the given entries\n   */\n  static fromObject<T = any>(object: Record<string, T>): SearchableMap<T> {\n    return SearchableMap.from<T>(Object.entries(object));\n  }\n}\n\nconst trackDown = <T = any>(\n  tree: RadixTree<T> | undefined,\n  key: string,\n  path: Path<T> = [],\n): [RadixTree<T> | undefined, Path<T>] => {\n  if (key.length === 0 || tree == null) return [tree, path];\n\n  for (const treeKey of tree.keys())\n    if (treeKey !== LEAF && key.startsWith(treeKey)) {\n      path.push([tree, treeKey]); // performance: update in place\n\n      return trackDown(tree.get(treeKey), key.slice(treeKey.length), path);\n    }\n\n  path.push([tree, key]); // performance: update in place\n\n  return trackDown(undefined, \"\", path);\n};\n\nconst lookup = <T = any>(\n  tree: RadixTree<T>,\n  key: string,\n): RadixTree<T> | undefined => {\n  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n  if (key.length === 0 || !tree) return tree;\n\n  for (const treeKey of tree.keys())\n    if (treeKey !== LEAF && key.startsWith(treeKey))\n      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n      return lookup(tree.get(treeKey)!, key.slice(treeKey.length));\n};\n\n// Create a path in the radix tree for the given key, and returns the deepest\n// node. This function is in the hot path for indexing. It avoids unnecessary\n// string operations and recursion for performance.\nconst createPath = <T = any>(node: RadixTree<T>, key: string): RadixTree<T> => {\n  const keyLength = key.length;\n\n  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n  outer: for (let pos = 0; node && pos < keyLength; ) {\n    // Check whether this key is a candidate: the first characters must match.\n    for (const k of node.keys())\n      if (k !== LEAF && key[pos] === k[0]) {\n        const len = Math.min(keyLength - pos, k.length);\n\n        // Advance offset to the point where key and k no longer match.\n        let offset = 1;\n\n        while (offset < len && key[pos + offset] === k[offset]) ++offset;\n\n        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n        const child = node.get(k)!;\n\n        if (offset === k.length) {\n          // The existing key is shorter than the key we need to create.\n          node = child;\n        } else {\n          // Partial match: we need to insert an intermediate node to contain\n          // both the existing subtree and the new node.\n          const intermediate = new Map();\n\n          intermediate.set(k.slice(offset), child);\n          node.set(key.slice(pos, pos + offset), intermediate);\n          node.delete(k);\n          node = intermediate;\n        }\n\n        pos += offset;\n        continue outer;\n      }\n\n    // Create a final child node to contain the final suffix of the key.\n    const child = new Map();\n\n    node.set(key.slice(pos), child);\n\n    return child;\n  }\n\n  return node;\n};\n\nconst remove = <T = any>(tree: RadixTree<T>, key: string): void => {\n  const [node, path] = trackDown(tree, key);\n\n  if (node === undefined) return;\n\n  node.delete(LEAF);\n\n  if (node.size === 0) {\n    cleanup(path);\n  } else if (node.size === 1) {\n    const [key, value] = (\n      node.entries().next() as IteratorResult<\n        [string, RadixTree<T>],\n        [string, RadixTree<T>]\n      >\n    ).value;\n\n    merge(path, key, value);\n  }\n};\n\nconst cleanup = <T = any>(path: Path<T>): void => {\n  if (path.length === 0) return;\n\n  const [node, key] = last(path);\n\n  node.delete(key);\n\n  if (node.size === 0) {\n    cleanup(path.slice(0, -1));\n  } else if (node.size === 1) {\n    const [key, value] = (\n      node.entries().next() as IteratorResult<\n        [string, RadixTree<T>],\n        [string, RadixTree<T>]\n      >\n    ).value;\n\n    if (key !== LEAF) merge(path.slice(0, -1), key, value);\n  }\n};\n\nconst merge = <T = any>(\n  path: Path<T>,\n  key: string,\n  value: RadixTree<T>,\n): void => {\n  if (path.length === 0) return;\n\n  const [node, nodeKey] = last(path);\n\n  node.set(nodeKey + key, value);\n  node.delete(nodeKey);\n};\n\nconst last = <T = any>(array: T[]): T => {\n  return array[array.length - 1];\n};\n"],"names":["ENTRIES","KEYS","VALUES","LEAF","TreeIterator","set","type","node","keys","value","last","child","key","array","fuzzySearch","query","maxDistance","results","n","m","matrix","j","i","recurse","prefix","offset","distance","pos","char","thisRowOffset","prevRowOffset","minDistance","jmin","jmax","different","rpl","del","ins","dist","SearchableMap","tree","path","trackDown","parentNode","k","remove","fn","maxEditDistance","lookup","createPath","iter","initial","entries","object","treeKey","keyLength","outer","len","intermediate","cleanup","merge","nodeKey"],"mappings":"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"}