@libp2p/pubsub 1.2.6 → 1.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/errors.d.ts +4 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +4 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +24 -22
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +141 -99
- package/dist/src/index.js.map +1 -1
- package/dist/src/message/rpc.d.ts +586 -175
- package/dist/src/message/rpc.js +1238 -67
- package/dist/src/message/sign.d.ts +1 -1
- package/dist/src/message/sign.d.ts.map +1 -1
- package/dist/src/message/sign.js +4 -8
- package/dist/src/message/sign.js.map +1 -1
- package/dist/src/peer-streams.d.ts +2 -2
- package/dist/src/peer-streams.d.ts.map +1 -1
- package/dist/src/peer-streams.js +3 -3
- package/dist/src/peer-streams.js.map +1 -1
- package/dist/src/utils.d.ts +2 -6
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +10 -17
- package/dist/src/utils.js.map +1 -1
- package/package.json +14 -18
- package/src/errors.ts +4 -0
- package/src/index.ts +171 -106
- package/src/message/rpc.d.ts +586 -175
- package/src/message/rpc.js +1238 -67
- package/src/message/rpc.proto +38 -4
- package/src/message/sign.ts +6 -9
- package/src/peer-streams.ts +4 -4
- package/src/utils.ts +11 -20
package/src/index.ts
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
import { logger } from '@libp2p/logger'
|
2
|
-
import { EventEmitter, CustomEvent } from '@libp2p/interfaces'
|
2
|
+
import { EventEmitter, CustomEvent, EventHandler } from '@libp2p/interfaces'
|
3
3
|
import errcode from 'err-code'
|
4
4
|
import { pipe } from 'it-pipe'
|
5
5
|
import Queue from 'p-queue'
|
6
|
-
import {
|
6
|
+
import { createTopology } from '@libp2p/topology'
|
7
7
|
import { codes } from './errors.js'
|
8
8
|
import { PeerStreams as PeerStreamsImpl } from './peer-streams.js'
|
9
|
-
import {
|
9
|
+
import { toMessage, ensureArray, randomSeqno, noSignMsgId, msgId, toRpcMessage } from './utils.js'
|
10
10
|
import {
|
11
11
|
signMessage,
|
12
12
|
verifySignature
|
@@ -14,7 +14,7 @@ import {
|
|
14
14
|
import type { PeerId } from '@libp2p/interfaces/peer-id'
|
15
15
|
import type { Registrar, IncomingStreamData } from '@libp2p/interfaces/registrar'
|
16
16
|
import type { Connection } from '@libp2p/interfaces/connection'
|
17
|
-
import type { PubSub, Message, StrictNoSign, StrictSign, PubSubOptions, PubSubEvents,
|
17
|
+
import type { PubSub, Message, StrictNoSign, StrictSign, PubSubOptions, PubSubEvents, RPC, PeerStreams, RPCSubscription, RPCMessage } from '@libp2p/interfaces/pubsub'
|
18
18
|
import type { Logger } from '@libp2p/logger'
|
19
19
|
import { base58btc } from 'multiformats/bases/base58'
|
20
20
|
import { peerMap } from '@libp2p/peer-map'
|
@@ -29,7 +29,7 @@ export interface TopicValidator { (topic: string, message: Message): Promise<voi
|
|
29
29
|
* PubsubBaseProtocol handles the peers and connections logic for pubsub routers
|
30
30
|
* and specifies the API that pubsub routers should have.
|
31
31
|
*/
|
32
|
-
export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap & PubSubEvents> implements PubSub<EventMap & PubSubEvents> {
|
32
|
+
export abstract class PubsubBaseProtocol<EventMap extends PubSubEvents = PubSubEvents> extends EventEmitter<EventMap & PubSubEvents> implements PubSub<EventMap & PubSubEvents> {
|
33
33
|
public peerId: PeerId
|
34
34
|
public started: boolean
|
35
35
|
/**
|
@@ -125,7 +125,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
125
125
|
|
126
126
|
// register protocol with topology
|
127
127
|
// Topology callbacks called on connection manager changes
|
128
|
-
const topology =
|
128
|
+
const topology = createTopology({
|
129
129
|
onConnect: this._onPeerConnected,
|
130
130
|
onDisconnect: this._onPeerDisconnected
|
131
131
|
})
|
@@ -172,10 +172,10 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
172
172
|
protected _onIncomingStream (evt: CustomEvent<IncomingStreamData>) {
|
173
173
|
const { protocol, stream, connection } = evt.detail
|
174
174
|
const peerId = connection.remotePeer
|
175
|
-
const peer = this.
|
175
|
+
const peer = this.addPeer(peerId, protocol)
|
176
176
|
const inboundStream = peer.attachInboundStream(stream)
|
177
177
|
|
178
|
-
this.
|
178
|
+
this.processMessages(peerId, inboundStream, peer)
|
179
179
|
.catch(err => this.log(err))
|
180
180
|
}
|
181
181
|
|
@@ -187,14 +187,14 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
187
187
|
|
188
188
|
try {
|
189
189
|
const { stream, protocol } = await conn.newStream(this.multicodecs)
|
190
|
-
const peer = this.
|
190
|
+
const peer = this.addPeer(peerId, protocol)
|
191
191
|
await peer.attachOutboundStream(stream)
|
192
192
|
} catch (err: any) {
|
193
193
|
this.log.error(err)
|
194
194
|
}
|
195
195
|
|
196
196
|
// Immediately send my own subscriptions to the newly established conn
|
197
|
-
this.
|
197
|
+
this.send(peerId, { subscriptions: Array.from(this.subscriptions).map(sub => sub.toString()), subscribe: true })
|
198
198
|
}
|
199
199
|
|
200
200
|
/**
|
@@ -210,7 +210,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
210
210
|
/**
|
211
211
|
* Notifies the router that a peer has been connected
|
212
212
|
*/
|
213
|
-
|
213
|
+
addPeer (peerId: PeerId, protocol: string): PeerStreams {
|
214
214
|
const existing = this.peers.get(peerId)
|
215
215
|
|
216
216
|
// If peer streams already exists, do nothing
|
@@ -264,31 +264,41 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
264
264
|
/**
|
265
265
|
* Responsible for processing each RPC message received by other peers.
|
266
266
|
*/
|
267
|
-
async
|
267
|
+
async processMessages (peerId: PeerId, stream: AsyncIterable<Uint8Array>, peerStreams: PeerStreams) {
|
268
268
|
try {
|
269
269
|
await pipe(
|
270
270
|
stream,
|
271
271
|
async (source) => {
|
272
272
|
for await (const data of source) {
|
273
|
-
const rpcMsg = this.
|
273
|
+
const rpcMsg = this.decodeRpc(data)
|
274
|
+
const messages: RPCMessage[] = []
|
275
|
+
|
276
|
+
for (const msg of (rpcMsg.messages ?? [])) {
|
277
|
+
if (msg.from == null || msg.data == null || msg.topic == null) {
|
278
|
+
this.log('message from %p was missing from, data or topic fields, dropping', peerId)
|
279
|
+
continue
|
280
|
+
}
|
281
|
+
|
282
|
+
messages.push({
|
283
|
+
from: msg.from,
|
284
|
+
data: msg.data,
|
285
|
+
topic: msg.topic,
|
286
|
+
seqno: msg.seqno ?? undefined,
|
287
|
+
signature: msg.signature ?? undefined,
|
288
|
+
key: msg.key ?? undefined
|
289
|
+
})
|
290
|
+
}
|
274
291
|
|
275
|
-
// Since
|
292
|
+
// Since processRpc may be overridden entirely in unsafe ways,
|
276
293
|
// the simplest/safest option here is to wrap in a function and capture all errors
|
277
294
|
// to prevent a top-level unhandled exception
|
278
295
|
// This processing of rpc messages should happen without awaiting full validation/execution of prior messages
|
279
296
|
this.processRpc(peerId, peerStreams, {
|
280
297
|
subscriptions: (rpcMsg.subscriptions).map(sub => ({
|
281
298
|
subscribe: Boolean(sub.subscribe),
|
282
|
-
|
299
|
+
topic: sub.topic ?? ''
|
283
300
|
})),
|
284
|
-
|
285
|
-
from: msg.from ?? peerId.multihash.bytes,
|
286
|
-
data: msg.data ?? new Uint8Array(0),
|
287
|
-
topicIDs: msg.topicIDs ?? [],
|
288
|
-
seqno: msg.seqno ?? undefined,
|
289
|
-
signature: msg.signature ?? undefined,
|
290
|
-
key: msg.key ?? undefined
|
291
|
-
}))
|
301
|
+
messages
|
292
302
|
})
|
293
303
|
.catch(err => this.log(err))
|
294
304
|
}
|
@@ -303,56 +313,54 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
303
313
|
* Handles an rpc request from a peer
|
304
314
|
*/
|
305
315
|
async processRpc (from: PeerId, peerStreams: PeerStreams, rpc: RPC) {
|
316
|
+
if (!this.acceptFrom(from)) {
|
317
|
+
this.log('received message from unacceptable peer %p', from)
|
318
|
+
return
|
319
|
+
}
|
320
|
+
|
306
321
|
this.log('rpc from %p', from)
|
307
|
-
const subs = rpc.subscriptions
|
308
|
-
const msgs = rpc.msgs
|
309
322
|
|
310
|
-
|
323
|
+
const { subscriptions, messages } = rpc
|
324
|
+
|
325
|
+
if (subscriptions.length > 0) {
|
326
|
+
this.log('subscription update from %p', from)
|
327
|
+
|
311
328
|
// update peer subscriptions
|
312
|
-
|
313
|
-
this.
|
329
|
+
subscriptions.forEach((subOpt) => {
|
330
|
+
this.processRpcSubOpt(from, subOpt)
|
314
331
|
})
|
315
|
-
this.dispatchEvent(new CustomEvent('pubsub:subscription-change', {
|
316
|
-
detail: { peerId: peerStreams.id, subscriptions: subs }
|
317
|
-
}))
|
318
|
-
}
|
319
332
|
|
320
|
-
|
321
|
-
|
322
|
-
|
333
|
+
super.dispatchEvent(new CustomEvent('pubsub:subscription-change', {
|
334
|
+
detail: { peerId: peerStreams.id, subscriptions }
|
335
|
+
}))
|
323
336
|
}
|
324
337
|
|
325
|
-
if (
|
326
|
-
this.
|
327
|
-
const topics = message.topicIDs != null ? message.topicIDs : []
|
328
|
-
const hasSubscription = topics.some((topic) => this.subscriptions.has(topic))
|
338
|
+
if (messages.length > 0) {
|
339
|
+
this.log('messages from %p', from)
|
329
340
|
|
330
|
-
|
341
|
+
this.queue.addAll(messages.map(message => async () => {
|
342
|
+
if (!this.subscriptions.has(message.topic) && !this.canRelayMessage) {
|
331
343
|
this.log('received message we didn\'t subscribe to. Dropping.')
|
332
344
|
return
|
333
345
|
}
|
334
346
|
|
335
347
|
try {
|
336
|
-
const msg = toMessage(
|
337
|
-
...message,
|
338
|
-
from: from.multihash.bytes
|
339
|
-
})
|
348
|
+
const msg = await toMessage(message)
|
340
349
|
|
341
|
-
await this.
|
350
|
+
await this.processMessage(from, msg)
|
342
351
|
} catch (err: any) {
|
343
352
|
this.log.error(err)
|
344
353
|
}
|
345
354
|
}))
|
346
355
|
.catch(err => this.log(err))
|
347
356
|
}
|
348
|
-
return true
|
349
357
|
}
|
350
358
|
|
351
359
|
/**
|
352
360
|
* Handles a subscription change from a peer
|
353
361
|
*/
|
354
|
-
|
355
|
-
const t = subOpt.
|
362
|
+
processRpcSubOpt (id: PeerId, subOpt: RPCSubscription) {
|
363
|
+
const t = subOpt.topic
|
356
364
|
|
357
365
|
if (t == null) {
|
358
366
|
return
|
@@ -376,8 +384,8 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
376
384
|
/**
|
377
385
|
* Handles an message from a peer
|
378
386
|
*/
|
379
|
-
async
|
380
|
-
if (this.peerId.equals(
|
387
|
+
async processMessage (from: PeerId, msg: Message) {
|
388
|
+
if (this.peerId.equals(from) && !this.emitSelf) {
|
381
389
|
return
|
382
390
|
}
|
383
391
|
|
@@ -389,23 +397,17 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
389
397
|
return
|
390
398
|
}
|
391
399
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
return await this._publish(toRpcMessage(msg))
|
396
|
-
}
|
400
|
+
if (this.subscriptions.has(msg.topic)) {
|
401
|
+
const isFromSelf = this.peerId.equals(from)
|
397
402
|
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
emitMessage (message: Message) {
|
402
|
-
message.topicIDs.forEach((topic) => {
|
403
|
-
if (this.subscriptions.has(topic)) {
|
404
|
-
this.dispatchEvent(new CustomEvent(topic, {
|
405
|
-
detail: message
|
403
|
+
if (!isFromSelf || this.emitSelf) {
|
404
|
+
super.dispatchEvent(new CustomEvent(msg.topic, {
|
405
|
+
detail: msg
|
406
406
|
}))
|
407
407
|
}
|
408
|
-
}
|
408
|
+
}
|
409
|
+
|
410
|
+
await this.publishMessage(from, msg)
|
409
411
|
}
|
410
412
|
|
411
413
|
/**
|
@@ -420,7 +422,11 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
420
422
|
throw errcode(new Error('Need seqno when signature policy is StrictSign but it was missing'), codes.ERR_MISSING_SEQNO)
|
421
423
|
}
|
422
424
|
|
423
|
-
|
425
|
+
if (msg.key == null) {
|
426
|
+
throw errcode(new Error('Need key when signature policy is StrictSign but it was missing'), codes.ERR_MISSING_KEY)
|
427
|
+
}
|
428
|
+
|
429
|
+
return msgId(msg.key, msg.seqno)
|
424
430
|
case 'StrictNoSign':
|
425
431
|
return noSignMsgId(msg.data)
|
426
432
|
default:
|
@@ -432,7 +438,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
432
438
|
* Whether to accept a message from a peer
|
433
439
|
* Override to create a graylist
|
434
440
|
*/
|
435
|
-
|
441
|
+
acceptFrom (id: PeerId) {
|
436
442
|
return true
|
437
443
|
}
|
438
444
|
|
@@ -440,7 +446,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
440
446
|
* Decode Uint8Array into an RPC object.
|
441
447
|
* This can be override to use a custom router protobuf.
|
442
448
|
*/
|
443
|
-
|
449
|
+
decodeRpc (bytes: Uint8Array) {
|
444
450
|
return RPCProto.decode(bytes)
|
445
451
|
}
|
446
452
|
|
@@ -448,14 +454,26 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
448
454
|
* Encode RPC object into a Uint8Array.
|
449
455
|
* This can be override to use a custom router protobuf.
|
450
456
|
*/
|
451
|
-
|
457
|
+
encodeRpc (rpc: IRPC) {
|
452
458
|
return RPCProto.encode(rpc).finish()
|
453
459
|
}
|
454
460
|
|
455
461
|
/**
|
456
462
|
* Send an rpc object to a peer
|
457
463
|
*/
|
458
|
-
|
464
|
+
send (peer: PeerId, data: { messages?: Message[], subscriptions?: string[], subscribe?: boolean }) {
|
465
|
+
const { messages, subscriptions, subscribe } = data
|
466
|
+
|
467
|
+
return this.sendRpc(peer, {
|
468
|
+
subscriptions: (subscriptions ?? []).map(str => ({ topic: str, subscribe: Boolean(subscribe) })),
|
469
|
+
messages: (messages ?? []).map(toRpcMessage)
|
470
|
+
})
|
471
|
+
}
|
472
|
+
|
473
|
+
/**
|
474
|
+
* Send an rpc object to a peer
|
475
|
+
*/
|
476
|
+
sendRpc (peer: PeerId, rpc: IRPC) {
|
459
477
|
const peerStreams = this.peers.get(peer)
|
460
478
|
|
461
479
|
if (peerStreams == null || !peerStreams.isWritable) {
|
@@ -465,16 +483,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
465
483
|
return
|
466
484
|
}
|
467
485
|
|
468
|
-
peerStreams.write(this.
|
469
|
-
}
|
470
|
-
|
471
|
-
/**
|
472
|
-
* Send subscriptions to a peer
|
473
|
-
*/
|
474
|
-
_sendSubscriptions (id: PeerId, topics: string[], subscribe: boolean) {
|
475
|
-
return this._sendRpc(id, {
|
476
|
-
subscriptions: topics.map(t => ({ topicID: t, subscribe: subscribe }))
|
477
|
-
})
|
486
|
+
peerStreams.write(this.encodeRpc(rpc))
|
478
487
|
}
|
479
488
|
|
480
489
|
/**
|
@@ -510,11 +519,10 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
510
519
|
throw errcode(new Error('Cannot validate message: unhandled signature policy'), codes.ERR_UNHANDLED_SIGNATURE_POLICY)
|
511
520
|
}
|
512
521
|
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
}
|
522
|
+
const validatorFn = this.topicValidators.get(message.topic)
|
523
|
+
|
524
|
+
if (validatorFn != null) {
|
525
|
+
await validatorFn(message.topic, message)
|
518
526
|
}
|
519
527
|
}
|
520
528
|
|
@@ -522,7 +530,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
522
530
|
* Normalizes the message and signs it, if signing is enabled.
|
523
531
|
* Should be used by the routers to create the message to send.
|
524
532
|
*/
|
525
|
-
|
533
|
+
async buildMessage (message: Message) {
|
526
534
|
const signaturePolicy = this.globalSignaturePolicy
|
527
535
|
switch (signaturePolicy) {
|
528
536
|
case 'StrictSign':
|
@@ -549,7 +557,7 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
549
557
|
throw errcode(new Error('topic is required'), 'ERR_NOT_VALID_TOPIC')
|
550
558
|
}
|
551
559
|
|
552
|
-
const peersInTopic = this.topics.get(topic)
|
560
|
+
const peersInTopic = this.topics.get(topic.toString())
|
553
561
|
|
554
562
|
if (peersInTopic == null) {
|
555
563
|
return []
|
@@ -561,34 +569,55 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
561
569
|
/**
|
562
570
|
* Publishes messages to all subscribed peers
|
563
571
|
*/
|
564
|
-
|
572
|
+
dispatchEvent (event: CustomEvent): boolean {
|
565
573
|
if (!this.started) {
|
566
574
|
throw new Error('Pubsub has not started')
|
567
575
|
}
|
568
576
|
|
569
|
-
|
577
|
+
const topic = event.type
|
578
|
+
let message: Message = event.detail
|
570
579
|
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
580
|
+
if (message instanceof Uint8Array) {
|
581
|
+
message = {
|
582
|
+
from: this.peerId,
|
583
|
+
topic,
|
584
|
+
data: message
|
585
|
+
}
|
575
586
|
}
|
576
587
|
|
577
|
-
|
578
|
-
|
588
|
+
this.log('publish', topic, message)
|
589
|
+
|
590
|
+
Promise.resolve().then(async () => {
|
591
|
+
message = await this.buildMessage(message)
|
592
|
+
|
593
|
+
// dispatch the event if we are interested
|
594
|
+
if (this.emitSelf) {
|
595
|
+
if (this.subscriptions.has(topic)) {
|
596
|
+
super.dispatchEvent(new CustomEvent(topic, {
|
597
|
+
detail: message
|
598
|
+
}))
|
579
599
|
|
580
|
-
|
581
|
-
|
600
|
+
if (this.listenerCount(topic) === 0) {
|
601
|
+
this.unsubscribe(topic)
|
602
|
+
}
|
603
|
+
}
|
604
|
+
}
|
582
605
|
|
583
|
-
|
584
|
-
|
606
|
+
// send to all the other peers
|
607
|
+
await this.publishMessage(this.peerId, message)
|
608
|
+
})
|
609
|
+
.catch(err => {
|
610
|
+
this.log.error(err)
|
611
|
+
})
|
612
|
+
|
613
|
+
return true
|
585
614
|
}
|
586
615
|
|
587
616
|
/**
|
588
617
|
* Overriding the implementation of publish should handle the appropriate algorithms for the publish/subscriber implementation.
|
589
618
|
* For example, a Floodsub implementation might simply publish each message to each topic for every peer
|
590
619
|
*/
|
591
|
-
abstract
|
620
|
+
abstract publishMessage (peerId: PeerId, message: Message): Promise<void>
|
592
621
|
|
593
622
|
/**
|
594
623
|
* Subscribes to a given topic.
|
@@ -598,28 +627,48 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
598
627
|
throw new Error('Pubsub has not started')
|
599
628
|
}
|
600
629
|
|
601
|
-
|
602
|
-
|
630
|
+
const topicStr = topic.toString()
|
631
|
+
|
632
|
+
if (topicStr === 'pubsub:subscription-change') {
|
633
|
+
return
|
634
|
+
}
|
635
|
+
|
636
|
+
if (!this.subscriptions.has(topicStr)) {
|
637
|
+
this.subscriptions.add(topicStr)
|
603
638
|
|
604
639
|
for (const peerId of this.peers.keys()) {
|
605
|
-
this.
|
640
|
+
this.send(peerId, { subscriptions: [topicStr], subscribe: true })
|
606
641
|
}
|
607
642
|
}
|
608
643
|
}
|
609
644
|
|
610
645
|
/**
|
611
|
-
* Unsubscribe from the given topic
|
646
|
+
* Unsubscribe from the given topic
|
612
647
|
*/
|
613
648
|
unsubscribe (topic: string) {
|
614
649
|
if (!this.started) {
|
615
650
|
throw new Error('Pubsub is not started')
|
616
651
|
}
|
617
652
|
|
618
|
-
|
619
|
-
|
653
|
+
// @ts-expect-error topic should be a key of the event map
|
654
|
+
super.removeEventListener(topic)
|
655
|
+
|
656
|
+
const topicStr = topic.toString()
|
657
|
+
|
658
|
+
if (topicStr === 'pubsub:subscription-change') {
|
659
|
+
return
|
660
|
+
}
|
661
|
+
|
662
|
+
const wasSubscribed = this.subscriptions.has(topicStr)
|
663
|
+
const listeners = this.listenerCount(topicStr)
|
664
|
+
|
665
|
+
this.log('unsubscribe from %s - am subscribed %s, listeners %d', topic, wasSubscribed, listeners)
|
666
|
+
|
667
|
+
if (wasSubscribed && listeners === 0) {
|
668
|
+
this.subscriptions.delete(topicStr)
|
620
669
|
|
621
670
|
for (const peerId of this.peers.keys()) {
|
622
|
-
this.
|
671
|
+
this.send(peerId, { subscriptions: [topicStr], subscribe: false })
|
623
672
|
}
|
624
673
|
}
|
625
674
|
}
|
@@ -642,4 +691,20 @@ export abstract class PubsubBaseProtocol<EventMap> extends EventEmitter<EventMap
|
|
642
691
|
|
643
692
|
return Array.from(this.peers.keys())
|
644
693
|
}
|
694
|
+
|
695
|
+
addEventListener<U extends keyof EventMap> (type: U, callback: EventHandler<EventMap[U]>, options?: AddEventListenerOptions | boolean) {
|
696
|
+
this.subscribe(type.toString())
|
697
|
+
|
698
|
+
super.addEventListener(type, callback, options)
|
699
|
+
}
|
700
|
+
|
701
|
+
removeEventListener<U extends keyof EventMap> (type: U, callback: EventHandler<EventMap[U]> | undefined, options?: EventListenerOptions | boolean) {
|
702
|
+
super.removeEventListener(type, callback, options)
|
703
|
+
|
704
|
+
const topicStr = type.toString()
|
705
|
+
|
706
|
+
if (this.listenerCount(topicStr) === 0) {
|
707
|
+
this.unsubscribe(topicStr)
|
708
|
+
}
|
709
|
+
}
|
645
710
|
}
|