@helia/ipns 9.2.0 → 9.2.1-17530ed8

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 (69) hide show
  1. package/README.md +21 -57
  2. package/dist/index.min.js +9 -23
  3. package/dist/index.min.js.map +4 -4
  4. package/dist/src/errors.d.ts +33 -5
  5. package/dist/src/errors.d.ts.map +1 -1
  6. package/dist/src/errors.js +33 -20
  7. package/dist/src/errors.js.map +1 -1
  8. package/dist/src/index.d.ts +62 -99
  9. package/dist/src/index.d.ts.map +1 -1
  10. package/dist/src/index.js +21 -60
  11. package/dist/src/index.js.map +1 -1
  12. package/dist/src/ipns/publisher.d.ts +5 -9
  13. package/dist/src/ipns/publisher.d.ts.map +1 -1
  14. package/dist/src/ipns/publisher.js +30 -22
  15. package/dist/src/ipns/publisher.js.map +1 -1
  16. package/dist/src/ipns/republisher.d.ts +3 -5
  17. package/dist/src/ipns/republisher.d.ts.map +1 -1
  18. package/dist/src/ipns/republisher.js +18 -9
  19. package/dist/src/ipns/republisher.js.map +1 -1
  20. package/dist/src/ipns/resolver.d.ts +6 -5
  21. package/dist/src/ipns/resolver.d.ts.map +1 -1
  22. package/dist/src/ipns/resolver.js +32 -78
  23. package/dist/src/ipns/resolver.js.map +1 -1
  24. package/dist/src/ipns.d.ts +6 -4
  25. package/dist/src/ipns.d.ts.map +1 -1
  26. package/dist/src/ipns.js +33 -4
  27. package/dist/src/ipns.js.map +1 -1
  28. package/dist/src/pb/ipns.d.ts +62 -0
  29. package/dist/src/pb/ipns.d.ts.map +1 -0
  30. package/dist/src/pb/ipns.js +203 -0
  31. package/dist/src/pb/ipns.js.map +1 -0
  32. package/dist/src/pb/metadata.d.ts +1 -1
  33. package/dist/src/pb/metadata.d.ts.map +1 -1
  34. package/dist/src/records.d.ts +155 -0
  35. package/dist/src/records.d.ts.map +1 -0
  36. package/dist/src/records.js +88 -0
  37. package/dist/src/records.js.map +1 -0
  38. package/dist/src/routing/pubsub.d.ts +3 -0
  39. package/dist/src/routing/pubsub.d.ts.map +1 -1
  40. package/dist/src/routing/pubsub.js +15 -10
  41. package/dist/src/routing/pubsub.js.map +1 -1
  42. package/dist/src/selector.d.ts +14 -0
  43. package/dist/src/selector.d.ts.map +1 -0
  44. package/dist/src/selector.js +47 -0
  45. package/dist/src/selector.js.map +1 -0
  46. package/dist/src/utils.d.ts +29 -2
  47. package/dist/src/utils.d.ts.map +1 -1
  48. package/dist/src/utils.js +308 -0
  49. package/dist/src/utils.js.map +1 -1
  50. package/dist/src/validator.d.ts +18 -0
  51. package/dist/src/validator.d.ts.map +1 -0
  52. package/dist/src/validator.js +58 -0
  53. package/dist/src/validator.js.map +1 -0
  54. package/package.json +32 -28
  55. package/src/errors.ts +40 -25
  56. package/src/index.ts +63 -100
  57. package/src/ipns/publisher.ts +34 -29
  58. package/src/ipns/republisher.ts +24 -13
  59. package/src/ipns/resolver.ts +40 -88
  60. package/src/ipns.ts +44 -7
  61. package/src/pb/ipns.proto +39 -0
  62. package/src/pb/ipns.ts +280 -0
  63. package/src/pb/metadata.ts +1 -1
  64. package/src/records.ts +273 -0
  65. package/src/routing/pubsub.ts +17 -10
  66. package/src/selector.ts +55 -0
  67. package/src/utils.ts +371 -2
  68. package/src/validator.ts +67 -0
  69. package/dist/typedoc-urls.json +0 -51
package/src/utils.ts CHANGED
@@ -1,15 +1,34 @@
1
+ import { isPublicKey } from '@helia/interface'
1
2
  import { InvalidParametersError } from '@libp2p/interface'
3
+ import * as cborg from 'cborg'
2
4
  import { Key } from 'interface-datastore'
5
+ import { base36 } from 'multiformats/bases/base36'
6
+ import { base58btc } from 'multiformats/bases/base58'
7
+ import { CID } from 'multiformats/cid'
8
+ import * as Digest from 'multiformats/hashes/digest'
9
+ import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
10
+ import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
11
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
3
12
  import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
4
13
  import { DHT_EXPIRY_MS, REPUBLISH_THRESHOLD } from './constants.ts'
5
- import type { IPNSRecord } from 'ipns'
6
- import type { CID } from 'multiformats/cid'
14
+ import { InvalidEmbeddedPublicKeyError, InvalidRecordDataError, InvalidValueError, RecordTooLargeError, SignatureVerificationError, UnsupportedValidityError } from './errors.ts'
15
+ import { IpnsEntry } from './pb/ipns.ts'
16
+ import type { IPNSRecord, IPNSRecordV2, IPNSRecordData } from './records.ts'
17
+ import type { PublicKey, Keychain } from '@helia/interface'
18
+ import type { AbortOptions } from '@libp2p/interface'
19
+ import type { MultibaseDecoder } from 'multiformats/cid'
7
20
  import type { MultihashDigest } from 'multiformats/hashes/interface'
8
21
 
9
22
  export const LIBP2P_KEY_CODEC = 0x72
10
23
  export const IDENTITY_CODEC = 0x0
11
24
  export const SHA2_256_CODEC = 0x12
12
25
 
26
+ /**
27
+ * Limit valid IPNS record sizes to 10kb
28
+ */
29
+ const MAX_RECORD_SIZE = 1024 * 10
30
+
31
+ const IPNS_PREFIX = uint8ArrayFromString('/ipns/')
13
32
  export const IPNS_STRING_PREFIX = '/ipns/'
14
33
 
15
34
  export function isCodec <T extends number> (digest: MultihashDigest, codec: T): digest is MultihashDigest<T> {
@@ -78,3 +97,353 @@ export function isLibp2pCID (obj?: any): obj is CID<unknown, 0x72, 0x00 | 0x12,
78
97
 
79
98
  return true
80
99
  }
100
+
101
+ /**
102
+ * Utility for creating the record data for being signed
103
+ */
104
+ export function ipnsRecordDataForV1Sig (value: string, validityType: IpnsEntry.ValidityType, validity: Uint8Array): Uint8Array {
105
+ const validityTypeBuffer = uint8ArrayFromString(validityType)
106
+ const valueBytes = uint8ArrayFromString(value)
107
+
108
+ return uint8ArrayConcat([
109
+ valueBytes,
110
+ validity,
111
+ validityTypeBuffer
112
+ ], valueBytes.byteLength + validity.byteLength + validityTypeBuffer.byteLength)
113
+ }
114
+
115
+ /**
116
+ * Utility for creating the record data for being signed
117
+ */
118
+ export function ipnsRecordDataForV2Sig (data: Uint8Array): Uint8Array {
119
+ const entryData = uint8ArrayFromString('ipns-signature:')
120
+
121
+ return uint8ArrayConcat([entryData, data])
122
+ }
123
+
124
+ export function marshalIPNSRecord (obj: IPNSRecord | IPNSRecordV2): Uint8Array {
125
+ let publicKey: Uint8Array | undefined = obj.publicKey?.toProtobuf()
126
+
127
+ // do not embed public keys whose multihash is an identity hash as these can
128
+ // be derived from the routing key
129
+ if (obj.publicKey?.toMultihash().code === 0x00) {
130
+ publicKey = undefined
131
+ }
132
+
133
+ if ('signatureV1' in obj) {
134
+ return IpnsEntry.encode({
135
+ value: uint8ArrayFromString(obj.value),
136
+ signatureV1: obj.signatureV1,
137
+ validityType: obj.validityType,
138
+ validity: uint8ArrayFromString(obj.validity),
139
+ sequence: obj.sequence,
140
+ ttl: obj.ttl,
141
+ publicKey,
142
+ signatureV2: obj.signatureV2,
143
+ data: obj.data
144
+ })
145
+ } else {
146
+ return IpnsEntry.encode({
147
+ publicKey,
148
+ signatureV2: obj.signatureV2,
149
+ data: obj.data
150
+ })
151
+ }
152
+ }
153
+
154
+ function valueToString (value: Uint8Array): string {
155
+ // handle legacy case where record value is raw CID bytes
156
+ try {
157
+ const cid = CID.decode(value)
158
+ return `/ipfs/${cid}`
159
+ } catch {
160
+ // ignore error
161
+ }
162
+
163
+ return uint8ArrayToString(value)
164
+ }
165
+
166
+ export async function unmarshalIPNSRecord (routingKey: Uint8Array, marshalledRecord: Uint8Array, keychain: Keychain, options?: AbortOptions): Promise<IPNSRecord> {
167
+ if (marshalledRecord.byteLength > MAX_RECORD_SIZE) {
168
+ throw new RecordTooLargeError('The record is too large')
169
+ }
170
+
171
+ const message = IpnsEntry.decode(marshalledRecord)
172
+
173
+ // Check if we have the data field. If we don't, we fail. We've been producing
174
+ // V1+V2 records for quite a while and we don't support V1-only records during
175
+ // validation any more
176
+ if (message.signatureV2 == null || message.data == null) {
177
+ throw new SignatureVerificationError('Missing data or signatureV2')
178
+ }
179
+
180
+ const data = parseCborData(message.data)
181
+ const validity = uint8ArrayToString(data.Validity)
182
+
183
+ let publicKey: PublicKey | undefined
184
+
185
+ // try to extract public key from routing key
186
+ const routingMultihash = multihashFromIPNSRoutingKey(routingKey)
187
+
188
+ // identity hash
189
+ if (isCodec(routingMultihash, 0x0)) {
190
+ publicKey = await keychain.loadPublicKeyFromProtobuf(routingMultihash.digest, options)
191
+ }
192
+
193
+ // otherwise try to load key from message
194
+ if (publicKey == null && message.publicKey != null) {
195
+ publicKey = await keychain.loadPublicKeyFromProtobuf(message.publicKey, options)
196
+ }
197
+
198
+ if (publicKey == null) {
199
+ throw new InvalidEmbeddedPublicKeyError('Could not extract public key from IPNS record or routing key')
200
+ }
201
+
202
+ if (message.value != null && message.signatureV1 != null) {
203
+ // V1+V2
204
+ validateCborDataMatchesPbData(message)
205
+
206
+ return {
207
+ value: valueToString(data.Value),
208
+ validityType: IpnsEntry.ValidityType.EOL,
209
+ validity,
210
+ sequence: data.Sequence,
211
+ ttl: data.TTL,
212
+ publicKey,
213
+ signatureV1: message.signatureV1,
214
+ signatureV2: message.signatureV2,
215
+ data: message.data,
216
+ bytes: marshalledRecord
217
+ }
218
+ } else if (message.signatureV2 != null) {
219
+ // V2-only
220
+ return {
221
+ value: valueToString(data.Value),
222
+ validityType: IpnsEntry.ValidityType.EOL,
223
+ validity,
224
+ sequence: data.Sequence,
225
+ ttl: data.TTL,
226
+ publicKey,
227
+ signatureV2: message.signatureV2,
228
+ data: message.data,
229
+ bytes: marshalledRecord
230
+ }
231
+ } else {
232
+ throw new Error('invalid record: does not include signatureV1 or signatureV2')
233
+ }
234
+ }
235
+
236
+ export function multihashToIPNSRoutingKey (digest: MultihashDigest): Uint8Array {
237
+ return uint8ArrayConcat([
238
+ IPNS_PREFIX,
239
+ digest.bytes
240
+ ])
241
+ }
242
+
243
+ export function multihashFromIPNSRoutingKey (key: Uint8Array): MultihashDigest {
244
+ return Digest.decode(key.slice(IPNS_PREFIX.length))
245
+ }
246
+
247
+ export function createCborData (value: string, validityType: IpnsEntry.ValidityType, validity: Uint8Array, sequence: bigint, ttl: bigint): Uint8Array {
248
+ let ValidityType
249
+
250
+ if (validityType === IpnsEntry.ValidityType.EOL) {
251
+ ValidityType = 0
252
+ } else {
253
+ throw new UnsupportedValidityError('The validity type is unsupported')
254
+ }
255
+
256
+ const data = {
257
+ Value: uint8ArrayFromString(value),
258
+ Validity: validity,
259
+ ValidityType,
260
+ Sequence: sequence,
261
+ TTL: ttl
262
+ }
263
+
264
+ return cborg.encode(data)
265
+ }
266
+
267
+ export function parseCborData (buf: Uint8Array): IPNSRecordData {
268
+ const data = cborg.decode(buf)
269
+
270
+ if (data.ValidityType === 0) {
271
+ data.ValidityType = IpnsEntry.ValidityType.EOL
272
+ } else {
273
+ throw new UnsupportedValidityError('The validity type is unsupported')
274
+ }
275
+
276
+ if (Number.isInteger(data.Sequence)) {
277
+ // sequence must be a BigInt, but DAG-CBOR doesn't preserve this for Numbers within the safe-integer range
278
+ data.Sequence = BigInt(data.Sequence)
279
+ }
280
+
281
+ if (Number.isInteger(data.TTL)) {
282
+ // ttl must be a BigInt, but DAG-CBOR doesn't preserve this for Numbers within the safe-integer range
283
+ data.TTL = BigInt(data.TTL)
284
+ }
285
+
286
+ return data
287
+ }
288
+
289
+ /**
290
+ * Normalizes the given record value. It ensures it is a PeerID, a CID or a
291
+ * string starting with '/'. PeerIDs become `/ipns/${cidV1Libp2pKey}`,
292
+ * CIDs become `/ipfs/${cidAsV1}`.
293
+ */
294
+ export function normalizeValue (value?: PublicKey | CID | MultihashDigest | string): string {
295
+ if (value != null) {
296
+ if (isPublicKey(value)) {
297
+ return `/ipns/${value.toCID().toV1().toString(base36)}`
298
+ }
299
+
300
+ const cid = asCID(value)
301
+
302
+ // if we have a CID, turn it into an ipfs path
303
+ if (cid != null) {
304
+ // PeerID encoded as a CID
305
+ if (cid.code === LIBP2P_KEY_CODEC) {
306
+ return `/ipns/${cid.toV1().toString(base36)}`
307
+ }
308
+
309
+ return `/ipfs/${cid.toV1()}`
310
+ }
311
+
312
+ if (hasBytes(value)) {
313
+ return `/ipns/${base36.encode(value.bytes)}`
314
+ }
315
+
316
+ // if we have a path, check it is a valid path
317
+ const string = value.toString().trim()
318
+
319
+ if (string.startsWith('/ipfs/')) {
320
+ const [, name, ...rest] = string.split('/')
321
+ .filter(component => component.trim() !== '')
322
+
323
+ return `/ipfs/${CID.parse(name).toV1()}${rest.length > 0 ? `/${rest.join('/')}` : ''}`
324
+ }
325
+
326
+ if (string.startsWith('/') && string.length > 1) {
327
+ return string
328
+ }
329
+ }
330
+
331
+ throw new InvalidValueError('Value must be a valid content path starting with /')
332
+ }
333
+
334
+ function isMultihashDigest (obj: any): obj is MultihashDigest {
335
+ return typeof obj.code === 'number' && obj.digest instanceof Uint8Array && typeof obj.size === 'number' && obj.bytes instanceof Uint8Array
336
+ }
337
+
338
+ export function normalizeKey (key?: PublicKey | CID<unknown, 0x72> | MultihashDigest | string): { digest: MultihashDigest, path: string } {
339
+ if (key != null) {
340
+ if (isPublicKey(key)) {
341
+ return {
342
+ digest: key.toMultihash(),
343
+ path: '/'
344
+ }
345
+ }
346
+
347
+ const cid = asCID(key)
348
+
349
+ // if we have a CID, turn it into an ipfs path
350
+ if (cid != null) {
351
+ // PeerID encoded as a CID
352
+ if (cid.code !== LIBP2P_KEY_CODEC) {
353
+ throw new InvalidValueError('CIDs must have the `libp2p-key` codec')
354
+ }
355
+
356
+ return {
357
+ digest: cid.multihash,
358
+ path: '/'
359
+ }
360
+ }
361
+
362
+ if (isMultihashDigest(key)) {
363
+ return {
364
+ digest: key,
365
+ path: '/'
366
+ }
367
+ }
368
+
369
+ key = key.toString()
370
+
371
+ if (key.startsWith('/ipns/')) {
372
+ let [,, name, ...rest] = key.split('/')
373
+ let codec: MultibaseDecoder<any> = base36
374
+
375
+ // base58btc encoded public key hash or protobuf in identity hash
376
+ if (name.startsWith('1') || name.startsWith('Q')) {
377
+ name = `z${name}`
378
+ codec = base58btc
379
+ }
380
+
381
+ const buf = codec.decode(name)
382
+ let digest: MultihashDigest
383
+
384
+ try {
385
+ digest = CID.decode(buf).multihash
386
+ } catch {
387
+ digest = Digest.decode(buf)
388
+ }
389
+
390
+ return {
391
+ digest,
392
+ path: `/${rest.join('/')}`
393
+ }
394
+ }
395
+ }
396
+
397
+ throw new InvalidValueError('Value must be a valid IPNS path starting with /')
398
+ }
399
+
400
+ function validateCborDataMatchesPbData (entry: IpnsEntry): void {
401
+ if (entry.data == null) {
402
+ throw new InvalidRecordDataError('Record data is missing')
403
+ }
404
+
405
+ const data = parseCborData(entry.data)
406
+
407
+ if (!uint8ArrayEquals(data.Value, entry.value ?? new Uint8Array(0))) {
408
+ throw new SignatureVerificationError('Field "value" did not match between protobuf and CBOR')
409
+ }
410
+
411
+ if (!uint8ArrayEquals(data.Validity, entry.validity ?? new Uint8Array(0))) {
412
+ throw new SignatureVerificationError('Field "validity" did not match between protobuf and CBOR')
413
+ }
414
+
415
+ if (data.ValidityType !== entry.validityType) {
416
+ throw new SignatureVerificationError('Field "validityType" did not match between protobuf and CBOR')
417
+ }
418
+
419
+ if (data.Sequence !== entry.sequence) {
420
+ throw new SignatureVerificationError('Field "sequence" did not match between protobuf and CBOR')
421
+ }
422
+
423
+ if (data.TTL !== entry.ttl) {
424
+ throw new SignatureVerificationError('Field "ttl" did not match between protobuf and CBOR')
425
+ }
426
+ }
427
+
428
+ function hasBytes (obj?: any): obj is { bytes: Uint8Array } {
429
+ return obj.bytes instanceof Uint8Array
430
+ }
431
+
432
+ function hasToCID (obj?: any): obj is { toCID(): CID } {
433
+ return typeof obj?.toCID === 'function'
434
+ }
435
+
436
+ function asCID (obj?: any): CID | null {
437
+ if (hasToCID(obj)) {
438
+ return obj.toCID()
439
+ }
440
+
441
+ // try parsing as a CID string
442
+ try {
443
+ return CID.parse(obj)
444
+ } catch {
445
+ // fall through
446
+ }
447
+
448
+ return CID.asCID(obj)
449
+ }
@@ -0,0 +1,67 @@
1
+ import NanoDate from 'timestamp-nano'
2
+ import { InvalidEmbeddedPublicKeyError, RecordExpiredError, SignatureVerificationError, UnsupportedValidityError } from './errors.ts'
3
+ import { IpnsEntry } from './pb/ipns.ts'
4
+ import { ipnsRecordDataForV2Sig } from './utils.ts'
5
+ import type { IPNSRecord } from './index.ts'
6
+ import type { AbortOptions } from '@libp2p/interface'
7
+
8
+ /**
9
+ * Validate the given IPNS record against the given routing key.
10
+ *
11
+ * @see https://specs.ipfs.tech/ipns/ipns-record/#routing-record for the binary
12
+ * format of the routing key
13
+ */
14
+ export async function ipnsValidator (record: IPNSRecord, options?: AbortOptions): Promise<void> {
15
+ if (record.publicKey == null) {
16
+ throw new InvalidEmbeddedPublicKeyError('The record had no public key associated with it')
17
+ }
18
+
19
+ // Validate Signature V2
20
+ let isValid
21
+
22
+ try {
23
+ const dataForSignature = ipnsRecordDataForV2Sig(record.data)
24
+ isValid = await record.publicKey.verify(dataForSignature, record.signatureV2, options)
25
+ } catch {
26
+ isValid = false
27
+ }
28
+
29
+ if (!isValid) {
30
+ throw new SignatureVerificationError('Record signature verification failed')
31
+ }
32
+
33
+ // Validate according to the validity type
34
+ if (record.validityType === IpnsEntry.ValidityType.EOL) {
35
+ if (NanoDate.fromString(record.validity).toDate().getTime() < Date.now()) {
36
+ throw new RecordExpiredError('record has expired')
37
+ }
38
+ } else if (record.validityType != null) {
39
+ throw new UnsupportedValidityError('The validity type is unsupported')
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Returns the number of milliseconds until the record expires.
45
+ * If the record is already expired, returns 0.
46
+ *
47
+ * @param record - The IPNS record to validate.
48
+ * @returns The number of milliseconds until the record expires, or 0 if the record is already expired.
49
+ */
50
+ export function validFor (record: IPNSRecord): number {
51
+ if (record.validityType !== IpnsEntry.ValidityType.EOL) {
52
+ throw new UnsupportedValidityError()
53
+ }
54
+
55
+ if (record.validity == null) {
56
+ throw new UnsupportedValidityError()
57
+ }
58
+
59
+ const validUntil = NanoDate.fromString(record.validity).toDate().getTime()
60
+ const now = Date.now()
61
+
62
+ if (validUntil < now) {
63
+ return 0
64
+ }
65
+
66
+ return validUntil - now
67
+ }
@@ -1,51 +0,0 @@
1
- {
2
- "IPNS": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNS.html",
3
- ".:IPNS": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNS.html",
4
- "IPNSComponents": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNSComponents.html",
5
- ".:IPNSComponents": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNSComponents.html",
6
- "IPNSOptions": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNSOptions.html",
7
- ".:IPNSOptions": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNSOptions.html",
8
- "IPNSPublishResult": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNSPublishResult.html",
9
- ".:IPNSPublishResult": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNSPublishResult.html",
10
- "IPNSRecordMetadata": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNSRecordMetadata.html",
11
- ".:IPNSRecordMetadata": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNSRecordMetadata.html",
12
- "IPNSResolver": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNSResolver.html",
13
- ".:IPNSResolver": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNSResolver.html",
14
- "IPNSResolveResult": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNSResolveResult.html",
15
- ".:IPNSResolveResult": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNSResolveResult.html",
16
- "IPNSResolverOptions": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNSResolverOptions.html",
17
- ".:IPNSResolverOptions": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.IPNSResolverOptions.html",
18
- "PublishOptions": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.PublishOptions.html",
19
- ".:PublishOptions": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.PublishOptions.html",
20
- "ResolveOptions": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.ResolveOptions.html",
21
- ".:ResolveOptions": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.ResolveOptions.html",
22
- "ResolveResult": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.ResolveResult.html",
23
- ".:ResolveResult": "https://ipfs.github.io/helia/interfaces/_helia_ipns.index.ResolveResult.html",
24
- "DatastoreProgressEvents": "https://ipfs.github.io/helia/types/_helia_ipns.index.DatastoreProgressEvents.html",
25
- ".:DatastoreProgressEvents": "https://ipfs.github.io/helia/types/_helia_ipns.index.DatastoreProgressEvents.html",
26
- "PublishProgressEvents": "https://ipfs.github.io/helia/types/_helia_ipns.index.PublishProgressEvents.html",
27
- ".:PublishProgressEvents": "https://ipfs.github.io/helia/types/_helia_ipns.index.PublishProgressEvents.html",
28
- "ResolveProgressEvents": "https://ipfs.github.io/helia/types/_helia_ipns.index.ResolveProgressEvents.html",
29
- ".:ResolveProgressEvents": "https://ipfs.github.io/helia/types/_helia_ipns.index.ResolveProgressEvents.html",
30
- "ipns": "https://ipfs.github.io/helia/functions/_helia_ipns.index.ipns.html",
31
- ".:ipns": "https://ipfs.github.io/helia/functions/_helia_ipns.index.ipns.html",
32
- "ipnsResolver": "https://ipfs.github.io/helia/functions/_helia_ipns.index.ipnsResolver.html",
33
- ".:ipnsResolver": "https://ipfs.github.io/helia/functions/_helia_ipns.index.ipnsResolver.html",
34
- "GetOptions": "https://ipfs.github.io/helia/interfaces/_helia_ipns.routing.GetOptions.html",
35
- "./routing:GetOptions": "https://ipfs.github.io/helia/interfaces/_helia_ipns.routing.GetOptions.html",
36
- "IPNSRouting": "https://ipfs.github.io/helia/interfaces/_helia_ipns.routing.IPNSRouting.html",
37
- "./routing:IPNSRouting": "https://ipfs.github.io/helia/interfaces/_helia_ipns.routing.IPNSRouting.html",
38
- "Message": "https://ipfs.github.io/helia/interfaces/_helia_ipns.routing.Message.html",
39
- "PublishResult": "https://ipfs.github.io/helia/interfaces/_helia_ipns.routing.PublishResult.html",
40
- "PubSub": "https://ipfs.github.io/helia/interfaces/_helia_ipns.routing.PubSub.html",
41
- "PubSubEvents": "https://ipfs.github.io/helia/interfaces/_helia_ipns.routing.PubSubEvents.html",
42
- "PubsubRoutingComponents": "https://ipfs.github.io/helia/interfaces/_helia_ipns.routing.PubsubRoutingComponents.html",
43
- "PutOptions": "https://ipfs.github.io/helia/interfaces/_helia_ipns.routing.PutOptions.html",
44
- "./routing:PutOptions": "https://ipfs.github.io/helia/interfaces/_helia_ipns.routing.PutOptions.html",
45
- "HeliaRoutingProgressEvents": "https://ipfs.github.io/helia/types/_helia_ipns.routing.HeliaRoutingProgressEvents.html",
46
- "IPNSRoutingProgressEvents": "https://ipfs.github.io/helia/types/_helia_ipns.routing.IPNSRoutingProgressEvents.html",
47
- "./routing:IPNSRoutingProgressEvents": "https://ipfs.github.io/helia/types/_helia_ipns.routing.IPNSRoutingProgressEvents.html",
48
- "PubSubProgressEvents": "https://ipfs.github.io/helia/types/_helia_ipns.routing.PubSubProgressEvents.html",
49
- "helia": "https://ipfs.github.io/helia/functions/_helia_ipns.routing.helia.html",
50
- "pubsub": "https://ipfs.github.io/helia/functions/_helia_ipns.routing.pubsub.html"
51
- }