@helia/verified-fetch 2.6.17 → 2.6.19
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/dist/index.min.js +49 -49
- package/dist/index.min.js.map +4 -4
- package/dist/src/index.d.ts +5 -5
- package/dist/src/index.js +5 -5
- package/dist/src/plugins/plugin-handle-dag-pb.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-dag-pb.js +7 -2
- package/dist/src/plugins/plugin-handle-dag-pb.js.map +1 -1
- package/dist/src/plugins/plugin-handle-dag-walk.d.ts.map +1 -1
- package/dist/src/plugins/plugin-handle-dag-walk.js +2 -2
- package/dist/src/plugins/plugin-handle-dag-walk.js.map +1 -1
- package/dist/src/types.d.ts +1 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/byte-range-context.d.ts.map +1 -1
- package/dist/src/utils/dir-index-html.d.ts.map +1 -1
- package/dist/src/utils/get-stream-from-async-iterable.js +1 -1
- package/dist/src/utils/get-stream-from-async-iterable.js.map +1 -1
- package/dist/src/utils/parse-url-string.d.ts +2 -2
- package/dist/src/utils/parse-url-string.d.ts.map +1 -1
- package/dist/src/utils/parse-url-string.js +17 -9
- package/dist/src/utils/parse-url-string.js.map +1 -1
- package/dist/src/utils/responses.d.ts +4 -4
- package/dist/src/utils/responses.d.ts.map +1 -1
- package/dist/src/utils/responses.js +3 -3
- package/dist/src/utils/tlru.d.ts.map +1 -1
- package/dist/src/utils/tlru.js +7 -13
- package/dist/src/utils/tlru.js.map +1 -1
- package/dist/src/utils/walk-path.d.ts.map +1 -1
- package/dist/src/utils/walk-path.js +4 -1
- package/dist/src/utils/walk-path.js.map +1 -1
- package/dist/src/verified-fetch.d.ts.map +1 -1
- package/dist/src/verified-fetch.js +12 -7
- package/dist/src/verified-fetch.js.map +1 -1
- package/dist/typedoc-urls.json +5 -3
- package/package.json +14 -22
- package/src/index.ts +5 -5
- package/src/plugins/plugin-handle-dag-pb.ts +7 -2
- package/src/plugins/plugin-handle-dag-walk.ts +3 -2
- package/src/types.ts +1 -1
- package/src/utils/dir-index-html.ts +2 -2
- package/src/utils/get-stream-from-async-iterable.ts +1 -1
- package/src/utils/libp2p-defaults.browser.ts +1 -1
- package/src/utils/libp2p-defaults.ts +1 -1
- package/src/utils/parse-url-string.ts +15 -9
- package/src/utils/responses.ts +4 -4
- package/src/utils/tlru.ts +8 -18
- package/src/utils/walk-path.ts +5 -1
- package/src/verified-fetch.ts +13 -8
package/dist/typedoc-urls.json
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
+
"start": "https://ipfs.github.io/helia-verified-fetch/functions/index.verifiedFetch.start.html",
|
|
3
|
+
"stop": "https://ipfs.github.io/helia-verified-fetch/functions/index.verifiedFetch.stop.html",
|
|
2
4
|
"BasePlugin": "https://ipfs.github.io/helia-verified-fetch/classes/index.BasePlugin.html",
|
|
3
5
|
"DirIndexHtmlPlugin": "https://ipfs.github.io/helia-verified-fetch/classes/index.DirIndexHtmlPlugin.html",
|
|
4
6
|
"PluginError": "https://ipfs.github.io/helia-verified-fetch/classes/index.PluginError.html",
|
|
@@ -27,8 +29,8 @@
|
|
|
27
29
|
".:Resource": "https://ipfs.github.io/helia-verified-fetch/types/index.Resource.html",
|
|
28
30
|
"VerifiedFetchProgressEvents": "https://ipfs.github.io/helia-verified-fetch/types/index.VerifiedFetchProgressEvents.html",
|
|
29
31
|
".:VerifiedFetchProgressEvents": "https://ipfs.github.io/helia-verified-fetch/types/index.VerifiedFetchProgressEvents.html",
|
|
32
|
+
"dirIndexHtmlPluginFactory": "https://ipfs.github.io/helia-verified-fetch/variables/index.dirIndexHtmlPluginFactory.html",
|
|
33
|
+
"verifiedFetch": "https://ipfs.github.io/helia-verified-fetch/variables/index.verifiedFetch.html",
|
|
30
34
|
"createVerifiedFetch": "https://ipfs.github.io/helia-verified-fetch/functions/index.createVerifiedFetch.html",
|
|
31
|
-
".:createVerifiedFetch": "https://ipfs.github.io/helia-verified-fetch/functions/index.createVerifiedFetch.html"
|
|
32
|
-
"dirIndexHtmlPluginFactory": "https://ipfs.github.io/helia-verified-fetch/functions/index.dirIndexHtmlPluginFactory.html",
|
|
33
|
-
"verifiedFetch": "https://ipfs.github.io/helia-verified-fetch/functions/index.verifiedFetch-1.html"
|
|
35
|
+
".:createVerifiedFetch": "https://ipfs.github.io/helia-verified-fetch/functions/index.createVerifiedFetch.html"
|
|
34
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helia/verified-fetch",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.19",
|
|
4
4
|
"description": "A fetch-like API for obtaining verified & trustless IPFS content on the web",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"homepage": "https://github.com/ipfs/helia-verified-fetch/tree/main/packages/verified-fetch#readme",
|
|
@@ -54,13 +54,6 @@
|
|
|
54
54
|
"import": "./dist/src/plugins/plugins.js"
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
|
-
"eslintConfig": {
|
|
58
|
-
"extends": "ipfs",
|
|
59
|
-
"parserOptions": {
|
|
60
|
-
"project": true,
|
|
61
|
-
"sourceType": "module"
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
57
|
"release": {
|
|
65
58
|
"branches": [
|
|
66
59
|
"main"
|
|
@@ -180,17 +173,16 @@
|
|
|
180
173
|
"@helia/unixfs": "^5.0.2",
|
|
181
174
|
"@ipld/dag-cbor": "^9.2.3",
|
|
182
175
|
"@ipld/dag-json": "^10.2.4",
|
|
183
|
-
"@ipld/dag-pb": "^4.1.
|
|
184
|
-
"@libp2p/interface": "^2.
|
|
185
|
-
"@libp2p/kad-dht": "^15.
|
|
186
|
-
"@libp2p/logger": "^5.1.
|
|
187
|
-
"@libp2p/peer-id": "^5.1.
|
|
188
|
-
"@libp2p/webrtc": "^5.2.
|
|
189
|
-
"@libp2p/websockets": "^9.2.
|
|
176
|
+
"@ipld/dag-pb": "^4.1.5",
|
|
177
|
+
"@libp2p/interface": "^2.10.1",
|
|
178
|
+
"@libp2p/kad-dht": "^15.1.1",
|
|
179
|
+
"@libp2p/logger": "^5.1.17",
|
|
180
|
+
"@libp2p/peer-id": "^5.1.4",
|
|
181
|
+
"@libp2p/webrtc": "^5.2.14",
|
|
182
|
+
"@libp2p/websockets": "^9.2.12",
|
|
190
183
|
"@multiformats/dns": "^1.0.6",
|
|
191
|
-
"cborg": "^4.2.
|
|
184
|
+
"cborg": "^4.2.11",
|
|
192
185
|
"file-type": "^20.5.0",
|
|
193
|
-
"hashlru": "^2.3.0",
|
|
194
186
|
"helia": "^5.4.1",
|
|
195
187
|
"interface-blockstore": "^5.3.1",
|
|
196
188
|
"interface-datastore": "^8.3.1",
|
|
@@ -200,10 +192,10 @@
|
|
|
200
192
|
"it-pipe": "^3.0.1",
|
|
201
193
|
"it-tar": "^6.0.5",
|
|
202
194
|
"it-to-browser-readablestream": "^2.0.11",
|
|
203
|
-
"libp2p": "^2.8.
|
|
204
|
-
"
|
|
205
|
-
"multiformats": "^13.3.3",
|
|
195
|
+
"libp2p": "^2.8.7",
|
|
196
|
+
"multiformats": "^13.3.6",
|
|
206
197
|
"progress-events": "^1.0.1",
|
|
198
|
+
"quick-lru": "^7.0.1",
|
|
207
199
|
"uint8arrays": "^5.1.0"
|
|
208
200
|
},
|
|
209
201
|
"devDependencies": {
|
|
@@ -212,9 +204,9 @@
|
|
|
212
204
|
"@helia/http": "^2.1.1",
|
|
213
205
|
"@helia/json": "^4.0.5",
|
|
214
206
|
"@ipld/car": "^5.4.1",
|
|
215
|
-
"@libp2p/crypto": "^5.1.
|
|
207
|
+
"@libp2p/crypto": "^5.1.3",
|
|
216
208
|
"@types/sinon": "^17.0.4",
|
|
217
|
-
"aegir": "^
|
|
209
|
+
"aegir": "^47.0.11",
|
|
218
210
|
"blockstore-core": "^5.0.2",
|
|
219
211
|
"browser-readablestream-to-it": "^2.0.9",
|
|
220
212
|
"datastore-core": "^10.0.2",
|
package/src/index.ts
CHANGED
|
@@ -591,8 +591,8 @@
|
|
|
591
591
|
*
|
|
592
592
|
* #### Unsupported response types
|
|
593
593
|
*
|
|
594
|
-
*
|
|
595
|
-
*
|
|
594
|
+
* - Returning IPLD nodes or DAGs as JS objects is not supported, as there is no currently well-defined structure for representing this data in an [HTTP Response](https://developer.mozilla.org/en-US/docs/Web/API/Response). Instead, users should request `aplication/vnd.ipld.car` or use the [`helia`](https://github.com/ipfs/helia) library directly for this use case.
|
|
595
|
+
* - Others? Open an issue or PR!
|
|
596
596
|
*
|
|
597
597
|
* ### Response headers
|
|
598
598
|
*
|
|
@@ -600,9 +600,9 @@
|
|
|
600
600
|
*
|
|
601
601
|
* Some known header specifications:
|
|
602
602
|
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
605
|
-
*
|
|
603
|
+
* - https://specs.ipfs.tech/http-gateways/path-gateway/#response-headers
|
|
604
|
+
* - https://specs.ipfs.tech/http-gateways/trustless-gateway/#response-headers
|
|
605
|
+
* - https://specs.ipfs.tech/http-gateways/subdomain-gateway/#response-headers
|
|
606
606
|
*
|
|
607
607
|
* #### Server Timing headers
|
|
608
608
|
*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { unixfs } from '@helia/unixfs'
|
|
2
2
|
import { code as dagPbCode } from '@ipld/dag-pb'
|
|
3
|
+
import { AbortError } from '@libp2p/interface'
|
|
3
4
|
import { exporter } from 'ipfs-unixfs-exporter'
|
|
4
5
|
import { CustomProgressEvent } from 'progress-events'
|
|
5
6
|
import { getStreamFromAsyncIterable } from '../utils/get-stream-from-async-iterable.js'
|
|
@@ -98,8 +99,10 @@ export class DagPbPlugin extends BasePlugin {
|
|
|
98
99
|
path = rootFilePath
|
|
99
100
|
resolvedCID = entry.cid
|
|
100
101
|
} catch (err: any) {
|
|
102
|
+
if (options?.signal?.aborted) {
|
|
103
|
+
throw new AbortError(options?.signal?.reason)
|
|
104
|
+
}
|
|
101
105
|
this.log.error('error loading path %c/%s', dirCid, rootFilePath, err)
|
|
102
|
-
options?.signal?.throwIfAborted()
|
|
103
106
|
context.isDirectory = true
|
|
104
107
|
context.directoryEntries = []
|
|
105
108
|
context.modified++
|
|
@@ -161,7 +164,9 @@ export class DagPbPlugin extends BasePlugin {
|
|
|
161
164
|
|
|
162
165
|
return response
|
|
163
166
|
} catch (err: any) {
|
|
164
|
-
options?.signal?.
|
|
167
|
+
if (options?.signal?.aborted) {
|
|
168
|
+
throw new AbortError(options?.signal?.reason)
|
|
169
|
+
}
|
|
165
170
|
log.error('error streaming %c/%s', cid, path, err)
|
|
166
171
|
if (byteRangeContext.isRangeRequest && err.code === 'ERR_INVALID_PARAMS') {
|
|
167
172
|
return badRangeResponse(resource)
|
|
@@ -29,10 +29,10 @@ export class DagWalkPlugin extends BasePlugin {
|
|
|
29
29
|
const { cid, resource, options, withServerTiming = false } = context
|
|
30
30
|
const { getBlockstore, handleServerTiming } = this.pluginOptions
|
|
31
31
|
const blockstore = getBlockstore(cid, resource, options?.session ?? true, options)
|
|
32
|
+
|
|
32
33
|
// TODO: migrate handlePathWalking into this plugin
|
|
33
34
|
const pathDetails = await handleServerTiming('path-walking', '', async () => handlePathWalking({ ...context, blockstore, log: this.log }), withServerTiming)
|
|
34
35
|
|
|
35
|
-
context.modified++
|
|
36
36
|
if (pathDetails instanceof Response) {
|
|
37
37
|
this.log.trace('path walking failed')
|
|
38
38
|
|
|
@@ -42,10 +42,11 @@ export class DagWalkPlugin extends BasePlugin {
|
|
|
42
42
|
return pathDetails
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// some error walking the path
|
|
45
|
+
// some other error walking the path (codec doesn't support pathing, etc..), let the next plugin try to handle it
|
|
46
46
|
return null
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
context.modified++
|
|
49
50
|
context.pathDetails = pathDetails
|
|
50
51
|
|
|
51
52
|
return null
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type RequestFormatShorthand = 'raw' | 'car' | 'tar' | 'ipns-record' | 'dag-json' | 'dag-cbor' | 'json' | 'cbor'
|
|
2
2
|
|
|
3
|
-
export type SupportedBodyTypes = string | ArrayBuffer | Blob | ReadableStream<Uint8Array> | null
|
|
3
|
+
export type SupportedBodyTypes = string | Uint8Array | ArrayBuffer | Blob | ReadableStream<Uint8Array> | null
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* A ContentTypeParser attempts to return the mime type of a given file. It
|
|
@@ -4,8 +4,8 @@ import type { UnixFSEntry } from 'ipfs-unixfs-exporter'
|
|
|
4
4
|
/**
|
|
5
5
|
* Types taken from:
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* - https://github.com/ipfs/boxo/blob/09b0013e1c3e09468009b02dfc9b2b9041199d5d/gateway/assets/assets.go#L92C1-L96C2
|
|
8
|
+
* - https://github.com/ipfs/boxo/blob/09b0013e1c3e09468009b02dfc9b2b9041199d5d/gateway/assets/assets.go#L114C1-L135C2
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
interface GlobalData {
|
|
@@ -25,7 +25,7 @@ export async function getStreamFromAsyncIterable (iterator: AsyncIterable<Uint8A
|
|
|
25
25
|
},
|
|
26
26
|
async pull (controller) {
|
|
27
27
|
const { value, done } = await reader.next()
|
|
28
|
-
if (options?.signal?.aborted
|
|
28
|
+
if (options?.signal?.aborted) {
|
|
29
29
|
controller.error(new AbortError(options.signal.reason ?? 'signal aborted by user'))
|
|
30
30
|
controller.close()
|
|
31
31
|
return
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { webRTCDirect } from '@libp2p/webrtc'
|
|
2
2
|
import { webSockets } from '@libp2p/websockets'
|
|
3
3
|
import { libp2pDefaults } from 'helia'
|
|
4
|
-
import type { ServiceFactoryMap } from './libp2p-types'
|
|
4
|
+
import type { ServiceFactoryMap } from './libp2p-types.js'
|
|
5
5
|
import type { DefaultLibp2pServices } from 'helia'
|
|
6
6
|
import type { Libp2pOptions } from 'libp2p'
|
|
7
7
|
|
|
@@ -2,7 +2,7 @@ import { kadDHT } from '@libp2p/kad-dht'
|
|
|
2
2
|
import { libp2pDefaults } from 'helia'
|
|
3
3
|
import { ipnsSelector } from 'ipns/selector'
|
|
4
4
|
import { ipnsValidator } from 'ipns/validator'
|
|
5
|
-
import type { ServiceFactoryMap } from './libp2p-types'
|
|
5
|
+
import type { ServiceFactoryMap } from './libp2p-types.js'
|
|
6
6
|
import type { DefaultLibp2pServices } from 'helia'
|
|
7
7
|
import type { Libp2pOptions } from 'libp2p'
|
|
8
8
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AbortError } from '@libp2p/interface'
|
|
1
2
|
import { CID } from 'multiformats/cid'
|
|
2
3
|
import { getPeerIdFromString } from './get-peer-id-from-string.js'
|
|
3
4
|
import { serverTiming } from './server-timing.js'
|
|
@@ -65,9 +66,9 @@ interface MatchUrlGroups {
|
|
|
65
66
|
|
|
66
67
|
function matchUrlGroupsGuard (groups?: null | { [key in string]: string; } | MatchUrlGroups): groups is MatchUrlGroups {
|
|
67
68
|
const protocol = groups?.protocol
|
|
68
|
-
if (protocol == null) return false
|
|
69
|
+
if (protocol == null) { return false }
|
|
69
70
|
const cidOrPeerIdOrDnsLink = groups?.cidOrPeerIdOrDnsLink
|
|
70
|
-
if (cidOrPeerIdOrDnsLink == null) return false
|
|
71
|
+
if (cidOrPeerIdOrDnsLink == null) { return false }
|
|
71
72
|
const path = groups?.path
|
|
72
73
|
const queryString = groups?.queryString
|
|
73
74
|
|
|
@@ -132,8 +133,8 @@ function isInlinedDnsLink (label: string): boolean {
|
|
|
132
133
|
|
|
133
134
|
/**
|
|
134
135
|
* DNSLink label decoding
|
|
135
|
-
*
|
|
136
|
-
*
|
|
136
|
+
* - Every standalone - is replaced with .
|
|
137
|
+
* - Every remaining -- is replaced with -
|
|
137
138
|
*
|
|
138
139
|
* @example en-wikipedia--on--ipfs-org.ipns.example.net -> example.net/ipns/en.wikipedia-on-ipfs.org
|
|
139
140
|
*/
|
|
@@ -145,8 +146,8 @@ function dnsLinkLabelDecoder (linkLabel: string): string {
|
|
|
145
146
|
* A function that parses ipfs:// and ipns:// URLs, returning an object with easily recognizable properties.
|
|
146
147
|
*
|
|
147
148
|
* After determining the protocol successfully, we process the cidOrPeerIdOrDnsLink:
|
|
148
|
-
*
|
|
149
|
-
*
|
|
149
|
+
* - If it's ipfs, it parses the CID or throws Error[]
|
|
150
|
+
* - If it's ipns, it attempts to resolve the PeerId and then the DNSLink. If both fail, Error[] is thrown.
|
|
150
151
|
*
|
|
151
152
|
* @todo we need to break out each step of this function (cid parsing, ipns resolving, dnslink resolving) into separate functions and then remove the eslint-disable comment
|
|
152
153
|
*
|
|
@@ -212,7 +213,9 @@ export async function parseUrlString ({ urlString, ipns, logger, withServerTimin
|
|
|
212
213
|
resolvedPath = resolveResult?.path
|
|
213
214
|
log.trace('resolved %s to %c', cidOrPeerIdOrDnsLink, cid)
|
|
214
215
|
} catch (err) {
|
|
215
|
-
options?.signal?.
|
|
216
|
+
if (options?.signal?.aborted) {
|
|
217
|
+
throw new AbortError(options?.signal?.reason)
|
|
218
|
+
}
|
|
216
219
|
if (peerId == null) {
|
|
217
220
|
log.error('could not parse PeerId string "%s"', cidOrPeerIdOrDnsLink, err)
|
|
218
221
|
errors.push(new TypeError(`Could not parse PeerId in ipns url "${cidOrPeerIdOrDnsLink}", ${(err as Error).message}`))
|
|
@@ -249,7 +252,10 @@ export async function parseUrlString ({ urlString, ipns, logger, withServerTimin
|
|
|
249
252
|
resolvedPath = resolveResult?.path
|
|
250
253
|
log.trace('resolved %s to %c', decodedDnsLinkLabel, cid)
|
|
251
254
|
} catch (err: any) {
|
|
252
|
-
|
|
255
|
+
// eslint-disable-next-line max-depth
|
|
256
|
+
if (options?.signal?.aborted) {
|
|
257
|
+
throw new AbortError(options?.signal?.reason)
|
|
258
|
+
}
|
|
253
259
|
log.error('could not resolve DnsLink for "%s"', cidOrPeerIdOrDnsLink, err)
|
|
254
260
|
errors.push(err)
|
|
255
261
|
}
|
|
@@ -264,7 +270,7 @@ export async function parseUrlString ({ urlString, ipns, logger, withServerTimin
|
|
|
264
270
|
|
|
265
271
|
errors.push(new Error(`Invalid resource. Cannot determine CID from URL "${urlString}".`))
|
|
266
272
|
|
|
267
|
-
// eslint-disable-next-line @typescript-eslint/
|
|
273
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
268
274
|
throw errors
|
|
269
275
|
}
|
|
270
276
|
|
package/src/utils/responses.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ByteRangeContext } from './byte-range-context'
|
|
1
|
+
import type { ByteRangeContext } from './byte-range-context.js'
|
|
2
2
|
import type { SupportedBodyTypes } from '../types.js'
|
|
3
3
|
import type { Logger } from '@libp2p/interface'
|
|
4
4
|
|
|
@@ -193,9 +193,9 @@ export function okRangeResponse (url: string, body: SupportedBodyTypes, { byteRa
|
|
|
193
193
|
|
|
194
194
|
/**
|
|
195
195
|
* We likely need to catch errors handled by upstream helia libraries if range-request throws an error. Some examples:
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
196
|
+
* - The range is out of bounds
|
|
197
|
+
* - The range is invalid
|
|
198
|
+
* - The range is not supported for the given type
|
|
199
199
|
*/
|
|
200
200
|
export function badRangeResponse (url: string, body?: SupportedBodyTypes, init?: ResponseInit): Response {
|
|
201
201
|
const response = new Response(body, {
|
package/src/utils/tlru.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import QuickLRU from 'quick-lru'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Time Aware Least Recent Used Cache
|
|
@@ -6,30 +6,20 @@ import hashlru from 'hashlru'
|
|
|
6
6
|
* @see https://arxiv.org/pdf/1801.00390
|
|
7
7
|
*/
|
|
8
8
|
export class TLRU<T> {
|
|
9
|
-
private readonly lru:
|
|
9
|
+
private readonly lru: QuickLRU<string, T>
|
|
10
10
|
|
|
11
11
|
constructor (maxSize: number) {
|
|
12
|
-
this.lru =
|
|
12
|
+
this.lru = new QuickLRU({ maxSize })
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
get (key: string): T | undefined {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (value != null) {
|
|
19
|
-
if (value.expire != null && value.expire < Date.now()) {
|
|
20
|
-
this.lru.remove(key)
|
|
21
|
-
|
|
22
|
-
return undefined
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return value.value
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return undefined
|
|
16
|
+
return this.lru.get(key)
|
|
29
17
|
}
|
|
30
18
|
|
|
31
19
|
set (key: string, value: T, ttlMs: number): void {
|
|
32
|
-
this.lru.set(key,
|
|
20
|
+
this.lru.set(key, value, {
|
|
21
|
+
maxAge: Date.now() + ttlMs
|
|
22
|
+
})
|
|
33
23
|
}
|
|
34
24
|
|
|
35
25
|
has (key: string): boolean {
|
|
@@ -43,7 +33,7 @@ export class TLRU<T> {
|
|
|
43
33
|
}
|
|
44
34
|
|
|
45
35
|
remove (key: string): void {
|
|
46
|
-
this.lru.
|
|
36
|
+
this.lru.delete(key)
|
|
47
37
|
}
|
|
48
38
|
|
|
49
39
|
clear (): void {
|
package/src/utils/walk-path.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DoesNotExistError } from '@helia/unixfs/errors'
|
|
2
|
+
import { AbortError } from '@libp2p/interface'
|
|
2
3
|
import { walkPath as exporterWalk } from 'ipfs-unixfs-exporter'
|
|
3
4
|
import { badGatewayResponse, notFoundResponse } from './responses.js'
|
|
4
5
|
import type { PluginContext } from '../plugins/types.js'
|
|
@@ -53,7 +54,10 @@ export async function handlePathWalking ({ cid, path, resource, options, blockst
|
|
|
53
54
|
try {
|
|
54
55
|
return await walkPath(blockstore, `${cid.toString()}/${path}`, options)
|
|
55
56
|
} catch (err: any) {
|
|
56
|
-
options?.signal?.
|
|
57
|
+
if (options?.signal?.aborted) {
|
|
58
|
+
throw new AbortError(options?.signal?.reason)
|
|
59
|
+
}
|
|
60
|
+
|
|
57
61
|
if (['ERR_NO_PROP', 'ERR_NO_TERMINAL_ELEMENT', 'ERR_NOT_FOUND'].includes(err.code)) {
|
|
58
62
|
return notFoundResponse(resource)
|
|
59
63
|
}
|
package/src/verified-fetch.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { ipns as heliaIpns } from '@helia/ipns'
|
|
2
|
+
import { AbortError } from '@libp2p/interface'
|
|
2
3
|
import { prefixLogger } from '@libp2p/logger'
|
|
3
|
-
import { LRUCache } from 'lru-cache'
|
|
4
4
|
import { CustomProgressEvent } from 'progress-events'
|
|
5
|
+
import QuickLRU from 'quick-lru'
|
|
5
6
|
import { ByteRangeContextPlugin } from './plugins/plugin-handle-byte-range-context.js'
|
|
6
7
|
import { CarPlugin } from './plugins/plugin-handle-car.js'
|
|
7
8
|
import { DagCborPlugin } from './plugins/plugin-handle-dag-cbor.js'
|
|
@@ -62,7 +63,7 @@ export class VerifiedFetch {
|
|
|
62
63
|
private readonly ipns: IPNS
|
|
63
64
|
private readonly log: Logger
|
|
64
65
|
private readonly contentTypeParser: ContentTypeParser | undefined
|
|
65
|
-
private readonly blockstoreSessions:
|
|
66
|
+
private readonly blockstoreSessions: QuickLRU<string, SessionBlockstore>
|
|
66
67
|
private serverTimingHeaders: string[] = []
|
|
67
68
|
private readonly withServerTiming: boolean
|
|
68
69
|
private readonly plugins: VerifiedFetchPlugin[] = []
|
|
@@ -72,10 +73,10 @@ export class VerifiedFetch {
|
|
|
72
73
|
this.log = helia.logger.forComponent('helia:verified-fetch')
|
|
73
74
|
this.ipns = ipns ?? heliaIpns(helia)
|
|
74
75
|
this.contentTypeParser = init?.contentTypeParser ?? contentTypeParser
|
|
75
|
-
this.blockstoreSessions = new
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
this.blockstoreSessions = new QuickLRU({
|
|
77
|
+
maxSize: init?.sessionCacheSize ?? SESSION_CACHE_MAX_SIZE,
|
|
78
|
+
maxAge: init?.sessionTTLms ?? SESSION_CACHE_TTL_MS,
|
|
79
|
+
onEviction: (key, store) => {
|
|
79
80
|
store.close()
|
|
80
81
|
}
|
|
81
82
|
})
|
|
@@ -282,7 +283,9 @@ export class VerifiedFetch {
|
|
|
282
283
|
break
|
|
283
284
|
}
|
|
284
285
|
} catch (err: any) {
|
|
285
|
-
context.options?.signal?.
|
|
286
|
+
if (context.options?.signal?.aborted) {
|
|
287
|
+
throw new AbortError(context.options?.signal?.reason)
|
|
288
|
+
}
|
|
286
289
|
this.log.error('Error in plugin:', plugin.constructor.name, err)
|
|
287
290
|
// if fatal, short-circuit the pipeline
|
|
288
291
|
if (err.name === 'PluginFatalError') {
|
|
@@ -341,7 +344,9 @@ export class VerifiedFetch {
|
|
|
341
344
|
parsedResult = await this.handleServerTiming('parse-resource', '', async () => parseResource(resource, { ipns: this.ipns, logger: this.helia.logger }, { withServerTiming, ...options }), withServerTiming)
|
|
342
345
|
this.serverTimingHeaders.push(...parsedResult.serverTimings.map(({ header }) => header))
|
|
343
346
|
} catch (err: any) {
|
|
344
|
-
options?.signal?.
|
|
347
|
+
if (options?.signal?.aborted) {
|
|
348
|
+
throw new AbortError(options?.signal?.reason)
|
|
349
|
+
}
|
|
345
350
|
this.log.error('error parsing resource %s', resource, err)
|
|
346
351
|
|
|
347
352
|
return this.handleFinalResponse(badRequestResponse(resource.toString(), err))
|