UNPKG

@chris.troutner/ipfs-message-port-server

Version:
330 lines (297 loc) 7.89 kB
'use strict' /* eslint-env browser */ const { decodeIterable, encodeIterable, decodeCallback } = require('@chris.troutner/ipfs-message-port-protocol/src/core') const { decodeCID, encodeCID } = require('@chris.troutner/ipfs-message-port-protocol/src/cid') /** * @typedef {import('cids').CIDVersion} CIDVersion * @typedef {import('@chris.troutner/ipfs-core-types').IPFS} IPFS * @typedef {import('@chris.troutner/ipfs-core-types/src/root').AddOptions} AddOptions * @typedef {import('@chris.troutner/ipfs-core-types/src/root').AddAllOptions} AddAllOptions * @typedef {import('@chris.troutner/ipfs-core-types/src/root').IPFSEntry} IPFSEntry * @typedef {import('@chris.troutner/ipfs-message-port-protocol/src/cid').EncodedCID} EncodedCID * @typedef {import('@chris.troutner/ipfs-core-types/src/utils').ImportCandidateStream} ImportCandidateStream * @typedef {import('@chris.troutner/ipfs-core-types/src/utils').ImportCandidate} ImportCandidate * @typedef {import('@chris.troutner/ipfs-core-types/src/root').AddResult} AddResult * @typedef {import('@chris.troutner/ipfs-message-port-protocol/src/root').EncodedAddInput} EncodedAddInput * @typedef {import('@chris.troutner/ipfs-message-port-protocol/src/root').EncodedAddAllInput} EncodedAddAllInput * @typedef {import('@chris.troutner/ipfs-message-port-protocol/src/root').EncodedFileContent} EncodedFileContent * @typedef {import('@chris.troutner/ipfs-message-port-protocol/src/root').EncodedIPFSEntry} EncodedIPFSEntry */ /** * @typedef {import('@chris.troutner/ipfs-message-port-protocol/src/core').RemoteCallback} RemoteCallback */ /** * @template T * @typedef {import('@chris.troutner/ipfs-message-port-protocol/src/core').RemoteIterable<T>} RemoteIterable */ /** * @typedef {Object} AddAllInput * @property {EncodedAddAllInput} input * @property {RemoteCallback} [progressCallback] * * @typedef {Object} AddInput * @property {EncodedAddInput} input * @property {RemoteCallback} [progressCallback] * * @typedef {AddInput & AddOptions} AddQuery * @typedef {AddAllInput & AddAllOptions} AddAllQuery */ exports.CoreService = class CoreService { /** * @param {IPFS} ipfs */ constructor (ipfs) { this.ipfs = ipfs } /** * @param {AddAllQuery} query */ addAll (query) { const { input } = query const { chunker, cidVersion, enableShardingExperiment, hashAlg, onlyHash, pin, progressCallback, rawLeaves, shardSplitThreshold, trickle, wrapWithDirectory, timeout, signal } = query let progress if (progressCallback) { const fn = decodeCallback(progressCallback) /** @type {import('@chris.troutner/ipfs-core-types/src/root').AddProgressFn} */ progress = (bytes, fileName) => { fn([bytes, fileName]) } } /** @type {AddAllOptions} */ const options = { chunker, cidVersion, enableShardingExperiment, hashAlg, onlyHash, pin, rawLeaves, shardSplitThreshold, trickle, wrapWithDirectory, timeout, progress, signal } const content = decodeAddAllInput(input) return encodeAddAllResult(this.ipfs.addAll(content, options)) } /** * @param {AddQuery} query */ async add (query) { const { input } = query const { chunker, cidVersion, hashAlg, onlyHash, pin, progressCallback, rawLeaves, trickle, wrapWithDirectory, timeout, signal } = query let progress if (progressCallback) { const fn = decodeCallback(progressCallback) /** @type {import('@chris.troutner/ipfs-core-types/src/root').AddProgressFn} */ progress = (bytes, fileName) => { fn([bytes, fileName]) } } /** @type {AddOptions} */ const options = { chunker, cidVersion, hashAlg, onlyHash, pin, rawLeaves, trickle, wrapWithDirectory, timeout, progress, signal } const content = decodeAddInput(input) return encodeAddResult(await this.ipfs.add(content, options)) } /** * @typedef {Object} CatQuery * @property {string|EncodedCID} path * @property {number} [offset] * @property {number} [length] * @property {number} [timeout] * @property {AbortSignal} [signal] * * @param {CatQuery} query */ cat (query) { const { path, offset, length, timeout, signal } = query const location = typeof path === 'string' ? path : decodeCID(path) const content = this.ipfs.cat(location, { offset, length, timeout, signal }) return encodeCatResult(content) } /** * @typedef {Object} LsQuery * @property {string|EncodedCID} path * @property {boolean} [preload] * @property {boolean} [recursive] * @property {number} [timeout] * @property {AbortSignal} [signal] * * @param {LsQuery} query */ ls (query) { const { path, recursive, preload, timeout, signal } = query const location = typeof path === 'string' ? path : decodeCID(path) const entries = this.ipfs.ls(location, { recursive, preload, timeout, signal }) return encodeLsResult(entries) } } /** * @param {EncodedAddAllInput} input * @returns {ImportCandidateStream} */ const decodeAddAllInput = input => decodeIterable(input, decodeFileInput) /** * @param {*} input */ const decodeAddInput = input => matchInput( input, data => { if (data.type === 'RemoteIterable') { return { content: decodeIterable(data, decodeFileInput) } } else { return decodeFileInput(data) } } ) /** * * @param {*} input * @returns */ const decodeFileInput = input => matchInput(input, file => ({ ...file, content: file.content && decodeFileContent(file.content) })) /** * @param {EncodedFileContent} content */ const decodeFileContent = content => matchInput(content, input => decodeIterable(input, identity)) /** * @template I, O * @param {I} input * @param {(input: any) => O} decode * @returns {I | O} */ const matchInput = (input, decode) => { if ( typeof input === 'string' || input instanceof ArrayBuffer || input instanceof Blob || ArrayBuffer.isView(input) ) { return input } else { return decode(input) } } /** * @param {AsyncIterable<AddResult>} out */ const encodeAddAllResult = out => { /** @type {Transferable[]} */ const transfer = [] return { data: encodeIterable(out, encodeFileOutput, transfer), transfer } } /** * @param {AddResult} out */ const encodeAddResult = out => { /** @type {Transferable[]} */ const transfer = [] return { data: encodeFileOutput(out, transfer), transfer } } /** * @param {AsyncIterable<Uint8Array>} content */ const encodeCatResult = content => { /** @type {Transferable[]} */ const transfer = [] return { data: encodeIterable(content, moveBuffer, transfer), transfer } } /** * @param {AsyncIterable<IPFSEntry>} entries */ const encodeLsResult = entries => { /** @type {Transferable[]} */ const transfer = [] return { data: encodeIterable(entries, encodeLsEntry, transfer), transfer } } /** * @param {IPFSEntry} entry */ const encodeLsEntry = ({ depth, name, path, size, cid, type, mode, mtime }) => ({ cid: encodeCID(cid), type, name, path, mode, mtime, size, depth }) /** * Adds underlying `ArrayBuffer` to the transfer list. * * @param {Uint8Array} buffer * @param {Transferable[]} transfer * @returns {Uint8Array} */ const moveBuffer = (buffer, transfer) => { transfer.push(buffer.buffer) return buffer } /** * @param {AddResult} file * @param {Transferable[]} _transfer */ const encodeFileOutput = (file, _transfer) => ({ ...file, cid: encodeCID(file.cid) }) /** * @template T * @param {T} v * @returns {T} */ const identity = v => v