@helia/ipns 5.0.0 → 6.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
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * With {@link IPNSRouting} routers:
9
9
  *
10
- * ```typescript
10
+ * ```TypeScript
11
11
  * import { createHelia } from 'helia'
12
12
  * import { ipns } from '@helia/ipns'
13
13
  * import { unixfs } from '@helia/unixfs'
@@ -27,7 +27,78 @@
27
27
  * await name.publish(peerId, cid)
28
28
  *
29
29
  * // resolve the name
30
- * const cid = name.resolve(peerId)
30
+ * const result = name.resolve(peerId)
31
+ *
32
+ * console.info(result.cid, result.path)
33
+ * ```
34
+ *
35
+ * @example Publishing a recursive record
36
+ *
37
+ * A recursive record is a one that points to another record rather than to a
38
+ * value.
39
+ *
40
+ * ```TypeScript
41
+ * import { createHelia } from 'helia'
42
+ * import { ipns } from '@helia/ipns'
43
+ * import { unixfs } from '@helia/unixfs'
44
+ *
45
+ * const helia = await createHelia()
46
+ * const name = ipns(helia)
47
+ *
48
+ * // create a public key to publish as an IPNS name
49
+ * const keyInfo = await helia.libp2p.services.keychain.createKey('my-key')
50
+ * const peerId = await helia.libp2p.services.keychain.exportPeerId(keyInfo.name)
51
+ *
52
+ * // store some data to publish
53
+ * const fs = unixfs(helia)
54
+ * const cid = await fs.add(Uint8Array.from([0, 1, 2, 3, 4]))
55
+ *
56
+ * // publish the name
57
+ * await name.publish(peerId, cid)
58
+ *
59
+ * // create another public key to re-publish the original record
60
+ * const recursiveKeyInfo = await helia.libp2p.services.keychain.createKey('my-recursive-key')
61
+ * const recursivePeerId = await helia.libp2p.services.keychain.exportPeerId(recursiveKeyInfo.name)
62
+ *
63
+ * // publish the recursive name
64
+ * await name.publish(recursivePeerId, peerId)
65
+ *
66
+ * // resolve the name recursively - it resolves until a CID is found
67
+ * const result = name.resolve(recursivePeerId)
68
+ * console.info(result.cid.toString() === cid.toString()) // true
69
+ * ```
70
+ *
71
+ * @example Publishing a record with a path
72
+ *
73
+ * It is possible to publish CIDs with an associated path.
74
+ *
75
+ * ```TypeScript
76
+ * import { createHelia } from 'helia'
77
+ * import { ipns } from '@helia/ipns'
78
+ * import { unixfs } from '@helia/unixfs'
79
+ *
80
+ * const helia = await createHelia()
81
+ * const name = ipns(helia)
82
+ *
83
+ * // create a public key to publish as an IPNS name
84
+ * const keyInfo = await helia.libp2p.services.keychain.createKey('my-key')
85
+ * const peerId = await helia.libp2p.services.keychain.exportPeerId(keyInfo.name)
86
+ *
87
+ * // store some data to publish
88
+ * const fs = unixfs(helia)
89
+ * const fileCid = await fs.add(Uint8Array.from([0, 1, 2, 3, 4]))
90
+ *
91
+ * // store the file in a directory
92
+ * const dirCid = await fs.mkdir()
93
+ * const finalDirCid = await fs.cp(fileCid, dirCid, '/foo.txt')
94
+ *
95
+ * // publish the name
96
+ * await name.publish(peerId, `/ipfs/${finalDirCid}/foo.txt)
97
+ *
98
+ * // resolve the name
99
+ * const result = name.resolve(peerId)
100
+ *
101
+ * console.info(result.cid, result.path) // QmFoo.. 'foo.txt'
31
102
  * ```
32
103
  *
33
104
  * @example Using custom PubSub router
@@ -46,7 +117,7 @@
46
117
  * and multiple peers are listening on the topic(s), otherwise update messages
47
118
  * may fail to be published with "Insufficient peers" errors.
48
119
  *
49
- * ```typescript
120
+ * ```TypeScript
50
121
  * import { createHelia, libp2pDefaults } from 'helia'
51
122
  * import { ipns } from '@helia/ipns'
52
123
  * import { pubsub } from '@helia/ipns/routing'
@@ -77,14 +148,14 @@
77
148
  * await name.publish(peerId, cid)
78
149
  *
79
150
  * // resolve the name
80
- * const cid = name.resolve(peerId)
151
+ * const { cid, path } = name.resolve(peerId)
81
152
  * ```
82
153
  *
83
154
  * @example Using custom DNS over HTTPS resolvers
84
155
  *
85
156
  * With default {@link DNSResolver} resolvers:
86
157
  *
87
- * ```typescript
158
+ * ```TypeScript
88
159
  * import { createHelia } from 'helia'
89
160
  * import { ipns } from '@helia/ipns'
90
161
  * import { unixfs } from '@helia/unixfs'
@@ -97,14 +168,14 @@
97
168
  * ]
98
169
  * })
99
170
  *
100
- * const cid = name.resolveDns('some-domain-with-dnslink-entry.com')
171
+ * const { cid, path } = name.resolveDns('some-domain-with-dnslink-entry.com')
101
172
  * ```
102
173
  *
103
174
  * @example Resolving a domain with a dnslink entry
104
175
  *
105
176
  * Calling `resolveDns` with the `@helia/ipns` instance:
106
177
  *
107
- * ```typescript
178
+ * ```TypeScript
108
179
  * // resolve a CID from a TXT record in a DNS zone file, using the default
109
180
  * // resolver for the current platform eg:
110
181
  * // > dig _dnslink.ipfs.io TXT
@@ -114,7 +185,7 @@
114
185
  * // ;; ANSWER SECTION:
115
186
  * // _dnslink.website.ipfs.io. 60 IN TXT "dnslink=/ipfs/QmWebsite"
116
187
  *
117
- * const cid = name.resolveDns('ipfs.io')
188
+ * const { cid, path } = name.resolveDns('ipfs.io')
118
189
  *
119
190
  * console.info(cid)
120
191
  * // QmWebsite
@@ -128,11 +199,11 @@
128
199
  *
129
200
  * If this is a concern, use the DNS-JSON-Over-HTTPS resolver instead.
130
201
  *
131
- * ```typescript
202
+ * ```TypeScript
132
203
  * // use DNS-Over-HTTPS
133
204
  * import { dnsOverHttps } from '@helia/ipns/dns-resolvers'
134
205
  *
135
- * const cid = name.resolveDns('ipfs.io', {
206
+ * const { cid, path } = name.resolveDns('ipfs.io', {
136
207
  * resolvers: [
137
208
  * dnsOverHttps('https://mozilla.cloudflare-dns.com/dns-query')
138
209
  * ]
@@ -144,11 +215,11 @@
144
215
  * DNS-JSON-Over-HTTPS resolvers use the RFC 8427 `application/dns-json` and can
145
216
  * result in a smaller browser bundle due to the response being plain JSON.
146
217
  *
147
- * ```typescript
218
+ * ```TypeScript
148
219
  * // use DNS-JSON-Over-HTTPS
149
220
  * import { dnsJsonOverHttps } from '@helia/ipns/dns-resolvers'
150
221
  *
151
- * const cid = name.resolveDns('ipfs.io', {
222
+ * const { cid, path } = name.resolveDns('ipfs.io', {
152
223
  * resolvers: [
153
224
  * dnsJsonOverHttps('https://mozilla.cloudflare-dns.com/dns-query')
154
225
  * ]
@@ -266,24 +337,29 @@ export interface RepublishOptions extends AbortOptions, ProgressOptions<Republis
266
337
  interval?: number
267
338
  }
268
339
 
340
+ export interface ResolveResult {
341
+ cid: CID
342
+ path: string
343
+ }
344
+
269
345
  export interface IPNS {
270
346
  /**
271
347
  * Creates an IPNS record signed by the passed PeerId that will resolve to the passed value
272
348
  *
273
349
  * If the value is a PeerId, a recursive IPNS record will be created.
274
350
  */
275
- publish(key: PeerId, value: CID | PeerId, options?: PublishOptions): Promise<IPNSRecord>
351
+ publish(key: PeerId, value: CID | PeerId | string, options?: PublishOptions): Promise<IPNSRecord>
276
352
 
277
353
  /**
278
354
  * Accepts a public key formatted as a libp2p PeerID and resolves the IPNS record
279
355
  * corresponding to that public key until a value is found
280
356
  */
281
- resolve(key: PeerId, options?: ResolveOptions): Promise<CID>
357
+ resolve(key: PeerId, options?: ResolveOptions): Promise<ResolveResult>
282
358
 
283
359
  /**
284
360
  * Resolve a CID from a dns-link style IPNS record
285
361
  */
286
- resolveDns(domain: string, options?: ResolveDNSOptions): Promise<CID>
362
+ resolveDns(domain: string, options?: ResolveDNSOptions): Promise<ResolveResult>
287
363
 
288
364
  /**
289
365
  * Periodically republish all IPNS records found in the datastore
@@ -313,7 +389,7 @@ class DefaultIPNS implements IPNS {
313
389
  this.defaultResolvers = resolvers.length > 0 ? resolvers : [defaultResolver()]
314
390
  }
315
391
 
316
- async publish (key: PeerId, value: CID | PeerId, options: PublishOptions = {}): Promise<IPNSRecord> {
392
+ async publish (key: PeerId, value: CID | PeerId | string, options: PublishOptions = {}): Promise<IPNSRecord> {
317
393
  try {
318
394
  let sequenceNumber = 1n
319
395
  const routingKey = peerIdToRoutingKey(key)
@@ -343,14 +419,14 @@ class DefaultIPNS implements IPNS {
343
419
  }
344
420
  }
345
421
 
346
- async resolve (key: PeerId, options: ResolveOptions = {}): Promise<CID> {
422
+ async resolve (key: PeerId, options: ResolveOptions = {}): Promise<ResolveResult> {
347
423
  const routingKey = peerIdToRoutingKey(key)
348
424
  const record = await this.#findIpnsRecord(routingKey, options)
349
425
 
350
426
  return this.#resolve(record.value, options)
351
427
  }
352
428
 
353
- async resolveDns (domain: string, options: ResolveDNSOptions = {}): Promise<CID> {
429
+ async resolveDns (domain: string, options: ResolveDNSOptions = {}): Promise<ResolveResult> {
354
430
  const resolvers = options.resolvers ?? this.defaultResolvers
355
431
 
356
432
  const dnslink = await Promise.any(
@@ -396,17 +472,28 @@ class DefaultIPNS implements IPNS {
396
472
  }, options.interval ?? DEFAULT_REPUBLISH_INTERVAL_MS)
397
473
  }
398
474
 
399
- async #resolve (ipfsPath: string, options: ResolveOptions = {}): Promise<CID> {
475
+ async #resolve (ipfsPath: string, options: ResolveOptions = {}): Promise<ResolveResult> {
400
476
  const parts = ipfsPath.split('/')
401
-
402
- if (parts.length === 3) {
477
+ try {
403
478
  const scheme = parts[1]
404
479
 
405
480
  if (scheme === 'ipns') {
406
- return this.resolve(peerIdFromString(parts[2]), options)
481
+ const { cid } = await this.resolve(peerIdFromString(parts[2]), options)
482
+ const path = parts.slice(3).join('/')
483
+ return {
484
+ cid,
485
+ path
486
+ }
407
487
  } else if (scheme === 'ipfs') {
408
- return CID.parse(parts[2])
488
+ const cid = CID.parse(parts[2])
489
+ const path = parts.slice(3).join('/')
490
+ return {
491
+ cid,
492
+ path
493
+ }
409
494
  }
495
+ } catch (err) {
496
+ log.error('error parsing ipfs path', err)
410
497
  }
411
498
 
412
499
  log.error('invalid ipfs path %s', ipfsPath)
@@ -484,5 +571,5 @@ export function ipns (components: IPNSComponents, { routers = [], resolvers = []
484
571
  return new DefaultIPNS(components, routers, resolvers)
485
572
  }
486
573
 
487
- export { ipnsValidator }
574
+ export { ipnsValidator, type IPNSRoutingEvents }
488
575
  export { ipnsSelector } from 'ipns/selector'