wspromisify 2.5.0 → 2.6.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/dist/bundle.cjs +1 -1
- package/dist/bundle.d.ts +9 -2
- package/dist/bundle.mjs +1 -1
- package/package.json +13 -25
- package/src/WSC.ts +53 -69
- package/src/types.ts +8 -1
- package/src/utils.ts +7 -1
- package/test/index.ts +2 -2
- package/test/mock/WS.ts +1 -0
- package/test/mock/server.ts +2 -1
- package/test/specs/drops.ts +16 -17
- package/test/specs/existing_socket.ts +32 -34
- package/test/specs/lazy.ts +10 -15
- package/test/specs/no-native-throws.ts +1 -1
- package/test/specs/reconnect.ts +36 -18
- package/test/utils.ts +4 -2
- package/tsconfig.json +1 -1
- package/.circleci/config.yml +0 -20
- package/LAST_FEATURES.md +0 -0
- package/_config.yml +0 -1
package/dist/bundle.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const
|
|
1
|
+
"use strict";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 Q=r(((t,e)=>e.map(t))),{floor:z}=Math,C="0123456789abcdefghijklmnopqrstuvwxyz",O=_((t=>Object.fromEntries(t)),Q(((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=C.startsWith(t),this.c2pos=O(t)}zip(t){const{abc:e,abclen:n}=this;let o="";for(;t>0;)o=e[t%n]+o,t=z(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||C+"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),D={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:{}}},U=Symbol("Placeholder"),x=t=>{let e=0;for(const n of t)n!==U&&e++;return e},F=(t,e)=>{const n=t.length,o=t.slice(),s=e.length;let r=s,i=0;for(;r&&i<n;i++)o[i]===U&&(o[i]=e[s-r],r--);for(i=n;r;i++,r--)o[i]=e[s-r];return o},J=(t,e,n)=>{const o=t.length-e.length-x(n);if(o<1)return t(...F(e,n));{const s=(...o)=>J(t,F(e,n),o);return s.$args_left=o,s}},L=t=>(...e)=>t.length>x(e)?J(t,[],e):t(...e);function M(t){return function(e,n){const o=e===U,s=arguments.length;if(1===s&&o)throw new Error("Senseless placeholder usage.");return s>1?o?(t=>function(e){return e===U?t:t(e)})((e=>t(e,n))):t(e,n):n=>t(e,n)}}function G(t){return L(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=L(((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===U?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 $;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 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.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()}this.forcibly_closed=!1})),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(this.ws=n,!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)}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(),this.ws=null,this.forcibly_closed=!0,t(null)})),this.ws.close())}))}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({},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.init_flush(),this.config.lazy||this.connect()}};
|
package/dist/bundle.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ declare namespace wsc {
|
|
|
4
4
|
interface DataObject {
|
|
5
5
|
[key: string]: any;
|
|
6
6
|
}
|
|
7
|
-
type WSEvent = "open" | "message" | "close" | "error";
|
|
7
|
+
type WSEvent = "open" | "message" | "close" | "error" | "timeout";
|
|
8
8
|
/** Minimal socket-like interface. */
|
|
9
9
|
interface Socket {
|
|
10
10
|
readyState: number;
|
|
@@ -47,6 +47,13 @@ declare namespace wsc {
|
|
|
47
47
|
top: any;
|
|
48
48
|
data_type: DataType;
|
|
49
49
|
}
|
|
50
|
+
interface Message {
|
|
51
|
+
msg: any;
|
|
52
|
+
ff(x: any): any;
|
|
53
|
+
data_type: DataType;
|
|
54
|
+
sent_time: number | null;
|
|
55
|
+
timeout: NodeJS.Timeout;
|
|
56
|
+
}
|
|
50
57
|
}
|
|
51
58
|
declare class WebSocketClient {
|
|
52
59
|
private open;
|
|
@@ -54,12 +61,12 @@ declare class WebSocketClient {
|
|
|
54
61
|
private forcibly_closed;
|
|
55
62
|
private reconnect_timeout;
|
|
56
63
|
private queue;
|
|
57
|
-
private messages;
|
|
58
64
|
private onReadyQueue;
|
|
59
65
|
private onCloseQueue;
|
|
60
66
|
private handlers;
|
|
61
67
|
private config;
|
|
62
68
|
private init_flush;
|
|
69
|
+
private call;
|
|
63
70
|
private log;
|
|
64
71
|
private initSocket;
|
|
65
72
|
private connect;
|
package/dist/bundle.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
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 Q=r(((t,e)=>e.map(t))),{floor:z}=Math,C="0123456789abcdefghijklmnopqrstuvwxyz",O=_((t=>Object.fromEntries(t)),Q(((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=C.startsWith(t),this.c2pos=O(t)}zip(t){const{abc:e,abclen:n}=this;let o="";for(;t>0;)o=e[t%n]+o,t=z(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||C+"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),D={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:{}}},U=Symbol("Placeholder"),x=t=>{let e=0;for(const n of t)n!==U&&e++;return e},F=(t,e)=>{const n=t.length,o=t.slice(),s=e.length;let r=s,i=0;for(;r&&i<n;i++)o[i]===U&&(o[i]=e[s-r],r--);for(i=n;r;i++,r--)o[i]=e[s-r];return o},J=(t,e,n)=>{const o=t.length-e.length-x(n);if(o<1)return t(...F(e,n));{const s=(...o)=>J(t,F(e,n),o);return s.$args_left=o,s}},L=t=>(...e)=>t.length>x(e)?J(t,[],e):t(...e);function M(t){return function(e,n){const o=e===U,s=arguments.length;if(1===s&&o)throw new Error("Senseless placeholder usage.");return s>1?o?(t=>function(e){return e===U?t:t(e)})((e=>t(e,n))):t(e,n):n=>t(e,n)}}function G(t){return L(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=L(((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===U?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.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()}this.forcibly_closed=!1})),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(this.ws=n,!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)}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(),this.ws=null,this.forcibly_closed=!0,t(null)})),this.ws.close())}))}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({},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.init_flush(),this.config.lazy||this.connect()}}export{kt as default};
|
package/package.json
CHANGED
|
@@ -41,48 +41,36 @@
|
|
|
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.6.1",
|
|
45
45
|
"type": "module",
|
|
46
46
|
"exports": {
|
|
47
47
|
".": {
|
|
48
|
+
"types": "./dist/bundle.d.ts",
|
|
48
49
|
"import": "./dist/bundle.mjs",
|
|
49
50
|
"require": "./dist/bundle.cjs"
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
"ava": {
|
|
53
|
-
"extensions": {
|
|
54
|
-
"js": true,
|
|
55
|
-
"ts": "module"
|
|
56
51
|
},
|
|
57
|
-
"
|
|
58
|
-
"./test/specs/*.ts"
|
|
59
|
-
],
|
|
60
|
-
"failFast": true,
|
|
61
|
-
"timeout": "2m",
|
|
62
|
-
"nodeArguments": [
|
|
63
|
-
"--import=tsimp"
|
|
64
|
-
]
|
|
52
|
+
"./src": "./src/*"
|
|
65
53
|
},
|
|
66
54
|
"devDependencies": {
|
|
67
|
-
"@rollup/plugin-commonjs": "^28.0.
|
|
68
|
-
"@rollup/plugin-node-resolve": "^16.0.
|
|
55
|
+
"@rollup/plugin-commonjs": "^28.0.3",
|
|
56
|
+
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
69
57
|
"@rollup/plugin-replace": "^6.0.2",
|
|
70
58
|
"@rollup/plugin-terser": "^0.4.4",
|
|
71
|
-
"@types/express": "^5.0.
|
|
72
|
-
"@types/node": "^22.
|
|
73
|
-
"@types/ws": "^8.
|
|
59
|
+
"@types/express": "^5.0.1",
|
|
60
|
+
"@types/node": "^22.15.2",
|
|
61
|
+
"@types/ws": "^8.18.1",
|
|
74
62
|
"codecov": "^3.8.3",
|
|
75
63
|
"cross-env": "^7.0.3",
|
|
76
64
|
"dts-bundle-generator": "^9.5.1",
|
|
77
65
|
"nyc": "^17.1.0",
|
|
78
|
-
"pepka": "^1.6.
|
|
79
|
-
"rollup": "^4.
|
|
66
|
+
"pepka": "^1.6.3",
|
|
67
|
+
"rollup": "^4.40.0",
|
|
80
68
|
"rollup-plugin-typescript2": "^0.36.0",
|
|
81
69
|
"ts-node": "^10.9.2",
|
|
82
|
-
"tsx": "^4.19.
|
|
83
|
-
"typescript": "^5.
|
|
70
|
+
"tsx": "^4.19.3",
|
|
71
|
+
"typescript": "^5.8.3",
|
|
84
72
|
"uvu": "^0.5.6",
|
|
85
|
-
"ws": "^8.18.
|
|
73
|
+
"ws": "^8.18.1"
|
|
86
74
|
},
|
|
87
75
|
"types": "./dist/bundle.d.ts",
|
|
88
76
|
"dependencies": {
|
package/src/WSC.ts
CHANGED
|
@@ -2,28 +2,38 @@ import './types'
|
|
|
2
2
|
import { Zipnum } from 'zipnum'
|
|
3
3
|
import { add_event, sett } from './utils'
|
|
4
4
|
import { processConfig } from './config'
|
|
5
|
-
import { AnyFunc, once, T } from 'pepka'
|
|
5
|
+
import { AnyFunc, F, once, qfilter, T } from 'pepka'
|
|
6
6
|
|
|
7
7
|
const MAX_32 = 2**31 - 1
|
|
8
8
|
const zipnum = new Zipnum()
|
|
9
9
|
|
|
10
|
+
type EventHandler<T extends keyof WebSocketEventMap> = AnyFunc<any, [WebSocketEventMap[T]]>
|
|
11
|
+
type EventHandlers = {
|
|
12
|
+
open: EventHandler<'open'>[]
|
|
13
|
+
close: EventHandler<'close'>[]
|
|
14
|
+
error: EventHandler<'error'>[]
|
|
15
|
+
message: AnyFunc<any, [WebSocketEventMap['message'] & {data: any}]>[]
|
|
16
|
+
timeout: AnyFunc<any, [data: any]>[]
|
|
17
|
+
}
|
|
18
|
+
|
|
10
19
|
class WebSocketClient {
|
|
11
20
|
private open = false
|
|
12
21
|
private ws: wsc.Socket|null = null
|
|
13
22
|
private forcibly_closed = false
|
|
14
23
|
private reconnect_timeout: NodeJS.Timeout|null = null
|
|
15
|
-
private queue = {}
|
|
16
|
-
private messages: any[] = []
|
|
24
|
+
private queue: Record<string, wsc.Message> = {}
|
|
17
25
|
private onReadyQueue: AnyFunc[] = []
|
|
18
26
|
private onCloseQueue: AnyFunc[] = []
|
|
19
|
-
private handlers =
|
|
20
|
-
open: [], close: [], message: [], error: []
|
|
21
|
-
}
|
|
27
|
+
private handlers: EventHandlers = { open: [], close: [], message: [], error: [], timeout: [] }
|
|
22
28
|
private config = <wsc.Config>{}
|
|
23
29
|
|
|
24
30
|
private init_flush(): void {
|
|
25
|
-
|
|
26
|
-
this.
|
|
31
|
+
// TODO: reject them or save somehow ?..
|
|
32
|
+
qfilter(F, this.queue)
|
|
33
|
+
}
|
|
34
|
+
private call(event_name: wsc.WSEvent, ...args: any[]) {
|
|
35
|
+
// this.handlers.open[0]()
|
|
36
|
+
for(const h of this.handlers[event_name]) h(...args)
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
private log(event: string, message: any = null, time: number|null = null): void {
|
|
@@ -40,16 +50,14 @@ class WebSocketClient {
|
|
|
40
50
|
}
|
|
41
51
|
|
|
42
52
|
private initSocket(ws: wsc.Socket) {
|
|
43
|
-
const config = this
|
|
53
|
+
const {queue, config} = this
|
|
44
54
|
this.open = true
|
|
45
55
|
this.onReadyQueue.forEach((fn: Function) => fn())
|
|
46
56
|
this.onReadyQueue.splice(0)
|
|
47
57
|
const {id_key, data_key} = config.server
|
|
48
58
|
// Works also on previously opened sockets that do not fire 'open' event.
|
|
49
|
-
this.
|
|
50
|
-
|
|
51
|
-
this.messages.forEach((message: any) => message.send())
|
|
52
|
-
// It's reconnecting.
|
|
59
|
+
this.call('open', ws)
|
|
60
|
+
for(const msg_id in queue) ws.send(queue[msg_id].msg)
|
|
53
61
|
if(this.reconnect_timeout !== null) {
|
|
54
62
|
clearInterval(this.reconnect_timeout)
|
|
55
63
|
this.reconnect_timeout = null
|
|
@@ -63,9 +71,10 @@ class WebSocketClient {
|
|
|
63
71
|
add_event(ws, 'close', async (...e) => {
|
|
64
72
|
this.log('close')
|
|
65
73
|
this.open = false
|
|
74
|
+
this.ws = null
|
|
66
75
|
this.onCloseQueue.forEach((fn: Function) => fn())
|
|
67
76
|
this.onCloseQueue.splice(0)
|
|
68
|
-
this.
|
|
77
|
+
this.call('close', ...e)
|
|
69
78
|
// Auto reconnect.
|
|
70
79
|
const reconnect = config.reconnect
|
|
71
80
|
if(
|
|
@@ -81,15 +90,11 @@ class WebSocketClient {
|
|
|
81
90
|
}
|
|
82
91
|
// If some error occured, try again.
|
|
83
92
|
const status = await this.connect()
|
|
84
|
-
if(status !== null)
|
|
93
|
+
if(status !== null)
|
|
85
94
|
this.reconnect_timeout = setTimeout(reconnectFunc, reconnect * 1000)
|
|
86
|
-
}
|
|
87
95
|
}
|
|
88
|
-
//
|
|
96
|
+
// TODO: test normal close by server. Would it be infinite ?
|
|
89
97
|
reconnectFunc()
|
|
90
|
-
} else {
|
|
91
|
-
this.ws = null
|
|
92
|
-
this.open = false
|
|
93
98
|
}
|
|
94
99
|
// reset the flag to reuse.
|
|
95
100
|
this.forcibly_closed = false
|
|
@@ -97,7 +102,7 @@ class WebSocketClient {
|
|
|
97
102
|
add_event(ws, 'message', (e) => {
|
|
98
103
|
try {
|
|
99
104
|
const data = config.decode(e.data)
|
|
100
|
-
this.
|
|
105
|
+
this.call('message', {...e, data})
|
|
101
106
|
if(data[id_key]) {
|
|
102
107
|
const q = this.queue[data[id_key]]
|
|
103
108
|
if(q) {
|
|
@@ -106,8 +111,6 @@ class WebSocketClient {
|
|
|
106
111
|
this.log('message', data[data_key], time)
|
|
107
112
|
// Play.
|
|
108
113
|
q.ff(data[data_key])
|
|
109
|
-
clearTimeout(q.timeout)
|
|
110
|
-
delete this.queue[data[id_key]]
|
|
111
114
|
}
|
|
112
115
|
}
|
|
113
116
|
} catch (err) {
|
|
@@ -116,7 +119,7 @@ class WebSocketClient {
|
|
|
116
119
|
})
|
|
117
120
|
}
|
|
118
121
|
|
|
119
|
-
private
|
|
122
|
+
private connect() { // returns status if won't open or null if ok.
|
|
120
123
|
return new Promise((ff) => {
|
|
121
124
|
if(this.open === true) {
|
|
122
125
|
return ff(null)
|
|
@@ -124,48 +127,39 @@ class WebSocketClient {
|
|
|
124
127
|
const config = this.config
|
|
125
128
|
const ws = config.socket || config.adapter(config.url, config.protocols)
|
|
126
129
|
this.ws = ws
|
|
127
|
-
|
|
128
130
|
if(!ws || ws.readyState > 1) {
|
|
129
131
|
this.ws = null
|
|
130
132
|
this.log('error', 'ready() on closing or closed state! status 2.')
|
|
131
133
|
return ff(2)
|
|
132
134
|
}
|
|
133
|
-
|
|
135
|
+
const ffo = once(ff)
|
|
134
136
|
add_event(ws, 'error', once((e) => {
|
|
135
|
-
this.log('error', 'status 3.')
|
|
136
|
-
this.
|
|
137
|
+
this.log('error', 'status 3. Err: '+e.message)
|
|
138
|
+
this.call('error', e)
|
|
137
139
|
this.ws = null
|
|
138
140
|
// Some network error: Connection refused or so.
|
|
139
|
-
|
|
141
|
+
ffo(3)
|
|
140
142
|
}))
|
|
141
143
|
// Because 'open' won't be envoked on opened socket.
|
|
142
144
|
if(ws.readyState) {
|
|
143
145
|
this.initSocket(ws)
|
|
144
|
-
|
|
146
|
+
ffo(null)
|
|
145
147
|
} else {
|
|
146
148
|
add_event(ws, 'open', once(() => {
|
|
147
149
|
this.log('open')
|
|
148
150
|
this.initSocket(ws)
|
|
149
|
-
|
|
151
|
+
ffo(null)
|
|
150
152
|
}))
|
|
151
153
|
}
|
|
152
154
|
})
|
|
153
155
|
}
|
|
154
|
-
|
|
155
|
-
public get socket() {
|
|
156
|
-
return this.ws
|
|
157
|
-
}
|
|
158
|
-
|
|
156
|
+
public get socket() { return this.ws }
|
|
159
157
|
public async ready() {
|
|
160
158
|
return new Promise<void>((ff) => {
|
|
161
|
-
if(this.open)
|
|
162
|
-
|
|
163
|
-
} else {
|
|
164
|
-
this.onReadyQueue.push(ff)
|
|
165
|
-
}
|
|
159
|
+
if(this.open) ff()
|
|
160
|
+
else this.onReadyQueue.push(ff)
|
|
166
161
|
})
|
|
167
162
|
}
|
|
168
|
-
|
|
169
163
|
public on(
|
|
170
164
|
event_name: wsc.WSEvent,
|
|
171
165
|
handler: (data: any) => any,
|
|
@@ -205,10 +199,9 @@ class WebSocketClient {
|
|
|
205
199
|
opts = <wsc.SendOptions>{}
|
|
206
200
|
): Promise<ResponseDataType> {
|
|
207
201
|
this.log('send', message_data)
|
|
208
|
-
const config
|
|
202
|
+
const {config, ws, forcibly_closed, queue} = this
|
|
209
203
|
const message = {}
|
|
210
204
|
const data_key = config.server.data_key
|
|
211
|
-
const first_time_lazy = config.lazy && !this.open
|
|
212
205
|
|
|
213
206
|
const message_id = zipnum.zip((Math.random()*(MAX_32-10))|0)
|
|
214
207
|
if(typeof opts.top === 'object') {
|
|
@@ -217,53 +210,44 @@ class WebSocketClient {
|
|
|
217
210
|
}
|
|
218
211
|
Object.assign(message, opts.top)
|
|
219
212
|
}
|
|
213
|
+
config.pipes.forEach((pipe) => message_data = pipe(message_data))
|
|
220
214
|
|
|
221
|
-
|
|
222
|
-
(pipe) => message_data = pipe(message_data)
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
if(this.open === true) {
|
|
226
|
-
(this.ws as wsc.Socket).send(config.encode(message_id, message_data, config))
|
|
227
|
-
} else if(this.open === false || first_time_lazy) {
|
|
228
|
-
this.messages.push({
|
|
229
|
-
send: () => (this.ws as wsc.Socket).send(config.encode(message_id, message_data, config))
|
|
230
|
-
})
|
|
231
|
-
if(first_time_lazy) {
|
|
232
|
-
this.connect()
|
|
233
|
-
}
|
|
234
|
-
} else if(this.open === null) {
|
|
215
|
+
if(forcibly_closed)
|
|
235
216
|
throw new Error('Attempting to send via closed WebSocket connection!')
|
|
236
|
-
|
|
217
|
+
if(!this.open) this.connect()
|
|
218
|
+
const msg = await config.encode(message_id, message_data, config)
|
|
219
|
+
if(ws?.readyState===1) // The only opened state.
|
|
220
|
+
ws.send(msg)
|
|
237
221
|
|
|
238
222
|
return new Promise((ff, rj) => {
|
|
239
223
|
this.queue[message_id] = {
|
|
240
|
-
ff
|
|
224
|
+
msg, ff(x: any) {
|
|
225
|
+
ff(x)
|
|
226
|
+
// cleanup.
|
|
227
|
+
clearTimeout(this.timeout) // from this object!
|
|
228
|
+
delete queue[message_id]
|
|
229
|
+
},
|
|
241
230
|
data_type: config.data_type,
|
|
242
231
|
sent_time: config.timer ? Date.now() : null,
|
|
243
232
|
timeout: sett(config.timeout, () => {
|
|
244
233
|
if(this.queue[message_id]) {
|
|
234
|
+
this.call('timeout', message_data)
|
|
245
235
|
rj({
|
|
246
236
|
'Websocket timeout expired: ': config.timeout,
|
|
247
237
|
'for the message ': message_data
|
|
248
238
|
})
|
|
249
|
-
delete
|
|
239
|
+
delete queue[message_id]
|
|
250
240
|
}
|
|
251
241
|
})
|
|
252
242
|
}
|
|
253
243
|
})
|
|
254
244
|
}
|
|
255
245
|
|
|
246
|
+
// TODO: Add .on handlers to config!
|
|
256
247
|
constructor(user_config: wsc.UserConfig = {}) {
|
|
257
248
|
this.config = processConfig(user_config)
|
|
258
|
-
// Init.
|
|
259
249
|
this.init_flush()
|
|
260
|
-
|
|
261
|
-
this.open = false
|
|
262
|
-
this.reconnect_timeout = null
|
|
263
|
-
this.forcibly_closed = false
|
|
264
|
-
if(!this.config.lazy) {
|
|
265
|
-
this.connect()
|
|
266
|
-
}
|
|
250
|
+
if(!this.config.lazy) this.connect()
|
|
267
251
|
}
|
|
268
252
|
}
|
|
269
253
|
|
package/src/types.ts
CHANGED
|
@@ -4,7 +4,7 @@ declare namespace wsc {
|
|
|
4
4
|
[key: string]: any
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
export type WSEvent = 'open' | 'message' | 'close' | 'error'
|
|
7
|
+
export type WSEvent = 'open' | 'message' | 'close' | 'error' | 'timeout'
|
|
8
8
|
|
|
9
9
|
/** Minimal socket-like interface. */
|
|
10
10
|
interface Socket {
|
|
@@ -55,4 +55,11 @@ declare namespace wsc {
|
|
|
55
55
|
top: any
|
|
56
56
|
data_type: DataType
|
|
57
57
|
}
|
|
58
|
+
|
|
59
|
+
export interface Message {
|
|
60
|
+
msg: any, ff(x: any): any,
|
|
61
|
+
data_type: DataType,
|
|
62
|
+
sent_time: number | null,
|
|
63
|
+
timeout: NodeJS.Timeout
|
|
64
|
+
}
|
|
58
65
|
}
|
package/src/utils.ts
CHANGED
|
@@ -9,4 +9,10 @@ export const add_event = (o: wsc.Socket, e: string, handler: wsc.EventHandler) =
|
|
|
9
9
|
export const sett = (
|
|
10
10
|
a: number,
|
|
11
11
|
b: { (): void; (...args: any[]): void; }
|
|
12
|
-
) => setTimeout(b, a)
|
|
12
|
+
) => setTimeout(b, a)
|
|
13
|
+
|
|
14
|
+
export const try_splice = <T = any>(arr: T[], el: T) => {
|
|
15
|
+
const i = arr.indexOf(el)
|
|
16
|
+
if(~i) return arr.splice(arr.indexOf(el), 1)
|
|
17
|
+
else return []
|
|
18
|
+
}
|
package/test/index.ts
CHANGED
|
@@ -11,10 +11,10 @@ import './specs/no-native-throws'
|
|
|
11
11
|
import mockServer from './mock/server'
|
|
12
12
|
import {test} from './suite'
|
|
13
13
|
|
|
14
|
-
const {shutDown} = await mockServer()
|
|
14
|
+
const {shutDown, isRunning} = await mockServer()
|
|
15
15
|
test.after(() => {
|
|
16
16
|
setTimeout(async () => {
|
|
17
|
-
await shutDown()
|
|
17
|
+
if(isRunning()) await shutDown()
|
|
18
18
|
process.exit()
|
|
19
19
|
}, 100)
|
|
20
20
|
})
|
package/test/mock/WS.ts
CHANGED
|
@@ -9,6 +9,7 @@ const createServer = (port = 40510) => new Promise<WebSocketServer>((ff, rj) =>
|
|
|
9
9
|
server = new WebSocketServer({ port }, () => {
|
|
10
10
|
server!.on('connection', (socket: WebSocket&{isAlive: boolean}) => {
|
|
11
11
|
socket.on('message', (rawMessage: string) => {
|
|
12
|
+
// console.log({rawMessage: rawMessage.toString()})
|
|
12
13
|
const {id, data} = JSON.parse(rawMessage)
|
|
13
14
|
let response = ''
|
|
14
15
|
if(data.shut) {
|
package/test/mock/server.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import { WebSocketServer } from 'ws'
|
|
3
|
-
import { createServer, killServer } from './WS
|
|
3
|
+
import { createServer, killServer } from './WS'
|
|
4
4
|
|
|
5
5
|
let port: number,
|
|
6
6
|
server: WebSocketServer|null
|
|
@@ -12,6 +12,7 @@ export default async (new_port?: number) => {
|
|
|
12
12
|
}
|
|
13
13
|
return {
|
|
14
14
|
server, port,
|
|
15
|
+
isRunning() {return Boolean(server)},
|
|
15
16
|
shutDown: async () => { await killServer(); server=null }
|
|
16
17
|
}
|
|
17
18
|
}
|
package/test/specs/drops.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createNew } from '../utils'
|
|
2
2
|
import mockServer from '../mock/server'
|
|
3
3
|
import { test } from '../suite'
|
|
4
|
+
import { wait } from 'pepka'
|
|
4
5
|
|
|
5
6
|
/** Rejects messages by timout */
|
|
6
7
|
test('drops', () => new Promise(async (ff, rj) => {
|
|
@@ -8,23 +9,21 @@ test('drops', () => new Promise(async (ff, rj) => {
|
|
|
8
9
|
const ws = createNew({timeout: 500}, port)
|
|
9
10
|
|
|
10
11
|
await shutDown()
|
|
11
|
-
|
|
12
|
+
await wait(200)
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
to = setTimeout(() => {
|
|
18
|
-
return rj()
|
|
19
|
-
}, 600)
|
|
20
|
-
await ws.send(msg)
|
|
21
|
-
if(to) clearTimeout(to)
|
|
22
|
-
await lauchServ()
|
|
14
|
+
const msg = {echo: true, msg: 'hello!'}
|
|
15
|
+
let to: NodeJS.Timeout
|
|
16
|
+
try {
|
|
17
|
+
to = setTimeout(() => {
|
|
23
18
|
return rj()
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
19
|
+
}, 600)
|
|
20
|
+
await ws.send(msg)
|
|
21
|
+
if(to) clearTimeout(to)
|
|
22
|
+
await mockServer(port)
|
|
23
|
+
return rj()
|
|
24
|
+
} catch(e) {
|
|
25
|
+
if(to!) clearTimeout(to)
|
|
26
|
+
await mockServer(port)
|
|
27
|
+
return ff()
|
|
28
|
+
}
|
|
30
29
|
}))
|
|
@@ -9,47 +9,45 @@ import { equals } from 'pepka'
|
|
|
9
9
|
const addr = (port: number) => 'ws://localhost:' + port
|
|
10
10
|
|
|
11
11
|
/** If an existing socket connection is provided via config. */
|
|
12
|
-
test('existing_socket', () => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const existing_addr = addr(port)
|
|
12
|
+
test('existing_socket', () => new Promise(async (ff, rj) => {
|
|
13
|
+
const {port} = await mockServer()
|
|
14
|
+
const to = setTimeout(() => rj(), 4e4)
|
|
15
|
+
const existing_addr = addr(port)
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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)
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
if(ws1.socket?.readyState !== 0) return rj('not ready.')
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const msg1 = {echo: true, msg: 'existing_socket!'}
|
|
24
|
+
const response1 = await ws1.send(msg1)
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
if(
|
|
27
|
+
ws1.socket?.readyState as number !== 1
|
|
28
|
+
|| !equals(response1, msg1)
|
|
29
|
+
) return rj('not ready.')
|
|
30
|
+
await ws1.close()
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
// This one DO CAN connect as fast as we send to it,
|
|
33
|
+
// So readyState should be 1.
|
|
34
|
+
const ws2_0 = new WS(existing_addr)
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
ws2_0.addEventListener('open', async () => {
|
|
37
|
+
const ws2 = createNew({socket: ws2_0}, port)
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
if(ws2.socket?.readyState !== 1) return rj('not ready.')
|
|
40
|
+
|
|
41
|
+
const msg2 = {echo: true, msg: 'existing_socket!'}
|
|
42
|
+
const response2 = await ws2.send(msg2)
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
if(
|
|
45
|
+
ws2.socket?.readyState as number !== 1
|
|
46
|
+
|| !equals(response2, msg2)
|
|
47
|
+
) return rj('not ready.')
|
|
48
|
+
await ws2.close()
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
})
|
|
50
|
+
clearTimeout(to)
|
|
51
|
+
ff()
|
|
54
52
|
})
|
|
55
|
-
})
|
|
53
|
+
}))
|
package/test/specs/lazy.ts
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
import { createNew, timeout } from '../utils'
|
|
2
2
|
import mockServer from '../mock/server'
|
|
3
|
-
import { equals } from 'pepka'
|
|
3
|
+
import { equals, wait } from 'pepka'
|
|
4
4
|
import { test } from '../suite'
|
|
5
5
|
|
|
6
6
|
/** Lazy connect */
|
|
7
|
-
test('lazy', timeout(2e3, () => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const ws = createNew({ lazy: true }, port)
|
|
7
|
+
test('lazy', timeout(2e3, async () => {
|
|
8
|
+
const {port} = await mockServer()
|
|
9
|
+
const ws = createNew({ lazy: true }, port)
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if(equals(response, msg)) ff(); else rj()
|
|
19
|
-
}
|
|
20
|
-
}, 500)
|
|
21
|
-
})
|
|
11
|
+
await wait(500)
|
|
12
|
+
if(ws.socket !== null) throw new Error('Socket is not open.')
|
|
13
|
+
else {
|
|
14
|
+
const msg = {echo: true, msg: 'hello!'}
|
|
15
|
+
if(!equals(await ws.send(msg), msg)) throw new Error('msg\s not equal.')
|
|
16
|
+
}
|
|
22
17
|
}))
|
package/test/specs/reconnect.ts
CHANGED
|
@@ -1,23 +1,41 @@
|
|
|
1
1
|
import { createNew, timeout } from '../utils'
|
|
2
2
|
import mockServer from '../mock/server'
|
|
3
|
-
import { equals } from 'pepka'
|
|
3
|
+
import { equals, wait } from 'pepka'
|
|
4
4
|
import { test } from '../suite'
|
|
5
5
|
|
|
6
6
|
/** Reconnects if connection is broken. */
|
|
7
|
-
test('reconnect', timeout(1e4, () =>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
})
|
|
23
|
-
)
|
|
7
|
+
test('reconnect', timeout(1e4, () => async () => {
|
|
8
|
+
const {port, shutDown} = await mockServer()
|
|
9
|
+
const ws = createNew({ reconnect: 1 }, port)
|
|
10
|
+
await wait(200)
|
|
11
|
+
await shutDown()
|
|
12
|
+
await wait(500)
|
|
13
|
+
await mockServer(port)
|
|
14
|
+
await wait(600)
|
|
15
|
+
const msg = {echo: true, msg: 'hello!'}
|
|
16
|
+
const response = await ws.send(msg)
|
|
17
|
+
if(!equals(response, msg)) throw new Error('not equals.')
|
|
18
|
+
}))
|
|
19
|
+
/** Should send messages that were queued while being disconnected right after the reconnect. */
|
|
20
|
+
test('reconnect-queue', timeout(1e4, async () => {
|
|
21
|
+
const {port, shutDown} = await mockServer()
|
|
22
|
+
const ws = createNew({ reconnect: 1, timeout: 5e3 }, port)
|
|
23
|
+
await ws.ready()
|
|
24
|
+
await shutDown()
|
|
25
|
+
const msg1 = {echo: true, msg: 'hello!'}
|
|
26
|
+
const msg2 = {echo: true, msg: 'hello 2!'}
|
|
27
|
+
const msg3 = {echo: true, msg: 'hello 3!'}
|
|
28
|
+
let ok = false
|
|
29
|
+
const p = new Promise<void>((ff) => ws.send(msg1).then((res) => {
|
|
30
|
+
if(!equals(res, msg1)) throw new Error('msg1 not equals.')
|
|
31
|
+
else ws.send(msg2).then((res) => {
|
|
32
|
+
if(!equals(res, msg2)) throw new Error('msg2 not equals.')
|
|
33
|
+
else {ok=true}
|
|
34
|
+
ff()
|
|
35
|
+
})
|
|
36
|
+
}))
|
|
37
|
+
await mockServer(port)
|
|
38
|
+
await p
|
|
39
|
+
if(!ok) throw new Error('Not sent.')
|
|
40
|
+
if(!equals(await ws.send(msg3), msg3)) throw new Error('not sent or msg3 not equals.')
|
|
41
|
+
}))
|
package/test/utils.ts
CHANGED
|
@@ -6,7 +6,7 @@ import WS from 'ws'
|
|
|
6
6
|
|
|
7
7
|
export const createNew = (config = {}, port: number) => new WebSocketClient(Object.assign({
|
|
8
8
|
url: 'ws://127.0.0.1:' + port,
|
|
9
|
-
// log: (...a) => console.log(...a),
|
|
9
|
+
// log: (...a: any[]) => console.log(...a),
|
|
10
10
|
adapter: (host: string, protocols?: string|string[]) => new (native_ws || WS)(host, protocols)
|
|
11
11
|
}, config)
|
|
12
12
|
)
|
|
@@ -15,12 +15,14 @@ export const createNew = (config = {}, port: number) => new WebSocketClient(Obje
|
|
|
15
15
|
export const timeout = (time: number, handler: AnyFunc) => async (context: AnyObject) => {
|
|
16
16
|
let timer: NodeJS.Timeout
|
|
17
17
|
try {
|
|
18
|
-
|
|
18
|
+
await Promise.race([
|
|
19
19
|
handler(context),
|
|
20
20
|
new Promise((_resolve, reject) =>
|
|
21
21
|
timer = setTimeout(() => reject(new Error('timeout')), time)
|
|
22
22
|
)
|
|
23
23
|
])
|
|
24
|
+
} catch(e) {
|
|
25
|
+
throw e
|
|
24
26
|
} finally {
|
|
25
27
|
if(timer!) clearTimeout(timer)
|
|
26
28
|
}
|
package/tsconfig.json
CHANGED
package/.circleci/config.yml
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
version: 2
|
|
3
|
-
|
|
4
|
-
jobs:
|
|
5
|
-
build:
|
|
6
|
-
working_directory: ~/test
|
|
7
|
-
docker:
|
|
8
|
-
- image: circleci/node
|
|
9
|
-
steps:
|
|
10
|
-
- checkout
|
|
11
|
-
- run:
|
|
12
|
-
name: install
|
|
13
|
-
command: npm i
|
|
14
|
-
- run:
|
|
15
|
-
name: npm test
|
|
16
|
-
command: |
|
|
17
|
-
nodejs -v
|
|
18
|
-
npm -v
|
|
19
|
-
npm run lint
|
|
20
|
-
npm run test:report
|
package/LAST_FEATURES.md
DELETED
|
File without changes
|
package/_config.yml
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
theme: jekyll-theme-cayman
|