wspromisify 2.6.4 → 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 +1 -1
- package/dist/bundle.d.ts +6 -1
- package/dist/bundle.mjs +1 -1
- package/package.json +9 -9
- package/src/WSC.ts +61 -47
- package/src/config.ts +1 -0
- package/src/types.ts +1 -0
- package/test/specs/close.ts +1 -1
- package/test/specs/drops.ts +1 -1
- package/test/specs/echo.ts +1 -1
- package/test/specs/existing-socket.ts +6 -13
- package/test/specs/lazy-send-before-open.ts +1 -1
- package/test/specs/lazy.ts +2 -3
- package/test/specs/ready.ts +1 -1
- package/test/specs/reconnect.ts +2 -2
- package/test/specs/socket.ts +1 -1
- package/test/utils.ts +7 -3
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,
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
60
|
-
"@types/node": "^
|
|
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": "^
|
|
63
|
+
"cross-env": "^10.0.0",
|
|
64
64
|
"dts-bundle-generator": "^9.5.1",
|
|
65
65
|
"nyc": "^17.1.0",
|
|
66
|
-
"rollup": "^4.
|
|
66
|
+
"rollup": "^4.46.0",
|
|
67
67
|
"rollup-plugin-typescript2": "^0.36.0",
|
|
68
68
|
"ts-node": "^10.9.2",
|
|
69
|
-
"tsx": "^4.
|
|
69
|
+
"tsx": "^4.20.3",
|
|
70
70
|
"typescript": "^5.8.3",
|
|
71
71
|
"uvu": "^0.5.6",
|
|
72
|
-
"ws": "^8.18.
|
|
72
|
+
"ws": "^8.18.3"
|
|
73
73
|
},
|
|
74
74
|
"types": "./dist/bundle.d.ts",
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"pepka": "^1.6.
|
|
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
|
-
|
|
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(
|
|
86
|
+
this.onCloseQueue.forEach(callit)
|
|
77
87
|
this.onCloseQueue.splice(0)
|
|
78
88
|
this.call('close', ...e)
|
|
79
89
|
// Auto reconnect.
|
|
80
|
-
|
|
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
|
|
89
|
-
this.ws
|
|
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
|
|
95
|
-
this.reconnect_timeout = setTimeout(reconnectFunc, reconnect
|
|
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
|
|
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.
|
|
126
|
-
|
|
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.
|
|
166
|
+
if(this.opened) ff()
|
|
160
167
|
else this.onReadyQueue.push(ff)
|
|
161
168
|
})
|
|
162
169
|
}
|
|
@@ -188,18 +195,24 @@ 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
|
-
this.ws = null
|
|
195
|
-
this.forcibly_closed = true
|
|
196
200
|
ff(null)
|
|
197
201
|
})
|
|
198
202
|
this.ws.close()
|
|
203
|
+
this.ws = null
|
|
204
|
+
this.forcibly_closed = true
|
|
199
205
|
}
|
|
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,
|
|
213
|
-
const message
|
|
214
|
-
const data_key = config
|
|
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
|
-
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
package/test/specs/close.ts
CHANGED
|
@@ -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 {
|
package/test/specs/drops.ts
CHANGED
|
@@ -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)
|
package/test/specs/echo.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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('
|
|
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!'}
|
package/test/specs/lazy.ts
CHANGED
|
@@ -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!'}
|
package/test/specs/ready.ts
CHANGED
package/test/specs/reconnect.ts
CHANGED
|
@@ -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!'}
|
package/test/specs/socket.ts
CHANGED
|
@@ -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(
|
|
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) => {
|