wspromisify 3.0.2 → 3.1.0
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/README.md +83 -97
- package/dist/bundle.cjs +39 -19
- package/dist/bundle.d.ts +2 -1
- package/dist/bundle.mjs +39 -19
- package/package.json +13 -13
package/README.md
CHANGED
|
@@ -11,85 +11,75 @@ const responseData = await ws.send({catSaid: 'Meow!'})
|
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
// If you detected some bug, want some stuff to be added, feel free to open an issue!
|
|
14
|
-
Large data support (chunking), plugins and different server-side implementations are coming.
|
|
15
14
|
To see a Node.js server-side part example, please, take a look on test/mock in github repo.
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
Makes a Promise-like WebSocket connection.
|
|
19
18
|
Features (almost all are tunable via constructor config below.)
|
|
20
|
-
-
|
|
21
|
-
- ES-module and
|
|
19
|
+
- Fully asynchronous with promises.
|
|
20
|
+
- ES-module and CommonJS versions.
|
|
22
21
|
- Types (d.ts) included.
|
|
23
|
-
- Automatically reconnects.
|
|
22
|
+
- Automatically reconnects, resends, closes when idle, pings. All parametrized.
|
|
24
23
|
- Streams are supported. For example, you can stream your AI response.
|
|
24
|
+
- Data piping, routing (via .route()).
|
|
25
|
+
- Handy logging API.
|
|
25
26
|
- Supports existent native WebSocket or ws-like implementation (ws npm package) via `socket` property.
|
|
26
|
-
- And provide your own socket instance via socket config prop.
|
|
27
|
+
- And provide your own socket instance via socket config fabric prop.
|
|
27
28
|
- Any id and data keys to negotiate with your back-end.
|
|
28
29
|
- Any (serialiser)/Decoder(deserialiser).
|
|
29
30
|
- Lazy connect: connects only if something sent, then send all of them!
|
|
30
|
-
- Supports middleware-adapter. E.g. you can use 'ws' package
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- Rejects if sent into closed socket or after some timeout without response.
|
|
35
|
-
- If something sent before connection is estabilished, it sends when it's ready.
|
|
36
|
-
- Pings to stay connected if necessary.
|
|
31
|
+
- Supports middleware-adapter. E.g. you can use 'ws' package.
|
|
32
|
+
- .on() method with or without condition: analog with .addEventListener.
|
|
33
|
+
|
|
34
|
+
for more, please, take a look at its' typescript types or ask in [discussions](https://github.com/houd1ni/WebsocketPromisify/discussions).
|
|
37
35
|
|
|
38
36
|
How it on Server Side ?
|
|
39
37
|
```
|
|
38
|
+
Let thisnk you use default (JSON) adapter with default (id, data) props. Then:
|
|
40
39
|
1. Serialized JSON is sent by this lib = {id: 'generated_id', data: your data}
|
|
41
|
-
... or some entity from your .encode function(message_id, message_data)
|
|
42
40
|
2. Some Server processing...
|
|
43
41
|
3. Serialized JSON is sent back by the Server = {id: 'the same generated_id', data: feedback data}
|
|
44
|
-
|
|
42
|
+
3.1 if it was stream, it may respond with multiple (id, data) pairs, but in the last message it must add `done: true` prop: {id, data, done: true} to let the library know that it is the last one.
|
|
45
43
|
```
|
|
46
44
|
|
|
47
45
|
|
|
48
|
-
Default constructor config is
|
|
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
|
-
id_key: 'id',
|
|
86
|
-
data_key: 'data'
|
|
87
|
-
},
|
|
88
|
-
// Pings to avoid interruptions. null to disable.
|
|
89
|
-
ping: {
|
|
90
|
-
interval: 55, // seconds.
|
|
91
|
-
content: {} // goes to `data` => { id, data: {} } by default.
|
|
92
|
-
}
|
|
46
|
+
Default constructor config is (all time units are in seconds):
|
|
47
|
+
```typescript
|
|
48
|
+
interface TimeFnParams { base: number, max: number, jitter: number }
|
|
49
|
+
interface Config {
|
|
50
|
+
log: ((event, time, message): any) // only one place where time is in milliseconds.
|
|
51
|
+
timer: false // add or not time deltas to the fn above.
|
|
52
|
+
// Required if `socket` is not set. URL to connect without a protocol.
|
|
53
|
+
// Can start with /, then current page host and port will be used.
|
|
54
|
+
url: 'localhost',
|
|
55
|
+
// Timeout after sending a message before it drops with error.
|
|
56
|
+
timeout: 1.4
|
|
57
|
+
reconnect: { // Reconnect timeout in seconds or false to disable.
|
|
58
|
+
stop_after: number // seconds before giving up.
|
|
59
|
+
on_timeout: boolean // should it reconnect on message timeout ?
|
|
60
|
+
on_break: boolean // should it reconnect on the connection break ?
|
|
61
|
+
time_fn: (params: TimeFnParams, attempt: number): number
|
|
62
|
+
params: { base: number, max: number, jitter: number } // params for the function.
|
|
63
|
+
}
|
|
64
|
+
max_idle_time: Infinity // Time in seconds after the connection is closed if nothing was sent explicitly by send() or stream().
|
|
65
|
+
lazy: false // Lazy connect: connects only if something sent.
|
|
66
|
+
socket: null // Existing socket if you already have one to augment with wspromisify.
|
|
67
|
+
adapter: ((host, protocols): new WebSocket(host, protocols)) // your own middleware here.
|
|
68
|
+
// You can replace original serialisation to your own or even binary stuff. Eg MessagePack or CBOR.
|
|
69
|
+
encode: (message_id, message_data, config): data
|
|
70
|
+
decode: (raw_message): { message_id, message_data } // id_key and data_key could be taken from the config argument.
|
|
71
|
+
protocols: [] // WebSocket constructor's protocol field.
|
|
72
|
+
server: { // Unique id's and data keys to negotiate with back-end.
|
|
73
|
+
id_key: 'id'
|
|
74
|
+
data_key: 'data'
|
|
75
|
+
on_collision: 'error'|'pass'|'ignore' // if a message with unregistred id_key value has come. pass -> handle in 'message-ext'.
|
|
76
|
+
}
|
|
77
|
+
ping: { // Pings to avoid interruptions or false to disable.
|
|
78
|
+
interval: number // inter-ping interval. default: 55.
|
|
79
|
+
timeout: number // when a server does not reply with its' `pong` in this time, close the connection. set to Infinity to disable. default: 30.
|
|
80
|
+
out: string|Uint8Array // frame content for pinging the server. default: 'ping'
|
|
81
|
+
in: string|Uint8Array // frame content the server must send back. default: 'pong'
|
|
82
|
+
}
|
|
93
83
|
}
|
|
94
84
|
```
|
|
95
85
|
|
|
@@ -102,55 +92,51 @@ Fields/Props:
|
|
|
102
92
|
|
|
103
93
|
Methods:
|
|
104
94
|
```javascript
|
|
105
|
-
|
|
106
|
-
//
|
|
107
|
-
ready()
|
|
108
|
-
// sends any type of message and returns a Promise.
|
|
109
|
-
send(message),
|
|
95
|
+
ready(timeout?: number) // Returns Promise that connection is open. Works even if it already opened.
|
|
96
|
+
send(message) // sends any type of message and returns a Promise.
|
|
110
97
|
// Streams as async generator, resolving in chunks.
|
|
111
98
|
// The server must send the same id for chunks then add done: true in the last one.
|
|
112
|
-
*stream(message)
|
|
99
|
+
*stream(message)
|
|
113
100
|
// .addEventListener with optional predicate that works after reconnections.
|
|
114
|
-
on(event_name, handler, predicate
|
|
101
|
+
on(event_name, handler, predicate?: ((WebSocketEvent) => boolean), raw?: boolean)
|
|
102
|
+
off(event_name, handler, raw?: boolean) // `raw` is to attach it to the socket itself.
|
|
103
|
+
addEventListener(event_name, handler, {predicate, raw}) // almost alias for .on()
|
|
104
|
+
removeEventListener(event_name, handler, {predicate, raw}) // almost alias for .off()
|
|
115
105
|
// Closes the connection and free up memory. Returns Promise that it has been done.
|
|
116
|
-
close()
|
|
106
|
+
close(timeout?: number)
|
|
107
|
+
// Routers or modifies frames before the library. Call next(data) to route it to the lib.
|
|
108
|
+
route(handler: (data: T, next: Function) => any)
|
|
117
109
|
```
|
|
118
110
|
|
|
119
111
|
Example (more in `tests` dir in the repo):
|
|
120
|
-
```
|
|
112
|
+
```typescript
|
|
121
113
|
|
|
122
|
-
import
|
|
114
|
+
import {WebSocketClient} from 'wspromisify' // or const WSP = require('wspromisify') in Node.
|
|
123
115
|
|
|
124
116
|
const somehost = 'example.com:8080'
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
} else {
|
|
139
|
-
console.log(event, message)
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
try {
|
|
145
|
-
// You can wait for ready by calling await ws.ready() or send it right now:
|
|
146
|
-
// the messages will be sent as soon as the connection is opened.
|
|
147
|
-
const data = await ws.send({catSaid: 'Meow!'})
|
|
148
|
-
console.log({data})
|
|
149
|
-
} catch(error) {
|
|
150
|
-
console.error('Cannot send a message due to ', error)
|
|
117
|
+
type Protocol = Uint8Array // or string (in the native WebSocket by default).
|
|
118
|
+
|
|
119
|
+
const ws = new WebSocketClient<Protocol>({
|
|
120
|
+
// if url starts with /,
|
|
121
|
+
// it results in ws(s if in https)://currentHost:currentPort/thisUrl
|
|
122
|
+
url: 'ws://example.com/ws',
|
|
123
|
+
timeout: 2, // 2s by default.
|
|
124
|
+
timer: true, // false by default.
|
|
125
|
+
// to log data trips. Events: open, close, send, reconnect, error.
|
|
126
|
+
// if timer isn't enabled, the signature is log(event, message)
|
|
127
|
+
log(event, time, message = '') {
|
|
128
|
+
if(time !== null) console.log(event, `in ${time}ms`, message)
|
|
129
|
+
else console.log(event, message)
|
|
151
130
|
}
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
// You can wait for ready by calling await ws.ready() or send it right now:
|
|
135
|
+
// the messages will be sent as soon as the connection is opened.
|
|
136
|
+
const data = await ws.send({catSaid: 'Meow!'})
|
|
137
|
+
console.log({data})
|
|
138
|
+
} catch(error) {
|
|
139
|
+
console.error('Cannot send a message due to ', error)
|
|
152
140
|
}
|
|
153
141
|
|
|
154
|
-
someFunction()
|
|
155
|
-
|
|
156
142
|
```
|
package/dist/bundle.cjs
CHANGED
|
@@ -284,7 +284,7 @@ const default_config = () => ({
|
|
|
284
284
|
}),
|
|
285
285
|
decode: (rawMessage) => JSON.parse(rawMessage),
|
|
286
286
|
protocols: [], pipes: [],
|
|
287
|
-
server: { id_key: 'id', data_key: 'data' },
|
|
287
|
+
server: { id_key: 'id', data_key: 'data', on_collision: 'error' },
|
|
288
288
|
ping: { interval: 55, timeout: 30, out: 'ping', in: 'pong' }
|
|
289
289
|
});
|
|
290
290
|
const processConfig = (config) => {
|
|
@@ -313,6 +313,7 @@ const nil = null, inf = Infinity;
|
|
|
313
313
|
const resolved = Promise.resolve(nil);
|
|
314
314
|
const label_message = 'message';
|
|
315
315
|
const label_message_ext = 'message-ext';
|
|
316
|
+
const label_error = 'error';
|
|
316
317
|
const zipnum = new J();
|
|
317
318
|
const dnow = () => Date.now();
|
|
318
319
|
const now = () => dnow() / 1e3;
|
|
@@ -334,7 +335,7 @@ const timeout_rm = (q, ff, rj, timeout = .5) => {
|
|
|
334
335
|
};
|
|
335
336
|
const genid = (q) => {
|
|
336
337
|
const id = zipnum.zip((random() * (MAX_32 - 10)) | 0);
|
|
337
|
-
return id
|
|
338
|
+
return q.has(id) ? genid(q) : id;
|
|
338
339
|
};
|
|
339
340
|
const call_q = (q, ...args) => {
|
|
340
341
|
for (const fn of q)
|
|
@@ -415,21 +416,24 @@ class WebSocketClient {
|
|
|
415
416
|
async reconnect(attempt = 0) {
|
|
416
417
|
if (this._reconnecting && attempt === 0)
|
|
417
418
|
return;
|
|
419
|
+
const { reconnect } = this.config;
|
|
420
|
+
if (!reconnect)
|
|
421
|
+
throw new Error('WSC: reconnecting is disabled, but reconned h.b. called!');
|
|
418
422
|
this.log('reconnect');
|
|
419
423
|
this._reconnecting = true;
|
|
420
424
|
this.reconnect_start = now();
|
|
421
425
|
if (!isNil(this.ws))
|
|
422
426
|
this.terminate();
|
|
423
427
|
const { queue } = this;
|
|
424
|
-
if (attempt > 0 && isNil(await this.connect())) {
|
|
428
|
+
if (attempt > 0 && isNil(await this.connect())) { // connected.
|
|
425
429
|
clear_q(call_q(queue.on_ready));
|
|
426
430
|
clear_q(queue.on_ready_fail);
|
|
427
431
|
this._reconnecting = false;
|
|
428
432
|
this.reconnect_timeout = nil;
|
|
429
433
|
}
|
|
430
434
|
else {
|
|
431
|
-
const { stop_after, time_fn, params } =
|
|
432
|
-
if (now() - this.reconnect_start > stop_after) {
|
|
435
|
+
const { stop_after, time_fn, params } = reconnect;
|
|
436
|
+
if (now() - this.reconnect_start > stop_after) { // give up.
|
|
433
437
|
this.terminate();
|
|
434
438
|
clear_q(call_q(queue.on_ready_fail));
|
|
435
439
|
clear_q(queue.on_ready);
|
|
@@ -437,7 +441,8 @@ class WebSocketClient {
|
|
|
437
441
|
this.reconnect_timeout = nil;
|
|
438
442
|
}
|
|
439
443
|
else
|
|
440
|
-
this.reconnect_timeout = sett(
|
|
444
|
+
this.reconnect_timeout = sett(// try more.
|
|
445
|
+
ms(time_fn(params, attempt)), this.reconnect.bind(this, attempt + 1));
|
|
441
446
|
}
|
|
442
447
|
}
|
|
443
448
|
resetReconnect() {
|
|
@@ -448,9 +453,10 @@ class WebSocketClient {
|
|
|
448
453
|
}
|
|
449
454
|
initSocket(ws) {
|
|
450
455
|
const { queue, config, router } = this;
|
|
456
|
+
const { reconnect } = config;
|
|
451
457
|
this.ws = ws;
|
|
452
458
|
clear_q(call_q(this.queue.on_ready));
|
|
453
|
-
const { id_key, data_key } = config.server;
|
|
459
|
+
const { id_key, data_key, on_collision } = config.server;
|
|
454
460
|
// works also on previously opened sockets that do not fire 'open' event.
|
|
455
461
|
this.call('open', ws);
|
|
456
462
|
for (const { msg } of queue.send.values())
|
|
@@ -463,7 +469,7 @@ class WebSocketClient {
|
|
|
463
469
|
this.ws = nil;
|
|
464
470
|
clear_q(call_q(queue.on_close));
|
|
465
471
|
this.call('close', ...e);
|
|
466
|
-
if (!this.intentionally_closed &&
|
|
472
|
+
if (!this.intentionally_closed && reconnect && reconnect.on_break)
|
|
467
473
|
this.reconnect();
|
|
468
474
|
});
|
|
469
475
|
const { ping } = config;
|
|
@@ -481,11 +487,22 @@ class WebSocketClient {
|
|
|
481
487
|
this.call(label_message, d);
|
|
482
488
|
q.ff(d);
|
|
483
489
|
}
|
|
490
|
+
else
|
|
491
|
+
switch (on_collision) {
|
|
492
|
+
case 'error':
|
|
493
|
+
const err = {
|
|
494
|
+
data,
|
|
495
|
+
message: `WSP: id_key exists in the incoming message,
|
|
496
|
+
but does not exist in the queue!`
|
|
497
|
+
};
|
|
498
|
+
this.log(label_error, err);
|
|
499
|
+
this.call(label_error, err);
|
|
500
|
+
case 'ignore': return;
|
|
501
|
+
case 'pass': break;
|
|
502
|
+
}
|
|
484
503
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
this.call(label_message_ext, { data });
|
|
488
|
-
}
|
|
504
|
+
this.log(label_message_ext, data);
|
|
505
|
+
this.call(label_message_ext, { data });
|
|
489
506
|
}
|
|
490
507
|
catch (err) {
|
|
491
508
|
console.error(err, `WSP: Decode error. Got: ${raw}`);
|
|
@@ -566,7 +583,7 @@ class WebSocketClient {
|
|
|
566
583
|
this.ws = nil;
|
|
567
584
|
this.intentionally_closed = true;
|
|
568
585
|
}
|
|
569
|
-
|
|
586
|
+
close(timeout = .5) {
|
|
570
587
|
return new Promise((ff, rj) => {
|
|
571
588
|
if (isNull(this.ws))
|
|
572
589
|
ff(nil);
|
|
@@ -592,6 +609,7 @@ class WebSocketClient {
|
|
|
592
609
|
this.log('send', message_data);
|
|
593
610
|
const { config, queue: { send: send_q, on_ready_fail } } = this;
|
|
594
611
|
const { pipes, server: { data_key } } = config;
|
|
612
|
+
const { reconnect } = config;
|
|
595
613
|
const { top } = opts;
|
|
596
614
|
const id = genid(send_q);
|
|
597
615
|
if (isObj(top) && data_key in top)
|
|
@@ -611,12 +629,14 @@ class WebSocketClient {
|
|
|
611
629
|
const timeout = (rj) => sett(ms(timeout_time), () => {
|
|
612
630
|
if (send_q.has(id)) {
|
|
613
631
|
this.call('timeout', message_data);
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
632
|
+
const reject = () => {
|
|
633
|
+
cleanup();
|
|
634
|
+
rj({
|
|
635
|
+
'Websocket timeout expired': timeout_time,
|
|
636
|
+
'for the message': message_data
|
|
637
|
+
});
|
|
638
|
+
};
|
|
639
|
+
if (reconnect && reconnect.on_timeout) {
|
|
620
640
|
on_ready_fail.push(reject);
|
|
621
641
|
this.reconnect();
|
|
622
642
|
}
|
package/dist/bundle.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ declare namespace wsc {
|
|
|
43
43
|
max: number;
|
|
44
44
|
jitter: number;
|
|
45
45
|
};
|
|
46
|
-
};
|
|
46
|
+
} | false;
|
|
47
47
|
max_idle_time: number;
|
|
48
48
|
lazy: boolean;
|
|
49
49
|
socket: Socket | null;
|
|
@@ -57,6 +57,7 @@ declare namespace wsc {
|
|
|
57
57
|
server: {
|
|
58
58
|
id_key: string;
|
|
59
59
|
data_key: string;
|
|
60
|
+
on_collision: "error" | "pass" | "ignore";
|
|
60
61
|
};
|
|
61
62
|
ping: {
|
|
62
63
|
interval: number;
|
package/dist/bundle.mjs
CHANGED
|
@@ -282,7 +282,7 @@ const default_config = () => ({
|
|
|
282
282
|
}),
|
|
283
283
|
decode: (rawMessage) => JSON.parse(rawMessage),
|
|
284
284
|
protocols: [], pipes: [],
|
|
285
|
-
server: { id_key: 'id', data_key: 'data' },
|
|
285
|
+
server: { id_key: 'id', data_key: 'data', on_collision: 'error' },
|
|
286
286
|
ping: { interval: 55, timeout: 30, out: 'ping', in: 'pong' }
|
|
287
287
|
});
|
|
288
288
|
const processConfig = (config) => {
|
|
@@ -311,6 +311,7 @@ const nil = null, inf = Infinity;
|
|
|
311
311
|
const resolved = Promise.resolve(nil);
|
|
312
312
|
const label_message = 'message';
|
|
313
313
|
const label_message_ext = 'message-ext';
|
|
314
|
+
const label_error = 'error';
|
|
314
315
|
const zipnum = new J();
|
|
315
316
|
const dnow = () => Date.now();
|
|
316
317
|
const now = () => dnow() / 1e3;
|
|
@@ -332,7 +333,7 @@ const timeout_rm = (q, ff, rj, timeout = .5) => {
|
|
|
332
333
|
};
|
|
333
334
|
const genid = (q) => {
|
|
334
335
|
const id = zipnum.zip((random() * (MAX_32 - 10)) | 0);
|
|
335
|
-
return id
|
|
336
|
+
return q.has(id) ? genid(q) : id;
|
|
336
337
|
};
|
|
337
338
|
const call_q = (q, ...args) => {
|
|
338
339
|
for (const fn of q)
|
|
@@ -413,21 +414,24 @@ class WebSocketClient {
|
|
|
413
414
|
async reconnect(attempt = 0) {
|
|
414
415
|
if (this._reconnecting && attempt === 0)
|
|
415
416
|
return;
|
|
417
|
+
const { reconnect } = this.config;
|
|
418
|
+
if (!reconnect)
|
|
419
|
+
throw new Error('WSC: reconnecting is disabled, but reconned h.b. called!');
|
|
416
420
|
this.log('reconnect');
|
|
417
421
|
this._reconnecting = true;
|
|
418
422
|
this.reconnect_start = now();
|
|
419
423
|
if (!isNil(this.ws))
|
|
420
424
|
this.terminate();
|
|
421
425
|
const { queue } = this;
|
|
422
|
-
if (attempt > 0 && isNil(await this.connect())) {
|
|
426
|
+
if (attempt > 0 && isNil(await this.connect())) { // connected.
|
|
423
427
|
clear_q(call_q(queue.on_ready));
|
|
424
428
|
clear_q(queue.on_ready_fail);
|
|
425
429
|
this._reconnecting = false;
|
|
426
430
|
this.reconnect_timeout = nil;
|
|
427
431
|
}
|
|
428
432
|
else {
|
|
429
|
-
const { stop_after, time_fn, params } =
|
|
430
|
-
if (now() - this.reconnect_start > stop_after) {
|
|
433
|
+
const { stop_after, time_fn, params } = reconnect;
|
|
434
|
+
if (now() - this.reconnect_start > stop_after) { // give up.
|
|
431
435
|
this.terminate();
|
|
432
436
|
clear_q(call_q(queue.on_ready_fail));
|
|
433
437
|
clear_q(queue.on_ready);
|
|
@@ -435,7 +439,8 @@ class WebSocketClient {
|
|
|
435
439
|
this.reconnect_timeout = nil;
|
|
436
440
|
}
|
|
437
441
|
else
|
|
438
|
-
this.reconnect_timeout = sett(
|
|
442
|
+
this.reconnect_timeout = sett(// try more.
|
|
443
|
+
ms(time_fn(params, attempt)), this.reconnect.bind(this, attempt + 1));
|
|
439
444
|
}
|
|
440
445
|
}
|
|
441
446
|
resetReconnect() {
|
|
@@ -446,9 +451,10 @@ class WebSocketClient {
|
|
|
446
451
|
}
|
|
447
452
|
initSocket(ws) {
|
|
448
453
|
const { queue, config, router } = this;
|
|
454
|
+
const { reconnect } = config;
|
|
449
455
|
this.ws = ws;
|
|
450
456
|
clear_q(call_q(this.queue.on_ready));
|
|
451
|
-
const { id_key, data_key } = config.server;
|
|
457
|
+
const { id_key, data_key, on_collision } = config.server;
|
|
452
458
|
// works also on previously opened sockets that do not fire 'open' event.
|
|
453
459
|
this.call('open', ws);
|
|
454
460
|
for (const { msg } of queue.send.values())
|
|
@@ -461,7 +467,7 @@ class WebSocketClient {
|
|
|
461
467
|
this.ws = nil;
|
|
462
468
|
clear_q(call_q(queue.on_close));
|
|
463
469
|
this.call('close', ...e);
|
|
464
|
-
if (!this.intentionally_closed &&
|
|
470
|
+
if (!this.intentionally_closed && reconnect && reconnect.on_break)
|
|
465
471
|
this.reconnect();
|
|
466
472
|
});
|
|
467
473
|
const { ping } = config;
|
|
@@ -479,11 +485,22 @@ class WebSocketClient {
|
|
|
479
485
|
this.call(label_message, d);
|
|
480
486
|
q.ff(d);
|
|
481
487
|
}
|
|
488
|
+
else
|
|
489
|
+
switch (on_collision) {
|
|
490
|
+
case 'error':
|
|
491
|
+
const err = {
|
|
492
|
+
data,
|
|
493
|
+
message: `WSP: id_key exists in the incoming message,
|
|
494
|
+
but does not exist in the queue!`
|
|
495
|
+
};
|
|
496
|
+
this.log(label_error, err);
|
|
497
|
+
this.call(label_error, err);
|
|
498
|
+
case 'ignore': return;
|
|
499
|
+
case 'pass': break;
|
|
500
|
+
}
|
|
482
501
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
this.call(label_message_ext, { data });
|
|
486
|
-
}
|
|
502
|
+
this.log(label_message_ext, data);
|
|
503
|
+
this.call(label_message_ext, { data });
|
|
487
504
|
}
|
|
488
505
|
catch (err) {
|
|
489
506
|
console.error(err, `WSP: Decode error. Got: ${raw}`);
|
|
@@ -564,7 +581,7 @@ class WebSocketClient {
|
|
|
564
581
|
this.ws = nil;
|
|
565
582
|
this.intentionally_closed = true;
|
|
566
583
|
}
|
|
567
|
-
|
|
584
|
+
close(timeout = .5) {
|
|
568
585
|
return new Promise((ff, rj) => {
|
|
569
586
|
if (isNull(this.ws))
|
|
570
587
|
ff(nil);
|
|
@@ -590,6 +607,7 @@ class WebSocketClient {
|
|
|
590
607
|
this.log('send', message_data);
|
|
591
608
|
const { config, queue: { send: send_q, on_ready_fail } } = this;
|
|
592
609
|
const { pipes, server: { data_key } } = config;
|
|
610
|
+
const { reconnect } = config;
|
|
593
611
|
const { top } = opts;
|
|
594
612
|
const id = genid(send_q);
|
|
595
613
|
if (isObj(top) && data_key in top)
|
|
@@ -609,12 +627,14 @@ class WebSocketClient {
|
|
|
609
627
|
const timeout = (rj) => sett(ms(timeout_time), () => {
|
|
610
628
|
if (send_q.has(id)) {
|
|
611
629
|
this.call('timeout', message_data);
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
630
|
+
const reject = () => {
|
|
631
|
+
cleanup();
|
|
632
|
+
rj({
|
|
633
|
+
'Websocket timeout expired': timeout_time,
|
|
634
|
+
'for the message': message_data
|
|
635
|
+
});
|
|
636
|
+
};
|
|
637
|
+
if (reconnect && reconnect.on_timeout) {
|
|
618
638
|
on_ready_fail.push(reject);
|
|
619
639
|
this.reconnect();
|
|
620
640
|
}
|
package/package.json
CHANGED
|
@@ -30,18 +30,7 @@
|
|
|
30
30
|
"type": "git",
|
|
31
31
|
"url": "git+https://github.com/houd1ni/WebsocketPromisify.git"
|
|
32
32
|
},
|
|
33
|
-
"
|
|
34
|
-
"lint": "tslint src/*.ts",
|
|
35
|
-
"test": "tsx test/index",
|
|
36
|
-
"test:report": "nyc npm test && nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
|
37
|
-
"gentypes": "dts-bundle-generator --no-check -o dist/bundle.d.ts src/WSC.ts",
|
|
38
|
-
"dev": "cross-env NODE_ENV=development BUILD=es rollup -c",
|
|
39
|
-
"prod:cjs": "cross-env NODE_ENV=production BUILD=cjs rollup -c",
|
|
40
|
-
"prod:es": "cross-env NODE_ENV=production BUILD=es rollup -c",
|
|
41
|
-
"prod": "npm run gentypes && npm run prod:es && npm run prod:cjs",
|
|
42
|
-
"all": "npm run dev && npm run prod"
|
|
43
|
-
},
|
|
44
|
-
"version": "3.0.2",
|
|
33
|
+
"version": "3.1.0",
|
|
45
34
|
"type": "module",
|
|
46
35
|
"exports": {
|
|
47
36
|
".": {
|
|
@@ -75,5 +64,16 @@
|
|
|
75
64
|
"dependencies": {
|
|
76
65
|
"pepka": "^1.13.0",
|
|
77
66
|
"zipnum": "^2.1.3"
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"lint": "tslint src/*.ts",
|
|
70
|
+
"test": "tsx test/index",
|
|
71
|
+
"test:report": "nyc npm test && nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
|
72
|
+
"gentypes": "dts-bundle-generator --no-check -o dist/bundle.d.ts src/WSC.ts",
|
|
73
|
+
"dev": "cross-env NODE_ENV=development BUILD=es rollup -c",
|
|
74
|
+
"prod:cjs": "cross-env NODE_ENV=production BUILD=cjs rollup -c",
|
|
75
|
+
"prod:es": "cross-env NODE_ENV=production BUILD=es rollup -c",
|
|
76
|
+
"prod": "npm run gentypes && npm run prod:es && npm run prod:cjs",
|
|
77
|
+
"all": "npm run dev && npm run prod"
|
|
78
78
|
}
|
|
79
|
-
}
|
|
79
|
+
}
|