wspromisify 2.7.1 → 2.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/bundle.cjs +1 -1
- package/dist/bundle.d.ts +8 -4
- package/dist/bundle.mjs +1 -1
- package/package.json +1 -1
- package/src/WSC.ts +39 -33
- package/src/config.ts +1 -0
- package/src/types.ts +4 -2
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ Default constructor config is
|
|
|
48
48
|
```javascript
|
|
49
49
|
{
|
|
50
50
|
// You can also use plain text and blobs in future.
|
|
51
|
-
|
|
51
|
+
data_type: 'json',
|
|
52
52
|
// Debug features. Not required.
|
|
53
53
|
log: ((event, time, message) => null),
|
|
54
54
|
// Will count milliseconds for responses and put them to log function above.
|
|
@@ -62,7 +62,9 @@ Default constructor config is
|
|
|
62
62
|
// Reconnect timeout in seconds or null.
|
|
63
63
|
reconnect: 2,
|
|
64
64
|
// Attempts before silently givin' up.
|
|
65
|
-
reconnection_attempts:
|
|
65
|
+
reconnection_attempts: Infinity,
|
|
66
|
+
// Time in seconds after the connection is closed if nothing was sent explicitly by send().
|
|
67
|
+
max_idle_time: Infinity,
|
|
66
68
|
// Lazy connect: connects only if something sent (then sends all of them!)
|
|
67
69
|
lazy: false,
|
|
68
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(),i=n.length;let r=i,l=0;for(;r&&l<s;l++)o[l]===e&&(o[l]=n[i-r],r--);for(l=s;r;l++,r--)o[l]=n[i-r];return o},s=(e,o,i)=>{const r=e.length-o.length-t(i);if(r<1)return e(...n(o,i));{const t=(...t)=>s(e,n(o,i),t);return t.$args_left=r,t}},o=e=>(...n)=>e.length>t(n)?s(e,[],n):e(...n);function i(t){return function(n,s){const o=n===e,i=arguments.length;if(1===i&&o)throw new Error("Senseless placeholder usage.");return i>1?o?(t=>function(n){return n===e?t:t(n)})((e=>t(e,s))):t(n,s):e=>t(n,e)}}function r(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)},g=e=>e.length,p=i(((e,t)=>e===t)),m=i(((e,t)=>{const n=d(e);if(p(n,d(t))&&(p(n,"Object")||p(n,"Array"))){if(a(e)||a(t))return p(e,t);if(p(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(p(n,t)&&s in e||p(n,e)&&s in t&&m(e[s],t[s])))return!1;return!0}return p(e,t)})),y=i(((e,t)=>(t.push(e),t))),w=r(((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 i=g(t)-1;i>-1;i--)o?(o=!1,s=t[i](...n)):s=s===e?t[i]():t[i](s);return s},S=i(((e,t)=>t[e])),k=r(((e,t,n)=>n.slice(e,(e=>"number"==u(e))(t)?t:c))),P=S(0);k(1,c);const v=i(((e,t)=>t.find(e))),E=e=>()=>e,N=i(((e,t)=>t.split(e))),j=E(!0),A=E(!1),W=i(((e,t)=>w(((t,n)=>v((t=>e(n,t)),t)?t:y(n,t)),[],t)))(m),q=r(((e,t,n)=>g(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=i(((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(g(t=e)),g,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 i=0;for(let t=0;t<o;t++)i+=s[e[t]]*n**(o-t-1);return i}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 i=o,r=0;for(;i&&r<n;r++)s[r]===D&&(s[r]=t[o-i],i--);for(r=n;i;r++,i--)s[r]=t[o-i];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)),ie=e=>e.length,re=M(((e,t)=>e===t)),le=M(((e,t)=>{const n=se(e);if(re(n,se(t))&&(re(n,"Object")||re(n,"Array")||(e=>H.test(e))(n))){if(Y(e)||Y(t))return re(e,t);if(re(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(re(n,t)&&s in e||re(n,e)&&s in t&&le(e[s],t[s])))return!1;return!0}return re(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=ie(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])),ge=G(((e,t,n)=>n.slice(e,Z(t)?t:V))),pe=de(0);ge(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)=>ie(t)?ee(n)?e:fe((s=>s in n?ve(e,ge(1,V,t),n[s]):e),pe)(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)),qe={_is_ping:!0};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,qe),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:i,server:{data_key:r}}=n,{top:l,_is_ping:c}=t,u=je.zip(2147483637*Math.random()|0);if("object"==typeof l){if(l[r])throw new Error("Attempting to set data key/token via send() options!");Object.assign(o,l)}for(const t of i)e=t(e);const[a,h]=await Promise.all([n.encode(u,e,n),this.connect()]);if(h)throw new Error("ERR while opening connection #"+h);return this.opened&&(this.ws.send(a),c||this.resetPing(),this.resetIdle()),new Promise(((t,o)=>{this.queue[u]={msg:a,ff(e){clearTimeout(this.timeout),delete s[u],t(e)},data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:x(n.timeout,(()=>{u in this.queue&&(this.call("timeout",e),o({"Websocket timeout expired":n.timeout,"for the message":e}),delete s[u])}))}}))}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;
|
|
@@ -45,10 +46,11 @@ declare namespace wsc {
|
|
|
45
46
|
};
|
|
46
47
|
}
|
|
47
48
|
type UserConfig = Partial<Config>;
|
|
48
|
-
|
|
49
|
+
type SendOptions = Partial<{
|
|
49
50
|
top: any;
|
|
50
51
|
data_type: DataType;
|
|
51
|
-
|
|
52
|
+
_is_ping: boolean;
|
|
53
|
+
}>;
|
|
52
54
|
interface Message {
|
|
53
55
|
msg: any;
|
|
54
56
|
ff(x: any): any;
|
|
@@ -59,19 +61,21 @@ declare namespace wsc {
|
|
|
59
61
|
}
|
|
60
62
|
declare class WebSocketClient {
|
|
61
63
|
private ws;
|
|
62
|
-
private
|
|
64
|
+
private intentionally_closed;
|
|
63
65
|
private reconnect_timeout;
|
|
64
66
|
private queue;
|
|
65
67
|
private onReadyQueue;
|
|
66
68
|
private onCloseQueue;
|
|
67
69
|
private handlers;
|
|
68
70
|
private config;
|
|
69
|
-
private
|
|
71
|
+
private ping_timer;
|
|
72
|
+
private idle_timer;
|
|
70
73
|
private get opened();
|
|
71
74
|
private init_flush;
|
|
72
75
|
private call;
|
|
73
76
|
private log;
|
|
74
77
|
private resetPing;
|
|
78
|
+
private resetIdle;
|
|
75
79
|
private initSocket;
|
|
76
80
|
private opening;
|
|
77
81
|
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(),i=n.length;let r=i,l=0;for(;r&&l<s;l++)o[l]===e&&(o[l]=n[i-r],r--);for(l=s;r;l++,r--)o[l]=n[i-r];return o},s=(e,o,i)=>{const r=e.length-o.length-t(i);if(r<1)return e(...n(o,i));{const t=(...t)=>s(e,n(o,i),t);return t.$args_left=r,t}},o=e=>(...n)=>e.length>t(n)?s(e,[],n):e(...n);function i(t){return function(n,s){const o=n===e,i=arguments.length;if(1===i&&o)throw new Error("Senseless placeholder usage.");return i>1?o?(t=>function(n){return n===e?t:t(n)})((e=>t(e,s))):t(n,s):e=>t(n,e)}}function r(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)},g=e=>e.length,p=i(((e,t)=>e===t)),m=i(((e,t)=>{const n=d(e);if(p(n,d(t))&&(p(n,"Object")||p(n,"Array"))){if(a(e)||a(t))return p(e,t);if(p(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(p(n,t)&&s in e||p(n,e)&&s in t&&m(e[s],t[s])))return!1;return!0}return p(e,t)})),y=i(((e,t)=>(t.push(e),t))),w=r(((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 i=g(t)-1;i>-1;i--)o?(o=!1,s=t[i](...n)):s=s===e?t[i]():t[i](s);return s},S=i(((e,t)=>t[e])),k=r(((e,t,n)=>n.slice(e,(e=>"number"==u(e))(t)?t:c))),P=S(0);k(1,c);const v=i(((e,t)=>t.find(e))),E=e=>()=>e,N=i(((e,t)=>t.split(e))),j=E(!0),A=E(!1),W=i(((e,t)=>w(((t,n)=>v((t=>e(n,t)),t)?t:y(n,t)),[],t)))(m),q=r(((e,t,n)=>g(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=i(((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(g(t=e)),g,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 i=0;for(let t=0;t<o;t++)i+=s[e[t]]*n**(o-t-1);return i}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 i=o,r=0;for(;i&&r<n;r++)s[r]===D&&(s[r]=t[o-i],i--);for(r=n;i;r++,i--)s[r]=t[o-i];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)),ie=e=>e.length,re=M(((e,t)=>e===t)),le=M(((e,t)=>{const n=se(e);if(re(n,se(t))&&(re(n,"Object")||re(n,"Array")||(e=>H.test(e))(n))){if(Y(e)||Y(t))return re(e,t);if(re(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(re(n,t)&&s in e||re(n,e)&&s in t&&le(e[s],t[s])))return!1;return!0}return re(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=ie(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])),ge=G(((e,t,n)=>n.slice(e,Z(t)?t:V))),pe=de(0);ge(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)=>ie(t)?ee(n)?e:fe((s=>s in n?ve(e,ge(1,V,t),n[s]):e),pe)(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)),qe={_is_ping:!0};class Ce{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,qe),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:i,server:{data_key:r}}=n,{top:l,_is_ping:c}=t,u=je.zip(2147483637*Math.random()|0);if("object"==typeof l){if(l[r])throw new Error("Attempting to set data key/token via send() options!");Object.assign(o,l)}for(const t of i)e=t(e);const[a,h]=await Promise.all([n.encode(u,e,n),this.connect()]);if(h)throw new Error("ERR while opening connection #"+h);return this.opened&&(this.ws.send(a),c||this.resetPing(),this.resetIdle()),new Promise(((t,o)=>{this.queue[u]={msg:a,ff(e){clearTimeout(this.timeout),delete s[u],t(e)},data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:x(n.timeout,(()=>{u in this.queue&&(this.call("timeout",e),o({"Websocket timeout expired":n.timeout,"for the message":e}),delete s[u])}))}}))}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{Ce as default};
|
package/package.json
CHANGED
package/src/WSC.ts
CHANGED
|
@@ -8,6 +8,7 @@ const MAX_32 = 2**31 - 1
|
|
|
8
8
|
const zipnum = new Zipnum()
|
|
9
9
|
const callit = callWith([])
|
|
10
10
|
const isNumber = both(typeIs('Number'), notf(isNaN))
|
|
11
|
+
const ping_send_opts: wsc.SendOptions = {_is_ping: true}
|
|
11
12
|
|
|
12
13
|
type EventHandler<T extends keyof WebSocketEventMap> = AnyFunc<any, [WebSocketEventMap[T]]>
|
|
13
14
|
type EventHandlers = {
|
|
@@ -20,14 +21,15 @@ type EventHandlers = {
|
|
|
20
21
|
|
|
21
22
|
class WebSocketClient {
|
|
22
23
|
private ws: wsc.Socket|null = null
|
|
23
|
-
private
|
|
24
|
+
private intentionally_closed = false
|
|
24
25
|
private reconnect_timeout: NodeJS.Timeout|null = null
|
|
25
26
|
private queue: Record<string, wsc.Message> = {}
|
|
26
27
|
private onReadyQueue: AnyFunc[] = []
|
|
27
28
|
private onCloseQueue: AnyFunc[] = []
|
|
28
29
|
private handlers: EventHandlers = { open: [], close: [], message: [], error: [], timeout: [] }
|
|
29
30
|
private config = <wsc.Config>{}
|
|
30
|
-
private
|
|
31
|
+
private ping_timer: NodeJS.Timeout|null = null
|
|
32
|
+
private idle_timer: NodeJS.Timeout|null = null
|
|
31
33
|
private get opened() { return this.ws?.readyState===1 } // The only opened state.
|
|
32
34
|
|
|
33
35
|
private init_flush(): void {
|
|
@@ -39,30 +41,34 @@ class WebSocketClient {
|
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
private log(event: string, message: any = null, time: number|null = null): void {
|
|
42
|
-
const config = this
|
|
43
|
-
if(time
|
|
44
|
+
const {config} = this
|
|
45
|
+
if(time === null)
|
|
46
|
+
if(config.timer) config.log(event, null, message)
|
|
47
|
+
else config.log(event, message)
|
|
48
|
+
else
|
|
44
49
|
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
50
|
}
|
|
53
51
|
|
|
54
52
|
private resetPing() {
|
|
55
|
-
const {config} = this
|
|
56
|
-
if(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
this.
|
|
60
|
-
|
|
61
|
-
if(
|
|
62
|
-
await this.send(
|
|
53
|
+
const {config: {ping}, ping_timer} = this
|
|
54
|
+
if(ping) {
|
|
55
|
+
if(!isNil(ping_timer))
|
|
56
|
+
clearTimeout(ping_timer as NodeJS.Timeout)
|
|
57
|
+
this.ping_timer = sett(ping.interval*1e3, async () => {
|
|
58
|
+
const {ping_timer, opened} = this
|
|
59
|
+
if(opened) {
|
|
60
|
+
await this.send(ping.content, ping_send_opts)
|
|
63
61
|
this.resetPing()
|
|
64
|
-
}
|
|
65
|
-
}
|
|
62
|
+
} else clearTimeout(ping_timer!)
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private resetIdle() {
|
|
68
|
+
const {config: {max_idle_time: time}, idle_timer} = this
|
|
69
|
+
if(time!==Infinity) {
|
|
70
|
+
if(!isNil(idle_timer)) clearTimeout(idle_timer!)
|
|
71
|
+
this.idle_timer = sett(time*1e3, () => this.opened && this.close())
|
|
66
72
|
}
|
|
67
73
|
}
|
|
68
74
|
|
|
@@ -79,7 +85,7 @@ class WebSocketClient {
|
|
|
79
85
|
clearInterval(this.reconnect_timeout)
|
|
80
86
|
this.reconnect_timeout = null
|
|
81
87
|
}
|
|
82
|
-
this.resetPing()
|
|
88
|
+
this.resetPing(); this.resetIdle()
|
|
83
89
|
add_event(ws, 'close', async (...e) => {
|
|
84
90
|
this.log('close')
|
|
85
91
|
this.ws = null
|
|
@@ -90,7 +96,7 @@ class WebSocketClient {
|
|
|
90
96
|
let {reconnect, reconnection_attempts} = config
|
|
91
97
|
if(isNumber(reconnect)) {
|
|
92
98
|
const reconnectFunc = async () => {
|
|
93
|
-
if(this.
|
|
99
|
+
if(this.intentionally_closed || !reconnection_attempts) return;
|
|
94
100
|
reconnection_attempts--
|
|
95
101
|
this.log('reconnect')
|
|
96
102
|
if(!isNil(this.ws)) {
|
|
@@ -124,6 +130,7 @@ class WebSocketClient {
|
|
|
124
130
|
console.error(err, `WSP: Decode error. Got: ${e.data}`)
|
|
125
131
|
}
|
|
126
132
|
this.resetPing()
|
|
133
|
+
this.resetIdle()
|
|
127
134
|
})
|
|
128
135
|
}
|
|
129
136
|
|
|
@@ -202,14 +209,14 @@ class WebSocketClient {
|
|
|
202
209
|
})
|
|
203
210
|
this.ws.close()
|
|
204
211
|
this.ws = null
|
|
205
|
-
this.
|
|
212
|
+
this.intentionally_closed = true
|
|
206
213
|
}
|
|
207
214
|
})
|
|
208
215
|
}
|
|
209
216
|
|
|
210
217
|
public open() {
|
|
211
218
|
if(!this.opened) {
|
|
212
|
-
this.
|
|
219
|
+
this.intentionally_closed = false
|
|
213
220
|
return this.connect()
|
|
214
221
|
}
|
|
215
222
|
}
|
|
@@ -223,21 +230,19 @@ class WebSocketClient {
|
|
|
223
230
|
opts = <wsc.SendOptions>{}
|
|
224
231
|
): Promise<ResponseDataType> {
|
|
225
232
|
this.log('send', message_data)
|
|
226
|
-
const {config,
|
|
233
|
+
const {config, queue} = this
|
|
227
234
|
const message = {}
|
|
228
235
|
const {pipes, server: {data_key}} = config
|
|
236
|
+
const {top, _is_ping} = opts
|
|
229
237
|
|
|
230
238
|
const message_id = zipnum.zip((Math.random()*(MAX_32-10))|0)
|
|
231
|
-
if(typeof
|
|
232
|
-
if(
|
|
239
|
+
if(typeof top === 'object') {
|
|
240
|
+
if(top[data_key]) {
|
|
233
241
|
throw new Error('Attempting to set data key/token via send() options!')
|
|
234
242
|
}
|
|
235
|
-
Object.assign(message,
|
|
243
|
+
Object.assign(message, top)
|
|
236
244
|
}
|
|
237
245
|
for(const pipe of pipes) message_data = pipe(message_data)
|
|
238
|
-
|
|
239
|
-
if(forcibly_closed)
|
|
240
|
-
throw new Error('Attempting to send via closed WebSocket connection!')
|
|
241
246
|
const [msg, err] = await Promise.all([
|
|
242
247
|
config.encode(message_id, message_data, config),
|
|
243
248
|
this.connect()
|
|
@@ -245,7 +250,8 @@ class WebSocketClient {
|
|
|
245
250
|
if(err) throw new Error('ERR while opening connection #'+err)
|
|
246
251
|
if(this.opened) {
|
|
247
252
|
this.ws!.send(msg)
|
|
248
|
-
this.resetPing()
|
|
253
|
+
if(!_is_ping) this.resetPing()
|
|
254
|
+
this.resetIdle()
|
|
249
255
|
}
|
|
250
256
|
|
|
251
257
|
return new Promise((ff, rj) => {
|
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)),
|
package/src/types.ts
CHANGED
|
@@ -32,6 +32,7 @@ declare namespace wsc {
|
|
|
32
32
|
timeout: number
|
|
33
33
|
reconnect: number
|
|
34
34
|
reconnection_attempts: number
|
|
35
|
+
max_idle_time: number
|
|
35
36
|
lazy: boolean
|
|
36
37
|
socket: Socket | null
|
|
37
38
|
adapter: (host: string, protocols?: string[]) => Socket
|
|
@@ -53,10 +54,11 @@ declare namespace wsc {
|
|
|
53
54
|
|
|
54
55
|
export type UserConfig = Partial<Config>
|
|
55
56
|
|
|
56
|
-
export
|
|
57
|
+
export type SendOptions = Partial<{
|
|
57
58
|
top: any
|
|
58
59
|
data_type: DataType
|
|
59
|
-
|
|
60
|
+
_is_ping: boolean
|
|
61
|
+
}>
|
|
60
62
|
|
|
61
63
|
export interface Message {
|
|
62
64
|
msg: any, ff(x: any): any,
|