wspromisify 2.4.4 → 2.6.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/LICENSE +1 -1
- package/README.md +1 -2
- package/TODO +9 -0
- package/dist/bundle.cjs +1 -1
- package/dist/bundle.d.ts +9 -3
- package/dist/bundle.mjs +1 -1
- package/package.json +79 -94
- package/rollup.config.js +1 -1
- package/src/WSC.ts +266 -0
- package/src/config.ts +7 -6
- package/src/types.ts +1 -1
- package/src/utils.ts +5 -23
- package/test/index.ts +21 -0
- package/test/mock/WS.ts +43 -0
- package/test/mock/server.ts +17 -0
- package/test/specs/close.ts +18 -0
- package/test/specs/drops.ts +30 -0
- package/test/specs/echo.ts +24 -0
- package/test/specs/encode-decode.ts +1 -0
- package/test/specs/existing_socket.ts +55 -0
- package/test/specs/lazy.ts +22 -0
- package/test/specs/lazySendBeforeOpen.ts +19 -0
- package/test/specs/no-native-throws.ts +18 -0
- package/test/specs/ready.ts +12 -0
- package/test/specs/reconnect.ts +23 -0
- package/test/specs/socket.ts +13 -0
- package/test/suite.ts +3 -0
- package/test/utils.ts +27 -0
- package/tsconfig.json +3 -2
- package/src/WS.ts +0 -166
- package/src/connectLib.ts +0 -120
- package/test/mock/WS.js +0 -51
- package/test/mock/index.js +0 -52
- package/test/specs/close.js +0 -25
- package/test/specs/drops.js +0 -29
- package/test/specs/echo.js +0 -26
- package/test/specs/encode-decode.js +0 -3
- package/test/specs/existing_socket.js +0 -55
- package/test/specs/lazy.js +0 -26
- package/test/specs/ready.js +0 -16
- package/test/specs/reconnect.js +0 -28
- package/test/specs/sendBeforeOpen.js +0 -23
- package/test/specs/socket.js +0 -22
- package/test/specs/utils_once.js +0 -16
- package/test/utils.js +0 -27
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# WebsocketPromisify
|
|
2
|
-
|
|
3
|
-
[](https://circleci.com/gh/houd1ni/WebsocketPromisify/tree/master) [](https://codecov.io/gh/houd1ni/WebsocketPromisify) [](https://bundlephobia.com/result?p=wspromisify) [](https://www.npmjs.com/package/wspromisify) [](https://david-dm.org/houd1ni/WebsocketPromisify) [](https://david-dm.org/houd1ni/WebsocketPromisify)
|
|
2
|
+
[](https://scrutinizer-ci.com/g/houd1ni/WebsocketPromisify/build-status/master) [](https://scrutinizer-ci.com/g/houd1ni/WebsocketPromisify/code-structure/master) [](https://codecov.io/gh/houd1ni/WebsocketPromisify) [](https://deno.bundlejs.com/badge?q=wspromisify@2.4.4&treeshake=[*]) [](https://www.npmjs.com/package/wspromisify)
|
|
4
3
|
|
|
5
4
|
A nice-looking this readme version: https://houd1ni.github.io/WebsocketPromisify/
|
|
6
5
|
|
package/TODO
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#v3 at least:
|
|
2
|
+
|
|
3
|
+
[The milestone](https://github.com/houd1ni/WebsocketPromisify/milestone/1)
|
|
4
|
+
|
|
5
|
+
- [x] [close event bug](https://github.com/houd1ni/WebsocketPromisify/issues/38) (fixed in 2.5.0)
|
|
6
|
+
- [ ] [BREAKING] export named WebSocketClient instead of default export.
|
|
7
|
+
- [ ] [ping-pong without id](https://github.com/houd1ni/WebsocketPromisify/issues/39)
|
|
8
|
+
- [ ] [implement prepare](https://github.com/houd1ni/WebsocketPromisify/issues/34)
|
|
9
|
+
- [ ] [wiki](https://github.com/houd1ni/WebsocketPromisify/issues/4)
|
package/dist/bundle.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const e=Symbol("Placeholder"),t=t=>{let n=0;for(const s of t)s!==e&&n++;return n},n=(t,n)=>{const s=t.length,o=t.slice(),r=n.length;let i=r,
|
|
1
|
+
"use strict";const e=Symbol("Placeholder"),t=t=>{let n=0;for(const s of t)s!==e&&n++;return n},n=(t,n)=>{const s=t.length,o=t.slice(),r=n.length;let i=r,l=0;for(;i&&l<s;l++)o[l]===e&&(o[l]=n[r-i],i--);for(l=s;i;l++,i--)o[l]=n[r-i];return o},s=(e,o,r)=>{const i=e.length-o.length-t(r);if(i<1)return e(...n(o,r));{const t=(...t)=>s(e,n(o,r),t);return t.$args_left=i,t}},o=e=>(...n)=>e.length>t(n)?s(e,[],n):e(...n);function r(t){return function(n,s){const o=n===e,r=arguments.length;if(1===r&&o)throw new Error("Senseless placeholder usage.");return r>1?o?(t=>function(n){return n===e?t:t(n)})((e=>t(e,s))):t(n,s):e=>t(n,e)}}function i(e){return o(e)}const l=void 0,c=1/0,u=e=>typeof e,a=e=>null===e,h={u:"U",b:"B",n:"N",s:"S",f:"F"},f=Symbol(),d=e=>{const t=u(e);return"object"===t?a(e)?"Null":e.constructor.name:h[t[0]]+t.slice(1)},p=e=>e.length,g=r(((e,t)=>e===t)),m=r(((e,t)=>{const n=d(e);if(g(n,d(t))&&(g(n,"Object")||g(n,"Array"))){if(a(e)||a(t))return g(e,t);if(g(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(g(n,t)&&s in e||g(n,e)&&s in t&&m(e[s],t[s])))return!1;return!0}return g(e,t)})),y=r(((e,t)=>(t.push(e),t))),w=i(((e,t,n)=>n.reduce(e,t))),b=o(((e,t,n,s)=>e(s)?t(s):n(s))),_=(...t)=>(...n)=>{let s,o=!0;for(let r=p(t)-1;r>-1;r--)o?(o=!1,s=t[r](...n)):s=s===e?t[r]():t[r](s);return s},k=r(((e,t)=>t[e])),S=i(((e,t,n)=>n.slice(e,(e=>"number"==u(e))(t)?t:c))),v=k(0);S(1,c);const E=r(((e,t)=>t.find(e))),P=e=>()=>e,W=r(((e,t)=>t.split(e))),j=P(!0),N=P(!1),q=r(((e,t)=>w(((t,n)=>E((t=>e(n,t)),t)?t:y(n,t)),[],t)))(m),z=i(((e,t,n)=>p(t)?(e=>a(e)||(e=>e===l)(e))(n)?e:_((s=>s in n?z(e,S(1,c,t),n[s]):e),v)(t):n));z(l),_(b(m(f),N,j),z(f));const Q=r(((e,t)=>t.map(e))),{floor:A}=Math,C="0123456789abcdefghijklmnopqrstuvwxyz",O=_((e=>Object.fromEntries(e)),Q(((e,t)=>[e,t])),W(""));class ${abc;abclen;c2pos;standard;setABC(e){if(!_(m(p(t=e)),p,q,W(""))(t))throw new Error("Not all chars are unique!");var t;this.abc=e,this.abclen=e.length,this.standard=C.startsWith(e),this.c2pos=O(e)}zip(e){const{abc:t,abclen:n}=this;let s="";for(;e>0;)s=t[e%n]+s,e=A(e/n);return s||"0"}unzip(e){const{standard:t,abclen:n,c2pos:s}=this;if(t)return parseInt(e,n);const o=e.length;let r=0;for(let t=0;t<o;t++)r+=s[e[t]]*n**(o-t-1);return r}constructor(e){this.setABC(e||C+"ABCDEFGHIJKLMNOPQRSTUVWXYZ")}}const B=new $;B.setABC.bind(B),B.zip.bind(B),B.unzip.bind(B);const R=(()=>{try{return WebSocket||null}catch{return null}})(),I=(e,t,n)=>e.addEventListener(t,n),T=(e,t)=>setTimeout(t,e),D={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,lazy:!1,socket:null,adapter:(e,t)=>new WebSocket(e,t),encode:(e,t,{server:n})=>JSON.stringify({[n.id_key]:e,[n.data_key]:t}),decode:e=>JSON.parse(e),protocols:[],pipes:[],server:{id_key:"id",data_key:"data"},ping:{interval:55,content:{}}},U=Symbol("Placeholder"),x=e=>{let t=0;for(const n of e)n!==U&&t++;return t},F=(e,t)=>{const n=e.length,s=e.slice(),o=t.length;let r=o,i=0;for(;r&&i<n;i++)s[i]===U&&(s[i]=t[o-r],r--);for(i=n;r;i++,r--)s[i]=t[o-r];return s},J=(e,t,n)=>{const s=e.length-t.length-x(n);if(s<1)return e(...F(t,n));{const o=(...s)=>J(e,F(t,n),s);return o.$args_left=s,o}},L=e=>(...t)=>e.length>x(t)?J(e,[],t):e(...t);function M(e){return function(t,n){const s=t===U,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return o>1?s?(e=>function(t){return t===U?e:e(t)})((t=>e(t,n))):e(t,n):n=>e(t,n)}}function G(e){return L(e)}const H=void 0,K=1/0,V=e=>typeof e,X=e=>null===e,Y=e=>"number"==V(e),Z={u:"U",b:"B",n:"N",s:"S",f:"F"},ee=Symbol(),te=e=>{const t=V(e);return"object"===t?X(e)?"Null":e.constructor.name:Z[t[0]]+t.slice(1)},ne=e=>e.length,se=e=>X(e)||(e=>e===H)(e),oe=M(((e,t)=>e===t)),re=M(((e,t)=>{const n=te(e);if(oe(n,te(t))&&(oe(n,"Object")||oe(n,"Array"))){if(X(e)||X(t))return oe(e,t);if(oe(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(oe(n,t)&&s in e||oe(n,e)&&s in t&&re(e[s],t[s])))return!1;return!0}return oe(e,t)})),ie=M(((e,t)=>(t.push(e),t))),le=G(((e,t,n)=>n.reduce(e,t))),ce=L(((e,t,n,s)=>e(s)?t(s):n(s))),ue=(...e)=>(...t)=>{let n,s=!0;for(let o=ne(e)-1;o>-1;o--)s?(s=!1,n=e[o](...t)):n=n===U?e[o]():e[o](n);return n},ae=M(((e,t)=>t[e])),he=G(((e,t,n)=>n.slice(e,Y(t)?t:K))),fe=ae(0);he(1,K);const de=M(((e,t)=>t.find(e))),pe=e=>()=>e,ge=pe(!0),me=pe(!1),ye=M(((e,t)=>le(((t,n)=>de((t=>e(n,t)),t)?t:ie(n,t)),[],t)));ye(re);const we=e=>{let t,n=!1;return(...s)=>n?t:(n=!0,t=e(...s))},be=G(((e,t,n)=>ne(t)?se(n)?e:ue((s=>s in n?be(e,he(1,K,t),n[s]):e),fe)(t):n));be(H),ue(ce(re(ee),me,ge),be(ee));const _e=new $;module.exports=class{open=!1;ws=null;forcibly_closed=!1;reconnect_timeout=null;queue={};messages=[];onReadyQueue=[];onCloseQueue=[];handlers={open:[],close:[],message:[],error:[],timeout:[]};config={};init_flush(){this.queue={},this.messages=[]}call(e,...t){for(const n of this.handlers[e])n(...t)}log(e,t=null,n=null){const s=this.config;null!==n?s.log(e,n,t):s.timer?s.log(e,null,t):s.log(e,t)}initSocket(e){const t=this.config;this.open=!0,this.onReadyQueue.forEach((e=>e())),this.onReadyQueue.splice(0);const{id_key:n,data_key:s}=t.server;if(this.call("open",e),this.messages.forEach((e=>e.send())),null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),t.ping){const e=setInterval((()=>{this.open&&this.send(t.ping.content),this.forcibly_closed&&clearInterval(e)}),1e3*t.ping.interval)}I(e,"close",(async(...e)=>{this.log("close"),this.open=!1,this.onCloseQueue.forEach((e=>e())),this.onCloseQueue.splice(0),this.call("close",...e);const n=t.reconnect;if("number"!=typeof n||isNaN(n)||this.forcibly_closed)this.ws=null,this.open=!1;else{const e=async()=>{this.log("reconnect"),null!==this.ws&&(this.ws.close(),this.ws=null);null!==await this.connect()&&(this.reconnect_timeout=setTimeout(e,1e3*n))};e()}this.forcibly_closed=!1})),I(e,"message",(e=>{try{const o=t.decode(e.data);if(this.call("message",{...e,data:o}),o[n]){const e=this.queue[o[n]];if(e){const t=e.sent_time?Date.now()-e.sent_time:null;this.log("message",o[s],t),e.ff(o[s]),clearTimeout(e.timeout),delete this.queue[o[n]]}}}catch(t){console.error(t,`WSP: Decode error. Got: ${e.data}`)}}))}connect(){return new Promise((e=>{if(!0===this.open)return e(null);const t=this.config,n=t.socket||t.adapter(t.url,t.protocols);if(this.ws=n,!n||n.readyState>1)return this.ws=null,this.log("error","ready() on closing or closed state! status 2."),e(2);const s=we(e);I(n,"error",we((e=>{this.log("error","status 3."),this.call("error",e),this.ws=null,s(3)}))),n.readyState?(this.initSocket(n),s(null)):I(n,"open",we((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((e=>{this.open?e():this.onReadyQueue.push(e)}))}on(e,t,n=ge,s=!1){const o=e=>n(e)&&t(e);return s?I(this.ws,e,o):this.handlers[e].push(o)}async close(){return new Promise(((e,t)=>{null===this.ws?t("WSP: closing a non-inited socket!"):(this.open=!1,this.onCloseQueue.push((()=>{this.init_flush(),this.ws=null,this.forcibly_closed=!0,e(null)})),this.ws.close())}))}async send(e,t={}){this.log("send",e);const n=this.config,s={},o=n.server.data_key,r=n.lazy&&!this.open,i=_e.zip(2147483637*Math.random()|0);if("object"==typeof t.top){if(t.top[o])throw new Error("Attempting to set data key/token via send() options!");Object.assign(s,t.top)}if(n.pipes.forEach((t=>e=t(e))),!0===this.open)this.ws.send(n.encode(i,e,n));else if(!1===this.open||r)this.messages.push({send:()=>this.ws.send(n.encode(i,e,n))}),r&&this.connect();else if(null===this.open)throw new Error("Attempting to send via closed WebSocket connection!");return new Promise(((t,s)=>{this.queue[i]={ff:t,data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:T(n.timeout,(()=>{this.queue[i]&&(this.call("timeout",e),s({"Websocket timeout expired: ":n.timeout,"for the message ":e}),delete this.queue[i])}))}}))}constructor(e={}){this.config=(e=>{if(null===R&&!("adapter"in e))throw new Error("\n This platform has no native WebSocket implementation.\n Please use 'ws' package as an adapter.\n See https://github.com/houd1ni/WebsocketPromisify/issues/23\n ");const t=Object.assign({},D,e),n=t.url;if("/"==n[0])try{const e=location.protocol.includes("s:")?"wss":"ws";t.url=`${e}://${location.hostname}:${location.port}${n}`}catch(e){throw new Error("WSP: URL starting with / in non-browser environment!")}return t})(e),this.init_flush(),this.config.lazy||this.connect()}};
|
package/dist/bundle.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// Generated by dts-bundle-generator
|
|
1
|
+
// Generated by dts-bundle-generator v9.5.1
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
declare namespace wsc {
|
|
4
4
|
interface DataObject {
|
|
5
5
|
[key: string]: any;
|
|
6
6
|
}
|
|
7
|
-
type WSEvent = "open" | "message" | "close" | "error";
|
|
7
|
+
type WSEvent = "open" | "message" | "close" | "error" | "timeout";
|
|
8
8
|
/** Minimal socket-like interface. */
|
|
9
9
|
interface Socket {
|
|
10
10
|
readyState: number;
|
|
@@ -60,12 +60,18 @@ declare class WebSocketClient {
|
|
|
60
60
|
private handlers;
|
|
61
61
|
private config;
|
|
62
62
|
private init_flush;
|
|
63
|
+
private call;
|
|
63
64
|
private log;
|
|
65
|
+
private initSocket;
|
|
64
66
|
private connect;
|
|
65
67
|
get socket(): wsc.Socket | null;
|
|
66
68
|
ready(): Promise<void>;
|
|
67
69
|
on(event_name: wsc.WSEvent, handler: (data: any) => any, predicate?: (data: any) => boolean, raw?: boolean): number | void;
|
|
68
70
|
close(): wsc.AsyncErrCode;
|
|
71
|
+
/** .send(your_data) wraps request to server with {id: `hash`, data: `actually your data`},
|
|
72
|
+
returns a Promise that will be rejected after a timeout or
|
|
73
|
+
resolved if server returns the same signature: {id: `same_hash`, data: `response data`}.
|
|
74
|
+
*/
|
|
69
75
|
send<RequestDataType = any, ResponseDataType = any>(message_data: RequestDataType, opts?: wsc.SendOptions): Promise<ResponseDataType>;
|
|
70
76
|
constructor(user_config?: wsc.UserConfig);
|
|
71
77
|
}
|
package/dist/bundle.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=Symbol("Placeholder"),t=t=>{let n=0;for(const s of t)s!==e&&n++;return n},n=(t,n)=>{const s=t.length,o=t.slice(),r=n.length;let i=r,
|
|
1
|
+
const e=Symbol("Placeholder"),t=t=>{let n=0;for(const s of t)s!==e&&n++;return n},n=(t,n)=>{const s=t.length,o=t.slice(),r=n.length;let i=r,l=0;for(;i&&l<s;l++)o[l]===e&&(o[l]=n[r-i],i--);for(l=s;i;l++,i--)o[l]=n[r-i];return o},s=(e,o,r)=>{const i=e.length-o.length-t(r);if(i<1)return e(...n(o,r));{const t=(...t)=>s(e,n(o,r),t);return t.$args_left=i,t}},o=e=>(...n)=>e.length>t(n)?s(e,[],n):e(...n);function r(t){return function(n,s){const o=n===e,r=arguments.length;if(1===r&&o)throw new Error("Senseless placeholder usage.");return r>1?o?(t=>function(n){return n===e?t:t(n)})((e=>t(e,s))):t(n,s):e=>t(n,e)}}function i(e){return o(e)}const l=void 0,c=1/0,u=e=>typeof e,a=e=>null===e,h={u:"U",b:"B",n:"N",s:"S",f:"F"},f=Symbol(),d=e=>{const t=u(e);return"object"===t?a(e)?"Null":e.constructor.name:h[t[0]]+t.slice(1)},p=e=>e.length,g=r(((e,t)=>e===t)),m=r(((e,t)=>{const n=d(e);if(g(n,d(t))&&(g(n,"Object")||g(n,"Array"))){if(a(e)||a(t))return g(e,t);if(g(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(g(n,t)&&s in e||g(n,e)&&s in t&&m(e[s],t[s])))return!1;return!0}return g(e,t)})),y=r(((e,t)=>(t.push(e),t))),w=i(((e,t,n)=>n.reduce(e,t))),b=o(((e,t,n,s)=>e(s)?t(s):n(s))),_=(...t)=>(...n)=>{let s,o=!0;for(let r=p(t)-1;r>-1;r--)o?(o=!1,s=t[r](...n)):s=s===e?t[r]():t[r](s);return s},k=r(((e,t)=>t[e])),S=i(((e,t,n)=>n.slice(e,(e=>"number"==u(e))(t)?t:c))),v=k(0);S(1,c);const E=r(((e,t)=>t.find(e))),P=e=>()=>e,W=r(((e,t)=>t.split(e))),j=P(!0),N=P(!1),q=r(((e,t)=>w(((t,n)=>E((t=>e(n,t)),t)?t:y(n,t)),[],t)))(m),z=i(((e,t,n)=>p(t)?(e=>a(e)||(e=>e===l)(e))(n)?e:_((s=>s in n?z(e,S(1,c,t),n[s]):e),v)(t):n));z(l),_(b(m(f),N,j),z(f));const Q=r(((e,t)=>t.map(e))),{floor:A}=Math,C="0123456789abcdefghijklmnopqrstuvwxyz",O=_((e=>Object.fromEntries(e)),Q(((e,t)=>[e,t])),W(""));class ${abc;abclen;c2pos;standard;setABC(e){if(!_(m(p(t=e)),p,q,W(""))(t))throw new Error("Not all chars are unique!");var t;this.abc=e,this.abclen=e.length,this.standard=C.startsWith(e),this.c2pos=O(e)}zip(e){const{abc:t,abclen:n}=this;let s="";for(;e>0;)s=t[e%n]+s,e=A(e/n);return s||"0"}unzip(e){const{standard:t,abclen:n,c2pos:s}=this;if(t)return parseInt(e,n);const o=e.length;let r=0;for(let t=0;t<o;t++)r+=s[e[t]]*n**(o-t-1);return r}constructor(e){this.setABC(e||C+"ABCDEFGHIJKLMNOPQRSTUVWXYZ")}}const B=new $;B.setABC.bind(B),B.zip.bind(B),B.unzip.bind(B);const R=(()=>{try{return WebSocket||null}catch{return null}})(),I=(e,t,n)=>e.addEventListener(t,n),T=(e,t)=>setTimeout(t,e),D={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,lazy:!1,socket:null,adapter:(e,t)=>new WebSocket(e,t),encode:(e,t,{server:n})=>JSON.stringify({[n.id_key]:e,[n.data_key]:t}),decode:e=>JSON.parse(e),protocols:[],pipes:[],server:{id_key:"id",data_key:"data"},ping:{interval:55,content:{}}},U=Symbol("Placeholder"),x=e=>{let t=0;for(const n of e)n!==U&&t++;return t},F=(e,t)=>{const n=e.length,s=e.slice(),o=t.length;let r=o,i=0;for(;r&&i<n;i++)s[i]===U&&(s[i]=t[o-r],r--);for(i=n;r;i++,r--)s[i]=t[o-r];return s},J=(e,t,n)=>{const s=e.length-t.length-x(n);if(s<1)return e(...F(t,n));{const o=(...s)=>J(e,F(t,n),s);return o.$args_left=s,o}},L=e=>(...t)=>e.length>x(t)?J(e,[],t):e(...t);function M(e){return function(t,n){const s=t===U,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return o>1?s?(e=>function(t){return t===U?e:e(t)})((t=>e(t,n))):e(t,n):n=>e(t,n)}}function G(e){return L(e)}const H=void 0,K=1/0,V=e=>typeof e,X=e=>null===e,Y=e=>"number"==V(e),Z={u:"U",b:"B",n:"N",s:"S",f:"F"},ee=Symbol(),te=e=>{const t=V(e);return"object"===t?X(e)?"Null":e.constructor.name:Z[t[0]]+t.slice(1)},ne=e=>e.length,se=e=>X(e)||(e=>e===H)(e),oe=M(((e,t)=>e===t)),re=M(((e,t)=>{const n=te(e);if(oe(n,te(t))&&(oe(n,"Object")||oe(n,"Array"))){if(X(e)||X(t))return oe(e,t);if(oe(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(oe(n,t)&&s in e||oe(n,e)&&s in t&&re(e[s],t[s])))return!1;return!0}return oe(e,t)})),ie=M(((e,t)=>(t.push(e),t))),le=G(((e,t,n)=>n.reduce(e,t))),ce=L(((e,t,n,s)=>e(s)?t(s):n(s))),ue=(...e)=>(...t)=>{let n,s=!0;for(let o=ne(e)-1;o>-1;o--)s?(s=!1,n=e[o](...t)):n=n===U?e[o]():e[o](n);return n},ae=M(((e,t)=>t[e])),he=G(((e,t,n)=>n.slice(e,Y(t)?t:K))),fe=ae(0);he(1,K);const de=M(((e,t)=>t.find(e))),pe=e=>()=>e,ge=pe(!0),me=pe(!1),ye=M(((e,t)=>le(((t,n)=>de((t=>e(n,t)),t)?t:ie(n,t)),[],t)));ye(re);const we=e=>{let t,n=!1;return(...s)=>n?t:(n=!0,t=e(...s))},be=G(((e,t,n)=>ne(t)?se(n)?e:ue((s=>s in n?be(e,he(1,K,t),n[s]):e),fe)(t):n));be(H),ue(ce(re(ee),me,ge),be(ee));const _e=new $;class ke{open=!1;ws=null;forcibly_closed=!1;reconnect_timeout=null;queue={};messages=[];onReadyQueue=[];onCloseQueue=[];handlers={open:[],close:[],message:[],error:[],timeout:[]};config={};init_flush(){this.queue={},this.messages=[]}call(e,...t){for(const n of this.handlers[e])n(...t)}log(e,t=null,n=null){const s=this.config;null!==n?s.log(e,n,t):s.timer?s.log(e,null,t):s.log(e,t)}initSocket(e){const t=this.config;this.open=!0,this.onReadyQueue.forEach((e=>e())),this.onReadyQueue.splice(0);const{id_key:n,data_key:s}=t.server;if(this.call("open",e),this.messages.forEach((e=>e.send())),null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),t.ping){const e=setInterval((()=>{this.open&&this.send(t.ping.content),this.forcibly_closed&&clearInterval(e)}),1e3*t.ping.interval)}I(e,"close",(async(...e)=>{this.log("close"),this.open=!1,this.onCloseQueue.forEach((e=>e())),this.onCloseQueue.splice(0),this.call("close",...e);const n=t.reconnect;if("number"!=typeof n||isNaN(n)||this.forcibly_closed)this.ws=null,this.open=!1;else{const e=async()=>{this.log("reconnect"),null!==this.ws&&(this.ws.close(),this.ws=null);null!==await this.connect()&&(this.reconnect_timeout=setTimeout(e,1e3*n))};e()}this.forcibly_closed=!1})),I(e,"message",(e=>{try{const o=t.decode(e.data);if(this.call("message",{...e,data:o}),o[n]){const e=this.queue[o[n]];if(e){const t=e.sent_time?Date.now()-e.sent_time:null;this.log("message",o[s],t),e.ff(o[s]),clearTimeout(e.timeout),delete this.queue[o[n]]}}}catch(t){console.error(t,`WSP: Decode error. Got: ${e.data}`)}}))}connect(){return new Promise((e=>{if(!0===this.open)return e(null);const t=this.config,n=t.socket||t.adapter(t.url,t.protocols);if(this.ws=n,!n||n.readyState>1)return this.ws=null,this.log("error","ready() on closing or closed state! status 2."),e(2);const s=we(e);I(n,"error",we((e=>{this.log("error","status 3."),this.call("error",e),this.ws=null,s(3)}))),n.readyState?(this.initSocket(n),s(null)):I(n,"open",we((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((e=>{this.open?e():this.onReadyQueue.push(e)}))}on(e,t,n=ge,s=!1){const o=e=>n(e)&&t(e);return s?I(this.ws,e,o):this.handlers[e].push(o)}async close(){return new Promise(((e,t)=>{null===this.ws?t("WSP: closing a non-inited socket!"):(this.open=!1,this.onCloseQueue.push((()=>{this.init_flush(),this.ws=null,this.forcibly_closed=!0,e(null)})),this.ws.close())}))}async send(e,t={}){this.log("send",e);const n=this.config,s={},o=n.server.data_key,r=n.lazy&&!this.open,i=_e.zip(2147483637*Math.random()|0);if("object"==typeof t.top){if(t.top[o])throw new Error("Attempting to set data key/token via send() options!");Object.assign(s,t.top)}if(n.pipes.forEach((t=>e=t(e))),!0===this.open)this.ws.send(n.encode(i,e,n));else if(!1===this.open||r)this.messages.push({send:()=>this.ws.send(n.encode(i,e,n))}),r&&this.connect();else if(null===this.open)throw new Error("Attempting to send via closed WebSocket connection!");return new Promise(((t,s)=>{this.queue[i]={ff:t,data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:T(n.timeout,(()=>{this.queue[i]&&(this.call("timeout",e),s({"Websocket timeout expired: ":n.timeout,"for the message ":e}),delete this.queue[i])}))}}))}constructor(e={}){this.config=(e=>{if(null===R&&!("adapter"in e))throw new Error("\n This platform has no native WebSocket implementation.\n Please use 'ws' package as an adapter.\n See https://github.com/houd1ni/WebsocketPromisify/issues/23\n ");const t=Object.assign({},D,e),n=t.url;if("/"==n[0])try{const e=location.protocol.includes("s:")?"wss":"ws";t.url=`${e}://${location.hostname}:${location.port}${n}`}catch(e){throw new Error("WSP: URL starting with / in non-browser environment!")}return t})(e),this.init_flush(),this.config.lazy||this.connect()}}export{ke as default};
|
package/package.json
CHANGED
|
@@ -1,94 +1,79 @@
|
|
|
1
|
-
{
|
|
2
|
-
"author": {
|
|
3
|
-
"name": "Michael Akiliev"
|
|
4
|
-
},
|
|
5
|
-
"bugs": {
|
|
6
|
-
"url": "https://github.com/houd1ni/WebsocketPromisify/issues"
|
|
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
|
-
"test": "npm
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"prod
|
|
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
|
-
"nyc": "^15.1.0",
|
|
81
|
-
"randomatic": "^3.1.1",
|
|
82
|
-
"rollup": "^3.10.0",
|
|
83
|
-
"rollup-plugin-typescript2": "^0.34.1",
|
|
84
|
-
"ts-node": "^10.9.1",
|
|
85
|
-
"tslint": "^6.1.3",
|
|
86
|
-
"typescript": "^4.9.4",
|
|
87
|
-
"ws": "^8.12.0"
|
|
88
|
-
},
|
|
89
|
-
"types": "./dist/bundle.d.ts",
|
|
90
|
-
"dependencies": {
|
|
91
|
-
"pepka": "^1.0.0-beta.1",
|
|
92
|
-
"zipnum": "^1.0.0"
|
|
93
|
-
}
|
|
94
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"author": {
|
|
3
|
+
"name": "Michael Akiliev"
|
|
4
|
+
},
|
|
5
|
+
"bugs": {
|
|
6
|
+
"url": "https://github.com/houd1ni/WebsocketPromisify/issues"
|
|
7
|
+
},
|
|
8
|
+
"deprecated": false,
|
|
9
|
+
"description": "Wraps your WebSockets into Promise-based class with full d.ts typings on client & server",
|
|
10
|
+
"homepage": "https://github.com/houd1ni/WebsocketPromisify#readme",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"WebSockets",
|
|
13
|
+
"WS",
|
|
14
|
+
"Promise",
|
|
15
|
+
"Socket",
|
|
16
|
+
"REST",
|
|
17
|
+
"Ajax",
|
|
18
|
+
"Easy",
|
|
19
|
+
"realtime",
|
|
20
|
+
"Middleware",
|
|
21
|
+
"JSON",
|
|
22
|
+
"Data",
|
|
23
|
+
"transport",
|
|
24
|
+
"API",
|
|
25
|
+
"async"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"name": "wspromisify",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/houd1ni/WebsocketPromisify.git"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
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": "2.6.0",
|
|
45
|
+
"type": "module",
|
|
46
|
+
"exports": {
|
|
47
|
+
".": {
|
|
48
|
+
"types": "./dist/bundle.d.ts",
|
|
49
|
+
"import": "./dist/bundle.mjs",
|
|
50
|
+
"require": "./dist/bundle.cjs"
|
|
51
|
+
},
|
|
52
|
+
"./src": "./src/*"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@rollup/plugin-commonjs": "^28.0.3",
|
|
56
|
+
"@rollup/plugin-node-resolve": "^16.0.0",
|
|
57
|
+
"@rollup/plugin-replace": "^6.0.2",
|
|
58
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
59
|
+
"@types/express": "^5.0.0",
|
|
60
|
+
"@types/node": "^22.13.9",
|
|
61
|
+
"@types/ws": "^8.18.0",
|
|
62
|
+
"codecov": "^3.8.3",
|
|
63
|
+
"cross-env": "^7.0.3",
|
|
64
|
+
"dts-bundle-generator": "^9.5.1",
|
|
65
|
+
"nyc": "^17.1.0",
|
|
66
|
+
"pepka": "^1.6.2",
|
|
67
|
+
"rollup": "^4.34.9",
|
|
68
|
+
"rollup-plugin-typescript2": "^0.36.0",
|
|
69
|
+
"ts-node": "^10.9.2",
|
|
70
|
+
"tsx": "^4.19.3",
|
|
71
|
+
"typescript": "^5.8.2",
|
|
72
|
+
"uvu": "^0.5.6",
|
|
73
|
+
"ws": "^8.18.1"
|
|
74
|
+
},
|
|
75
|
+
"types": "./dist/bundle.d.ts",
|
|
76
|
+
"dependencies": {
|
|
77
|
+
"zipnum": "^2.0.0"
|
|
78
|
+
}
|
|
79
|
+
}
|
package/rollup.config.js
CHANGED
package/src/WSC.ts
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import './types'
|
|
2
|
+
import { Zipnum } from 'zipnum'
|
|
3
|
+
import { add_event, sett } from './utils'
|
|
4
|
+
import { processConfig } from './config'
|
|
5
|
+
import { AnyFunc, once, T } from 'pepka'
|
|
6
|
+
|
|
7
|
+
const MAX_32 = 2**31 - 1
|
|
8
|
+
const zipnum = new Zipnum()
|
|
9
|
+
|
|
10
|
+
type EventHandler<T extends keyof WebSocketEventMap> = AnyFunc<any, [WebSocketEventMap[T]]>
|
|
11
|
+
type EventHandlers = {
|
|
12
|
+
open: EventHandler<'open'>[]
|
|
13
|
+
close: EventHandler<'close'>[]
|
|
14
|
+
error: EventHandler<'error'>[]
|
|
15
|
+
message: AnyFunc<any, [WebSocketEventMap['message'] & {data: any}]>[]
|
|
16
|
+
timeout: AnyFunc<any, [data: any]>[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class WebSocketClient {
|
|
20
|
+
private open = false
|
|
21
|
+
private ws: wsc.Socket|null = null
|
|
22
|
+
private forcibly_closed = false
|
|
23
|
+
private reconnect_timeout: NodeJS.Timeout|null = null
|
|
24
|
+
private queue = {}
|
|
25
|
+
private messages: any[] = []
|
|
26
|
+
private onReadyQueue: AnyFunc[] = []
|
|
27
|
+
private onCloseQueue: AnyFunc[] = []
|
|
28
|
+
private handlers: EventHandlers = { open: [], close: [], message: [], error: [], timeout: [] }
|
|
29
|
+
private config = <wsc.Config>{}
|
|
30
|
+
|
|
31
|
+
private init_flush(): void {
|
|
32
|
+
this.queue = {} // data queuse
|
|
33
|
+
this.messages = [] // send() queue
|
|
34
|
+
}
|
|
35
|
+
private call(event_name: wsc.WSEvent, ...args: any[]) {
|
|
36
|
+
// this.handlers.open[0]()
|
|
37
|
+
for(const h of this.handlers[event_name]) h(...args)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private log(event: string, message: any = null, time: number|null = null): void {
|
|
41
|
+
const config = this.config
|
|
42
|
+
if(time !== null) {
|
|
43
|
+
config.log(event, time, message)
|
|
44
|
+
} else {
|
|
45
|
+
if(config.timer) {
|
|
46
|
+
config.log(event, null, message)
|
|
47
|
+
} else {
|
|
48
|
+
config.log(event, message)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private initSocket(ws: wsc.Socket) {
|
|
54
|
+
const config = this.config
|
|
55
|
+
this.open = true
|
|
56
|
+
this.onReadyQueue.forEach((fn: Function) => fn())
|
|
57
|
+
this.onReadyQueue.splice(0)
|
|
58
|
+
const {id_key, data_key} = config.server
|
|
59
|
+
// Works also on previously opened sockets that do not fire 'open' event.
|
|
60
|
+
this.call('open', ws)
|
|
61
|
+
// Send all pending messages.
|
|
62
|
+
this.messages.forEach((message: any) => message.send())
|
|
63
|
+
// It's reconnecting.
|
|
64
|
+
if(this.reconnect_timeout !== null) {
|
|
65
|
+
clearInterval(this.reconnect_timeout)
|
|
66
|
+
this.reconnect_timeout = null
|
|
67
|
+
}
|
|
68
|
+
if(config.ping) {
|
|
69
|
+
const ping_interval = setInterval(() => {
|
|
70
|
+
if(this.open) this.send(config.ping.content)
|
|
71
|
+
if(this.forcibly_closed) clearInterval(ping_interval)
|
|
72
|
+
}, config.ping.interval*1e3)
|
|
73
|
+
}
|
|
74
|
+
add_event(ws, 'close', async (...e) => {
|
|
75
|
+
this.log('close')
|
|
76
|
+
this.open = false
|
|
77
|
+
this.onCloseQueue.forEach((fn: Function) => fn())
|
|
78
|
+
this.onCloseQueue.splice(0)
|
|
79
|
+
this.call('close', ...e)
|
|
80
|
+
// Auto reconnect.
|
|
81
|
+
const reconnect = config.reconnect
|
|
82
|
+
if(
|
|
83
|
+
typeof reconnect === 'number' &&
|
|
84
|
+
!isNaN(reconnect) &&
|
|
85
|
+
!this.forcibly_closed
|
|
86
|
+
) {
|
|
87
|
+
const reconnectFunc = async () => {
|
|
88
|
+
this.log('reconnect')
|
|
89
|
+
if(this.ws !== null) {
|
|
90
|
+
this.ws.close()
|
|
91
|
+
this.ws = null
|
|
92
|
+
}
|
|
93
|
+
// If some error occured, try again.
|
|
94
|
+
const status = await this.connect()
|
|
95
|
+
if(status !== null)
|
|
96
|
+
this.reconnect_timeout = setTimeout(reconnectFunc, reconnect * 1000)
|
|
97
|
+
}
|
|
98
|
+
// TODO: test normal close by server. Would it be infinite ?
|
|
99
|
+
reconnectFunc()
|
|
100
|
+
} else {
|
|
101
|
+
this.ws = null
|
|
102
|
+
this.open = false
|
|
103
|
+
}
|
|
104
|
+
// reset the flag to reuse.
|
|
105
|
+
this.forcibly_closed = false
|
|
106
|
+
})
|
|
107
|
+
add_event(ws, 'message', (e) => {
|
|
108
|
+
try {
|
|
109
|
+
const data = config.decode(e.data)
|
|
110
|
+
this.call('message', {...e, data})
|
|
111
|
+
if(data[id_key]) {
|
|
112
|
+
const q = this.queue[data[id_key]]
|
|
113
|
+
if(q) {
|
|
114
|
+
// Debug, Log.
|
|
115
|
+
const time = q.sent_time ? (Date.now() - q.sent_time) : null
|
|
116
|
+
this.log('message', data[data_key], time)
|
|
117
|
+
// Play.
|
|
118
|
+
q.ff(data[data_key])
|
|
119
|
+
clearTimeout(q.timeout)
|
|
120
|
+
delete this.queue[data[id_key]]
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
console.error(err, `WSP: Decode error. Got: ${e.data}`)
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private connect() { // returns status if won't open or null if ok.
|
|
130
|
+
return new Promise((ff) => {
|
|
131
|
+
if(this.open === true) {
|
|
132
|
+
return ff(null)
|
|
133
|
+
}
|
|
134
|
+
const config = this.config
|
|
135
|
+
const ws = config.socket || config.adapter(config.url, config.protocols)
|
|
136
|
+
this.ws = ws
|
|
137
|
+
if(!ws || ws.readyState > 1) {
|
|
138
|
+
this.ws = null
|
|
139
|
+
this.log('error', 'ready() on closing or closed state! status 2.')
|
|
140
|
+
return ff(2)
|
|
141
|
+
}
|
|
142
|
+
const ffo = once(ff)
|
|
143
|
+
add_event(ws, 'error', once((e) => {
|
|
144
|
+
this.log('error', 'status 3.')
|
|
145
|
+
this.call('error', e)
|
|
146
|
+
this.ws = null
|
|
147
|
+
// Some network error: Connection refused or so.
|
|
148
|
+
ffo(3)
|
|
149
|
+
}))
|
|
150
|
+
// Because 'open' won't be envoked on opened socket.
|
|
151
|
+
if(ws.readyState) {
|
|
152
|
+
this.initSocket(ws)
|
|
153
|
+
ffo(null)
|
|
154
|
+
} else {
|
|
155
|
+
add_event(ws, 'open', once(() => {
|
|
156
|
+
this.log('open')
|
|
157
|
+
this.initSocket(ws)
|
|
158
|
+
ffo(null)
|
|
159
|
+
}))
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
public get socket() { return this.ws }
|
|
164
|
+
public async ready() {
|
|
165
|
+
return new Promise<void>((ff) => {
|
|
166
|
+
if(this.open) {
|
|
167
|
+
ff()
|
|
168
|
+
} else {
|
|
169
|
+
this.onReadyQueue.push(ff)
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
public on(
|
|
174
|
+
event_name: wsc.WSEvent,
|
|
175
|
+
handler: (data: any) => any,
|
|
176
|
+
predicate: (data: any) => boolean = T,
|
|
177
|
+
raw = false
|
|
178
|
+
) {
|
|
179
|
+
const _handler: wsc.EventHandler = (event) =>
|
|
180
|
+
predicate(event) && handler(event)
|
|
181
|
+
return raw
|
|
182
|
+
? add_event(this.ws as wsc.Socket, event_name, _handler)
|
|
183
|
+
: this.handlers[event_name].push(_handler)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
public async close(): wsc.AsyncErrCode {
|
|
187
|
+
return new Promise((ff, rj) => {
|
|
188
|
+
if(this.ws === null) {
|
|
189
|
+
rj('WSP: closing a non-inited socket!')
|
|
190
|
+
} else {
|
|
191
|
+
this.open = false
|
|
192
|
+
this.onCloseQueue.push(() => {
|
|
193
|
+
this.init_flush()
|
|
194
|
+
this.ws = null
|
|
195
|
+
this.forcibly_closed = true
|
|
196
|
+
ff(null)
|
|
197
|
+
})
|
|
198
|
+
this.ws.close()
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** .send(your_data) wraps request to server with {id: `hash`, data: `actually your data`},
|
|
204
|
+
returns a Promise that will be rejected after a timeout or
|
|
205
|
+
resolved if server returns the same signature: {id: `same_hash`, data: `response data`}.
|
|
206
|
+
*/
|
|
207
|
+
public async send<RequestDataType = any, ResponseDataType = any>(
|
|
208
|
+
message_data: RequestDataType,
|
|
209
|
+
opts = <wsc.SendOptions>{}
|
|
210
|
+
): Promise<ResponseDataType> {
|
|
211
|
+
this.log('send', message_data)
|
|
212
|
+
const config = this.config
|
|
213
|
+
const message = {}
|
|
214
|
+
const data_key = config.server.data_key
|
|
215
|
+
const first_time_lazy = config.lazy && !this.open
|
|
216
|
+
|
|
217
|
+
const message_id = zipnum.zip((Math.random()*(MAX_32-10))|0)
|
|
218
|
+
if(typeof opts.top === 'object') {
|
|
219
|
+
if(opts.top[data_key]) {
|
|
220
|
+
throw new Error('Attempting to set data key/token via send() options!')
|
|
221
|
+
}
|
|
222
|
+
Object.assign(message, opts.top)
|
|
223
|
+
}
|
|
224
|
+
config.pipes.forEach((pipe) => message_data = pipe(message_data))
|
|
225
|
+
|
|
226
|
+
if(this.open === true) {
|
|
227
|
+
(this.ws as wsc.Socket).send(config.encode(message_id, message_data, config))
|
|
228
|
+
} else if(this.open === false || first_time_lazy) {
|
|
229
|
+
this.messages.push({
|
|
230
|
+
send: () => (this.ws as wsc.Socket).send(config.encode(message_id, message_data, config))
|
|
231
|
+
})
|
|
232
|
+
if(first_time_lazy) this.connect()
|
|
233
|
+
} else if(this.open === null) {
|
|
234
|
+
throw new Error('Attempting to send via closed WebSocket connection!')
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return new Promise((ff, rj) => {
|
|
238
|
+
// TODO: Make it class Message.
|
|
239
|
+
this.queue[message_id] = {
|
|
240
|
+
ff,
|
|
241
|
+
data_type: config.data_type,
|
|
242
|
+
sent_time: config.timer ? Date.now() : null,
|
|
243
|
+
timeout: sett(config.timeout, () => {
|
|
244
|
+
if(this.queue[message_id]) {
|
|
245
|
+
this.call('timeout', message_data)
|
|
246
|
+
rj({
|
|
247
|
+
'Websocket timeout expired: ': config.timeout,
|
|
248
|
+
'for the message ': message_data
|
|
249
|
+
})
|
|
250
|
+
delete this.queue[message_id]
|
|
251
|
+
}
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// TODO: Add .on handlers to config!
|
|
258
|
+
constructor(user_config: wsc.UserConfig = {}) {
|
|
259
|
+
this.config = processConfig(user_config)
|
|
260
|
+
this.init_flush()
|
|
261
|
+
if(!this.config.lazy) this.connect()
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* TODO: v3: @.deprecated. Use named import { WebSocketClient } instead. */
|
|
266
|
+
export default WebSocketClient
|