@helia/delegated-routing-v1-http-api-client 5.1.0 → 5.1.2
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 +5 -5
- package/dist/index.min.js.map +4 -4
- package/dist/src/client.d.ts +8 -4
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +30 -27
- package/dist/src/client.js.map +1 -1
- package/dist/src/index.d.ts +19 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +18 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/routings.d.ts +4 -2
- package/dist/src/routings.d.ts.map +1 -1
- package/dist/src/routings.js +8 -1
- package/dist/src/routings.js.map +1 -1
- package/dist/typedoc-urls.json +5 -1
- package/package.json +1 -1
- package/src/client.ts +34 -30
- package/src/index.ts +30 -4
- package/src/routings.ts +12 -3
package/src/client.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { NotFoundError, contentRoutingSymbol, peerRoutingSymbol, setMaxListeners } from '@libp2p/interface'
|
|
2
|
-
import { logger } from '@libp2p/logger'
|
|
3
2
|
import { peerIdFromString } from '@libp2p/peer-id'
|
|
4
3
|
import { multiaddr } from '@multiformats/multiaddr'
|
|
5
4
|
import { anySignal } from 'any-signal'
|
|
@@ -11,14 +10,12 @@ import defer from 'p-defer'
|
|
|
11
10
|
import PQueue from 'p-queue'
|
|
12
11
|
import { BadResponseError, InvalidRequestError } from './errors.js'
|
|
13
12
|
import { DelegatedRoutingV1HttpApiClientContentRouting, DelegatedRoutingV1HttpApiClientPeerRouting } from './routings.js'
|
|
14
|
-
import type { DelegatedRoutingV1HttpApiClient, DelegatedRoutingV1HttpApiClientInit, GetProvidersOptions, GetPeersOptions, GetIPNSOptions, PeerRecord } from './index.js'
|
|
15
|
-
import type { ContentRouting, PeerRouting, AbortOptions, PeerId } from '@libp2p/interface'
|
|
13
|
+
import type { DelegatedRoutingV1HttpApiClient as DelegatedRoutingV1HttpApiClientInterface, DelegatedRoutingV1HttpApiClientInit, GetProvidersOptions, GetPeersOptions, GetIPNSOptions, PeerRecord, DelegatedRoutingV1HttpApiClientComponents } from './index.js'
|
|
14
|
+
import type { ContentRouting, PeerRouting, AbortOptions, PeerId, Logger } from '@libp2p/interface'
|
|
16
15
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
17
16
|
import type { IPNSRecord } from 'ipns'
|
|
18
17
|
import type { CID } from 'multiformats'
|
|
19
18
|
|
|
20
|
-
const log = logger('delegated-routing-v1-http-api-client')
|
|
21
|
-
|
|
22
19
|
const defaultValues = {
|
|
23
20
|
concurrentRequests: 4,
|
|
24
21
|
timeout: 30e3,
|
|
@@ -26,11 +23,11 @@ const defaultValues = {
|
|
|
26
23
|
cacheName: 'delegated-routing-v1-cache'
|
|
27
24
|
}
|
|
28
25
|
|
|
29
|
-
export class
|
|
26
|
+
export class DelegatedRoutingV1HttpApiClient implements DelegatedRoutingV1HttpApiClientInterface {
|
|
27
|
+
public readonly url: URL
|
|
30
28
|
private started: boolean
|
|
31
29
|
private readonly httpQueue: PQueue
|
|
32
30
|
private readonly shutDownController: AbortController
|
|
33
|
-
private readonly clientUrl: URL
|
|
34
31
|
private readonly timeout: number
|
|
35
32
|
private readonly contentRouting: ContentRouting
|
|
36
33
|
private readonly peerRouting: PeerRouting
|
|
@@ -40,10 +37,13 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
40
37
|
private readonly cacheName: string
|
|
41
38
|
private cache?: Cache
|
|
42
39
|
private readonly cacheTTL: number
|
|
40
|
+
private log: Logger
|
|
41
|
+
|
|
43
42
|
/**
|
|
44
43
|
* Create a new DelegatedContentRouting instance
|
|
45
44
|
*/
|
|
46
|
-
constructor (
|
|
45
|
+
constructor (components: DelegatedRoutingV1HttpApiClientComponents, init: DelegatedRoutingV1HttpApiClientInit & { url: string | URL }) {
|
|
46
|
+
this.log = components.logger.forComponent('delegated-routing-v1-http-api-client')
|
|
47
47
|
this.started = false
|
|
48
48
|
this.shutDownController = new AbortController()
|
|
49
49
|
setMaxListeners(Infinity, this.shutDownController.signal)
|
|
@@ -51,7 +51,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
51
51
|
concurrency: init.concurrentRequests ?? defaultValues.concurrentRequests
|
|
52
52
|
})
|
|
53
53
|
this.inFlightRequests = new Map() // Tracks in-flight requests to avoid duplicate requests
|
|
54
|
-
this.
|
|
54
|
+
this.url = init.url instanceof URL ? init.url : new URL(init.url)
|
|
55
55
|
this.timeout = init.timeout ?? defaultValues.timeout
|
|
56
56
|
this.filterAddrs = init.filterAddrs
|
|
57
57
|
this.filterProtocols = init.filterProtocols
|
|
@@ -85,7 +85,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
85
85
|
this.cache = await globalThis.caches?.open(this.cacheName)
|
|
86
86
|
|
|
87
87
|
if (this.cache != null) {
|
|
88
|
-
log('cache enabled with ttl %d', this.cacheTTL)
|
|
88
|
+
this.log('cache enabled with ttl %d', this.cacheTTL)
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
}
|
|
@@ -101,7 +101,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
async * getProviders (cid: CID, options: GetProvidersOptions = {}): AsyncGenerator<PeerRecord> {
|
|
104
|
-
log('getProviders starts: %c', cid)
|
|
104
|
+
this.log('getProviders starts: %c', cid)
|
|
105
105
|
|
|
106
106
|
const timeoutSignal = AbortSignal.timeout(this.timeout)
|
|
107
107
|
const signal = anySignal([this.shutDownController.signal, timeoutSignal, options.signal])
|
|
@@ -118,7 +118,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
118
118
|
await onStart.promise
|
|
119
119
|
|
|
120
120
|
// https://specs.ipfs.tech/routing/http-routing-v1/
|
|
121
|
-
const url = new URL(`${this.
|
|
121
|
+
const url = new URL(`${this.url}routing/v1/providers/${cid}`)
|
|
122
122
|
|
|
123
123
|
this.#addFilterParams(url, options.filterAddrs, options.filterProtocols)
|
|
124
124
|
const getOptions = { headers: { Accept: 'application/x-ndjson' }, signal }
|
|
@@ -175,12 +175,12 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
175
175
|
} finally {
|
|
176
176
|
signal.clear()
|
|
177
177
|
onFinish.resolve()
|
|
178
|
-
log('getProviders finished: %c', cid)
|
|
178
|
+
this.log('getProviders finished: %c', cid)
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
async * getPeers (peerId: PeerId, options: GetPeersOptions = {}): AsyncGenerator<PeerRecord> {
|
|
183
|
-
log('getPeers starts: %c', peerId)
|
|
183
|
+
this.log('getPeers starts: %c', peerId)
|
|
184
184
|
|
|
185
185
|
const timeoutSignal = AbortSignal.timeout(this.timeout)
|
|
186
186
|
const signal = anySignal([this.shutDownController.signal, timeoutSignal, options.signal])
|
|
@@ -197,7 +197,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
197
197
|
await onStart.promise
|
|
198
198
|
|
|
199
199
|
// https://specs.ipfs.tech/routing/http-routing-v1/
|
|
200
|
-
const url = new URL(`${this.
|
|
200
|
+
const url = new URL(`${this.url}routing/v1/peers/${peerId.toCID().toString()}`)
|
|
201
201
|
this.#addFilterParams(url, options.filterAddrs, options.filterProtocols)
|
|
202
202
|
|
|
203
203
|
const getOptions = { headers: { Accept: 'application/x-ndjson' }, signal }
|
|
@@ -241,16 +241,16 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
} catch (err) {
|
|
244
|
-
log.error('getPeers errored
|
|
244
|
+
this.log.error('getPeers errored - %e', err)
|
|
245
245
|
} finally {
|
|
246
246
|
signal.clear()
|
|
247
247
|
onFinish.resolve()
|
|
248
|
-
log('getPeers finished: %c', peerId)
|
|
248
|
+
this.log('getPeers finished: %c', peerId)
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
async getIPNS (libp2pKey: CID<unknown, 0x72, 0x00 | 0x12, 1>, options: GetIPNSOptions = {}): Promise<IPNSRecord> {
|
|
253
|
-
log('getIPNS starts: %s', libp2pKey)
|
|
253
|
+
this.log('getIPNS starts: %s', libp2pKey)
|
|
254
254
|
|
|
255
255
|
const timeoutSignal = AbortSignal.timeout(this.timeout)
|
|
256
256
|
const signal = anySignal([this.shutDownController.signal, timeoutSignal, options.signal])
|
|
@@ -264,7 +264,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
264
264
|
})
|
|
265
265
|
|
|
266
266
|
// https://specs.ipfs.tech/routing/http-routing-v1/
|
|
267
|
-
const resource = `${this.
|
|
267
|
+
const resource = `${this.url}routing/v1/ipns/${libp2pKey}`
|
|
268
268
|
|
|
269
269
|
try {
|
|
270
270
|
await onStart.promise
|
|
@@ -272,7 +272,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
272
272
|
const getOptions = { headers: { Accept: 'application/vnd.ipfs.ipns-record' }, signal }
|
|
273
273
|
const res = await this.#makeRequest(resource, getOptions)
|
|
274
274
|
|
|
275
|
-
log('getIPNS GET %s %d', resource, res.status)
|
|
275
|
+
this.log('getIPNS GET %s %d', resource, res.status)
|
|
276
276
|
|
|
277
277
|
// Per IPIP-0513: Handle 404 as "no record found" for backward compatibility
|
|
278
278
|
// IPNS is different - we still throw NotFoundError for 404 (backward compat)
|
|
@@ -311,18 +311,18 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
311
311
|
|
|
312
312
|
return unmarshalIPNSRecord(body)
|
|
313
313
|
} catch (err: any) {
|
|
314
|
-
log.error('getIPNS GET %s error
|
|
314
|
+
this.log.error('getIPNS GET %s error - %e', resource, err)
|
|
315
315
|
|
|
316
316
|
throw err
|
|
317
317
|
} finally {
|
|
318
318
|
signal.clear()
|
|
319
319
|
onFinish.resolve()
|
|
320
|
-
log('getIPNS finished: %s', libp2pKey)
|
|
320
|
+
this.log('getIPNS finished: %s', libp2pKey)
|
|
321
321
|
}
|
|
322
322
|
}
|
|
323
323
|
|
|
324
324
|
async putIPNS (libp2pKey: CID<unknown, 0x72, 0x00 | 0x12, 1>, record: IPNSRecord, options: AbortOptions = {}): Promise<void> {
|
|
325
|
-
log('putIPNS starts: %c', libp2pKey)
|
|
325
|
+
this.log('putIPNS starts: %c', libp2pKey)
|
|
326
326
|
|
|
327
327
|
const timeoutSignal = AbortSignal.timeout(this.timeout)
|
|
328
328
|
const signal = anySignal([this.shutDownController.signal, timeoutSignal, options.signal])
|
|
@@ -336,7 +336,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
336
336
|
})
|
|
337
337
|
|
|
338
338
|
// https://specs.ipfs.tech/routing/http-routing-v1/
|
|
339
|
-
const resource = `${this.
|
|
339
|
+
const resource = `${this.url}routing/v1/ipns/${libp2pKey}`
|
|
340
340
|
|
|
341
341
|
try {
|
|
342
342
|
await onStart.promise
|
|
@@ -346,19 +346,19 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
346
346
|
const getOptions = { method: 'PUT', headers: { 'Content-Type': 'application/vnd.ipfs.ipns-record' }, body, signal }
|
|
347
347
|
const res = await this.#makeRequest(resource, getOptions)
|
|
348
348
|
|
|
349
|
-
log('putIPNS PUT %s %d', resource, res.status)
|
|
349
|
+
this.log('putIPNS PUT %s %d', resource, res.status)
|
|
350
350
|
|
|
351
351
|
if (res.status !== 200) {
|
|
352
352
|
throw new BadResponseError('PUT ipns response had status other than 200')
|
|
353
353
|
}
|
|
354
354
|
} catch (err: any) {
|
|
355
|
-
log.error('putIPNS PUT %s error
|
|
355
|
+
this.log.error('putIPNS PUT %s error - %e', resource, err.stack)
|
|
356
356
|
|
|
357
357
|
throw err
|
|
358
358
|
} finally {
|
|
359
359
|
signal.clear()
|
|
360
360
|
onFinish.resolve()
|
|
361
|
-
log('putIPNS finished: %c', libp2pKey)
|
|
361
|
+
this.log('putIPNS finished: %c', libp2pKey)
|
|
362
362
|
}
|
|
363
363
|
}
|
|
364
364
|
|
|
@@ -384,7 +384,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
384
384
|
Protocols: protocols
|
|
385
385
|
}
|
|
386
386
|
} catch (err) {
|
|
387
|
-
log.error('could not conform record to peer schema', err)
|
|
387
|
+
this.log.error('could not conform record to peer schema - %e', err)
|
|
388
388
|
}
|
|
389
389
|
}
|
|
390
390
|
|
|
@@ -420,7 +420,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
420
420
|
// Check if the cached response has expired
|
|
421
421
|
const expires = parseInt(cachedResponse.headers.get('x-cache-expires') ?? '0', 10)
|
|
422
422
|
if (expires > Date.now()) {
|
|
423
|
-
log('returning cached response for %s', key)
|
|
423
|
+
this.log('returning cached response for %s', key)
|
|
424
424
|
return cachedResponse
|
|
425
425
|
} else {
|
|
426
426
|
// Remove expired response from cache
|
|
@@ -433,7 +433,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
433
433
|
const existingRequest = this.inFlightRequests.get(key)
|
|
434
434
|
if (existingRequest != null) {
|
|
435
435
|
const response = await existingRequest
|
|
436
|
-
log('deduplicating outgoing request for %s', key)
|
|
436
|
+
this.log('deduplicating outgoing request for %s', key)
|
|
437
437
|
return response.clone()
|
|
438
438
|
}
|
|
439
439
|
|
|
@@ -464,4 +464,8 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
|
|
|
464
464
|
const response = await requestPromise
|
|
465
465
|
return response
|
|
466
466
|
}
|
|
467
|
+
|
|
468
|
+
toString (): string {
|
|
469
|
+
return `DefaultDelegatedRoutingV1HttpApiClient(${this.url})`
|
|
470
|
+
}
|
|
467
471
|
}
|
package/src/index.ts
CHANGED
|
@@ -83,8 +83,9 @@
|
|
|
83
83
|
* ```
|
|
84
84
|
*/
|
|
85
85
|
|
|
86
|
-
import {
|
|
87
|
-
import
|
|
86
|
+
import { defaultLogger } from '@libp2p/logger'
|
|
87
|
+
import { DelegatedRoutingV1HttpApiClient as DelegatedRoutingV1HttpApiClientClass } from './client.js'
|
|
88
|
+
import type { AbortOptions, ComponentLogger, PeerId } from '@libp2p/interface'
|
|
88
89
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
89
90
|
import type { IPNSRecord } from 'ipns'
|
|
90
91
|
import type { CID } from 'multiformats/cid'
|
|
@@ -155,6 +156,10 @@ export interface DelegatedRoutingV1HttpApiClientInit extends FilterOptions {
|
|
|
155
156
|
cacheName?: string
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
export interface DelegatedRoutingV1HttpApiClientComponents {
|
|
160
|
+
logger: ComponentLogger
|
|
161
|
+
}
|
|
162
|
+
|
|
158
163
|
export interface GetIPNSOptions extends AbortOptions {
|
|
159
164
|
/**
|
|
160
165
|
* By default incoming IPNS records are validated, pass false here to skip
|
|
@@ -169,6 +174,11 @@ export type GetProvidersOptions = FilterOptions & AbortOptions
|
|
|
169
174
|
export type GetPeersOptions = FilterOptions & AbortOptions
|
|
170
175
|
|
|
171
176
|
export interface DelegatedRoutingV1HttpApiClient {
|
|
177
|
+
/**
|
|
178
|
+
* The URL that requests are sent to
|
|
179
|
+
*/
|
|
180
|
+
url: URL
|
|
181
|
+
|
|
172
182
|
/**
|
|
173
183
|
* Returns an async generator of {@link PeerRecord}s that can provide the
|
|
174
184
|
* content for the passed {@link CID}
|
|
@@ -206,7 +216,23 @@ export interface DelegatedRoutingV1HttpApiClient {
|
|
|
206
216
|
|
|
207
217
|
/**
|
|
208
218
|
* Create and return a client to use with a Routing V1 HTTP API server
|
|
219
|
+
*
|
|
220
|
+
* @deprecated use `delegatedRoutingV1HttpApiClient` instead - this function will be removed in a future release
|
|
221
|
+
*/
|
|
222
|
+
export function createDelegatedRoutingV1HttpApiClient (url: URL | string, init: Omit<DelegatedRoutingV1HttpApiClientInit, 'url'> = {}): DelegatedRoutingV1HttpApiClient {
|
|
223
|
+
return new DelegatedRoutingV1HttpApiClientClass({
|
|
224
|
+
logger: defaultLogger()
|
|
225
|
+
}, {
|
|
226
|
+
...init,
|
|
227
|
+
url: new URL(url)
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Create and return a client to use with a Routing V1 HTTP API server
|
|
233
|
+
*
|
|
234
|
+
* TODO: add `url` to `DelegatedRoutingV1HttpApiClientInit` interface and release as breaking change
|
|
209
235
|
*/
|
|
210
|
-
export function
|
|
211
|
-
return
|
|
236
|
+
export function delegatedRoutingV1HttpApiClient (init: DelegatedRoutingV1HttpApiClientInit & { url: string | URL }): (components: DelegatedRoutingV1HttpApiClientComponents) => DelegatedRoutingV1HttpApiClient {
|
|
237
|
+
return (components) => new DelegatedRoutingV1HttpApiClientClass(components, init)
|
|
212
238
|
}
|
package/src/routings.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { CID } from 'multiformats/cid'
|
|
|
6
6
|
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
|
|
7
7
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
8
8
|
import type { DelegatedRoutingV1HttpApiClient } from './index.js'
|
|
9
|
-
import type { ContentRouting, PeerRouting, AbortOptions, PeerId, PeerInfo } from '@libp2p/interface'
|
|
9
|
+
import type { ContentRouting, PeerRouting, AbortOptions, PeerId, PeerInfo, Provider } from '@libp2p/interface'
|
|
10
10
|
|
|
11
11
|
const IPNS_PREFIX = uint8ArrayFromString('/ipns/')
|
|
12
12
|
|
|
@@ -24,12 +24,13 @@ export class DelegatedRoutingV1HttpApiClientContentRouting implements ContentRou
|
|
|
24
24
|
this.client = client
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
async * findProviders (cid: CID, options: AbortOptions = {}): AsyncIterable<
|
|
27
|
+
async * findProviders (cid: CID, options: AbortOptions = {}): AsyncIterable<Provider> {
|
|
28
28
|
try {
|
|
29
29
|
yield * map(this.client.getProviders(cid, options), (record) => {
|
|
30
30
|
return {
|
|
31
31
|
id: record.ID,
|
|
32
|
-
multiaddrs: record.Addrs ?? []
|
|
32
|
+
multiaddrs: record.Addrs ?? [],
|
|
33
|
+
routing: 'delegated-http-routing-v1'
|
|
33
34
|
}
|
|
34
35
|
})
|
|
35
36
|
} catch (err) {
|
|
@@ -85,6 +86,10 @@ export class DelegatedRoutingV1HttpApiClientContentRouting implements ContentRou
|
|
|
85
86
|
throw err
|
|
86
87
|
}
|
|
87
88
|
}
|
|
89
|
+
|
|
90
|
+
toString (): string {
|
|
91
|
+
return `DelegatedRoutingV1HttpApiClientContentRouting(${this.client.url})`
|
|
92
|
+
}
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
/**
|
|
@@ -113,4 +118,8 @@ export class DelegatedRoutingV1HttpApiClientPeerRouting implements PeerRouting {
|
|
|
113
118
|
async * getClosestPeers (key: Uint8Array, options: AbortOptions = {}): AsyncIterable<PeerInfo> {
|
|
114
119
|
// noop
|
|
115
120
|
}
|
|
121
|
+
|
|
122
|
+
toString (): string {
|
|
123
|
+
return `DelegatedRoutingV1HttpApiClientPeerRouting(${this.client.url})`
|
|
124
|
+
}
|
|
116
125
|
}
|