Source: storage/ipfs-block.js

/**
 * @namespace Storage-IPFS
 * @memberof module:Storage
 * @description
 * IPFSBlockStorage uses IPFS to store data as raw blocks.
 */
import { CID } from 'multiformats/cid'
import { base58btc } from 'multiformats/bases/base58'
import drain from 'it-drain'
import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
import { anySignal } from 'any-signal'

const DefaultTimeout = 30000 // 30 seconds

/**
 * Creates an instance of IPFSBlockStorage.
 * @function
 * @param {Object} params One or more parameters for configuring
 * IPFSBlockStorage.
 * @param {IPFS} params.ipfs An IPFS instance.
 * @param {boolean} [params.pin=false] True, if the block should be pinned,
 * false otherwise.
 * @param {number} [params.timeout=defaultTimeout] A timeout in ms.
 * @return {module:Storage.Storage-IPFS} An instance of IPFSBlockStorage.
 * @memberof module:Storage
 * @throw An instance of ipfs is required if params.ipfs is not specified.
 * @instance
 */
const IPFSBlockStorage = async ({ ipfs, pin, timeout } = {}) => {
  if (!ipfs) throw new Error('An instance of ipfs is required.')

  const shutDownController = new AbortController()

  /**
   * Puts data to an IPFS block.
   * @function
   * @param {string} hash The hash of the block to put.
   * @param {*} data The data to store in the IPFS block.
   * @memberof module:Storage.Storage-IPFS
   * @instance
   */
  const put = async (hash, data, signal) => {
    const cid = CID.parse(hash, base58btc)
    const combinedSignal = anySignal([
      shutDownController.signal,
      AbortSignal.timeout(timeout ?? DefaultTimeout),
      signal
    ])

    try {
      await ipfs.blockstore.put(cid, data, { signal: combinedSignal })
      await persist(hash, combinedSignal)
    } finally {
      combinedSignal.clear()
    }
  }

  const del = async (hash) => {}

  /**
   * Gets data from an IPFS block.
   * @function
   * @param {string} hash The hash of the block to get.
   * @return {Uint8Array} The block.
   * @memberof module:Storage.Storage-IPFS
   * @instance
   */
  const get = async (hash, signal) => {
    const cid = CID.parse(hash, base58btc)
    const combinedSignal = anySignal([
      shutDownController.signal,
      AbortSignal.timeout(timeout ?? DefaultTimeout),
      signal
    ])

    try {
      const chunks = []
      for await (const chunk of ipfs.blockstore.get(cid, { signal: combinedSignal })) {
        chunks.push(chunk)
      }

      if (chunks.length > 0) {
        return uint8ArrayConcat(chunks)
      }
    } finally {
      combinedSignal.clear()
    }
  }

  const persist = async (hash, signal) => {
    const cid = CID.parse(hash, base58btc)
    if (pin && !(await ipfs.pins.isPinned(cid, { signal }))) {
      await drain(ipfs.pins.add(cid, { signal }))
    }
  }

  const iterator = async function * () {}

  const merge = async (other) => {}

  const clear = async () => {}

  const close = async () => {
    shutDownController.abort()
  }

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

export default IPFSBlockStorage