insite-ws 1.0.354 → 1.0.355

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "insite-ws",
3
- "version": "1.0.354",
3
+ "version": "1.0.355",
4
4
  "description": "inSite WebSockets",
5
5
  "type": "module",
6
6
  "exports": {
package/.conveyer.js DELETED
@@ -1,43 +0,0 @@
1
- import path from "node:path";
2
- import { Conveyer, ESBuild } from "@nesvet/conveyer/stages";
3
- import { Packages } from "@nesvet/conveyer/Packages";
4
-
5
-
6
- const { NODE_ENV } = process.env;
7
-
8
- const distDir = "dist";
9
-
10
- const common = {
11
- external: new Packages().external().asNames(),
12
- format: "esm",
13
- sourcemap: true,
14
- define: {
15
- "process.env.NODE_ENV": JSON.stringify(NODE_ENV)
16
- }
17
- };
18
-
19
-
20
- new Conveyer([
21
-
22
- new ESBuild({
23
- title: "Server",
24
- entryPoints: [ "src/server/index.js" ],
25
- outfile: path.resolve(distDir, "server.js"),
26
- platform: "node",
27
- target: "node20",
28
- ...common
29
- }),
30
-
31
- new ESBuild({
32
- title: "Client",
33
- entryPoints: [ "src/client/index.js" ],
34
- outfile: path.resolve(distDir, "client.js"),
35
- platform: "neutral",
36
- target: "es2020",
37
- ...common
38
- })
39
-
40
- ], {
41
- initialCleanup: distDir,
42
- bumpVersions: { ignored: [ path.resolve(distDir, "**") ] }
43
- });
@@ -1,225 +0,0 @@
1
- import { debounce, noop } from "@nesvet/n";
2
- import EventEmitter from "eventemitter3";
3
- import { heartbeatGap, heartbeatInterval } from "../common";
4
- import { extendEventEmitter } from "../extendEventEmitter";
5
-
6
-
7
- const reconnectTimeout = 2000;
8
-
9
- let i = 0;
10
-
11
-
12
- class InSiteWebSocket extends EventEmitter {
13
- constructor(url, options = {}) {
14
- super();
15
-
16
- extendEventEmitter(this);
17
-
18
- this.url = url;
19
-
20
- const {
21
- name = i++,
22
- protocols,
23
- immediately = true,
24
- autoReconnect = true,
25
- on
26
- } = options;
27
-
28
- this.name = name;
29
-
30
- this.protocols = protocols;
31
-
32
- this.autoReconnect = autoReconnect;
33
-
34
- if (on)
35
- for (const eventName in on)
36
- if (on[eventName])
37
- this.on(eventName, on[eventName]);
38
-
39
- if (this.url && immediately)
40
- this.open().catch(noop);
41
-
42
- }
43
-
44
- webSocket = null;
45
-
46
- #defib = debounce(function () {
47
-
48
- if (this.isOpen) {
49
- if (process.env.NODE_ENV === "development")
50
- console.info(`WebSocket ${this.name} has no heartbeat - going offline`);
51
-
52
- this.webSocket.close();
53
- }
54
-
55
- }, heartbeatInterval + heartbeatGap);
56
-
57
- #openResolve;
58
- #openReject;
59
-
60
- get isConnecting() {
61
- return this.webSocket ? this.webSocket.readyState === this.webSocket.CONNECTING : null;
62
- }
63
-
64
- get isOpen() {
65
- return this.webSocket ? this.webSocket.readyState === this.webSocket.OPEN : null;
66
- }
67
-
68
- get isClosing() {
69
- return this.webSocket ? this.webSocket.readyState === this.webSocket.CLOSING : null;
70
- }
71
-
72
- get isClosed() {
73
- return this.webSocket ? this.webSocket.readyState === this.webSocket.CLOSED : null;
74
- }
75
-
76
- isChanged = false;
77
-
78
- #handleWebSocketOpen = () => {
79
-
80
- this.#openResolve();
81
- this.#openResolve = undefined;
82
- this.#openReject = undefined;
83
-
84
- this.emit("open");
85
-
86
- this.#defib();
87
-
88
- };
89
-
90
- #handleWebSocketMessage = ({ data: message }) => {
91
-
92
- this.#defib();
93
-
94
- if (message)
95
- try {
96
- const [ kind, ...rest ] = JSON.parse(message);
97
-
98
- this.emit("message", kind, ...rest);
99
- this.emit(`message:${kind}`, ...rest);
100
- } catch (error) {
101
- this.#handleWebSocketError(error);
102
- }
103
- else
104
- this.webSocket.send("");
105
-
106
- };
107
-
108
- #handleWebSocketError = error => {
109
- if (error instanceof Event)
110
- error = undefined;
111
-
112
- if (this.#openReject) {
113
- this.#openReject(error);
114
- this.#openResolve = undefined;
115
- this.#openReject = undefined;
116
- }
117
-
118
- this.emit("error", error);
119
-
120
- };
121
-
122
- #reconnectTimeout;
123
-
124
- #handleWebSocketClose = event => {
125
-
126
- if (process.env.NODE_ENV === "development")
127
- console.info(`WebSocket ${this.name} is closed with code ${event.code} and reason "${event.reason}"`);
128
-
129
- this.#defib.clear();
130
-
131
- this.emit("close", event);
132
-
133
- if (this.autoReconnect && ![ 1002, 3500, 4000 ].includes(event.code)) {
134
- if (process.env.NODE_ENV === "development")
135
- console.info(`WebSocket ${this.name} will try to reconnect in 2 sec…`);
136
-
137
- this.#reconnectTimeout = setTimeout(() => this.open().catch(noop), reconnectTimeout);
138
- }
139
-
140
- };
141
-
142
- open(options = {}) {
143
-
144
- clearTimeout(this.#reconnectTimeout);
145
-
146
- return this.close(4000, "reopen").then(() => {
147
-
148
- if (options.url)
149
- this.url = options.url;
150
-
151
- if (options.protocols)
152
- this.protocols = options.protocols;
153
-
154
- return new Promise((resolve, reject) => {
155
- this.#openResolve = resolve;
156
- this.#openReject = reject;
157
-
158
- if (this.webSocket) {
159
- this.isChanged = this.webSocket._url !== this.url;
160
- if (this.isChanged)
161
- this.emit("server-change");
162
- }
163
-
164
- this.webSocket = new WebSocket(this.url, this.protocols);
165
- this.webSocket._url = this.url;
166
-
167
- this.emit("connecting");
168
-
169
- this.send = this.webSocket.send.bind(this.webSocket);
170
-
171
- this.webSocket.addEventListener("open", this.#handleWebSocketOpen);
172
- this.webSocket.addEventListener("message", this.#handleWebSocketMessage);
173
- this.webSocket.addEventListener("error", this.#handleWebSocketError);
174
- this.webSocket.addEventListener("close", this.#handleWebSocketClose);
175
-
176
- });
177
- });
178
- }
179
-
180
- connect = this.open;
181
-
182
- close(code = 3500, reason = "manual") {
183
-
184
- clearTimeout(this.#reconnectTimeout);
185
-
186
- return new Promise(resolve => {
187
- if (this.isConnecting || this.isOpen) {
188
- this.webSocket.addEventListener("close", resolve);
189
- this.webSocket.close(code, reason);
190
- } else
191
- resolve();
192
-
193
- });
194
- }
195
-
196
- disconnect = this.close;
197
-
198
- #queue = [];
199
-
200
- #releaseQueue = () => {
201
-
202
- for (const message of this.#queue)
203
- this.webSocket.send(message);
204
-
205
- this.#queue.length = 0;
206
-
207
- };
208
-
209
- sendMessage(...args) {
210
- const message = JSON.stringify(args);
211
-
212
- if (this.isOpen)
213
- this.webSocket.send(message);
214
- else {
215
- if (!this.#queue.length)
216
- this.once("open", this.#releaseQueue);
217
-
218
- this.#queue.push(message);
219
- }
220
-
221
- }
222
-
223
- }
224
-
225
- export { InSiteWebSocket as WebSocket };
@@ -1 +0,0 @@
1
- export * from "./WebSocket";
package/src/common.js DELETED
@@ -1,2 +0,0 @@
1
- export const heartbeatInterval = 5_000;
2
- export const heartbeatGap = 60_000;
@@ -1,46 +0,0 @@
1
- /** @this EventEmitter */
2
- function extendedOn(...args) {
3
-
4
- for (const listenerHandler of this.listenerHandlers)
5
- if (listenerHandler.call(this, true, ...args) !== undefined)
6
- return;
7
-
8
- this.directOn(...args);
9
-
10
- }
11
-
12
- /** @this EventEmitter */
13
- function extendedOnce(...args) {
14
-
15
- for (const listenerHandler of this.listenerHandlers)
16
- if (listenerHandler.call(this, true, ...args) !== undefined)
17
- return;
18
-
19
- this.directOnce(...args);
20
-
21
- }
22
-
23
- /** @this EventEmitter */
24
- function extendedOff(...args) {
25
-
26
- for (const listenerHandler of this.listenerHandlers)
27
- if (listenerHandler.call(this, false, ...args) !== undefined)
28
- return;
29
-
30
- this.directOff(...args);
31
-
32
- }
33
-
34
- export function extendEventEmitter(eventEmitter) {
35
-
36
- eventEmitter.directOn = eventEmitter.on;
37
- eventEmitter.directOnce = eventEmitter.once;
38
- eventEmitter.directOff = eventEmitter.off;
39
-
40
- eventEmitter.listenerHandlers = new Set();
41
-
42
- eventEmitter.on = extendedOn;
43
- eventEmitter.once = extendedOnce;
44
- eventEmitter.off = extendedOff;
45
-
46
- }
@@ -1,123 +0,0 @@
1
- import http from "node:http";
2
- import https from "node:https";
3
- import { debounce } from "@nesvet/n";
4
- import { WebSocketServer } from "ws";
5
- import { heartbeatGap, heartbeatInterval } from "../common";
6
- import { extendEventEmitter } from "../extendEventEmitter";
7
- import { defibSymbol, heartbeatIntervalSymbol, pingTsSymbol } from "./symbols";
8
- import { WebSocketServerClient } from "./WebSocketServerClient";
9
-
10
-
11
- class InSiteWebSocketServer extends WebSocketServer {
12
- constructor(options = {}, props, handleListen) {
13
- const {
14
- upgrade,
15
- ssl,
16
- port,
17
- server = ssl ? https.createServer({ ...ssl }) : http.createServer(),
18
- ...wssOptions
19
- } = options;
20
-
21
- super({
22
- ...wssOptions,
23
- WebSocket: WebSocketServerClient,
24
- server
25
- });
26
-
27
- extendEventEmitter(this);
28
-
29
- if ("upgrade" in this)
30
- throw new Error("WebSocketServer now contains an \"upgrade\" property");
31
-
32
- if (props)
33
- if (typeof props == "function") {
34
- handleListen = props;
35
- props = undefined;
36
- } else
37
- Object.assign(this, props);
38
-
39
- this.upgrade = upgrade;
40
-
41
- this.on("connection", this.#handleConnection);
42
-
43
- server.listen(port, handleListen);
44
-
45
- }
46
-
47
- isWebSocketServer = true;
48
-
49
- #handleConnection(ws, request) {
50
- ws.wss = this;
51
-
52
- ws.userAgent = request.headers["user-agent"];
53
- ws.remoteAddress = request.headers["x-real-ip"] ?? request.headers["x-forwarded-for"] ?? "127.0.0.1";
54
-
55
- ws.on("message", InSiteWebSocketServer.handleClientMessage);
56
- ws.on("error", InSiteWebSocketServer.handleClientError);
57
- ws.on("close", InSiteWebSocketServer.handleClientClose);
58
-
59
- ws[defibSymbol] = debounce(() => ws.terminate(), heartbeatInterval + heartbeatGap);
60
- ws[defibSymbol]();
61
-
62
- ws.latency = 0;
63
-
64
- ws[heartbeatIntervalSymbol] = setInterval(() => {
65
-
66
- ws[pingTsSymbol] = Date.now();
67
- ws.send("");
68
-
69
- }, heartbeatInterval);
70
-
71
- this.emit("client-connect", ws, request);
72
-
73
- }
74
-
75
- static handleClientMessage(data) {
76
- const message = data.toString();
77
-
78
- this[defibSymbol]();
79
-
80
- if (message)
81
- try {
82
- const [ kind, ...rest ] = JSON.parse(message);
83
-
84
- this.emit(`message:${kind}`, ...rest);
85
- this.wss.emit("client-message", this, kind, ...rest);
86
- this.wss.emit(`client-message:${kind}`, this, ...rest);
87
- } catch (error) {
88
- InSiteWebSocketServer.handleClientError.call(this, error);
89
- }
90
- else if (this[pingTsSymbol]) {
91
- this.latency = Date.now() - this[pingTsSymbol];
92
- delete this[pingTsSymbol];
93
- }
94
-
95
- }
96
-
97
- static handleClientError(error) {
98
- if (error instanceof Event)
99
- error = undefined;
100
-
101
- if (process.env.NODE_ENV === "development")
102
- console.error("WebSocketServer Client error", error);
103
-
104
- this.wss.emit("client-error", this, error);
105
-
106
- }
107
-
108
- static handleClientClose(...args) {
109
-
110
- if (process.env.NODE_ENV === "development")
111
- console.info("WebSocketServer Client closed", ...args);
112
-
113
- this[defibSymbol].clear();
114
- clearInterval(this[heartbeatIntervalSymbol]);
115
-
116
- this.wss.emit("client-close", this);
117
- this.wss.emit("client-closed", this);
118
-
119
- }
120
-
121
- }
122
-
123
- export { InSiteWebSocketServer as WebSocketServer };
@@ -1,31 +0,0 @@
1
- import WebSocket from "ws";
2
-
3
-
4
- class InSiteWebSocketServerClient extends WebSocket {
5
-
6
- isWebSocketServerClient = true;
7
-
8
- get isConnecting() {
9
- return this.readyState === this.CONNECTING;
10
- }
11
-
12
- get isOpen() {
13
- return this.readyState === this.OPEN;
14
- }
15
-
16
- get isClosing() {
17
- return this.readyState === this.CLOSING;
18
- }
19
-
20
- get isClosed() {
21
- return this.readyState === this.CLOSED;
22
- }
23
-
24
- sendMessage(...args) {
25
- this.send(JSON.stringify(args));
26
-
27
- }
28
-
29
- }
30
-
31
- export { InSiteWebSocketServerClient as WebSocketServerClient };
@@ -1 +0,0 @@
1
- export * from "./WebSocketServer";
@@ -1,3 +0,0 @@
1
- export const heartbeatIntervalSymbol = Symbol("heartbeatInterval");
2
- export const defibSymbol = Symbol("defib");
3
- export const pingTsSymbol = Symbol("pingTs");