@helia/verified-fetch 4.1.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -40
- package/dist/index.min.js +73 -534
- package/dist/index.min.js.map +4 -4
- package/dist/src/constants.d.ts +2 -0
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +2 -0
- package/dist/src/constants.js.map +1 -1
- package/dist/src/index.d.ts +162 -68
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +11 -43
- package/dist/src/index.js.map +1 -1
- package/dist/src/plugins/index.d.ts +0 -5
- package/dist/src/plugins/index.d.ts.map +1 -1
- package/dist/src/plugins/index.js +0 -4
- package/dist/src/plugins/index.js.map +1 -1
- package/dist/src/plugins/plugin-base.d.ts +8 -9
- package/dist/src/plugins/plugin-base.d.ts.map +1 -1
- package/dist/src/plugins/plugin-base.js +5 -6
- package/dist/src/plugins/plugin-base.js.map +1 -1
- package/dist/src/plugins/plugin-handle-car.d.ts +3 -3
- package/dist/src/plugins/plugin-handle-car.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-car.js +38 -39
- package/dist/src/plugins/plugin-handle-car.js.map +1 -1
- package/dist/src/plugins/plugin-handle-ipld.d.ts +12 -0
- package/dist/src/plugins/plugin-handle-ipld.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-ipld.js +83 -0
- package/dist/src/plugins/plugin-handle-ipld.js.map +1 -0
- package/dist/src/plugins/plugin-handle-ipns-record.d.ts +3 -3
- package/dist/src/plugins/plugin-handle-ipns-record.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-ipns-record.js +25 -34
- package/dist/src/plugins/plugin-handle-ipns-record.js.map +1 -1
- package/dist/src/plugins/plugin-handle-tar.d.ts +3 -3
- package/dist/src/plugins/plugin-handle-tar.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-tar.js +20 -22
- package/dist/src/plugins/plugin-handle-tar.js.map +1 -1
- package/dist/src/plugins/plugin-handle-unixfs.d.ts +14 -0
- package/dist/src/plugins/plugin-handle-unixfs.d.ts.map +1 -0
- package/dist/src/plugins/plugin-handle-unixfs.js +180 -0
- package/dist/src/plugins/plugin-handle-unixfs.js.map +1 -0
- package/dist/src/plugins/types.d.ts +1 -77
- package/dist/src/plugins/types.d.ts.map +1 -1
- package/dist/src/url-resolver.d.ts +29 -11
- package/dist/src/url-resolver.d.ts.map +1 -1
- package/dist/src/url-resolver.js +152 -74
- package/dist/src/url-resolver.js.map +1 -1
- package/dist/src/utils/content-type-parser.d.ts.map +1 -1
- package/dist/src/utils/content-type-parser.js +4 -3
- package/dist/src/utils/content-type-parser.js.map +1 -1
- package/dist/src/utils/content-types.d.ts +26 -0
- package/dist/src/utils/content-types.d.ts.map +1 -0
- package/dist/src/utils/content-types.js +137 -0
- package/dist/src/utils/content-types.js.map +1 -0
- package/dist/src/utils/convert-output.d.ts +17 -0
- package/dist/src/utils/convert-output.d.ts.map +1 -0
- package/dist/src/utils/convert-output.js +176 -0
- package/dist/src/utils/convert-output.js.map +1 -0
- package/dist/src/utils/error-to-response.d.ts +3 -0
- package/dist/src/utils/error-to-response.d.ts.map +1 -0
- package/dist/src/utils/error-to-response.js +40 -0
- package/dist/src/utils/error-to-response.js.map +1 -0
- package/dist/src/utils/get-content-disposition-filename.d.ts +1 -1
- package/dist/src/utils/get-content-disposition-filename.d.ts.map +1 -1
- package/dist/src/utils/get-content-disposition-filename.js +4 -0
- package/dist/src/utils/get-content-disposition-filename.js.map +1 -1
- package/dist/src/utils/get-e-tag.d.ts +20 -15
- package/dist/src/utils/get-e-tag.d.ts.map +1 -1
- package/dist/src/utils/get-e-tag.js +8 -22
- package/dist/src/utils/get-e-tag.js.map +1 -1
- package/dist/src/utils/get-offset-and-length.d.ts +12 -2
- package/dist/src/utils/get-offset-and-length.d.ts.map +1 -1
- package/dist/src/utils/get-offset-and-length.js +63 -21
- package/dist/src/utils/get-offset-and-length.js.map +1 -1
- package/dist/src/utils/get-range-header.d.ts +22 -0
- package/dist/src/utils/get-range-header.d.ts.map +1 -0
- package/dist/src/utils/get-range-header.js +69 -0
- package/dist/src/utils/get-range-header.js.map +1 -0
- package/dist/src/utils/parse-url-string.d.ts +2 -1
- package/dist/src/utils/parse-url-string.d.ts.map +1 -1
- package/dist/src/utils/parse-url-string.js +46 -71
- package/dist/src/utils/parse-url-string.js.map +1 -1
- package/dist/src/utils/resource-to-cache-key.d.ts +3 -3
- package/dist/src/utils/resource-to-cache-key.js +5 -5
- package/dist/src/utils/resource-to-cache-key.js.map +1 -1
- package/dist/src/utils/response-headers.d.ts +4 -14
- package/dist/src/utils/response-headers.d.ts.map +1 -1
- package/dist/src/utils/response-headers.js +36 -36
- package/dist/src/utils/response-headers.js.map +1 -1
- package/dist/src/utils/responses.d.ts +30 -11
- package/dist/src/utils/responses.d.ts.map +1 -1
- package/dist/src/utils/responses.js +146 -39
- package/dist/src/utils/responses.js.map +1 -1
- package/dist/src/verified-fetch.d.ts +16 -15
- package/dist/src/verified-fetch.d.ts.map +1 -1
- package/dist/src/verified-fetch.js +307 -236
- package/dist/src/verified-fetch.js.map +1 -1
- package/dist/typedoc-urls.json +64 -45
- package/package.json +4 -3
- package/src/constants.ts +3 -0
- package/src/index.ts +203 -71
- package/src/plugins/index.ts +0 -6
- package/src/plugins/plugin-base.ts +8 -10
- package/src/plugins/plugin-handle-car.ts +48 -46
- package/src/plugins/plugin-handle-ipld.ts +93 -0
- package/src/plugins/plugin-handle-ipns-record.ts +31 -41
- package/src/plugins/plugin-handle-tar.ts +25 -29
- package/src/plugins/plugin-handle-unixfs.ts +217 -0
- package/src/plugins/types.ts +0 -86
- package/src/url-resolver.ts +197 -83
- package/src/utils/content-type-parser.ts +4 -3
- package/src/utils/content-types.ts +159 -0
- package/src/utils/convert-output.ts +187 -0
- package/src/utils/error-to-response.ts +49 -0
- package/src/utils/get-content-disposition-filename.ts +7 -1
- package/src/utils/get-e-tag.ts +26 -35
- package/src/utils/get-offset-and-length.ts +75 -21
- package/src/utils/get-range-header.ts +107 -0
- package/src/utils/parse-url-string.ts +51 -80
- package/src/utils/resource-to-cache-key.ts +5 -5
- package/src/utils/response-headers.ts +40 -41
- package/src/utils/responses.ts +186 -45
- package/src/verified-fetch.ts +359 -267
- package/dist/src/plugins/plugin-handle-byte-range-context.d.ts +0 -14
- package/dist/src/plugins/plugin-handle-byte-range-context.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-byte-range-context.js +0 -25
- package/dist/src/plugins/plugin-handle-byte-range-context.js.map +0 -1
- package/dist/src/plugins/plugin-handle-cbor.d.ts +0 -17
- package/dist/src/plugins/plugin-handle-cbor.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-cbor.js +0 -94
- package/dist/src/plugins/plugin-handle-cbor.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.d.ts +0 -27
- package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.js +0 -279
- package/dist/src/plugins/plugin-handle-dag-cbor-html-preview.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-cbor.d.ts +0 -17
- package/dist/src/plugins/plugin-handle-dag-cbor.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-cbor.js +0 -66
- package/dist/src/plugins/plugin-handle-dag-cbor.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-pb.d.ts +0 -17
- package/dist/src/plugins/plugin-handle-dag-pb.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-pb.js +0 -209
- package/dist/src/plugins/plugin-handle-dag-pb.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-walk.d.ts +0 -21
- package/dist/src/plugins/plugin-handle-dag-walk.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dag-walk.js +0 -95
- package/dist/src/plugins/plugin-handle-dag-walk.js.map +0 -1
- package/dist/src/plugins/plugin-handle-dir-index-html.d.ts +0 -10
- package/dist/src/plugins/plugin-handle-dir-index-html.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-dir-index-html.js +0 -59
- package/dist/src/plugins/plugin-handle-dir-index-html.js.map +0 -1
- package/dist/src/plugins/plugin-handle-json.d.ts +0 -12
- package/dist/src/plugins/plugin-handle-json.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-json.js +0 -73
- package/dist/src/plugins/plugin-handle-json.js.map +0 -1
- package/dist/src/plugins/plugin-handle-raw.d.ts +0 -9
- package/dist/src/plugins/plugin-handle-raw.d.ts.map +0 -1
- package/dist/src/plugins/plugin-handle-raw.js +0 -92
- package/dist/src/plugins/plugin-handle-raw.js.map +0 -1
- package/dist/src/plugins/plugins.d.ts +0 -6
- package/dist/src/plugins/plugins.d.ts.map +0 -1
- package/dist/src/plugins/plugins.js +0 -6
- package/dist/src/plugins/plugins.js.map +0 -1
- package/dist/src/utils/byte-range-context.d.ts +0 -103
- package/dist/src/utils/byte-range-context.d.ts.map +0 -1
- package/dist/src/utils/byte-range-context.js +0 -504
- package/dist/src/utils/byte-range-context.js.map +0 -1
- package/dist/src/utils/dag-cbor-to-safe-json.d.ts +0 -15
- package/dist/src/utils/dag-cbor-to-safe-json.d.ts.map +0 -1
- package/dist/src/utils/dag-cbor-to-safe-json.js +0 -54
- package/dist/src/utils/dag-cbor-to-safe-json.js.map +0 -1
- package/dist/src/utils/dir-index-html.d.ts +0 -19
- package/dist/src/utils/dir-index-html.d.ts.map +0 -1
- package/dist/src/utils/dir-index-html.js +0 -438
- package/dist/src/utils/dir-index-html.js.map +0 -1
- package/dist/src/utils/get-peer-id-from-string.d.ts +0 -3
- package/dist/src/utils/get-peer-id-from-string.d.ts.map +0 -1
- package/dist/src/utils/get-peer-id-from-string.js +0 -10
- package/dist/src/utils/get-peer-id-from-string.js.map +0 -1
- package/dist/src/utils/get-resolved-accept-header.d.ts +0 -9
- package/dist/src/utils/get-resolved-accept-header.d.ts.map +0 -1
- package/dist/src/utils/get-resolved-accept-header.js +0 -27
- package/dist/src/utils/get-resolved-accept-header.js.map +0 -1
- package/dist/src/utils/get-stream-from-async-iterable.d.ts +0 -9
- package/dist/src/utils/get-stream-from-async-iterable.d.ts.map +0 -1
- package/dist/src/utils/get-stream-from-async-iterable.js +0 -43
- package/dist/src/utils/get-stream-from-async-iterable.js.map +0 -1
- package/dist/src/utils/handle-redirects.d.ts +0 -16
- package/dist/src/utils/handle-redirects.d.ts.map +0 -1
- package/dist/src/utils/handle-redirects.js +0 -84
- package/dist/src/utils/handle-redirects.js.map +0 -1
- package/dist/src/utils/is-accept-explicit.d.ts +0 -15
- package/dist/src/utils/is-accept-explicit.d.ts.map +0 -1
- package/dist/src/utils/is-accept-explicit.js +0 -26
- package/dist/src/utils/is-accept-explicit.js.map +0 -1
- package/dist/src/utils/request-headers.d.ts +0 -13
- package/dist/src/utils/request-headers.d.ts.map +0 -1
- package/dist/src/utils/request-headers.js +0 -63
- package/dist/src/utils/request-headers.js.map +0 -1
- package/dist/src/utils/select-output-type.d.ts +0 -17
- package/dist/src/utils/select-output-type.d.ts.map +0 -1
- package/dist/src/utils/select-output-type.js +0 -153
- package/dist/src/utils/select-output-type.js.map +0 -1
- package/dist/src/utils/tlru.d.ts +0 -15
- package/dist/src/utils/tlru.d.ts.map +0 -1
- package/dist/src/utils/tlru.js +0 -34
- package/dist/src/utils/tlru.js.map +0 -1
- package/dist/src/utils/walk-path.d.ts +0 -27
- package/dist/src/utils/walk-path.d.ts.map +0 -1
- package/dist/src/utils/walk-path.js +0 -45
- package/dist/src/utils/walk-path.js.map +0 -1
- package/src/plugins/plugin-handle-byte-range-context.ts +0 -30
- package/src/plugins/plugin-handle-cbor.ts +0 -107
- package/src/plugins/plugin-handle-dag-cbor-html-preview.ts +0 -295
- package/src/plugins/plugin-handle-dag-cbor.ts +0 -83
- package/src/plugins/plugin-handle-dag-pb.ts +0 -248
- package/src/plugins/plugin-handle-dag-walk.ts +0 -110
- package/src/plugins/plugin-handle-dir-index-html.ts +0 -72
- package/src/plugins/plugin-handle-json.ts +0 -80
- package/src/plugins/plugin-handle-raw.ts +0 -110
- package/src/plugins/plugins.ts +0 -5
- package/src/utils/byte-range-context.ts +0 -597
- package/src/utils/dag-cbor-to-safe-json.ts +0 -63
- package/src/utils/dir-index-html.ts +0 -505
- package/src/utils/get-peer-id-from-string.ts +0 -12
- package/src/utils/get-resolved-accept-header.ts +0 -42
- package/src/utils/get-stream-from-async-iterable.ts +0 -49
- package/src/utils/handle-redirects.ts +0 -109
- package/src/utils/is-accept-explicit.ts +0 -38
- package/src/utils/request-headers.ts +0 -65
- package/src/utils/select-output-type.ts +0 -175
- package/src/utils/tlru.ts +0 -42
- package/src/utils/walk-path.ts +0 -68
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { code as dagPbCode } from '@ipld/dag-pb';
|
|
2
|
+
import { isPromise } from '@libp2p/utils';
|
|
3
|
+
import { exporter } from 'ipfs-unixfs-exporter';
|
|
4
|
+
import first from 'it-first';
|
|
5
|
+
import itToBrowserReadableStream from 'it-to-browser-readablestream';
|
|
6
|
+
import toBuffer from 'it-to-buffer';
|
|
7
|
+
import * as raw from 'multiformats/codecs/raw';
|
|
8
|
+
import { MEDIA_TYPE_OCTET_STREAM, MEDIA_TYPE_DAG_PB } from "../utils/content-types.js";
|
|
9
|
+
import { getContentDispositionFilename } from "../utils/get-content-disposition-filename.js";
|
|
10
|
+
import { badGatewayResponse, movedPermanentlyResponse, partialContentResponse, okResponse } from '../utils/responses.js';
|
|
11
|
+
import { BasePlugin } from './plugin-base.js';
|
|
12
|
+
/**
|
|
13
|
+
* @see https://specs.ipfs.tech/http-gateways/path-gateway/#use-in-directory-url-normalization
|
|
14
|
+
*/
|
|
15
|
+
function getRedirectUrl(resource, url, terminalElement) {
|
|
16
|
+
let uri;
|
|
17
|
+
try {
|
|
18
|
+
// try the requested resource
|
|
19
|
+
uri = new URL(resource);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// fall back to the canonical URL
|
|
23
|
+
uri = url;
|
|
24
|
+
}
|
|
25
|
+
// directories must be requested with a trailing slash
|
|
26
|
+
if (terminalElement?.type === 'directory' && !uri.pathname.endsWith('/')) {
|
|
27
|
+
// make sure we append slash to end of the path
|
|
28
|
+
uri.pathname += '/';
|
|
29
|
+
return uri.toString();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Handles UnixFS content
|
|
34
|
+
*/
|
|
35
|
+
export class UnixFSPlugin extends BasePlugin {
|
|
36
|
+
id = 'unixfs-plugin';
|
|
37
|
+
codes = [dagPbCode, raw.code];
|
|
38
|
+
canHandle({ terminalElement, accept }) {
|
|
39
|
+
const supportsCid = this.codes.includes(terminalElement.cid.code);
|
|
40
|
+
const supportsAccept = accept.length === 0 || accept.some(header => header.contentType.mediaType === MEDIA_TYPE_OCTET_STREAM ||
|
|
41
|
+
header.contentType.mediaType === MEDIA_TYPE_DAG_PB);
|
|
42
|
+
return supportsCid && supportsAccept;
|
|
43
|
+
}
|
|
44
|
+
async handle(context) {
|
|
45
|
+
let { url, resource, terminalElement, ipfsRoots } = context;
|
|
46
|
+
let filename = url.searchParams.get('filename') ?? terminalElement.name;
|
|
47
|
+
let redirected;
|
|
48
|
+
if (terminalElement.type === 'directory') {
|
|
49
|
+
const redirectUrl = getRedirectUrl(resource, url, terminalElement);
|
|
50
|
+
if (redirectUrl != null) {
|
|
51
|
+
this.log.trace('directory url normalization spec requires redirect');
|
|
52
|
+
if (context.options?.redirect === 'error') {
|
|
53
|
+
this.log('could not redirect to %s as redirect option was set to "error"', redirectUrl);
|
|
54
|
+
throw new TypeError('Failed to fetch');
|
|
55
|
+
}
|
|
56
|
+
else if (context.options?.redirect === 'manual') {
|
|
57
|
+
this.log('returning 301 permanent redirect to %s', redirectUrl);
|
|
58
|
+
return movedPermanentlyResponse(context.resource, redirectUrl);
|
|
59
|
+
}
|
|
60
|
+
this.log('following redirect to %s', redirectUrl);
|
|
61
|
+
// fall-through simulates following the redirect?
|
|
62
|
+
resource = redirectUrl;
|
|
63
|
+
redirected = true;
|
|
64
|
+
}
|
|
65
|
+
const dirCid = terminalElement.cid;
|
|
66
|
+
// if not disabled, search the directory for an index.html file
|
|
67
|
+
if (context.options?.supportDirectoryIndexes !== false) {
|
|
68
|
+
const rootFilePath = 'index.html';
|
|
69
|
+
try {
|
|
70
|
+
this.log.trace('found directory at %c/%s, looking for index.html', dirCid, url.pathname);
|
|
71
|
+
const entry = await context.serverTiming.time('exporter-dir', '', exporter(`/ipfs/${dirCid}/${rootFilePath}`, context.blockstore, context.options));
|
|
72
|
+
if (entry.type === 'directory' || entry.type === 'object') {
|
|
73
|
+
return badGatewayResponse(resource, 'Unable to stream content');
|
|
74
|
+
}
|
|
75
|
+
// use `index.html` as the file name to help with content types
|
|
76
|
+
filename = rootFilePath;
|
|
77
|
+
this.log.trace('found directory index at %c/%s with cid %c', dirCid, rootFilePath, entry.cid);
|
|
78
|
+
return await this.streamFile(resource, entry, filename, redirected, context.range, context.options);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
if (err.name !== 'NotFoundError') {
|
|
82
|
+
this.log.error('error loading path %c/%s - %e', dirCid, rootFilePath, err);
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// no index file found, return the directory listing
|
|
88
|
+
const block = await toBuffer(context.blockstore.get(dirCid, context.options));
|
|
89
|
+
return okResponse(resource, block, {
|
|
90
|
+
headers: {
|
|
91
|
+
'content-type': MEDIA_TYPE_DAG_PB,
|
|
92
|
+
'content-length': `${block.byteLength}`,
|
|
93
|
+
'content-disposition': `${url.searchParams.get('download') === 'true' ? 'attachment' : 'inline'}; ${getContentDispositionFilename(`${dirCid}.dir`)}`,
|
|
94
|
+
'x-ipfs-roots': ipfsRoots.map(cid => cid.toV1()).join(','),
|
|
95
|
+
'accept-ranges': 'bytes'
|
|
96
|
+
},
|
|
97
|
+
redirected
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
else if (terminalElement.type === 'file' || terminalElement.type === 'raw' || terminalElement.type === 'identity') {
|
|
101
|
+
this.log('streaming file');
|
|
102
|
+
return this.streamFile(resource, terminalElement, filename, redirected, context.range, context.options);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
this.log.error('cannot stream terminal element type %s', terminalElement.type);
|
|
106
|
+
return badGatewayResponse(resource, 'Unable to stream content');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async streamFile(resource, entry, filename, redirected, rangeHeader, options) {
|
|
110
|
+
let contentType = MEDIA_TYPE_OCTET_STREAM;
|
|
111
|
+
// only detect content type for non-range requests to avoid loading blocks
|
|
112
|
+
// we aren't going to stream to the user
|
|
113
|
+
if (rangeHeader == null) {
|
|
114
|
+
contentType = await this.detectContentType(entry, filename, options);
|
|
115
|
+
}
|
|
116
|
+
if (rangeHeader != null) {
|
|
117
|
+
return partialContentResponse(resource, (offset, length) => {
|
|
118
|
+
return entry.content({
|
|
119
|
+
...(options ?? {}),
|
|
120
|
+
offset,
|
|
121
|
+
length
|
|
122
|
+
});
|
|
123
|
+
}, rangeHeader, entry.size, {
|
|
124
|
+
headers: {
|
|
125
|
+
'content-type': contentType,
|
|
126
|
+
'content-disposition': `inline; ${getContentDispositionFilename(filename)}`,
|
|
127
|
+
'x-ipfs-roots': entry.cid.toString(),
|
|
128
|
+
'accept-ranges': 'bytes'
|
|
129
|
+
},
|
|
130
|
+
redirected
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
// nb. if streaming the output fails (network error, unresolvable block,
|
|
134
|
+
// etc), a "TypeError: Failed to fetch" error will occur
|
|
135
|
+
return okResponse(resource, itToBrowserReadableStream(entry.content(options)), {
|
|
136
|
+
headers: {
|
|
137
|
+
'content-type': contentType,
|
|
138
|
+
'content-length': `${entry.size}`,
|
|
139
|
+
'content-disposition': `inline; ${getContentDispositionFilename(filename)}`,
|
|
140
|
+
'x-ipfs-roots': entry.cid.toString(),
|
|
141
|
+
'accept-ranges': 'bytes'
|
|
142
|
+
},
|
|
143
|
+
redirected
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
async detectContentType(entry, filename, options) {
|
|
147
|
+
let buf;
|
|
148
|
+
if (entry.type === 'raw' || entry.type === 'identity') {
|
|
149
|
+
buf = entry.node;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
// read the first block of the file
|
|
153
|
+
buf = await first(entry.content(options));
|
|
154
|
+
}
|
|
155
|
+
if (buf == null) {
|
|
156
|
+
throw new Error('stream ended before first block was read');
|
|
157
|
+
}
|
|
158
|
+
let contentType;
|
|
159
|
+
if (this.pluginOptions.contentTypeParser != null) {
|
|
160
|
+
try {
|
|
161
|
+
const parsed = this.pluginOptions.contentTypeParser(buf, filename);
|
|
162
|
+
if (isPromise(parsed)) {
|
|
163
|
+
const result = await parsed;
|
|
164
|
+
if (result != null) {
|
|
165
|
+
contentType = result;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else if (parsed != null) {
|
|
169
|
+
contentType = parsed;
|
|
170
|
+
}
|
|
171
|
+
this.log.trace('contentTypeParser returned %s for file with name %s', contentType, filename);
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
this.log.error('error parsing content type - %e', err);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return contentType ?? MEDIA_TYPE_OCTET_STREAM;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=plugin-handle-unixfs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-handle-unixfs.js","sourceRoot":"","sources":["../../../src/plugins/plugin-handle-unixfs.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,78 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import type { ByteRangeContext } from '../utils/byte-range-context.js';
|
|
3
|
-
import type { AcceptHeader } from '../utils/select-output-type.ts';
|
|
4
|
-
import type { ServerTiming } from '../utils/server-timing.ts';
|
|
5
|
-
import type { PathWalkerResponse } from '../utils/walk-path.js';
|
|
6
|
-
import type { IPNSResolver } from '@helia/ipns';
|
|
7
|
-
import type { AbortOptions, Logger } from '@libp2p/interface';
|
|
8
|
-
import type { Helia } from 'helia';
|
|
9
|
-
import type { Blockstore } from 'interface-blockstore';
|
|
10
|
-
import type { UnixFSEntry } from 'ipfs-unixfs-exporter';
|
|
11
|
-
import type { CID } from 'multiformats/cid';
|
|
12
|
-
import type { CustomProgressEvent } from 'progress-events';
|
|
13
|
-
/**
|
|
14
|
-
* Contains common components and functions required by plugins to handle a request.
|
|
15
|
-
* - Read-Only: Plugins can read but shouldn't rewrite them.
|
|
16
|
-
* - Persistent: Relevant even after the request completes (e.g., logging or metrics).
|
|
17
|
-
*/
|
|
18
|
-
export interface PluginOptions {
|
|
19
|
-
logger: Logger;
|
|
20
|
-
getBlockstore(cid: CID, resource: string | CID, useSession?: boolean, options?: AbortOptions): Blockstore;
|
|
21
|
-
contentTypeParser?: ContentTypeParser;
|
|
22
|
-
helia: Helia;
|
|
23
|
-
ipnsResolver: IPNSResolver;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Represents the ephemeral, modifiable state used by the pipeline.
|
|
27
|
-
* - Mutable: Evolves as you walk the plugin chain.
|
|
28
|
-
* - Shared Data: Allows plugins to communicate partial results, discovered data, or interim errors.
|
|
29
|
-
* - Ephemeral: Typically discarded once fetch(...) completes.
|
|
30
|
-
*/
|
|
31
|
-
export interface PluginContext extends ResolveURLResult {
|
|
32
|
-
readonly resource: string;
|
|
33
|
-
readonly accept?: AcceptHeader;
|
|
34
|
-
/**
|
|
35
|
-
* An array of plugin IDs that are all enabled. You can use this to check if a plugin is enabled and respond accordingly.
|
|
36
|
-
*/
|
|
37
|
-
plugins: string[];
|
|
38
|
-
/**
|
|
39
|
-
* The last time the context is modified, so we know whether a plugin has modified it.
|
|
40
|
-
* A plugin should increment this value if it modifies the context.
|
|
41
|
-
*/
|
|
42
|
-
modified: number;
|
|
43
|
-
onProgress?(evt: CustomProgressEvent<any>): void;
|
|
44
|
-
options?: Omit<VerifiedFetchInit, 'signal'> & AbortOptions;
|
|
45
|
-
isDirectory?: boolean;
|
|
46
|
-
directoryEntries?: UnixFSEntry[];
|
|
47
|
-
reqFormat?: RequestFormatShorthand;
|
|
48
|
-
pathDetails?: PathWalkerResponse;
|
|
49
|
-
/**
|
|
50
|
-
* ByteRangeContext contains information about the size of the content and range requests.
|
|
51
|
-
* This can be used to set the Content-Length header without loading the entire body.
|
|
52
|
-
*
|
|
53
|
-
* This is set by the ByteRangeContextPlugin
|
|
54
|
-
*/
|
|
55
|
-
byteRangeContext?: ByteRangeContext;
|
|
56
|
-
serverTiming: ServerTiming;
|
|
57
|
-
ipfsPath: string;
|
|
58
|
-
/**
|
|
59
|
-
* Allow arbitrary keys/values
|
|
60
|
-
*/
|
|
61
|
-
[key: string]: unknown;
|
|
62
|
-
}
|
|
63
|
-
export interface VerifiedFetchPlugin {
|
|
64
|
-
readonly id: string;
|
|
65
|
-
readonly codes: number[];
|
|
66
|
-
readonly log: Logger;
|
|
67
|
-
canHandle(context: PluginContext): boolean;
|
|
68
|
-
handle(context: PluginContext): Promise<Response | null>;
|
|
69
|
-
}
|
|
70
|
-
export interface VerifiedFetchPluginFactory {
|
|
71
|
-
(options: PluginOptions): VerifiedFetchPlugin;
|
|
72
|
-
}
|
|
73
|
-
export interface PluginErrorOptions {
|
|
74
|
-
fatal?: boolean;
|
|
75
|
-
details?: Record<string, any>;
|
|
76
|
-
response?: Response;
|
|
77
|
-
}
|
|
1
|
+
export {};
|
|
78
2
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/plugins/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/plugins/types.ts"],"names":[],"mappings":""}
|
|
@@ -1,22 +1,40 @@
|
|
|
1
1
|
import { CID } from 'multiformats/cid';
|
|
2
|
-
import
|
|
3
|
-
import type {
|
|
4
|
-
import type { ServerTiming } from './utils/server-timing.ts';
|
|
2
|
+
import { ServerTiming } from './utils/server-timing.ts';
|
|
3
|
+
import type { ResolveURLResult, URLResolver as URLResolverInterface } from './index.ts';
|
|
5
4
|
import type { DNSLink } from '@helia/dnslink';
|
|
6
5
|
import type { IPNSResolver } from '@helia/ipns';
|
|
7
|
-
import type { AbortOptions
|
|
6
|
+
import type { AbortOptions } from '@libp2p/interface';
|
|
7
|
+
import type { Helia } from 'helia';
|
|
8
|
+
import type { Blockstore } from 'interface-blockstore';
|
|
9
|
+
import type { UnixFSEntry } from 'ipfs-unixfs-exporter';
|
|
10
|
+
export interface WalkPathResult {
|
|
11
|
+
ipfsRoots: CID[];
|
|
12
|
+
terminalElement: UnixFSEntry;
|
|
13
|
+
blockstore: Blockstore;
|
|
14
|
+
}
|
|
8
15
|
export interface URLResolverComponents {
|
|
16
|
+
helia: Helia;
|
|
9
17
|
ipnsResolver: IPNSResolver;
|
|
10
18
|
dnsLink: DNSLink;
|
|
11
|
-
|
|
19
|
+
}
|
|
20
|
+
export interface URLResolverInit {
|
|
21
|
+
sessionCacheSize?: number;
|
|
22
|
+
sessionTTLms?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface ResolveURLOptions extends AbortOptions {
|
|
25
|
+
session?: boolean;
|
|
26
|
+
isRawBlockRequest?: boolean;
|
|
27
|
+
onlyIfCached?: boolean;
|
|
12
28
|
}
|
|
13
29
|
export declare class URLResolver implements URLResolverInterface {
|
|
14
30
|
private readonly components;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
resolveDNSLink
|
|
20
|
-
resolveIPNSName
|
|
31
|
+
private readonly blockstoreSessions;
|
|
32
|
+
constructor(components: URLResolverComponents, init?: URLResolverInit);
|
|
33
|
+
resolve(url: URL, serverTiming?: ServerTiming, options?: ResolveURLOptions): Promise<ResolveURLResult>;
|
|
34
|
+
private getBlockstore;
|
|
35
|
+
private resolveDNSLink;
|
|
36
|
+
private resolveIPNSName;
|
|
37
|
+
private resolveIPFSPath;
|
|
38
|
+
private walkPath;
|
|
21
39
|
}
|
|
22
40
|
//# sourceMappingURL=url-resolver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url-resolver.d.ts","sourceRoot":"","sources":["../../src/url-resolver.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"url-resolver.d.ts","sourceRoot":"","sources":["../../src/url-resolver.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAMtC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,YAAY,CAAA;AACvF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EAAE,KAAK,EAAqB,MAAM,OAAO,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAyBvD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,GAAG,EAAE,CAAA;IAChB,eAAe,EAAE,WAAW,CAAA;IAC5B,UAAU,EAAE,UAAU,CAAA;CACvB;AAiBD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,KAAK,CAAA;IACZ,YAAY,EAAE,YAAY,CAAA;IAC1B,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACrD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,qBAAa,WAAY,YAAW,oBAAoB;IACtD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuB;IAClD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqC;gBAE3D,UAAU,EAAE,qBAAqB,EAAE,IAAI,GAAE,eAAoB;IAYpE,OAAO,CAAE,GAAG,EAAE,GAAG,EAAE,YAAY,GAAE,YAAiC,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgBrI,OAAO,CAAC,aAAa;YAgBP,cAAc;YAiCd,eAAe;YAmBf,eAAe;YAWf,QAAQ;CAuDvB"}
|
package/dist/src/url-resolver.js
CHANGED
|
@@ -1,106 +1,184 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DoesNotExistError } from '@helia/unixfs/errors';
|
|
2
|
+
import * as dagCbor from '@ipld/dag-cbor';
|
|
3
|
+
import * as dagJson from '@ipld/dag-json';
|
|
4
|
+
import * as dagPb from '@ipld/dag-pb';
|
|
5
|
+
import { peerIdFromString } from '@libp2p/peer-id';
|
|
6
|
+
import { InvalidParametersError, walkPath } from 'ipfs-unixfs-exporter';
|
|
7
|
+
import toBuffer from 'it-to-buffer';
|
|
2
8
|
import { CID } from 'multiformats/cid';
|
|
3
|
-
import
|
|
4
|
-
|
|
9
|
+
import * as json from 'multiformats/codecs/json';
|
|
10
|
+
import * as raw from 'multiformats/codecs/raw';
|
|
11
|
+
import QuickLRU from 'quick-lru';
|
|
12
|
+
import { SESSION_CACHE_MAX_SIZE, SESSION_CACHE_TTL_MS, CODEC_CBOR, CODEC_IDENTITY } from "./constants.js";
|
|
13
|
+
import { resourceToSessionCacheKey } from "./utils/resource-to-cache-key.js";
|
|
14
|
+
import { ServerTiming } from "./utils/server-timing.js";
|
|
15
|
+
// 1 year in seconds for ipfs content
|
|
16
|
+
const IPFS_CONTENT_TTL = 29030400;
|
|
17
|
+
const ENTITY_CODECS = [
|
|
18
|
+
CODEC_CBOR,
|
|
19
|
+
json.code,
|
|
20
|
+
raw.code
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* These are supported by the UnixFS exporter
|
|
24
|
+
*/
|
|
25
|
+
const EXPORTABLE_CODECS = [
|
|
26
|
+
dagPb.code,
|
|
27
|
+
dagCbor.code,
|
|
28
|
+
dagJson.code,
|
|
29
|
+
raw.code
|
|
30
|
+
];
|
|
31
|
+
function basicEntry(type, cid, bytes) {
|
|
32
|
+
return {
|
|
33
|
+
name: cid.toString(),
|
|
34
|
+
path: cid.toString(),
|
|
35
|
+
depth: 0,
|
|
36
|
+
type,
|
|
37
|
+
node: bytes,
|
|
38
|
+
cid,
|
|
39
|
+
size: BigInt(bytes.byteLength),
|
|
40
|
+
content: async function* () {
|
|
41
|
+
yield bytes;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
5
45
|
export class URLResolver {
|
|
6
46
|
components;
|
|
7
|
-
|
|
47
|
+
blockstoreSessions;
|
|
48
|
+
constructor(components, init = {}) {
|
|
8
49
|
this.components = components;
|
|
50
|
+
this.blockstoreSessions = new QuickLRU({
|
|
51
|
+
maxSize: init.sessionCacheSize ?? SESSION_CACHE_MAX_SIZE,
|
|
52
|
+
maxAge: init.sessionTTLms ?? SESSION_CACHE_TTL_MS,
|
|
53
|
+
onEviction: (key, store) => {
|
|
54
|
+
store.close();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
9
57
|
}
|
|
10
|
-
async resolve(
|
|
11
|
-
if (
|
|
12
|
-
return this.
|
|
58
|
+
async resolve(url, serverTiming = new ServerTiming(), options = {}) {
|
|
59
|
+
if (url.protocol === 'ipfs:') {
|
|
60
|
+
return this.resolveIPFSPath(url, serverTiming, options);
|
|
13
61
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return this.resolveCIDResource(cid, {
|
|
17
|
-
url: new URL(`ipfs://${cid}`)
|
|
18
|
-
}, options);
|
|
62
|
+
if (url.protocol === 'ipns:') {
|
|
63
|
+
return this.resolveIPNSName(url, serverTiming, options);
|
|
19
64
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
async parseUrlString(urlString, options = {}) {
|
|
23
|
-
const result = parseURLString(urlString);
|
|
24
|
-
if (result.protocol === 'ipfs') {
|
|
25
|
-
const cid = CID.parse(result.cidOrPeerIdOrDnsLink);
|
|
26
|
-
return this.resolveCIDResource(cid, result, options);
|
|
27
|
-
}
|
|
28
|
-
if (result.protocol === 'ipns') {
|
|
29
|
-
// try to parse target as peer id
|
|
30
|
-
let peerId;
|
|
31
|
-
try {
|
|
32
|
-
peerId = peerIdFromString(result.cidOrPeerIdOrDnsLink);
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
// fall back to DNSLink (e.g. /ipns/example.com)
|
|
36
|
-
return this.resolveDNSLink(result.cidOrPeerIdOrDnsLink, result, options);
|
|
37
|
-
}
|
|
38
|
-
// parse multihash from string (e.g. /ipns/QmFoo...)
|
|
39
|
-
return this.resolveIPNSName(result.cidOrPeerIdOrDnsLink, peerId, result, options);
|
|
65
|
+
if (url.protocol === 'dnslink:') {
|
|
66
|
+
return this.resolveDNSLink(url, serverTiming, options);
|
|
40
67
|
}
|
|
41
|
-
throw new
|
|
68
|
+
throw new InvalidParametersError(`Invalid resource. Unsupported protocol in URL, must be ipfs:, ipns:, or dnslink: ${url}`);
|
|
42
69
|
}
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
return this.resolveIPNSName(cid.toString(), peerIdFromCID(cid), parsed, options);
|
|
70
|
+
getBlockstore(root, options = {}) {
|
|
71
|
+
if (options.session === false) {
|
|
72
|
+
return this.components.helia.blockstore;
|
|
47
73
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
ttl: 29030400, // 1 year for ipfs content
|
|
56
|
-
ipfsPath: `/ipfs/${cid}${parsed.url.pathname}`
|
|
57
|
-
};
|
|
74
|
+
const key = resourceToSessionCacheKey(root);
|
|
75
|
+
let session = this.blockstoreSessions.get(key);
|
|
76
|
+
if (session == null) {
|
|
77
|
+
session = this.components.helia.blockstore.createSession(root, options);
|
|
78
|
+
this.blockstoreSessions.set(key, session);
|
|
79
|
+
}
|
|
80
|
+
return session;
|
|
58
81
|
}
|
|
59
|
-
async resolveDNSLink(
|
|
60
|
-
const results = await
|
|
82
|
+
async resolveDNSLink(url, serverTiming, options) {
|
|
83
|
+
const results = await serverTiming.time('dnsLink.resolve', `Resolve DNSLink ${url.hostname}`, this.components.dnsLink.resolve(url.hostname, options));
|
|
61
84
|
const result = results?.[0];
|
|
62
85
|
if (result == null) {
|
|
63
|
-
throw new TypeError(`Invalid resource. Cannot resolve DNSLink from domain: ${
|
|
86
|
+
throw new TypeError(`Invalid resource. Cannot resolve DNSLink from domain: ${url.hostname}`);
|
|
64
87
|
}
|
|
65
88
|
// dnslink resolved to IPNS name
|
|
66
89
|
if (result.namespace === 'ipns') {
|
|
67
|
-
return this.resolveIPNSName(
|
|
90
|
+
return this.resolveIPNSName(url, serverTiming, options);
|
|
68
91
|
}
|
|
69
92
|
// dnslink resolved to CID
|
|
70
93
|
if (result.namespace !== 'ipfs') {
|
|
71
94
|
// @ts-expect-error result namespace should only be ipns or ipfs
|
|
72
95
|
throw new TypeError(`Invalid resource. Unexpected DNSLink namespace ${result.namespace} from domain: ${domain}`);
|
|
73
96
|
}
|
|
97
|
+
if (result.path != null && url.pathname !== '') {
|
|
98
|
+
// path conflict?
|
|
99
|
+
}
|
|
100
|
+
const ipfsUrl = new URL(`ipfs://${result.cid}/${url.pathname}`);
|
|
101
|
+
const ipfsResult = await this.resolveIPFSPath(ipfsUrl, serverTiming, options);
|
|
74
102
|
return {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
fragment: parsed.fragment,
|
|
79
|
-
// dnslink is mutable so return 'ipns' protocol so we do not include immutable in cache-control header
|
|
80
|
-
protocol: 'ipns',
|
|
81
|
-
ttl: result.answer.TTL,
|
|
82
|
-
query: parsed.query,
|
|
83
|
-
ipfsPath: `/ipns/${domain}${parsed.url.pathname}`
|
|
103
|
+
...ipfsResult,
|
|
104
|
+
url,
|
|
105
|
+
ttl: result.answer.TTL
|
|
84
106
|
};
|
|
85
107
|
}
|
|
86
|
-
async resolveIPNSName(
|
|
87
|
-
const
|
|
108
|
+
async resolveIPNSName(url, serverTiming, options) {
|
|
109
|
+
const peerId = peerIdFromString(url.hostname);
|
|
110
|
+
const result = await serverTiming.time('ipns.resolve', `Resolve IPNS name ${peerId}`, this.components.ipnsResolver.resolve(peerId, options));
|
|
111
|
+
if (result.path != null && url.pathname !== '') {
|
|
112
|
+
// path conflict?
|
|
113
|
+
}
|
|
114
|
+
const ipfsUrl = new URL(`ipfs://${result.cid}/${url.pathname}`);
|
|
115
|
+
const ipfsResult = await this.resolveIPFSPath(ipfsUrl, serverTiming, options);
|
|
88
116
|
return {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
path: concatPaths(...(result.path ?? '').split('/'), ...(parsed.path ?? [])),
|
|
92
|
-
query: parsed.query ?? {},
|
|
93
|
-
fragment: parsed.fragment ?? '',
|
|
94
|
-
protocol: 'ipns',
|
|
117
|
+
...ipfsResult,
|
|
118
|
+
url,
|
|
95
119
|
// IPNS ttl is in nanoseconds, convert to seconds
|
|
96
|
-
ttl: Number((result.record.ttl ?? 0n) / BigInt(1e9))
|
|
97
|
-
|
|
120
|
+
ttl: Number((result.record.ttl ?? 0n) / BigInt(1e9))
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
async resolveIPFSPath(url, serverTiming, options) {
|
|
124
|
+
const walkPathResult = await serverTiming.time('ipfs.resolve', '', this.walkPath(url, options));
|
|
125
|
+
return {
|
|
126
|
+
...walkPathResult,
|
|
127
|
+
url,
|
|
128
|
+
ttl: IPFS_CONTENT_TTL,
|
|
129
|
+
blockstore: walkPathResult.blockstore
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
async walkPath(url, options = {}) {
|
|
133
|
+
const cid = CID.parse(url.hostname);
|
|
134
|
+
const blockstore = this.getBlockstore(cid, options);
|
|
135
|
+
if (EXPORTABLE_CODECS.includes(cid.code)) {
|
|
136
|
+
const ipfsRoots = [];
|
|
137
|
+
let terminalElement;
|
|
138
|
+
const ipfsPath = toIPFSPath(url);
|
|
139
|
+
// @ts-expect-error offline is a helia option
|
|
140
|
+
for await (const entry of walkPath(ipfsPath, blockstore, {
|
|
141
|
+
...options,
|
|
142
|
+
offline: options.onlyIfCached === true,
|
|
143
|
+
extended: options.isRawBlockRequest !== true
|
|
144
|
+
})) {
|
|
145
|
+
ipfsRoots.push(entry.cid);
|
|
146
|
+
terminalElement = entry;
|
|
147
|
+
}
|
|
148
|
+
if (terminalElement == null) {
|
|
149
|
+
throw new DoesNotExistError('No terminal element found');
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
ipfsRoots,
|
|
153
|
+
terminalElement,
|
|
154
|
+
blockstore
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
let bytes;
|
|
158
|
+
if (cid.multihash.code === CODEC_IDENTITY) {
|
|
159
|
+
bytes = cid.multihash.digest;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
bytes = await toBuffer(blockstore.get(cid, options));
|
|
163
|
+
}
|
|
164
|
+
// entity codecs contain all the bytes for an entity in one block and no
|
|
165
|
+
// path walking outside of that block is possible
|
|
166
|
+
if (ENTITY_CODECS.includes(cid.code)) {
|
|
167
|
+
return {
|
|
168
|
+
ipfsRoots: [cid],
|
|
169
|
+
terminalElement: basicEntry('object', cid, bytes),
|
|
170
|
+
blockstore
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
// may be an unknown codec
|
|
174
|
+
return {
|
|
175
|
+
ipfsRoots: [cid],
|
|
176
|
+
terminalElement: basicEntry('raw', cid, bytes),
|
|
177
|
+
blockstore
|
|
98
178
|
};
|
|
99
179
|
}
|
|
100
180
|
}
|
|
101
|
-
function
|
|
102
|
-
|
|
103
|
-
return paths
|
|
104
|
-
.filter(p => p != null && p !== '');
|
|
181
|
+
function toIPFSPath(url) {
|
|
182
|
+
return `/ipfs/${url.hostname}${decodeURI(url.pathname)}`;
|
|
105
183
|
}
|
|
106
184
|
//# sourceMappingURL=url-resolver.js.map
|