@helia/ipns 4.0.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/src/index.ts CHANGED
@@ -3,20 +3,64 @@
3
3
  *
4
4
  * IPNS operations using a Helia node
5
5
  *
6
- * @example Using libp2p and pubsub routers
6
+ * @example Getting started
7
7
  *
8
8
  * With {@link IPNSRouting} routers:
9
9
  *
10
10
  * ```typescript
11
11
  * import { createHelia } from 'helia'
12
12
  * import { ipns } from '@helia/ipns'
13
- * import { libp2p, pubsub } from '@helia/ipns/routing'
14
13
  * import { unixfs } from '@helia/unixfs'
15
14
  *
16
15
  * const helia = await createHelia()
16
+ * const name = ipns(helia)
17
+ *
18
+ * // create a public key to publish as an IPNS name
19
+ * const keyInfo = await helia.libp2p.services.keychain.createKey('my-key')
20
+ * const peerId = await helia.libp2p.services.keychain.exportPeerId(keyInfo.name)
21
+ *
22
+ * // store some data to publish
23
+ * const fs = unixfs(helia)
24
+ * const cid = await fs.add(Uint8Array.from([0, 1, 2, 3, 4]))
25
+ *
26
+ * // publish the name
27
+ * await name.publish(peerId, cid)
28
+ *
29
+ * // resolve the name
30
+ * const cid = name.resolve(peerId)
31
+ * ```
32
+ *
33
+ * @example Using custom PubSub router
34
+ *
35
+ * Additional IPNS routers can be configured - these enable alternative means to
36
+ * publish and resolve IPNS names.
37
+ *
38
+ * One example is the PubSub router - this requires an instance of Helia with
39
+ * libp2p PubSub configured.
40
+ *
41
+ * It works by subscribing to a pubsub topic for each IPNS name that we try to
42
+ * resolve. Updated IPNS records are shared on these topics so an update must
43
+ * occur before the name is resolvable.
44
+ *
45
+ * This router is only suitable for networks where IPNS updates are frequent
46
+ * and multiple peers are listening on the topic(s), otherwise update messages
47
+ * may fail to be published with "Insufficient peers" errors.
48
+ *
49
+ * ```typescript
50
+ * import { createHelia, libp2pDefaults } from 'helia'
51
+ * import { ipns } from '@helia/ipns'
52
+ * import { pubsub } from '@helia/ipns/routing'
53
+ * import { unixfs } from '@helia/unixfs'
54
+ * import { gossipsub } from '@chainsafe/libp2p-gossipsub'
55
+ *
56
+ * const libp2pOptions = libp2pDefaults()
57
+ * libp2pOptions.services.pubsub = gossipsub()
58
+ *
59
+ * const helia = await createHelia({
60
+ * libp2p: libp2pOptions
61
+ * })
17
62
  * const name = ipns(helia, {
18
63
  * routers: [
19
- * libp2p(helia),
20
64
  * pubsub(helia)
21
65
  * ]
22
66
  * })
@@ -121,9 +165,11 @@ import { ipnsValidator } from 'ipns/validator'
121
165
  import { CID } from 'multiformats/cid'
122
166
  import { CustomProgressEvent } from 'progress-events'
123
167
  import { defaultResolver } from './dns-resolvers/default.js'
168
+ import { helia } from './routing/helia.js'
124
169
  import { localStore, type LocalStore } from './routing/local-store.js'
125
170
  import type { IPNSRouting, IPNSRoutingEvents } from './routing/index.js'
126
171
  import type { DNSResponse } from './utils/dns.js'
172
+ import type { Routing } from '@helia/interface'
127
173
  import type { AbortOptions, PeerId } from '@libp2p/interface'
128
174
  import type { Datastore } from 'interface-datastore'
129
175
  import type { IPNSRecord } from 'ipns'
@@ -249,6 +295,7 @@ export type { IPNSRouting } from './routing/index.js'
249
295
 
250
296
  export interface IPNSComponents {
251
297
  datastore: Datastore
298
+ routing: Routing
252
299
  }
253
300
 
254
301
  class DefaultIPNS implements IPNS {
@@ -258,7 +305,10 @@ class DefaultIPNS implements IPNS {
258
305
  private readonly defaultResolvers: DNSResolver[]
259
306
 
260
307
  constructor (components: IPNSComponents, routers: IPNSRouting[] = [], resolvers: DNSResolver[] = []) {
261
- this.routers = routers
308
+ this.routers = [
309
+ helia(components.routing),
310
+ ...routers
311
+ ]
262
312
  this.localStore = localStore(components.datastore)
263
313
  this.defaultResolvers = resolvers.length > 0 ? resolvers : [defaultResolver()]
264
314
  }
@@ -376,21 +426,44 @@ class DefaultIPNS implements IPNS {
376
426
  }
377
427
 
378
428
  const records: Uint8Array[] = []
429
+ let foundInvalid = 0
379
430
 
380
431
  await Promise.all(
381
432
  routers.map(async (router) => {
433
+ let record: Uint8Array
434
+
435
+ try {
436
+ record = await router.get(routingKey, {
437
+ ...options,
438
+ validate: false
439
+ })
440
+ } catch (err: any) {
441
+ if (router === this.localStore && err.code === 'ERR_NOT_FOUND') {
442
+ log('did not have record locally')
443
+ } else {
444
+ log.error('error finding IPNS record', err)
445
+ }
446
+
447
+ return
448
+ }
449
+
382
450
  try {
383
- const record = await router.get(routingKey, options)
384
451
  await ipnsValidator(routingKey, record)
385
452
 
386
453
  records.push(record)
387
454
  } catch (err) {
455
+ // we found a record, but the validator rejected it
456
+ foundInvalid++
388
457
  log.error('error finding IPNS record', err)
389
458
  }
390
459
  })
391
460
  )
392
461
 
393
462
  if (records.length === 0) {
463
+ if (foundInvalid > 0) {
464
+ throw new CodeError(`${foundInvalid > 1 ? `${foundInvalid} records` : 'Record'} found for routing key ${foundInvalid > 1 ? 'were' : 'was'} invalid`, 'ERR_RECORDS_FAILED_VALIDATION')
465
+ }
466
+
394
467
  throw new CodeError('Could not find record for routing key', 'ERR_NOT_FOUND')
395
468
  }
396
469
 
@@ -407,7 +480,7 @@ export interface IPNSOptions {
407
480
  resolvers?: DNSResolver[]
408
481
  }
409
482
 
410
- export function ipns (components: IPNSComponents, { routers = [], resolvers = [] }: IPNSOptions): IPNS {
483
+ export function ipns (components: IPNSComponents, { routers = [], resolvers = [] }: IPNSOptions = {}): IPNS {
411
484
  return new DefaultIPNS(components, routers, resolvers)
412
485
  }
413
486
 
@@ -0,0 +1,45 @@
1
+ import { CustomProgressEvent, type ProgressEvent } from 'progress-events'
2
+ import type { GetOptions, PutOptions } from './index.js'
3
+ import type { IPNSRouting } from '../index.js'
4
+ import type { Routing } from '@helia/interface'
5
+
6
+ export interface HeliaRoutingComponents {
7
+ routing: Routing
8
+ }
9
+
10
+ export type HeliaRoutingProgressEvents =
11
+ ProgressEvent<'ipns:routing:helia:error', Error>
12
+
13
+ export class HeliaRouting implements IPNSRouting {
14
+ private readonly routing: Routing
15
+
16
+ constructor (routing: Routing) {
17
+ this.routing = routing
18
+ }
19
+
20
+ async put (routingKey: Uint8Array, marshaledRecord: Uint8Array, options: PutOptions = {}): Promise<void> {
21
+ try {
22
+ await this.routing.put(routingKey, marshaledRecord, options)
23
+ } catch (err: any) {
24
+ options.onProgress?.(new CustomProgressEvent<Error>('ipns:routing:helia:error', err))
25
+ }
26
+ }
27
+
28
+ async get (routingKey: Uint8Array, options: GetOptions = {}): Promise<Uint8Array> {
29
+ try {
30
+ return await this.routing.get(routingKey, options)
31
+ } catch (err: any) {
32
+ options.onProgress?.(new CustomProgressEvent<Error>('ipns:routing:helia:error', err))
33
+ }
34
+
35
+ throw new Error('Not found')
36
+ }
37
+ }
38
+
39
+ /**
40
+ * The helia routing uses any available Routers configured on the passed Helia
41
+ * node. This could be libp2p, HTTP API Delegated Routing, etc.
42
+ */
43
+ export function helia (routing: Routing): IPNSRouting {
44
+ return new HeliaRouting(routing)
45
+ }
@@ -1,4 +1,4 @@
1
- import type { Libp2pContentRoutingProgressEvents } from './libp2p.js'
1
+ import type { HeliaRoutingProgressEvents } from './helia.js'
2
2
  import type { DatastoreProgressEvents } from './local-store.js'
3
3
  import type { PubSubProgressEvents } from './pubsub.js'
4
4
  import type { AbortOptions } from '@libp2p/interface'
@@ -9,7 +9,12 @@ export interface PutOptions extends AbortOptions, ProgressOptions {
9
9
  }
10
10
 
11
11
  export interface GetOptions extends AbortOptions, ProgressOptions {
12
-
12
+ /**
13
+ * Pass false to not perform validation actions
14
+ *
15
+ * @default true
16
+ */
17
+ validate?: boolean
13
18
  }
14
19
 
15
20
  export interface IPNSRouting {
@@ -19,8 +24,8 @@ export interface IPNSRouting {
19
24
 
20
25
  export type IPNSRoutingEvents =
21
26
  DatastoreProgressEvents |
22
- Libp2pContentRoutingProgressEvents |
27
+ HeliaRoutingProgressEvents |
23
28
  PubSubProgressEvents
24
29
 
25
- export { libp2p } from './libp2p.js'
30
+ export { helia } from './helia.js'
26
31
  export { pubsub } from './pubsub.js'
@@ -1,22 +0,0 @@
1
- import { type ProgressEvent } from 'progress-events';
2
- import type { GetOptions, PutOptions } from './index.js';
3
- import type { IPNSRouting } from '../index.js';
4
- import type { ContentRouting } from '@libp2p/interface';
5
- export interface Libp2pContentRoutingComponents {
6
- libp2p: {
7
- contentRouting: ContentRouting;
8
- };
9
- }
10
- export type Libp2pContentRoutingProgressEvents = ProgressEvent<'ipns:routing:libp2p:error', Error>;
11
- export declare class Libp2pContentRouting implements IPNSRouting {
12
- private readonly contentRouting;
13
- constructor(components: Libp2pContentRoutingComponents);
14
- put(routingKey: Uint8Array, marshaledRecord: Uint8Array, options?: PutOptions): Promise<void>;
15
- get(routingKey: Uint8Array, options?: GetOptions): Promise<Uint8Array>;
16
- }
17
- /**
18
- * The libp2p routing uses any available Content Routers configured on the
19
- * passed libp2p node. This could be KadDHT, HTTP API Delegated Routing, etc.
20
- */
21
- export declare function libp2p(components: Libp2pContentRoutingComponents): IPNSRouting;
22
- //# sourceMappingURL=libp2p.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"libp2p.d.ts","sourceRoot":"","sources":["../../../src/routing/libp2p.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACzE,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAEvD,MAAM,WAAW,8BAA8B;IAC7C,MAAM,EAAE;QACN,cAAc,EAAE,cAAc,CAAA;KAC/B,CAAA;CACF;AAED,MAAM,MAAM,kCAAkC,GAC5C,aAAa,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAA;AAEnD,qBAAa,oBAAqB,YAAW,WAAW;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;gBAElC,UAAU,EAAE,8BAA8B;IAIjD,GAAG,CAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlG,GAAG,CAAE,UAAU,EAAE,UAAU,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,UAAU,CAAC;CASlF;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAE,UAAU,EAAE,8BAA8B,GAAG,WAAW,CAE/E"}
@@ -1,32 +0,0 @@
1
- import { CustomProgressEvent } from 'progress-events';
2
- export class Libp2pContentRouting {
3
- contentRouting;
4
- constructor(components) {
5
- this.contentRouting = components.libp2p.contentRouting;
6
- }
7
- async put(routingKey, marshaledRecord, options = {}) {
8
- try {
9
- await this.contentRouting.put(routingKey, marshaledRecord, options);
10
- }
11
- catch (err) {
12
- options.onProgress?.(new CustomProgressEvent('ipns:routing:libp2p:error', err));
13
- }
14
- }
15
- async get(routingKey, options = {}) {
16
- try {
17
- return await this.contentRouting.get(routingKey, options);
18
- }
19
- catch (err) {
20
- options.onProgress?.(new CustomProgressEvent('ipns:routing:libp2p:error', err));
21
- }
22
- throw new Error('Not found');
23
- }
24
- }
25
- /**
26
- * The libp2p routing uses any available Content Routers configured on the
27
- * passed libp2p node. This could be KadDHT, HTTP API Delegated Routing, etc.
28
- */
29
- export function libp2p(components) {
30
- return new Libp2pContentRouting(components);
31
- }
32
- //# sourceMappingURL=libp2p.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"libp2p.js","sourceRoot":"","sources":["../../../src/routing/libp2p.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAsB,MAAM,iBAAiB,CAAA;AAczE,MAAM,OAAO,oBAAoB;IACd,cAAc,CAAgB;IAE/C,YAAa,UAA0C;QACrD,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC,cAAc,CAAA;IACxD,CAAC;IAED,KAAK,CAAC,GAAG,CAAE,UAAsB,EAAE,eAA2B,EAAE,UAAsB,EAAE;QACtF,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,UAAU,EAAE,eAAe,EAAE,OAAO,CAAC,CAAA;QACrE,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,mBAAmB,CAAQ,2BAA2B,EAAE,GAAG,CAAC,CAAC,CAAA;QACxF,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAE,UAAsB,EAAE,UAAsB,EAAE;QACzD,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;QAC3D,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,mBAAmB,CAAQ,2BAA2B,EAAE,GAAG,CAAC,CAAC,CAAA;QACxF,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,CAAA;IAC9B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAE,UAA0C;IAChE,OAAO,IAAI,oBAAoB,CAAC,UAAU,CAAC,CAAA;AAC7C,CAAC"}
@@ -1,47 +0,0 @@
1
- import { CustomProgressEvent, type ProgressEvent } from 'progress-events'
2
- import type { GetOptions, PutOptions } from './index.js'
3
- import type { IPNSRouting } from '../index.js'
4
- import type { ContentRouting } from '@libp2p/interface'
5
-
6
- export interface Libp2pContentRoutingComponents {
7
- libp2p: {
8
- contentRouting: ContentRouting
9
- }
10
- }
11
-
12
- export type Libp2pContentRoutingProgressEvents =
13
- ProgressEvent<'ipns:routing:libp2p:error', Error>
14
-
15
- export class Libp2pContentRouting implements IPNSRouting {
16
- private readonly contentRouting: ContentRouting
17
-
18
- constructor (components: Libp2pContentRoutingComponents) {
19
- this.contentRouting = components.libp2p.contentRouting
20
- }
21
-
22
- async put (routingKey: Uint8Array, marshaledRecord: Uint8Array, options: PutOptions = {}): Promise<void> {
23
- try {
24
- await this.contentRouting.put(routingKey, marshaledRecord, options)
25
- } catch (err: any) {
26
- options.onProgress?.(new CustomProgressEvent<Error>('ipns:routing:libp2p:error', err))
27
- }
28
- }
29
-
30
- async get (routingKey: Uint8Array, options: GetOptions = {}): Promise<Uint8Array> {
31
- try {
32
- return await this.contentRouting.get(routingKey, options)
33
- } catch (err: any) {
34
- options.onProgress?.(new CustomProgressEvent<Error>('ipns:routing:libp2p:error', err))
35
- }
36
-
37
- throw new Error('Not found')
38
- }
39
- }
40
-
41
- /**
42
- * The libp2p routing uses any available Content Routers configured on the
43
- * passed libp2p node. This could be KadDHT, HTTP API Delegated Routing, etc.
44
- */
45
- export function libp2p (components: Libp2pContentRoutingComponents): IPNSRouting {
46
- return new Libp2pContentRouting(components)
47
- }