Source: storage/composed.js

/**
 * @namespace Storage-Composed
 * @memberof module:Storage
 * @description
 * ComposedStorage stores data to multiple storage backends.
 * @example <caption>Store to LRU and Level</caption>
 * await ComposedStorage(await LRUStorage(), await LevelStorage())
 * @example <caption>Store to memory and IPFS</caption>
 * await ComposedStorage(await MemoryStorage(), await IPFSBlockStorage())
 * @example <caption>Store to LRU and a nested ComposedStorage</caption>
 * const storage1 = await ComposedStorage(await LRUStorage(), await LevelStorage())
 * await ComposedStorage(storage1, await IPFSBlockStorage())
 */

/**
  * Creates an instance of ComposedStorage.
  * @function
  * @param {module:Storage} storage1 A storage instance.
  * @param {module:Storage} storage2 A storage instance.
  * @return {module:Storage.Storage-Composed} An instance of ComposedStorage.
  * @memberof module:Storage
  * @instance
  */
const ComposedStorage = async (storage1, storage2) => {
  /**
   * Puts data to all configured storages.
   * @function
   * @param {string} hash The hash of the data to put.
   * @param {*} data The data to store.
   * @memberof module:Storage.Storage-Composed
   * @instance
   */
  const put = async (hash, data) => {
    await storage1.put(hash, data)
    await storage2.put(hash, data)
  }

  /**
   * Gets data from the composed storage.
   *
   * Get will fetch the data from storage1 first. If no value is found, an
   * attempt is made to fetch the data from storage2. If data exists in
   * storage2 but not in storage1, the data is added to storage1.
   * @function
   * @param {string} hash The hash of the data to get.
   * @memberof module:Storage.Storage-Composed
   * @instance
   */
  const get = async (hash) => {
    let value = await storage1.get(hash)
    if (!value) {
      value = await storage2.get(hash)
      if (value) {
        await storage1.put(hash, value)
      }
    }
    return value
  }

  /**
   * Deletes a value from storage.
   * @function
   * @param {string} hash The hash of the value to delete.
   * @memberof module:Storage.Storage-Composed
   * @instance
   */
  const del = async (hash) => {
    await storage1.del(hash)
    await storage2.del(hash)
  }

  /**
   * Iterates over records stored in both storages.
   * @function
   * @yields [string, string] The next key/value pair from all storages.
   * @memberof module:Storage.Storage-Composed
   * @instance
   */
  const iterator = async function * ({ amount, reverse } = {}) {
    const keys = []
    const iteratorOptions = { amount: amount || -1, reverse: reverse || false }
    for (const storage of [storage1, storage2]) {
      for await (const [key, value] of storage.iterator(iteratorOptions)) {
        if (!keys[key]) {
          keys[key] = true
          yield [key, value]
        }
      }
    }
  }

  /**
   * Merges data from another source into each of the composed storages.
   * @function
   * @param {module:Storage} other Another storage instance.
   * @memberof module:Storage.Storage-Composed
   * @instance
   */
  const merge = async (other) => {
    await storage1.merge(other)
    await storage2.merge(other)
    await other.merge(storage1)
    await other.merge(storage2)
  }

  /**
   * Calls clear on each of the composed storages.
   * @function
   * @memberof module:Storage.Storage-Composed
   * @instance
   */
  const clear = async () => {
    await storage1.clear()
    await storage2.clear()
  }

  /**
   * Calls close on each of the composed storages.
   * @function
   * @memberof module:Storage.Storage-Composed
   * @instance
   */
  const close = async () => {
    await storage1.close()
    await storage2.close()
  }

  return {
    put,
    get,
    del,
    iterator,
    merge,
    clear,
    close
  }
}

export default ComposedStorage