wspromisify 2.6.5 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- 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 +62 -48
- 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/README.md
CHANGED
|
@@ -61,6 +61,8 @@ Default constructor config is
|
|
|
61
61
|
timeout: 1400,
|
|
62
62
|
// Reconnect timeout in seconds or null.
|
|
63
63
|
reconnect: 2,
|
|
64
|
+
// Attempts before silently givin' up.
|
|
65
|
+
reconnection_attempts: 25 // defaults to Infinity.
|
|
64
66
|
// Lazy connect: connects only if something sent (then sends all of them!)
|
|
65
67
|
lazy: false,
|
|
66
68
|
// Existing socket if you already have one to augment with this force.
|
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}`)}this.resetPing()}))}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){clearTimeout(this.timeout),delete o[l],e(t)},data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:x(n.timeout,(()=>{l in this.queue&&(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}`)}this.resetPing()}))}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){clearTimeout(this.timeout),delete o[l],e(t)},data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:x(n.timeout,(()=>{l in this.queue&&(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.1",
|
|
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 // Makes the flag useless.
|
|
102
108
|
})
|
|
103
109
|
add_event(ws, 'message', (e) => {
|
|
104
110
|
try {
|
|
@@ -117,26 +123,28 @@ class WebSocketClient {
|
|
|
117
123
|
} catch (err) {
|
|
118
124
|
console.error(err, `WSP: Decode error. Got: ${e.data}`)
|
|
119
125
|
}
|
|
126
|
+
this.resetPing()
|
|
120
127
|
})
|
|
121
128
|
}
|
|
122
129
|
|
|
130
|
+
private opening = false
|
|
123
131
|
private connect() { // returns status if won't open or null if ok.
|
|
124
|
-
return new Promise((ff) => {
|
|
125
|
-
if(this.
|
|
126
|
-
|
|
127
|
-
}
|
|
132
|
+
return new Promise<null|number>((ff) => {
|
|
133
|
+
if(this.opened||this.opening) return ff(null)
|
|
134
|
+
this.opening = true
|
|
128
135
|
const config = this.config
|
|
129
136
|
const ws = config.socket || config.adapter(config.url, config.protocols)
|
|
130
137
|
if(!ws || ws.readyState > 1) {
|
|
138
|
+
this.opening = false
|
|
131
139
|
this.ws = null
|
|
132
140
|
this.log('error', 'ready() on closing or closed state! status 2.')
|
|
133
141
|
return ff(2)
|
|
134
142
|
}
|
|
135
|
-
const ffo = once(ff)
|
|
143
|
+
const ffo = once((s: null|number) => {this.opening=false; ff(s)})
|
|
136
144
|
add_event(ws, 'error', once((e) => {
|
|
145
|
+
this.ws = null
|
|
137
146
|
this.log('error', 'status 3. Err: '+e.message)
|
|
138
147
|
this.call('error', e)
|
|
139
|
-
this.ws = null
|
|
140
148
|
// Some network error: Connection refused or so.
|
|
141
149
|
ffo(3)
|
|
142
150
|
}))
|
|
@@ -156,7 +164,7 @@ class WebSocketClient {
|
|
|
156
164
|
public get socket() { return this.ws }
|
|
157
165
|
public async ready() {
|
|
158
166
|
return new Promise<void>((ff) => {
|
|
159
|
-
if(this.
|
|
167
|
+
if(this.opened) ff()
|
|
160
168
|
else this.onReadyQueue.push(ff)
|
|
161
169
|
})
|
|
162
170
|
}
|
|
@@ -188,7 +196,6 @@ class WebSocketClient {
|
|
|
188
196
|
if(this.ws === null) {
|
|
189
197
|
rj('WSP: closing a non-inited socket!')
|
|
190
198
|
} else {
|
|
191
|
-
this.open = false
|
|
192
199
|
this.onCloseQueue.push(() => {
|
|
193
200
|
this.init_flush()
|
|
194
201
|
ff(null)
|
|
@@ -200,6 +207,13 @@ class WebSocketClient {
|
|
|
200
207
|
})
|
|
201
208
|
}
|
|
202
209
|
|
|
210
|
+
public open() {
|
|
211
|
+
if(!this.opened) {
|
|
212
|
+
this.forcibly_closed = false
|
|
213
|
+
return this.connect()
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
203
217
|
/** .send(your_data) wraps request to server with {id: `hash`, data: `actually your data`},
|
|
204
218
|
returns a Promise that will be rejected after a timeout or
|
|
205
219
|
resolved if server returns the same signature: {id: `same_hash`, data: `response data`}.
|
|
@@ -209,9 +223,9 @@ class WebSocketClient {
|
|
|
209
223
|
opts = <wsc.SendOptions>{}
|
|
210
224
|
): Promise<ResponseDataType> {
|
|
211
225
|
this.log('send', message_data)
|
|
212
|
-
const {config,
|
|
213
|
-
const message
|
|
214
|
-
const data_key = config
|
|
226
|
+
const {config, forcibly_closed, queue} = this
|
|
227
|
+
const message = {}
|
|
228
|
+
const {pipes, server: {data_key}} = config
|
|
215
229
|
|
|
216
230
|
const message_id = zipnum.zip((Math.random()*(MAX_32-10))|0)
|
|
217
231
|
if(typeof opts.top === 'object') {
|
|
@@ -220,32 +234,33 @@ class WebSocketClient {
|
|
|
220
234
|
}
|
|
221
235
|
Object.assign(message, opts.top)
|
|
222
236
|
}
|
|
223
|
-
|
|
237
|
+
for(const pipe of pipes) message_data = pipe(message_data)
|
|
224
238
|
|
|
225
239
|
if(forcibly_closed)
|
|
226
240
|
throw new Error('Attempting to send via closed WebSocket connection!')
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
241
|
+
const [msg, err] = await Promise.all([
|
|
242
|
+
config.encode(message_id, message_data, config),
|
|
243
|
+
this.connect()
|
|
244
|
+
])
|
|
245
|
+
if(err) throw new Error('ERR while opening connection #'+err)
|
|
246
|
+
if(this.opened) {
|
|
247
|
+
this.ws!.send(msg)
|
|
248
|
+
this.resetPing()
|
|
249
|
+
}
|
|
231
250
|
|
|
232
251
|
return new Promise((ff, rj) => {
|
|
233
252
|
this.queue[message_id] = {
|
|
234
253
|
msg, ff(x: any) {
|
|
235
|
-
ff(x)
|
|
236
|
-
// cleanup.
|
|
237
254
|
clearTimeout(this.timeout) // from this object!
|
|
238
255
|
delete queue[message_id]
|
|
256
|
+
ff(x)
|
|
239
257
|
},
|
|
240
258
|
data_type: config.data_type,
|
|
241
259
|
sent_time: config.timer ? Date.now() : null,
|
|
242
260
|
timeout: sett(config.timeout, () => {
|
|
243
|
-
if(this.queue
|
|
261
|
+
if(message_id in this.queue) {
|
|
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) => {
|