wspromisify 3.0.2 → 3.0.3

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 CHANGED
@@ -11,85 +11,74 @@ 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
- - Async/await ready.
21
- - ES-module and commonjs built-in.
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 in Node!
31
- - Custom easy .on method with or without condition: analog to .addEventListener.
32
- - Can log messages/frames/response time into console or wherever you want to. (Hello, firefox 57+!)
33
- - Any protocols field.
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
- ... or some entity that could be parsed by your .decode function(raw_data)
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
- ```javascript
50
- {
51
- // You can also use plain text and blobs in future.
52
- data_type: 'json',
53
- // Debug features. Not required.
54
- log: ((event, time, message) => null),
55
- // Will count milliseconds for responses and put them to log function above.
56
- timer: false,
57
- // Set up.
58
- // Required. URL to connect without a protocol.
59
- // Can start with /, then current page host and port will be used.
60
- url: 'localhost',
61
- // Timeout after sending a message before it drops with error.
62
- timeout: 1400,
63
- // Reconnect timeout in seconds or null.
64
- reconnect: 2,
65
- // Attempts before silently givin' up.
66
- reconnection_attempts: Infinity,
67
- // Time in seconds after the connection is closed if nothing was sent explicitly by send().
68
- max_idle_time: Infinity,
69
- // Lazy connect: connects only if something sent (then sends all of them!)
70
- lazy: false,
71
- // Existing socket if you already have one to augment with this force.
72
- socket: null,
73
- // You can set your own middleware here.
74
- adapter: ((host, protocols) => new WebSocket(host, protocols)),
75
- // You can replace original serialisation to your own or even binary stuff. Eg MessagePack or CBOR.
76
- encode: (message_id, message_data, config) => data,
77
- // You can replace original deserialisation to your own or even
78
- // making the message object from binary data.
79
- // id_key and data_key could be taken from the config argument.
80
- decode: (raw_message) => { message_id, message_data },
81
- // WebSocket constructor's protocol field.
82
- protocols: [],
83
- // Unique id's and data keys to negotiate with back-end.
84
- server: {
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
+ }
76
+ ping: { // Pings to avoid interruptions or false to disable.
77
+ interval: number // inter-ping interval. default: 55.
78
+ timeout: number // when a server does not reply with its' `pong` in this time, close the connection. set to Infinity to disable. default: 30.
79
+ out: string|Uint8Array // frame content for pinging the server. default: 'ping'
80
+ in: string|Uint8Array // frame content the server must send back. default: 'pong'
81
+ }
93
82
  }
94
83
  ```
95
84
 
@@ -102,55 +91,51 @@ Fields/Props:
102
91
 
103
92
  Methods:
104
93
  ```javascript
105
-
106
- // Returns Promise that connection is open. Works even if it already opened.
107
- ready()
108
- // sends any type of message and returns a Promise.
109
- send(message),
94
+ ready(timeout?: number) // Returns Promise that connection is open. Works even if it already opened.
95
+ send(message) // sends any type of message and returns a Promise.
110
96
  // Streams as async generator, resolving in chunks.
111
97
  // The server must send the same id for chunks then add done: true in the last one.
112
- *stream(message),
98
+ *stream(message)
113
99
  // .addEventListener with optional predicate that works after reconnections.
114
- on(event_name, handler, predicate = (WebSocketEvent) => true),
100
+ on(event_name, handler, predicate?: ((WebSocketEvent) => boolean), raw?: boolean)
101
+ off(event_name, handler, raw?: boolean) // `raw` is to attach it to the socket itself.
102
+ addEventListener(event_name, handler, {predicate, raw}) // almost alias for .on()
103
+ removeEventListener(event_name, handler, {predicate, raw}) // almost alias for .off()
115
104
  // Closes the connection and free up memory. Returns Promise that it has been done.
116
- close()
105
+ close(timeout?: number)
106
+ // Routers or modifies frames before the library. Call next(data) to route it to the lib.
107
+ route(handler: (data: T, next: Function) => any)
117
108
  ```
118
109
 
119
110
  Example (more in `tests` dir in the repo):
120
- ```javascript
111
+ ```typescript
121
112
 
122
- import WSP from 'wspromisify' // or const WSP = require('wspromisify') in Node.
113
+ import {WebSocketClient} from 'wspromisify' // or const WSP = require('wspromisify') in Node.
123
114
 
124
115
  const somehost = 'example.com:8080'
125
-
126
- const someFunction = async () => {
127
- const ws = new WSP({
128
- // If url starts with /,
129
- // it results in ws(s if in https)://currentHost:currentPort/thisUrl
130
- url: 'ws://example.com/ws',
131
- timeout: 2e3, // 1400ms by default.
132
- timer: true, // false by default.
133
- // To log data trips. Events: open, close, send, reconnect, error.
134
- // If timer isn't enabled, the signature is log(event, message)
135
- log(event, time, message = '') {
136
- if(time !== null) {
137
- console.log(event, `in ${time}ms`, message)
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)
116
+ type Protocol = Uint8Array // or string (in the native WebSocket by default).
117
+
118
+ const ws = new WebSocketClient<Protocol>({
119
+ // if url starts with /,
120
+ // it results in ws(s if in https)://currentHost:currentPort/thisUrl
121
+ url: 'ws://example.com/ws',
122
+ timeout: 2, // 2s by default.
123
+ timer: true, // false by default.
124
+ // to log data trips. Events: open, close, send, reconnect, error.
125
+ // if timer isn't enabled, the signature is log(event, message)
126
+ log(event, time, message = '') {
127
+ if(time !== null) console.log(event, `in ${time}ms`, message)
128
+ else console.log(event, message)
151
129
  }
130
+ })
131
+
132
+ try {
133
+ // You can wait for ready by calling await ws.ready() or send it right now:
134
+ // the messages will be sent as soon as the connection is opened.
135
+ const data = await ws.send({catSaid: 'Meow!'})
136
+ console.log({data})
137
+ } catch(error) {
138
+ console.error('Cannot send a message due to ', error)
152
139
  }
153
140
 
154
- someFunction()
155
-
156
141
  ```
package/dist/bundle.cjs CHANGED
@@ -334,7 +334,7 @@ const timeout_rm = (q, ff, rj, timeout = .5) => {
334
334
  };
335
335
  const genid = (q) => {
336
336
  const id = zipnum.zip((random() * (MAX_32 - 10)) | 0);
337
- return id in q ? genid(q) : id;
337
+ return q.has(id) ? genid(q) : id;
338
338
  };
339
339
  const call_q = (q, ...args) => {
340
340
  for (const fn of q)
@@ -415,21 +415,24 @@ class WebSocketClient {
415
415
  async reconnect(attempt = 0) {
416
416
  if (this._reconnecting && attempt === 0)
417
417
  return;
418
+ const { reconnect } = this.config;
419
+ if (!reconnect)
420
+ throw new Error('WSC: reconnecting is disabled, but reconned h.b. called!');
418
421
  this.log('reconnect');
419
422
  this._reconnecting = true;
420
423
  this.reconnect_start = now();
421
424
  if (!isNil(this.ws))
422
425
  this.terminate();
423
426
  const { queue } = this;
424
- if (attempt > 0 && isNil(await this.connect())) {
427
+ if (attempt > 0 && isNil(await this.connect())) { // connected.
425
428
  clear_q(call_q(queue.on_ready));
426
429
  clear_q(queue.on_ready_fail);
427
430
  this._reconnecting = false;
428
431
  this.reconnect_timeout = nil;
429
432
  }
430
433
  else {
431
- const { stop_after, time_fn, params } = this.config.reconnect;
432
- if (now() - this.reconnect_start > stop_after) {
434
+ const { stop_after, time_fn, params } = reconnect;
435
+ if (now() - this.reconnect_start > stop_after) { // give up.
433
436
  this.terminate();
434
437
  clear_q(call_q(queue.on_ready_fail));
435
438
  clear_q(queue.on_ready);
@@ -437,7 +440,8 @@ class WebSocketClient {
437
440
  this.reconnect_timeout = nil;
438
441
  }
439
442
  else
440
- this.reconnect_timeout = sett(ms(time_fn(params, attempt)), this.reconnect.bind(this, attempt + 1));
443
+ this.reconnect_timeout = sett(// try more.
444
+ ms(time_fn(params, attempt)), this.reconnect.bind(this, attempt + 1));
441
445
  }
442
446
  }
443
447
  resetReconnect() {
@@ -448,6 +452,7 @@ class WebSocketClient {
448
452
  }
449
453
  initSocket(ws) {
450
454
  const { queue, config, router } = this;
455
+ const { reconnect } = config;
451
456
  this.ws = ws;
452
457
  clear_q(call_q(this.queue.on_ready));
453
458
  const { id_key, data_key } = config.server;
@@ -463,7 +468,7 @@ class WebSocketClient {
463
468
  this.ws = nil;
464
469
  clear_q(call_q(queue.on_close));
465
470
  this.call('close', ...e);
466
- if (!this.intentionally_closed && config.reconnect.on_break)
471
+ if (!this.intentionally_closed && reconnect && reconnect.on_break)
467
472
  this.reconnect();
468
473
  });
469
474
  const { ping } = config;
@@ -566,7 +571,7 @@ class WebSocketClient {
566
571
  this.ws = nil;
567
572
  this.intentionally_closed = true;
568
573
  }
569
- async close(timeout = .5) {
574
+ close(timeout = .5) {
570
575
  return new Promise((ff, rj) => {
571
576
  if (isNull(this.ws))
572
577
  ff(nil);
@@ -592,6 +597,7 @@ class WebSocketClient {
592
597
  this.log('send', message_data);
593
598
  const { config, queue: { send: send_q, on_ready_fail } } = this;
594
599
  const { pipes, server: { data_key } } = config;
600
+ const { reconnect } = config;
595
601
  const { top } = opts;
596
602
  const id = genid(send_q);
597
603
  if (isObj(top) && data_key in top)
@@ -611,12 +617,14 @@ class WebSocketClient {
611
617
  const timeout = (rj) => sett(ms(timeout_time), () => {
612
618
  if (send_q.has(id)) {
613
619
  this.call('timeout', message_data);
614
- cleanup();
615
- const reject = () => rj({
616
- 'Websocket timeout expired': timeout_time,
617
- 'for the message': message_data
618
- });
619
- if (config.reconnect.on_timeout) {
620
+ const reject = () => {
621
+ cleanup();
622
+ rj({
623
+ 'Websocket timeout expired': timeout_time,
624
+ 'for the message': message_data
625
+ });
626
+ };
627
+ if (reconnect && reconnect.on_timeout) {
620
628
  on_ready_fail.push(reject);
621
629
  this.reconnect();
622
630
  }
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;
package/dist/bundle.mjs CHANGED
@@ -332,7 +332,7 @@ const timeout_rm = (q, ff, rj, timeout = .5) => {
332
332
  };
333
333
  const genid = (q) => {
334
334
  const id = zipnum.zip((random() * (MAX_32 - 10)) | 0);
335
- return id in q ? genid(q) : id;
335
+ return q.has(id) ? genid(q) : id;
336
336
  };
337
337
  const call_q = (q, ...args) => {
338
338
  for (const fn of q)
@@ -413,21 +413,24 @@ class WebSocketClient {
413
413
  async reconnect(attempt = 0) {
414
414
  if (this._reconnecting && attempt === 0)
415
415
  return;
416
+ const { reconnect } = this.config;
417
+ if (!reconnect)
418
+ throw new Error('WSC: reconnecting is disabled, but reconned h.b. called!');
416
419
  this.log('reconnect');
417
420
  this._reconnecting = true;
418
421
  this.reconnect_start = now();
419
422
  if (!isNil(this.ws))
420
423
  this.terminate();
421
424
  const { queue } = this;
422
- if (attempt > 0 && isNil(await this.connect())) {
425
+ if (attempt > 0 && isNil(await this.connect())) { // connected.
423
426
  clear_q(call_q(queue.on_ready));
424
427
  clear_q(queue.on_ready_fail);
425
428
  this._reconnecting = false;
426
429
  this.reconnect_timeout = nil;
427
430
  }
428
431
  else {
429
- const { stop_after, time_fn, params } = this.config.reconnect;
430
- if (now() - this.reconnect_start > stop_after) {
432
+ const { stop_after, time_fn, params } = reconnect;
433
+ if (now() - this.reconnect_start > stop_after) { // give up.
431
434
  this.terminate();
432
435
  clear_q(call_q(queue.on_ready_fail));
433
436
  clear_q(queue.on_ready);
@@ -435,7 +438,8 @@ class WebSocketClient {
435
438
  this.reconnect_timeout = nil;
436
439
  }
437
440
  else
438
- this.reconnect_timeout = sett(ms(time_fn(params, attempt)), this.reconnect.bind(this, attempt + 1));
441
+ this.reconnect_timeout = sett(// try more.
442
+ ms(time_fn(params, attempt)), this.reconnect.bind(this, attempt + 1));
439
443
  }
440
444
  }
441
445
  resetReconnect() {
@@ -446,6 +450,7 @@ class WebSocketClient {
446
450
  }
447
451
  initSocket(ws) {
448
452
  const { queue, config, router } = this;
453
+ const { reconnect } = config;
449
454
  this.ws = ws;
450
455
  clear_q(call_q(this.queue.on_ready));
451
456
  const { id_key, data_key } = config.server;
@@ -461,7 +466,7 @@ class WebSocketClient {
461
466
  this.ws = nil;
462
467
  clear_q(call_q(queue.on_close));
463
468
  this.call('close', ...e);
464
- if (!this.intentionally_closed && config.reconnect.on_break)
469
+ if (!this.intentionally_closed && reconnect && reconnect.on_break)
465
470
  this.reconnect();
466
471
  });
467
472
  const { ping } = config;
@@ -564,7 +569,7 @@ class WebSocketClient {
564
569
  this.ws = nil;
565
570
  this.intentionally_closed = true;
566
571
  }
567
- async close(timeout = .5) {
572
+ close(timeout = .5) {
568
573
  return new Promise((ff, rj) => {
569
574
  if (isNull(this.ws))
570
575
  ff(nil);
@@ -590,6 +595,7 @@ class WebSocketClient {
590
595
  this.log('send', message_data);
591
596
  const { config, queue: { send: send_q, on_ready_fail } } = this;
592
597
  const { pipes, server: { data_key } } = config;
598
+ const { reconnect } = config;
593
599
  const { top } = opts;
594
600
  const id = genid(send_q);
595
601
  if (isObj(top) && data_key in top)
@@ -609,12 +615,14 @@ class WebSocketClient {
609
615
  const timeout = (rj) => sett(ms(timeout_time), () => {
610
616
  if (send_q.has(id)) {
611
617
  this.call('timeout', message_data);
612
- cleanup();
613
- const reject = () => rj({
614
- 'Websocket timeout expired': timeout_time,
615
- 'for the message': message_data
616
- });
617
- if (config.reconnect.on_timeout) {
618
+ const reject = () => {
619
+ cleanup();
620
+ rj({
621
+ 'Websocket timeout expired': timeout_time,
622
+ 'for the message': message_data
623
+ });
624
+ };
625
+ if (reconnect && reconnect.on_timeout) {
618
626
  on_ready_fail.push(reject);
619
627
  this.reconnect();
620
628
  }
package/package.json CHANGED
@@ -41,7 +41,7 @@
41
41
  "prod": "npm run gentypes && npm run prod:es && npm run prod:cjs",
42
42
  "all": "npm run dev && npm run prod"
43
43
  },
44
- "version": "3.0.2",
44
+ "version": "3.0.3",
45
45
  "type": "module",
46
46
  "exports": {
47
47
  ".": {