Skip to content

Commit 27292ed

Browse files
authored
feat!: unify events under one source (#2473)
* move health indicator under waku.events and expose from Waku as a value * update tests * make new type for libp2p event handlers * fix types
1 parent 14085de commit 27292ed

File tree

11 files changed

+128
-124
lines changed

11 files changed

+128
-124
lines changed

packages/core/src/lib/connection_manager/connection_limiter.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
ConnectionManagerOptions,
44
IWakuEventEmitter,
55
Libp2p,
6+
Libp2pEventHandler,
67
Tags
78
} from "@waku/interfaces";
89
import { Logger } from "@waku/utils";
@@ -12,8 +13,6 @@ import { NetworkMonitor } from "./network_monitor.js";
1213

1314
const log = new Logger("connection-limiter");
1415

15-
type Libp2pEventHandler<T> = (e: CustomEvent<T>) => void;
16-
1716
type ConnectionLimiterConstructorOptions = {
1817
libp2p: Libp2p;
1918
events: IWakuEventEmitter;

packages/core/src/lib/connection_manager/discovery_dialer.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
import { Peer, PeerId, PeerInfo } from "@libp2p/interface";
1+
import { Libp2p, Peer, PeerId, PeerInfo } from "@libp2p/interface";
22
import { Multiaddr } from "@multiformats/multiaddr";
3+
import { Libp2pEventHandler } from "@waku/interfaces";
34
import { Logger } from "@waku/utils";
4-
import { Libp2p } from "libp2p";
55

66
import { Dialer } from "./dialer.js";
77

8-
type Libp2pEventHandler<T> = (e: CustomEvent<T>) => void;
9-
108
type DiscoveryDialerConstructorOptions = {
119
libp2p: Libp2p;
1210
dialer: Dialer;

packages/interfaces/src/health_indicator.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export enum HealthStatus {
2+
/**
3+
* No peer connections
4+
*/
5+
Unhealthy = "Unhealthy",
6+
7+
/**
8+
* At least 1 peer supporting both Filter and LightPush protocols
9+
*/
10+
MinimallyHealthy = "MinimallyHealthy",
11+
12+
/**
13+
* At least 2 peers supporting both Filter and LightPush protocols
14+
*/
15+
SufficientlyHealthy = "SufficientlyHealthy"
16+
}

packages/interfaces/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ export * from "./metadata.js";
1717
export * from "./constants.js";
1818
export * from "./local_storage.js";
1919
export * from "./sharding.js";
20-
export * from "./health_indicator.js";
20+
export * from "./health_status.js";

packages/interfaces/src/libp2p.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,5 @@ export type CreateLibp2pOptions = Libp2pOptions & {
3636
*/
3737
filterMultiaddrs?: boolean;
3838
};
39+
40+
export type Libp2pEventHandler<T> = (e: CustomEvent<T>) => void;

packages/interfaces/src/waku.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type {
77
import type { MultiaddrInput } from "@multiformats/multiaddr";
88

99
import type { IFilter } from "./filter.js";
10-
import type { IHealthIndicator } from "./health_indicator.js";
10+
import type { HealthStatus } from "./health_status.js";
1111
import type { Libp2p } from "./libp2p.js";
1212
import type { ILightPush } from "./light_push.js";
1313
import { IDecodedMessage, IDecoder, IEncoder } from "./message.js";
@@ -35,7 +35,27 @@ export type CreateEncoderParams = CreateDecoderParams & {
3535
};
3636

3737
export interface IWakuEvents {
38+
/**
39+
* Emitted when a connection is established or lost.
40+
*
41+
* @example
42+
* ```typescript
43+
* waku.addEventListener("waku:connection", (event) => {
44+
* console.log(event.detail); // true if connected, false if disconnected
45+
* });
46+
*/
3847
"waku:connection": CustomEvent<boolean>;
48+
49+
/**
50+
* Emitted when the health status changes.
51+
*
52+
* @example
53+
* ```typescript
54+
* waku.addEventListener("waku:health", (event) => {
55+
* console.log(event.detail); // 'Unhealthy', 'MinimallyHealthy', or 'SufficientlyHealthy'
56+
* });
57+
*/
58+
"waku:health": CustomEvent<HealthStatus>;
3959
}
4060

4161
export type IWakuEventEmitter = TypedEventEmitter<IWakuEvents>;
@@ -47,7 +67,19 @@ export interface IWaku {
4767
filter?: IFilter;
4868
lightPush?: ILightPush;
4969

50-
health: IHealthIndicator;
70+
/**
71+
* Emits events related to the Waku node.
72+
* Those are:
73+
* - "waku:connection"
74+
* - "waku:health"
75+
*
76+
* @example
77+
* ```typescript
78+
* waku.events.addEventListener("waku:connection", (event) => {
79+
* console.log(event.detail); // true if connected, false if disconnected
80+
* });
81+
* ```
82+
*/
5183
events: IWakuEventEmitter;
5284

5385
/**
@@ -60,6 +92,19 @@ export interface IWaku {
6092
*/
6193
peerId: PeerId;
6294

95+
/**
96+
* The health status can be one of three states:
97+
* - Unhealthy: No peer connections
98+
* - MinimallyHealthy: At least 1 peer supporting both Filter and LightPush protocols
99+
* - SufficientlyHealthy: At least 2 peers supporting both Filter and LightPush protocols
100+
*
101+
* @example
102+
* ```typescript
103+
* console.log(waku.health); // 'Unhealthy'
104+
* ```
105+
*/
106+
health: HealthStatus;
107+
63108
/**
64109
* Returns a list of supported protocols.
65110
*

packages/sdk/src/health_indicator/health_indicator.spec.ts

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
import { Connection, Peer } from "@libp2p/interface";
22
import { FilterCodecs, LightPushCodec } from "@waku/core";
3-
import {
4-
HealthStatus,
5-
HealthStatusChangeEvents,
6-
Libp2p
7-
} from "@waku/interfaces";
3+
import { HealthStatus, IWakuEventEmitter, Libp2p } from "@waku/interfaces";
84
import { expect } from "chai";
95
import sinon from "sinon";
106

117
import { HealthIndicator } from "./health_indicator.js";
128

139
describe("HealthIndicator", () => {
1410
let libp2p: Libp2p;
11+
let events: IWakuEventEmitter;
1512
let healthIndicator: HealthIndicator;
1613

1714
beforeEach(() => {
1815
libp2p = mockLibp2p();
19-
healthIndicator = new HealthIndicator({ libp2p });
16+
events = mockEvents();
17+
healthIndicator = new HealthIndicator({ libp2p, events });
2018
healthIndicator.start();
2119
});
2220

@@ -26,14 +24,13 @@ describe("HealthIndicator", () => {
2624
});
2725

2826
it("should initialize with Unhealthy status", () => {
29-
expect(healthIndicator.toString()).to.equal(HealthStatus.Unhealthy);
27+
expect(healthIndicator.toValue()).to.equal(HealthStatus.Unhealthy);
3028
});
3129

3230
it("should transition to Unhealthy when no connections", async () => {
3331
const statusChangePromise = new Promise<HealthStatus>((resolve) => {
34-
healthIndicator.addEventListener(
35-
HealthStatusChangeEvents.StatusChange,
36-
(e: CustomEvent<HealthStatus>) => resolve(e.detail)
32+
events.addEventListener("waku:health", (e: CustomEvent<HealthStatus>) =>
33+
resolve(e.detail)
3734
);
3835
});
3936

@@ -44,14 +41,13 @@ describe("HealthIndicator", () => {
4441

4542
const changedStatus = await statusChangePromise;
4643
expect(changedStatus).to.equal(HealthStatus.Unhealthy);
47-
expect(healthIndicator.toString()).to.equal(HealthStatus.Unhealthy);
44+
expect(healthIndicator.toValue()).to.equal(HealthStatus.Unhealthy);
4845
});
4946

5047
it("should transition to MinimallyHealthy with one compatible peer", async () => {
5148
const statusChangePromise = new Promise<HealthStatus>((resolve) => {
52-
healthIndicator.addEventListener(
53-
HealthStatusChangeEvents.StatusChange,
54-
(e: CustomEvent<HealthStatus>) => resolve(e.detail)
49+
events.addEventListener("waku:health", (e: CustomEvent<HealthStatus>) =>
50+
resolve(e.detail)
5551
);
5652
});
5753

@@ -66,14 +62,13 @@ describe("HealthIndicator", () => {
6662

6763
const changedStatus = await statusChangePromise;
6864
expect(changedStatus).to.equal(HealthStatus.MinimallyHealthy);
69-
expect(healthIndicator.toString()).to.equal(HealthStatus.MinimallyHealthy);
65+
expect(healthIndicator.toValue()).to.equal(HealthStatus.MinimallyHealthy);
7066
});
7167

7268
it("should transition to SufficientlyHealthy with multiple compatible peers", async () => {
7369
const statusChangePromise = new Promise<HealthStatus>((resolve) => {
74-
healthIndicator.addEventListener(
75-
HealthStatusChangeEvents.StatusChange,
76-
(e: CustomEvent<HealthStatus>) => resolve(e.detail)
70+
events.addEventListener("waku:health", (e: CustomEvent<HealthStatus>) =>
71+
resolve(e.detail)
7772
);
7873
});
7974

@@ -92,7 +87,7 @@ describe("HealthIndicator", () => {
9287

9388
const changedStatus = await statusChangePromise;
9489
expect(changedStatus).to.equal(HealthStatus.SufficientlyHealthy);
95-
expect(healthIndicator.toString()).to.equal(
90+
expect(healthIndicator.toValue()).to.equal(
9691
HealthStatus.SufficientlyHealthy
9792
);
9893
});
@@ -135,6 +130,18 @@ function mockLibp2p(): Libp2p {
135130
} as unknown as Libp2p;
136131
}
137132

133+
function mockEvents(): IWakuEventEmitter {
134+
const events = new EventTarget();
135+
136+
return {
137+
addEventListener: (event: string, handler: EventListener) =>
138+
events.addEventListener(event, handler),
139+
removeEventListener: (event: string, handler: EventListener) =>
140+
events.removeEventListener(event, handler),
141+
dispatchEvent: (event: Event) => events.dispatchEvent(event)
142+
} as unknown as IWakuEventEmitter;
143+
}
144+
138145
function mockPeer(id: string, protocols: string[]): Peer {
139146
return {
140147
id,

packages/sdk/src/health_indicator/health_indicator.ts

Lines changed: 19 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,37 @@
1-
import { TypedEventEmitter } from "@libp2p/interface";
21
import type { IdentifyResult, PeerId } from "@libp2p/interface";
32
import { FilterCodecs, LightPushCodec } from "@waku/core";
4-
import {
5-
HealthIndicatorEvents,
6-
HealthIndicatorParams,
7-
HealthStatus,
8-
HealthStatusChangeEvents,
9-
IHealthIndicator,
10-
Libp2p
11-
} from "@waku/interfaces";
3+
import { HealthStatus, IWakuEventEmitter, Libp2p } from "@waku/interfaces";
124
import { Logger } from "@waku/utils";
135

146
type PeerEvent<T> = (_event: CustomEvent<T>) => void;
157

168
const log = new Logger("health-indicator");
179

18-
/**
19-
* HealthIndicator monitors the health status of a Waku node by tracking peer connections
20-
* and their supported protocols.
21-
*
22-
* The health status can be one of three states:
23-
* - Unhealthy: No peer connections
24-
* - MinimallyHealthy: At least 1 peer supporting both Filter and LightPush protocols
25-
* - SufficientlyHealthy: At least 2 peers supporting both Filter and LightPush protocols
26-
*
27-
* @example
28-
* // Create and start a health indicator
29-
* const healthIndicator = new HealthIndicator({ libp2p: node.libp2p });
30-
* healthIndicator.start();
31-
*
32-
* // Listen for health status changes
33-
* healthIndicator.addEventListener(HealthStatusChangeEvents.StatusChange, (event) => {
34-
* console.log(`Health status changed to: ${event.detail}`);
35-
* });
36-
*
37-
* // Get current health status
38-
* console.log(`Current health: ${healthIndicator.toString()}`);
39-
*
40-
* // Clean up when done
41-
* healthIndicator.stop();
42-
*
43-
* @implements {IHealthIndicator}
44-
*/
45-
export class HealthIndicator
46-
extends TypedEventEmitter<HealthIndicatorEvents>
47-
implements IHealthIndicator
48-
{
10+
type HealthIndicatorParams = {
11+
libp2p: Libp2p;
12+
events: IWakuEventEmitter;
13+
};
14+
15+
interface IHealthIndicator {
16+
start(): void;
17+
stop(): void;
18+
toValue(): HealthStatus;
19+
}
20+
21+
export class HealthIndicator implements IHealthIndicator {
4922
private readonly libp2p: Libp2p;
23+
private readonly events: IWakuEventEmitter;
24+
5025
private value: HealthStatus = HealthStatus.Unhealthy;
5126

5227
public constructor(params: HealthIndicatorParams) {
53-
super();
5428
this.libp2p = params.libp2p;
29+
this.events = params.events;
5530

5631
this.onPeerIdentify = this.onPeerIdentify.bind(this);
5732
this.onPeerDisconnected = this.onPeerDisconnected.bind(this);
5833
}
5934

60-
/**
61-
* Starts monitoring the health status by adding event listeners to libp2p events.
62-
* Listens to peer connect and disconnect events to determine the node's health status.
63-
*/
6435
public start(): void {
6536
log.info("start: adding listeners to libp2p");
6637

@@ -74,10 +45,6 @@ export class HealthIndicator
7445
);
7546
}
7647

77-
/**
78-
* Stops monitoring the health status by removing event listeners from libp2p events.
79-
* Cleans up the peer connect and disconnect event listeners.
80-
*/
8148
public stop(): void {
8249
log.info("stop: removing listeners to libp2p");
8350

@@ -91,19 +58,7 @@ export class HealthIndicator
9158
);
9259
}
9360

94-
/**
95-
* Returns the current health status as a string.
96-
* @returns {string} Current health status (Unhealthy, MinimallyHealthy, or SufficientlyHealthy)
97-
*/
98-
public toString(): string {
99-
return this.value;
100-
}
101-
102-
/**
103-
* Returns the current health status value.
104-
* @returns {string} Current health status (Unhealthy, MinimallyHealthy, or SufficientlyHealthy)
105-
*/
106-
public toValue(): string {
61+
public toValue(): HealthStatus {
10762
return this.value;
10863
}
10964

@@ -163,8 +118,8 @@ export class HealthIndicator
163118
}
164119

165120
private dispatchHealthEvent(): void {
166-
this.dispatchEvent(
167-
new CustomEvent<HealthStatus>(HealthStatusChangeEvents.StatusChange, {
121+
this.events.dispatchEvent(
122+
new CustomEvent<HealthStatus>("waku:health", {
168123
detail: this.value
169124
})
170125
);

0 commit comments

Comments
 (0)