@helia/ipns 10.0.0 → 10.0.1-883ff053

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 (84) hide show
  1. package/README.md +3 -7
  2. package/dist/index.min.js +16 -5
  3. package/dist/index.min.js.map +4 -4
  4. package/dist/src/errors.d.ts +0 -8
  5. package/dist/src/errors.d.ts.map +1 -1
  6. package/dist/src/errors.js +0 -8
  7. package/dist/src/errors.js.map +1 -1
  8. package/dist/src/index.d.ts +68 -31
  9. package/dist/src/index.d.ts.map +1 -1
  10. package/dist/src/index.js +11 -11
  11. package/dist/src/index.js.map +1 -1
  12. package/dist/src/ipns/publisher.d.ts +2 -2
  13. package/dist/src/ipns/publisher.d.ts.map +1 -1
  14. package/dist/src/ipns/publisher.js +27 -14
  15. package/dist/src/ipns/publisher.js.map +1 -1
  16. package/dist/src/ipns/republisher.d.ts.map +1 -1
  17. package/dist/src/ipns/republisher.js +22 -9
  18. package/dist/src/ipns/republisher.js.map +1 -1
  19. package/dist/src/ipns/resolver.d.ts +2 -2
  20. package/dist/src/ipns/resolver.d.ts.map +1 -1
  21. package/dist/src/ipns/resolver.js +39 -17
  22. package/dist/src/ipns/resolver.js.map +1 -1
  23. package/dist/src/ipns.d.ts +3 -3
  24. package/dist/src/ipns.d.ts.map +1 -1
  25. package/dist/src/ipns.js +8 -8
  26. package/dist/src/ipns.js.map +1 -1
  27. package/dist/src/local-store.d.ts +4 -4
  28. package/dist/src/local-store.d.ts.map +1 -1
  29. package/dist/src/local-store.js +2 -1
  30. package/dist/src/local-store.js.map +1 -1
  31. package/dist/src/pb/ipns.d.ts +29 -29
  32. package/dist/src/pb/ipns.d.ts.map +1 -1
  33. package/dist/src/pb/ipns.js +15 -15
  34. package/dist/src/records.d.ts +27 -142
  35. package/dist/src/records.d.ts.map +1 -1
  36. package/dist/src/records.js +39 -46
  37. package/dist/src/records.js.map +1 -1
  38. package/dist/src/routing/helia.d.ts +5 -5
  39. package/dist/src/routing/helia.d.ts.map +1 -1
  40. package/dist/src/routing/helia.js +3 -3
  41. package/dist/src/routing/helia.js.map +1 -1
  42. package/dist/src/routing/index.d.ts +7 -7
  43. package/dist/src/routing/index.d.ts.map +1 -1
  44. package/dist/src/routing/index.js +2 -2
  45. package/dist/src/routing/index.js.map +1 -1
  46. package/dist/src/routing/local-store.d.ts +1 -1
  47. package/dist/src/routing/local-store.d.ts.map +1 -1
  48. package/dist/src/routing/local-store.js +3 -3
  49. package/dist/src/routing/local-store.js.map +1 -1
  50. package/dist/src/routing/pubsub.d.ts +11 -11
  51. package/dist/src/routing/pubsub.d.ts.map +1 -1
  52. package/dist/src/routing/pubsub.js +11 -12
  53. package/dist/src/routing/pubsub.js.map +1 -1
  54. package/dist/src/selector.d.ts +2 -2
  55. package/dist/src/selector.d.ts.map +1 -1
  56. package/dist/src/selector.js +13 -9
  57. package/dist/src/selector.js.map +1 -1
  58. package/dist/src/utils.d.ts +10 -11
  59. package/dist/src/utils.d.ts.map +1 -1
  60. package/dist/src/utils.js +20 -128
  61. package/dist/src/utils.js.map +1 -1
  62. package/dist/src/validator.d.ts +9 -3
  63. package/dist/src/validator.d.ts.map +1 -1
  64. package/dist/src/validator.js +57 -14
  65. package/dist/src/validator.js.map +1 -1
  66. package/package.json +6 -31
  67. package/src/errors.ts +0 -10
  68. package/src/index.ts +85 -36
  69. package/src/ipns/publisher.ts +29 -16
  70. package/src/ipns/republisher.ts +31 -13
  71. package/src/ipns/resolver.ts +40 -22
  72. package/src/ipns.ts +11 -11
  73. package/src/local-store.ts +8 -7
  74. package/src/pb/ipns.proto +1 -1
  75. package/src/pb/ipns.ts +37 -37
  76. package/src/records.ts +57 -210
  77. package/src/routing/helia.ts +6 -6
  78. package/src/routing/index.ts +7 -7
  79. package/src/routing/local-store.ts +6 -6
  80. package/src/routing/pubsub.ts +24 -25
  81. package/src/selector.ts +14 -11
  82. package/src/utils.ts +26 -148
  83. package/src/validator.ts +73 -16
  84. package/dist/typedoc-urls.json +0 -49
package/src/records.ts CHANGED
@@ -2,259 +2,106 @@ import { logger } from '@libp2p/logger'
2
2
  import NanoDate from 'timestamp-nano'
3
3
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
4
4
  import { SignatureCreationError } from './errors.ts'
5
- import { IpnsEntry } from './pb/ipns.ts'
6
- import { createCborData, ipnsRecordDataForV1Sig, ipnsRecordDataForV2Sig, marshalIPNSRecord } from './utils.ts'
5
+ import { IPNSEntry } from './pb/ipns.ts'
6
+ import { encodeExtensibleData, IDENTITY_CODEC, ipnsRecordDataForV1Sig, ipnsRecordDataForV2Sig } from './utils.ts'
7
7
  import type { PrivateKey, PublicKey } from '@helia/interface'
8
8
  import type { AbortOptions } from 'abort-error'
9
- import type { Key } from 'interface-datastore/key'
10
9
 
11
10
  const log = logger('ipns')
12
- const DEFAULT_TTL_NS = 5 * 60 * 1e+9 // 5 Minutes or 300 Seconds, as suggested by https://specs.ipfs.tech/ipns/ipns-record/#ttl-uint64
13
-
14
- export const namespace = '/ipns/'
15
- export const namespaceLength = namespace.length
16
-
17
- export interface IPNSRecordV1V2 {
18
- /**
19
- * value of the record
20
- */
21
- value: string
22
-
23
- /**
24
- * signature of the record
25
- */
26
- signatureV1: Uint8Array
27
-
28
- /**
29
- * Type of validation being used
30
- */
31
- validityType: IpnsEntry.ValidityType
32
-
33
- /**
34
- * expiration datetime for the record in RFC3339 format
35
- */
36
- validity: string
37
-
38
- /**
39
- * number representing the version of the record
40
- */
41
- sequence: bigint
42
-
43
- /**
44
- * ttl in nanoseconds
45
- */
46
- ttl?: bigint
47
-
48
- /**
49
- * the public portion of the key that signed this record
50
- */
51
- publicKey: PublicKey
52
-
53
- /**
54
- * the v2 signature of the record
55
- */
56
- signatureV2: Uint8Array
57
-
58
- /**
59
- * extensible data
60
- */
61
- data: Uint8Array
62
-
63
- /**
64
- * The marshalled record
65
- */
66
- bytes: Uint8Array
67
- }
68
-
69
- export interface IPNSRecordV2 {
70
- /**
71
- * value of the record
72
- */
73
- value: string
74
-
75
- /**
76
- * the v2 signature of the record
77
- */
78
- signatureV2: Uint8Array
79
-
80
- /**
81
- * Type of validation being used
82
- */
83
- validityType: IpnsEntry.ValidityType
84
-
85
- /**
86
- * If the validity type is EOL, this is the expiration datetime for the record
87
- * in RFC3339 format
88
- */
89
- validity: string
11
+ const DEFAULT_TTL_NS = 300_000_000_000n // 5 Minutes or 300 Seconds, as suggested by https://specs.ipfs.tech/ipns/ipns-record/#ttl-uint64
90
12
 
13
+ export interface CreateIPNSRecordOptions extends AbortOptions {
91
14
  /**
92
- * number representing the version of the record
15
+ * By default a IPNS V1 and a V2 signature is added to every record. Pass
16
+ * false here to only add a V2 signature.
17
+ *
18
+ * @default true
93
19
  */
94
- sequence: bigint
95
-
96
- /**
97
- * ttl in nanoseconds
98
- */
99
- ttl?: bigint
100
-
101
- /**
102
- * the public portion of the key that signed this record
103
- */
104
- publicKey: PublicKey
20
+ v1Compatible?: boolean
105
21
 
106
22
  /**
107
- * extensible data
23
+ * The TTL of the record in ms - after this many ms have expired, resolving
24
+ * the record will query the routing for an updated version.
25
+ *
26
+ * Before this many ms have expired any locally stored copy will be treated as
27
+ * the latest version and the routing will not be queried.
28
+ *
29
+ * @default 300_000_000_000n
108
30
  */
109
- data: Uint8Array
31
+ ttlNs?: bigint
110
32
 
111
33
  /**
112
- * The marshalled record
34
+ * Extensible data that will be added to the IPNS record data and signed to
35
+ * verify it's integrity.
36
+ *
37
+ * Note that this data will be encoded as DAG-CBOR so it must be valid.
113
38
  */
114
- bytes: Uint8Array
115
- }
116
-
117
- export type IPNSRecord = IPNSRecordV1V2 | IPNSRecordV2
118
-
119
- export interface IPNSRecordData {
120
- Value: Uint8Array
121
- Validity: Uint8Array
122
- ValidityType: IpnsEntry.ValidityType
123
- Sequence: bigint
124
- TTL: bigint
125
- }
126
-
127
- export interface IDKeys {
128
- routingPubKey: Key
129
- pkKey: Key
130
- routingKey: Key
131
- ipnsKey: Key
132
- }
133
-
134
- export interface CreateOptions extends AbortOptions {
135
- ttlNs?: number | bigint
136
- v1Compatible?: boolean
137
- }
138
-
139
- export interface CreateV2OrV1Options extends AbortOptions {
140
- v1Compatible: true
141
- }
142
-
143
- export interface CreateV2Options extends AbortOptions {
144
- v1Compatible: false
145
- }
146
-
147
- const defaultCreateOptions: CreateOptions = {
148
- v1Compatible: true,
149
- ttlNs: DEFAULT_TTL_NS
39
+ data?: Record<string, any>
150
40
  }
151
41
 
152
42
  /**
153
- * Creates a new IPNS record and signs it with the given private key.
154
- * The IPNS Record validity should follow the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt} with nanoseconds precision.
155
- * Note: This function does not embed the public key. If you want to do that, use `EmbedPublicKey`.
156
- *
157
- * The passed value can be a CID, a PublicKey or an arbitrary string path e.g. `/ipfs/...` or `/ipns/...`.
43
+ * A low-level function that creates a new IPNS record and signs it with the
44
+ * passed private key.
158
45
  *
159
- * CIDs will be converted to v1 and stored in the record as a string similar to: `/ipfs/${cid}`
160
- * PublicKeys will create recursive records, eg. the record value will be `/ipns/${cidV1Libp2pKey}`
161
- * String paths will be stored in the record as-is, but they must start with `"/"`
46
+ * The IPNS Record validity should follow the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt}
47
+ * with nanosecond precision.
162
48
  *
163
- * @param {PrivateKey} privateKey - the private key for signing the record.
164
- * @param {CID | PublicKey | string} value - content to be stored in the record.
165
- * @param {number | bigint} seq - number representing the current version of the record.
166
- * @param {number} lifetime - lifetime of the record (in milliseconds).
167
- * @param {CreateOptions} options - additional create options.
49
+ * The passed value should be a string path e.g. `/ipfs/...` or `/ipns/...`.
168
50
  */
169
- export async function createIPNSRecord (privateKey: PrivateKey, value: string, seq: number | bigint, lifetime: number, options?: CreateV2OrV1Options): Promise<IPNSRecordV1V2>
170
- export async function createIPNSRecord (privateKey: PrivateKey, value: string, seq: number | bigint, lifetime: number, options: CreateV2Options): Promise<IPNSRecordV2>
171
- export async function createIPNSRecord (privateKey: PrivateKey, value: string, seq: number | bigint, lifetime: number, options: CreateOptions): Promise<IPNSRecordV1V2>
172
- export async function createIPNSRecord (privateKey: PrivateKey, value: string, seq: number | bigint, lifetime: number, options: CreateOptions = defaultCreateOptions): Promise<IPNSRecord> {
51
+ export async function createIPNSRecord (privateKey: PrivateKey, val: string, seq: number | bigint, lifetime: number, options?: CreateIPNSRecordOptions): Promise<IPNSEntry> {
52
+ seq = BigInt(seq)
53
+ const value = uint8ArrayFromString(val)
54
+ // convert ttl from milliseconds to nanoseconds as createIPNSRecord expects
55
+ const ttlNs = options?.ttlNs ?? DEFAULT_TTL_NS
56
+
173
57
  // Validity in ISOString with nanoseconds precision and validity type EOL
174
58
  const expirationDate = new NanoDate(Date.now() + Number(lifetime))
175
- const validityType = IpnsEntry.ValidityType.EOL
176
- const ttlNs = BigInt(options.ttlNs ?? DEFAULT_TTL_NS)
177
-
178
- return _create(privateKey, value, seq, validityType, expirationDate.toString(), ttlNs, options)
179
- }
180
-
181
- /**
182
- * Same as create(), but instead of generating a new Date, it receives the intended expiration time
183
- * WARNING: nano precision is not standard, make sure the value in seconds is 9 orders of magnitude lesser than the one provided.
184
- *
185
- * The passed value can be a CID, a PublicKey or an arbitrary string path e.g. `/ipfs/...` or `/ipns/...`.
186
- *
187
- * CIDs will be converted to v1 and stored in the record as a string similar to: `/ipfs/${cid}`
188
- * PublicKeys will create recursive records, eg. the record value will be `/ipns/${cidV1Libp2pKey}`
189
- * String paths will be stored in the record as-is, but they must start with `"/"`
190
- *
191
- * @param {PrivateKey} privateKey - the private key for signing the record.
192
- * @param {CID | PublicKey | string} value - content to be stored in the record.
193
- * @param {number | bigint} seq - number representing the current version of the record.
194
- * @param {string} expiration - expiration datetime for record in the [RFC3339]{@link https://www.ietf.org/rfc/rfc3339.txt} with nanoseconds precision.
195
- * @param {CreateOptions} options - additional creation options.
196
- */
197
- export async function createIPNSRecordWithExpiration (privateKey: PrivateKey, value: string, seq: number | bigint, expiration: string, options?: CreateV2OrV1Options): Promise<IPNSRecordV1V2>
198
- export async function createIPNSRecordWithExpiration (privateKey: PrivateKey, value: string, seq: number | bigint, expiration: string, options: CreateV2Options): Promise<IPNSRecordV2>
199
- export async function createIPNSRecordWithExpiration (privateKey: PrivateKey, value: string, seq: number | bigint, expiration: string, options: CreateOptions): Promise<IPNSRecordV1V2>
200
- export async function createIPNSRecordWithExpiration (privateKey: PrivateKey, value: string, seq: number | bigint, expiration: string, options: CreateOptions = defaultCreateOptions): Promise<IPNSRecord> {
201
- const expirationDate = NanoDate.fromString(expiration)
202
- const validityType = IpnsEntry.ValidityType.EOL
203
- const ttlNs = BigInt(options.ttlNs ?? DEFAULT_TTL_NS)
204
-
205
- return _create(privateKey, value, seq, validityType, expirationDate.toString(), ttlNs, options)
206
- }
59
+ const validityType = IPNSEntry.ValidityType.EOL
60
+ const validity = uint8ArrayFromString(expirationDate.toString())
61
+
62
+ const data = encodeExtensibleData({
63
+ ...(options?.data ?? {}),
64
+ Value: value,
65
+ Validity: validity,
66
+ // @ts-expect-error should be a number
67
+ ValidityType: 0,
68
+ Sequence: seq,
69
+ TTL: ttlNs
70
+ })
207
71
 
208
- const _create = async (privateKey: PrivateKey, value: string, seq: number | bigint, validityType: IpnsEntry.ValidityType, validity: string, ttl: bigint, options: CreateOptions = defaultCreateOptions): Promise<IPNSRecord> => {
209
- seq = BigInt(seq)
210
- const isoValidity = uint8ArrayFromString(validity)
211
- const data = createCborData(value, validityType, isoValidity, seq, ttl)
212
72
  const sigData = ipnsRecordDataForV2Sig(data)
213
73
  const signatureV2 = await privateKey.sign(sigData, options)
214
- const publicKey = shouldEmbedPublicKey(privateKey.publicKey) ? privateKey.publicKey : undefined
215
- let record: any
216
-
217
- if (options.v1Compatible === true) {
218
- const signatureV1 = await signLegacyV1(privateKey, value, validityType, isoValidity)
74
+ let record: IPNSEntry
219
75
 
76
+ if (options?.v1Compatible === false) {
220
77
  record = {
221
- value,
222
- signatureV1,
223
- validity,
224
- validityType,
225
- sequence: seq,
226
- ttl,
227
78
  signatureV2,
228
- data,
229
- publicKey
79
+ data
230
80
  }
231
81
  } else {
232
82
  record = {
233
83
  value,
234
- validity,
84
+ signatureV1: await signLegacyV1(privateKey, value, validityType, validity),
235
85
  validityType,
86
+ validity,
236
87
  sequence: seq,
237
- ttl,
88
+ ttl: ttlNs,
238
89
  signatureV2,
239
- data,
240
- publicKey
90
+ data
241
91
  }
242
92
  }
243
93
 
244
- record.bytes = marshalIPNSRecord(record)
94
+ if (shouldEmbedPublicKey(privateKey.publicKey)) {
95
+ record.publicKey = privateKey.publicKey.toProtobuf()
96
+ }
245
97
 
246
98
  return record
247
99
  }
248
100
 
249
- export { unmarshalIPNSRecord } from './utils.ts'
250
- export { marshalIPNSRecord } from './utils.ts'
251
- export { multihashToIPNSRoutingKey } from './utils.ts'
252
- export { multihashFromIPNSRoutingKey } from './utils.ts'
253
-
254
101
  /**
255
102
  * Sign ipns record data using the legacy V1 signature scheme
256
103
  */
257
- const signLegacyV1 = async (privateKey: PrivateKey, value: string, validityType: IpnsEntry.ValidityType, validity: Uint8Array, options?: AbortOptions): Promise<Uint8Array> => {
104
+ const signLegacyV1 = async (privateKey: PrivateKey, value: Uint8Array, validityType: IPNSEntry.ValidityType, validity: Uint8Array, options?: AbortOptions): Promise<Uint8Array<ArrayBuffer>> => {
258
105
  try {
259
106
  const dataForSignature = ipnsRecordDataForV1Sig(value, validityType, validity)
260
107
 
@@ -269,5 +116,5 @@ const signLegacyV1 = async (privateKey: PrivateKey, value: string, validityType:
269
116
  * Returns true if the public key multihash is not an identity hash
270
117
  */
271
118
  function shouldEmbedPublicKey (key: PublicKey): boolean {
272
- return key.toMultihash().code !== 0
119
+ return key.toMultihash().code !== IDENTITY_CODEC
273
120
  }
@@ -1,5 +1,5 @@
1
1
  import { CustomProgressEvent } from 'progress-events'
2
- import type { GetOptions, PutOptions } from './index.ts'
2
+ import type { IPNSRoutingGetOptions, IPNSRoutingPutOptions } from './index.ts'
3
3
  import type { IPNSRouting } from '../index.ts'
4
4
  import type { Routing } from '@helia/interface'
5
5
  import type { ProgressEvent } from 'progress-events'
@@ -11,14 +11,14 @@ export interface HeliaRoutingComponents {
11
11
  export type HeliaRoutingProgressEvents =
12
12
  ProgressEvent<'ipns:routing:helia:error', Error>
13
13
 
14
- export class HeliaRouting implements IPNSRouting {
14
+ export class HeliaIPNSRouting implements IPNSRouting {
15
15
  private readonly routing: Routing
16
16
 
17
17
  constructor (routing: Routing) {
18
18
  this.routing = routing
19
19
  }
20
20
 
21
- async put (routingKey: Uint8Array, marshaledRecord: Uint8Array, options: PutOptions = {}): Promise<void> {
21
+ async put (routingKey: Uint8Array, marshaledRecord: Uint8Array, options: IPNSRoutingPutOptions = {}): Promise<void> {
22
22
  try {
23
23
  await this.routing.put(routingKey, marshaledRecord, options)
24
24
  } catch (err: any) {
@@ -27,7 +27,7 @@ export class HeliaRouting implements IPNSRouting {
27
27
  }
28
28
  }
29
29
 
30
- async get (routingKey: Uint8Array, options: GetOptions = {}): Promise<Uint8Array> {
30
+ async get (routingKey: Uint8Array, options: IPNSRoutingGetOptions = {}): Promise<Uint8Array<ArrayBuffer>> {
31
31
  try {
32
32
  return await this.routing.get(routingKey, options)
33
33
  } catch (err: any) {
@@ -45,6 +45,6 @@ export class HeliaRouting implements IPNSRouting {
45
45
  * The helia routing uses any available Routers configured on the passed Helia
46
46
  * node. This could be libp2p, HTTP API Delegated Routing, etc.
47
47
  */
48
- export function helia (routing: Routing): IPNSRouting {
49
- return new HeliaRouting(routing)
48
+ export function heliaIPNSRouting (routing: Routing): IPNSRouting {
49
+ return new HeliaIPNSRouting(routing)
50
50
  }
@@ -5,11 +5,11 @@ import type { IPNSPublishMetadata } from '../pb/metadata.ts'
5
5
  import type { AbortOptions } from '@libp2p/interface'
6
6
  import type { ProgressOptions } from 'progress-events'
7
7
 
8
- export interface PutOptions extends AbortOptions, ProgressOptions {
8
+ export interface IPNSRoutingPutOptions extends AbortOptions, ProgressOptions {
9
9
  metadata?: IPNSPublishMetadata
10
10
  }
11
11
 
12
- export interface GetOptions extends AbortOptions, ProgressOptions {
12
+ export interface IPNSRoutingGetOptions extends AbortOptions, ProgressOptions {
13
13
  /**
14
14
  * Pass false to not perform validation actions
15
15
  *
@@ -19,8 +19,8 @@ export interface GetOptions extends AbortOptions, ProgressOptions {
19
19
  }
20
20
 
21
21
  export interface IPNSRouting {
22
- put(routingKey: Uint8Array, marshaledRecord: Uint8Array, options?: PutOptions): Promise<void>
23
- get(routingKey: Uint8Array, options?: GetOptions): Promise<Uint8Array>
22
+ put(routingKey: Uint8Array, marshaledRecord: Uint8Array, options?: IPNSRoutingPutOptions): Promise<void>
23
+ get(routingKey: Uint8Array, options?: IPNSRoutingGetOptions): Promise<Uint8Array>
24
24
  }
25
25
 
26
26
  export type { DatastoreProgressEvents }
@@ -32,6 +32,6 @@ export type IPNSRoutingProgressEvents =
32
32
  HeliaRoutingProgressEvents |
33
33
  PubSubProgressEvents
34
34
 
35
- export { helia } from './helia.ts'
36
- export { pubsub } from './pubsub.ts'
37
- export type { PubsubRoutingComponents, PubSub, Message, PublishResult, PubSubEvents } from './pubsub.ts'
35
+ export { heliaIPNSRouting } from './helia.ts'
36
+ export { pubSubIPNSRouting } from './pubsub.ts'
37
+ export type { PubsubRoutingComponents, PubSub, PubSubMessage, PublishResult, PubSubEvents, PubSubSubscription, PubSubSubscriptionChangeData } from './pubsub.ts'
@@ -1,18 +1,18 @@
1
1
  import type { LocalStore } from '../local-store.ts'
2
- import type { IPNSRouting, PutOptions, GetOptions } from './index.ts'
2
+ import type { IPNSRouting, IPNSRoutingPutOptions, IPNSRoutingGetOptions } from './index.ts'
3
3
 
4
- class LocalStoreRouting {
4
+ class LocalStoreIPNSRouting {
5
5
  private localStore: LocalStore
6
6
 
7
7
  constructor (localStore: LocalStore) {
8
8
  this.localStore = localStore
9
9
  }
10
10
 
11
- async put (routingKey: Uint8Array, marshaledRecord: Uint8Array, options?: PutOptions): Promise<void> {
11
+ async put (routingKey: Uint8Array, marshaledRecord: Uint8Array, options?: IPNSRoutingPutOptions): Promise<void> {
12
12
  await this.localStore.put(routingKey, marshaledRecord, options)
13
13
  }
14
14
 
15
- async get (routingKey: Uint8Array, options?: GetOptions): Promise<Uint8Array> {
15
+ async get (routingKey: Uint8Array, options?: IPNSRoutingGetOptions): Promise<Uint8Array<ArrayBuffer>> {
16
16
  const { record } = await this.localStore.get(routingKey, options)
17
17
 
18
18
  return record
@@ -26,6 +26,6 @@ class LocalStoreRouting {
26
26
  /**
27
27
  * Returns an IPNSRouting implementation that reads/writes to the local store
28
28
  */
29
- export function localStoreRouting (localStore: LocalStore): IPNSRouting {
30
- return new LocalStoreRouting(localStore)
29
+ export function localStoreIPNSRouting (localStore: LocalStore): IPNSRouting {
30
+ return new LocalStoreIPNSRouting(localStore)
31
31
  }
@@ -9,13 +9,13 @@ import { raceSignal } from 'race-signal'
9
9
  import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
10
10
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
11
11
  import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
12
+ import { withArrayBuffer } from 'uint8arrays/with-array-buffer'
12
13
  import { InvalidTopicError } from '../errors.ts'
13
14
  import { localStore } from '../local-store.ts'
14
- import { multihashToIPNSRoutingKey } from '../records.ts'
15
15
  import { ipnsSelector } from '../selector.ts'
16
- import { IPNS_STRING_PREFIX, unmarshalIPNSRecord } from '../utils.ts'
16
+ import { IPNS_STRING_PREFIX, multihashToIPNSRoutingKey } from '../utils.ts'
17
17
  import { ipnsValidator } from '../validator.ts'
18
- import type { GetOptions, IPNSRouting, PutOptions } from './index.ts'
18
+ import type { IPNSRoutingGetOptions, IPNSRouting, IPNSRoutingPutOptions } from './index.ts'
19
19
  import type { LocalStore } from '../local-store.ts'
20
20
  import type { Keychain } from '@helia/interface'
21
21
  import type { Fetch } from '@libp2p/fetch'
@@ -26,26 +26,26 @@ import type { ProgressEvent } from 'progress-events'
26
26
 
27
27
  const log = logger('helia:ipns:routing:pubsub')
28
28
 
29
- export interface Message {
29
+ export interface PubSubMessage {
30
30
  type: 'signed' | 'unsigned'
31
31
  from: PeerId
32
32
  topic: string
33
33
  data: Uint8Array
34
34
  }
35
35
 
36
- export interface Subscription {
36
+ export interface PubSubSubscription {
37
37
  topic: string
38
38
  subscribe: boolean
39
39
  }
40
40
 
41
- export interface SubscriptionChangeData {
41
+ export interface PubSubSubscriptionChangeData {
42
42
  peerId: PeerId
43
- subscriptions: Subscription[]
43
+ subscriptions: PubSubSubscription[]
44
44
  }
45
45
 
46
46
  export interface PubSubEvents {
47
- 'subscription-change': CustomEvent<SubscriptionChangeData>
48
- message: CustomEvent<Message>
47
+ 'subscription-change': CustomEvent<PubSubSubscriptionChangeData>
48
+ message: CustomEvent<PubSubMessage>
49
49
  }
50
50
 
51
51
  export interface PublishResult {
@@ -96,7 +96,7 @@ export type PubSubProgressEvents =
96
96
  ProgressEvent<'ipns:pubsub:subscribe', { topic: string }> |
97
97
  ProgressEvent<'ipns:pubsub:error', Error>
98
98
 
99
- export class PubSubRouting implements IPNSRouting, Startable {
99
+ export class PubSubIPNSRouting implements IPNSRouting, Startable {
100
100
  private readonly subscriptions: Set<string>
101
101
  private readonly localStore: LocalStore
102
102
  private readonly libp2p: Pick<Libp2p<{ pubsub: PubSub, fetch?: Fetch }>, 'peerId' | 'register' | 'unregister' | 'services'>
@@ -196,7 +196,7 @@ export class PubSubRouting implements IPNSRouting, Startable {
196
196
  }
197
197
  }
198
198
 
199
- async #processPubSubMessage (message: Message, options?: AbortOptions): Promise<void> {
199
+ async #processPubSubMessage (message: PubSubMessage, options?: AbortOptions): Promise<void> {
200
200
  log('message received for topic', message.topic)
201
201
 
202
202
  if (message.type !== 'signed') {
@@ -212,7 +212,7 @@ export class PubSubRouting implements IPNSRouting, Startable {
212
212
  await this.#handleRecord(message.topic, topicToKey(message.topic), message.data, false, options)
213
213
  }
214
214
 
215
- async #fetchFromPeer (topic: string, routingKey: Uint8Array, peerId: PeerId, options?: AbortOptions): Promise<Uint8Array> {
215
+ async #fetchFromPeer (topic: string, routingKey: Uint8Array, peerId: PeerId, options?: AbortOptions): Promise<Uint8Array<ArrayBuffer>> {
216
216
  const marshalledRecord = await this.fetchQueue.add(async ({ signal }) => {
217
217
  log('fetching ipns record for %m from peer %s', routingKey, peerId)
218
218
 
@@ -238,18 +238,17 @@ export class PubSubRouting implements IPNSRouting, Startable {
238
238
  return this.#handleRecord(topic, routingKey, marshalledRecord, true, options)
239
239
  }
240
240
 
241
- async #handleRecord (topic: string, routingKey: Uint8Array, marshalledRecord: Uint8Array, publish: boolean, options?: AbortOptions): Promise<Uint8Array> {
242
- const record = await unmarshalIPNSRecord(routingKey, marshalledRecord, this.keychain, options)
243
- await ipnsValidator(record, options)
241
+ async #handleRecord (topic: string, routingKey: Uint8Array, marshalledRecord: Uint8Array, publish: boolean, options?: AbortOptions): Promise<Uint8Array<ArrayBuffer>> {
242
+ const record = await ipnsValidator(routingKey, marshalledRecord, this.keychain, options)
244
243
  this.shutdownController.signal.throwIfAborted()
245
244
 
246
245
  if (await this.localStore.has(routingKey)) {
247
246
  const { record: marshaledCurrentRecord } = await this.localStore.get(routingKey, options)
248
- const currentRecord = await unmarshalIPNSRecord(routingKey, marshaledCurrentRecord, this.keychain, options)
247
+ const currentRecord = await ipnsValidator(routingKey, marshaledCurrentRecord, this.keychain, options)
249
248
 
250
- if (uint8ArrayEquals(marshaledCurrentRecord, record.bytes)) {
249
+ if (uint8ArrayEquals(marshaledCurrentRecord, marshalledRecord)) {
251
250
  log.trace('found identical record for %m', routingKey)
252
- return currentRecord.bytes
251
+ return marshaledCurrentRecord
253
252
  }
254
253
 
255
254
  const records = [currentRecord, record]
@@ -257,7 +256,7 @@ export class PubSubRouting implements IPNSRouting, Startable {
257
256
 
258
257
  if (index === 0) {
259
258
  log.trace('found old record for %m', routingKey)
260
- return currentRecord.bytes
259
+ return marshaledCurrentRecord
261
260
  }
262
261
  }
263
262
 
@@ -276,13 +275,13 @@ export class PubSubRouting implements IPNSRouting, Startable {
276
275
  }
277
276
  }
278
277
 
279
- return marshalledRecord
278
+ return withArrayBuffer(marshalledRecord)
280
279
  }
281
280
 
282
281
  /**
283
282
  * Put a value to the pubsub datastore indexed by the received key properly encoded
284
283
  */
285
- async put (routingKey: Uint8Array, marshaledRecord: Uint8Array, options: PutOptions = {}): Promise<void> {
284
+ async put (routingKey: Uint8Array, marshaledRecord: Uint8Array, options: IPNSRoutingPutOptions = {}): Promise<void> {
286
285
  try {
287
286
  const topic = keyToTopic(routingKey)
288
287
 
@@ -303,7 +302,7 @@ export class PubSubRouting implements IPNSRouting, Startable {
303
302
  * Also, the identifier topic is subscribed to and the pubsub datastore records will be
304
303
  * updated once new publishes occur
305
304
  */
306
- async get (routingKey: Uint8Array, options: GetOptions = {}): Promise<Uint8Array> {
305
+ async get (routingKey: Uint8Array, options: IPNSRoutingGetOptions = {}): Promise<Uint8Array<ArrayBuffer>> {
307
306
  const topic = keyToTopic(routingKey)
308
307
 
309
308
  try {
@@ -324,7 +323,7 @@ export class PubSubRouting implements IPNSRouting, Startable {
324
323
  await raceSignal(delay(this.fetchDelay), this.shutdownController.signal)
325
324
 
326
325
  const fetchController = new AbortController()
327
- const promises: Array<Promise<Uint8Array>> = []
326
+ const promises: Array<Promise<Uint8Array<ArrayBuffer>>> = []
328
327
 
329
328
  for (const peerId of this.libp2p.services.pubsub.getSubscribers(topic)) {
330
329
  const signal = anySignal([
@@ -447,6 +446,6 @@ function topicToKey (topic: string): Uint8Array {
447
446
  * updated records, so the first call to `.get` should be expected
448
447
  * to fail!
449
448
  */
450
- export function pubsub (components: PubsubRoutingComponents, init: PubsubRoutingInit = {}): IPNSRouting {
451
- return new PubSubRouting(components, init)
449
+ export function pubSubIPNSRouting (components: PubsubRoutingComponents, init: PubsubRoutingInit = {}): IPNSRouting {
450
+ return new PubSubIPNSRouting(components, init)
452
451
  }
package/src/selector.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import NanoDate from 'timestamp-nano'
2
- import { IpnsEntry } from './pb/ipns.ts'
3
- import type { IPNSRecord } from './records.ts'
2
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
3
+ import { IPNSEntry } from './pb/ipns.ts'
4
+ import { decodeExtensibleData } from './utils.ts'
4
5
 
5
6
  /**
6
7
  * Selects the latest valid IPNS record from an array of marshalled IPNS records.
@@ -13,19 +14,21 @@ import type { IPNSRecord } from './records.ts'
13
14
  * @param data - Array of marshalled IPNS records to select from
14
15
  * @returns The index of the most valid record from the input array
15
16
  */
16
- export function ipnsSelector (key: Uint8Array, data: IPNSRecord[]): number {
17
+ export function ipnsSelector (key: Uint8Array, data: IPNSEntry[]): number {
17
18
  const entries = data.map((record, index) => ({
18
19
  record,
19
20
  index
20
21
  }))
21
22
 
22
23
  entries.sort((a, b) => {
23
- // Before we'd sort based on the signature version. Unmarshal now fails if
24
- // a record does not have SignatureV2, so that is no longer needed. V1-only
25
- // records haven't been issues in a long time.
24
+ // Before we'd sort based on the signature version. Validation fails if a
25
+ // record does not have SignatureV2, so that is no longer needed. V1-only
26
+ // records will have all expired by this point.
27
+ const aData = decodeExtensibleData(a.record.data)
28
+ const bData = decodeExtensibleData(b.record.data)
26
29
 
27
- const aSeq = a.record.sequence
28
- const bSeq = b.record.sequence
30
+ const aSeq = aData.Sequence
31
+ const bSeq = bData.Sequence
29
32
 
30
33
  // choose later sequence number
31
34
  if (aSeq > bSeq) {
@@ -34,10 +37,10 @@ export function ipnsSelector (key: Uint8Array, data: IPNSRecord[]): number {
34
37
  return 1
35
38
  }
36
39
 
37
- if (a.record.validityType === IpnsEntry.ValidityType.EOL && b.record.validityType === IpnsEntry.ValidityType.EOL) {
40
+ if (aData.ValidityType === IPNSEntry.ValidityType.EOL && bData.ValidityType === IPNSEntry.ValidityType.EOL) {
38
41
  // choose longer lived record if sequence numbers the same
39
- const recordAValidityDate = NanoDate.fromString(a.record.validity).toDate()
40
- const recordBValidityDate = NanoDate.fromString(b.record.validity).toDate()
42
+ const recordAValidityDate = NanoDate.fromString(uint8ArrayToString(aData.Validity)).toDate()
43
+ const recordBValidityDate = NanoDate.fromString(uint8ArrayToString(bData.Validity)).toDate()
41
44
 
42
45
  if (recordAValidityDate.getTime() > recordBValidityDate.getTime()) {
43
46
  return -1