wspromisify 2.7.0 → 2.8.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 +4 -0
- package/dist/bundle.cjs +1 -1
- package/dist/bundle.d.ts +5 -2
- package/dist/bundle.mjs +1 -1
- package/package.json +1 -1
- package/src/WSC.ts +36 -32
- package/src/config.ts +1 -0
- package/src/types.ts +1 -0
package/README.md
CHANGED
|
@@ -61,6 +61,10 @@ Default constructor config is
|
|
|
61
61
|
timeout: 1400,
|
|
62
62
|
// Reconnect timeout in seconds or null.
|
|
63
63
|
reconnect: 2,
|
|
64
|
+
// Attempts before silently givin' up. Defaults to Infinity.
|
|
65
|
+
reconnection_attempts: 25
|
|
66
|
+
// Time in seconds after the connection is closed if nothing was sent explicitly by send(). Defaults to Infinity.
|
|
67
|
+
max_idle_time: 25
|
|
64
68
|
// Lazy connect: connects only if something sent (then sends all of them!)
|
|
65
69
|
lazy: false,
|
|
66
70
|
// Existing socket if you already have one to augment with this force.
|
package/dist/bundle.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const
|
|
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))),_=o(((e,t,n,s)=>e(s)?t(s):n(s))),b=(...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},S=r(((e,t)=>t[e])),k=i(((e,t,n)=>n.slice(e,(e=>"number"==u(e))(t)?t:c))),P=S(0);k(1,c);const v=r(((e,t)=>t.find(e))),E=e=>()=>e,N=r(((e,t)=>t.split(e))),j=E(!0),A=E(!1),W=r(((e,t)=>w(((t,n)=>v((t=>e(n,t)),t)?t:y(n,t)),[],t)))(m),q=i(((e,t,n)=>p(t)?(e=>a(e)||(e=>e===l)(e))(n)?e:b((s=>s in n?q(e,k(1,c,t),n[s]):e),P)(t):n));q(l),b(_(m(f),A,j),q(f));const C=r(((e,t)=>t.map(e))),{floor:O}=Math,Q="0123456789abcdefghijklmnopqrstuvwxyz",$=b((e=>Object.fromEntries(e)),C(((e,t)=>[e,t])),N(""));class z{abc;abclen;c2pos;standard;setABC(e){if(!b(m(p(t=e)),p,W,N(""))(t))throw new Error("Not all chars are unique!");var t;this.abc=e,this.abclen=e.length,this.standard=Q.startsWith(e),this.c2pos=$(e)}zip(e){const{abc:t,abclen:n}=this;let s="";for(;e>0;)s=t[e%n]+s,e=O(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||Q+"ABCDEFGHIJKLMNOPQRSTUVWXYZ")}}const R=new z;R.setABC.bind(R),R.zip.bind(R),R.unzip.bind(R);const T=(()=>{try{return WebSocket||null}catch{return null}})(),I=(e,t,n)=>e.addEventListener(t,n),x=(e,t)=>setTimeout(t,e),B={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,reconnection_attempts:1/0,max_idle_time:1/0,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:{}}},D=Symbol("Placeholder"),L=e=>{let t=0;for(const n of e)n!==D&&t++;return t},U=(e,t)=>{const n=e.length,s=e.slice(),o=t.length;let r=o,i=0;for(;r&&i<n;i++)s[i]===D&&(s[i]=t[o-r],r--);for(i=n;r;i++,r--)s[i]=t[o-r];return s},F=(e,t,n)=>{const s=e.length-t.length-L(n);if(s<1)return e(...U(t,n));{const o=(...s)=>F(e,U(t,n),s);return o.$args_left=s,o}},J=e=>(...t)=>e.length>L(t)?F(e,[],t):e(...t);function M(e){return function(t,n){const s=t===D,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return o>1?s?(e=>function(t){return t===D?e:e(t)})((t=>e(t,n))):e(t,n):n=>e(t,n)}}function G(e){return J(e)}const H=/^(.*?)(8|16|32|64)(Clamped)?Array$/,K=void 0,V=1/0,X=e=>typeof e,Y=e=>null===e,Z=e=>"number"==X(e);const ee=e=>Y(e)||(e=>e===K)(e),te={u:"U",b:"B",n:"N",s:"S",f:"F"},ne=Symbol(),se=e=>{const t=X(e);return"object"===t?Y(e)?"Null":e.constructor.name:te[t[0]]+t.slice(1)},oe=M(((e,t)=>se(t)===e)),re=e=>e.length,ie=M(((e,t)=>e===t)),le=M(((e,t)=>{const n=se(e);if(ie(n,se(t))&&(ie(n,"Object")||ie(n,"Array")||(e=>H.test(e))(n))){if(Y(e)||Y(t))return ie(e,t);if(ie(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(ie(n,t)&&s in e||ie(n,e)&&s in t&&le(e[s],t[s])))return!1;return!0}return ie(e,t)})),ce=M(((e,t)=>(t.push(e),t))),ue=G(((e,t,n)=>n.reduce(e,t))),ae=M(((e,t)=>{const n=(e=>Array.isArray(e))(t);let s,o;n&&(s=0,o=[]);for(let s in t)e(t[s],s)||(n?o.push(+s):delete t[s]);if(n)for(const e of o)t.splice(e-s++,1);return t})),he=J(((e,t,n,s)=>e(s)?t(s):n(s))),fe=(...e)=>(...t)=>{let n,s=!0;for(let o=re(e)-1;o>-1;o--)s?(s=!1,n=e[o](...t)):n=n===D?e[o]():e[o](n);return n},de=M(((e,t)=>t[e])),pe=G(((e,t,n)=>n.slice(e,Z(t)?t:V))),ge=de(0);pe(1,V);const me=M(((e,t)=>t.find(e))),ye=e=>()=>e,we=ye(!0),_e=ye(!1),be=M(((e,t)=>t(...e))),Se=e=>(...t)=>{const n=e(...t),s=function(e){return"function"===X(e)}(n);return!s||s&&n.$args_left<=0?(e=>!e)(n):Se(n)},ke=M(((e,t)=>ue(((t,n)=>me((t=>e(n,t)),t)?t:ce(n,t)),[],t)));ke(le);const Pe=e=>{let t,n=!1;return(...s)=>n?t:(n=!0,t=e(...s))},ve=G(((e,t,n)=>re(t)?ee(n)?e:fe((s=>s in n?ve(e,pe(1,V,t),n[s]):e),ge)(t):n));ve(K),fe(he(le(ne),_e,we),ve(ne));const Ee=G(((e,t,n)=>t(n)&&e(n))),Ne=Se,je=new z,Ae=be([]),We=Ee(oe("Number"),Ne(isNaN));module.exports=class{ws=null;intentionally_closed=!1;reconnect_timeout=null;queue={};onReadyQueue=[];onCloseQueue=[];handlers={open:[],close:[],message:[],error:[],timeout:[]};config={};ping_timer=null;idle_timer=null;get opened(){return 1===this.ws?.readyState}init_flush(){ae(_e,this.queue)}call(e,...t){for(const n of this.handlers[e])n(...t)}log(e,t=null,n=null){const{config:s}=this;null===n?s.timer?s.log(e,null,t):s.log(e,t):s.log(e,n,t)}resetPing(){const{config:{ping:e},ping_timer:t}=this;e&&(ee(t)||clearTimeout(t),this.ping_timer=x(1e3*e.interval,(async()=>{const{ping_timer:t,opened:n}=this;n?(await this.send(e.content),this.resetPing()):clearTimeout(t)})))}resetIdle(){const{config:{max_idle_time:e},idle_timer:t}=this;e!==1/0&&(ee(t)||clearTimeout(t),this.idle_timer=x(1e3*e,(()=>this.opened&&this.close())))}initSocket(e){const{queue:t,config:n}=this;this.ws=e,this.onReadyQueue.forEach((e=>e())),this.onReadyQueue.splice(0);const{id_key:s,data_key:o}=n.server;this.call("open",e);for(const n in t)e.send(t[n].msg);null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),this.resetPing(),this.resetIdle(),I(e,"close",(async(...e)=>{this.log("close"),this.ws=null,this.onCloseQueue.forEach(Ae),this.onCloseQueue.splice(0),this.call("close",...e);let{reconnect:t,reconnection_attempts:s}=n;if(We(t)){const e=async()=>{if(this.intentionally_closed||!s)return;s--,this.log("reconnect"),ee(this.ws)||(this.ws.close(),this.ws=null);const n=await this.connect();ee(n)||(this.reconnect_timeout=setTimeout(e,1e3*t))};e()}})),I(e,"message",(e=>{try{const t=n.decode(e.data);if(this.call("message",{...e,data:t}),t[s]){const e=this.queue[t[s]];if(e){const n=e.sent_time?Date.now()-e.sent_time:null;this.log("message",t[o],n),e.ff(t[o])}}}catch(t){console.error(t,`WSP: Decode error. Got: ${e.data}`)}this.resetPing(),this.resetIdle()}))}opening=!1;connect(){return new Promise((e=>{if(this.opened||this.opening)return e(null);this.opening=!0;const t=this.config,n=t.socket||t.adapter(t.url,t.protocols);if(!n||n.readyState>1)return this.opening=!1,this.ws=null,this.log("error","ready() on closing or closed state! status 2."),e(2);const s=Pe((t=>{this.opening=!1,e(t)}));I(n,"error",Pe((e=>{this.ws=null,this.log("error","status 3. Err: "+e.message),this.call("error",e),s(3)}))),n.readyState?(this.initSocket(n),s(null)):I(n,"open",Pe((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((e=>{this.opened?e():this.onReadyQueue.push(e)}))}on(e,t,n=we,s=!1){const o=e=>n(e)&&t(e);return s?I(this.ws,e,o):this.handlers[e].push(o),o}off(e,t,n=!1){if(n)return((e,t,n)=>e.removeEventListener(t,n))(this.ws,e,t);const s=this.handlers[e],o=s.indexOf(t);~o&&s.splice(o,1)}async close(){return new Promise(((e,t)=>{null===this.ws?t("WSP: closing a non-inited socket!"):(this.onCloseQueue.push((()=>{this.init_flush(),e(null)})),this.ws.close(),this.ws=null,this.intentionally_closed=!0)}))}open(){if(!this.opened)return this.intentionally_closed=!1,this.connect()}async send(e,t={}){this.log("send",e);const{config:n,queue:s}=this,o={},{pipes:r,server:{data_key:i}}=n,l=je.zip(2147483637*Math.random()|0);if("object"==typeof t.top){if(t.top[i])throw new Error("Attempting to set data key/token via send() options!");Object.assign(o,t.top)}for(const t of r)e=t(e);const[c,u]=await Promise.all([n.encode(l,e,n),this.connect()]);if(u)throw new Error("ERR while opening connection #"+u);return this.opened&&(this.ws.send(c),this.resetPing(),this.resetIdle()),new Promise(((t,o)=>{this.queue[l]={msg:c,ff(e){clearTimeout(this.timeout),delete s[l],t(e)},data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:x(n.timeout,(()=>{l in this.queue&&(this.call("timeout",e),o({"Websocket timeout expired":n.timeout,"for the message":e}),delete s[l])}))}}))}constructor(e={}){this.config=(e=>{if(null===T&&!("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({},B,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.config.lazy||this.connect()}};
|
package/dist/bundle.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ declare namespace wsc {
|
|
|
26
26
|
timeout: number;
|
|
27
27
|
reconnect: number;
|
|
28
28
|
reconnection_attempts: number;
|
|
29
|
+
max_idle_time: number;
|
|
29
30
|
lazy: boolean;
|
|
30
31
|
socket: Socket | null;
|
|
31
32
|
adapter: (host: string, protocols?: string[]) => Socket;
|
|
@@ -59,19 +60,21 @@ declare namespace wsc {
|
|
|
59
60
|
}
|
|
60
61
|
declare class WebSocketClient {
|
|
61
62
|
private ws;
|
|
62
|
-
private
|
|
63
|
+
private intentionally_closed;
|
|
63
64
|
private reconnect_timeout;
|
|
64
65
|
private queue;
|
|
65
66
|
private onReadyQueue;
|
|
66
67
|
private onCloseQueue;
|
|
67
68
|
private handlers;
|
|
68
69
|
private config;
|
|
69
|
-
private
|
|
70
|
+
private ping_timer;
|
|
71
|
+
private idle_timer;
|
|
70
72
|
private get opened();
|
|
71
73
|
private init_flush;
|
|
72
74
|
private call;
|
|
73
75
|
private log;
|
|
74
76
|
private resetPing;
|
|
77
|
+
private resetIdle;
|
|
75
78
|
private initSocket;
|
|
76
79
|
private opening;
|
|
77
80
|
private connect;
|
package/dist/bundle.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
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))),_=o(((e,t,n,s)=>e(s)?t(s):n(s))),b=(...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},S=r(((e,t)=>t[e])),k=i(((e,t,n)=>n.slice(e,(e=>"number"==u(e))(t)?t:c))),P=S(0);k(1,c);const v=r(((e,t)=>t.find(e))),E=e=>()=>e,N=r(((e,t)=>t.split(e))),j=E(!0),A=E(!1),W=r(((e,t)=>w(((t,n)=>v((t=>e(n,t)),t)?t:y(n,t)),[],t)))(m),q=i(((e,t,n)=>p(t)?(e=>a(e)||(e=>e===l)(e))(n)?e:b((s=>s in n?q(e,k(1,c,t),n[s]):e),P)(t):n));q(l),b(_(m(f),A,j),q(f));const C=r(((e,t)=>t.map(e))),{floor:O}=Math,Q="0123456789abcdefghijklmnopqrstuvwxyz",$=b((e=>Object.fromEntries(e)),C(((e,t)=>[e,t])),N(""));class z{abc;abclen;c2pos;standard;setABC(e){if(!b(m(p(t=e)),p,W,N(""))(t))throw new Error("Not all chars are unique!");var t;this.abc=e,this.abclen=e.length,this.standard=Q.startsWith(e),this.c2pos=$(e)}zip(e){const{abc:t,abclen:n}=this;let s="";for(;e>0;)s=t[e%n]+s,e=O(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||Q+"ABCDEFGHIJKLMNOPQRSTUVWXYZ")}}const R=new z;R.setABC.bind(R),R.zip.bind(R),R.unzip.bind(R);const T=(()=>{try{return WebSocket||null}catch{return null}})(),I=(e,t,n)=>e.addEventListener(t,n),x=(e,t)=>setTimeout(t,e),B={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,reconnection_attempts:1/0,max_idle_time:1/0,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:{}}},D=Symbol("Placeholder"),L=e=>{let t=0;for(const n of e)n!==D&&t++;return t},U=(e,t)=>{const n=e.length,s=e.slice(),o=t.length;let r=o,i=0;for(;r&&i<n;i++)s[i]===D&&(s[i]=t[o-r],r--);for(i=n;r;i++,r--)s[i]=t[o-r];return s},F=(e,t,n)=>{const s=e.length-t.length-L(n);if(s<1)return e(...U(t,n));{const o=(...s)=>F(e,U(t,n),s);return o.$args_left=s,o}},J=e=>(...t)=>e.length>L(t)?F(e,[],t):e(...t);function M(e){return function(t,n){const s=t===D,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return o>1?s?(e=>function(t){return t===D?e:e(t)})((t=>e(t,n))):e(t,n):n=>e(t,n)}}function G(e){return J(e)}const H=/^(.*?)(8|16|32|64)(Clamped)?Array$/,K=void 0,V=1/0,X=e=>typeof e,Y=e=>null===e,Z=e=>"number"==X(e);const ee=e=>Y(e)||(e=>e===K)(e),te={u:"U",b:"B",n:"N",s:"S",f:"F"},ne=Symbol(),se=e=>{const t=X(e);return"object"===t?Y(e)?"Null":e.constructor.name:te[t[0]]+t.slice(1)},oe=M(((e,t)=>se(t)===e)),re=e=>e.length,ie=M(((e,t)=>e===t)),le=M(((e,t)=>{const n=se(e);if(ie(n,se(t))&&(ie(n,"Object")||ie(n,"Array")||(e=>H.test(e))(n))){if(Y(e)||Y(t))return ie(e,t);if(ie(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(ie(n,t)&&s in e||ie(n,e)&&s in t&&le(e[s],t[s])))return!1;return!0}return ie(e,t)})),ce=M(((e,t)=>(t.push(e),t))),ue=G(((e,t,n)=>n.reduce(e,t))),ae=M(((e,t)=>{const n=(e=>Array.isArray(e))(t);let s,o;n&&(s=0,o=[]);for(let s in t)e(t[s],s)||(n?o.push(+s):delete t[s]);if(n)for(const e of o)t.splice(e-s++,1);return t})),he=J(((e,t,n,s)=>e(s)?t(s):n(s))),fe=(...e)=>(...t)=>{let n,s=!0;for(let o=re(e)-1;o>-1;o--)s?(s=!1,n=e[o](...t)):n=n===D?e[o]():e[o](n);return n},de=M(((e,t)=>t[e])),pe=G(((e,t,n)=>n.slice(e,Z(t)?t:V))),ge=de(0);pe(1,V);const me=M(((e,t)=>t.find(e))),ye=e=>()=>e,we=ye(!0),_e=ye(!1),be=M(((e,t)=>t(...e))),Se=e=>(...t)=>{const n=e(...t),s=function(e){return"function"===X(e)}(n);return!s||s&&n.$args_left<=0?(e=>!e)(n):Se(n)},ke=M(((e,t)=>ue(((t,n)=>me((t=>e(n,t)),t)?t:ce(n,t)),[],t)));ke(le);const Pe=e=>{let t,n=!1;return(...s)=>n?t:(n=!0,t=e(...s))},ve=G(((e,t,n)=>re(t)?ee(n)?e:fe((s=>s in n?ve(e,pe(1,V,t),n[s]):e),ge)(t):n));ve(K),fe(he(le(ne),_e,we),ve(ne));const Ee=G(((e,t,n)=>t(n)&&e(n))),Ne=Se,je=new z,Ae=be([]),We=Ee(oe("Number"),Ne(isNaN));class qe{ws=null;intentionally_closed=!1;reconnect_timeout=null;queue={};onReadyQueue=[];onCloseQueue=[];handlers={open:[],close:[],message:[],error:[],timeout:[]};config={};ping_timer=null;idle_timer=null;get opened(){return 1===this.ws?.readyState}init_flush(){ae(_e,this.queue)}call(e,...t){for(const n of this.handlers[e])n(...t)}log(e,t=null,n=null){const{config:s}=this;null===n?s.timer?s.log(e,null,t):s.log(e,t):s.log(e,n,t)}resetPing(){const{config:{ping:e},ping_timer:t}=this;e&&(ee(t)||clearTimeout(t),this.ping_timer=x(1e3*e.interval,(async()=>{const{ping_timer:t,opened:n}=this;n?(await this.send(e.content),this.resetPing()):clearTimeout(t)})))}resetIdle(){const{config:{max_idle_time:e},idle_timer:t}=this;e!==1/0&&(ee(t)||clearTimeout(t),this.idle_timer=x(1e3*e,(()=>this.opened&&this.close())))}initSocket(e){const{queue:t,config:n}=this;this.ws=e,this.onReadyQueue.forEach((e=>e())),this.onReadyQueue.splice(0);const{id_key:s,data_key:o}=n.server;this.call("open",e);for(const n in t)e.send(t[n].msg);null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),this.resetPing(),this.resetIdle(),I(e,"close",(async(...e)=>{this.log("close"),this.ws=null,this.onCloseQueue.forEach(Ae),this.onCloseQueue.splice(0),this.call("close",...e);let{reconnect:t,reconnection_attempts:s}=n;if(We(t)){const e=async()=>{if(this.intentionally_closed||!s)return;s--,this.log("reconnect"),ee(this.ws)||(this.ws.close(),this.ws=null);const n=await this.connect();ee(n)||(this.reconnect_timeout=setTimeout(e,1e3*t))};e()}})),I(e,"message",(e=>{try{const t=n.decode(e.data);if(this.call("message",{...e,data:t}),t[s]){const e=this.queue[t[s]];if(e){const n=e.sent_time?Date.now()-e.sent_time:null;this.log("message",t[o],n),e.ff(t[o])}}}catch(t){console.error(t,`WSP: Decode error. Got: ${e.data}`)}this.resetPing(),this.resetIdle()}))}opening=!1;connect(){return new Promise((e=>{if(this.opened||this.opening)return e(null);this.opening=!0;const t=this.config,n=t.socket||t.adapter(t.url,t.protocols);if(!n||n.readyState>1)return this.opening=!1,this.ws=null,this.log("error","ready() on closing or closed state! status 2."),e(2);const s=Pe((t=>{this.opening=!1,e(t)}));I(n,"error",Pe((e=>{this.ws=null,this.log("error","status 3. Err: "+e.message),this.call("error",e),s(3)}))),n.readyState?(this.initSocket(n),s(null)):I(n,"open",Pe((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((e=>{this.opened?e():this.onReadyQueue.push(e)}))}on(e,t,n=we,s=!1){const o=e=>n(e)&&t(e);return s?I(this.ws,e,o):this.handlers[e].push(o),o}off(e,t,n=!1){if(n)return((e,t,n)=>e.removeEventListener(t,n))(this.ws,e,t);const s=this.handlers[e],o=s.indexOf(t);~o&&s.splice(o,1)}async close(){return new Promise(((e,t)=>{null===this.ws?t("WSP: closing a non-inited socket!"):(this.onCloseQueue.push((()=>{this.init_flush(),e(null)})),this.ws.close(),this.ws=null,this.intentionally_closed=!0)}))}open(){if(!this.opened)return this.intentionally_closed=!1,this.connect()}async send(e,t={}){this.log("send",e);const{config:n,queue:s}=this,o={},{pipes:r,server:{data_key:i}}=n,l=je.zip(2147483637*Math.random()|0);if("object"==typeof t.top){if(t.top[i])throw new Error("Attempting to set data key/token via send() options!");Object.assign(o,t.top)}for(const t of r)e=t(e);const[c,u]=await Promise.all([n.encode(l,e,n),this.connect()]);if(u)throw new Error("ERR while opening connection #"+u);return this.opened&&(this.ws.send(c),this.resetPing(),this.resetIdle()),new Promise(((t,o)=>{this.queue[l]={msg:c,ff(e){clearTimeout(this.timeout),delete s[l],t(e)},data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:x(n.timeout,(()=>{l in this.queue&&(this.call("timeout",e),o({"Websocket timeout expired":n.timeout,"for the message":e}),delete s[l])}))}}))}constructor(e={}){this.config=(e=>{if(null===T&&!("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({},B,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.config.lazy||this.connect()}}export{qe as default};
|
package/package.json
CHANGED
package/src/WSC.ts
CHANGED
|
@@ -20,14 +20,15 @@ type EventHandlers = {
|
|
|
20
20
|
|
|
21
21
|
class WebSocketClient {
|
|
22
22
|
private ws: wsc.Socket|null = null
|
|
23
|
-
private
|
|
23
|
+
private intentionally_closed = false
|
|
24
24
|
private reconnect_timeout: NodeJS.Timeout|null = null
|
|
25
25
|
private queue: Record<string, wsc.Message> = {}
|
|
26
26
|
private onReadyQueue: AnyFunc[] = []
|
|
27
27
|
private onCloseQueue: AnyFunc[] = []
|
|
28
28
|
private handlers: EventHandlers = { open: [], close: [], message: [], error: [], timeout: [] }
|
|
29
29
|
private config = <wsc.Config>{}
|
|
30
|
-
private
|
|
30
|
+
private ping_timer: NodeJS.Timeout|null = null
|
|
31
|
+
private idle_timer: NodeJS.Timeout|null = null
|
|
31
32
|
private get opened() { return this.ws?.readyState===1 } // The only opened state.
|
|
32
33
|
|
|
33
34
|
private init_flush(): void {
|
|
@@ -39,30 +40,34 @@ class WebSocketClient {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
private log(event: string, message: any = null, time: number|null = null): void {
|
|
42
|
-
const config = this
|
|
43
|
-
if(time
|
|
43
|
+
const {config} = this
|
|
44
|
+
if(time === null)
|
|
45
|
+
if(config.timer) config.log(event, null, message)
|
|
46
|
+
else config.log(event, message)
|
|
47
|
+
else
|
|
44
48
|
config.log(event, time, message)
|
|
45
|
-
} else {
|
|
46
|
-
if(config.timer) {
|
|
47
|
-
config.log(event, null, message)
|
|
48
|
-
} else {
|
|
49
|
-
config.log(event, message)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
49
|
}
|
|
53
50
|
|
|
54
51
|
private resetPing() {
|
|
55
|
-
const {config} = this
|
|
56
|
-
if(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
this.
|
|
60
|
-
|
|
61
|
-
if(
|
|
62
|
-
await this.send(
|
|
52
|
+
const {config: {ping}, ping_timer} = this
|
|
53
|
+
if(ping) {
|
|
54
|
+
if(!isNil(ping_timer))
|
|
55
|
+
clearTimeout(ping_timer as NodeJS.Timeout)
|
|
56
|
+
this.ping_timer = sett(ping.interval*1e3, async () => {
|
|
57
|
+
const {ping_timer, opened} = this
|
|
58
|
+
if(opened) {
|
|
59
|
+
await this.send(ping.content)
|
|
63
60
|
this.resetPing()
|
|
64
|
-
}
|
|
65
|
-
}
|
|
61
|
+
} else clearTimeout(ping_timer!)
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private resetIdle() {
|
|
67
|
+
const {config: {max_idle_time: time}, idle_timer} = this
|
|
68
|
+
if(time!==Infinity) {
|
|
69
|
+
if(!isNil(idle_timer)) clearTimeout(idle_timer!)
|
|
70
|
+
this.idle_timer = sett(time*1e3, () => this.opened && this.close())
|
|
66
71
|
}
|
|
67
72
|
}
|
|
68
73
|
|
|
@@ -79,7 +84,7 @@ class WebSocketClient {
|
|
|
79
84
|
clearInterval(this.reconnect_timeout)
|
|
80
85
|
this.reconnect_timeout = null
|
|
81
86
|
}
|
|
82
|
-
this.resetPing()
|
|
87
|
+
this.resetPing(); this.resetIdle()
|
|
83
88
|
add_event(ws, 'close', async (...e) => {
|
|
84
89
|
this.log('close')
|
|
85
90
|
this.ws = null
|
|
@@ -90,7 +95,7 @@ class WebSocketClient {
|
|
|
90
95
|
let {reconnect, reconnection_attempts} = config
|
|
91
96
|
if(isNumber(reconnect)) {
|
|
92
97
|
const reconnectFunc = async () => {
|
|
93
|
-
if(this.
|
|
98
|
+
if(this.intentionally_closed || !reconnection_attempts) return;
|
|
94
99
|
reconnection_attempts--
|
|
95
100
|
this.log('reconnect')
|
|
96
101
|
if(!isNil(this.ws)) {
|
|
@@ -123,6 +128,8 @@ class WebSocketClient {
|
|
|
123
128
|
} catch (err) {
|
|
124
129
|
console.error(err, `WSP: Decode error. Got: ${e.data}`)
|
|
125
130
|
}
|
|
131
|
+
this.resetPing()
|
|
132
|
+
this.resetIdle()
|
|
126
133
|
})
|
|
127
134
|
}
|
|
128
135
|
|
|
@@ -201,14 +208,14 @@ class WebSocketClient {
|
|
|
201
208
|
})
|
|
202
209
|
this.ws.close()
|
|
203
210
|
this.ws = null
|
|
204
|
-
this.
|
|
211
|
+
this.intentionally_closed = true
|
|
205
212
|
}
|
|
206
213
|
})
|
|
207
214
|
}
|
|
208
215
|
|
|
209
216
|
public open() {
|
|
210
217
|
if(!this.opened) {
|
|
211
|
-
this.
|
|
218
|
+
this.intentionally_closed = false
|
|
212
219
|
return this.connect()
|
|
213
220
|
}
|
|
214
221
|
}
|
|
@@ -222,7 +229,7 @@ class WebSocketClient {
|
|
|
222
229
|
opts = <wsc.SendOptions>{}
|
|
223
230
|
): Promise<ResponseDataType> {
|
|
224
231
|
this.log('send', message_data)
|
|
225
|
-
const {config,
|
|
232
|
+
const {config, queue} = this
|
|
226
233
|
const message = {}
|
|
227
234
|
const {pipes, server: {data_key}} = config
|
|
228
235
|
|
|
@@ -234,9 +241,6 @@ class WebSocketClient {
|
|
|
234
241
|
Object.assign(message, opts.top)
|
|
235
242
|
}
|
|
236
243
|
for(const pipe of pipes) message_data = pipe(message_data)
|
|
237
|
-
|
|
238
|
-
if(forcibly_closed)
|
|
239
|
-
throw new Error('Attempting to send via closed WebSocket connection!')
|
|
240
244
|
const [msg, err] = await Promise.all([
|
|
241
245
|
config.encode(message_id, message_data, config),
|
|
242
246
|
this.connect()
|
|
@@ -245,20 +249,20 @@ class WebSocketClient {
|
|
|
245
249
|
if(this.opened) {
|
|
246
250
|
this.ws!.send(msg)
|
|
247
251
|
this.resetPing()
|
|
252
|
+
this.resetIdle()
|
|
248
253
|
}
|
|
249
254
|
|
|
250
255
|
return new Promise((ff, rj) => {
|
|
251
256
|
this.queue[message_id] = {
|
|
252
257
|
msg, ff(x: any) {
|
|
253
|
-
ff(x)
|
|
254
|
-
// cleanup.
|
|
255
258
|
clearTimeout(this.timeout) // from this object!
|
|
256
259
|
delete queue[message_id]
|
|
260
|
+
ff(x)
|
|
257
261
|
},
|
|
258
262
|
data_type: config.data_type,
|
|
259
263
|
sent_time: config.timer ? Date.now() : null,
|
|
260
264
|
timeout: sett(config.timeout, () => {
|
|
261
|
-
if(this.queue
|
|
265
|
+
if(message_id in this.queue) {
|
|
262
266
|
this.call('timeout', message_data)
|
|
263
267
|
rj({'Websocket timeout expired': config.timeout, 'for the message': message_data})
|
|
264
268
|
delete queue[message_id]
|
package/src/config.ts
CHANGED
|
@@ -11,6 +11,7 @@ const default_config = <wsc.Config>{
|
|
|
11
11
|
timeout: 1400,
|
|
12
12
|
reconnect: 2, // Reconnect timeout in seconds or null.
|
|
13
13
|
reconnection_attempts: Infinity,
|
|
14
|
+
max_idle_time: Infinity,
|
|
14
15
|
lazy: false,
|
|
15
16
|
socket: null,
|
|
16
17
|
adapter: ((host, protocols) => new WebSocket(host, protocols)),
|