-
-
Notifications
You must be signed in to change notification settings - Fork 840
Expand file tree
/
Copy pathserver-web-socket.ts
More file actions
89 lines (77 loc) · 2.25 KB
/
server-web-socket.ts
File metadata and controls
89 lines (77 loc) · 2.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import { noop } from '@utils';
import type { Server } from 'http';
import WebSocket, { type ServerOptions, WebSocketServer } from 'ws';
import type * as d from '../declarations';
export function createWebSocket(
httpServer: Server,
onMessageFromClient: (msg: d.DevServerMessage) => void,
): DevWebSocket {
const wsConfig: ServerOptions = {
server: httpServer,
};
const wsServer = new WebSocketServer(wsConfig);
function heartbeat(this: WebSocket) {
// we need to coerce the `ws` type to our custom `DevWS` type here, since
// this function is going to be passed in to `ws.on('pong'` which expects
// to be passed a function where `this` is bound to `ws`.
(this as DevWS).isAlive = true;
}
wsServer.on('connection', (ws: DevWS) => {
ws.on('message', (data) => {
// the server process has received a message from the browser
// pass the message received from the browser to the main cli process
try {
onMessageFromClient(JSON.parse(data.toString()));
} catch (e) {
console.error(e);
}
});
ws.isAlive = true;
ws.on('pong', heartbeat);
// ignore invalid close frames sent by Safari 15
ws.on('error', console.error);
});
const pingInterval = setInterval(() => {
(wsServer.clients as Set<DevWS>).forEach((ws: DevWS) => {
if (!ws.isAlive) {
return ws.close(1000);
}
ws.isAlive = false;
ws.ping(noop);
});
}, 10000);
return {
sendToBrowser: (msg: d.DevServerMessage) => {
if (msg && wsServer && wsServer.clients) {
const data = JSON.stringify(msg);
wsServer.clients.forEach((ws) => {
if (ws.readyState === ws.OPEN) {
ws.send(data);
}
});
}
},
close: () => {
return new Promise((resolve, reject) => {
clearInterval(pingInterval);
wsServer.clients.forEach((ws) => {
ws.close(1000);
});
wsServer.close((err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
},
};
}
export interface DevWebSocket {
sendToBrowser: (msg: d.DevServerMessage) => void;
close: () => Promise<void>;
}
interface DevWS extends WebSocket {
isAlive: boolean;
}