libp2p 0.35.8 → 0.36.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.
Files changed (107) hide show
  1. package/dist/index.min.js +55 -0
  2. package/dist/src/circuit/auto-relay.d.ts +8 -10
  3. package/dist/src/circuit/auto-relay.d.ts.map +1 -1
  4. package/dist/src/config.d.ts +2 -0
  5. package/dist/src/config.d.ts.map +1 -1
  6. package/dist/src/connection-manager/auto-dialler.d.ts +1 -1
  7. package/dist/src/connection-manager/auto-dialler.d.ts.map +1 -1
  8. package/dist/src/connection-manager/index.d.ts +1 -2
  9. package/dist/src/connection-manager/index.d.ts.map +1 -1
  10. package/dist/src/content-routing/utils.d.ts +3 -3
  11. package/dist/src/content-routing/utils.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 +12 -7
  14. package/dist/src/dialer/index.d.ts.map +1 -1
  15. package/dist/src/errors.d.ts +2 -0
  16. package/dist/src/fetch/constants.d.ts +2 -0
  17. package/dist/src/fetch/constants.d.ts.map +1 -0
  18. package/dist/src/fetch/index.d.ts +76 -0
  19. package/dist/src/fetch/index.d.ts.map +1 -0
  20. package/dist/src/fetch/proto.d.ts +134 -0
  21. package/dist/src/identify/index.d.ts +4 -4
  22. package/dist/src/identify/index.d.ts.map +1 -1
  23. package/dist/src/index.d.ts +116 -7
  24. package/dist/src/index.d.ts.map +1 -1
  25. package/dist/src/metrics/index.d.ts +9 -4
  26. package/dist/src/metrics/index.d.ts.map +1 -1
  27. package/dist/src/metrics/tracked-map.d.ts +6 -1
  28. package/dist/src/metrics/tracked-map.d.ts.map +1 -1
  29. package/dist/src/peer-routing.d.ts +1 -1
  30. package/dist/src/peer-routing.d.ts.map +1 -1
  31. package/dist/src/peer-store/address-book.d.ts +30 -84
  32. package/dist/src/peer-store/address-book.d.ts.map +1 -1
  33. package/dist/src/peer-store/index.d.ts +28 -94
  34. package/dist/src/peer-store/index.d.ts.map +1 -1
  35. package/dist/src/peer-store/key-book.d.ts +31 -15
  36. package/dist/src/peer-store/key-book.d.ts.map +1 -1
  37. package/dist/src/peer-store/metadata-book.d.ts +33 -29
  38. package/dist/src/peer-store/metadata-book.d.ts.map +1 -1
  39. package/dist/src/peer-store/pb/peer.d.ts +222 -0
  40. package/dist/src/peer-store/proto-book.d.ts +27 -28
  41. package/dist/src/peer-store/proto-book.d.ts.map +1 -1
  42. package/dist/src/peer-store/store.d.ts +73 -0
  43. package/dist/src/peer-store/store.d.ts.map +1 -0
  44. package/dist/src/peer-store/types.d.ts +202 -0
  45. package/dist/src/peer-store/types.d.ts.map +1 -0
  46. package/dist/src/ping/index.d.ts.map +1 -1
  47. package/dist/src/record/peer-record/index.d.ts +2 -2
  48. package/dist/src/record/peer-record/index.d.ts.map +1 -1
  49. package/dist/src/registrar.d.ts +5 -5
  50. package/dist/src/registrar.d.ts.map +1 -1
  51. package/dist/src/types.d.ts +90 -0
  52. package/dist/src/types.d.ts.map +1 -0
  53. package/dist/src/upgrader.d.ts +8 -2
  54. package/dist/src/upgrader.d.ts.map +1 -1
  55. package/package.json +23 -26
  56. package/src/circuit/auto-relay.js +52 -38
  57. package/src/config.js +2 -0
  58. package/src/connection-manager/auto-dialler.js +20 -8
  59. package/src/connection-manager/index.js +42 -22
  60. package/src/content-routing/utils.js +4 -4
  61. package/src/dialer/dial-request.js +4 -1
  62. package/src/dialer/index.js +38 -8
  63. package/src/errors.js +2 -0
  64. package/src/fetch/README.md +36 -0
  65. package/src/fetch/constants.js +6 -0
  66. package/src/fetch/index.js +159 -0
  67. package/src/fetch/proto.d.ts +134 -0
  68. package/src/fetch/proto.js +333 -0
  69. package/src/fetch/proto.proto +15 -0
  70. package/src/identify/index.js +47 -38
  71. package/src/index.js +72 -27
  72. package/src/metrics/index.js +15 -8
  73. package/src/metrics/tracked-map.js +40 -14
  74. package/src/peer-routing.js +4 -1
  75. package/src/peer-store/address-book.js +254 -226
  76. package/src/peer-store/index.js +79 -110
  77. package/src/peer-store/key-book.js +99 -54
  78. package/src/peer-store/metadata-book.js +171 -101
  79. package/src/peer-store/pb/peer.d.ts +222 -0
  80. package/src/peer-store/pb/peer.js +643 -0
  81. package/src/peer-store/pb/peer.proto +31 -0
  82. package/src/peer-store/proto-book.js +171 -105
  83. package/src/peer-store/store.js +263 -0
  84. package/src/peer-store/types.ts +245 -0
  85. package/src/ping/index.js +6 -0
  86. package/src/record/peer-record/index.js +1 -1
  87. package/src/record/utils.js +1 -1
  88. package/src/registrar.js +4 -4
  89. package/src/types.ts +98 -0
  90. package/src/upgrader.js +32 -2
  91. package/dist/src/peer-store/book.d.ts +0 -81
  92. package/dist/src/peer-store/book.d.ts.map +0 -1
  93. package/dist/src/peer-store/persistent/consts.d.ts +0 -6
  94. package/dist/src/peer-store/persistent/consts.d.ts.map +0 -1
  95. package/dist/src/peer-store/persistent/index.d.ts +0 -129
  96. package/dist/src/peer-store/persistent/index.d.ts.map +0 -1
  97. package/dist/src/peer-store/persistent/pb/address-book.d.ts +0 -198
  98. package/dist/src/peer-store/persistent/pb/proto-book.d.ts +0 -59
  99. package/src/peer-store/book.js +0 -124
  100. package/src/peer-store/persistent/consts.js +0 -15
  101. package/src/peer-store/persistent/index.js +0 -408
  102. package/src/peer-store/persistent/pb/address-book.d.ts +0 -198
  103. package/src/peer-store/persistent/pb/address-book.js +0 -522
  104. package/src/peer-store/persistent/pb/address-book.proto +0 -27
  105. package/src/peer-store/persistent/pb/proto-book.d.ts +0 -59
  106. package/src/peer-store/persistent/pb/proto-book.js +0 -157
  107. 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,263 @@
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
+ // dedupe addresses
104
+ const addressSet = new Set()
105
+
106
+ const buf = PeerPB.encode({
107
+ addresses: peer.addresses
108
+ .filter(address => {
109
+ if (addressSet.has(address.multiaddr.toString())) {
110
+ return false
111
+ }
112
+
113
+ addressSet.add(address.multiaddr.toString())
114
+ return true
115
+ })
116
+ .sort((a, b) => {
117
+ return a.multiaddr.toString().localeCompare(b.multiaddr.toString())
118
+ })
119
+ .map(({ multiaddr, isCertified }) => ({
120
+ multiaddr: multiaddr.bytes,
121
+ isCertified
122
+ })),
123
+ protocols: peer.protocols.sort(),
124
+ pubKey: peer.pubKey ? marshalPublicKey(peer.pubKey) : undefined,
125
+ metadata: [...peer.metadata.keys()].sort().map(key => ({ key, value: peer.metadata.get(key) })),
126
+ peerRecordEnvelope: peer.peerRecordEnvelope
127
+ }).finish()
128
+
129
+ await this._datastore.put(this._peerIdToDatastoreKey(peer.id), buf)
130
+
131
+ return this.load(peer.id)
132
+ }
133
+
134
+ /**
135
+ * @param {PeerId} peerId
136
+ * @param {Partial<Peer>} data
137
+ */
138
+ async patch (peerId, data) {
139
+ const peer = await this.load(peerId)
140
+
141
+ return await this._patch(peerId, data, peer)
142
+ }
143
+
144
+ /**
145
+ * @param {PeerId} peerId
146
+ * @param {Partial<Peer>} data
147
+ */
148
+ async patchOrCreate (peerId, data) {
149
+ /** @type {Peer} */
150
+ let peer
151
+
152
+ try {
153
+ peer = await this.load(peerId)
154
+ } catch (/** @type {any} */ err) {
155
+ if (err.code !== codes.ERR_NOT_FOUND) {
156
+ throw err
157
+ }
158
+
159
+ peer = { id: peerId, addresses: [], protocols: [], metadata: new Map() }
160
+ }
161
+
162
+ return await this._patch(peerId, data, peer)
163
+ }
164
+
165
+ /**
166
+ * @param {PeerId} peerId
167
+ * @param {Partial<Peer>} data
168
+ * @param {Peer} peer
169
+ */
170
+ async _patch (peerId, data, peer) {
171
+ return await this.save({
172
+ ...peer,
173
+ ...data,
174
+ id: peerId
175
+ })
176
+ }
177
+
178
+ /**
179
+ * @param {PeerId} peerId
180
+ * @param {Partial<Peer>} data
181
+ */
182
+ async merge (peerId, data) {
183
+ const peer = await this.load(peerId)
184
+
185
+ return this._merge(peerId, data, peer)
186
+ }
187
+
188
+ /**
189
+ * @param {PeerId} peerId
190
+ * @param {Partial<Peer>} data
191
+ */
192
+ async mergeOrCreate (peerId, data) {
193
+ /** @type {Peer} */
194
+ let peer
195
+
196
+ try {
197
+ peer = await this.load(peerId)
198
+ } catch (/** @type {any} */ err) {
199
+ if (err.code !== codes.ERR_NOT_FOUND) {
200
+ throw err
201
+ }
202
+
203
+ peer = { id: peerId, addresses: [], protocols: [], metadata: new Map() }
204
+ }
205
+
206
+ return await this._merge(peerId, data, peer)
207
+ }
208
+
209
+ /**
210
+ * @param {PeerId} peerId
211
+ * @param {Partial<Peer>} data
212
+ * @param {Peer} peer
213
+ */
214
+ async _merge (peerId, data, peer) {
215
+ // if the peer has certified addresses, use those in
216
+ // favour of the supplied versions
217
+ /** @type {Map<string, boolean>} */
218
+ const addresses = new Map()
219
+
220
+ ;(data.addresses || []).forEach(addr => {
221
+ addresses.set(addr.multiaddr.toString(), addr.isCertified)
222
+ })
223
+
224
+ peer.addresses.forEach(({ multiaddr, isCertified }) => {
225
+ const addrStr = multiaddr.toString()
226
+ addresses.set(addrStr, Boolean(addresses.get(addrStr) || isCertified))
227
+ })
228
+
229
+ return await this.save({
230
+ id: peerId,
231
+ addresses: Array.from(addresses.entries()).map(([addrStr, isCertified]) => {
232
+ return {
233
+ multiaddr: new Multiaddr(addrStr),
234
+ isCertified
235
+ }
236
+ }),
237
+ protocols: Array.from(new Set([
238
+ ...(peer.protocols || []),
239
+ ...(data.protocols || [])
240
+ ])),
241
+ metadata: new Map([
242
+ ...(peer.metadata ? peer.metadata.entries() : []),
243
+ ...(data.metadata ? data.metadata.entries() : [])
244
+ ]),
245
+ pubKey: data.pubKey || (peer != null ? peer.pubKey : undefined),
246
+ peerRecordEnvelope: data.peerRecordEnvelope || (peer != null ? peer.peerRecordEnvelope : undefined)
247
+ })
248
+ }
249
+
250
+ async * all () {
251
+ for await (const key of this._datastore.queryKeys({
252
+ prefix: NAMESPACE_COMMON
253
+ })) {
254
+ // /peers/${peer-id-as-libp2p-key-cid-string-in-base-32}
255
+ const base32Str = key.toString().split('/')[2]
256
+ const buf = base32.decode(base32Str)
257
+
258
+ yield this.load(PeerId.createFromBytes(buf))
259
+ }
260
+ }
261
+ }
262
+
263
+ module.exports = PersistentStore