Index = ({ directory } = {}) => async () => {
  const index = await LevelStorage({ path: directory, valueEncoding })
  const indexedEntries = await LevelStorage({ path: pathJoin(directory, '/_indexedEntries/'), valueEncoding })

  const update = async (log, entry) => {
    const keys = new Set()
    const toBeIndexed = new Set()
    const latest = entry.hash

    // Function to check if a hash is in the entry index
    const isIndexed = async (hash) => (await indexedEntries.get(hash)) === true
    const isNotIndexed = async (hash) => !(await isIndexed(hash))

    // Function to decide when the log traversal should be stopped
    const shoudStopTraverse = async (entry) => {
      // Go through the nexts of an entry and if any is not yet
      // indexed, add it to the list of entries-to-be-indexed
      for await (const hash of entry.next) {
        if (await isNotIndexed(hash)) {
          toBeIndexed.add(hash)
        }
      }
      // If the latest entry and all its nexts are indexed and to-be-indexed list is empty,
      // we don't have anything more to process, so return true to stop the traversal
      return await isIndexed(latest) && toBeIndexed.size === 0
    }

    // Traverse the log and stop when everything has been processed
    for await (const entry of log.traverse(null, shoudStopTraverse)) {
      const { hash, payload } = entry
      // If an entry is not yet indexed, process it
      if (await isNotIndexed(hash)) {
        const { op, key } = payload
        if (op === 'PUT' && !keys.has(key)) {
          keys.add(key)
          await index.put(key, entry)
          await indexedEntries.put(hash, true)
        } else if (op === 'DEL' && !keys.has(key)) {
          keys.add(key)
          await index.del(key)
          await indexedEntries.put(hash, true)
        }
        // Remove the entry (hash) from the list of to-be-indexed entries
        toBeIndexed.delete(hash)
      }
    }
  }

  /**
   * Closes the index and its storages.
   */
  const close = async () => {
    await index.close()
    await indexedEntries.close()
  }

  /**
   * Drops all records from the index and its storages.
   */
  const drop = async () => {
    await index.clear()
    await indexedEntries.clear()
  }

  return {
    get: index.get,
    iterator: index.iterator,
    update,
    close,
    drop
  }
}
Defines an index for a KeyValue database.
@param {string} [directory] A location for storing the index-related data
@return {Index} A Index function.