websocket-ts 1.1.1 → 2.1.1

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 (72) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +157 -93
  3. package/package.json +27 -14
  4. package/src/backoff/backoff.ts +21 -13
  5. package/src/backoff/constantbackoff.ts +29 -11
  6. package/src/backoff/exponentialbackoff.ts +68 -28
  7. package/src/backoff/linearbackoff.ts +77 -26
  8. package/src/index.ts +23 -9
  9. package/src/queue/array_queue.ts +41 -0
  10. package/src/queue/queue.ts +46 -0
  11. package/src/queue/ring_queue.ts +69 -0
  12. package/src/websocket.ts +463 -138
  13. package/src/websocket_buffer.ts +18 -0
  14. package/src/websocket_builder.ts +274 -0
  15. package/src/websocket_event.ts +88 -0
  16. package/src/websocket_options.ts +23 -0
  17. package/src/websocket_retry_options.ts +22 -0
  18. package/{tsconfig.json → tsconfig.cjs.json} +8 -8
  19. package/tsconfig.esm.json +71 -0
  20. package/jest.config.js +0 -9
  21. package/lib/backoff/backoff.d.ts +0 -18
  22. package/lib/backoff/backoff.d.ts.map +0 -1
  23. package/lib/backoff/backoff.js +0 -3
  24. package/lib/backoff/backoff.js.map +0 -1
  25. package/lib/backoff/constantbackoff.d.ts +0 -11
  26. package/lib/backoff/constantbackoff.d.ts.map +0 -1
  27. package/lib/backoff/constantbackoff.js +0 -20
  28. package/lib/backoff/constantbackoff.js.map +0 -1
  29. package/lib/backoff/exponentialbackoff.d.ts +0 -22
  30. package/lib/backoff/exponentialbackoff.d.ts.map +0 -1
  31. package/lib/backoff/exponentialbackoff.js +0 -35
  32. package/lib/backoff/exponentialbackoff.js.map +0 -1
  33. package/lib/backoff/linearbackoff.d.ts +0 -19
  34. package/lib/backoff/linearbackoff.d.ts.map +0 -1
  35. package/lib/backoff/linearbackoff.js +0 -34
  36. package/lib/backoff/linearbackoff.js.map +0 -1
  37. package/lib/buffer/buffer.d.ts +0 -41
  38. package/lib/buffer/buffer.d.ts.map +0 -1
  39. package/lib/buffer/buffer.js +0 -3
  40. package/lib/buffer/buffer.js.map +0 -1
  41. package/lib/buffer/lrubuffer.d.ts +0 -24
  42. package/lib/buffer/lrubuffer.d.ts.map +0 -1
  43. package/lib/buffer/lrubuffer.js +0 -76
  44. package/lib/buffer/lrubuffer.js.map +0 -1
  45. package/lib/buffer/timebuffer.d.ts +0 -25
  46. package/lib/buffer/timebuffer.d.ts.map +0 -1
  47. package/lib/buffer/timebuffer.js +0 -92
  48. package/lib/buffer/timebuffer.js.map +0 -1
  49. package/lib/index.d.ts +0 -10
  50. package/lib/index.d.ts.map +0 -1
  51. package/lib/index.js +0 -22
  52. package/lib/index.js.map +0 -1
  53. package/lib/websocket.d.ts +0 -47
  54. package/lib/websocket.d.ts.map +0 -1
  55. package/lib/websocket.js +0 -120
  56. package/lib/websocket.js.map +0 -1
  57. package/lib/websocketBuilder.d.ts +0 -32
  58. package/lib/websocketBuilder.d.ts.map +0 -1
  59. package/lib/websocketBuilder.js +0 -68
  60. package/lib/websocketBuilder.js.map +0 -1
  61. package/src/buffer/buffer.ts +0 -45
  62. package/src/buffer/lrubuffer.ts +0 -80
  63. package/src/buffer/timebuffer.ts +0 -105
  64. package/src/websocketBuilder.ts +0 -98
  65. package/test/backoff/constantbackoff.test.ts +0 -21
  66. package/test/backoff/exponentialbackoff.test.ts +0 -13
  67. package/test/backoff/linearbackoff.test.ts +0 -32
  68. package/test/buffer/common.ts +0 -10
  69. package/test/buffer/lrubuffer.test.ts +0 -128
  70. package/test/buffer/timebuffer.test.ts +0 -115
  71. package/test/websocket.test.ts +0 -341
  72. package/test/websocketBuilder.test.ts +0 -36
@@ -1,115 +0,0 @@
1
- import {TimeBuffer} from "../../src";
2
- import {createEmptyArray, createSequentialArray} from "./common";
3
-
4
- describe("Testsuite for TimeBuffer", () => {
5
- const maxAge = 1000;
6
- let tb: TimeBuffer<number>;
7
-
8
- beforeEach(() => {
9
- tb = new TimeBuffer<number>(maxAge); // no older than 1000 ms
10
- });
11
-
12
- test("TimeBuffer length", async () => {
13
- expect(tb.len()).toBe(0);
14
- for (let i = 0; i < 10; i++) {
15
- expect(tb.write([i])).toBe(1);
16
- expect(tb.len()).toBe(i + 1);
17
- }
18
-
19
- await delay(maxAge + 500).then(() => {
20
- expect(tb.len()).toBe(0);
21
- });
22
- });
23
-
24
- test("TimeBuffer capacity", () => {
25
- expect(tb.cap()).toBe(Number.POSITIVE_INFINITY);
26
- });
27
-
28
- test("TimeBuffer elements should expire when they reach their maxAge", async () => {
29
- // add 10 items
30
- for (let i = 0; i < 10; i++) {
31
- expect(tb.write([i])).toBe(1);
32
- expect(tb.len()).toBe(i + 1);
33
- }
34
-
35
- // give elements some time to "age"
36
- await delay(500).then(() => {
37
-
38
- // all items should still be in range
39
- expect(tb.len()).toBe(10);
40
-
41
- // add 10 more items
42
- for (let i = 0; i < 5; i++) {
43
- expect(tb.write([i])).toBe(1);
44
- expect(tb.len()).toBe(10 + i + 1);
45
- }
46
- });
47
-
48
- // the first 10 items should now be removed, there are 5 items remaining
49
- await delay(800).then(() => {
50
- expect(tb.len()).toBe(5);
51
- });
52
- });
53
-
54
- test("TimeBuffer read should return items up to maxAge ", async () => {
55
- const w1 = createSequentialArray(10);
56
- expect(tb.write(w1)).toBe(10);
57
- const r1 = createEmptyArray(10);
58
- expect(tb.read(r1)).toBe(10);
59
- for (let i = 0; i < 10; i++)
60
- expect(w1[i]).toBe(r1[i]);
61
-
62
- await delay(500).then(() => {
63
- const r2 = createEmptyArray(10);
64
- expect(tb.read(r2)).toBe(10);
65
- for (let i = 0; i < 10; i++)
66
- expect(w1[i]).toBe(r2[i]);
67
-
68
- const w2 = createSequentialArray(5);
69
- expect(tb.write(w2)).toBe(5);
70
- const r3 = createEmptyArray(15);
71
- expect(tb.read(r3)).toBe(15);
72
- for (let i = 0; i < 15; i++)
73
- if (i < 10)
74
- expect(r3[i]).toBe(w1[i]);
75
- else
76
- expect(r3[i]).toBe(w2[i - 10]);
77
- });
78
- });
79
-
80
- test("TimeBuffer read on empty should return no item", () => {
81
- const r1 = createEmptyArray(10);
82
- expect(tb.read(r1)).toBe(0);
83
- expect(tb.read([])).toBe(0);
84
- });
85
-
86
- test("TimeBuffer forEach() should only be invoked on valid items", async () => {
87
- const w1 = createSequentialArray(10);
88
- expect(tb.write(w1)).toBe(10);
89
- const r2 = [] as number[];
90
- tb.forEach(e => r2.push(e));
91
- expect(r2.length).toBe(10);
92
-
93
- await delay(500).then(() => {
94
- const w2 = createSequentialArray(5);
95
- expect(tb.write(w2)).toBe(5);
96
- const r3 = [] as number[]
97
- tb.forEach(e => r3.push(e));
98
- expect(r3.length).toBe(15);
99
- });
100
-
101
- await delay(800).then(() => {
102
- const r4 = [] as number[];
103
- tb.forEach(e => r4.push(e));
104
- expect(r4.length).toBe(5);
105
- })
106
- })
107
- });
108
-
109
- function delay(ms: number): Promise<void> {
110
- return new Promise<void>(resolve => {
111
- setTimeout(() => {
112
- resolve();
113
- }, ms);
114
- })
115
- }
@@ -1,341 +0,0 @@
1
- import {ConstantBackoff, LRUBuffer, Websocket, WebsocketBuilder, WebsocketEvents} from "../src";
2
- import {Server} from "ws";
3
-
4
- describe("Testsuite for Websocket", () => {
5
- const port = 42421;
6
- const url = `ws://localhost:${port}`;
7
-
8
- let ws: Websocket | undefined;
9
- let wss: Server | undefined;
10
-
11
- type WsInstanceWithEvent<K extends Event> = { instance: Websocket, event: K };
12
-
13
- beforeEach(async () => {
14
- await startServer(port).then(server => {
15
- wss = server;
16
- });
17
- });
18
-
19
- afterEach(async () => {
20
- if (ws !== undefined)
21
- await shutdownClientOrTimeout(ws, 100);
22
- if (wss !== undefined)
23
- await shutdownServerOrTimeout(wss, 100);
24
- });
25
-
26
- test("Websocket should fire onOpen-event when connection is established", async () => {
27
- await new Promise<WsInstanceWithEvent<Event>>(resolve => {
28
- ws = new WebsocketBuilder(url).onOpen((instance, event) => {
29
- resolve({instance, event});
30
- }).build();
31
- }).then(e => {
32
- expect(e.instance).toBe(ws);
33
- expect(e.event.type).toBe(WebsocketEvents.open);
34
- expect(e.instance.underlyingWebsocket?.readyState).toBe(WebSocket.OPEN);
35
- });
36
- });
37
-
38
- test("Websocket should fire onClose-event when the server closes the connection", async () => {
39
- await new Promise<WsInstanceWithEvent<CloseEvent>>(resolve => {
40
- ws = new WebsocketBuilder(url)
41
- .onClose((instance, event) => {
42
- resolve({instance, event});
43
- }).build();
44
- wss?.close(); // close server
45
- }).then(e => {
46
- expect(e.instance).toBe(ws);
47
- expect(e.event.type).toBe(WebsocketEvents.close);
48
- expect(e.instance.underlyingWebsocket?.readyState).toBe(WebSocket.CLOSED);
49
- });
50
- });
51
-
52
- test("Websocket should fire onClose-event when the client closes the connection", async () => {
53
- const closeCode = 1000;
54
- const closeReason = "client closed the connection";
55
-
56
- await new Promise<WsInstanceWithEvent<CloseEvent>>(resolve => {
57
- ws = new WebsocketBuilder(url)
58
- .onOpen((instance, _) => {
59
- instance.close(closeCode, closeReason); // close client
60
- })
61
- .onClose((instance, event) => {
62
- resolve({instance, event});
63
- }).build();
64
- }).then(e => {
65
- expect(e.instance).toBe(ws);
66
- expect(e.event.type).toBe(WebsocketEvents.close);
67
- expect(e.event.code).toBe(closeCode);
68
- expect(e.event.reason).toBe(closeReason);
69
- expect(e.instance.underlyingWebsocket?.readyState).toBe(WebSocket.CLOSED);
70
- });
71
- });
72
-
73
- test("Websocket should fire onError-event when the server refuses the connection", async () => {
74
- await new Promise<WsInstanceWithEvent<Event>>(resolve => {
75
- wss?.close(); // close server
76
- ws = new WebsocketBuilder(url)
77
- .onError((instance, event) => {
78
- resolve({instance, event});
79
- }).build();
80
- }).then(e => {
81
- expect(e.instance).toBe(ws);
82
- expect(e.event.type).toBe(WebsocketEvents.error);
83
- expect(e.instance.underlyingWebsocket?.readyState).toBe(WebSocket.CLOSED);
84
- });
85
- });
86
-
87
- test("Websocket should fire onMessage-event when a message is received", async () => {
88
- const testMessage = "this is a test message.";
89
- const onMessagePromise = new Promise<WsInstanceWithEvent<MessageEvent>>(resolve => {
90
- ws = new WebsocketBuilder(url)
91
- .onMessage((instance, event) => {
92
- resolve({instance, event});
93
- }).build();
94
- }).then(e => {
95
- expect(e.instance).toBe(ws);
96
- expect(e.event.type).toBe(WebsocketEvents.message);
97
- expect(e.event.data).toBe(testMessage);
98
- expect(e.instance.underlyingWebsocket?.readyState).toBe(WebSocket.OPEN);
99
- });
100
-
101
- // wait for the client to connect to the server and then send a message to the client
102
- if (wss !== undefined) {
103
- await onClientConnected(wss).then(client => {
104
- client.send(testMessage);
105
- })
106
- }
107
-
108
- // wait for the client to receive the message, it should fire the 'onMessage'-event
109
- await onMessagePromise;
110
- });
111
-
112
- test("Websocket should send messages when connected", async () => {
113
- const testMessage = "this is a test message.";
114
- const onConnectAndSendPromise = new Promise<void>(resolve => {
115
- ws = new WebsocketBuilder(url)
116
- .onOpen((instance, _) => {
117
- instance.send(testMessage); // send message as soon as we are connected
118
- resolve();
119
- }).build();
120
- });
121
- const onReceivePromise = new Promise<string>(resolve => {
122
- wss?.on('connection', socket => {
123
- socket.onmessage = me => {
124
- resolve(me.data.toString());
125
- }
126
- });
127
- });
128
-
129
- await onConnectAndSendPromise; // wait for client to connect and send the message
130
- await onReceivePromise.then(actual => { // wait for server to receive the message and compare
131
- expect(actual).toBe(testMessage);
132
- })
133
- });
134
-
135
- test("Websocket should ignore send()-calls when the connection was closed by the user", async () => {
136
- const testMessage = "this is a test message.";
137
- await new Promise<void>(resolve => { // connect to server
138
- ws = new WebsocketBuilder(url)
139
- .onOpen((instance, _) => {
140
- resolve();
141
- }).build();
142
- });
143
-
144
- // monkey-patch the underlying websockets send()-method, to see if it is still called
145
- let messagesSent = 0;
146
- if (ws !== undefined && ws.underlyingWebsocket !== undefined) {
147
- ws.underlyingWebsocket.send = (_: string | ArrayBufferLike | Blob | ArrayBufferView) => {
148
- messagesSent++;
149
- }
150
- }
151
-
152
- ws?.close(); // close connection from client-side
153
- expect(ws!['closedByUser']).toBe(true); // should indicate that the connection was closed by user
154
- ws?.send(testMessage);
155
- expect(messagesSent).toBe(0); // should still be 0 since send() was never really called
156
- });
157
-
158
- test("Websocket should send buffered messages when the connection is (re-)established", async () => {
159
- const testMessages = ["one", "two", "three"];
160
-
161
- let onOpen: () => void;
162
- let onClose: () => void;
163
- let wsOnOpenPromise = new Promise<void>(resolve => onOpen = resolve);
164
- const wsOnClosePromise = new Promise<void>(resolve => onClose = resolve);
165
- ws = new WebsocketBuilder(url)
166
- .withBuffer(new LRUBuffer<string>(10))
167
- .withBackoff(new ConstantBackoff(100))
168
- .onOpen(() => onOpen())
169
- .onClose(() => onClose())
170
- .build();
171
-
172
- await wsOnOpenPromise; // wait for client to be connected
173
- if (wss !== undefined) // shutdown the server
174
- await shutdownServerOrTimeout(wss, 100);
175
- await wsOnClosePromise; // wait for client to register the disconnect
176
-
177
- // sending messages while disconnected should be buffered
178
- expect((ws!['buffer'] as LRUBuffer<string>).len()).toBe(0);
179
- testMessages.forEach(msg => ws?.send(msg));
180
- expect((ws!['buffer'] as LRUBuffer<string>).len()).toBe(testMessages.length);
181
-
182
- // re-start the server and create promise for when all messages are received
183
- let onMessagesReceived: (msg: string[]) => void;
184
- const onMessagesReceivedPromise = new Promise<string[]>(resolve => onMessagesReceived = resolve);
185
- await startServer(port).then(server => {
186
- wss = server;
187
- wss.on('connection', socket => {
188
- let buf = [] as string[];
189
- socket.onmessage = me => {
190
- buf.push(me.data.toString());
191
- if (buf.length === testMessages.length)
192
- onMessagesReceived(buf);
193
- }
194
- })
195
- });
196
-
197
- // wait for the client to re-connect, it should send out all pending messages...
198
- wsOnOpenPromise = new Promise<void>(resolve => onOpen = resolve);
199
- await wsOnOpenPromise;
200
-
201
- // ...which are then received by the server in correct order...
202
- await onMessagesReceivedPromise.then(actual => {
203
- expect(actual).toEqual(testMessages);
204
- })
205
-
206
- // ...after which the clients message-buffer should be empty again
207
- expect((ws!['buffer'] as LRUBuffer<string>).len()).toBe(0);
208
- });
209
-
210
- test("Websocket should remove event-listener correctly when removeEventListener() is called", async () => {
211
- let count = 0;
212
- const openEventListener = () => count++; // increment counter on every connect
213
- let onOpen: () => void;
214
- let onClose: () => void;
215
- let wsOnOpenPromise = new Promise<void>(resolve => onOpen = resolve);
216
- const wsOnClosePromise = new Promise<void>(resolve => onClose = resolve);
217
- ws = new WebsocketBuilder(url)
218
- .withBackoff(new ConstantBackoff(100))
219
- .onOpen(() => onOpen())
220
- .onOpen(openEventListener)
221
- .onClose(() => onClose())
222
- .build();
223
- await wsOnOpenPromise; // wait for initial connection
224
- expect(count).toBe(1); // openEventListener should be called exactly once at this point
225
- ws.removeEventListener(WebsocketEvents.open, openEventListener); // unregister the event-handler
226
- if (wss !== undefined) // shutdown the server
227
- await shutdownServerOrTimeout(wss, 100);
228
- await wsOnClosePromise; // wait for client to register the disconnect
229
-
230
- // restart the server and wait for the client to connect
231
- await startServer(port).then(server => wss = server);
232
- wsOnOpenPromise = new Promise<void>(resolve => onOpen = resolve);
233
- await wsOnOpenPromise;
234
- expect(count).toBe(1); // count should still be 1, since the incrementing event-handler was unregistered
235
- });
236
-
237
- test("Websocket should remove event-listeners if they declare the 'once'-property as true", async () => {
238
- let count = 0;
239
- const openEventListener = () => count++; // increment counter on every connect
240
- let onOpen: () => void;
241
- let onClose: () => void;
242
- let wsOnOpenPromise = new Promise<void>(resolve => onOpen = resolve);
243
- const wsOnClosePromise = new Promise<void>(resolve => onClose = resolve);
244
- ws = new WebsocketBuilder(url)
245
- .withBackoff(new ConstantBackoff(100))
246
- .onOpen(() => onOpen())
247
- .onOpen(openEventListener, {once: true} as AddEventListenerOptions) // declare 'once'-property
248
- .onClose(() => onClose())
249
- .build();
250
- await wsOnOpenPromise; // wait for initial connection
251
- expect(count).toBe(1); // openEventListener should be called exactly once at this point
252
- if (wss !== undefined) // shutdown the server
253
- await shutdownServerOrTimeout(wss, 100);
254
- await wsOnClosePromise; // wait for client to register the disconnect
255
-
256
- // restart the server and wait for the client to connect
257
- await startServer(port).then(server => wss = server);
258
- wsOnOpenPromise = new Promise<void>(resolve => onOpen = resolve);
259
- await wsOnOpenPromise;
260
- expect(count).toBe(1); // count should still be 1, since the incrementing event-handler was unregistered
261
- });
262
-
263
- test("Websocket should try to reconnect when the connection is lost", async () => {
264
-
265
- });
266
-
267
- test("Websocket should fire retryEvent when trying to reconnect", async () => {
268
- let retryCount = 0;
269
- let onOpen: () => void;
270
- let onClose: () => void;
271
- let onRetry: () => void;
272
- let wsOnOpenPromise = new Promise<void>(resolve => onOpen = resolve);
273
- const wsOnClosePromise = new Promise<void>(resolve => onClose = resolve);
274
- let wsOnRetryPromise = new Promise<void>(resolve => onRetry = resolve);
275
- ws = new WebsocketBuilder(url)
276
- .withBackoff(new ConstantBackoff(100)) // 100ms between retries
277
- .onOpen(() => onOpen())
278
- .onClose(() => onClose())
279
- .onRetry(() => retryCount++)
280
- .onRetry(() => onRetry())
281
- .build();
282
- await wsOnOpenPromise;
283
- if (wss !== undefined) // shutdown the server
284
- await shutdownServerOrTimeout(wss, 100);
285
- await wsOnClosePromise; // wait for client to register the disconnect
286
- await wsOnRetryPromise; // it should retry after 100ms and this will trigger the event
287
- await delay(450); // after 450 more ms, it should've triggered another 4 times
288
- expect(retryCount >= 3 || retryCount <= 6).toBeTruthy();
289
- });
290
- });
291
-
292
- function delay(ms: number): Promise<void> {
293
- return new Promise<void>(resolve => {
294
- setTimeout(() => {
295
- resolve();
296
- }, ms);
297
- })
298
- }
299
-
300
- function startServer(port: number): Promise<Server> {
301
- return new Promise(resolve => {
302
- const wss = new Server({port});
303
- wss.on('listening', () => {
304
- resolve(wss);
305
- });
306
- });
307
- }
308
-
309
- function shutdownServerOrTimeout(wss: Server, timeout: number) {
310
- let timeoutPromise = delay(timeout);
311
- let shutdownServerPromise = shutdownServer(wss);
312
- return Promise.race([timeoutPromise, shutdownServerPromise]);
313
- }
314
-
315
- function shutdownServer(wss: Server): Promise<void> {
316
- return new Promise<void>(resolve => {
317
- wss.addListener("close", () => resolve());
318
- wss.close();
319
- });
320
- }
321
-
322
- function shutdownClientOrTimeout(ws: Websocket, timeout: number) {
323
- let timeoutPromise = delay(timeout);
324
- let shutdownClientPromise = shutdownClient(ws);
325
- return Promise.race([timeoutPromise, shutdownClientPromise]);
326
- }
327
-
328
- function shutdownClient(ws: Websocket): Promise<void> {
329
- return new Promise<void>(resolve => {
330
- ws.addEventListener(WebsocketEvents.close, () => resolve());
331
- ws.close();
332
- });
333
- }
334
-
335
- function onClientConnected(wss: Server): Promise<WebSocket> {
336
- return new Promise(resolve => {
337
- wss.on('connection', (client: WebSocket) => {
338
- resolve(client);
339
- });
340
- });
341
- }
@@ -1,36 +0,0 @@
1
- import {WebsocketBuilder, LRUBuffer, ConstantBackoff} from "../src";
2
-
3
- describe("Testsuite for WebsocketBuilder", () => {
4
- const url = "ws://localhost:42421";
5
-
6
- test("WebsocketBuilder should set protocols", () => {
7
- const sut = new WebsocketBuilder(url);
8
- const expected = ["p1", "p2", "p3"];
9
- sut.withProtocols(expected);
10
- const actual = sut['protocols'];
11
- expect(actual).toEqual(expected);
12
- });
13
-
14
- test("WebsocketBuilder should set backoff", () => {
15
- const sut = new WebsocketBuilder(url);
16
- const expected = new ConstantBackoff(100);
17
- sut.withBackoff(expected);
18
- const actual = sut['backoff'];
19
- expect(actual).toBe(expected);
20
- });
21
-
22
- test("WebsocketBuilder should set buffer", () => {
23
- const sut = new WebsocketBuilder(url);
24
- const expected = new LRUBuffer(10);
25
- sut.withBuffer(expected);
26
- const actual = sut['buffer'];
27
- expect(actual).toBe(expected);
28
- });
29
-
30
- test("WebsocketBuilder should return the same instance on every build()-call", () => {
31
- const sut = new WebsocketBuilder(url);
32
- const ws = sut.build();
33
- const ws2 = sut.build();
34
- expect(ws).toBe(ws2);
35
- });
36
- });