Skip to content

Commit 02bf966

Browse files
authored
fix: bug where heartbeat wasn't working (#18)
* fix: bug where heartbeat wasn't working * chore: improve types * style: format code * test: add new test for heartbeat, update another tests * chore: remove unused code from vite.config
1 parent d41307d commit 02bf966

18 files changed

+205
-136
lines changed

src/close.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import { heartbeatStop } from './heartbeat'
2+
import type { WSGOConfig } from './types'
23

3-
export function close(ws: WebSocket, ...[code = 1000, reason]: Parameters<WebSocket['close']>): void {
4-
if (ws === undefined) return
5-
6-
// stop heartbeat interval
7-
heartbeatStop()
4+
export function close(
5+
ws: WebSocket,
6+
_config: WSGOConfig,
7+
...[code = 1000, reason]: Parameters<WebSocket['close']>
8+
): void {
9+
if (_config.heartbeat) {
10+
// stop heartbeat interval
11+
heartbeatStop()
12+
}
813

914
// close websocket connection
1015
ws.close(code, reason)

src/heartbeat/index.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,32 @@
11
import { send } from '../send'
2-
import type { WSGOConfig } from '../types'
2+
import type { WSGOConfig, WSGOMessage } from '../types'
33

44
const heartbeatMessage = 'ping'
55
const heartbeatInterval = 1000
6+
const heartbeatTimeout = 1000
67

7-
let heartbeatTimeout: ReturnType<typeof setTimeout> | undefined
8+
let heartbeatTimeoutWait: ReturnType<typeof setTimeout> | undefined
89

910
export function heartbeatStart(ws: WebSocket, _config: WSGOConfig): void {
10-
heartbeatStop()
11-
12-
heartbeatTimeout = setTimeout(() => {
11+
setTimeout(() => {
1312
send(ws, _config, heartbeatMessage, heartbeatMessage)
13+
heartbeatTimeoutWait = setTimeout(() => {
14+
// open()
15+
ws.close()
16+
}, heartbeatInterval + heartbeatTimeout)
1417
}, heartbeatInterval)
1518
}
1619

17-
export function heartbeatStop(): void {
18-
clearTimeout(heartbeatTimeout)
19-
heartbeatTimeout = undefined
20-
}
21-
22-
export function listenHeartbeat(ws: WebSocket, _config: WSGOConfig, e: MessageEvent<any>): void {
23-
if (e.data === heartbeatMessage) {
20+
export function heartbeatListen(ws: WebSocket, _config: WSGOConfig, e: WSGOMessage<any>): void {
21+
if (e.event === 'pong') {
22+
heartbeatStop()
2423
heartbeatStart(ws, _config)
2524
// eslint-disable-next-line no-useless-return
2625
return
2726
}
2827
}
28+
29+
export function heartbeatStop(): void {
30+
clearTimeout(heartbeatTimeoutWait)
31+
heartbeatTimeoutWait = undefined
32+
}
File renamed without changes.

src/index.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { type WSGOEventName, type WSGOConfig, type WSGOSubscriptions } from './types'
1+
import type { WSGOEventName, WSGOConfig, WSGOSubscriptions, WSGOMessage } from './types'
22

33
import { open } from './open'
44
import { close } from './close'
55
import { send } from './send'
66
import type { WSGOSendData } from './send/types'
77
import { subscribe } from './subscribe'
88
import type { WSGOSubscribeCallback } from './subscribe/types'
9-
import { heartbeatStart, heartbeatStop, listenHeartbeat } from './heartbeat'
9+
import { heartbeatStart, heartbeatStop, heartbeatListen } from './heartbeat'
1010

1111
/** Method allows you create new WebSocket connection */
1212
export default function create(
@@ -61,7 +61,7 @@ export default function create(
6161
close: () => {
6262
if (ws === undefined) return
6363

64-
close(ws)
64+
close(ws, _config)
6565
},
6666
send: (...args) => {
6767
if (ws === undefined) return
@@ -81,20 +81,8 @@ function _listen(ws: WebSocket, subscriptions: WSGOSubscriptions, _config: WSGOC
8181
heartbeatStart(ws, _config)
8282
}
8383

84-
ws.onclose = (ev) => {
85-
_config.onDisconnected?.(ws, ev)
86-
87-
heartbeatStop()
88-
}
89-
90-
ws.onerror = (ev) => {
91-
_config.onError?.(ws, ev)
92-
}
93-
9484
ws.onmessage = (e: MessageEvent<any>): any => {
95-
listenHeartbeat(ws, _config, e)
96-
97-
let message
85+
let message: WSGOMessage<any>
9886

9987
try {
10088
message = JSON.parse(e.data)
@@ -106,8 +94,20 @@ function _listen(ws: WebSocket, subscriptions: WSGOSubscriptions, _config: WSGOC
10694
return
10795
}
10896

97+
heartbeatListen(ws, _config, message)
98+
10999
if (message.event in subscriptions) {
110100
subscriptions[message.event](message)
111101
}
112102
}
103+
104+
ws.onerror = (ev) => {
105+
_config.onError?.(ws, ev)
106+
}
107+
108+
ws.onclose = (ev) => {
109+
_config.onDisconnected?.(ws, ev)
110+
111+
heartbeatStop()
112+
}
113113
}
File renamed without changes.

src/subscribe/types.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import type { WSGOMessage } from '../types'
2+
3+
export interface WSGOSubscribeResponse<T = any> extends WSGOMessage<T> {
4+
/** Time when the client received the event */
5+
timeReceived: number
6+
}
7+
8+
export type WSGOSubscribeCallback<T> = (message: WSGOSubscribeResponse<T>) => any

src/subscribe/types.ts

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/types/index.ts renamed to src/types.d.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { WSGOHeartbeat } from '../heartbeat/types'
1+
import type { WSGOHeartbeat } from './heartbeat/types'
22

33
export type WSGOEventName = string
44

@@ -13,3 +13,12 @@ export interface WSGOConfig {
1313
immediate: boolean
1414
heartbeat: WSGOHeartbeat
1515
}
16+
17+
export interface WSGOMessage<T> {
18+
/** Event name */
19+
event: WSGOEventName
20+
/** Event data */
21+
data: T
22+
/** Time when the server sent the event */
23+
timeSended: number
24+
}

src/types/utils.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

test/close.test.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,21 @@
1-
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
1+
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
22
import WSGO from '../src/index'
3-
import ws from 'ws'
43
import { createMockWSServer } from './utils'
54

65
describe('close', () => {
7-
let port: number = 0
8-
let server: ws.Server
6+
let mockWSServer: ReturnType<typeof createMockWSServer>
97

10-
beforeAll(() => {
11-
const mockWSServer = createMockWSServer(port)
12-
13-
server = mockWSServer.server
14-
port = mockWSServer.port
8+
beforeEach(() => {
9+
mockWSServer = createMockWSServer()
1510
})
1611

17-
afterAll(() => {
18-
server.close()
12+
afterEach(() => {
13+
mockWSServer.server.close()
1914
})
2015

2116
it('should close the WebSocket when it is not already open', () => {
2217
// Arrange
23-
const wsgo = WSGO(`ws://localhost:${port}`)
18+
const wsgo = WSGO(`ws://localhost:${mockWSServer.port}`)
2419

2520
// Act
2621
wsgo.close()

0 commit comments

Comments
 (0)