UNPKG

@helia/verified-fetch

Version:

A fetch-like API for obtaining verified & trustless IPFS content on the web

83 lines 3.91 kB
import * as ipldDagCbor from '@ipld/dag-cbor'; import * as ipldDagJson from '@ipld/dag-json'; import * as dagPb from '@ipld/dag-pb'; import toBuffer from 'it-to-buffer'; import * as json from 'multiformats/codecs/json'; import * as raw from 'multiformats/codecs/raw'; import { identity } from 'multiformats/hashes/identity'; import { CODEC_CBOR } from "../constants.js"; import { getContentTypesForCid, MEDIA_TYPE_CBOR, MEDIA_TYPE_DAG_CBOR, MEDIA_TYPE_DAG_JSON, MEDIA_TYPE_JSON, MEDIA_TYPE_OCTET_STREAM, MEDIA_TYPE_RAW } from "../utils/content-types.js"; import { convertOutput } from "../utils/convert-output.js"; import { getContentDispositionFilename } from "../utils/get-content-disposition-filename.js"; import { notAcceptableResponse, okResponse, partialContentResponse } from '../utils/responses.js'; import { BasePlugin } from './plugin-base.js'; /** * Handles loading JSON and CBOR content */ export class IpldPlugin extends BasePlugin { id = 'ipld-plugin'; codes = [ CODEC_CBOR, ipldDagCbor.code, ipldDagJson.code, json.code, raw.code, dagPb.code, identity.code ]; canHandle({ terminalElement, accept }) { const supportsCid = this.codes.includes(terminalElement.cid.code); const supportsAccept = accept.length === 0 || accept.some(header => header.contentType.mediaType === MEDIA_TYPE_CBOR || header.contentType.mediaType === MEDIA_TYPE_DAG_CBOR || header.contentType.mediaType === MEDIA_TYPE_JSON || header.contentType.mediaType === MEDIA_TYPE_DAG_JSON || header.contentType.mediaType === MEDIA_TYPE_RAW || header.contentType.mediaType === MEDIA_TYPE_OCTET_STREAM); return supportsCid && supportsAccept; } async handle(context) { const { url, resource, accept, ipfsRoots, terminalElement, blockstore, options, requestedMimeTypes } = context; this.log.trace('fetching %c/%s', terminalElement.cid, url.pathname); let block; if (terminalElement.node == null) { block = await toBuffer(blockstore.get(terminalElement.cid, options)); } else if (terminalElement.type === 'object' || terminalElement.type === 'raw' || terminalElement.type === 'identity') { block = terminalElement.node; } else { block = dagPb.encode(terminalElement.node); } let contentType; try { // maybe convert output to different binary format const result = convertOutput(terminalElement.cid, block, accept); block = result.output; contentType = result.contentType; } catch (err) { this.log.error('could not decode object from block - %e', err); return notAcceptableResponse(resource, requestedMimeTypes, getContentTypesForCid(terminalElement.cid)); } const headers = { 'content-length': `${block.byteLength}`, 'content-type': contentType.mediaType, 'content-disposition': `${url.searchParams.get('download') === 'true' ? 'attachment' : contentType.disposition}; ${getContentDispositionFilename(url.searchParams.get('filename') ?? `${terminalElement.cid}${contentType.extension}`)}`, 'x-ipfs-roots': ipfsRoots.map(cid => cid.toV1()).join(','), 'x-content-type-options': 'nosniff', 'accept-ranges': 'bytes' }; if (context.range != null) { return partialContentResponse(resource, async function* (offset, length) { yield block.subarray(offset, offset + length); }, context.range, block.byteLength, { headers }); } return okResponse(resource, block, { headers }); } } //# sourceMappingURL=plugin-handle-ipld.js.map