@waku/core 0.0.36-f911bf8.0 → 0.0.37-2ed5ddc.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 (79) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/bundle/index.js +5647 -1359
  3. package/bundle/lib/message/version_0.js +1 -2
  4. package/bundle/{version_0-CiYGrPc2.js → version_0-Bc0h7ah2.js} +1886 -31
  5. package/dist/.tsbuildinfo +1 -1
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.js +1 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/connection_manager/connection_limiter.d.ts +35 -0
  10. package/dist/lib/connection_manager/connection_limiter.js +103 -0
  11. package/dist/lib/connection_manager/connection_limiter.js.map +1 -0
  12. package/dist/lib/connection_manager/connection_manager.d.ts +19 -104
  13. package/dist/lib/connection_manager/connection_manager.js +64 -499
  14. package/dist/lib/connection_manager/connection_manager.js.map +1 -1
  15. package/dist/lib/connection_manager/discovery_dialer.d.ts +32 -0
  16. package/dist/lib/connection_manager/discovery_dialer.js +131 -0
  17. package/dist/lib/connection_manager/discovery_dialer.js.map +1 -0
  18. package/dist/lib/connection_manager/keep_alive_manager.d.ts +17 -7
  19. package/dist/lib/connection_manager/keep_alive_manager.js +110 -74
  20. package/dist/lib/connection_manager/keep_alive_manager.js.map +1 -1
  21. package/dist/lib/connection_manager/network_monitor.d.ts +22 -0
  22. package/dist/lib/connection_manager/network_monitor.js +63 -0
  23. package/dist/lib/connection_manager/network_monitor.js.map +1 -0
  24. package/dist/lib/connection_manager/shard_reader.d.ts +28 -0
  25. package/dist/lib/connection_manager/shard_reader.js +70 -0
  26. package/dist/lib/connection_manager/shard_reader.js.map +1 -0
  27. package/dist/lib/connection_manager/utils.d.ts +16 -1
  28. package/dist/lib/connection_manager/utils.js +23 -0
  29. package/dist/lib/connection_manager/utils.js.map +1 -1
  30. package/dist/lib/filter/filter.d.ts +7 -5
  31. package/dist/lib/filter/filter.js +14 -11
  32. package/dist/lib/filter/filter.js.map +1 -1
  33. package/dist/lib/light_push/light_push.d.ts +5 -5
  34. package/dist/lib/light_push/light_push.js +7 -7
  35. package/dist/lib/light_push/light_push.js.map +1 -1
  36. package/dist/lib/message/version_0.d.ts +3 -4
  37. package/dist/lib/message/version_0.js +1 -4
  38. package/dist/lib/message/version_0.js.map +1 -1
  39. package/dist/lib/message_hash/index.d.ts +1 -0
  40. package/dist/lib/message_hash/index.js +2 -0
  41. package/dist/lib/message_hash/index.js.map +1 -0
  42. package/dist/lib/message_hash/message_hash.d.ts +52 -0
  43. package/dist/lib/message_hash/message_hash.js +84 -0
  44. package/dist/lib/message_hash/message_hash.js.map +1 -0
  45. package/dist/lib/metadata/metadata.js +6 -4
  46. package/dist/lib/metadata/metadata.js.map +1 -1
  47. package/dist/lib/store/rpc.js +16 -10
  48. package/dist/lib/store/rpc.js.map +1 -1
  49. package/dist/lib/store/store.d.ts +5 -5
  50. package/dist/lib/store/store.js +19 -9
  51. package/dist/lib/store/store.js.map +1 -1
  52. package/dist/lib/stream_manager/stream_manager.d.ts +3 -4
  53. package/dist/lib/stream_manager/stream_manager.js +6 -8
  54. package/dist/lib/stream_manager/stream_manager.js.map +1 -1
  55. package/package.json +1 -1
  56. package/src/index.ts +2 -0
  57. package/src/lib/connection_manager/connection_limiter.ts +161 -0
  58. package/src/lib/connection_manager/connection_manager.ts +87 -668
  59. package/src/lib/connection_manager/discovery_dialer.ts +195 -0
  60. package/src/lib/connection_manager/keep_alive_manager.ts +154 -87
  61. package/src/lib/connection_manager/network_monitor.ts +88 -0
  62. package/src/lib/connection_manager/shard_reader.ts +134 -0
  63. package/src/lib/connection_manager/utils.ts +27 -1
  64. package/src/lib/filter/filter.ts +26 -15
  65. package/src/lib/light_push/light_push.ts +9 -10
  66. package/src/lib/message/version_0.ts +3 -7
  67. package/src/lib/message_hash/index.ts +1 -0
  68. package/src/lib/message_hash/message_hash.ts +106 -0
  69. package/src/lib/metadata/metadata.ts +8 -5
  70. package/src/lib/store/rpc.ts +23 -19
  71. package/src/lib/store/store.ts +22 -11
  72. package/src/lib/stream_manager/stream_manager.ts +8 -6
  73. package/bundle/base_protocol-DvQrudwy.js +0 -152
  74. package/bundle/index-CTo1my9M.js +0 -1543
  75. package/bundle/lib/base_protocol.js +0 -2
  76. package/dist/lib/base_protocol.d.ts +0 -18
  77. package/dist/lib/base_protocol.js +0 -25
  78. package/dist/lib/base_protocol.js.map +0 -1
  79. package/src/lib/base_protocol.ts +0 -44
@@ -0,0 +1,195 @@
1
+ import { Peer, PeerId, PeerInfo } from "@libp2p/interface";
2
+ import { Multiaddr } from "@multiformats/multiaddr";
3
+ import { Logger } from "@waku/utils";
4
+ import { Libp2p } from "libp2p";
5
+
6
+ import type { ShardReader } from "./shard_reader.js";
7
+
8
+ type Libp2pEventHandler<T> = (e: CustomEvent<T>) => void;
9
+
10
+ type DiscoveryDialerConstructorOptions = {
11
+ libp2p: Libp2p;
12
+ shardReader: ShardReader;
13
+ };
14
+
15
+ interface IDiscoveryDialer {
16
+ start(): void;
17
+ stop(): void;
18
+ }
19
+
20
+ const log = new Logger("discovery-dialer");
21
+
22
+ /**
23
+ * This class is responsible for dialing peers that are discovered by the libp2p node.
24
+ * Managing limits for the peers is out of scope for this class.
25
+ * Dialing after discovery is needed to identify the peer and get all other information: metadata, protocols, etc.
26
+ */
27
+ export class DiscoveryDialer implements IDiscoveryDialer {
28
+ private readonly libp2p: Libp2p;
29
+ private readonly shardReader: ShardReader;
30
+
31
+ private dialingInterval: NodeJS.Timeout | null = null;
32
+ private dialingQueue: PeerId[] = [];
33
+ private dialHistory: Set<string> = new Set();
34
+
35
+ public constructor(options: DiscoveryDialerConstructorOptions) {
36
+ this.libp2p = options.libp2p;
37
+ this.shardReader = options.shardReader;
38
+
39
+ this.onPeerDiscovery = this.onPeerDiscovery.bind(this);
40
+ }
41
+
42
+ public start(): void {
43
+ log.info("Starting discovery dialer");
44
+
45
+ this.libp2p.addEventListener(
46
+ "peer:discovery",
47
+ this.onPeerDiscovery as Libp2pEventHandler<PeerInfo>
48
+ );
49
+
50
+ if (!this.dialingInterval) {
51
+ this.dialingInterval = setInterval(() => {
52
+ void this.processQueue();
53
+ }, 500);
54
+
55
+ log.info("Started dialing interval processor");
56
+ }
57
+
58
+ this.dialHistory.clear();
59
+ }
60
+
61
+ public stop(): void {
62
+ log.info("Stopping discovery dialer");
63
+
64
+ this.libp2p.removeEventListener(
65
+ "peer:discovery",
66
+ this.onPeerDiscovery as Libp2pEventHandler<PeerInfo>
67
+ );
68
+
69
+ if (this.dialingInterval) {
70
+ clearInterval(this.dialingInterval);
71
+ this.dialingInterval = null;
72
+
73
+ log.info("Stopped dialing interval processor");
74
+ }
75
+
76
+ this.dialHistory.clear();
77
+ }
78
+
79
+ private async onPeerDiscovery(event: CustomEvent<PeerInfo>): Promise<void> {
80
+ const peerId = event.detail.id;
81
+ log.info(`Discovered new peer: ${peerId}`);
82
+
83
+ try {
84
+ const shouldSkip = await this.shouldSkipPeer(peerId);
85
+
86
+ if (shouldSkip) {
87
+ log.info(`Skipping peer: ${peerId}`);
88
+ return;
89
+ }
90
+
91
+ await this.updatePeerStore(peerId, event.detail.multiaddrs);
92
+
93
+ if (this.dialingQueue.length === 0) {
94
+ await this.dialPeer(peerId);
95
+ } else {
96
+ this.dialingQueue.push(peerId);
97
+
98
+ log.info(
99
+ `Added peer to dialing queue, queue size: ${this.dialingQueue.length}`
100
+ );
101
+ }
102
+ } catch (error) {
103
+ log.error(`Error dialing peer ${peerId}`, error);
104
+ }
105
+ }
106
+
107
+ private async processQueue(): Promise<void> {
108
+ if (this.dialingQueue.length === 0) return;
109
+
110
+ const peersToDial = this.dialingQueue.slice(0, 3);
111
+ this.dialingQueue = this.dialingQueue.slice(peersToDial.length);
112
+
113
+ log.info(
114
+ `Processing dial queue: dialing ${peersToDial.length} peers, ${this.dialingQueue.length} remaining in queue`
115
+ );
116
+
117
+ await Promise.all(peersToDial.map(this.dialPeer));
118
+ }
119
+
120
+ private async shouldSkipPeer(peerId: PeerId): Promise<boolean> {
121
+ if (this.dialHistory.has(peerId.toString())) {
122
+ return true;
123
+ }
124
+
125
+ const hasShardInfo = await this.shardReader.hasShardInfo(peerId);
126
+ if (!hasShardInfo) {
127
+ return false;
128
+ }
129
+
130
+ const isOnSameShard = await this.shardReader.isPeerOnNetwork(peerId);
131
+ if (!isOnSameShard) {
132
+ log.info(`Skipping peer ${peerId} - not on same shard`);
133
+ return true;
134
+ }
135
+
136
+ const hasConnection = this.libp2p.getPeers().some((p) => p.equals(peerId));
137
+ if (hasConnection) {
138
+ return true;
139
+ }
140
+
141
+ return false;
142
+ }
143
+
144
+ private async updatePeerStore(
145
+ peerId: PeerId,
146
+ multiaddrs: Multiaddr[]
147
+ ): Promise<void> {
148
+ try {
149
+ const peer = await this.getPeer(peerId);
150
+
151
+ if (!peer) {
152
+ await this.libp2p.peerStore.save(peerId, {
153
+ multiaddrs: multiaddrs
154
+ });
155
+ return;
156
+ }
157
+
158
+ const hasSameAddr = multiaddrs.every((addr) =>
159
+ peer.addresses.some((a) => a.multiaddr.equals(addr))
160
+ );
161
+
162
+ if (hasSameAddr) {
163
+ return;
164
+ }
165
+
166
+ await this.libp2p.peerStore.merge(peerId, {
167
+ multiaddrs: multiaddrs
168
+ });
169
+ } catch (error) {
170
+ log.error(`Error updating peer store for ${peerId}`, error);
171
+ }
172
+ }
173
+
174
+ private async dialPeer(peerId: PeerId): Promise<void> {
175
+ try {
176
+ log.info(`Dialing peer from queue: ${peerId}`);
177
+
178
+ await this.libp2p.dial(peerId);
179
+ this.dialHistory.add(peerId.toString());
180
+
181
+ log.info(`Successfully dialed peer from queue: ${peerId}`);
182
+ } catch (error) {
183
+ log.error(`Error dialing peer ${peerId}`, error);
184
+ }
185
+ }
186
+
187
+ private async getPeer(peerId: PeerId): Promise<Peer | undefined> {
188
+ try {
189
+ return await this.libp2p.peerStore.get(peerId);
190
+ } catch (error) {
191
+ log.error(`Error getting peer info for ${peerId}`, error);
192
+ return undefined;
193
+ }
194
+ }
195
+ }
@@ -1,5 +1,5 @@
1
1
  import type { PeerId } from "@libp2p/interface";
2
- import type { IRelay, Libp2p, PeerIdStr } from "@waku/interfaces";
2
+ import type { IEncoder, IRelay, Libp2p } from "@waku/interfaces";
3
3
  import { Logger, pubsubTopicToSingleShardInfo } from "@waku/utils";
4
4
  import { utf8ToBytes } from "@waku/utils/bytes";
5
5
 
@@ -19,7 +19,12 @@ type CreateKeepAliveManagerOptions = {
19
19
  relay?: IRelay;
20
20
  };
21
21
 
22
- export class KeepAliveManager {
22
+ interface IKeepAliveManager {
23
+ start(): void;
24
+ stop(): void;
25
+ }
26
+
27
+ export class KeepAliveManager implements IKeepAliveManager {
23
28
  private readonly relay?: IRelay;
24
29
  private readonly libp2p: Libp2p;
25
30
 
@@ -27,7 +32,7 @@ export class KeepAliveManager {
27
32
 
28
33
  private pingKeepAliveTimers: Map<string, ReturnType<typeof setInterval>> =
29
34
  new Map();
30
- private relayKeepAliveTimers: Map<PeerId, ReturnType<typeof setInterval>[]> =
35
+ private relayKeepAliveTimers: Map<string, ReturnType<typeof setInterval>[]> =
31
36
  new Map();
32
37
 
33
38
  public constructor({
@@ -38,122 +43,184 @@ export class KeepAliveManager {
38
43
  this.options = options;
39
44
  this.relay = relay;
40
45
  this.libp2p = libp2p;
46
+
47
+ this.onPeerConnect = this.onPeerConnect.bind(this);
48
+ this.onPeerDisconnect = this.onPeerDisconnect.bind(this);
41
49
  }
42
50
 
43
- public start(peerId: PeerId): void {
44
- // Just in case a timer already exists for this peer
45
- this.stop(peerId);
51
+ public start(): void {
52
+ this.libp2p.addEventListener("peer:connect", this.onPeerConnect);
53
+ this.libp2p.addEventListener("peer:disconnect", this.onPeerDisconnect);
54
+ }
46
55
 
47
- const { pingKeepAlive: pingPeriodSecs, relayKeepAlive: relayPeriodSecs } =
48
- this.options;
56
+ public stop(): void {
57
+ this.libp2p.removeEventListener("peer:connect", this.onPeerConnect);
58
+ this.libp2p.removeEventListener("peer:disconnect", this.onPeerDisconnect);
49
59
 
50
- const peerIdStr = peerId.toString();
60
+ for (const timer of this.pingKeepAliveTimers.values()) {
61
+ clearInterval(timer);
62
+ }
51
63
 
52
- // Ping the peer every pingPeriodSecs seconds
53
- // if pingPeriodSecs is 0, don't ping the peer
54
- if (pingPeriodSecs !== 0) {
55
- const interval = setInterval(() => {
56
- void (async () => {
57
- let ping: number;
58
- try {
59
- // ping the peer for keep alive
60
- // also update the peer store with the latency
61
- try {
62
- ping = await this.libp2p.services.ping.ping(peerId);
63
- log.info(`Ping succeeded (${peerIdStr})`, ping);
64
- } catch (error) {
65
- log.error(`Ping failed for peer (${peerIdStr}).
66
- Next ping will be attempted in ${pingPeriodSecs} seconds.
67
- `);
68
- return;
69
- }
70
-
71
- try {
72
- await this.libp2p.peerStore.merge(peerId, {
73
- metadata: {
74
- ping: utf8ToBytes(ping.toString())
75
- }
76
- });
77
- } catch (e) {
78
- log.error("Failed to update ping", e);
79
- }
80
- } catch (e) {
81
- log.error(`Ping failed (${peerIdStr})`, e);
82
- }
83
- })();
84
- }, pingPeriodSecs * 1000);
85
-
86
- this.pingKeepAliveTimers.set(peerIdStr, interval);
64
+ for (const timerArray of this.relayKeepAliveTimers.values()) {
65
+ for (const timer of timerArray) {
66
+ clearInterval(timer);
67
+ }
87
68
  }
88
69
 
89
- const relay = this.relay;
90
- if (relay && relayPeriodSecs !== 0) {
91
- const intervals = this.scheduleRelayPings(
92
- relay,
93
- relayPeriodSecs,
94
- peerId.toString()
70
+ this.pingKeepAliveTimers.clear();
71
+ this.relayKeepAliveTimers.clear();
72
+ }
73
+
74
+ private onPeerConnect(evt: CustomEvent<PeerId>): void {
75
+ const peerId = evt.detail;
76
+ this.startPingForPeer(peerId);
77
+ }
78
+
79
+ private onPeerDisconnect(evt: CustomEvent<PeerId>): void {
80
+ const peerId = evt.detail;
81
+ this.stopPingForPeer(peerId);
82
+ }
83
+
84
+ private startPingForPeer(peerId: PeerId): void {
85
+ // Just in case a timer already exists for this peer
86
+ this.stopPingForPeer(peerId);
87
+
88
+ this.startLibp2pPing(peerId);
89
+ this.startRelayPing(peerId);
90
+ }
91
+
92
+ private stopPingForPeer(peerId: PeerId): void {
93
+ this.stopLibp2pPing(peerId);
94
+ this.stopRelayPing(peerId);
95
+ }
96
+
97
+ private startLibp2pPing(peerId: PeerId): void {
98
+ if (this.options.pingKeepAlive === 0) {
99
+ log.warn(
100
+ `Ping keep alive is disabled pingKeepAlive:${this.options.pingKeepAlive}, skipping start for libp2p ping`
95
101
  );
96
- this.relayKeepAliveTimers.set(peerId, intervals);
102
+ return;
97
103
  }
98
- }
99
104
 
100
- public stop(peerId: PeerId): void {
101
105
  const peerIdStr = peerId.toString();
102
106
 
103
107
  if (this.pingKeepAliveTimers.has(peerIdStr)) {
104
- clearInterval(this.pingKeepAliveTimers.get(peerIdStr));
105
- this.pingKeepAliveTimers.delete(peerIdStr);
108
+ log.warn(
109
+ `Ping already started for peer: ${peerIdStr}, skipping start for libp2p ping`
110
+ );
111
+ return;
106
112
  }
107
113
 
108
- if (this.relayKeepAliveTimers.has(peerId)) {
109
- this.relayKeepAliveTimers.get(peerId)?.map(clearInterval);
110
- this.relayKeepAliveTimers.delete(peerId);
111
- }
114
+ const interval = setInterval(() => {
115
+ void this.pingLibp2p(peerId);
116
+ }, this.options.pingKeepAlive * 1000);
117
+
118
+ this.pingKeepAliveTimers.set(peerIdStr, interval);
112
119
  }
113
120
 
114
- public stopAll(): void {
115
- for (const timer of [
116
- ...Object.values(this.pingKeepAliveTimers),
117
- ...Object.values(this.relayKeepAliveTimers)
118
- ]) {
119
- clearInterval(timer);
121
+ private stopLibp2pPing(peerId: PeerId): void {
122
+ const peerIdStr = peerId.toString();
123
+
124
+ if (!this.pingKeepAliveTimers.has(peerIdStr)) {
125
+ log.warn(
126
+ `Ping not started for peer: ${peerIdStr}, skipping stop for ping`
127
+ );
128
+ return;
120
129
  }
121
130
 
122
- this.pingKeepAliveTimers.clear();
123
- this.relayKeepAliveTimers.clear();
131
+ clearInterval(this.pingKeepAliveTimers.get(peerIdStr));
132
+ this.pingKeepAliveTimers.delete(peerIdStr);
124
133
  }
125
134
 
126
- public connectionsExist(): boolean {
127
- return (
128
- this.pingKeepAliveTimers.size > 0 || this.relayKeepAliveTimers.size > 0
129
- );
130
- }
135
+ private startRelayPing(peerId: PeerId): void {
136
+ if (!this.relay) {
137
+ return;
138
+ }
139
+
140
+ if (this.options.relayKeepAlive === 0) {
141
+ log.warn(
142
+ `Relay keep alive is disabled relayKeepAlive:${this.options.relayKeepAlive}, skipping start for relay ping`
143
+ );
144
+ return;
145
+ }
146
+
147
+ if (this.relayKeepAliveTimers.has(peerId.toString())) {
148
+ log.warn(
149
+ `Relay ping already started for peer: ${peerId.toString()}, skipping start for relay ping`
150
+ );
151
+ return;
152
+ }
131
153
 
132
- private scheduleRelayPings(
133
- relay: IRelay,
134
- relayPeriodSecs: number,
135
- peerIdStr: PeerIdStr
136
- ): NodeJS.Timeout[] {
137
- // send a ping message to each PubsubTopic the peer is part of
138
154
  const intervals: NodeJS.Timeout[] = [];
139
- for (const topic of relay.pubsubTopics) {
140
- const meshPeers = relay.getMeshPeers(topic);
141
- if (!meshPeers.includes(peerIdStr)) continue;
155
+
156
+ for (const topic of this.relay.pubsubTopics) {
157
+ const meshPeers = this.relay.getMeshPeers(topic);
158
+
159
+ if (!meshPeers.includes(peerId.toString())) {
160
+ log.warn(
161
+ `Peer: ${peerId.toString()} is not in the mesh for topic: ${topic}, skipping start for relay ping`
162
+ );
163
+ continue;
164
+ }
142
165
 
143
166
  const encoder = createEncoder({
144
167
  pubsubTopicShardInfo: pubsubTopicToSingleShardInfo(topic),
145
168
  contentTopic: RelayPingContentTopic,
146
169
  ephemeral: true
147
170
  });
171
+
148
172
  const interval = setInterval(() => {
149
- log.info("Sending Waku Relay ping message");
150
- relay
151
- .send(encoder, { payload: new Uint8Array([1]) })
152
- .catch((e) => log.error("Failed to send relay ping", e));
153
- }, relayPeriodSecs * 1000);
173
+ void this.pingRelay(encoder);
174
+ }, this.options.relayKeepAlive * 1000);
175
+
154
176
  intervals.push(interval);
155
177
  }
156
178
 
157
- return intervals;
179
+ this.relayKeepAliveTimers.set(peerId.toString(), intervals);
180
+ }
181
+
182
+ private stopRelayPing(peerId: PeerId): void {
183
+ if (!this.relay) {
184
+ return;
185
+ }
186
+
187
+ const peerIdStr = peerId.toString();
188
+
189
+ if (!this.relayKeepAliveTimers.has(peerIdStr)) {
190
+ log.warn(
191
+ `Relay ping not started for peer: ${peerIdStr}, skipping stop for relay ping`
192
+ );
193
+ return;
194
+ }
195
+
196
+ this.relayKeepAliveTimers.get(peerIdStr)?.map(clearInterval);
197
+ this.relayKeepAliveTimers.delete(peerIdStr);
198
+ }
199
+
200
+ private async pingRelay(encoder: IEncoder): Promise<void> {
201
+ try {
202
+ log.info("Sending Waku Relay ping message");
203
+ await this.relay!.send(encoder, { payload: new Uint8Array([1]) });
204
+ } catch (e) {
205
+ log.error("Failed to send relay ping", e);
206
+ }
207
+ }
208
+
209
+ private async pingLibp2p(peerId: PeerId): Promise<void> {
210
+ try {
211
+ log.info(`Pinging libp2p peer (${peerId.toString()})`);
212
+ const ping = await this.libp2p.services.ping.ping(peerId);
213
+
214
+ log.info(`Ping succeeded (${peerId.toString()})`, ping);
215
+
216
+ await this.libp2p.peerStore.merge(peerId, {
217
+ metadata: {
218
+ ping: utf8ToBytes(ping.toString())
219
+ }
220
+ });
221
+ log.info(`Ping updated for peer (${peerId.toString()})`);
222
+ } catch (e) {
223
+ log.error(`Ping failed for peer (${peerId.toString()})`, e);
224
+ }
158
225
  }
159
226
  }
@@ -0,0 +1,88 @@
1
+ import { IWakuEventEmitter, Libp2p } from "@waku/interfaces";
2
+
3
+ type NetworkMonitorConstructorOptions = {
4
+ libp2p: Libp2p;
5
+ events: IWakuEventEmitter;
6
+ };
7
+
8
+ interface INetworkMonitor {
9
+ start(): void;
10
+ stop(): void;
11
+ }
12
+
13
+ export class NetworkMonitor implements INetworkMonitor {
14
+ private readonly libp2p: Libp2p;
15
+ private readonly events: IWakuEventEmitter;
16
+
17
+ private isNetworkConnected: boolean = false;
18
+
19
+ public constructor(options: NetworkMonitorConstructorOptions) {
20
+ this.libp2p = options.libp2p;
21
+ this.events = options.events;
22
+
23
+ this.onConnectedEvent = this.onConnectedEvent.bind(this);
24
+ this.onDisconnectedEvent = this.onDisconnectedEvent.bind(this);
25
+ this.dispatchNetworkEvent = this.dispatchNetworkEvent.bind(this);
26
+ }
27
+
28
+ public start(): void {
29
+ this.libp2p.addEventListener("peer:connect", this.onConnectedEvent);
30
+ this.libp2p.addEventListener("peer:disconnect", this.onDisconnectedEvent);
31
+
32
+ try {
33
+ globalThis.addEventListener("online", this.dispatchNetworkEvent);
34
+ globalThis.addEventListener("offline", this.dispatchNetworkEvent);
35
+ } catch (err) {
36
+ // ignore
37
+ }
38
+ }
39
+
40
+ public stop(): void {
41
+ this.libp2p.removeEventListener("peer:connect", this.onConnectedEvent);
42
+ this.libp2p.removeEventListener(
43
+ "peer:disconnect",
44
+ this.onDisconnectedEvent
45
+ );
46
+
47
+ try {
48
+ globalThis.removeEventListener("online", this.dispatchNetworkEvent);
49
+ globalThis.removeEventListener("offline", this.dispatchNetworkEvent);
50
+ } catch (err) {
51
+ // ignore
52
+ }
53
+ }
54
+
55
+ public isConnected(): boolean {
56
+ try {
57
+ if (globalThis?.navigator && !globalThis?.navigator?.onLine) {
58
+ return false;
59
+ }
60
+ } catch (err) {
61
+ // ignore
62
+ }
63
+
64
+ return this.isNetworkConnected;
65
+ }
66
+
67
+ private onConnectedEvent(): void {
68
+ if (!this.isNetworkConnected) {
69
+ this.isNetworkConnected = true;
70
+ this.dispatchNetworkEvent();
71
+ }
72
+ }
73
+
74
+ private onDisconnectedEvent(): void {
75
+ if (this.isNetworkConnected && this.libp2p.getConnections().length === 0) {
76
+ this.isNetworkConnected = false;
77
+ this.dispatchNetworkEvent();
78
+ }
79
+ }
80
+
81
+ private dispatchNetworkEvent(): void {
82
+ this.events.dispatchEvent(
83
+ new CustomEvent<boolean>("waku:connection", {
84
+ detail: this.isConnected()
85
+ })
86
+ );
87
+ }
88
+ }