wspromisify 2.6.5 → 2.7.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/dist/bundle.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";const t=Symbol("Placeholder"),e=e=>{let n=0;for(const s of e)s!==t&&n++;return n},n=(e,n)=>{const s=e.length,o=e.slice(),r=n.length;let i=r,l=0;for(;i&&l<s;l++)o[l]===t&&(o[l]=n[r-i],i--);for(l=s;i;l++,i--)o[l]=n[r-i];return o},s=(t,o,r)=>{const i=t.length-o.length-e(r);if(i<1)return t(...n(o,r));{const e=(...e)=>s(t,n(o,r),e);return e.$args_left=i,e}},o=t=>(...n)=>t.length>e(n)?s(t,[],n):t(...n);function r(e){return function(n,s){const o=n===t,r=arguments.length;if(1===r&&o)throw new Error("Senseless placeholder usage.");return r>1?o?(e=>function(n){return n===t?e:e(n)})((t=>e(t,s))):e(n,s):t=>e(n,t)}}function i(t){return o(t)}const l=void 0,c=1/0,u=t=>typeof t,a=t=>null===t,h={u:"U",b:"B",n:"N",s:"S",f:"F"},f=Symbol(),d=t=>{const e=u(t);return"object"===e?a(t)?"Null":t.constructor.name:h[e[0]]+e.slice(1)},p=t=>t.length,g=r(((t,e)=>t===e)),m=r(((t,e)=>{const n=d(t);if(g(n,d(e))&&(g(n,"Object")||g(n,"Array"))){if(a(t)||a(e))return g(t,e);if(g(t,e))return!0;for(const n of[t,e])for(const s in n)if(!(g(n,e)&&s in t||g(n,t)&&s in e&&m(t[s],e[s])))return!1;return!0}return g(t,e)})),y=r(((t,e)=>(e.push(t),e))),w=i(((t,e,n)=>n.reduce(t,e))),b=o(((t,e,n,s)=>t(s)?e(s):n(s))),_=(...e)=>(...n)=>{let s,o=!0;for(let r=p(e)-1;r>-1;r--)o?(o=!1,s=e[r](...n)):s=s===t?e[r]():e[r](s);return s},S=r(((t,e)=>e[t])),k=i(((t,e,n)=>n.slice(t,(t=>"number"==u(t))(e)?e:c))),v=S(0);k(1,c);const E=r(((t,e)=>e.find(t))),P=t=>()=>t,W=r(((t,e)=>e.split(t))),j=P(!0),A=P(!1),N=r(((t,e)=>w(((e,n)=>E((e=>t(n,e)),e)?e:y(n,e)),[],e)))(m),q=i(((t,e,n)=>p(e)?(t=>a(t)||(t=>t===l)(t))(n)?t:_((s=>s in n?q(t,k(1,c,e),n[s]):t),v)(e):n));q(l),_(b(m(f),A,j),q(f));const O=r(((t,e)=>e.map(t))),{floor:Q}=Math,z="0123456789abcdefghijklmnopqrstuvwxyz",C=_((t=>Object.fromEntries(t)),O(((t,e)=>[t,e])),W(""));class ${abc;abclen;c2pos;standard;setABC(t){if(!_(m(p(e=t)),p,N,W(""))(e))throw new Error("Not all chars are unique!");var e;this.abc=t,this.abclen=t.length,this.standard=z.startsWith(t),this.c2pos=C(t)}zip(t){const{abc:e,abclen:n}=this;let s="";for(;t>0;)s=e[t%n]+s,t=Q(t/n);return s||"0"}unzip(t){const{standard:e,abclen:n,c2pos:s}=this;if(e)return parseInt(t,n);const o=t.length;let r=0;for(let e=0;e<o;e++)r+=s[t[e]]*n**(o-e-1);return r}constructor(t){this.setABC(t||z+"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=(t,e,n)=>t.addEventListener(e,n),T=(t,e)=>setTimeout(e,t),x={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,lazy:!1,socket:null,adapter:(t,e)=>new WebSocket(t,e),encode:(t,e,{server:n})=>JSON.stringify({[n.id_key]:t,[n.data_key]:e}),decode:t=>JSON.parse(t),protocols:[],pipes:[],server:{id_key:"id",data_key:"data"},ping:{interval:55,content:{}}},D=Symbol("Placeholder"),L=t=>{let e=0;for(const n of t)n!==D&&e++;return e},U=(t,e)=>{const n=t.length,s=t.slice(),o=e.length;let r=o,i=0;for(;r&&i<n;i++)s[i]===D&&(s[i]=e[o-r],r--);for(i=n;r;i++,r--)s[i]=e[o-r];return s},F=(t,e,n)=>{const s=t.length-e.length-L(n);if(s<1)return t(...U(e,n));{const o=(...s)=>F(t,U(e,n),s);return o.$args_left=s,o}},J=t=>(...e)=>t.length>L(e)?F(t,[],e):t(...e);function M(t){return function(e,n){const s=e===D,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return o>1?s?(t=>function(e){return e===D?t:t(e)})((e=>t(e,n))):t(e,n):n=>t(e,n)}}function G(t){return J(t)}const H=void 0,K=1/0,V=t=>typeof t,X=t=>null===t,Y=t=>"number"==V(t),Z={u:"U",b:"B",n:"N",s:"S",f:"F"},tt=Symbol(),et=t=>{const e=V(t);return"object"===e?X(t)?"Null":t.constructor.name:Z[e[0]]+e.slice(1)},nt=t=>t.length,st=t=>X(t)||(t=>t===H)(t),ot=M(((t,e)=>t===e)),rt=M(((t,e)=>{const n=et(t);if(ot(n,et(e))&&(ot(n,"Object")||ot(n,"Array"))){if(X(t)||X(e))return ot(t,e);if(ot(t,e))return!0;for(const n of[t,e])for(const s in n)if(!(ot(n,e)&&s in t||ot(n,t)&&s in e&&rt(t[s],e[s])))return!1;return!0}return ot(t,e)})),it=M(((t,e)=>(e.push(t),e))),lt=G(((t,e,n)=>n.reduce(t,e))),ct=M(((t,e)=>{const n=(t=>Array.isArray(t))(e);let s,o;n&&(s=0,o=[]);for(let s in e)t(e[s],s)||(n?o.push(+s):delete e[s]);if(n)for(const t of o)e.splice(t-s++,1);return e})),ut=J(((t,e,n,s)=>t(s)?e(s):n(s))),at=(...t)=>(...e)=>{let n,s=!0;for(let o=nt(t)-1;o>-1;o--)s?(s=!1,n=t[o](...e)):n=n===D?t[o]():t[o](n);return n},ht=M(((t,e)=>e[t])),ft=G(((t,e,n)=>n.slice(t,Y(e)?e:K))),dt=ht(0);ft(1,K);const pt=M(((t,e)=>e.find(t))),gt=t=>()=>t,mt=gt(!0),yt=gt(!1),wt=M(((t,e)=>lt(((e,n)=>pt((e=>t(n,e)),e)?e:it(n,e)),[],e)));wt(rt);const bt=t=>{let e,n=!1;return(...s)=>n?e:(n=!0,e=t(...s))},_t=G(((t,e,n)=>nt(e)?st(n)?t:at((s=>s in n?_t(t,ft(1,K,e),n[s]):t),dt)(e):n));_t(H),at(ut(rt(tt),yt,mt),_t(tt));const St=new $;module.exports=class{open=!1;ws=null;forcibly_closed=!1;reconnect_timeout=null;queue={};onReadyQueue=[];onCloseQueue=[];handlers={open:[],close:[],message:[],error:[],timeout:[]};config={};init_flush(){ct(yt,this.queue)}call(t,...e){for(const n of this.handlers[t])n(...e)}log(t,e=null,n=null){const s=this.config;null!==n?s.log(t,n,e):s.timer?s.log(t,null,e):s.log(t,e)}initSocket(t){const{queue:e,config:n}=this;this.open=!0,this.ws=t,this.onReadyQueue.forEach((t=>t())),this.onReadyQueue.splice(0);const{id_key:s,data_key:o}=n.server;this.call("open",t);for(const n in e)t.send(e[n].msg);if(null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),n.ping){const t=setInterval((()=>{this.open&&this.send(n.ping.content),this.forcibly_closed&&clearInterval(t)}),1e3*n.ping.interval)}I(t,"close",(async(...t)=>{this.log("close"),this.open=!1,this.ws=null,this.onCloseQueue.forEach((t=>t())),this.onCloseQueue.splice(0),this.call("close",...t);const e=n.reconnect;if("number"==typeof e&&!isNaN(e)&&!this.forcibly_closed){const t=async()=>{this.log("reconnect"),null!==this.ws&&(this.ws.close(),this.ws=null);null!==await this.connect()&&(this.reconnect_timeout=setTimeout(t,1e3*e))};t()}})),I(t,"message",(t=>{try{const e=n.decode(t.data);if(this.call("message",{...t,data:e}),e[s]){const t=this.queue[e[s]];if(t){const n=t.sent_time?Date.now()-t.sent_time:null;this.log("message",e[o],n),t.ff(e[o])}}}catch(e){console.error(e,`WSP: Decode error. Got: ${t.data}`)}}))}connect(){return new Promise((t=>{if(!0===this.open)return t(null);const e=this.config,n=e.socket||e.adapter(e.url,e.protocols);if(!n||n.readyState>1)return this.ws=null,this.log("error","ready() on closing or closed state! status 2."),t(2);const s=bt(t);I(n,"error",bt((t=>{this.log("error","status 3. Err: "+t.message),this.call("error",t),this.ws=null,s(3)}))),n.readyState?(this.initSocket(n),s(null)):I(n,"open",bt((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((t=>{this.open?t():this.onReadyQueue.push(t)}))}on(t,e,n=mt,s=!1){const o=t=>n(t)&&e(t);return s?I(this.ws,t,o):this.handlers[t].push(o),o}off(t,e,n=!1){if(n)return((t,e,n)=>t.removeEventListener(e,n))(this.ws,t,e);const s=this.handlers[t],o=s.indexOf(e);~o&&s.splice(o,1)}async close(){return new Promise(((t,e)=>{null===this.ws?e("WSP: closing a non-inited socket!"):(this.open=!1,this.onCloseQueue.push((()=>{this.init_flush(),t(null)})),this.ws.close(),this.ws=null,this.forcibly_closed=!0)}))}async send(t,e={}){this.log("send",t);const{config:n,ws:s,forcibly_closed:o,queue:r}=this,i={},l=n.server.data_key,c=St.zip(2147483637*Math.random()|0);if("object"==typeof e.top){if(e.top[l])throw new Error("Attempting to set data key/token via send() options!");Object.assign(i,e.top)}if(n.pipes.forEach((e=>t=e(t))),o)throw new Error("Attempting to send via closed WebSocket connection!");this.open||this.connect();const u=await n.encode(c,t,n);return 1===s?.readyState&&s.send(u),new Promise(((e,s)=>{this.queue[c]={msg:u,ff(t){e(t),clearTimeout(this.timeout),delete r[c]},data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:T(n.timeout,(()=>{this.queue[c]&&(this.call("timeout",t),s({"Websocket timeout expired: ":n.timeout,"for the message ":t}),delete r[c])}))}}))}constructor(t={}){this.config=(t=>{if(null===R&&!("adapter"in t))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 e=Object.assign({},x,t),n=e.url;if("/"==n[0])try{const t=location.protocol.includes("s:")?"wss":"ws";e.url=`${t}://${location.hostname}:${location.port}${n}`}catch(t){throw new Error("WSP: URL starting with / in non-browser environment!")}return e})(t),this.init_flush(),this.config.lazy||this.connect()}};
1
+ "use strict";const t=Symbol("Placeholder"),e=e=>{let n=0;for(const s of e)s!==t&&n++;return n},n=(e,n)=>{const s=e.length,o=e.slice(),r=n.length;let i=r,c=0;for(;i&&c<s;c++)o[c]===t&&(o[c]=n[r-i],i--);for(c=s;i;c++,i--)o[c]=n[r-i];return o},s=(t,o,r)=>{const i=t.length-o.length-e(r);if(i<1)return t(...n(o,r));{const e=(...e)=>s(t,n(o,r),e);return e.$args_left=i,e}},o=t=>(...n)=>t.length>e(n)?s(t,[],n):t(...n);function r(e){return function(n,s){const o=n===t,r=arguments.length;if(1===r&&o)throw new Error("Senseless placeholder usage.");return r>1?o?(e=>function(n){return n===t?e:e(n)})((t=>e(t,s))):e(n,s):t=>e(n,t)}}function i(t){return o(t)}const c=void 0,l=1/0,u=t=>typeof t,a=t=>null===t,h={u:"U",b:"B",n:"N",s:"S",f:"F"},f=Symbol(),d=t=>{const e=u(t);return"object"===e?a(t)?"Null":t.constructor.name:h[e[0]]+e.slice(1)},p=t=>t.length,g=r(((t,e)=>t===e)),m=r(((t,e)=>{const n=d(t);if(g(n,d(e))&&(g(n,"Object")||g(n,"Array"))){if(a(t)||a(e))return g(t,e);if(g(t,e))return!0;for(const n of[t,e])for(const s in n)if(!(g(n,e)&&s in t||g(n,t)&&s in e&&m(t[s],e[s])))return!1;return!0}return g(t,e)})),y=r(((t,e)=>(e.push(t),e))),w=i(((t,e,n)=>n.reduce(t,e))),b=o(((t,e,n,s)=>t(s)?e(s):n(s))),_=(...e)=>(...n)=>{let s,o=!0;for(let r=p(e)-1;r>-1;r--)o?(o=!1,s=e[r](...n)):s=s===t?e[r]():e[r](s);return s},S=r(((t,e)=>e[t])),k=i(((t,e,n)=>n.slice(t,(t=>"number"==u(t))(e)?e:l))),v=S(0);k(1,l);const P=r(((t,e)=>e.find(t))),E=t=>()=>t,A=r(((t,e)=>e.split(t))),N=E(!0),W=E(!1),j=r(((t,e)=>w(((e,n)=>P((e=>t(n,e)),e)?e:y(n,e)),[],e)))(m),q=i(((t,e,n)=>p(e)?(t=>a(t)||(t=>t===c)(t))(n)?t:_((s=>s in n?q(t,k(1,l,e),n[s]):t),v)(e):n));q(c),_(b(m(f),W,N),q(f));const C=r(((t,e)=>e.map(t))),{floor:O}=Math,Q="0123456789abcdefghijklmnopqrstuvwxyz",$=_((t=>Object.fromEntries(t)),C(((t,e)=>[t,e])),A(""));class z{abc;abclen;c2pos;standard;setABC(t){if(!_(m(p(e=t)),p,j,A(""))(e))throw new Error("Not all chars are unique!");var e;this.abc=t,this.abclen=t.length,this.standard=Q.startsWith(t),this.c2pos=$(t)}zip(t){const{abc:e,abclen:n}=this;let s="";for(;t>0;)s=e[t%n]+s,t=O(t/n);return s||"0"}unzip(t){const{standard:e,abclen:n,c2pos:s}=this;if(e)return parseInt(t,n);const o=t.length;let r=0;for(let e=0;e<o;e++)r+=s[t[e]]*n**(o-e-1);return r}constructor(t){this.setABC(t||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}})(),B=(t,e,n)=>t.addEventListener(e,n),x=(t,e)=>setTimeout(e,t),D={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,reconnection_attempts:1/0,lazy:!1,socket:null,adapter:(t,e)=>new WebSocket(t,e),encode:(t,e,{server:n})=>JSON.stringify({[n.id_key]:t,[n.data_key]:e}),decode:t=>JSON.parse(t),protocols:[],pipes:[],server:{id_key:"id",data_key:"data"},ping:{interval:55,content:{}}},L=Symbol("Placeholder"),U=t=>{let e=0;for(const n of t)n!==L&&e++;return e},F=(t,e)=>{const n=t.length,s=t.slice(),o=e.length;let r=o,i=0;for(;r&&i<n;i++)s[i]===L&&(s[i]=e[o-r],r--);for(i=n;r;i++,r--)s[i]=e[o-r];return s},I=(t,e,n)=>{const s=t.length-e.length-U(n);if(s<1)return t(...F(e,n));{const o=(...s)=>I(t,F(e,n),s);return o.$args_left=s,o}},J=t=>(...e)=>t.length>U(e)?I(t,[],e):t(...e);function M(t){return function(e,n){const s=e===L,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return o>1?s?(t=>function(e){return e===L?t:t(e)})((e=>t(e,n))):t(e,n):n=>t(e,n)}}function G(t){return J(t)}const H=/^(.*?)(8|16|32|64)(Clamped)?Array$/,K=void 0,V=1/0,X=t=>typeof t,Y=t=>null===t,Z=t=>"number"==X(t);const tt=t=>Y(t)||(t=>t===K)(t),et={u:"U",b:"B",n:"N",s:"S",f:"F"},nt=Symbol(),st=t=>{const e=X(t);return"object"===e?Y(t)?"Null":t.constructor.name:et[e[0]]+e.slice(1)},ot=M(((t,e)=>st(e)===t)),rt=t=>t.length,it=M(((t,e)=>t===e)),ct=M(((t,e)=>{const n=st(t);if(it(n,st(e))&&(it(n,"Object")||it(n,"Array")||(t=>H.test(t))(n))){if(Y(t)||Y(e))return it(t,e);if(it(t,e))return!0;for(const n of[t,e])for(const s in n)if(!(it(n,e)&&s in t||it(n,t)&&s in e&&ct(t[s],e[s])))return!1;return!0}return it(t,e)})),lt=M(((t,e)=>(e.push(t),e))),ut=G(((t,e,n)=>n.reduce(t,e))),at=M(((t,e)=>{const n=(t=>Array.isArray(t))(e);let s,o;n&&(s=0,o=[]);for(let s in e)t(e[s],s)||(n?o.push(+s):delete e[s]);if(n)for(const t of o)e.splice(t-s++,1);return e})),ht=J(((t,e,n,s)=>t(s)?e(s):n(s))),ft=(...t)=>(...e)=>{let n,s=!0;for(let o=rt(t)-1;o>-1;o--)s?(s=!1,n=t[o](...e)):n=n===L?t[o]():t[o](n);return n},dt=M(((t,e)=>e[t])),pt=G(((t,e,n)=>n.slice(t,Z(e)?e:V))),gt=dt(0);pt(1,V);const mt=M(((t,e)=>e.find(t))),yt=t=>()=>t,wt=yt(!0),bt=yt(!1),_t=M(((t,e)=>e(...t))),St=t=>(...e)=>{const n=t(...e),s=function(t){return"function"===X(t)}(n);return!s||s&&n.$args_left<=0?(t=>!t)(n):St(n)},kt=M(((t,e)=>ut(((e,n)=>mt((e=>t(n,e)),e)?e:lt(n,e)),[],e)));kt(ct);const vt=t=>{let e,n=!1;return(...s)=>n?e:(n=!0,e=t(...s))},Pt=G(((t,e,n)=>rt(e)?tt(n)?t:ft((s=>s in n?Pt(t,pt(1,V,e),n[s]):t),gt)(e):n));Pt(K),ft(ht(ct(nt),bt,wt),Pt(nt));const Et=G(((t,e,n)=>e(n)&&t(n))),At=St,Nt=new z,Wt=_t([]),jt=Et(ot("Number"),At(isNaN));module.exports=class{ws=null;forcibly_closed=!1;reconnect_timeout=null;queue={};onReadyQueue=[];onCloseQueue=[];handlers={open:[],close:[],message:[],error:[],timeout:[]};config={};pinger=null;get opened(){return 1===this.ws?.readyState}init_flush(){at(bt,this.queue)}call(t,...e){for(const n of this.handlers[t])n(...e)}log(t,e=null,n=null){const s=this.config;null!==n?s.log(t,n,e):s.timer?s.log(t,null,e):s.log(t,e)}resetPing(){const{config:t}=this;tt(this.pinger)||clearTimeout(this.pinger),t.ping&&(this.pinger=setTimeout((async()=>{if(this.forcibly_closed)return clearTimeout(this.pinger);this.opened&&(await this.send(t.ping.content),this.resetPing())}),1e3*t.ping.interval))}initSocket(t){const{queue:e,config:n}=this;this.ws=t,this.onReadyQueue.forEach((t=>t())),this.onReadyQueue.splice(0);const{id_key:s,data_key:o}=n.server;this.call("open",t);for(const n in e)t.send(e[n].msg);null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),this.resetPing(),B(t,"close",(async(...t)=>{this.log("close"),this.ws=null,this.onCloseQueue.forEach(Wt),this.onCloseQueue.splice(0),this.call("close",...t);let{reconnect:e,reconnection_attempts:s}=n;if(jt(e)){const t=async()=>{if(this.forcibly_closed||!s)return;s--,this.log("reconnect"),tt(this.ws)||(this.ws.close(),this.ws=null);const n=await this.connect();tt(n)||(this.reconnect_timeout=setTimeout(t,1e3*e))};t()}})),B(t,"message",(t=>{try{const e=n.decode(t.data);if(this.call("message",{...t,data:e}),e[s]){const t=this.queue[e[s]];if(t){const n=t.sent_time?Date.now()-t.sent_time:null;this.log("message",e[o],n),t.ff(e[o])}}}catch(e){console.error(e,`WSP: Decode error. Got: ${t.data}`)}}))}opening=!1;connect(){return new Promise((t=>{if(this.opened||this.opening)return t(null);this.opening=!0;const e=this.config,n=e.socket||e.adapter(e.url,e.protocols);if(!n||n.readyState>1)return this.opening=!1,this.ws=null,this.log("error","ready() on closing or closed state! status 2."),t(2);const s=vt((e=>{this.opening=!1,t(e)}));B(n,"error",vt((t=>{this.ws=null,this.log("error","status 3. Err: "+t.message),this.call("error",t),s(3)}))),n.readyState?(this.initSocket(n),s(null)):B(n,"open",vt((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((t=>{this.opened?t():this.onReadyQueue.push(t)}))}on(t,e,n=wt,s=!1){const o=t=>n(t)&&e(t);return s?B(this.ws,t,o):this.handlers[t].push(o),o}off(t,e,n=!1){if(n)return((t,e,n)=>t.removeEventListener(e,n))(this.ws,t,e);const s=this.handlers[t],o=s.indexOf(e);~o&&s.splice(o,1)}async close(){return new Promise(((t,e)=>{null===this.ws?e("WSP: closing a non-inited socket!"):(this.onCloseQueue.push((()=>{this.init_flush(),t(null)})),this.ws.close(),this.ws=null,this.forcibly_closed=!0)}))}open(){if(!this.opened)return this.forcibly_closed=!1,this.connect()}async send(t,e={}){this.log("send",t);const{config:n,forcibly_closed:s,queue:o}=this,r={},{pipes:i,server:{data_key:c}}=n,l=Nt.zip(2147483637*Math.random()|0);if("object"==typeof e.top){if(e.top[c])throw new Error("Attempting to set data key/token via send() options!");Object.assign(r,e.top)}for(const e of i)t=e(t);if(s)throw new Error("Attempting to send via closed WebSocket connection!");const[u,a]=await Promise.all([n.encode(l,t,n),this.connect()]);if(a)throw new Error("ERR while opening connection #"+a);return this.opened&&(this.ws.send(u),this.resetPing()),new Promise(((e,s)=>{this.queue[l]={msg:u,ff(t){e(t),clearTimeout(this.timeout),delete o[l]},data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:x(n.timeout,(()=>{this.queue[l]&&(this.call("timeout",t),s({"Websocket timeout expired":n.timeout,"for the message":t}),delete o[l])}))}}))}constructor(t={}){this.config=(t=>{if(null===T&&!("adapter"in t))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 e=Object.assign({},D,t),n=e.url;if("/"==n[0])try{const t=location.protocol.includes("s:")?"wss":"ws";e.url=`${t}://${location.hostname}:${location.port}${n}`}catch(t){throw new Error("WSP: URL starting with / in non-browser environment!")}return e})(t),this.config.lazy||this.connect()}};
package/dist/bundle.d.ts CHANGED
@@ -25,6 +25,7 @@ declare namespace wsc {
25
25
  url: string;
26
26
  timeout: number;
27
27
  reconnect: number;
28
+ reconnection_attempts: number;
28
29
  lazy: boolean;
29
30
  socket: Socket | null;
30
31
  adapter: (host: string, protocols?: string[]) => Socket;
@@ -57,7 +58,6 @@ declare namespace wsc {
57
58
  }
58
59
  }
59
60
  declare class WebSocketClient {
60
- private open;
61
61
  private ws;
62
62
  private forcibly_closed;
63
63
  private reconnect_timeout;
@@ -66,16 +66,21 @@ declare class WebSocketClient {
66
66
  private onCloseQueue;
67
67
  private handlers;
68
68
  private config;
69
+ private pinger;
70
+ private get opened();
69
71
  private init_flush;
70
72
  private call;
71
73
  private log;
74
+ private resetPing;
72
75
  private initSocket;
76
+ private opening;
73
77
  private connect;
74
78
  get socket(): wsc.Socket | null;
75
79
  ready(): Promise<void>;
76
80
  on(event_name: wsc.WSEvent, handler: (data: any) => any, predicate?: (data: any) => boolean, raw?: boolean): wsc.EventHandler;
77
81
  off(event_name: wsc.WSEvent, handler: (data: any) => any, raw?: boolean): void;
78
82
  close(): wsc.AsyncErrCode;
83
+ open(): Promise<number | null> | undefined;
79
84
  /** .send(your_data) wraps request to server with {id: `hash`, data: `actually your data`},
80
85
  returns a Promise that will be rejected after a timeout or
81
86
  resolved if server returns the same signature: {id: `same_hash`, data: `response data`}.
package/dist/bundle.mjs CHANGED
@@ -1 +1 @@
1
- const t=Symbol("Placeholder"),e=e=>{let n=0;for(const o of e)o!==t&&n++;return n},n=(e,n)=>{const o=e.length,s=e.slice(),r=n.length;let i=r,l=0;for(;i&&l<o;l++)s[l]===t&&(s[l]=n[r-i],i--);for(l=o;i;l++,i--)s[l]=n[r-i];return s},o=(t,s,r)=>{const i=t.length-s.length-e(r);if(i<1)return t(...n(s,r));{const e=(...e)=>o(t,n(s,r),e);return e.$args_left=i,e}},s=t=>(...n)=>t.length>e(n)?o(t,[],n):t(...n);function r(e){return function(n,o){const s=n===t,r=arguments.length;if(1===r&&s)throw new Error("Senseless placeholder usage.");return r>1?s?(e=>function(n){return n===t?e:e(n)})((t=>e(t,o))):e(n,o):t=>e(n,t)}}function i(t){return s(t)}const l=void 0,c=1/0,u=t=>typeof t,a=t=>null===t,h={u:"U",b:"B",n:"N",s:"S",f:"F"},f=Symbol(),d=t=>{const e=u(t);return"object"===e?a(t)?"Null":t.constructor.name:h[e[0]]+e.slice(1)},p=t=>t.length,g=r(((t,e)=>t===e)),m=r(((t,e)=>{const n=d(t);if(g(n,d(e))&&(g(n,"Object")||g(n,"Array"))){if(a(t)||a(e))return g(t,e);if(g(t,e))return!0;for(const n of[t,e])for(const o in n)if(!(g(n,e)&&o in t||g(n,t)&&o in e&&m(t[o],e[o])))return!1;return!0}return g(t,e)})),y=r(((t,e)=>(e.push(t),e))),w=i(((t,e,n)=>n.reduce(t,e))),b=s(((t,e,n,o)=>t(o)?e(o):n(o))),_=(...e)=>(...n)=>{let o,s=!0;for(let r=p(e)-1;r>-1;r--)s?(s=!1,o=e[r](...n)):o=o===t?e[r]():e[r](o);return o},S=r(((t,e)=>e[t])),k=i(((t,e,n)=>n.slice(t,(t=>"number"==u(t))(e)?e:c))),v=S(0);k(1,c);const E=r(((t,e)=>e.find(t))),P=t=>()=>t,W=r(((t,e)=>e.split(t))),j=P(!0),A=P(!1),N=r(((t,e)=>w(((e,n)=>E((e=>t(n,e)),e)?e:y(n,e)),[],e)))(m),q=i(((t,e,n)=>p(e)?(t=>a(t)||(t=>t===l)(t))(n)?t:_((o=>o in n?q(t,k(1,c,e),n[o]):t),v)(e):n));q(l),_(b(m(f),A,j),q(f));const O=r(((t,e)=>e.map(t))),{floor:Q}=Math,z="0123456789abcdefghijklmnopqrstuvwxyz",C=_((t=>Object.fromEntries(t)),O(((t,e)=>[t,e])),W(""));class ${abc;abclen;c2pos;standard;setABC(t){if(!_(m(p(e=t)),p,N,W(""))(e))throw new Error("Not all chars are unique!");var e;this.abc=t,this.abclen=t.length,this.standard=z.startsWith(t),this.c2pos=C(t)}zip(t){const{abc:e,abclen:n}=this;let o="";for(;t>0;)o=e[t%n]+o,t=Q(t/n);return o||"0"}unzip(t){const{standard:e,abclen:n,c2pos:o}=this;if(e)return parseInt(t,n);const s=t.length;let r=0;for(let e=0;e<s;e++)r+=o[t[e]]*n**(s-e-1);return r}constructor(t){this.setABC(t||z+"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=(t,e,n)=>t.addEventListener(e,n),T=(t,e)=>setTimeout(e,t),x={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,lazy:!1,socket:null,adapter:(t,e)=>new WebSocket(t,e),encode:(t,e,{server:n})=>JSON.stringify({[n.id_key]:t,[n.data_key]:e}),decode:t=>JSON.parse(t),protocols:[],pipes:[],server:{id_key:"id",data_key:"data"},ping:{interval:55,content:{}}},D=Symbol("Placeholder"),L=t=>{let e=0;for(const n of t)n!==D&&e++;return e},U=(t,e)=>{const n=t.length,o=t.slice(),s=e.length;let r=s,i=0;for(;r&&i<n;i++)o[i]===D&&(o[i]=e[s-r],r--);for(i=n;r;i++,r--)o[i]=e[s-r];return o},F=(t,e,n)=>{const o=t.length-e.length-L(n);if(o<1)return t(...U(e,n));{const s=(...o)=>F(t,U(e,n),o);return s.$args_left=o,s}},J=t=>(...e)=>t.length>L(e)?F(t,[],e):t(...e);function M(t){return function(e,n){const o=e===D,s=arguments.length;if(1===s&&o)throw new Error("Senseless placeholder usage.");return s>1?o?(t=>function(e){return e===D?t:t(e)})((e=>t(e,n))):t(e,n):n=>t(e,n)}}function G(t){return J(t)}const H=void 0,K=1/0,V=t=>typeof t,X=t=>null===t,Y=t=>"number"==V(t),Z={u:"U",b:"B",n:"N",s:"S",f:"F"},tt=Symbol(),et=t=>{const e=V(t);return"object"===e?X(t)?"Null":t.constructor.name:Z[e[0]]+e.slice(1)},nt=t=>t.length,ot=t=>X(t)||(t=>t===H)(t),st=M(((t,e)=>t===e)),rt=M(((t,e)=>{const n=et(t);if(st(n,et(e))&&(st(n,"Object")||st(n,"Array"))){if(X(t)||X(e))return st(t,e);if(st(t,e))return!0;for(const n of[t,e])for(const o in n)if(!(st(n,e)&&o in t||st(n,t)&&o in e&&rt(t[o],e[o])))return!1;return!0}return st(t,e)})),it=M(((t,e)=>(e.push(t),e))),lt=G(((t,e,n)=>n.reduce(t,e))),ct=M(((t,e)=>{const n=(t=>Array.isArray(t))(e);let o,s;n&&(o=0,s=[]);for(let o in e)t(e[o],o)||(n?s.push(+o):delete e[o]);if(n)for(const t of s)e.splice(t-o++,1);return e})),ut=J(((t,e,n,o)=>t(o)?e(o):n(o))),at=(...t)=>(...e)=>{let n,o=!0;for(let s=nt(t)-1;s>-1;s--)o?(o=!1,n=t[s](...e)):n=n===D?t[s]():t[s](n);return n},ht=M(((t,e)=>e[t])),ft=G(((t,e,n)=>n.slice(t,Y(e)?e:K))),dt=ht(0);ft(1,K);const pt=M(((t,e)=>e.find(t))),gt=t=>()=>t,mt=gt(!0),yt=gt(!1),wt=M(((t,e)=>lt(((e,n)=>pt((e=>t(n,e)),e)?e:it(n,e)),[],e)));wt(rt);const bt=t=>{let e,n=!1;return(...o)=>n?e:(n=!0,e=t(...o))},_t=G(((t,e,n)=>nt(e)?ot(n)?t:at((o=>o in n?_t(t,ft(1,K,e),n[o]):t),dt)(e):n));_t(H),at(ut(rt(tt),yt,mt),_t(tt));const St=new $;class kt{open=!1;ws=null;forcibly_closed=!1;reconnect_timeout=null;queue={};onReadyQueue=[];onCloseQueue=[];handlers={open:[],close:[],message:[],error:[],timeout:[]};config={};init_flush(){ct(yt,this.queue)}call(t,...e){for(const n of this.handlers[t])n(...e)}log(t,e=null,n=null){const o=this.config;null!==n?o.log(t,n,e):o.timer?o.log(t,null,e):o.log(t,e)}initSocket(t){const{queue:e,config:n}=this;this.open=!0,this.ws=t,this.onReadyQueue.forEach((t=>t())),this.onReadyQueue.splice(0);const{id_key:o,data_key:s}=n.server;this.call("open",t);for(const n in e)t.send(e[n].msg);if(null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),n.ping){const t=setInterval((()=>{this.open&&this.send(n.ping.content),this.forcibly_closed&&clearInterval(t)}),1e3*n.ping.interval)}I(t,"close",(async(...t)=>{this.log("close"),this.open=!1,this.ws=null,this.onCloseQueue.forEach((t=>t())),this.onCloseQueue.splice(0),this.call("close",...t);const e=n.reconnect;if("number"==typeof e&&!isNaN(e)&&!this.forcibly_closed){const t=async()=>{this.log("reconnect"),null!==this.ws&&(this.ws.close(),this.ws=null);null!==await this.connect()&&(this.reconnect_timeout=setTimeout(t,1e3*e))};t()}})),I(t,"message",(t=>{try{const e=n.decode(t.data);if(this.call("message",{...t,data:e}),e[o]){const t=this.queue[e[o]];if(t){const n=t.sent_time?Date.now()-t.sent_time:null;this.log("message",e[s],n),t.ff(e[s])}}}catch(e){console.error(e,`WSP: Decode error. Got: ${t.data}`)}}))}connect(){return new Promise((t=>{if(!0===this.open)return t(null);const e=this.config,n=e.socket||e.adapter(e.url,e.protocols);if(!n||n.readyState>1)return this.ws=null,this.log("error","ready() on closing or closed state! status 2."),t(2);const o=bt(t);I(n,"error",bt((t=>{this.log("error","status 3. Err: "+t.message),this.call("error",t),this.ws=null,o(3)}))),n.readyState?(this.initSocket(n),o(null)):I(n,"open",bt((()=>{this.log("open"),this.initSocket(n),o(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((t=>{this.open?t():this.onReadyQueue.push(t)}))}on(t,e,n=mt,o=!1){const s=t=>n(t)&&e(t);return o?I(this.ws,t,s):this.handlers[t].push(s),s}off(t,e,n=!1){if(n)return((t,e,n)=>t.removeEventListener(e,n))(this.ws,t,e);const o=this.handlers[t],s=o.indexOf(e);~s&&o.splice(s,1)}async close(){return new Promise(((t,e)=>{null===this.ws?e("WSP: closing a non-inited socket!"):(this.open=!1,this.onCloseQueue.push((()=>{this.init_flush(),t(null)})),this.ws.close(),this.ws=null,this.forcibly_closed=!0)}))}async send(t,e={}){this.log("send",t);const{config:n,ws:o,forcibly_closed:s,queue:r}=this,i={},l=n.server.data_key,c=St.zip(2147483637*Math.random()|0);if("object"==typeof e.top){if(e.top[l])throw new Error("Attempting to set data key/token via send() options!");Object.assign(i,e.top)}if(n.pipes.forEach((e=>t=e(t))),s)throw new Error("Attempting to send via closed WebSocket connection!");this.open||this.connect();const u=await n.encode(c,t,n);return 1===o?.readyState&&o.send(u),new Promise(((e,o)=>{this.queue[c]={msg:u,ff(t){e(t),clearTimeout(this.timeout),delete r[c]},data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:T(n.timeout,(()=>{this.queue[c]&&(this.call("timeout",t),o({"Websocket timeout expired: ":n.timeout,"for the message ":t}),delete r[c])}))}}))}constructor(t={}){this.config=(t=>{if(null===R&&!("adapter"in t))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 e=Object.assign({},x,t),n=e.url;if("/"==n[0])try{const t=location.protocol.includes("s:")?"wss":"ws";e.url=`${t}://${location.hostname}:${location.port}${n}`}catch(t){throw new Error("WSP: URL starting with / in non-browser environment!")}return e})(t),this.init_flush(),this.config.lazy||this.connect()}}export{kt as default};
1
+ const t=Symbol("Placeholder"),e=e=>{let n=0;for(const s of e)s!==t&&n++;return n},n=(e,n)=>{const s=e.length,o=e.slice(),r=n.length;let i=r,c=0;for(;i&&c<s;c++)o[c]===t&&(o[c]=n[r-i],i--);for(c=s;i;c++,i--)o[c]=n[r-i];return o},s=(t,o,r)=>{const i=t.length-o.length-e(r);if(i<1)return t(...n(o,r));{const e=(...e)=>s(t,n(o,r),e);return e.$args_left=i,e}},o=t=>(...n)=>t.length>e(n)?s(t,[],n):t(...n);function r(e){return function(n,s){const o=n===t,r=arguments.length;if(1===r&&o)throw new Error("Senseless placeholder usage.");return r>1?o?(e=>function(n){return n===t?e:e(n)})((t=>e(t,s))):e(n,s):t=>e(n,t)}}function i(t){return o(t)}const c=void 0,l=1/0,u=t=>typeof t,a=t=>null===t,h={u:"U",b:"B",n:"N",s:"S",f:"F"},f=Symbol(),d=t=>{const e=u(t);return"object"===e?a(t)?"Null":t.constructor.name:h[e[0]]+e.slice(1)},p=t=>t.length,g=r(((t,e)=>t===e)),m=r(((t,e)=>{const n=d(t);if(g(n,d(e))&&(g(n,"Object")||g(n,"Array"))){if(a(t)||a(e))return g(t,e);if(g(t,e))return!0;for(const n of[t,e])for(const s in n)if(!(g(n,e)&&s in t||g(n,t)&&s in e&&m(t[s],e[s])))return!1;return!0}return g(t,e)})),y=r(((t,e)=>(e.push(t),e))),w=i(((t,e,n)=>n.reduce(t,e))),b=o(((t,e,n,s)=>t(s)?e(s):n(s))),_=(...e)=>(...n)=>{let s,o=!0;for(let r=p(e)-1;r>-1;r--)o?(o=!1,s=e[r](...n)):s=s===t?e[r]():e[r](s);return s},S=r(((t,e)=>e[t])),k=i(((t,e,n)=>n.slice(t,(t=>"number"==u(t))(e)?e:l))),v=S(0);k(1,l);const P=r(((t,e)=>e.find(t))),E=t=>()=>t,A=r(((t,e)=>e.split(t))),N=E(!0),W=E(!1),j=r(((t,e)=>w(((e,n)=>P((e=>t(n,e)),e)?e:y(n,e)),[],e)))(m),q=i(((t,e,n)=>p(e)?(t=>a(t)||(t=>t===c)(t))(n)?t:_((s=>s in n?q(t,k(1,l,e),n[s]):t),v)(e):n));q(c),_(b(m(f),W,N),q(f));const C=r(((t,e)=>e.map(t))),{floor:O}=Math,Q="0123456789abcdefghijklmnopqrstuvwxyz",$=_((t=>Object.fromEntries(t)),C(((t,e)=>[t,e])),A(""));class z{abc;abclen;c2pos;standard;setABC(t){if(!_(m(p(e=t)),p,j,A(""))(e))throw new Error("Not all chars are unique!");var e;this.abc=t,this.abclen=t.length,this.standard=Q.startsWith(t),this.c2pos=$(t)}zip(t){const{abc:e,abclen:n}=this;let s="";for(;t>0;)s=e[t%n]+s,t=O(t/n);return s||"0"}unzip(t){const{standard:e,abclen:n,c2pos:s}=this;if(e)return parseInt(t,n);const o=t.length;let r=0;for(let e=0;e<o;e++)r+=s[t[e]]*n**(o-e-1);return r}constructor(t){this.setABC(t||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}})(),B=(t,e,n)=>t.addEventListener(e,n),x=(t,e)=>setTimeout(e,t),D={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,reconnection_attempts:1/0,lazy:!1,socket:null,adapter:(t,e)=>new WebSocket(t,e),encode:(t,e,{server:n})=>JSON.stringify({[n.id_key]:t,[n.data_key]:e}),decode:t=>JSON.parse(t),protocols:[],pipes:[],server:{id_key:"id",data_key:"data"},ping:{interval:55,content:{}}},L=Symbol("Placeholder"),U=t=>{let e=0;for(const n of t)n!==L&&e++;return e},F=(t,e)=>{const n=t.length,s=t.slice(),o=e.length;let r=o,i=0;for(;r&&i<n;i++)s[i]===L&&(s[i]=e[o-r],r--);for(i=n;r;i++,r--)s[i]=e[o-r];return s},I=(t,e,n)=>{const s=t.length-e.length-U(n);if(s<1)return t(...F(e,n));{const o=(...s)=>I(t,F(e,n),s);return o.$args_left=s,o}},J=t=>(...e)=>t.length>U(e)?I(t,[],e):t(...e);function M(t){return function(e,n){const s=e===L,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return o>1?s?(t=>function(e){return e===L?t:t(e)})((e=>t(e,n))):t(e,n):n=>t(e,n)}}function G(t){return J(t)}const H=/^(.*?)(8|16|32|64)(Clamped)?Array$/,K=void 0,V=1/0,X=t=>typeof t,Y=t=>null===t,Z=t=>"number"==X(t);const tt=t=>Y(t)||(t=>t===K)(t),et={u:"U",b:"B",n:"N",s:"S",f:"F"},nt=Symbol(),st=t=>{const e=X(t);return"object"===e?Y(t)?"Null":t.constructor.name:et[e[0]]+e.slice(1)},ot=M(((t,e)=>st(e)===t)),rt=t=>t.length,it=M(((t,e)=>t===e)),ct=M(((t,e)=>{const n=st(t);if(it(n,st(e))&&(it(n,"Object")||it(n,"Array")||(t=>H.test(t))(n))){if(Y(t)||Y(e))return it(t,e);if(it(t,e))return!0;for(const n of[t,e])for(const s in n)if(!(it(n,e)&&s in t||it(n,t)&&s in e&&ct(t[s],e[s])))return!1;return!0}return it(t,e)})),lt=M(((t,e)=>(e.push(t),e))),ut=G(((t,e,n)=>n.reduce(t,e))),at=M(((t,e)=>{const n=(t=>Array.isArray(t))(e);let s,o;n&&(s=0,o=[]);for(let s in e)t(e[s],s)||(n?o.push(+s):delete e[s]);if(n)for(const t of o)e.splice(t-s++,1);return e})),ht=J(((t,e,n,s)=>t(s)?e(s):n(s))),ft=(...t)=>(...e)=>{let n,s=!0;for(let o=rt(t)-1;o>-1;o--)s?(s=!1,n=t[o](...e)):n=n===L?t[o]():t[o](n);return n},dt=M(((t,e)=>e[t])),pt=G(((t,e,n)=>n.slice(t,Z(e)?e:V))),gt=dt(0);pt(1,V);const mt=M(((t,e)=>e.find(t))),yt=t=>()=>t,wt=yt(!0),bt=yt(!1),_t=M(((t,e)=>e(...t))),St=t=>(...e)=>{const n=t(...e),s=function(t){return"function"===X(t)}(n);return!s||s&&n.$args_left<=0?(t=>!t)(n):St(n)},kt=M(((t,e)=>ut(((e,n)=>mt((e=>t(n,e)),e)?e:lt(n,e)),[],e)));kt(ct);const vt=t=>{let e,n=!1;return(...s)=>n?e:(n=!0,e=t(...s))},Pt=G(((t,e,n)=>rt(e)?tt(n)?t:ft((s=>s in n?Pt(t,pt(1,V,e),n[s]):t),gt)(e):n));Pt(K),ft(ht(ct(nt),bt,wt),Pt(nt));const Et=G(((t,e,n)=>e(n)&&t(n))),At=St,Nt=new z,Wt=_t([]),jt=Et(ot("Number"),At(isNaN));class qt{ws=null;forcibly_closed=!1;reconnect_timeout=null;queue={};onReadyQueue=[];onCloseQueue=[];handlers={open:[],close:[],message:[],error:[],timeout:[]};config={};pinger=null;get opened(){return 1===this.ws?.readyState}init_flush(){at(bt,this.queue)}call(t,...e){for(const n of this.handlers[t])n(...e)}log(t,e=null,n=null){const s=this.config;null!==n?s.log(t,n,e):s.timer?s.log(t,null,e):s.log(t,e)}resetPing(){const{config:t}=this;tt(this.pinger)||clearTimeout(this.pinger),t.ping&&(this.pinger=setTimeout((async()=>{if(this.forcibly_closed)return clearTimeout(this.pinger);this.opened&&(await this.send(t.ping.content),this.resetPing())}),1e3*t.ping.interval))}initSocket(t){const{queue:e,config:n}=this;this.ws=t,this.onReadyQueue.forEach((t=>t())),this.onReadyQueue.splice(0);const{id_key:s,data_key:o}=n.server;this.call("open",t);for(const n in e)t.send(e[n].msg);null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),this.resetPing(),B(t,"close",(async(...t)=>{this.log("close"),this.ws=null,this.onCloseQueue.forEach(Wt),this.onCloseQueue.splice(0),this.call("close",...t);let{reconnect:e,reconnection_attempts:s}=n;if(jt(e)){const t=async()=>{if(this.forcibly_closed||!s)return;s--,this.log("reconnect"),tt(this.ws)||(this.ws.close(),this.ws=null);const n=await this.connect();tt(n)||(this.reconnect_timeout=setTimeout(t,1e3*e))};t()}})),B(t,"message",(t=>{try{const e=n.decode(t.data);if(this.call("message",{...t,data:e}),e[s]){const t=this.queue[e[s]];if(t){const n=t.sent_time?Date.now()-t.sent_time:null;this.log("message",e[o],n),t.ff(e[o])}}}catch(e){console.error(e,`WSP: Decode error. Got: ${t.data}`)}}))}opening=!1;connect(){return new Promise((t=>{if(this.opened||this.opening)return t(null);this.opening=!0;const e=this.config,n=e.socket||e.adapter(e.url,e.protocols);if(!n||n.readyState>1)return this.opening=!1,this.ws=null,this.log("error","ready() on closing or closed state! status 2."),t(2);const s=vt((e=>{this.opening=!1,t(e)}));B(n,"error",vt((t=>{this.ws=null,this.log("error","status 3. Err: "+t.message),this.call("error",t),s(3)}))),n.readyState?(this.initSocket(n),s(null)):B(n,"open",vt((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((t=>{this.opened?t():this.onReadyQueue.push(t)}))}on(t,e,n=wt,s=!1){const o=t=>n(t)&&e(t);return s?B(this.ws,t,o):this.handlers[t].push(o),o}off(t,e,n=!1){if(n)return((t,e,n)=>t.removeEventListener(e,n))(this.ws,t,e);const s=this.handlers[t],o=s.indexOf(e);~o&&s.splice(o,1)}async close(){return new Promise(((t,e)=>{null===this.ws?e("WSP: closing a non-inited socket!"):(this.onCloseQueue.push((()=>{this.init_flush(),t(null)})),this.ws.close(),this.ws=null,this.forcibly_closed=!0)}))}open(){if(!this.opened)return this.forcibly_closed=!1,this.connect()}async send(t,e={}){this.log("send",t);const{config:n,forcibly_closed:s,queue:o}=this,r={},{pipes:i,server:{data_key:c}}=n,l=Nt.zip(2147483637*Math.random()|0);if("object"==typeof e.top){if(e.top[c])throw new Error("Attempting to set data key/token via send() options!");Object.assign(r,e.top)}for(const e of i)t=e(t);if(s)throw new Error("Attempting to send via closed WebSocket connection!");const[u,a]=await Promise.all([n.encode(l,t,n),this.connect()]);if(a)throw new Error("ERR while opening connection #"+a);return this.opened&&(this.ws.send(u),this.resetPing()),new Promise(((e,s)=>{this.queue[l]={msg:u,ff(t){e(t),clearTimeout(this.timeout),delete o[l]},data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:x(n.timeout,(()=>{this.queue[l]&&(this.call("timeout",t),s({"Websocket timeout expired":n.timeout,"for the message":t}),delete o[l])}))}}))}constructor(t={}){this.config=(t=>{if(null===T&&!("adapter"in t))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 e=Object.assign({},D,t),n=e.url;if("/"==n[0])try{const t=location.protocol.includes("s:")?"wss":"ws";e.url=`${t}://${location.hostname}:${location.port}${n}`}catch(t){throw new Error("WSP: URL starting with / in non-browser environment!")}return e})(t),this.config.lazy||this.connect()}}export{qt as default};
package/package.json CHANGED
@@ -41,7 +41,7 @@
41
41
  "prod": "npm run gentypes && npm run prod:es && npm run prod:cjs",
42
42
  "all": "npm run dev && npm run prod"
43
43
  },
44
- "version": "2.6.5",
44
+ "version": "2.7.0",
45
45
  "type": "module",
46
46
  "exports": {
47
47
  ".": {
@@ -52,28 +52,28 @@
52
52
  "./src": "./src/*"
53
53
  },
54
54
  "devDependencies": {
55
- "@rollup/plugin-commonjs": "^28.0.3",
55
+ "@rollup/plugin-commonjs": "^28.0.6",
56
56
  "@rollup/plugin-node-resolve": "^16.0.1",
57
57
  "@rollup/plugin-replace": "^6.0.2",
58
58
  "@rollup/plugin-terser": "^0.4.4",
59
- "@types/express": "^5.0.1",
60
- "@types/node": "^22.15.17",
59
+ "@types/express": "^5.0.3",
60
+ "@types/node": "^24.1.0",
61
61
  "@types/ws": "^8.18.1",
62
62
  "codecov": "^3.8.3",
63
- "cross-env": "^7.0.3",
63
+ "cross-env": "^10.0.0",
64
64
  "dts-bundle-generator": "^9.5.1",
65
65
  "nyc": "^17.1.0",
66
- "rollup": "^4.40.2",
66
+ "rollup": "^4.46.0",
67
67
  "rollup-plugin-typescript2": "^0.36.0",
68
68
  "ts-node": "^10.9.2",
69
- "tsx": "^4.19.4",
69
+ "tsx": "^4.20.3",
70
70
  "typescript": "^5.8.3",
71
71
  "uvu": "^0.5.6",
72
- "ws": "^8.18.2"
72
+ "ws": "^8.18.3"
73
73
  },
74
74
  "types": "./dist/bundle.d.ts",
75
75
  "dependencies": {
76
- "pepka": "^1.6.3",
76
+ "pepka": "^1.6.5",
77
77
  "zipnum": "^2.0.0"
78
78
  }
79
79
  }
package/src/WSC.ts CHANGED
@@ -2,10 +2,12 @@ import './types'
2
2
  import { Zipnum } from 'zipnum'
3
3
  import { add_event, rm_event, sett } from './utils'
4
4
  import { processConfig } from './config'
5
- import { AnyFunc, F, once, qfilter, T } from 'pepka'
5
+ import { AnyFunc, both, callWith, F, isNil, notf, once, qfilter, T, typeIs } from 'pepka'
6
6
 
7
7
  const MAX_32 = 2**31 - 1
8
8
  const zipnum = new Zipnum()
9
+ const callit = callWith([])
10
+ const isNumber = both(typeIs('Number'), notf(isNaN))
9
11
 
10
12
  type EventHandler<T extends keyof WebSocketEventMap> = AnyFunc<any, [WebSocketEventMap[T]]>
11
13
  type EventHandlers = {
@@ -17,7 +19,6 @@ type EventHandlers = {
17
19
  }
18
20
 
19
21
  class WebSocketClient {
20
- private open = false
21
22
  private ws: wsc.Socket|null = null
22
23
  private forcibly_closed = false
23
24
  private reconnect_timeout: NodeJS.Timeout|null = null
@@ -26,13 +27,14 @@ class WebSocketClient {
26
27
  private onCloseQueue: AnyFunc[] = []
27
28
  private handlers: EventHandlers = { open: [], close: [], message: [], error: [], timeout: [] }
28
29
  private config = <wsc.Config>{}
30
+ private pinger: NodeJS.Timeout|null = null
31
+ private get opened() { return this.ws?.readyState===1 } // The only opened state.
29
32
 
30
33
  private init_flush(): void {
31
34
  // TODO: reject them or save somehow ?..
32
35
  qfilter(F, this.queue)
33
36
  }
34
37
  private call(event_name: wsc.WSEvent, ...args: any[]) {
35
- // this.handlers.open[0]()
36
38
  for(const h of this.handlers[event_name]) h(...args)
37
39
  }
38
40
 
@@ -49,9 +51,23 @@ class WebSocketClient {
49
51
  }
50
52
  }
51
53
 
54
+ private resetPing() {
55
+ const {config} = this
56
+ if(!isNil(this.pinger))
57
+ clearTimeout(this.pinger as NodeJS.Timeout)
58
+ if(config.ping) {
59
+ this.pinger = setTimeout(async () => {
60
+ if(this.forcibly_closed) return clearTimeout(this.pinger as NodeJS.Timeout)
61
+ if(this.opened) {
62
+ await this.send(config.ping.content)
63
+ this.resetPing()
64
+ }
65
+ }, config.ping.interval*1e3)
66
+ }
67
+ }
68
+
52
69
  private initSocket(ws: wsc.Socket) {
53
70
  const {queue, config} = this
54
- this.open = true
55
71
  this.ws = ws
56
72
  this.onReadyQueue.forEach((fn: Function) => fn())
57
73
  this.onReadyQueue.splice(0)
@@ -63,42 +79,32 @@ class WebSocketClient {
63
79
  clearInterval(this.reconnect_timeout)
64
80
  this.reconnect_timeout = null
65
81
  }
66
- if(config.ping) {
67
- const ping_interval = setInterval(() => {
68
- if(this.open) this.send(config.ping.content)
69
- if(this.forcibly_closed) clearInterval(ping_interval)
70
- }, config.ping.interval*1e3)
71
- }
82
+ this.resetPing()
72
83
  add_event(ws, 'close', async (...e) => {
73
84
  this.log('close')
74
- this.open = false
75
85
  this.ws = null
76
- this.onCloseQueue.forEach((fn: Function) => fn())
86
+ this.onCloseQueue.forEach(callit)
77
87
  this.onCloseQueue.splice(0)
78
88
  this.call('close', ...e)
79
89
  // Auto reconnect.
80
- const reconnect = config.reconnect
81
- if(
82
- typeof reconnect === 'number' &&
83
- !isNaN(reconnect) &&
84
- !this.forcibly_closed
85
- ) {
90
+ let {reconnect, reconnection_attempts} = config
91
+ if(isNumber(reconnect)) {
86
92
  const reconnectFunc = async () => {
93
+ if(this.forcibly_closed || !reconnection_attempts) return;
94
+ reconnection_attempts--
87
95
  this.log('reconnect')
88
- if(this.ws !== null) {
89
- this.ws.close()
96
+ if(!isNil(this.ws)) {
97
+ this.ws!.close()
90
98
  this.ws = null
91
99
  }
92
100
  // If some error occured, try again.
93
101
  const status = await this.connect()
94
- if(status !== null)
95
- this.reconnect_timeout = setTimeout(reconnectFunc, reconnect * 1000)
102
+ if(!isNil(status))
103
+ this.reconnect_timeout = setTimeout(reconnectFunc, reconnect*1e3)
96
104
  }
97
105
  // TODO: test normal close by server. Would it be infinite ?
98
106
  reconnectFunc()
99
107
  }
100
- // reset the flag to reuse.
101
- // this.forcibly_closed = false // Makes the flag useless.
102
108
  })
103
109
  add_event(ws, 'message', (e) => {
104
110
  try {
@@ -120,23 +126,24 @@ class WebSocketClient {
120
126
  })
121
127
  }
122
128
 
129
+ private opening = false
123
130
  private connect() { // returns status if won't open or null if ok.
124
- return new Promise((ff) => {
125
- if(this.open === true) {
126
- return ff(null)
127
- }
131
+ return new Promise<null|number>((ff) => {
132
+ if(this.opened||this.opening) return ff(null)
133
+ this.opening = true
128
134
  const config = this.config
129
135
  const ws = config.socket || config.adapter(config.url, config.protocols)
130
136
  if(!ws || ws.readyState > 1) {
137
+ this.opening = false
131
138
  this.ws = null
132
139
  this.log('error', 'ready() on closing or closed state! status 2.')
133
140
  return ff(2)
134
141
  }
135
- const ffo = once(ff)
142
+ const ffo = once((s: null|number) => {this.opening=false; ff(s)})
136
143
  add_event(ws, 'error', once((e) => {
144
+ this.ws = null
137
145
  this.log('error', 'status 3. Err: '+e.message)
138
146
  this.call('error', e)
139
- this.ws = null
140
147
  // Some network error: Connection refused or so.
141
148
  ffo(3)
142
149
  }))
@@ -156,7 +163,7 @@ class WebSocketClient {
156
163
  public get socket() { return this.ws }
157
164
  public async ready() {
158
165
  return new Promise<void>((ff) => {
159
- if(this.open) ff()
166
+ if(this.opened) ff()
160
167
  else this.onReadyQueue.push(ff)
161
168
  })
162
169
  }
@@ -188,7 +195,6 @@ class WebSocketClient {
188
195
  if(this.ws === null) {
189
196
  rj('WSP: closing a non-inited socket!')
190
197
  } else {
191
- this.open = false
192
198
  this.onCloseQueue.push(() => {
193
199
  this.init_flush()
194
200
  ff(null)
@@ -200,6 +206,13 @@ class WebSocketClient {
200
206
  })
201
207
  }
202
208
 
209
+ public open() {
210
+ if(!this.opened) {
211
+ this.forcibly_closed = false
212
+ return this.connect()
213
+ }
214
+ }
215
+
203
216
  /** .send(your_data) wraps request to server with {id: `hash`, data: `actually your data`},
204
217
  returns a Promise that will be rejected after a timeout or
205
218
  resolved if server returns the same signature: {id: `same_hash`, data: `response data`}.
@@ -209,9 +222,9 @@ class WebSocketClient {
209
222
  opts = <wsc.SendOptions>{}
210
223
  ): Promise<ResponseDataType> {
211
224
  this.log('send', message_data)
212
- const {config, ws, forcibly_closed, queue} = this
213
- const message = {}
214
- const data_key = config.server.data_key
225
+ const {config, forcibly_closed, queue} = this
226
+ const message = {}
227
+ const {pipes, server: {data_key}} = config
215
228
 
216
229
  const message_id = zipnum.zip((Math.random()*(MAX_32-10))|0)
217
230
  if(typeof opts.top === 'object') {
@@ -220,14 +233,19 @@ class WebSocketClient {
220
233
  }
221
234
  Object.assign(message, opts.top)
222
235
  }
223
- config.pipes.forEach((pipe) => message_data = pipe(message_data))
236
+ for(const pipe of pipes) message_data = pipe(message_data)
224
237
 
225
238
  if(forcibly_closed)
226
239
  throw new Error('Attempting to send via closed WebSocket connection!')
227
- if(!this.open) this.connect()
228
- const msg = await config.encode(message_id, message_data, config)
229
- if(ws?.readyState===1) // The only opened state.
230
- ws.send(msg)
240
+ const [msg, err] = await Promise.all([
241
+ config.encode(message_id, message_data, config),
242
+ this.connect()
243
+ ])
244
+ if(err) throw new Error('ERR while opening connection #'+err)
245
+ if(this.opened) {
246
+ this.ws!.send(msg)
247
+ this.resetPing()
248
+ }
231
249
 
232
250
  return new Promise((ff, rj) => {
233
251
  this.queue[message_id] = {
@@ -242,10 +260,7 @@ class WebSocketClient {
242
260
  timeout: sett(config.timeout, () => {
243
261
  if(this.queue[message_id]) {
244
262
  this.call('timeout', message_data)
245
- rj({
246
- 'Websocket timeout expired: ': config.timeout,
247
- 'for the message ': message_data
248
- })
263
+ rj({'Websocket timeout expired': config.timeout, 'for the message': message_data})
249
264
  delete queue[message_id]
250
265
  }
251
266
  })
@@ -256,7 +271,6 @@ class WebSocketClient {
256
271
  // TODO: Add .on handlers to config!
257
272
  constructor(user_config: wsc.UserConfig = {}) {
258
273
  this.config = processConfig(user_config)
259
- this.init_flush()
260
274
  if(!this.config.lazy) this.connect()
261
275
  }
262
276
  }
package/src/config.ts CHANGED
@@ -10,6 +10,7 @@ const default_config = <wsc.Config>{
10
10
  url: 'localhost',
11
11
  timeout: 1400,
12
12
  reconnect: 2, // Reconnect timeout in seconds or null.
13
+ reconnection_attempts: Infinity,
13
14
  lazy: false,
14
15
  socket: null,
15
16
  adapter: ((host, protocols) => new WebSocket(host, protocols)),
package/src/types.ts CHANGED
@@ -31,6 +31,7 @@ declare namespace wsc {
31
31
  url: string
32
32
  timeout: number
33
33
  reconnect: number
34
+ reconnection_attempts: number
34
35
  lazy: boolean
35
36
  socket: Socket | null
36
37
  adapter: (host: string, protocols?: string[]) => Socket
@@ -5,7 +5,7 @@ import mockServer from '../mock/server'
5
5
  /** Closes the connenction. */
6
6
  test('close', () => new Promise<void>(async (ff, rj) => {
7
7
  const {port} = await mockServer()
8
- const ws = createNew({}, port)
8
+ const ws = await createNew({}, port)
9
9
 
10
10
  setTimeout(async () => {
11
11
  try {
@@ -6,7 +6,7 @@ import { wait } from 'pepka'
6
6
  /** Rejects messages by timout */
7
7
  test('drops', () => new Promise(async (ff, rj) => {
8
8
  const {port, shutDown} = await mockServer()
9
- const ws = createNew({timeout: 500}, port)
9
+ const ws = await createNew({timeout: 500}, port)
10
10
 
11
11
  await shutDown()
12
12
  await wait(200)
@@ -7,7 +7,7 @@ import { test } from '../suite'
7
7
  test('echo', timeout(5e3, () => new Promise<void>(async (ff, rj) => {
8
8
  const {port} = await mockServer()
9
9
  let to = setTimeout(() => rj('cannot create'), 2e2)
10
- const ws = createNew({}, port)
10
+ const ws = await createNew({}, port)
11
11
  clearTimeout(to)
12
12
 
13
13
  to = setTimeout(() => rj('cannot ready'), 2e2)
@@ -14,29 +14,22 @@ test('existing_socket', () => new Promise(async (ff, rj) => {
14
14
  const to = setTimeout(() => rj(), 4e4)
15
15
  const existing_addr = addr(port)
16
16
 
17
- // This one CANNOT connect as fast as we send to it,
18
- // So readyState is 0.
19
- const ws1 = createNew({socket: new WS(existing_addr)}, port)
20
-
21
- if(ws1.socket?.readyState !== 0) return rj('not ready.')
17
+ const ws1 = await createNew({socket: new WS(existing_addr)}, port)
22
18
 
23
19
  const msg1 = {echo: true, msg: 'existing_socket!'}
24
20
  const response1 = await ws1.send(msg1)
25
21
 
26
- if(
27
- ws1.socket?.readyState as number !== 1
28
- || !equals(response1, msg1)
29
- ) return rj('not ready.')
22
+ if(!equals(response1, msg1)) return rj('Bad echo.')
30
23
  await ws1.close()
31
24
 
32
- // This one DO CAN connect as fast as we send to it,
33
- // So readyState should be 1.
34
25
  const ws2_0 = new WS(existing_addr)
35
26
 
36
27
  ws2_0.addEventListener('open', async () => {
37
- const ws2 = createNew({socket: ws2_0}, port)
28
+ console.log('START')
29
+ const ws2 = await createNew({socket: ws2_0}, port)
30
+ console.log({ws1})
38
31
 
39
- if(ws2.socket?.readyState !== 1) return rj('not ready.')
32
+ if(ws2.socket?.readyState !== 1) return rj('Bad echo.')
40
33
 
41
34
  const msg2 = {echo: true, msg: 'existing_socket!'}
42
35
  const response2 = await ws2.send(msg2)
@@ -7,7 +7,7 @@ import { test } from '../suite.js'
7
7
  test('lazy send before open queued.', () => new Promise(async (ff, rj) => {
8
8
  const {port} = await mockServer()
9
9
  let to = setTimeout(() => rj('cannot create'), 2e2)
10
- const ws = createNew({lazy: true}, port)
10
+ const ws = await createNew({lazy: true}, port)
11
11
  clearTimeout(to)
12
12
 
13
13
  const msg = {echo: true, msg: 'hello!'}
@@ -3,12 +3,11 @@ import mockServer from '../mock/server'
3
3
  import { equals, wait } from 'pepka'
4
4
  import { test } from '../suite'
5
5
 
6
- /** Lazy connect */
6
+ /** Lazy connect. */
7
7
  test('lazy', timeout(2e3, async () => {
8
8
  const {port} = await mockServer()
9
- const ws = createNew({ lazy: true }, port)
9
+ const ws = await createNew({ lazy: true }, port)
10
10
 
11
- await wait(500)
12
11
  if(ws.socket !== null) throw new Error('Socket is not open.')
13
12
  else {
14
13
  const msg = {echo: true, msg: 'hello!'}
@@ -5,7 +5,7 @@ import { test } from '../suite'
5
5
  /** Ready method. */
6
6
  test('ready', timeout(4e3, async () => {
7
7
  const {port} = await mockServer()
8
- const ws = createNew({}, port)
8
+ const ws = await createNew({}, port)
9
9
 
10
10
  await ws.ready()
11
11
  if(!ws.socket) throw new Error()
@@ -6,7 +6,7 @@ import { test } from '../suite'
6
6
  /** Reconnects if connection is broken. */
7
7
  test('reconnect', timeout(1e4, () => async () => {
8
8
  const {port, shutDown} = await mockServer()
9
- const ws = createNew({ reconnect: 1 }, port)
9
+ const ws = await createNew({ reconnect: 1 }, port)
10
10
  await wait(200)
11
11
  await shutDown()
12
12
  await wait(500)
@@ -19,7 +19,7 @@ test('reconnect', timeout(1e4, () => async () => {
19
19
  /** Should send messages that were queued while being disconnected right after the reconnect. */
20
20
  test('reconnect-queue', timeout(1e4, async () => {
21
21
  const {port, shutDown} = await mockServer()
22
- const ws = createNew({ reconnect: 1, timeout: 5e3 }, port)
22
+ const ws = await createNew({ reconnect: 1, timeout: 5e3 }, port)
23
23
  await ws.ready()
24
24
  await shutDown()
25
25
  const msg1 = {echo: true, msg: 'hello!'}
@@ -5,7 +5,7 @@ import { test } from '../suite'
5
5
  /** Socket property check. */
6
6
  test('sockets', timeout(1e4, () => new Promise<void>(async (ff, rj) => {
7
7
  const {port} = await mockServer()
8
- const ws = createNew({}, port)
8
+ const ws = await createNew({}, port)
9
9
 
10
10
  await ws.ready()
11
11
 
package/test/utils.ts CHANGED
@@ -4,12 +4,16 @@ import {AnyFunc, AnyObject} from 'pepka'
4
4
  import { native_ws } from '../src/utils'
5
5
  import WS from 'ws'
6
6
 
7
- export const createNew = (config = {}, port: number) => new WebSocketClient(Object.assign({
7
+ export const createNew = (config = {} as wsc.UserConfig, port: number) => new Promise<WebSocketClient>((ff, rj) => {
8
+ const ws = new WebSocketClient(Object.assign({
8
9
  url: 'ws://127.0.0.1:' + port,
9
10
  // log: (...a: any[]) => console.log(...a),
10
11
  adapter: (host: string, protocols?: string|string[]) => new (native_ws || WS)(host, protocols)
11
- }, config)
12
- )
12
+ }, config))
13
+ if(ws.socket?.readyState===1 || config.lazy) return ff(ws)
14
+ ws.on('error', rj)
15
+ ws.on('open', () => {console.log('OPEN!'); ff(ws)})
16
+ })
13
17
 
14
18
  // Inspired by tinchoz49 https://github.com/lukeed/uvu/issues/33#issuecomment-879870292
15
19
  export const timeout = (time: number, handler: AnyFunc) => async (context: AnyObject) => {