wspromisify 2.4.4 → 2.6.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.
Files changed (45) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1 -2
  3. package/TODO +9 -0
  4. package/dist/bundle.cjs +1 -1
  5. package/dist/bundle.d.ts +9 -3
  6. package/dist/bundle.mjs +1 -1
  7. package/package.json +79 -94
  8. package/rollup.config.js +1 -1
  9. package/src/WSC.ts +266 -0
  10. package/src/config.ts +7 -6
  11. package/src/types.ts +1 -1
  12. package/src/utils.ts +5 -23
  13. package/test/index.ts +21 -0
  14. package/test/mock/WS.ts +43 -0
  15. package/test/mock/server.ts +17 -0
  16. package/test/specs/close.ts +18 -0
  17. package/test/specs/drops.ts +30 -0
  18. package/test/specs/echo.ts +24 -0
  19. package/test/specs/encode-decode.ts +1 -0
  20. package/test/specs/existing_socket.ts +55 -0
  21. package/test/specs/lazy.ts +22 -0
  22. package/test/specs/lazySendBeforeOpen.ts +19 -0
  23. package/test/specs/no-native-throws.ts +18 -0
  24. package/test/specs/ready.ts +12 -0
  25. package/test/specs/reconnect.ts +23 -0
  26. package/test/specs/socket.ts +13 -0
  27. package/test/suite.ts +3 -0
  28. package/test/utils.ts +27 -0
  29. package/tsconfig.json +3 -2
  30. package/src/WS.ts +0 -166
  31. package/src/connectLib.ts +0 -120
  32. package/test/mock/WS.js +0 -51
  33. package/test/mock/index.js +0 -52
  34. package/test/specs/close.js +0 -25
  35. package/test/specs/drops.js +0 -29
  36. package/test/specs/echo.js +0 -26
  37. package/test/specs/encode-decode.js +0 -3
  38. package/test/specs/existing_socket.js +0 -55
  39. package/test/specs/lazy.js +0 -26
  40. package/test/specs/ready.js +0 -16
  41. package/test/specs/reconnect.js +0 -28
  42. package/test/specs/sendBeforeOpen.js +0 -23
  43. package/test/specs/socket.js +0 -22
  44. package/test/specs/utils_once.js +0 -16
  45. package/test/utils.js +0 -27
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2017 Michael
3
+ Copyright (c) 2025 Michael Akiliev
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
  # WebsocketPromisify
2
-
3
- [![Build Status](https://circleci.com/gh/houd1ni/WebsocketPromisify/tree/master.svg?style=shield)](https://circleci.com/gh/houd1ni/WebsocketPromisify/tree/master) [![codecov](https://codecov.io/gh/houd1ni/WebsocketPromisify/branch/master/graph/badge.svg)](https://codecov.io/gh/houd1ni/WebsocketPromisify) [![bundlephobia](https://badgen.net/bundlephobia/minzip/wspromisify)](https://bundlephobia.com/result?p=wspromisify) [![npm](https://badgen.net/npm/v/wspromisify)](https://www.npmjs.com/package/wspromisify) [![Deps](https://david-dm.org/houd1ni/WebsocketPromisify.svg)](https://david-dm.org/houd1ni/WebsocketPromisify) [![DevDeps](https://david-dm.org/houd1ni/WebsocketPromisify/dev-status.svg)](https://david-dm.org/houd1ni/WebsocketPromisify)
2
+ [![Scrutinizer build](https://scrutinizer-ci.com/g/houd1ni/WebsocketPromisify/badges/build.png?b=master)](https://scrutinizer-ci.com/g/houd1ni/WebsocketPromisify/build-status/master) [![Scrutinizer code quality](https://scrutinizer-ci.com/g/houd1ni/WebsocketPromisify/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/houd1ni/WebsocketPromisify/code-structure/master) [![codecov](https://codecov.io/gh/houd1ni/WebsocketPromisify/branch/master/graph/badge.svg)](https://codecov.io/gh/houd1ni/WebsocketPromisify) [![bundlejs](https://deno.bundlejs.com/badge?q=wspromisify@2.4.4&treeshake=[*])](https://deno.bundlejs.com/badge?q=wspromisify@2.4.4&treeshake=[*]) [![npm](https://badgen.net/npm/v/wspromisify)](https://www.npmjs.com/package/wspromisify)
4
3
 
5
4
  A nice-looking this readme version: https://houd1ni.github.io/WebsocketPromisify/
6
5
 
package/TODO CHANGED
@@ -0,0 +1,9 @@
1
+ #v3 at least:
2
+
3
+ [The milestone](https://github.com/houd1ni/WebsocketPromisify/milestone/1)
4
+
5
+ - [x] [close event bug](https://github.com/houd1ni/WebsocketPromisify/issues/38) (fixed in 2.5.0)
6
+ - [ ] [BREAKING] export named WebSocketClient instead of default export.
7
+ - [ ] [ping-pong without id](https://github.com/houd1ni/WebsocketPromisify/issues/39)
8
+ - [ ] [implement prepare](https://github.com/houd1ni/WebsocketPromisify/issues/34)
9
+ - [ ] [wiki](https://github.com/houd1ni/WebsocketPromisify/issues/4)
package/dist/bundle.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";const e=Symbol("Placeholder"),t=t=>{let n=0;for(const s of t)s!==e&&n++;return n},n=(t,n)=>{const s=t.length,o=t.slice(),r=n.length;let i=r,c=0;for(;i&&c<s;c++)o[c]===e&&(o[c]=n[r-i],i--);for(c=s;i;c++,i--)o[c]=n[r-i];return o},s=(e,o,r)=>{const i=e.length-o.length-t(r);if(i<1)return e(...n(o,r));{const t=(...t)=>s(e,n(o,r),t);return t.$args_left=i,t}},o=e=>(...n)=>e.length>t(n)?s(e,[],n):e(...n),r=t=>function(n){return n===e?t:t(n)};function i(t){return function(n,s){const o=n===e,i=arguments.length;if(1===i&&o)throw new Error("Senseless placeholder usage.");return arguments.length>1?o?r((e=>t(e,s))):t(n,s):e=>t(n,e)}}function c(e){return o(e)}const l=void 0,u=1/0,a=e=>typeof e,h=e=>null===e,f={u:"U",b:"B",n:"N",s:"S",f:"F"},d=e=>{const t=a(e);return"object"===t?h(e)?"Null":e.constructor.name:f[t[0]]+t.slice(1)},g=i(((e,t)=>(t.push(e),t))),p=c(((e,t,n)=>n.reduce(e,t))),m=c(((e,t,n)=>{for(let s in n)switch(d(n[s])){case"Array":if(e>1&&"Array"===d(t[s]))switch(e){case 2:const o=t[s],r=n[s];for(const t in r)o[t]?m(e,o[t],r[t]):o[t]=r[t];break;case 3:t[s].push(...n[s])}else t[s]=n[s];break;case"Object":if("Object"===d(t[s])){m(e,t[s],n[s]);break}default:t[s]=n[s]}return t}));m(1),m(2),m(3);const y=i(((e,t)=>{const n=d(e);if(n===d(t)&&("Object"===n||"Array"==n)){if(h(e)||h(t))return e===t;if(e===t)return!0;for(const n of[e,t])for(const s in n)if(!(n===t&&s in e||n===e&&s in t&&y(e[s],t[s])))return!1;return!0}return e===t})),w=o(((e,t,n,s)=>e(s)?t(s):n(s))),b=(...t)=>(...n)=>{let s,o=!0;for(let r=j(t)-1;r>-1;r--)o?(o=!1,s=t[r](...n)):s=s===e?t[r]():t[r](s);return s},_=i(((e,t)=>t[e])),k=i(((e,t)=>{if((e=>"string"===a(e))(t))return t.includes(e);for(const n of t)if(y(n,e))return!0;return!1})),S=c(((e,t,n)=>n.slice(e,(e=>"number"==a(e))(t)?t:u))),v=_(0);S(1,u);const E=e=>h(e)||(e=>e===l)(e),j=e=>e.length,N=e=>()=>e,O=i(((e,t)=>t.split(e))),A=e=>p(((e,t)=>k(t,e)?e:g(t,e)),[],e),P=c(((e,t,n)=>({...n,[e]:t}))),q=i(((e,t)=>t[e])),Q=c(((e,t,n)=>w(j,(()=>E(n)?e:b(w(E,N(e),(n=>Q(e,S(1,u,t),n))),(e=>i(((t,n)=>e(n,t))))(q)(n),v)(t)),N(n),t)));Q(l);const $=/^(.*?)(8|16|32|64)(Clamped)?Array$/,W=(e,t=!1)=>{const n=d(e);switch(n){case"Null":case"String":case"Number":case"Boolean":case"Symbol":return e;case"Array":return t?[...e]:R(b(W,((...e)=>e[0])),e);case"Object":if(t)return{...e};const s={};for(let t in e)s[t]=W(e[t]);return s;default:return $.test(n)?e.constructor.from(e):e}},C=c(((e,t,n)=>p(e,W(t),n))),R=i(((e,t)=>t.map(e))),{floor:z}=Math;let B,D;const I=b((e=>C(((e,t)=>P(...t,e)),{},e)),R(((e,t)=>[e,t])),O(""));(e=>{if(!(e=>b(y(j(e)),j,A,O(""))(e))(e))throw new Error("Not all chars are unique!");B=e,D=B.length,I(B)})("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");const T=(e,t,n)=>e.addEventListener(t,n),U=e=>{let t=!1,n=null;return(...s)=>t?n:(t=!0,n=e(...s))},x=(e,t)=>setTimeout(t,e),F=function(e){const t=this.config;this.open=!0,this.onReadyQueue.forEach((e=>e())),this.onReadyQueue.splice(0);const{id_key:n,data_key:s}=t.server;if(this.handlers.open.forEach((e=>e())),this.messages.forEach((e=>e.send())),null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),t.ping){const e=setInterval((()=>{this.open&&this.send(t.ping.content),this.forcibly_closed&&clearInterval(e)}),1e3*t.ping.interval)}T(e,"close",(async()=>{this.log("close"),this.open=!1,this.onCloseQueue.forEach((e=>e())),this.onCloseQueue=[];const e=t.reconnect;if("number"!=typeof e||isNaN(e)||this.forcibly_closed)this.ws=null,this.open=null;else{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})),T(e,"message",(e=>{try{const o=t.decode(e.data);if(this.handlers.message.forEach((t=>t({...e,data:o}))),o[n]){const e=this.queue[o[n]];if(e){const t=e.sent_time?Date.now()-e.sent_time:null;this.log("message",o[s],t),e.ff(o[s]),clearTimeout(e.timeout),delete this.queue[o[n]]}}}catch(t){console.error(t,`WSP: Decode error. Got: ${e.data}`)}}))};var J=function(e){if(!0===this.open)return e(null);const t=this.config,n=t.socket||t.adapter(t.url,t.protocols);if(this.ws=n,!n||n.readyState>1)return this.ws=null,this.log("error","ready() on closing or closed state! status 2."),e(2);T(n,"error",U((t=>(this.log("error","status 3."),this.handlers.error.forEach((e=>e(t))),this.ws=null,e(3))))),n.readyState?(F.call(this,n),e(null)):T(n,"open",U((()=>(this.log("open"),F.call(this,n),e(null)))))};const L={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,lazy:!1,socket:null,adapter:(e,t)=>new WebSocket(e,t),encode:(e,t,{server:n})=>JSON.stringify({[n.id_key]:e,[n.data_key]:t}),decode:e=>JSON.parse(e),protocols:[],pipes:[],server:{id_key:"id",data_key:"data"},ping:{interval:55,content:{}}},M=Symbol("Placeholder"),G=e=>{let t=0;for(const n of e)n!==M&&t++;return t},H=(e,t)=>{const n=e.length,s=e.slice(),o=t.length;let r=o,i=0;for(;r&&i<n;i++)s[i]===M&&(s[i]=t[o-r],r--);for(i=n;r;i++,r--)s[i]=t[o-r];return s},K=(e,t,n)=>{const s=e.length-t.length-G(n);if(s<1)return e(...H(t,n));{const o=(...s)=>K(e,H(t,n),s);return o.$args_left=s,o}},V=e=>(...t)=>e.length>G(t)?K(e,[],t):e(...t),X=e=>function(t){return t===M?e:e(t)};function Y(e){return function(t,n){const s=t===M,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return arguments.length>1?s?X((t=>e(t,n))):e(t,n):n=>e(t,n)}}function Z(e){return V(e)}const ee=void 0,te=1/0,ne=e=>typeof e,se=e=>null===e,oe={u:"U",b:"B",n:"N",s:"S",f:"F"},re=e=>{const t=ne(e);return"object"===t?se(e)?"Null":e.constructor.name:oe[t[0]]+t.slice(1)},ie=Z(((e,t,n)=>{for(let s in n)switch(re(n[s])){case"Array":if(e>1&&"Array"===re(t[s]))switch(e){case 2:const o=t[s],r=n[s];for(const t in r)o[t]?ie(e,o[t],r[t]):o[t]=r[t];break;case 3:t[s].push(...n[s])}else t[s]=n[s];break;case"Object":if("Object"===re(t[s])){ie(e,t[s],n[s]);break}default:t[s]=n[s]}return t}));ie(1),ie(2),ie(3);const ce=V(((e,t,n,s)=>e(s)?t(s):n(s))),le=(...e)=>(...t)=>{let n,s=!0;for(let o=de(e)-1;o>-1;o--)s?(s=!1,n=e[o](...t)):n=n===M?e[o]():e[o](n);return n},ue=Y(((e,t)=>t[e])),ae=Z(((e,t,n)=>n.slice(e,(e=>"number"==ne(e))(t)?t:te))),he=ue(0);ae(1,te);const fe=e=>se(e)||(e=>e===ee)(e),de=e=>e.length,ge=e=>()=>e,pe=ge(!0),me=Y(((e,t)=>t[e])),ye=Z(((e,t,n)=>ce(de,(()=>fe(n)?e:le(ce(fe,ge(e),(n=>ye(e,ae(1,te,t),n))),(e=>Y(((t,n)=>e(n,t))))(me)(n),he)(t)),ge(n),t)));ye(ee);module.exports=class{open=!1;ws=null;forcibly_closed=!1;reconnect_timeout=null;queue={};messages=[];onReadyQueue=[];onCloseQueue=[];handlers={open:[],message:[],close:[],error:[]};config={};init_flush(){this.queue={},this.messages=[]}log(e,t=null,n=null){const s=this.config;null!==n?s.log(e,n,t):s.timer?s.log(e,null,t):s.log(e,t)}async connect(){return new Promise((e=>{J.call(this,e)}))}get socket(){return this.ws}async ready(){return new Promise((e=>{this.open?e():this.onReadyQueue.push(e)}))}on(e,t,n=pe,s=!1){const o=e=>n(e)&&t(e);return s?T(this.ws,e,o):this.handlers[e].push(o)}async close(){return new Promise(((e,t)=>{null===this.ws?t("WSP: closing a non-inited socket!"):(this.open=!1,this.onCloseQueue.push((()=>{this.init_flush(),this.ws=null,this.forcibly_closed=!0,e(null)})),this.ws.close())}))}async send(e,t={}){this.log("send",e);const n=this.config,s={},o=n.server.data_key,r=n.lazy&&!this.open,i=(e=>{let t="";for(;e>0;)t=B[e%D]+t,e=z(e/D);return t||"0"})(2147483637*Math.random()|0);if("object"==typeof t.top){if(t.top[o])throw new Error("Attempting to set data key/token via send() options!");Object.assign(s,t.top)}if(n.pipes.forEach((t=>e=t(e))),!0===this.open)this.ws.send(n.encode(i,e,n));else if(!1===this.open||r)this.messages.push({send:()=>this.ws.send(n.encode(i,e,n))}),r&&this.connect();else if(null===this.open)throw new Error("Attempting to send via closed WebSocket connection!");return new Promise(((t,s)=>{this.queue[i]={ff:t,data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:x(n.timeout,(()=>{this.queue[i]&&(s({"Websocket timeout expired: ":n.timeout,"for the message ":e}),delete this.queue[i])}))}}))}constructor(e={}){this.config=(e=>{const t=Object.assign({},L,e),n=t.url;if("/"==n[0])try{const e=location.protocol.includes("s:")?"wss":"ws";t.url=`${e}://${location.hostname}:${location.port}${n}`}catch(e){throw new Error("WSP: URL starting with / in non-browser environment!")}return t})(e),this.init_flush(),this.open=!1,this.reconnect_timeout=null,this.forcibly_closed=!1,this.config.lazy||this.connect()}};
1
+ "use strict";const e=Symbol("Placeholder"),t=t=>{let n=0;for(const s of t)s!==e&&n++;return n},n=(t,n)=>{const s=t.length,o=t.slice(),r=n.length;let i=r,l=0;for(;i&&l<s;l++)o[l]===e&&(o[l]=n[r-i],i--);for(l=s;i;l++,i--)o[l]=n[r-i];return o},s=(e,o,r)=>{const i=e.length-o.length-t(r);if(i<1)return e(...n(o,r));{const t=(...t)=>s(e,n(o,r),t);return t.$args_left=i,t}},o=e=>(...n)=>e.length>t(n)?s(e,[],n):e(...n);function r(t){return function(n,s){const o=n===e,r=arguments.length;if(1===r&&o)throw new Error("Senseless placeholder usage.");return r>1?o?(t=>function(n){return n===e?t:t(n)})((e=>t(e,s))):t(n,s):e=>t(n,e)}}function i(e){return o(e)}const l=void 0,c=1/0,u=e=>typeof e,a=e=>null===e,h={u:"U",b:"B",n:"N",s:"S",f:"F"},f=Symbol(),d=e=>{const t=u(e);return"object"===t?a(e)?"Null":e.constructor.name:h[t[0]]+t.slice(1)},p=e=>e.length,g=r(((e,t)=>e===t)),m=r(((e,t)=>{const n=d(e);if(g(n,d(t))&&(g(n,"Object")||g(n,"Array"))){if(a(e)||a(t))return g(e,t);if(g(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(g(n,t)&&s in e||g(n,e)&&s in t&&m(e[s],t[s])))return!1;return!0}return g(e,t)})),y=r(((e,t)=>(t.push(e),t))),w=i(((e,t,n)=>n.reduce(e,t))),b=o(((e,t,n,s)=>e(s)?t(s):n(s))),_=(...t)=>(...n)=>{let s,o=!0;for(let r=p(t)-1;r>-1;r--)o?(o=!1,s=t[r](...n)):s=s===e?t[r]():t[r](s);return s},k=r(((e,t)=>t[e])),S=i(((e,t,n)=>n.slice(e,(e=>"number"==u(e))(t)?t:c))),v=k(0);S(1,c);const E=r(((e,t)=>t.find(e))),P=e=>()=>e,W=r(((e,t)=>t.split(e))),j=P(!0),N=P(!1),q=r(((e,t)=>w(((t,n)=>E((t=>e(n,t)),t)?t:y(n,t)),[],t)))(m),z=i(((e,t,n)=>p(t)?(e=>a(e)||(e=>e===l)(e))(n)?e:_((s=>s in n?z(e,S(1,c,t),n[s]):e),v)(t):n));z(l),_(b(m(f),N,j),z(f));const Q=r(((e,t)=>t.map(e))),{floor:A}=Math,C="0123456789abcdefghijklmnopqrstuvwxyz",O=_((e=>Object.fromEntries(e)),Q(((e,t)=>[e,t])),W(""));class ${abc;abclen;c2pos;standard;setABC(e){if(!_(m(p(t=e)),p,q,W(""))(t))throw new Error("Not all chars are unique!");var t;this.abc=e,this.abclen=e.length,this.standard=C.startsWith(e),this.c2pos=O(e)}zip(e){const{abc:t,abclen:n}=this;let s="";for(;e>0;)s=t[e%n]+s,e=A(e/n);return s||"0"}unzip(e){const{standard:t,abclen:n,c2pos:s}=this;if(t)return parseInt(e,n);const o=e.length;let r=0;for(let t=0;t<o;t++)r+=s[e[t]]*n**(o-t-1);return r}constructor(e){this.setABC(e||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=(e,t,n)=>e.addEventListener(t,n),T=(e,t)=>setTimeout(t,e),D={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,lazy:!1,socket:null,adapter:(e,t)=>new WebSocket(e,t),encode:(e,t,{server:n})=>JSON.stringify({[n.id_key]:e,[n.data_key]:t}),decode:e=>JSON.parse(e),protocols:[],pipes:[],server:{id_key:"id",data_key:"data"},ping:{interval:55,content:{}}},U=Symbol("Placeholder"),x=e=>{let t=0;for(const n of e)n!==U&&t++;return t},F=(e,t)=>{const n=e.length,s=e.slice(),o=t.length;let r=o,i=0;for(;r&&i<n;i++)s[i]===U&&(s[i]=t[o-r],r--);for(i=n;r;i++,r--)s[i]=t[o-r];return s},J=(e,t,n)=>{const s=e.length-t.length-x(n);if(s<1)return e(...F(t,n));{const o=(...s)=>J(e,F(t,n),s);return o.$args_left=s,o}},L=e=>(...t)=>e.length>x(t)?J(e,[],t):e(...t);function M(e){return function(t,n){const s=t===U,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return o>1?s?(e=>function(t){return t===U?e:e(t)})((t=>e(t,n))):e(t,n):n=>e(t,n)}}function G(e){return L(e)}const H=void 0,K=1/0,V=e=>typeof e,X=e=>null===e,Y=e=>"number"==V(e),Z={u:"U",b:"B",n:"N",s:"S",f:"F"},ee=Symbol(),te=e=>{const t=V(e);return"object"===t?X(e)?"Null":e.constructor.name:Z[t[0]]+t.slice(1)},ne=e=>e.length,se=e=>X(e)||(e=>e===H)(e),oe=M(((e,t)=>e===t)),re=M(((e,t)=>{const n=te(e);if(oe(n,te(t))&&(oe(n,"Object")||oe(n,"Array"))){if(X(e)||X(t))return oe(e,t);if(oe(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(oe(n,t)&&s in e||oe(n,e)&&s in t&&re(e[s],t[s])))return!1;return!0}return oe(e,t)})),ie=M(((e,t)=>(t.push(e),t))),le=G(((e,t,n)=>n.reduce(e,t))),ce=L(((e,t,n,s)=>e(s)?t(s):n(s))),ue=(...e)=>(...t)=>{let n,s=!0;for(let o=ne(e)-1;o>-1;o--)s?(s=!1,n=e[o](...t)):n=n===U?e[o]():e[o](n);return n},ae=M(((e,t)=>t[e])),he=G(((e,t,n)=>n.slice(e,Y(t)?t:K))),fe=ae(0);he(1,K);const de=M(((e,t)=>t.find(e))),pe=e=>()=>e,ge=pe(!0),me=pe(!1),ye=M(((e,t)=>le(((t,n)=>de((t=>e(n,t)),t)?t:ie(n,t)),[],t)));ye(re);const we=e=>{let t,n=!1;return(...s)=>n?t:(n=!0,t=e(...s))},be=G(((e,t,n)=>ne(t)?se(n)?e:ue((s=>s in n?be(e,he(1,K,t),n[s]):e),fe)(t):n));be(H),ue(ce(re(ee),me,ge),be(ee));const _e=new $;module.exports=class{open=!1;ws=null;forcibly_closed=!1;reconnect_timeout=null;queue={};messages=[];onReadyQueue=[];onCloseQueue=[];handlers={open:[],close:[],message:[],error:[],timeout:[]};config={};init_flush(){this.queue={},this.messages=[]}call(e,...t){for(const n of this.handlers[e])n(...t)}log(e,t=null,n=null){const s=this.config;null!==n?s.log(e,n,t):s.timer?s.log(e,null,t):s.log(e,t)}initSocket(e){const t=this.config;this.open=!0,this.onReadyQueue.forEach((e=>e())),this.onReadyQueue.splice(0);const{id_key:n,data_key:s}=t.server;if(this.call("open",e),this.messages.forEach((e=>e.send())),null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),t.ping){const e=setInterval((()=>{this.open&&this.send(t.ping.content),this.forcibly_closed&&clearInterval(e)}),1e3*t.ping.interval)}I(e,"close",(async(...e)=>{this.log("close"),this.open=!1,this.onCloseQueue.forEach((e=>e())),this.onCloseQueue.splice(0),this.call("close",...e);const n=t.reconnect;if("number"!=typeof n||isNaN(n)||this.forcibly_closed)this.ws=null,this.open=!1;else{const e=async()=>{this.log("reconnect"),null!==this.ws&&(this.ws.close(),this.ws=null);null!==await this.connect()&&(this.reconnect_timeout=setTimeout(e,1e3*n))};e()}this.forcibly_closed=!1})),I(e,"message",(e=>{try{const o=t.decode(e.data);if(this.call("message",{...e,data:o}),o[n]){const e=this.queue[o[n]];if(e){const t=e.sent_time?Date.now()-e.sent_time:null;this.log("message",o[s],t),e.ff(o[s]),clearTimeout(e.timeout),delete this.queue[o[n]]}}}catch(t){console.error(t,`WSP: Decode error. Got: ${e.data}`)}}))}connect(){return new Promise((e=>{if(!0===this.open)return e(null);const t=this.config,n=t.socket||t.adapter(t.url,t.protocols);if(this.ws=n,!n||n.readyState>1)return this.ws=null,this.log("error","ready() on closing or closed state! status 2."),e(2);const s=we(e);I(n,"error",we((e=>{this.log("error","status 3."),this.call("error",e),this.ws=null,s(3)}))),n.readyState?(this.initSocket(n),s(null)):I(n,"open",we((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((e=>{this.open?e():this.onReadyQueue.push(e)}))}on(e,t,n=ge,s=!1){const o=e=>n(e)&&t(e);return s?I(this.ws,e,o):this.handlers[e].push(o)}async close(){return new Promise(((e,t)=>{null===this.ws?t("WSP: closing a non-inited socket!"):(this.open=!1,this.onCloseQueue.push((()=>{this.init_flush(),this.ws=null,this.forcibly_closed=!0,e(null)})),this.ws.close())}))}async send(e,t={}){this.log("send",e);const n=this.config,s={},o=n.server.data_key,r=n.lazy&&!this.open,i=_e.zip(2147483637*Math.random()|0);if("object"==typeof t.top){if(t.top[o])throw new Error("Attempting to set data key/token via send() options!");Object.assign(s,t.top)}if(n.pipes.forEach((t=>e=t(e))),!0===this.open)this.ws.send(n.encode(i,e,n));else if(!1===this.open||r)this.messages.push({send:()=>this.ws.send(n.encode(i,e,n))}),r&&this.connect();else if(null===this.open)throw new Error("Attempting to send via closed WebSocket connection!");return new Promise(((t,s)=>{this.queue[i]={ff:t,data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:T(n.timeout,(()=>{this.queue[i]&&(this.call("timeout",e),s({"Websocket timeout expired: ":n.timeout,"for the message ":e}),delete this.queue[i])}))}}))}constructor(e={}){this.config=(e=>{if(null===R&&!("adapter"in e))throw new Error("\n This platform has no native WebSocket implementation.\n Please use 'ws' package as an adapter.\n See https://github.com/houd1ni/WebsocketPromisify/issues/23\n ");const t=Object.assign({},D,e),n=t.url;if("/"==n[0])try{const e=location.protocol.includes("s:")?"wss":"ws";t.url=`${e}://${location.hostname}:${location.port}${n}`}catch(e){throw new Error("WSP: URL starting with / in non-browser environment!")}return t})(e),this.init_flush(),this.config.lazy||this.connect()}};
package/dist/bundle.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- // Generated by dts-bundle-generator v7.1.0
1
+ // Generated by dts-bundle-generator v9.5.1
2
2
 
3
- export declare namespace wsc {
3
+ 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;
@@ -60,12 +60,18 @@ declare class WebSocketClient {
60
60
  private handlers;
61
61
  private config;
62
62
  private init_flush;
63
+ private call;
63
64
  private log;
65
+ private initSocket;
64
66
  private connect;
65
67
  get socket(): wsc.Socket | null;
66
68
  ready(): Promise<void>;
67
69
  on(event_name: wsc.WSEvent, handler: (data: any) => any, predicate?: (data: any) => boolean, raw?: boolean): number | void;
68
70
  close(): wsc.AsyncErrCode;
71
+ /** .send(your_data) wraps request to server with {id: `hash`, data: `actually your data`},
72
+ returns a Promise that will be rejected after a timeout or
73
+ resolved if server returns the same signature: {id: `same_hash`, data: `response data`}.
74
+ */
69
75
  send<RequestDataType = any, ResponseDataType = any>(message_data: RequestDataType, opts?: wsc.SendOptions): Promise<ResponseDataType>;
70
76
  constructor(user_config?: wsc.UserConfig);
71
77
  }
package/dist/bundle.mjs CHANGED
@@ -1 +1 @@
1
- const e=Symbol("Placeholder"),t=t=>{let n=0;for(const s of t)s!==e&&n++;return n},n=(t,n)=>{const s=t.length,o=t.slice(),r=n.length;let i=r,c=0;for(;i&&c<s;c++)o[c]===e&&(o[c]=n[r-i],i--);for(c=s;i;c++,i--)o[c]=n[r-i];return o},s=(e,o,r)=>{const i=e.length-o.length-t(r);if(i<1)return e(...n(o,r));{const t=(...t)=>s(e,n(o,r),t);return t.$args_left=i,t}},o=e=>(...n)=>e.length>t(n)?s(e,[],n):e(...n),r=t=>function(n){return n===e?t:t(n)};function i(t){return function(n,s){const o=n===e,i=arguments.length;if(1===i&&o)throw new Error("Senseless placeholder usage.");return arguments.length>1?o?r((e=>t(e,s))):t(n,s):e=>t(n,e)}}function c(e){return o(e)}const l=void 0,u=1/0,a=e=>typeof e,h=e=>null===e,f={u:"U",b:"B",n:"N",s:"S",f:"F"},d=e=>{const t=a(e);return"object"===t?h(e)?"Null":e.constructor.name:f[t[0]]+t.slice(1)},g=i(((e,t)=>(t.push(e),t))),p=c(((e,t,n)=>n.reduce(e,t))),m=c(((e,t,n)=>{for(let s in n)switch(d(n[s])){case"Array":if(e>1&&"Array"===d(t[s]))switch(e){case 2:const o=t[s],r=n[s];for(const t in r)o[t]?m(e,o[t],r[t]):o[t]=r[t];break;case 3:t[s].push(...n[s])}else t[s]=n[s];break;case"Object":if("Object"===d(t[s])){m(e,t[s],n[s]);break}default:t[s]=n[s]}return t}));m(1),m(2),m(3);const y=i(((e,t)=>{const n=d(e);if(n===d(t)&&("Object"===n||"Array"==n)){if(h(e)||h(t))return e===t;if(e===t)return!0;for(const n of[e,t])for(const s in n)if(!(n===t&&s in e||n===e&&s in t&&y(e[s],t[s])))return!1;return!0}return e===t})),w=o(((e,t,n,s)=>e(s)?t(s):n(s))),b=(...t)=>(...n)=>{let s,o=!0;for(let r=j(t)-1;r>-1;r--)o?(o=!1,s=t[r](...n)):s=s===e?t[r]():t[r](s);return s},_=i(((e,t)=>t[e])),k=i(((e,t)=>{if((e=>"string"===a(e))(t))return t.includes(e);for(const n of t)if(y(n,e))return!0;return!1})),S=c(((e,t,n)=>n.slice(e,(e=>"number"==a(e))(t)?t:u))),v=_(0);S(1,u);const E=e=>h(e)||(e=>e===l)(e),j=e=>e.length,N=e=>()=>e,O=i(((e,t)=>t.split(e))),A=e=>p(((e,t)=>k(t,e)?e:g(t,e)),[],e),P=c(((e,t,n)=>({...n,[e]:t}))),q=i(((e,t)=>t[e])),Q=c(((e,t,n)=>w(j,(()=>E(n)?e:b(w(E,N(e),(n=>Q(e,S(1,u,t),n))),(e=>i(((t,n)=>e(n,t))))(q)(n),v)(t)),N(n),t)));Q(l);const $=/^(.*?)(8|16|32|64)(Clamped)?Array$/,W=(e,t=!1)=>{const n=d(e);switch(n){case"Null":case"String":case"Number":case"Boolean":case"Symbol":return e;case"Array":return t?[...e]:R(b(W,((...e)=>e[0])),e);case"Object":if(t)return{...e};const s={};for(let t in e)s[t]=W(e[t]);return s;default:return $.test(n)?e.constructor.from(e):e}},C=c(((e,t,n)=>p(e,W(t),n))),R=i(((e,t)=>t.map(e))),{floor:z}=Math;let B,D;const I=b((e=>C(((e,t)=>P(...t,e)),{},e)),R(((e,t)=>[e,t])),O(""));(e=>{if(!(e=>b(y(j(e)),j,A,O(""))(e))(e))throw new Error("Not all chars are unique!");B=e,D=B.length,I(B)})("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");const T=(e,t,n)=>e.addEventListener(t,n),U=e=>{let t=!1,n=null;return(...s)=>t?n:(t=!0,n=e(...s))},x=(e,t)=>setTimeout(t,e),F=function(e){const t=this.config;this.open=!0,this.onReadyQueue.forEach((e=>e())),this.onReadyQueue.splice(0);const{id_key:n,data_key:s}=t.server;if(this.handlers.open.forEach((e=>e())),this.messages.forEach((e=>e.send())),null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),t.ping){const e=setInterval((()=>{this.open&&this.send(t.ping.content),this.forcibly_closed&&clearInterval(e)}),1e3*t.ping.interval)}T(e,"close",(async()=>{this.log("close"),this.open=!1,this.onCloseQueue.forEach((e=>e())),this.onCloseQueue=[];const e=t.reconnect;if("number"!=typeof e||isNaN(e)||this.forcibly_closed)this.ws=null,this.open=null;else{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})),T(e,"message",(e=>{try{const o=t.decode(e.data);if(this.handlers.message.forEach((t=>t({...e,data:o}))),o[n]){const e=this.queue[o[n]];if(e){const t=e.sent_time?Date.now()-e.sent_time:null;this.log("message",o[s],t),e.ff(o[s]),clearTimeout(e.timeout),delete this.queue[o[n]]}}}catch(t){console.error(t,`WSP: Decode error. Got: ${e.data}`)}}))};var J=function(e){if(!0===this.open)return e(null);const t=this.config,n=t.socket||t.adapter(t.url,t.protocols);if(this.ws=n,!n||n.readyState>1)return this.ws=null,this.log("error","ready() on closing or closed state! status 2."),e(2);T(n,"error",U((t=>(this.log("error","status 3."),this.handlers.error.forEach((e=>e(t))),this.ws=null,e(3))))),n.readyState?(F.call(this,n),e(null)):T(n,"open",U((()=>(this.log("open"),F.call(this,n),e(null)))))};const L={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,lazy:!1,socket:null,adapter:(e,t)=>new WebSocket(e,t),encode:(e,t,{server:n})=>JSON.stringify({[n.id_key]:e,[n.data_key]:t}),decode:e=>JSON.parse(e),protocols:[],pipes:[],server:{id_key:"id",data_key:"data"},ping:{interval:55,content:{}}},M=Symbol("Placeholder"),G=e=>{let t=0;for(const n of e)n!==M&&t++;return t},H=(e,t)=>{const n=e.length,s=e.slice(),o=t.length;let r=o,i=0;for(;r&&i<n;i++)s[i]===M&&(s[i]=t[o-r],r--);for(i=n;r;i++,r--)s[i]=t[o-r];return s},K=(e,t,n)=>{const s=e.length-t.length-G(n);if(s<1)return e(...H(t,n));{const o=(...s)=>K(e,H(t,n),s);return o.$args_left=s,o}},V=e=>(...t)=>e.length>G(t)?K(e,[],t):e(...t),X=e=>function(t){return t===M?e:e(t)};function Y(e){return function(t,n){const s=t===M,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return arguments.length>1?s?X((t=>e(t,n))):e(t,n):n=>e(t,n)}}function Z(e){return V(e)}const ee=void 0,te=1/0,ne=e=>typeof e,se=e=>null===e,oe={u:"U",b:"B",n:"N",s:"S",f:"F"},re=e=>{const t=ne(e);return"object"===t?se(e)?"Null":e.constructor.name:oe[t[0]]+t.slice(1)},ie=Z(((e,t,n)=>{for(let s in n)switch(re(n[s])){case"Array":if(e>1&&"Array"===re(t[s]))switch(e){case 2:const o=t[s],r=n[s];for(const t in r)o[t]?ie(e,o[t],r[t]):o[t]=r[t];break;case 3:t[s].push(...n[s])}else t[s]=n[s];break;case"Object":if("Object"===re(t[s])){ie(e,t[s],n[s]);break}default:t[s]=n[s]}return t}));ie(1),ie(2),ie(3);const ce=V(((e,t,n,s)=>e(s)?t(s):n(s))),le=(...e)=>(...t)=>{let n,s=!0;for(let o=de(e)-1;o>-1;o--)s?(s=!1,n=e[o](...t)):n=n===M?e[o]():e[o](n);return n},ue=Y(((e,t)=>t[e])),ae=Z(((e,t,n)=>n.slice(e,(e=>"number"==ne(e))(t)?t:te))),he=ue(0);ae(1,te);const fe=e=>se(e)||(e=>e===ee)(e),de=e=>e.length,ge=e=>()=>e,pe=ge(!0),me=Y(((e,t)=>t[e])),ye=Z(((e,t,n)=>ce(de,(()=>fe(n)?e:le(ce(fe,ge(e),(n=>ye(e,ae(1,te,t),n))),(e=>Y(((t,n)=>e(n,t))))(me)(n),he)(t)),ge(n),t)));ye(ee);class we{open=!1;ws=null;forcibly_closed=!1;reconnect_timeout=null;queue={};messages=[];onReadyQueue=[];onCloseQueue=[];handlers={open:[],message:[],close:[],error:[]};config={};init_flush(){this.queue={},this.messages=[]}log(e,t=null,n=null){const s=this.config;null!==n?s.log(e,n,t):s.timer?s.log(e,null,t):s.log(e,t)}async connect(){return new Promise((e=>{J.call(this,e)}))}get socket(){return this.ws}async ready(){return new Promise((e=>{this.open?e():this.onReadyQueue.push(e)}))}on(e,t,n=pe,s=!1){const o=e=>n(e)&&t(e);return s?T(this.ws,e,o):this.handlers[e].push(o)}async close(){return new Promise(((e,t)=>{null===this.ws?t("WSP: closing a non-inited socket!"):(this.open=!1,this.onCloseQueue.push((()=>{this.init_flush(),this.ws=null,this.forcibly_closed=!0,e(null)})),this.ws.close())}))}async send(e,t={}){this.log("send",e);const n=this.config,s={},o=n.server.data_key,r=n.lazy&&!this.open,i=(e=>{let t="";for(;e>0;)t=B[e%D]+t,e=z(e/D);return t||"0"})(2147483637*Math.random()|0);if("object"==typeof t.top){if(t.top[o])throw new Error("Attempting to set data key/token via send() options!");Object.assign(s,t.top)}if(n.pipes.forEach((t=>e=t(e))),!0===this.open)this.ws.send(n.encode(i,e,n));else if(!1===this.open||r)this.messages.push({send:()=>this.ws.send(n.encode(i,e,n))}),r&&this.connect();else if(null===this.open)throw new Error("Attempting to send via closed WebSocket connection!");return new Promise(((t,s)=>{this.queue[i]={ff:t,data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:x(n.timeout,(()=>{this.queue[i]&&(s({"Websocket timeout expired: ":n.timeout,"for the message ":e}),delete this.queue[i])}))}}))}constructor(e={}){this.config=(e=>{const t=Object.assign({},L,e),n=t.url;if("/"==n[0])try{const e=location.protocol.includes("s:")?"wss":"ws";t.url=`${e}://${location.hostname}:${location.port}${n}`}catch(e){throw new Error("WSP: URL starting with / in non-browser environment!")}return t})(e),this.init_flush(),this.open=!1,this.reconnect_timeout=null,this.forcibly_closed=!1,this.config.lazy||this.connect()}}export{we as default};
1
+ const e=Symbol("Placeholder"),t=t=>{let n=0;for(const s of t)s!==e&&n++;return n},n=(t,n)=>{const s=t.length,o=t.slice(),r=n.length;let i=r,l=0;for(;i&&l<s;l++)o[l]===e&&(o[l]=n[r-i],i--);for(l=s;i;l++,i--)o[l]=n[r-i];return o},s=(e,o,r)=>{const i=e.length-o.length-t(r);if(i<1)return e(...n(o,r));{const t=(...t)=>s(e,n(o,r),t);return t.$args_left=i,t}},o=e=>(...n)=>e.length>t(n)?s(e,[],n):e(...n);function r(t){return function(n,s){const o=n===e,r=arguments.length;if(1===r&&o)throw new Error("Senseless placeholder usage.");return r>1?o?(t=>function(n){return n===e?t:t(n)})((e=>t(e,s))):t(n,s):e=>t(n,e)}}function i(e){return o(e)}const l=void 0,c=1/0,u=e=>typeof e,a=e=>null===e,h={u:"U",b:"B",n:"N",s:"S",f:"F"},f=Symbol(),d=e=>{const t=u(e);return"object"===t?a(e)?"Null":e.constructor.name:h[t[0]]+t.slice(1)},p=e=>e.length,g=r(((e,t)=>e===t)),m=r(((e,t)=>{const n=d(e);if(g(n,d(t))&&(g(n,"Object")||g(n,"Array"))){if(a(e)||a(t))return g(e,t);if(g(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(g(n,t)&&s in e||g(n,e)&&s in t&&m(e[s],t[s])))return!1;return!0}return g(e,t)})),y=r(((e,t)=>(t.push(e),t))),w=i(((e,t,n)=>n.reduce(e,t))),b=o(((e,t,n,s)=>e(s)?t(s):n(s))),_=(...t)=>(...n)=>{let s,o=!0;for(let r=p(t)-1;r>-1;r--)o?(o=!1,s=t[r](...n)):s=s===e?t[r]():t[r](s);return s},k=r(((e,t)=>t[e])),S=i(((e,t,n)=>n.slice(e,(e=>"number"==u(e))(t)?t:c))),v=k(0);S(1,c);const E=r(((e,t)=>t.find(e))),P=e=>()=>e,W=r(((e,t)=>t.split(e))),j=P(!0),N=P(!1),q=r(((e,t)=>w(((t,n)=>E((t=>e(n,t)),t)?t:y(n,t)),[],t)))(m),z=i(((e,t,n)=>p(t)?(e=>a(e)||(e=>e===l)(e))(n)?e:_((s=>s in n?z(e,S(1,c,t),n[s]):e),v)(t):n));z(l),_(b(m(f),N,j),z(f));const Q=r(((e,t)=>t.map(e))),{floor:A}=Math,C="0123456789abcdefghijklmnopqrstuvwxyz",O=_((e=>Object.fromEntries(e)),Q(((e,t)=>[e,t])),W(""));class ${abc;abclen;c2pos;standard;setABC(e){if(!_(m(p(t=e)),p,q,W(""))(t))throw new Error("Not all chars are unique!");var t;this.abc=e,this.abclen=e.length,this.standard=C.startsWith(e),this.c2pos=O(e)}zip(e){const{abc:t,abclen:n}=this;let s="";for(;e>0;)s=t[e%n]+s,e=A(e/n);return s||"0"}unzip(e){const{standard:t,abclen:n,c2pos:s}=this;if(t)return parseInt(e,n);const o=e.length;let r=0;for(let t=0;t<o;t++)r+=s[e[t]]*n**(o-t-1);return r}constructor(e){this.setABC(e||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=(e,t,n)=>e.addEventListener(t,n),T=(e,t)=>setTimeout(t,e),D={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,lazy:!1,socket:null,adapter:(e,t)=>new WebSocket(e,t),encode:(e,t,{server:n})=>JSON.stringify({[n.id_key]:e,[n.data_key]:t}),decode:e=>JSON.parse(e),protocols:[],pipes:[],server:{id_key:"id",data_key:"data"},ping:{interval:55,content:{}}},U=Symbol("Placeholder"),x=e=>{let t=0;for(const n of e)n!==U&&t++;return t},F=(e,t)=>{const n=e.length,s=e.slice(),o=t.length;let r=o,i=0;for(;r&&i<n;i++)s[i]===U&&(s[i]=t[o-r],r--);for(i=n;r;i++,r--)s[i]=t[o-r];return s},J=(e,t,n)=>{const s=e.length-t.length-x(n);if(s<1)return e(...F(t,n));{const o=(...s)=>J(e,F(t,n),s);return o.$args_left=s,o}},L=e=>(...t)=>e.length>x(t)?J(e,[],t):e(...t);function M(e){return function(t,n){const s=t===U,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return o>1?s?(e=>function(t){return t===U?e:e(t)})((t=>e(t,n))):e(t,n):n=>e(t,n)}}function G(e){return L(e)}const H=void 0,K=1/0,V=e=>typeof e,X=e=>null===e,Y=e=>"number"==V(e),Z={u:"U",b:"B",n:"N",s:"S",f:"F"},ee=Symbol(),te=e=>{const t=V(e);return"object"===t?X(e)?"Null":e.constructor.name:Z[t[0]]+t.slice(1)},ne=e=>e.length,se=e=>X(e)||(e=>e===H)(e),oe=M(((e,t)=>e===t)),re=M(((e,t)=>{const n=te(e);if(oe(n,te(t))&&(oe(n,"Object")||oe(n,"Array"))){if(X(e)||X(t))return oe(e,t);if(oe(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(oe(n,t)&&s in e||oe(n,e)&&s in t&&re(e[s],t[s])))return!1;return!0}return oe(e,t)})),ie=M(((e,t)=>(t.push(e),t))),le=G(((e,t,n)=>n.reduce(e,t))),ce=L(((e,t,n,s)=>e(s)?t(s):n(s))),ue=(...e)=>(...t)=>{let n,s=!0;for(let o=ne(e)-1;o>-1;o--)s?(s=!1,n=e[o](...t)):n=n===U?e[o]():e[o](n);return n},ae=M(((e,t)=>t[e])),he=G(((e,t,n)=>n.slice(e,Y(t)?t:K))),fe=ae(0);he(1,K);const de=M(((e,t)=>t.find(e))),pe=e=>()=>e,ge=pe(!0),me=pe(!1),ye=M(((e,t)=>le(((t,n)=>de((t=>e(n,t)),t)?t:ie(n,t)),[],t)));ye(re);const we=e=>{let t,n=!1;return(...s)=>n?t:(n=!0,t=e(...s))},be=G(((e,t,n)=>ne(t)?se(n)?e:ue((s=>s in n?be(e,he(1,K,t),n[s]):e),fe)(t):n));be(H),ue(ce(re(ee),me,ge),be(ee));const _e=new $;class ke{open=!1;ws=null;forcibly_closed=!1;reconnect_timeout=null;queue={};messages=[];onReadyQueue=[];onCloseQueue=[];handlers={open:[],close:[],message:[],error:[],timeout:[]};config={};init_flush(){this.queue={},this.messages=[]}call(e,...t){for(const n of this.handlers[e])n(...t)}log(e,t=null,n=null){const s=this.config;null!==n?s.log(e,n,t):s.timer?s.log(e,null,t):s.log(e,t)}initSocket(e){const t=this.config;this.open=!0,this.onReadyQueue.forEach((e=>e())),this.onReadyQueue.splice(0);const{id_key:n,data_key:s}=t.server;if(this.call("open",e),this.messages.forEach((e=>e.send())),null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),t.ping){const e=setInterval((()=>{this.open&&this.send(t.ping.content),this.forcibly_closed&&clearInterval(e)}),1e3*t.ping.interval)}I(e,"close",(async(...e)=>{this.log("close"),this.open=!1,this.onCloseQueue.forEach((e=>e())),this.onCloseQueue.splice(0),this.call("close",...e);const n=t.reconnect;if("number"!=typeof n||isNaN(n)||this.forcibly_closed)this.ws=null,this.open=!1;else{const e=async()=>{this.log("reconnect"),null!==this.ws&&(this.ws.close(),this.ws=null);null!==await this.connect()&&(this.reconnect_timeout=setTimeout(e,1e3*n))};e()}this.forcibly_closed=!1})),I(e,"message",(e=>{try{const o=t.decode(e.data);if(this.call("message",{...e,data:o}),o[n]){const e=this.queue[o[n]];if(e){const t=e.sent_time?Date.now()-e.sent_time:null;this.log("message",o[s],t),e.ff(o[s]),clearTimeout(e.timeout),delete this.queue[o[n]]}}}catch(t){console.error(t,`WSP: Decode error. Got: ${e.data}`)}}))}connect(){return new Promise((e=>{if(!0===this.open)return e(null);const t=this.config,n=t.socket||t.adapter(t.url,t.protocols);if(this.ws=n,!n||n.readyState>1)return this.ws=null,this.log("error","ready() on closing or closed state! status 2."),e(2);const s=we(e);I(n,"error",we((e=>{this.log("error","status 3."),this.call("error",e),this.ws=null,s(3)}))),n.readyState?(this.initSocket(n),s(null)):I(n,"open",we((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((e=>{this.open?e():this.onReadyQueue.push(e)}))}on(e,t,n=ge,s=!1){const o=e=>n(e)&&t(e);return s?I(this.ws,e,o):this.handlers[e].push(o)}async close(){return new Promise(((e,t)=>{null===this.ws?t("WSP: closing a non-inited socket!"):(this.open=!1,this.onCloseQueue.push((()=>{this.init_flush(),this.ws=null,this.forcibly_closed=!0,e(null)})),this.ws.close())}))}async send(e,t={}){this.log("send",e);const n=this.config,s={},o=n.server.data_key,r=n.lazy&&!this.open,i=_e.zip(2147483637*Math.random()|0);if("object"==typeof t.top){if(t.top[o])throw new Error("Attempting to set data key/token via send() options!");Object.assign(s,t.top)}if(n.pipes.forEach((t=>e=t(e))),!0===this.open)this.ws.send(n.encode(i,e,n));else if(!1===this.open||r)this.messages.push({send:()=>this.ws.send(n.encode(i,e,n))}),r&&this.connect();else if(null===this.open)throw new Error("Attempting to send via closed WebSocket connection!");return new Promise(((t,s)=>{this.queue[i]={ff:t,data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:T(n.timeout,(()=>{this.queue[i]&&(this.call("timeout",e),s({"Websocket timeout expired: ":n.timeout,"for the message ":e}),delete this.queue[i])}))}}))}constructor(e={}){this.config=(e=>{if(null===R&&!("adapter"in e))throw new Error("\n This platform has no native WebSocket implementation.\n Please use 'ws' package as an adapter.\n See https://github.com/houd1ni/WebsocketPromisify/issues/23\n ");const t=Object.assign({},D,e),n=t.url;if("/"==n[0])try{const e=location.protocol.includes("s:")?"wss":"ws";t.url=`${e}://${location.hostname}:${location.port}${n}`}catch(e){throw new Error("WSP: URL starting with / in non-browser environment!")}return t})(e),this.init_flush(),this.config.lazy||this.connect()}}export{ke as default};
package/package.json CHANGED
@@ -1,94 +1,79 @@
1
- {
2
- "author": {
3
- "name": "Michael Akiliev"
4
- },
5
- "bugs": {
6
- "url": "https://github.com/houd1ni/WebsocketPromisify/issues"
7
- },
8
- "bundleDependencies": [],
9
- "deprecated": false,
10
- "description": "Wraps your WebSockets into Promise-based class with full d.ts typings on client & server",
11
- "homepage": "https://github.com/houd1ni/WebsocketPromisify#readme",
12
- "keywords": [
13
- "WebSockets",
14
- "WS",
15
- "Promise",
16
- "Socket",
17
- "REST",
18
- "Ajax",
19
- "Easy",
20
- "realtime",
21
- "Middleware",
22
- "JSON",
23
- "Data",
24
- "transport",
25
- "API",
26
- "async"
27
- ],
28
- "license": "MIT",
29
- "name": "wspromisify",
30
- "repository": {
31
- "type": "git",
32
- "url": "git+https://github.com/houd1ni/WebsocketPromisify.git"
33
- },
34
- "scripts": {
35
- "lint": "tslint src/*.ts",
36
- "test": "npm run gentypes && npm run prod:es && ava",
37
- "test:report": "nyc npm test && nyc report --reporter=text-lcov > coverage.lcov && codecov",
38
- "test:lazy": "ava",
39
- "gentypes": "dts-bundle-generator --no-check -o dist/bundle.d.ts src/WS.ts",
40
- "dev": "cross-env NODE_ENV=development BUILD=es rollup -c",
41
- "prod:cjs": "cross-env NODE_ENV=production BUILD=cjs rollup -c",
42
- "prod:es": "cross-env NODE_ENV=production BUILD=es rollup -c",
43
- "prod": "npm run gentypes && npm run prod:es && npm run prod:cjs",
44
- "all": "npm run dev && npm run prod"
45
- },
46
- "version": "2.4.4",
47
- "type": "module",
48
- "exports": {
49
- ".": {
50
- "import": "./dist/bundle.mjs",
51
- "require": "./dist/bundle.cjs"
52
- }
53
- },
54
- "ava": {
55
- "files": [
56
- "./test/specs/*.js"
57
- ],
58
- "failFast": true,
59
- "timeout": "2m",
60
- "nodeArguments": [
61
- "--experimental-specifier-resolution=node"
62
- ]
63
- },
64
- "devDependencies": {
65
- "@babel/core": "^7.20.12",
66
- "@babel/polyfill": "^7.12.1",
67
- "@babel/register": "^7.18.9",
68
- "@rollup/plugin-commonjs": "^23.0.3",
69
- "@rollup/plugin-node-resolve": "^15.0.1",
70
- "@rollup/plugin-replace": "^5.0.1",
71
- "@rollup/plugin-terser": "^0.1.0",
72
- "@types/node": "^18.11.18",
73
- "@types/ws": "^8.5.4",
74
- "ava": "^5.1.1",
75
- "axios": "^1.2.2",
76
- "codecov": "^3.8.3",
77
- "cross-env": "^7.0.3",
78
- "dts-bundle-generator": "^7.1.0",
79
- "express": "^4.18.2",
80
- "nyc": "^15.1.0",
81
- "randomatic": "^3.1.1",
82
- "rollup": "^3.10.0",
83
- "rollup-plugin-typescript2": "^0.34.1",
84
- "ts-node": "^10.9.1",
85
- "tslint": "^6.1.3",
86
- "typescript": "^4.9.4",
87
- "ws": "^8.12.0"
88
- },
89
- "types": "./dist/bundle.d.ts",
90
- "dependencies": {
91
- "pepka": "^1.0.0-beta.1",
92
- "zipnum": "^1.0.0"
93
- }
94
- }
1
+ {
2
+ "author": {
3
+ "name": "Michael Akiliev"
4
+ },
5
+ "bugs": {
6
+ "url": "https://github.com/houd1ni/WebsocketPromisify/issues"
7
+ },
8
+ "deprecated": false,
9
+ "description": "Wraps your WebSockets into Promise-based class with full d.ts typings on client & server",
10
+ "homepage": "https://github.com/houd1ni/WebsocketPromisify#readme",
11
+ "keywords": [
12
+ "WebSockets",
13
+ "WS",
14
+ "Promise",
15
+ "Socket",
16
+ "REST",
17
+ "Ajax",
18
+ "Easy",
19
+ "realtime",
20
+ "Middleware",
21
+ "JSON",
22
+ "Data",
23
+ "transport",
24
+ "API",
25
+ "async"
26
+ ],
27
+ "license": "MIT",
28
+ "name": "wspromisify",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/houd1ni/WebsocketPromisify.git"
32
+ },
33
+ "scripts": {
34
+ "lint": "tslint src/*.ts",
35
+ "test": "tsx test/index",
36
+ "test:report": "nyc npm test && nyc report --reporter=text-lcov > coverage.lcov && codecov",
37
+ "gentypes": "dts-bundle-generator --no-check -o dist/bundle.d.ts src/WSC.ts",
38
+ "dev": "cross-env NODE_ENV=development BUILD=es rollup -c",
39
+ "prod:cjs": "cross-env NODE_ENV=production BUILD=cjs rollup -c",
40
+ "prod:es": "cross-env NODE_ENV=production BUILD=es rollup -c",
41
+ "prod": "npm run gentypes && npm run prod:es && npm run prod:cjs",
42
+ "all": "npm run dev && npm run prod"
43
+ },
44
+ "version": "2.6.0",
45
+ "type": "module",
46
+ "exports": {
47
+ ".": {
48
+ "types": "./dist/bundle.d.ts",
49
+ "import": "./dist/bundle.mjs",
50
+ "require": "./dist/bundle.cjs"
51
+ },
52
+ "./src": "./src/*"
53
+ },
54
+ "devDependencies": {
55
+ "@rollup/plugin-commonjs": "^28.0.3",
56
+ "@rollup/plugin-node-resolve": "^16.0.0",
57
+ "@rollup/plugin-replace": "^6.0.2",
58
+ "@rollup/plugin-terser": "^0.4.4",
59
+ "@types/express": "^5.0.0",
60
+ "@types/node": "^22.13.9",
61
+ "@types/ws": "^8.18.0",
62
+ "codecov": "^3.8.3",
63
+ "cross-env": "^7.0.3",
64
+ "dts-bundle-generator": "^9.5.1",
65
+ "nyc": "^17.1.0",
66
+ "pepka": "^1.6.2",
67
+ "rollup": "^4.34.9",
68
+ "rollup-plugin-typescript2": "^0.36.0",
69
+ "ts-node": "^10.9.2",
70
+ "tsx": "^4.19.3",
71
+ "typescript": "^5.8.2",
72
+ "uvu": "^0.5.6",
73
+ "ws": "^8.18.1"
74
+ },
75
+ "types": "./dist/bundle.d.ts",
76
+ "dependencies": {
77
+ "zipnum": "^2.0.0"
78
+ }
79
+ }
package/rollup.config.js CHANGED
@@ -6,7 +6,7 @@ import replace from '@rollup/plugin-replace'
6
6
  import tsc from 'typescript'
7
7
 
8
8
  export default {
9
- input: 'src/WS.ts',
9
+ input: 'src/WSC.ts',
10
10
  output: {
11
11
  file: process.env.NODE_ENV=='development'
12
12
  ? 'dist/bundle.dev.js'
package/src/WSC.ts ADDED
@@ -0,0 +1,266 @@
1
+ import './types'
2
+ import { Zipnum } from 'zipnum'
3
+ import { add_event, sett } from './utils'
4
+ import { processConfig } from './config'
5
+ import { AnyFunc, once, T } from 'pepka'
6
+
7
+ const MAX_32 = 2**31 - 1
8
+ const zipnum = new Zipnum()
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
+
19
+ class WebSocketClient {
20
+ private open = false
21
+ private ws: wsc.Socket|null = null
22
+ private forcibly_closed = false
23
+ private reconnect_timeout: NodeJS.Timeout|null = null
24
+ private queue = {}
25
+ private messages: any[] = []
26
+ private onReadyQueue: AnyFunc[] = []
27
+ private onCloseQueue: AnyFunc[] = []
28
+ private handlers: EventHandlers = { open: [], close: [], message: [], error: [], timeout: [] }
29
+ private config = <wsc.Config>{}
30
+
31
+ private init_flush(): void {
32
+ this.queue = {} // data queuse
33
+ this.messages = [] // send() queue
34
+ }
35
+ private call(event_name: wsc.WSEvent, ...args: any[]) {
36
+ // this.handlers.open[0]()
37
+ for(const h of this.handlers[event_name]) h(...args)
38
+ }
39
+
40
+ private log(event: string, message: any = null, time: number|null = null): void {
41
+ const config = this.config
42
+ if(time !== null) {
43
+ config.log(event, time, message)
44
+ } else {
45
+ if(config.timer) {
46
+ config.log(event, null, message)
47
+ } else {
48
+ config.log(event, message)
49
+ }
50
+ }
51
+ }
52
+
53
+ private initSocket(ws: wsc.Socket) {
54
+ const config = this.config
55
+ this.open = true
56
+ this.onReadyQueue.forEach((fn: Function) => fn())
57
+ this.onReadyQueue.splice(0)
58
+ const {id_key, data_key} = config.server
59
+ // Works also on previously opened sockets that do not fire 'open' event.
60
+ this.call('open', ws)
61
+ // Send all pending messages.
62
+ this.messages.forEach((message: any) => message.send())
63
+ // It's reconnecting.
64
+ if(this.reconnect_timeout !== null) {
65
+ clearInterval(this.reconnect_timeout)
66
+ this.reconnect_timeout = null
67
+ }
68
+ if(config.ping) {
69
+ const ping_interval = setInterval(() => {
70
+ if(this.open) this.send(config.ping.content)
71
+ if(this.forcibly_closed) clearInterval(ping_interval)
72
+ }, config.ping.interval*1e3)
73
+ }
74
+ add_event(ws, 'close', async (...e) => {
75
+ this.log('close')
76
+ this.open = false
77
+ this.onCloseQueue.forEach((fn: Function) => fn())
78
+ this.onCloseQueue.splice(0)
79
+ this.call('close', ...e)
80
+ // Auto reconnect.
81
+ const reconnect = config.reconnect
82
+ if(
83
+ typeof reconnect === 'number' &&
84
+ !isNaN(reconnect) &&
85
+ !this.forcibly_closed
86
+ ) {
87
+ const reconnectFunc = async () => {
88
+ this.log('reconnect')
89
+ if(this.ws !== null) {
90
+ this.ws.close()
91
+ this.ws = null
92
+ }
93
+ // If some error occured, try again.
94
+ const status = await this.connect()
95
+ if(status !== null)
96
+ this.reconnect_timeout = setTimeout(reconnectFunc, reconnect * 1000)
97
+ }
98
+ // TODO: test normal close by server. Would it be infinite ?
99
+ reconnectFunc()
100
+ } else {
101
+ this.ws = null
102
+ this.open = false
103
+ }
104
+ // reset the flag to reuse.
105
+ this.forcibly_closed = false
106
+ })
107
+ add_event(ws, 'message', (e) => {
108
+ try {
109
+ const data = config.decode(e.data)
110
+ this.call('message', {...e, data})
111
+ if(data[id_key]) {
112
+ const q = this.queue[data[id_key]]
113
+ if(q) {
114
+ // Debug, Log.
115
+ const time = q.sent_time ? (Date.now() - q.sent_time) : null
116
+ this.log('message', data[data_key], time)
117
+ // Play.
118
+ q.ff(data[data_key])
119
+ clearTimeout(q.timeout)
120
+ delete this.queue[data[id_key]]
121
+ }
122
+ }
123
+ } catch (err) {
124
+ console.error(err, `WSP: Decode error. Got: ${e.data}`)
125
+ }
126
+ })
127
+ }
128
+
129
+ private connect() { // returns status if won't open or null if ok.
130
+ return new Promise((ff) => {
131
+ if(this.open === true) {
132
+ return ff(null)
133
+ }
134
+ const config = this.config
135
+ const ws = config.socket || config.adapter(config.url, config.protocols)
136
+ this.ws = ws
137
+ if(!ws || ws.readyState > 1) {
138
+ this.ws = null
139
+ this.log('error', 'ready() on closing or closed state! status 2.')
140
+ return ff(2)
141
+ }
142
+ const ffo = once(ff)
143
+ add_event(ws, 'error', once((e) => {
144
+ this.log('error', 'status 3.')
145
+ this.call('error', e)
146
+ this.ws = null
147
+ // Some network error: Connection refused or so.
148
+ ffo(3)
149
+ }))
150
+ // Because 'open' won't be envoked on opened socket.
151
+ if(ws.readyState) {
152
+ this.initSocket(ws)
153
+ ffo(null)
154
+ } else {
155
+ add_event(ws, 'open', once(() => {
156
+ this.log('open')
157
+ this.initSocket(ws)
158
+ ffo(null)
159
+ }))
160
+ }
161
+ })
162
+ }
163
+ public get socket() { return this.ws }
164
+ public async ready() {
165
+ return new Promise<void>((ff) => {
166
+ if(this.open) {
167
+ ff()
168
+ } else {
169
+ this.onReadyQueue.push(ff)
170
+ }
171
+ })
172
+ }
173
+ public on(
174
+ event_name: wsc.WSEvent,
175
+ handler: (data: any) => any,
176
+ predicate: (data: any) => boolean = T,
177
+ raw = false
178
+ ) {
179
+ const _handler: wsc.EventHandler = (event) =>
180
+ predicate(event) && handler(event)
181
+ return raw
182
+ ? add_event(this.ws as wsc.Socket, event_name, _handler)
183
+ : this.handlers[event_name].push(_handler)
184
+ }
185
+
186
+ public async close(): wsc.AsyncErrCode {
187
+ return new Promise((ff, rj) => {
188
+ if(this.ws === null) {
189
+ rj('WSP: closing a non-inited socket!')
190
+ } else {
191
+ this.open = false
192
+ this.onCloseQueue.push(() => {
193
+ this.init_flush()
194
+ this.ws = null
195
+ this.forcibly_closed = true
196
+ ff(null)
197
+ })
198
+ this.ws.close()
199
+ }
200
+ })
201
+ }
202
+
203
+ /** .send(your_data) wraps request to server with {id: `hash`, data: `actually your data`},
204
+ returns a Promise that will be rejected after a timeout or
205
+ resolved if server returns the same signature: {id: `same_hash`, data: `response data`}.
206
+ */
207
+ public async send<RequestDataType = any, ResponseDataType = any>(
208
+ message_data: RequestDataType,
209
+ opts = <wsc.SendOptions>{}
210
+ ): Promise<ResponseDataType> {
211
+ this.log('send', message_data)
212
+ const config = this.config
213
+ const message = {}
214
+ const data_key = config.server.data_key
215
+ const first_time_lazy = config.lazy && !this.open
216
+
217
+ const message_id = zipnum.zip((Math.random()*(MAX_32-10))|0)
218
+ if(typeof opts.top === 'object') {
219
+ if(opts.top[data_key]) {
220
+ throw new Error('Attempting to set data key/token via send() options!')
221
+ }
222
+ Object.assign(message, opts.top)
223
+ }
224
+ config.pipes.forEach((pipe) => message_data = pipe(message_data))
225
+
226
+ if(this.open === true) {
227
+ (this.ws as wsc.Socket).send(config.encode(message_id, message_data, config))
228
+ } else if(this.open === false || first_time_lazy) {
229
+ this.messages.push({
230
+ send: () => (this.ws as wsc.Socket).send(config.encode(message_id, message_data, config))
231
+ })
232
+ if(first_time_lazy) this.connect()
233
+ } else if(this.open === null) {
234
+ throw new Error('Attempting to send via closed WebSocket connection!')
235
+ }
236
+
237
+ return new Promise((ff, rj) => {
238
+ // TODO: Make it class Message.
239
+ this.queue[message_id] = {
240
+ ff,
241
+ data_type: config.data_type,
242
+ sent_time: config.timer ? Date.now() : null,
243
+ timeout: sett(config.timeout, () => {
244
+ if(this.queue[message_id]) {
245
+ this.call('timeout', message_data)
246
+ rj({
247
+ 'Websocket timeout expired: ': config.timeout,
248
+ 'for the message ': message_data
249
+ })
250
+ delete this.queue[message_id]
251
+ }
252
+ })
253
+ }
254
+ })
255
+ }
256
+
257
+ // TODO: Add .on handlers to config!
258
+ constructor(user_config: wsc.UserConfig = {}) {
259
+ this.config = processConfig(user_config)
260
+ this.init_flush()
261
+ if(!this.config.lazy) this.connect()
262
+ }
263
+ }
264
+
265
+ /* TODO: v3: @.deprecated. Use named import { WebSocketClient } instead. */
266
+ export default WebSocketClient