@helia/verified-fetch 7.2.16 → 8.0.1

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.
Files changed (45) hide show
  1. package/README.md +8 -12
  2. package/dist/index.min.js +70 -59
  3. package/dist/index.min.js.map +4 -4
  4. package/dist/src/index.d.ts +18 -16
  5. package/dist/src/index.d.ts.map +1 -1
  6. package/dist/src/index.js +36 -34
  7. package/dist/src/index.js.map +1 -1
  8. package/dist/src/plugins/plugin-handle-ipns-record.d.ts.map +1 -1
  9. package/dist/src/plugins/plugin-handle-ipns-record.js +20 -6
  10. package/dist/src/plugins/plugin-handle-ipns-record.js.map +1 -1
  11. package/dist/src/plugins/plugin-handle-unixfs.d.ts.map +1 -1
  12. package/dist/src/plugins/plugin-handle-unixfs.js +7 -4
  13. package/dist/src/plugins/plugin-handle-unixfs.js.map +1 -1
  14. package/dist/src/url-resolver.d.ts.map +1 -1
  15. package/dist/src/url-resolver.js +27 -9
  16. package/dist/src/url-resolver.js.map +1 -1
  17. package/dist/src/utils/abbreviate.d.ts.map +1 -1
  18. package/dist/src/utils/abbreviate.js +1 -0
  19. package/dist/src/utils/abbreviate.js.map +1 -1
  20. package/dist/src/utils/convert-output.d.ts +2 -2
  21. package/dist/src/utils/convert-output.d.ts.map +1 -1
  22. package/dist/src/utils/convert-output.js +11 -5
  23. package/dist/src/utils/convert-output.js.map +1 -1
  24. package/dist/src/utils/ipfs-path-to-cid.d.ts +6 -0
  25. package/dist/src/utils/ipfs-path-to-cid.d.ts.map +1 -0
  26. package/dist/src/utils/ipfs-path-to-cid.js +9 -0
  27. package/dist/src/utils/ipfs-path-to-cid.js.map +1 -0
  28. package/dist/src/utils/libp2p-defaults.browser.js +1 -1
  29. package/dist/src/utils/libp2p-defaults.browser.js.map +1 -1
  30. package/dist/src/utils/libp2p-defaults.d.ts.map +1 -1
  31. package/dist/src/utils/libp2p-defaults.js +2 -10
  32. package/dist/src/utils/libp2p-defaults.js.map +1 -1
  33. package/dist/src/verified-fetch.js +6 -6
  34. package/dist/src/verified-fetch.js.map +1 -1
  35. package/package.json +28 -31
  36. package/src/index.ts +51 -40
  37. package/src/plugins/plugin-handle-ipns-record.ts +25 -6
  38. package/src/plugins/plugin-handle-unixfs.ts +7 -4
  39. package/src/url-resolver.ts +35 -9
  40. package/src/utils/abbreviate.ts +1 -0
  41. package/src/utils/convert-output.ts +13 -7
  42. package/src/utils/ipfs-path-to-cid.ts +9 -0
  43. package/src/utils/libp2p-defaults.browser.ts +2 -2
  44. package/src/utils/libp2p-defaults.ts +3 -11
  45. package/src/verified-fetch.ts +6 -6
@@ -1,13 +1,17 @@
1
1
  import { DoesNotExistError } from '@helia/unixfs/errors'
2
- import { peerIdFromCID, peerIdFromString } from '@libp2p/peer-id'
2
+ import * as cborg from 'cborg'
3
3
  import { exporter, InvalidParametersError, walkPath } from 'ipfs-unixfs-exporter'
4
+ import last from 'it-last'
4
5
  import toBuffer from 'it-to-buffer'
6
+ import { base58btc } from 'multiformats/bases/base58'
5
7
  import { CID } from 'multiformats/cid'
8
+ import * as Digest from 'multiformats/hashes/digest'
6
9
  import QuickLRU from 'quick-lru'
7
10
  import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
8
11
  import { CODEC_LIBP2P_KEY, SESSION_CACHE_MAX_SIZE, SESSION_CACHE_TTL_MS } from './constants.ts'
9
12
  import { abbreviate } from './utils/abbreviate.ts'
10
13
  import { applyRedirects } from './utils/apply-redirect.ts'
14
+ import { splitIPNSName } from './utils/ipfs-path-to-cid.ts'
11
15
  import { ServerTiming } from './utils/server-timing.ts'
12
16
  import type { ResolveURLOptions, ResolveURLResult, URLResolver as URLResolverInterface } from './index.ts'
13
17
  import type { DNSLink } from '@helia/dnslink'
@@ -16,6 +20,7 @@ import type { AbortOptions, Logger } from '@libp2p/interface'
16
20
  import type { Helia, ProviderOptions, SessionBlockstore } from 'helia'
17
21
  import type { Blockstore } from 'interface-blockstore'
18
22
  import type { PathEntry, UnixFSEntry } from 'ipfs-unixfs-exporter'
23
+ import type { MultihashDigest } from 'multiformats/cid'
19
24
  import type { ProgressOptions } from 'progress-events'
20
25
 
21
26
  // 1 year in seconds for ipfs content
@@ -137,7 +142,7 @@ export class URLResolver implements URLResolverInterface {
137
142
 
138
143
  if (result.namespace === 'ipns') {
139
144
  // dnslink resolved to IPNS name
140
- const ipnsUrl = new URL(`ipns://${result.peerId}${path}`)
145
+ const ipnsUrl = new URL(`ipns://${base58btc.baseEncode(result.value.bytes)}${path}`)
141
146
  resolveResult = await this.resolveIPNSName(ipnsUrl, serverTiming, options)
142
147
  } else if (result.namespace === 'ipfs') {
143
148
  // dnslink resolved to CID
@@ -161,11 +166,22 @@ export class URLResolver implements URLResolverInterface {
161
166
  }
162
167
 
163
168
  private async resolveIPNSName (url: URL, serverTiming: ServerTiming, options?: ResolveURLOptions): Promise<ResolveURLResult | Response> {
164
- const peerId = peerIdFromString(url.hostname)
165
- const result = await serverTiming.time(abbreviate('ipns.resolve'), '', this.components.ipnsResolver.resolve(peerId, options))
166
- const path = normalizePath(`${result.path ?? ''}/${url.pathname}`)
169
+ const multihash = parseMultihash(url.hostname)
170
+ const result = await serverTiming.time(abbreviate('ipns.resolve'), '', last(this.components.ipnsResolver.resolve(multihash, options)))
167
171
 
168
- const ipfsUrl = new URL(`ipfs://${result.cid}${path}`)
172
+ if (result == null) {
173
+ throw new InvalidParametersError('Could not resolve IPNS name')
174
+ }
175
+
176
+ const {
177
+ ns, name, path
178
+ } = splitIPNSName(result.value)
179
+
180
+ if (ns !== 'ipfs') {
181
+ throw new InvalidParametersError('IPNS name resolved to non-IPFS path')
182
+ }
183
+
184
+ const ipfsUrl = new URL(`ipfs://${name}${normalizePath(`${path ?? ''}/${url.pathname}`)}`)
169
185
  const ipfsResult = await this.resolveIPFSPath(ipfsUrl, serverTiming, options)
170
186
 
171
187
  if (ipfsResult instanceof Response) {
@@ -173,9 +189,11 @@ export class URLResolver implements URLResolverInterface {
173
189
  }
174
190
 
175
191
  let expires: Date | undefined
192
+ const data = cborg.decode(result.record.data ?? new Uint8Array(0))
176
193
 
177
- if (result.record.validityType === 'EOL') {
178
- expires = new Date(result.record.validity)
194
+ // 0 is EOL
195
+ if (data.ValidityType === 0 && data.Validity instanceof Uint8Array) {
196
+ expires = new Date(uint8ArrayToString(data.Validity))
179
197
  }
180
198
 
181
199
  return {
@@ -197,7 +215,7 @@ export class URLResolver implements URLResolverInterface {
197
215
 
198
216
  if (walkPathResult.terminalElement.cid.code === CODEC_LIBP2P_KEY) {
199
217
  // special case, peer id encoded as libp2p key CID - interpret as IPNS
200
- const ipnsUrl = new URL(`ipns://${peerIdFromCID(walkPathResult.terminalElement.cid)}`)
218
+ const ipnsUrl = new URL(`ipns://${base58btc.baseEncode(walkPathResult.terminalElement.cid.multihash.bytes)}`)
201
219
  const ipnsResult = await this.resolveIPNSName(ipnsUrl, serverTiming, options)
202
220
 
203
221
  if (ipnsResult instanceof Response) {
@@ -339,3 +357,11 @@ function normalizePath (path: string): string {
339
357
 
340
358
  return ''
341
359
  }
360
+
361
+ function parseMultihash (str: string): MultihashDigest {
362
+ if (str.startsWith('1') || str.startsWith('Q')) {
363
+ return Digest.decode(base58btc.baseDecode(str))
364
+ }
365
+
366
+ return CID.parse(str).multihash
367
+ }
@@ -13,6 +13,7 @@ const ABBREVIATIONS: Record<string, string> = {
13
13
 
14
14
  // routers
15
15
  'http-gateway-router': 'h',
16
+ 'fallback-router': 'h',
16
17
  'libp2p-router': 'l',
17
18
 
18
19
  // block brokers
@@ -4,12 +4,13 @@ import * as dagPb from '@ipld/dag-pb'
4
4
  import * as json from 'multiformats/codecs/json'
5
5
  import * as raw from 'multiformats/codecs/raw'
6
6
  import { identity } from 'multiformats/hashes/identity'
7
+ import { withArrayBuffer } from 'uint8arrays/with-array-buffer'
7
8
  import { CODEC_CBOR } from '../constants.ts'
8
9
  import { getContentTypesForCid, MEDIA_TYPE_CBOR, MEDIA_TYPE_DAG_CBOR, MEDIA_TYPE_DAG_JSON, MEDIA_TYPE_JSON, MEDIA_TYPE_OCTET_STREAM, MEDIA_TYPE_RAW } from './content-types.ts'
9
10
  import type { AcceptHeader, ContentType } from '../index.ts'
10
11
  import type { CID } from 'multiformats/cid'
11
12
 
12
- const CONVERSIONS: Record<number, Record<string, (buf: Uint8Array) => Uint8Array>> = {
13
+ const CONVERSIONS: Record<number, Record<string, (buf: Uint8Array<ArrayBuffer>) => Uint8Array<ArrayBuffer>>> = {
13
14
  [dagCbor.code]: {
14
15
  [MEDIA_TYPE_CBOR]: (buf) => {
15
16
  return buf
@@ -18,7 +19,8 @@ const CONVERSIONS: Record<number, Record<string, (buf: Uint8Array) => Uint8Array
18
19
  return buf
19
20
  },
20
21
  [MEDIA_TYPE_JSON]: (buf) => {
21
- return dagJson.encode(dagCbor.decode(buf))
22
+ // TODO: remove withArrayBuffer when dag-json is explicit about it's return type
23
+ return withArrayBuffer(dagJson.encode(dagCbor.decode(buf)))
22
24
  },
23
25
  [MEDIA_TYPE_DAG_JSON]: () => {
24
26
  throw new Error('Cannot perform conversion since IPIP-0524')
@@ -41,7 +43,8 @@ const CONVERSIONS: Record<number, Record<string, (buf: Uint8Array) => Uint8Array
41
43
  return buf
42
44
  },
43
45
  [MEDIA_TYPE_DAG_JSON]: (buf) => {
44
- return dagJson.encode(dagCbor.decode(buf))
46
+ // TODO: remove withArrayBuffer when dag-json is explicit about it's return type
47
+ return withArrayBuffer(dagJson.encode(dagCbor.decode(buf)))
45
48
  },
46
49
  [MEDIA_TYPE_RAW]: (buf) => {
47
50
  return buf
@@ -75,7 +78,8 @@ const CONVERSIONS: Record<number, Record<string, (buf: Uint8Array) => Uint8Array
75
78
  return buf
76
79
  },
77
80
  [MEDIA_TYPE_DAG_CBOR]: (buf) => {
78
- return dagCbor.encode(json.decode(buf))
81
+ // TODO: remove withArrayBuffer when dag-cbor is explicit about it's return type
82
+ return withArrayBuffer(dagCbor.encode(json.decode(buf)))
79
83
  },
80
84
  [MEDIA_TYPE_JSON]: (buf) => {
81
85
  return buf
@@ -135,13 +139,15 @@ const CONVERSIONS: Record<number, Record<string, (buf: Uint8Array) => Uint8Array
135
139
  return buf
136
140
  },
137
141
  [MEDIA_TYPE_DAG_CBOR]: (buf) => {
138
- return dagCbor.encode(buf)
142
+ // TODO: remove withArrayBuffer when dag-cbor is explicit about it's return type
143
+ return withArrayBuffer(dagCbor.encode(buf))
139
144
  },
140
145
  [MEDIA_TYPE_JSON]: (buf) => {
141
146
  return buf
142
147
  },
143
148
  [MEDIA_TYPE_DAG_JSON]: (buf) => {
144
- return dagJson.encode(buf)
149
+ // TODO: remove withArrayBuffer when dag-json is explicit about it's return type
150
+ return withArrayBuffer(dagJson.encode(buf))
145
151
  },
146
152
  [MEDIA_TYPE_RAW]: (buf) => {
147
153
  return buf
@@ -163,7 +169,7 @@ export interface ConvertedOutput {
163
169
  * the deserialized value to that format and serialize it back to a block -
164
170
  * return the first successful result.
165
171
  */
166
- export function convertOutput (cid: CID, block: Uint8Array, accept: AcceptHeader[]): { contentType: ContentType, output: Uint8Array } {
172
+ export function convertOutput (cid: CID, block: Uint8Array<ArrayBuffer>, accept: AcceptHeader[]): { contentType: ContentType, output: Uint8Array<ArrayBuffer> } {
167
173
  if (accept.length === 0) {
168
174
  // return current format
169
175
  return {
@@ -0,0 +1,9 @@
1
+ export function splitIPNSName (path: string): { ns: string, name: string, path: string } {
2
+ const [, ns, name, ...rest] = path.trim().split('/')
3
+
4
+ return {
5
+ ns,
6
+ name,
7
+ path: rest.length > 0 ? `${rest.join('/')}` : ''
8
+ }
9
+ }
@@ -1,8 +1,8 @@
1
+ import { libp2pDefaults } from '@helia/libp2p'
1
2
  import { webRTCDirect } from '@libp2p/webrtc'
2
3
  import { webSockets } from '@libp2p/websockets'
3
- import { libp2pDefaults } from 'helia'
4
4
  import type { ServiceFactoryMap } from './libp2p-types.ts'
5
- import type { DefaultLibp2pServices } from 'helia'
5
+ import type { DefaultLibp2pServices } from '@helia/libp2p'
6
6
  import type { Libp2pOptions } from 'libp2p'
7
7
 
8
8
  type ServiceMap = Pick<DefaultLibp2pServices, 'dcutr' | 'identify' | 'keychain' | 'ping'>
@@ -1,9 +1,7 @@
1
+ import { libp2pDefaults } from '@helia/libp2p'
1
2
  import { kadDHT } from '@libp2p/kad-dht'
2
- import { libp2pDefaults } from 'helia'
3
- import { ipnsSelector } from 'ipns/selector'
4
- import { ipnsValidator } from 'ipns/validator'
5
3
  import type { ServiceFactoryMap } from './libp2p-types.ts'
6
- import type { DefaultLibp2pServices } from 'helia'
4
+ import type { DefaultLibp2pServices } from '@helia/libp2p'
7
5
  import type { Libp2pOptions } from 'libp2p'
8
6
 
9
7
  type ServiceMap = Pick<DefaultLibp2pServices, 'autoNAT' | 'dcutr' | 'dht' | 'identify' | 'keychain' | 'ping' | 'upnp'>
@@ -17,13 +15,7 @@ export function getLibp2pConfig (): Libp2pOptions & Required<Pick<Libp2pOptions,
17
15
  autoNAT: libp2pDefaultOptions.services.autoNAT,
18
16
  dcutr: libp2pDefaultOptions.services.dcutr,
19
17
  dht: kadDHT({
20
- clientMode: true,
21
- validators: {
22
- ipns: ipnsValidator
23
- },
24
- selectors: {
25
- ipns: ipnsSelector
26
- }
18
+ clientMode: true
27
19
  }),
28
20
  identify: libp2pDefaultOptions.services.identify,
29
21
  keychain: libp2pDefaultOptions.services.keychain,
@@ -252,26 +252,26 @@ export class VerifiedFetch {
252
252
  options?.onProgress?.(evt)
253
253
 
254
254
  if (evt.type === 'helia:routing:find-providers:start') {
255
- routingTimers[evt.detail.routing] = {
255
+ routingTimers[evt.detail.router] = {
256
256
  start: performance.now(),
257
257
  found: 0
258
258
  }
259
259
  } else if (evt.type === 'helia:routing:find-providers:provider') {
260
- if (routingTimers[evt.detail.routing] == null) {
260
+ if (routingTimers[evt.detail.router] == null) {
261
261
  return
262
262
  }
263
263
 
264
- routingTimers[evt.detail.routing].found++
264
+ routingTimers[evt.detail.router].found++
265
265
 
266
- serverTiming.add(abbreviate('found-provider'), `${abbreviate(evt.detail.routing)},${truncate(evt.detail.provider.id)}`, performance.now() - routingTimers[evt.detail.routing].start)
266
+ serverTiming.add(abbreviate('found-provider'), `${abbreviate(evt.detail.router)},${truncate(evt.detail.provider.id)}`, performance.now() - routingTimers[evt.detail.router].start)
267
267
  } else if (evt.type === 'helia:routing:find-providers:end') {
268
- const routing = routingTimers[evt.detail.routing]
268
+ const routing = routingTimers[evt.detail.router]
269
269
 
270
270
  if (routing == null) {
271
271
  return
272
272
  }
273
273
 
274
- serverTiming.add(abbreviate('find-providers'), `${abbreviate(evt.detail.routing)},${routing.found}`, performance.now() - routing.start)
274
+ serverTiming.add(abbreviate('find-providers'), `${abbreviate(evt.detail.router)},${routing.found}`, performance.now() - routing.start)
275
275
  } else if (evt.type === 'helia:block-broker:connect') {
276
276
  connectTimers[`connect-${evt.detail.broker}-${evt.detail.provider}`] = {
277
277
  start: performance.now(),