/**
* @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