libp2p 0.35.4 → 0.35.8-bf1fc32

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 (93) hide show
  1. package/README.md +2 -2
  2. package/dist/index.min.js +55 -0
  3. package/dist/src/circuit/auto-relay.d.ts +8 -10
  4. package/dist/src/circuit/auto-relay.d.ts.map +1 -1
  5. package/dist/src/connection-manager/auto-dialler.d.ts +1 -1
  6. package/dist/src/connection-manager/auto-dialler.d.ts.map +1 -1
  7. package/dist/src/connection-manager/index.d.ts +1 -2
  8. package/dist/src/connection-manager/index.d.ts.map +1 -1
  9. package/dist/src/content-routing/utils.d.ts +3 -3
  10. package/dist/src/content-routing/utils.d.ts.map +1 -1
  11. package/dist/src/dht/dht-peer-routing.d.ts.map +1 -1
  12. package/dist/src/dialer/dial-request.d.ts.map +1 -1
  13. package/dist/src/dialer/index.d.ts +20 -16
  14. package/dist/src/dialer/index.d.ts.map +1 -1
  15. package/dist/src/identify/index.d.ts +4 -4
  16. package/dist/src/identify/index.d.ts.map +1 -1
  17. package/dist/src/index.d.ts +14 -9
  18. package/dist/src/index.d.ts.map +1 -1
  19. package/dist/src/metrics/index.d.ts +9 -4
  20. package/dist/src/metrics/index.d.ts.map +1 -1
  21. package/dist/src/metrics/stats.d.ts +3 -1
  22. package/dist/src/metrics/stats.d.ts.map +1 -1
  23. package/dist/src/metrics/tracked-map.d.ts +8 -0
  24. package/dist/src/metrics/tracked-map.d.ts.map +1 -0
  25. package/dist/src/peer-routing.d.ts +1 -1
  26. package/dist/src/peer-routing.d.ts.map +1 -1
  27. package/dist/src/peer-store/address-book.d.ts +28 -84
  28. package/dist/src/peer-store/address-book.d.ts.map +1 -1
  29. package/dist/src/peer-store/index.d.ts +25 -94
  30. package/dist/src/peer-store/index.d.ts.map +1 -1
  31. package/dist/src/peer-store/key-book.d.ts +31 -15
  32. package/dist/src/peer-store/key-book.d.ts.map +1 -1
  33. package/dist/src/peer-store/metadata-book.d.ts +33 -29
  34. package/dist/src/peer-store/metadata-book.d.ts.map +1 -1
  35. package/dist/src/peer-store/pb/peer.d.ts +222 -0
  36. package/dist/src/peer-store/proto-book.d.ts +27 -28
  37. package/dist/src/peer-store/proto-book.d.ts.map +1 -1
  38. package/dist/src/peer-store/store.d.ts +73 -0
  39. package/dist/src/peer-store/store.d.ts.map +1 -0
  40. package/dist/src/peer-store/types.d.ts +202 -0
  41. package/dist/src/peer-store/types.d.ts.map +1 -0
  42. package/dist/src/ping/index.d.ts.map +1 -1
  43. package/dist/src/record/peer-record/index.d.ts +2 -2
  44. package/dist/src/record/peer-record/index.d.ts.map +1 -1
  45. package/dist/src/registrar.d.ts +5 -5
  46. package/dist/src/registrar.d.ts.map +1 -1
  47. package/dist/src/upgrader.d.ts.map +1 -1
  48. package/package.json +26 -30
  49. package/src/circuit/auto-relay.js +19 -19
  50. package/src/connection-manager/auto-dialler.js +9 -4
  51. package/src/connection-manager/index.js +41 -31
  52. package/src/content-routing/utils.js +4 -4
  53. package/src/dht/dht-peer-routing.js +2 -6
  54. package/src/dialer/dial-request.js +4 -1
  55. package/src/dialer/index.js +33 -21
  56. package/src/identify/index.js +28 -23
  57. package/src/index.js +43 -35
  58. package/src/metrics/index.js +15 -8
  59. package/src/metrics/stats.js +1 -0
  60. package/src/metrics/tracked-map.js +94 -0
  61. package/src/peer-routing.js +11 -1
  62. package/src/peer-store/address-book.js +242 -227
  63. package/src/peer-store/index.js +77 -110
  64. package/src/peer-store/key-book.js +99 -54
  65. package/src/peer-store/metadata-book.js +171 -101
  66. package/src/peer-store/pb/peer.d.ts +222 -0
  67. package/src/peer-store/pb/peer.js +643 -0
  68. package/src/peer-store/pb/peer.proto +31 -0
  69. package/src/peer-store/proto-book.js +171 -105
  70. package/src/peer-store/store.js +250 -0
  71. package/src/peer-store/types.ts +245 -0
  72. package/src/ping/index.js +6 -0
  73. package/src/record/peer-record/index.js +1 -1
  74. package/src/record/utils.js +1 -1
  75. package/src/registrar.js +4 -4
  76. package/src/upgrader.js +3 -1
  77. package/dist/src/peer-store/book.d.ts +0 -81
  78. package/dist/src/peer-store/book.d.ts.map +0 -1
  79. package/dist/src/peer-store/persistent/consts.d.ts +0 -6
  80. package/dist/src/peer-store/persistent/consts.d.ts.map +0 -1
  81. package/dist/src/peer-store/persistent/index.d.ts +0 -129
  82. package/dist/src/peer-store/persistent/index.d.ts.map +0 -1
  83. package/dist/src/peer-store/persistent/pb/address-book.d.ts +0 -198
  84. package/dist/src/peer-store/persistent/pb/proto-book.d.ts +0 -59
  85. package/src/peer-store/book.js +0 -124
  86. package/src/peer-store/persistent/consts.js +0 -15
  87. package/src/peer-store/persistent/index.js +0 -408
  88. package/src/peer-store/persistent/pb/address-book.d.ts +0 -198
  89. package/src/peer-store/persistent/pb/address-book.js +0 -522
  90. package/src/peer-store/persistent/pb/address-book.proto +0 -27
  91. package/src/peer-store/persistent/pb/proto-book.d.ts +0 -59
  92. package/src/peer-store/persistent/pb/proto-book.js +0 -157
  93. package/src/peer-store/persistent/pb/proto-book.proto +0 -5
@@ -1,171 +1,237 @@
1
1
  'use strict'
2
2
 
3
3
  const debug = require('debug')
4
- const log = Object.assign(debug('libp2p:peer-store:proto-book'), {
5
- error: debug('libp2p:peer-store:proto-book:err')
6
- })
7
4
  const errcode = require('err-code')
5
+ const { codes } = require('../errors')
8
6
  const PeerId = require('peer-id')
9
7
 
10
- const Book = require('./book')
11
-
12
- const {
13
- codes: { ERR_INVALID_PARAMETERS }
14
- } = require('../errors')
15
-
16
8
  /**
17
- * @typedef {import('./')} PeerStore
9
+ * @typedef {import('./types').PeerStore} PeerStore
10
+ * @typedef {import('./types').ProtoBook} ProtoBook
18
11
  */
19
12
 
13
+ const log = Object.assign(debug('libp2p:peer-store:proto-book'), {
14
+ error: debug('libp2p:peer-store:proto-book:err')
15
+ })
16
+
17
+ const EVENT_NAME = 'change:protocols'
18
+
20
19
  /**
21
- * @extends {Book}
22
- *
23
- * @fires ProtoBook#change:protocols
20
+ * @implements {ProtoBook}
24
21
  */
25
- class ProtoBook extends Book {
22
+ class PersistentProtoBook {
26
23
  /**
27
- * The ProtoBook is responsible for keeping the known supported
28
- * protocols of a peer.
29
- *
30
- * @class
31
- * @param {PeerStore} peerStore
24
+ * @param {PeerStore["emit"]} emit
25
+ * @param {import('./types').Store} store
32
26
  */
33
- constructor (peerStore) {
34
- /**
35
- * PeerStore Event emitter, used by the ProtoBook to emit:
36
- * "change:protocols" - emitted when the known protocols of a peer change.
37
- */
38
- super({
39
- peerStore,
40
- eventName: 'change:protocols',
41
- eventProperty: 'protocols',
42
- eventTransformer: (data) => Array.from(data)
43
- })
44
-
45
- /**
46
- * Map known peers to their known protocols.
47
- *
48
- * @type {Map<string, Set<string>>}
49
- */
50
- this.data = new Map()
27
+ constructor (emit, store) {
28
+ this._emit = emit
29
+ this._store = store
30
+ }
31
+
32
+ /**
33
+ * @param {PeerId} peerId
34
+ */
35
+ async get (peerId) {
36
+ log('get wait for read lock')
37
+ const release = await this._store.lock.readLock()
38
+ log('get got read lock')
39
+
40
+ try {
41
+ const peer = await this._store.load(peerId)
42
+
43
+ return peer.protocols
44
+ } catch (/** @type {any} */ err) {
45
+ if (err.code !== codes.ERR_NOT_FOUND) {
46
+ throw err
47
+ }
48
+ } finally {
49
+ log('get release read lock')
50
+ release()
51
+ }
52
+
53
+ return []
51
54
  }
52
55
 
53
56
  /**
54
- * Set known protocols of a provided peer.
55
- * If the peer was not known before, it will be added.
56
- *
57
- * @override
58
57
  * @param {PeerId} peerId
59
58
  * @param {string[]} protocols
60
- * @returns {ProtoBook}
61
59
  */
62
- set (peerId, protocols) {
60
+ async set (peerId, protocols) {
63
61
  if (!PeerId.isPeerId(peerId)) {
64
62
  log.error('peerId must be an instance of peer-id to store data')
65
- throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
63
+ throw errcode(new Error('peerId must be an instance of peer-id'), codes.ERR_INVALID_PARAMETERS)
66
64
  }
67
65
 
68
- if (!protocols) {
66
+ if (!Array.isArray(protocols)) {
69
67
  log.error('protocols must be provided to store data')
70
- throw errcode(new Error('protocols must be provided'), ERR_INVALID_PARAMETERS)
68
+ throw errcode(new Error('protocols must be provided'), codes.ERR_INVALID_PARAMETERS)
71
69
  }
72
70
 
73
- const id = peerId.toB58String()
74
- const recSet = this.data.get(id)
75
- const newSet = new Set(protocols)
76
-
77
- /**
78
- * @param {Set<string>} a
79
- * @param {Set<string>} b
80
- */
81
- const isSetEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value))
82
-
83
- // Already knows the peer and the recorded protocols are the same?
84
- // If yes, no changes needed!
85
- if (recSet && isSetEqual(recSet, newSet)) {
86
- log(`the protocols provided to store are equal to the already stored for ${id}`)
87
- return this
88
- }
71
+ log('set await write lock')
72
+ const release = await this._store.lock.writeLock()
73
+ log('set got write lock')
74
+
75
+ let updatedPeer
76
+
77
+ try {
78
+ try {
79
+ const peer = await this._store.load(peerId)
80
+
81
+ if (new Set([
82
+ ...protocols
83
+ ]).size === peer.protocols.length) {
84
+ return
85
+ }
86
+ } catch (/** @type {any} */ err) {
87
+ if (err.code !== codes.ERR_NOT_FOUND) {
88
+ throw err
89
+ }
90
+ }
91
+
92
+ updatedPeer = await this._store.patchOrCreate(peerId, {
93
+ protocols
94
+ })
89
95
 
90
- this._setData(peerId, newSet)
91
- log(`stored provided protocols for ${id}`)
96
+ log(`stored provided protocols for ${peerId.toB58String()}`)
97
+ } finally {
98
+ log('set release write lock')
99
+ release()
100
+ }
92
101
 
93
- return this
102
+ this._emit(EVENT_NAME, { peerId, protocols: updatedPeer.protocols })
94
103
  }
95
104
 
96
105
  /**
97
- * Adds known protocols of a provided peer.
98
- * If the peer was not known before, it will be added.
99
- *
100
106
  * @param {PeerId} peerId
101
107
  * @param {string[]} protocols
102
- * @returns {ProtoBook}
103
108
  */
104
- add (peerId, protocols) {
109
+ async add (peerId, protocols) {
105
110
  if (!PeerId.isPeerId(peerId)) {
106
111
  log.error('peerId must be an instance of peer-id to store data')
107
- throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
112
+ throw errcode(new Error('peerId must be an instance of peer-id'), codes.ERR_INVALID_PARAMETERS)
108
113
  }
109
114
 
110
- if (!protocols) {
115
+ if (!Array.isArray(protocols)) {
111
116
  log.error('protocols must be provided to store data')
112
- throw errcode(new Error('protocols must be provided'), ERR_INVALID_PARAMETERS)
117
+ throw errcode(new Error('protocols must be provided'), codes.ERR_INVALID_PARAMETERS)
113
118
  }
114
119
 
115
- const id = peerId.toB58String()
116
- const recSet = this.data.get(id) || new Set()
117
- const newSet = new Set([...recSet, ...protocols]) // Set Union
120
+ log('add await write lock')
121
+ const release = await this._store.lock.writeLock()
122
+ log('add got write lock')
123
+
124
+ let updatedPeer
125
+
126
+ try {
127
+ try {
128
+ const peer = await this._store.load(peerId)
129
+
130
+ if (new Set([
131
+ ...peer.protocols,
132
+ ...protocols
133
+ ]).size === peer.protocols.length) {
134
+ return
135
+ }
136
+ } catch (/** @type {any} */ err) {
137
+ if (err.code !== codes.ERR_NOT_FOUND) {
138
+ throw err
139
+ }
140
+ }
118
141
 
119
- // Any new protocol added?
120
- if (recSet.size === newSet.size) {
121
- log(`the protocols provided to store are already stored for ${id}`)
122
- return this
123
- }
142
+ updatedPeer = await this._store.mergeOrCreate(peerId, {
143
+ protocols
144
+ })
124
145
 
125
- this._setData(peerId, newSet)
126
- log(`added provided protocols for ${id}`)
146
+ log(`added provided protocols for ${peerId.toB58String()}`)
147
+ } finally {
148
+ log('add release write lock')
149
+ release()
150
+ }
127
151
 
128
- return this
152
+ this._emit(EVENT_NAME, { peerId, protocols: updatedPeer.protocols })
129
153
  }
130
154
 
131
155
  /**
132
- * Removes known protocols of a provided peer.
133
- * If the protocols did not exist before, nothing will be done.
134
- *
135
156
  * @param {PeerId} peerId
136
157
  * @param {string[]} protocols
137
- * @returns {ProtoBook}
138
158
  */
139
- remove (peerId, protocols) {
159
+ async remove (peerId, protocols) {
140
160
  if (!PeerId.isPeerId(peerId)) {
141
161
  log.error('peerId must be an instance of peer-id to store data')
142
- throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
162
+ throw errcode(new Error('peerId must be an instance of peer-id'), codes.ERR_INVALID_PARAMETERS)
143
163
  }
144
164
 
145
- if (!protocols) {
165
+ if (!Array.isArray(protocols)) {
146
166
  log.error('protocols must be provided to store data')
147
- throw errcode(new Error('protocols must be provided'), ERR_INVALID_PARAMETERS)
167
+ throw errcode(new Error('protocols must be provided'), codes.ERR_INVALID_PARAMETERS)
148
168
  }
149
169
 
150
- const id = peerId.toB58String()
151
- const recSet = this.data.get(id)
170
+ log('remove await write lock')
171
+ const release = await this._store.lock.writeLock()
172
+ log('remove got write lock')
173
+
174
+ let updatedPeer
175
+
176
+ try {
177
+ try {
178
+ const peer = await this._store.load(peerId)
179
+ const protocolSet = new Set(peer.protocols)
180
+
181
+ for (const protocol of protocols) {
182
+ protocolSet.delete(protocol)
183
+ }
152
184
 
153
- if (recSet) {
154
- const newSet = new Set([
155
- ...recSet
156
- ].filter((p) => !protocols.includes(p)))
185
+ if (peer.protocols.length === protocolSet.size) {
186
+ return
187
+ }
157
188
 
158
- // Any protocol removed?
159
- if (recSet.size === newSet.size) {
160
- return this
189
+ protocols = Array.from(protocolSet)
190
+ } catch (/** @type {any} */ err) {
191
+ if (err.code !== codes.ERR_NOT_FOUND) {
192
+ throw err
193
+ }
161
194
  }
162
195
 
163
- this._setData(peerId, newSet)
164
- log(`removed provided protocols for ${id}`)
196
+ updatedPeer = await this._store.patchOrCreate(peerId, {
197
+ protocols
198
+ })
199
+ } finally {
200
+ log('remove release write lock')
201
+ release()
165
202
  }
166
203
 
167
- return this
204
+ this._emit(EVENT_NAME, { peerId, protocols: updatedPeer.protocols })
205
+ }
206
+
207
+ /**
208
+ * @param {PeerId} peerId
209
+ */
210
+ async delete (peerId) {
211
+ log('delete await write lock')
212
+ const release = await this._store.lock.writeLock()
213
+ log('delete got write lock')
214
+ let has
215
+
216
+ try {
217
+ has = await this._store.has(peerId)
218
+
219
+ await this._store.patchOrCreate(peerId, {
220
+ protocols: []
221
+ })
222
+ } catch (/** @type {any} */ err) {
223
+ if (err.code !== codes.ERR_NOT_FOUND) {
224
+ throw err
225
+ }
226
+ } finally {
227
+ log('delete release write lock')
228
+ release()
229
+ }
230
+
231
+ if (has) {
232
+ this._emit(EVENT_NAME, { peerId, protocols: [] })
233
+ }
168
234
  }
169
235
  }
170
236
 
171
- module.exports = ProtoBook
237
+ module.exports = PersistentProtoBook
@@ -0,0 +1,250 @@
1
+ 'use strict'
2
+
3
+ const debug = require('debug')
4
+ const PeerId = require('peer-id')
5
+ const errcode = require('err-code')
6
+ const { codes } = require('../errors')
7
+ const { Key } = require('interface-datastore/key')
8
+ const { base32 } = require('multiformats/bases/base32')
9
+ const { keys: { unmarshalPublicKey, marshalPublicKey } } = require('libp2p-crypto')
10
+ const { Multiaddr } = require('multiaddr')
11
+ const { Peer: PeerPB } = require('./pb/peer')
12
+ // @ts-expect-error no types
13
+ const mortice = require('mortice')
14
+ const { equals: uint8arrayEquals } = require('uint8arrays/equals')
15
+
16
+ const log = Object.assign(debug('libp2p:peer-store:store'), {
17
+ error: debug('libp2p:peer-store:store:err')
18
+ })
19
+
20
+ /**
21
+ * @typedef {import('./types').PeerStore} PeerStore
22
+ * @typedef {import('./types').EventName} EventName
23
+ * @typedef {import('./types').Peer} Peer
24
+ */
25
+
26
+ const NAMESPACE_COMMON = '/peers/'
27
+
28
+ class PersistentStore {
29
+ /**
30
+ * @param {import('interface-datastore').Datastore} datastore
31
+ */
32
+ constructor (datastore) {
33
+ this._datastore = datastore
34
+ this.lock = mortice('peer-store', {
35
+ singleProcess: true
36
+ })
37
+ }
38
+
39
+ /**
40
+ * @param {PeerId} peerId
41
+ * @returns {Key}
42
+ */
43
+ _peerIdToDatastoreKey (peerId) {
44
+ if (!PeerId.isPeerId(peerId)) {
45
+ log.error('peerId must be an instance of peer-id to store data')
46
+ throw errcode(new Error('peerId must be an instance of peer-id'), codes.ERR_INVALID_PARAMETERS)
47
+ }
48
+
49
+ const b32key = peerId.toString()
50
+ return new Key(`${NAMESPACE_COMMON}${b32key}`)
51
+ }
52
+
53
+ /**
54
+ * @param {PeerId} peerId
55
+ */
56
+ async has (peerId) {
57
+ return this._datastore.has(this._peerIdToDatastoreKey(peerId))
58
+ }
59
+
60
+ /**
61
+ * @param {PeerId} peerId
62
+ */
63
+ async delete (peerId) {
64
+ await this._datastore.delete(this._peerIdToDatastoreKey(peerId))
65
+ }
66
+
67
+ /**
68
+ * @param {PeerId} peerId
69
+ * @returns {Promise<import('./types').Peer>} peer
70
+ */
71
+ async load (peerId) {
72
+ const buf = await this._datastore.get(this._peerIdToDatastoreKey(peerId))
73
+ const peer = PeerPB.decode(buf)
74
+ const pubKey = peer.pubKey ? unmarshalPublicKey(peer.pubKey) : peerId.pubKey
75
+ const metadata = new Map()
76
+
77
+ for (const meta of peer.metadata) {
78
+ metadata.set(meta.key, meta.value)
79
+ }
80
+
81
+ return {
82
+ ...peer,
83
+ id: peerId,
84
+ pubKey,
85
+ addresses: peer.addresses.map(({ multiaddr, isCertified }) => ({
86
+ multiaddr: new Multiaddr(multiaddr),
87
+ isCertified: isCertified || false
88
+ })),
89
+ metadata,
90
+ peerRecordEnvelope: peer.peerRecordEnvelope || undefined
91
+ }
92
+ }
93
+
94
+ /**
95
+ * @param {Peer} peer
96
+ */
97
+ async save (peer) {
98
+ if (peer.pubKey != null && peer.id.pubKey != null && !uint8arrayEquals(peer.pubKey.bytes, peer.id.pubKey.bytes)) {
99
+ log.error('peer publicKey bytes do not match peer id publicKey bytes')
100
+ throw errcode(new Error('publicKey bytes do not match peer id publicKey bytes'), codes.ERR_INVALID_PARAMETERS)
101
+ }
102
+
103
+ const buf = PeerPB.encode({
104
+ addresses: peer.addresses.sort((a, b) => {
105
+ return a.multiaddr.toString().localeCompare(b.multiaddr.toString())
106
+ }).map(({ multiaddr, isCertified }) => ({
107
+ multiaddr: multiaddr.bytes,
108
+ isCertified
109
+ })),
110
+ protocols: peer.protocols.sort(),
111
+ pubKey: peer.pubKey ? marshalPublicKey(peer.pubKey) : undefined,
112
+ metadata: [...peer.metadata.keys()].sort().map(key => ({ key, value: peer.metadata.get(key) })),
113
+ peerRecordEnvelope: peer.peerRecordEnvelope
114
+ }).finish()
115
+
116
+ await this._datastore.put(this._peerIdToDatastoreKey(peer.id), buf)
117
+
118
+ return this.load(peer.id)
119
+ }
120
+
121
+ /**
122
+ * @param {PeerId} peerId
123
+ * @param {Partial<Peer>} data
124
+ */
125
+ async patch (peerId, data) {
126
+ const peer = await this.load(peerId)
127
+
128
+ return await this._patch(peerId, data, peer)
129
+ }
130
+
131
+ /**
132
+ * @param {PeerId} peerId
133
+ * @param {Partial<Peer>} data
134
+ */
135
+ async patchOrCreate (peerId, data) {
136
+ /** @type {Peer} */
137
+ let peer
138
+
139
+ try {
140
+ peer = await this.load(peerId)
141
+ } catch (/** @type {any} */ err) {
142
+ if (err.code !== codes.ERR_NOT_FOUND) {
143
+ throw err
144
+ }
145
+
146
+ peer = { id: peerId, addresses: [], protocols: [], metadata: new Map() }
147
+ }
148
+
149
+ return await this._patch(peerId, data, peer)
150
+ }
151
+
152
+ /**
153
+ * @param {PeerId} peerId
154
+ * @param {Partial<Peer>} data
155
+ * @param {Peer} peer
156
+ */
157
+ async _patch (peerId, data, peer) {
158
+ return await this.save({
159
+ ...peer,
160
+ ...data,
161
+ id: peerId
162
+ })
163
+ }
164
+
165
+ /**
166
+ * @param {PeerId} peerId
167
+ * @param {Partial<Peer>} data
168
+ */
169
+ async merge (peerId, data) {
170
+ const peer = await this.load(peerId)
171
+
172
+ return this._merge(peerId, data, peer)
173
+ }
174
+
175
+ /**
176
+ * @param {PeerId} peerId
177
+ * @param {Partial<Peer>} data
178
+ */
179
+ async mergeOrCreate (peerId, data) {
180
+ /** @type {Peer} */
181
+ let peer
182
+
183
+ try {
184
+ peer = await this.load(peerId)
185
+ } catch (/** @type {any} */ err) {
186
+ if (err.code !== codes.ERR_NOT_FOUND) {
187
+ throw err
188
+ }
189
+
190
+ peer = { id: peerId, addresses: [], protocols: [], metadata: new Map() }
191
+ }
192
+
193
+ return await this._merge(peerId, data, peer)
194
+ }
195
+
196
+ /**
197
+ * @param {PeerId} peerId
198
+ * @param {Partial<Peer>} data
199
+ * @param {Peer} peer
200
+ */
201
+ async _merge (peerId, data, peer) {
202
+ // if the peer has certified addresses, use those in
203
+ // favour of the supplied versions
204
+ /** @type {Map<string, boolean>} */
205
+ const addresses = new Map()
206
+
207
+ ;(data.addresses || []).forEach(addr => {
208
+ addresses.set(addr.multiaddr.toString(), addr.isCertified)
209
+ })
210
+
211
+ peer.addresses.forEach(({ multiaddr, isCertified }) => {
212
+ const addrStr = multiaddr.toString()
213
+ addresses.set(addrStr, Boolean(addresses.get(addrStr) || isCertified))
214
+ })
215
+
216
+ return await this.save({
217
+ id: peerId,
218
+ addresses: Array.from(addresses.entries()).map(([addrStr, isCertified]) => {
219
+ return {
220
+ multiaddr: new Multiaddr(addrStr),
221
+ isCertified
222
+ }
223
+ }),
224
+ protocols: Array.from(new Set([
225
+ ...(peer.protocols || []),
226
+ ...(data.protocols || [])
227
+ ])),
228
+ metadata: new Map([
229
+ ...(peer.metadata ? peer.metadata.entries() : []),
230
+ ...(data.metadata ? data.metadata.entries() : [])
231
+ ]),
232
+ pubKey: data.pubKey || (peer != null ? peer.pubKey : undefined),
233
+ peerRecordEnvelope: data.peerRecordEnvelope || (peer != null ? peer.peerRecordEnvelope : undefined)
234
+ })
235
+ }
236
+
237
+ async * all () {
238
+ for await (const key of this._datastore.queryKeys({
239
+ prefix: NAMESPACE_COMMON
240
+ })) {
241
+ // /peers/${peer-id-as-libp2p-key-cid-string-in-base-32}
242
+ const base32Str = key.toString().split('/')[2]
243
+ const buf = base32.decode(base32Str)
244
+
245
+ yield this.load(PeerId.createFromBytes(buf))
246
+ }
247
+ }
248
+ }
249
+
250
+ module.exports = PersistentStore